diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime')
484 files changed, 74689 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/ArgList.cpp b/Source/JavaScriptCore/runtime/ArgList.cpp new file mode 100644 index 000000000..5149815c2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArgList.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "ArgList.h" + +#include "HeapRootVisitor.h" +#include "JSCJSValue.h" +#include "JSObject.h" +#include "JSCInlines.h" + +using std::min; + +namespace JSC { + +void ArgList::getSlice(int startIndex, ArgList& result) const +{ + if (startIndex <= 0 || startIndex >= m_argCount) { + result = ArgList(); + return; + } + + result.m_args = m_args + startIndex; + result.m_argCount = m_argCount - startIndex; +} + +void MarkedArgumentBuffer::markLists(HeapRootVisitor& heapRootVisitor, ListSet& markSet) +{ + ListSet::iterator end = markSet.end(); + for (ListSet::iterator it = markSet.begin(); it != end; ++it) { + MarkedArgumentBuffer* list = *it; + for (int i = 0; i < list->m_size; ++i) + heapRootVisitor.visit(reinterpret_cast<JSValue*>(&list->slotFor(i))); + } +} + +void MarkedArgumentBuffer::slowAppend(JSValue v) +{ + int newCapacity = m_capacity * 4; + EncodedJSValue* newBuffer = new EncodedJSValue[newCapacity]; + for (int i = 0; i < m_capacity; ++i) + newBuffer[i] = m_buffer[i]; + + if (EncodedJSValue* base = mallocBase()) + delete [] base; + + m_buffer = newBuffer; + m_capacity = newCapacity; + + slotFor(m_size) = JSValue::encode(v); + ++m_size; + + if (m_markSet) + return; + + // As long as our size stays within our Vector's inline + // capacity, all our values are allocated on the stack, and + // therefore don't need explicit marking. Once our size exceeds + // our Vector's inline capacity, though, our values move to the + // heap, where they do need explicit marking. + for (int i = 0; i < m_size; ++i) { + Heap* heap = Heap::heap(JSValue::decode(slotFor(i))); + if (!heap) + continue; + + m_markSet = &heap->markListSet(); + m_markSet->add(this); + break; + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArgList.h b/Source/JavaScriptCore/runtime/ArgList.h new file mode 100644 index 000000000..9aafc9070 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArgList.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef ArgList_h +#define ArgList_h + +#include "CallFrame.h" +#include "Register.h" +#include <wtf/HashSet.h> +#include <wtf/Vector.h> + +namespace JSC { + +class SlotVisitor; + +class MarkedArgumentBuffer { + WTF_MAKE_NONCOPYABLE(MarkedArgumentBuffer); + friend class VM; + friend class ArgList; + +private: + static const size_t inlineCapacity = 8; + typedef HashSet<MarkedArgumentBuffer*> ListSet; + +public: + // Constructor for a read-write list, to which you may append values. + // FIXME: Remove all clients of this API, then remove this API. + MarkedArgumentBuffer() + : m_size(0) + , m_capacity(inlineCapacity) + , m_buffer(m_inlineBuffer) + , m_markSet(0) + { + } + + ~MarkedArgumentBuffer() + { + if (m_markSet) + m_markSet->remove(this); + + if (EncodedJSValue* base = mallocBase()) + delete [] base; + } + + size_t size() const { return m_size; } + bool isEmpty() const { return !m_size; } + + JSValue at(int i) const + { + if (i >= m_size) + return jsUndefined(); + + return JSValue::decode(slotFor(i)); + } + + void clear() + { + m_size = 0; + } + + void append(JSValue v) + { + if (m_size >= m_capacity) + return slowAppend(v); + + slotFor(m_size) = JSValue::encode(v); + ++m_size; + } + + void removeLast() + { + ASSERT(m_size); + m_size--; + } + + JSValue last() + { + ASSERT(m_size); + return JSValue::decode(slotFor(m_size - 1)); + } + + static void markLists(HeapRootVisitor&, ListSet&); + +private: + JS_EXPORT_PRIVATE void slowAppend(JSValue); + + EncodedJSValue& slotFor(int item) const + { + return m_buffer[item]; + } + + EncodedJSValue* mallocBase() + { + if (m_capacity == static_cast<int>(inlineCapacity)) + return 0; + return &slotFor(0); + } + + int m_size; + int m_capacity; + EncodedJSValue m_inlineBuffer[inlineCapacity]; + EncodedJSValue* m_buffer; + ListSet* m_markSet; + +private: + // Prohibits new / delete, which would break GC. + void* operator new(size_t size) + { + return fastMalloc(size); + } + void operator delete(void* p) + { + fastFree(p); + } + + void* operator new[](size_t); + void operator delete[](void*); + + void* operator new(size_t, void*); + void operator delete(void*, size_t); +}; + +class ArgList { + friend class Interpreter; + friend class JIT; +public: + ArgList() + : m_args(0) + , m_argCount(0) + { + } + + ArgList(ExecState* exec) + : m_args(reinterpret_cast<JSValue*>(&exec[CallFrame::argumentOffset(0)])) + , m_argCount(exec->argumentCount()) + { + } + + ArgList(const MarkedArgumentBuffer& args) + : m_args(reinterpret_cast<JSValue*>(args.m_buffer)) + , m_argCount(args.size()) + { + } + + JSValue at(int i) const + { + if (i >= m_argCount) + return jsUndefined(); + return m_args[i]; + } + + bool isEmpty() const { return !m_argCount; } + size_t size() const { return m_argCount; } + + JS_EXPORT_PRIVATE void getSlice(int startIndex, ArgList& result) const; + +private: + JSValue* data() const { return m_args; } + + JSValue* m_args; + int m_argCount; +}; + +} // namespace JSC + +#endif // ArgList_h diff --git a/Source/JavaScriptCore/runtime/ArgumentsMode.h b/Source/JavaScriptCore/runtime/ArgumentsMode.h new file mode 100644 index 000000000..67cd3348d --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArgumentsMode.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ArgumentsMode_h +#define ArgumentsMode_h + +namespace JSC { + +enum class ArgumentsMode { + Cloned, + FakeValues +}; + +} // namespace JSC + +#endif // ArgumentsMode_h + diff --git a/Source/JavaScriptCore/runtime/ArityCheckMode.h b/Source/JavaScriptCore/runtime/ArityCheckMode.h new file mode 100644 index 000000000..f2090c057 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArityCheckMode.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ArityCheckMode_h +#define ArityCheckMode_h + +namespace JSC { + +enum ArityCheckMode { + ArityCheckNotRequired, + MustCheckArity +}; + +} // namespace JSC + +#endif // ArityCheckMode_h + diff --git a/Source/JavaScriptCore/runtime/ArrayBuffer.cpp b/Source/JavaScriptCore/runtime/ArrayBuffer.cpp new file mode 100644 index 000000000..f78a0c0f0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayBuffer.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009, 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ArrayBuffer.h" + +#include "ArrayBufferNeuteringWatchpoint.h" +#include "JSArrayBufferView.h" +#include "JSCInlines.h" +#include <wtf/RefPtr.h> + +namespace JSC { + +bool ArrayBuffer::transfer(ArrayBufferContents& result) +{ + Ref<ArrayBuffer> protect(*this); + + if (!m_contents.m_data) { + result.m_data = 0; + return false; + } + + bool isNeuterable = !m_pinCount; + + if (isNeuterable) + m_contents.transfer(result); + else { + m_contents.copyTo(result); + if (!result.m_data) + return false; + } + + for (size_t i = numberOfIncomingReferences(); i--;) { + JSCell* cell = incomingReferenceAt(i); + if (JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(cell)) + view->neuter(); + else if (ArrayBufferNeuteringWatchpoint* watchpoint = jsDynamicCast<ArrayBufferNeuteringWatchpoint*>(cell)) + watchpoint->fireAll(); + } + return true; +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/ArrayBuffer.h b/Source/JavaScriptCore/runtime/ArrayBuffer.h new file mode 100644 index 000000000..a7b6f33d7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayBuffer.h @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2009, 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ArrayBuffer_h +#define ArrayBuffer_h + +#include "GCIncomingRefCounted.h" +#include "Weak.h" +#include <wtf/PassRefPtr.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> + +namespace JSC { + +class ArrayBuffer; +class ArrayBufferView; +class JSArrayBuffer; + +class ArrayBufferContents { + WTF_MAKE_NONCOPYABLE(ArrayBufferContents); +public: + ArrayBufferContents() + : m_data(0) + , m_sizeInBytes(0) + { } + + inline ~ArrayBufferContents(); + + void* data() { return m_data; } + unsigned sizeInBytes() { return m_sizeInBytes; } + +private: + ArrayBufferContents(void* data, unsigned sizeInBytes) + : m_data(data) + , m_sizeInBytes(sizeInBytes) + { } + + friend class ArrayBuffer; + + enum InitializationPolicy { + ZeroInitialize, + DontInitialize + }; + + static inline void tryAllocate(unsigned numElements, unsigned elementByteSize, InitializationPolicy, ArrayBufferContents&); + void transfer(ArrayBufferContents& other) + { + ASSERT(!other.m_data); + other.m_data = m_data; + other.m_sizeInBytes = m_sizeInBytes; + m_data = 0; + m_sizeInBytes = 0; + } + + void copyTo(ArrayBufferContents& other) + { + ASSERT(!other.m_data); + ArrayBufferContents::tryAllocate(m_sizeInBytes, sizeof(char), ArrayBufferContents::DontInitialize, other); + if (!other.m_data) + return; + memcpy(other.m_data, m_data, m_sizeInBytes); + other.m_sizeInBytes = m_sizeInBytes; + } + + void* m_data; + unsigned m_sizeInBytes; +}; + +class ArrayBuffer : public GCIncomingRefCounted<ArrayBuffer> { +public: + static inline PassRefPtr<ArrayBuffer> create(unsigned numElements, unsigned elementByteSize); + static inline PassRefPtr<ArrayBuffer> create(ArrayBuffer*); + static inline PassRefPtr<ArrayBuffer> create(const void* source, unsigned byteLength); + static inline PassRefPtr<ArrayBuffer> create(ArrayBufferContents&); + static inline PassRefPtr<ArrayBuffer> createAdopted(const void* data, unsigned byteLength); + + // Only for use by Uint8ClampedArray::createUninitialized and SharedBuffer::createArrayBuffer. + static inline PassRefPtr<ArrayBuffer> createUninitialized(unsigned numElements, unsigned elementByteSize); + + inline void* data(); + inline const void* data() const; + inline unsigned byteLength() const; + + inline size_t gcSizeEstimateInBytes() const; + + inline PassRefPtr<ArrayBuffer> slice(int begin, int end) const; + inline PassRefPtr<ArrayBuffer> slice(int begin) const; + + inline void pin(); + inline void unpin(); + + JS_EXPORT_PRIVATE bool transfer(ArrayBufferContents&); + bool isNeutered() { return !m_contents.m_data; } + + static ptrdiff_t offsetOfData() { return OBJECT_OFFSETOF(ArrayBuffer, m_contents) + OBJECT_OFFSETOF(ArrayBufferContents, m_data); } + + ~ArrayBuffer() { } + +private: + static inline PassRefPtr<ArrayBuffer> create(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy); + + inline ArrayBuffer(ArrayBufferContents&); + inline PassRefPtr<ArrayBuffer> sliceImpl(unsigned begin, unsigned end) const; + inline unsigned clampIndex(int index) const; + static inline int clampValue(int x, int left, int right); + + unsigned m_pinCount; + ArrayBufferContents m_contents; + +public: + Weak<JSArrayBuffer> m_wrapper; +}; + +int ArrayBuffer::clampValue(int x, int left, int right) +{ + ASSERT(left <= right); + if (x < left) + x = left; + if (right < x) + x = right; + return x; +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize) +{ + return create(numElements, elementByteSize, ArrayBufferContents::ZeroInitialize); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(ArrayBuffer* other) +{ + return ArrayBuffer::create(other->data(), other->byteLength()); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(const void* source, unsigned byteLength) +{ + ArrayBufferContents contents; + ArrayBufferContents::tryAllocate(byteLength, 1, ArrayBufferContents::ZeroInitialize, contents); + if (!contents.m_data) + return 0; + RefPtr<ArrayBuffer> buffer = adoptRef(new ArrayBuffer(contents)); + ASSERT(!byteLength || source); + memcpy(buffer->data(), source, byteLength); + return buffer.release(); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(ArrayBufferContents& contents) +{ + return adoptRef(new ArrayBuffer(contents)); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::createAdopted(const void* data, unsigned byteLength) +{ + ArrayBufferContents contents(const_cast<void*>(data), byteLength); + return create(contents); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::createUninitialized(unsigned numElements, unsigned elementByteSize) +{ + return create(numElements, elementByteSize, ArrayBufferContents::DontInitialize); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy policy) +{ + ArrayBufferContents contents; + ArrayBufferContents::tryAllocate(numElements, elementByteSize, policy, contents); + if (!contents.m_data) + return 0; + return adoptRef(new ArrayBuffer(contents)); +} + +ArrayBuffer::ArrayBuffer(ArrayBufferContents& contents) + : m_pinCount(0) +{ + contents.transfer(m_contents); +} + +void* ArrayBuffer::data() +{ + return m_contents.m_data; +} + +const void* ArrayBuffer::data() const +{ + return m_contents.m_data; +} + +unsigned ArrayBuffer::byteLength() const +{ + return m_contents.m_sizeInBytes; +} + +size_t ArrayBuffer::gcSizeEstimateInBytes() const +{ + return sizeof(ArrayBuffer) + static_cast<size_t>(byteLength()); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::slice(int begin, int end) const +{ + return sliceImpl(clampIndex(begin), clampIndex(end)); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::slice(int begin) const +{ + return sliceImpl(clampIndex(begin), byteLength()); +} + +PassRefPtr<ArrayBuffer> ArrayBuffer::sliceImpl(unsigned begin, unsigned end) const +{ + unsigned size = begin <= end ? end - begin : 0; + return ArrayBuffer::create(static_cast<const char*>(data()) + begin, size); +} + +unsigned ArrayBuffer::clampIndex(int index) const +{ + unsigned currentLength = byteLength(); + if (index < 0) + index = currentLength + index; + return clampValue(index, 0, currentLength); +} + +void ArrayBuffer::pin() +{ + m_pinCount++; +} + +void ArrayBuffer::unpin() +{ + m_pinCount--; +} + +void ArrayBufferContents::tryAllocate(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy policy, ArrayBufferContents& result) +{ + // Do not allow 31-bit overflow of the total size. + if (numElements) { + unsigned totalSize = numElements * elementByteSize; + if (totalSize / numElements != elementByteSize + || totalSize > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) { + result.m_data = 0; + return; + } + } + bool allocationSucceeded = false; + if (policy == ZeroInitialize) + allocationSucceeded = WTF::tryFastCalloc(numElements, elementByteSize).getValue(result.m_data); + else { + ASSERT(policy == DontInitialize); + allocationSucceeded = WTF::tryFastMalloc(numElements * elementByteSize).getValue(result.m_data); + } + + if (allocationSucceeded) { + result.m_sizeInBytes = numElements * elementByteSize; + return; + } + result.m_data = 0; +} + +ArrayBufferContents::~ArrayBufferContents() +{ + WTF::fastFree(m_data); +} + +} // namespace JSC + +using JSC::ArrayBuffer; + +#endif // ArrayBuffer_h + diff --git a/Source/JavaScriptCore/runtime/ArrayBufferNeuteringWatchpoint.cpp b/Source/JavaScriptCore/runtime/ArrayBufferNeuteringWatchpoint.cpp new file mode 100644 index 000000000..a15b50440 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayBufferNeuteringWatchpoint.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ArrayBufferNeuteringWatchpoint.h" + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo ArrayBufferNeuteringWatchpoint::s_info = { + "ArrayBufferNeuteringWatchpoint", 0, 0, + CREATE_METHOD_TABLE(ArrayBufferNeuteringWatchpoint) +}; + +ArrayBufferNeuteringWatchpoint::ArrayBufferNeuteringWatchpoint(VM& vm) + : Base(vm, vm.arrayBufferNeuteringWatchpointStructure.get()) + , m_set(adoptRef(new WatchpointSet(IsWatched))) +{ +} + +void ArrayBufferNeuteringWatchpoint::destroy(JSCell* cell) +{ + static_cast<ArrayBufferNeuteringWatchpoint*>(cell)->ArrayBufferNeuteringWatchpoint::~ArrayBufferNeuteringWatchpoint(); +} + +ArrayBufferNeuteringWatchpoint* ArrayBufferNeuteringWatchpoint::create(VM& vm) +{ + ArrayBufferNeuteringWatchpoint* result = new + (NotNull, allocateCell<ArrayBufferNeuteringWatchpoint>(vm.heap)) + ArrayBufferNeuteringWatchpoint(vm); + result->finishCreation(vm); + return result; +} + +Structure* ArrayBufferNeuteringWatchpoint::createStructure(VM& vm) +{ + return Structure::create(vm, 0, jsNull(), TypeInfo(CellType, StructureFlags), info()); +} + +void ArrayBufferNeuteringWatchpoint::fireAll() +{ + set()->fireAll("Array buffer was neutered"); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/ArrayBufferNeuteringWatchpoint.h b/Source/JavaScriptCore/runtime/ArrayBufferNeuteringWatchpoint.h new file mode 100644 index 000000000..ab26f0332 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayBufferNeuteringWatchpoint.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ArrayBufferNeuteringWatchpoint_h +#define ArrayBufferNeuteringWatchpoint_h + +#include "JSCell.h" +#include "Watchpoint.h" + +namespace JSC { + +class ArrayBufferNeuteringWatchpoint final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + DECLARE_INFO; + + static ArrayBufferNeuteringWatchpoint* create(VM&); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + static Structure* createStructure(VM&); + + WatchpointSet* set() { return m_set.get(); } + + void fireAll(); + +private: + explicit ArrayBufferNeuteringWatchpoint(VM&); + + RefPtr<WatchpointSet> m_set; +}; + +} // namespace JSC + +#endif // ArrayBufferNeuteringWatchpoint_h diff --git a/Source/JavaScriptCore/runtime/ArrayBufferView.cpp b/Source/JavaScriptCore/runtime/ArrayBufferView.cpp new file mode 100644 index 000000000..f0fe06be4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayBufferView.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ArrayBufferView.h" + +#include "ArrayBuffer.h" + +namespace JSC { + +ArrayBufferView::ArrayBufferView( + PassRefPtr<ArrayBuffer> buffer, + unsigned byteOffset) + : m_byteOffset(byteOffset) + , m_isNeuterable(true) + , m_buffer(buffer) +{ + m_baseAddress = m_buffer ? (static_cast<char*>(m_buffer->data()) + m_byteOffset) : 0; +} + +ArrayBufferView::~ArrayBufferView() +{ + if (!m_isNeuterable) + m_buffer->unpin(); +} + +void ArrayBufferView::setNeuterable(bool flag) +{ + if (flag == m_isNeuterable) + return; + + m_isNeuterable = flag; + + if (!m_buffer) + return; + + if (flag) + m_buffer->unpin(); + else + m_buffer->pin(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayBufferView.h b/Source/JavaScriptCore/runtime/ArrayBufferView.h new file mode 100644 index 000000000..3fc10b0dd --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayBufferView.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2009, 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ArrayBufferView_h +#define ArrayBufferView_h + +#include "ArrayBuffer.h" +#include "TypedArrayType.h" +#include <algorithm> +#include <limits.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace JSC { + +class JSArrayBufferView; +class JSGlobalObject; +class ExecState; + +class ArrayBufferView : public RefCounted<ArrayBufferView> { +public: + virtual TypedArrayType getType() const = 0; + + bool isNeutered() const + { + return !m_buffer || m_buffer->isNeutered(); + } + + PassRefPtr<ArrayBuffer> buffer() const + { + if (isNeutered()) + return 0; + return m_buffer; + } + + void* baseAddress() const + { + if (isNeutered()) + return 0; + return m_baseAddress; + } + + unsigned byteOffset() const + { + if (isNeutered()) + return 0; + return m_byteOffset; + } + + virtual unsigned byteLength() const = 0; + + JS_EXPORT_PRIVATE void setNeuterable(bool flag); + bool isNeuterable() const { return m_isNeuterable; } + + JS_EXPORT_PRIVATE virtual ~ArrayBufferView(); + + // Helper to verify byte offset is size aligned. + static bool verifyByteOffsetAlignment(unsigned byteOffset, size_t size) + { + return !(byteOffset & (size - 1)); + } + + // Helper to verify that a given sub-range of an ArrayBuffer is + // within range. + static bool verifySubRangeLength(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned numElements, size_t size) + { + unsigned byteLength = buffer->byteLength(); + if (byteOffset > byteLength) + return false; + unsigned remainingElements = (byteLength - byteOffset) / size; + if (numElements > remainingElements) + return false; + return true; + } + + virtual JSArrayBufferView* wrap(ExecState*, JSGlobalObject*) = 0; + +protected: + JS_EXPORT_PRIVATE ArrayBufferView(PassRefPtr<ArrayBuffer>, unsigned byteOffset); + + inline bool setImpl(ArrayBufferView*, unsigned byteOffset); + + inline bool setRangeImpl(const char* data, size_t dataByteLength, unsigned byteOffset); + + inline bool zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength); + + static inline void calculateOffsetAndLength( + int start, int end, unsigned arraySize, + unsigned* offset, unsigned* length); + + // Input offset is in number of elements from this array's view; + // output offset is in number of bytes from the underlying buffer's view. + template <typename T> + static void clampOffsetAndNumElements( + PassRefPtr<ArrayBuffer> buffer, + unsigned arrayByteOffset, + unsigned *offset, + unsigned *numElements) + { + unsigned maxOffset = (UINT_MAX - arrayByteOffset) / sizeof(T); + if (*offset > maxOffset) { + *offset = buffer->byteLength(); + *numElements = 0; + return; + } + *offset = arrayByteOffset + *offset * sizeof(T); + *offset = std::min(buffer->byteLength(), *offset); + unsigned remainingElements = (buffer->byteLength() - *offset) / sizeof(T); + *numElements = std::min(remainingElements, *numElements); + } + + // This is the address of the ArrayBuffer's storage, plus the byte offset. + void* m_baseAddress; + + unsigned m_byteOffset : 31; + bool m_isNeuterable : 1; + +private: + friend class ArrayBuffer; + RefPtr<ArrayBuffer> m_buffer; +}; + +bool ArrayBufferView::setImpl(ArrayBufferView* array, unsigned byteOffset) +{ + if (byteOffset > byteLength() + || byteOffset + array->byteLength() > byteLength() + || byteOffset + array->byteLength() < byteOffset) { + // Out of range offset or overflow + return false; + } + + char* base = static_cast<char*>(baseAddress()); + memmove(base + byteOffset, array->baseAddress(), array->byteLength()); + return true; +} + +bool ArrayBufferView::setRangeImpl(const char* data, size_t dataByteLength, unsigned byteOffset) +{ + if (byteOffset > byteLength() + || byteOffset + dataByteLength > byteLength() + || byteOffset + dataByteLength < byteOffset) { + // Out of range offset or overflow + return false; + } + + char* base = static_cast<char*>(baseAddress()); + memmove(base + byteOffset, data, dataByteLength); + return true; +} + +bool ArrayBufferView::zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength) +{ + if (byteOffset > byteLength() + || byteOffset + rangeByteLength > byteLength() + || byteOffset + rangeByteLength < byteOffset) { + // Out of range offset or overflow + return false; + } + + char* base = static_cast<char*>(baseAddress()); + memset(base + byteOffset, 0, rangeByteLength); + return true; +} + +void ArrayBufferView::calculateOffsetAndLength( + int start, int end, unsigned arraySize, unsigned* offset, unsigned* length) +{ + if (start < 0) + start += arraySize; + if (start < 0) + start = 0; + if (end < 0) + end += arraySize; + if (end < 0) + end = 0; + if (static_cast<unsigned>(end) > arraySize) + end = arraySize; + if (end < start) + end = start; + *offset = static_cast<unsigned>(start); + *length = static_cast<unsigned>(end - start); +} + +} // namespace JSC + +using JSC::ArrayBufferView; + +#endif // ArrayBufferView_h diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp new file mode 100644 index 000000000..194f9217c --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2003 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "ArrayConstructor.h" + +#include "ArrayPrototype.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "Lookup.h" +#include "JSCInlines.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*); + +} + +#include "ArrayConstructor.lut.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ArrayConstructor); + +const ClassInfo ArrayConstructor::s_info = { "Function", &InternalFunction::s_info, &arrayConstructorTable, CREATE_METHOD_TABLE(ArrayConstructor) }; + +/* Source for ArrayConstructor.lut.h +@begin arrayConstructorTable + isArray arrayConstructorIsArray DontEnum|Function 1 + of arrayConstructorOf DontEnum|Function 0 + from arrayConstructorFrom DontEnum|Function 0 +@end +*/ + +ArrayConstructor::ArrayConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void ArrayConstructor::finishCreation(VM& vm, ArrayPrototype* arrayPrototype) +{ + Base::finishCreation(vm, arrayPrototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, arrayPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +bool ArrayConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, arrayConstructorTable, jsCast<ArrayConstructor*>(object), propertyName, slot); +} + +// ------------------------------ Functions --------------------------- + +JSObject* constructArrayWithSizeQuirk(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, JSValue length) +{ + if (!length.isNumber()) + return constructArrayNegativeIndexed(exec, profile, globalObject, &length, 1); + + uint32_t n = length.toUInt32(exec); + if (n != length.toNumber(exec)) + return exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Array size is not a small enough positive integer."))); + return constructEmptyArray(exec, profile, globalObject, n); +} + +static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgList& args) +{ + JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject(); + + // a single numeric argument denotes the array size (!) + if (args.size() == 1) + return constructArrayWithSizeQuirk(exec, 0, globalObject, args.at(0)); + + // otherwise the array is constructed with the arguments in it + return constructArray(exec, 0, globalObject, args); +} + +static EncodedJSValue JSC_HOST_CALL constructWithArrayConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructArrayWithSizeQuirk(exec, args)); +} + +ConstructType ArrayConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithArrayConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callArrayConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructArrayWithSizeQuirk(exec, args)); +} + +CallType ArrayConstructor::getCallData(JSCell*, CallData& callData) +{ + // equivalent to 'new Array(....)' + callData.native.function = callArrayConstructor; + return CallTypeHost; +} + +EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState* exec) +{ + return JSValue::encode(jsBoolean(exec->argument(0).inherits(JSArray::info()))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.h b/Source/JavaScriptCore/runtime/ArrayConstructor.h new file mode 100644 index 000000000..040f26c4c --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ArrayConstructor_h +#define ArrayConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class ArrayAllocationProfile; +class ArrayPrototype; +class JSArray; + +class ArrayConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; + + static ArrayConstructor* create(VM& vm, Structure* structure, ArrayPrototype* arrayPrototype) + { + ArrayConstructor* constructor = new (NotNull, allocateCell<ArrayConstructor>(vm.heap)) ArrayConstructor(vm, structure); + constructor->finishCreation(vm, arrayPrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, ArrayPrototype*); + +private: + ArrayConstructor(VM&, Structure*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +JSObject* constructArrayWithSizeQuirk(ExecState*, ArrayAllocationProfile*, JSGlobalObject*, JSValue); + +} // namespace JSC + +#endif // ArrayConstructor_h diff --git a/Source/JavaScriptCore/runtime/ArrayConventions.h b/Source/JavaScriptCore/runtime/ArrayConventions.h new file mode 100644 index 000000000..9c62ea9b8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayConventions.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ArrayConventions_h +#define ArrayConventions_h + +#include "IndexingHeader.h" +#include "WriteBarrier.h" + +namespace JSC { + +// Overview of JSArray +// +// Properties of JSArray objects may be stored in one of three locations: +// * The regular JSObject property map. +// * A storage vector. +// * A sparse map of array entries. +// +// Properties with non-numeric identifiers, with identifiers that are not representable +// as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX +// (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit +// integer) are not considered array indices and will be stored in the JSObject property map. +// +// All properties with a numeric identifier, representable as an unsigned integer i, +// where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the +// storage vector or the sparse map. An array index i will be handled in the following +// fashion: +// +// * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector, +// unless the array is in SparseMode in which case all properties go into the map. +// * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either +// be stored in the storage vector or in the sparse array, depending on the density of +// data that would be stored in the vector (a vector being used where at least +// (1 / minDensityMultiplier) of the entries would be populated). +// * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored +// in the sparse array. + +// Define the maximum storage vector length to be 2^32 / sizeof(JSValue) / 2 to ensure that +// there is no risk of overflow. +#define MAX_STORAGE_VECTOR_LENGTH (static_cast<unsigned>(IndexingHeader::maximumLength)) + +// These values have to be macros to be used in max() and min() without introducing +// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. + +// If you grow an ArrayStorage array by more than this, then the array will go sparse. Note that we +// could probably make this smaller (it's large because it used to be conflated with +// MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH). +#define MIN_SPARSE_ARRAY_INDEX 100000U +// If you try to allocate a contiguous array larger than this, then we will allocate an ArrayStorage +// array instead. We allow for an array that occupies 1GB of VM. +#define MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH 1024 * 1024 * 1024 / 8 +#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1) +// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer. +#define MAX_ARRAY_INDEX 0xFFFFFFFEU + +// The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate +// for an array that was created with a sepcified length (e.g. a = new Array(123)) +#define BASE_VECTOR_LEN 4U + +// The upper bound to the size we'll grow a zero length array when the first element +// is added. +#define FIRST_VECTOR_GROW 4U + +#define MIN_BEYOND_LENGTH_SPARSE_INDEX 1000 + +// Our policy for when to use a vector and when to use a sparse map. +// For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector. +// When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector +// as long as it is 1/8 full. If more sparse than that, we use a map. +static const unsigned minDensityMultiplier = 8; + +inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) +{ + return length / minDensityMultiplier <= numValues; +} + +inline bool indexIsSufficientlyBeyondLengthForSparseMap(unsigned i, unsigned length) +{ + return i >= MIN_BEYOND_LENGTH_SPARSE_INDEX && i > length; +} + +inline IndexingHeader indexingHeaderForArray(unsigned length, unsigned vectorLength) +{ + IndexingHeader result; + result.setPublicLength(length); + result.setVectorLength(vectorLength); + return result; +} + +inline IndexingHeader baseIndexingHeaderForArray(unsigned length) +{ + return indexingHeaderForArray(length, BASE_VECTOR_LEN); +} + +} // namespace JSC + +#endif // ArrayConventions_h + diff --git a/Source/JavaScriptCore/runtime/ArrayIteratorPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayIteratorPrototype.cpp new file mode 100644 index 000000000..c33091259 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayIteratorPrototype.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ArrayIteratorPrototype.h" + +namespace JSC { + +} + +#include "ArrayIteratorPrototype.lut.h" + +#include "IteratorOperations.h" +#include "JSArrayIterator.h" +#include "JSCInlines.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "ObjectConstructor.h" +#include "StructureInlines.h" + +namespace JSC { + + +const ClassInfo ArrayIteratorPrototype::s_info = { "Array Iterator", &Base::s_info, &arrayIteratorPrototypeTable, CREATE_METHOD_TABLE(ArrayIteratorPrototype) }; + +/* Source for ArrayIteratorPrototype.lut.h +@begin arrayIteratorPrototypeTable + next arrayIteratorProtoFuncNext DontEnum|Function 0 +@end +*/ + +void ArrayIteratorPrototype::finishCreation(VM& vm, JSGlobalObject*) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); +} + +bool ArrayIteratorPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<Base>(exec, arrayIteratorPrototypeTable, jsCast<ArrayIteratorPrototype*>(object), propertyName, slot); +} + +// ------------------------------ Array Functions ---------------------------- + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayIteratorPrototype.h b/Source/JavaScriptCore/runtime/ArrayIteratorPrototype.h new file mode 100644 index 000000000..2b234f8ee --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayIteratorPrototype.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ArrayIteratorPrototype_h +#define ArrayIteratorPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class ArrayIteratorPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | Base::StructureFlags; + + static ArrayIteratorPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + ArrayIteratorPrototype* prototype = new (NotNull, allocateCell<ArrayIteratorPrototype>(vm.heap)) ArrayIteratorPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + ArrayIteratorPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(VM&, JSGlobalObject*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} + +#endif // !defined(ArrayIteratorPrototype_h) diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp new file mode 100644 index 000000000..3cc351b31 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -0,0 +1,918 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2009, 2011, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2003 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "ArrayPrototype.h" + +#include "ButterflyInlines.h" +#include "CachedCall.h" +#include "CodeBlock.h" +#include "CopiedSpaceInlines.h" +#include "Error.h" +#include "Interpreter.h" +#include "JIT.h" +#include "JSArrayIterator.h" +#include "JSCBuiltins.h" +#include "JSCInlines.h" +#include "JSStringBuilder.h" +#include "JSStringJoiner.h" +#include "Lookup.h" +#include "ObjectConstructor.h" +#include "ObjectPrototype.h" +#include "StringRecursionChecker.h" +#include <algorithm> +#include <wtf/Assertions.h> +#include <wtf/HashSet.h> + +namespace JSC { + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncKeys(ExecState*); +EncodedJSValue JSC_HOST_CALL arrayProtoFuncEntries(ExecState*); + +// ------------------------------ ArrayPrototype ---------------------------- + +const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, nullptr, CREATE_METHOD_TABLE(ArrayPrototype)}; + +ArrayPrototype* ArrayPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(vm.heap)) ArrayPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; +} + +// ECMA 15.4.4 +ArrayPrototype::ArrayPrototype(VM& vm, Structure* structure) + : JSArray(vm, structure, 0) +{ +} + +void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + putDirectWithoutTransition(vm, vm.propertyNames->values, globalObject->arrayProtoValuesFunction(), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), DontEnum); + + JSC_NATIVE_FUNCTION(vm.propertyNames->toString, arrayProtoFuncToString, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, DontEnum, 0); + JSC_NATIVE_FUNCTION("concat", arrayProtoFuncConcat, DontEnum, 1); + JSC_BUILTIN_FUNCTION("fill", arrayPrototypeFillCodeGenerator, DontEnum); + JSC_NATIVE_FUNCTION(vm.propertyNames->join, arrayProtoFuncJoin, DontEnum, 1); + JSC_NATIVE_INTRINSIC_FUNCTION("pop", arrayProtoFuncPop, DontEnum, 0, ArrayPopIntrinsic); + JSC_NATIVE_INTRINSIC_FUNCTION("push", arrayProtoFuncPush, DontEnum, 1, ArrayPushIntrinsic); + JSC_NATIVE_FUNCTION("reverse", arrayProtoFuncReverse, DontEnum, 0); + JSC_NATIVE_FUNCTION("shift", arrayProtoFuncShift, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2); + JSC_BUILTIN_FUNCTION("sort", arrayPrototypeSortCodeGenerator, DontEnum); + JSC_NATIVE_FUNCTION("splice", arrayProtoFuncSplice, DontEnum, 2); + JSC_NATIVE_FUNCTION("unshift", arrayProtoFuncUnShift, DontEnum, 1); + JSC_BUILTIN_FUNCTION("every", arrayPrototypeEveryCodeGenerator, DontEnum); + JSC_BUILTIN_FUNCTION("forEach", arrayPrototypeForEachCodeGenerator, DontEnum); + JSC_BUILTIN_FUNCTION("some", arrayPrototypeSomeCodeGenerator, DontEnum); + JSC_NATIVE_FUNCTION("indexOf", arrayProtoFuncIndexOf, DontEnum, 1); + JSC_NATIVE_FUNCTION("lastIndexOf", arrayProtoFuncLastIndexOf, DontEnum, 1); + JSC_BUILTIN_FUNCTION("filter", arrayPrototypeFilterCodeGenerator, DontEnum); + JSC_BUILTIN_FUNCTION("reduce", arrayPrototypeReduceCodeGenerator, DontEnum); + JSC_BUILTIN_FUNCTION("reduceRight", arrayPrototypeReduceRightCodeGenerator, DontEnum); + JSC_BUILTIN_FUNCTION("map", arrayPrototypeMapCodeGenerator, DontEnum); + JSC_NATIVE_FUNCTION(vm.propertyNames->entries, arrayProtoFuncEntries, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->keys, arrayProtoFuncKeys, DontEnum, 0); + JSC_BUILTIN_FUNCTION("find", arrayPrototypeFindCodeGenerator, DontEnum); + JSC_BUILTIN_FUNCTION("findIndex", arrayPrototypeFindIndexCodeGenerator, DontEnum); + JSC_BUILTIN_FUNCTION("includes", arrayPrototypeIncludesCodeGenerator, DontEnum); + JSC_BUILTIN_FUNCTION("copyWithin", arrayPrototypeCopyWithinCodeGenerator, DontEnum); + + JSObject* unscopables = constructEmptyObject(globalObject->globalExec(), globalObject->nullPrototypeObjectStructure()); + const char* unscopableNames[] = { + "copyWithin", + "entries", + "fill", + "find", + "findIndex", + "keys", + "values" + }; + for (const char* unscopableName : unscopableNames) + unscopables->putDirect(vm, Identifier::fromString(&vm, unscopableName), jsBoolean(true)); + putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, unscopables, DontEnum | ReadOnly); +} + +// ------------------------------ Array Functions ---------------------------- + +static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index) +{ + if (JSValue result = object->tryGetIndexQuickly(index)) + return result; + PropertySlot slot(object); + if (!object->getPropertySlot(exec, index, slot)) + return JSValue(); + return slot.getValue(exec, index); +} + +static ALWAYS_INLINE unsigned getLength(ExecState* exec, JSObject* obj) +{ + if (isJSArray(obj)) + return jsCast<JSArray*>(obj)->length(); + return obj->get(exec, exec->propertyNames().length).toUInt32(exec); +} + +static void putLength(ExecState* exec, JSObject* obj, JSValue value) +{ + PutPropertySlot slot(obj); + obj->methodTable()->put(obj, exec, exec->propertyNames().length, value, slot); +} + +static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0) +{ + JSValue value = exec->argument(argument); + if (value.isUndefined()) + return undefinedValue; + + double indexDouble = value.toInteger(exec); + if (indexDouble < 0) { + indexDouble += length; + return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble); + } + return indexDouble > length ? length : static_cast<unsigned>(indexDouble); +} + +// The shift/unshift function implement the shift/unshift behaviour required +// by the corresponding array prototype methods, and by splice. In both cases, +// the methods are operating an an array or array like object. +// +// header currentCount (remainder) +// [------][------------][-----------] +// header resultCount (remainder) +// [------][-----------][-----------] +// +// The set of properties in the range 'header' must be unchanged. The set of +// properties in the range 'remainder' (where remainder = length - header - +// currentCount) will be shifted to the left or right as appropriate; in the +// case of shift this must be removing values, in the case of unshift this +// must be introducing new values. + +template<JSArray::ShiftCountMode shiftCountMode> +void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length) +{ + RELEASE_ASSERT(currentCount > resultCount); + unsigned count = currentCount - resultCount; + + RELEASE_ASSERT(header <= length); + RELEASE_ASSERT(currentCount <= (length - header)); + + if (isJSArray(thisObj)) { + JSArray* array = asArray(thisObj); + if (array->length() == length && array->shiftCount<shiftCountMode>(exec, header, count)) + return; + } + + for (unsigned k = header; k < length - currentCount; ++k) { + unsigned from = k + currentCount; + unsigned to = k + resultCount; + if (JSValue value = getProperty(exec, thisObj, from)) { + if (exec->hadException()) + return; + thisObj->putByIndexInline(exec, to, value, true); + if (exec->hadException()) + return; + } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) { + throwTypeError(exec, ASCIILiteral("Unable to delete property.")); + return; + } + } + for (unsigned k = length; k > length - count; --k) { + if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, k - 1)) { + throwTypeError(exec, ASCIILiteral("Unable to delete property.")); + return; + } + } +} + +template<JSArray::ShiftCountMode shiftCountMode> +void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length) +{ + RELEASE_ASSERT(resultCount > currentCount); + unsigned count = resultCount - currentCount; + + RELEASE_ASSERT(header <= length); + RELEASE_ASSERT(currentCount <= (length - header)); + + // Guard against overflow. + if (count > (UINT_MAX - length)) { + throwOutOfMemoryError(exec); + return; + } + + if (isJSArray(thisObj)) { + JSArray* array = asArray(thisObj); + if (array->length() == length && array->unshiftCount<shiftCountMode>(exec, header, count)) + return; + } + + for (unsigned k = length - currentCount; k > header; --k) { + unsigned from = k + currentCount - 1; + unsigned to = k + resultCount - 1; + if (JSValue value = getProperty(exec, thisObj, from)) { + if (exec->hadException()) + return; + thisObj->putByIndexInline(exec, to, value, true); + } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) { + throwTypeError(exec, ASCIILiteral("Unable to delete property.")); + return; + } + if (exec->hadException()) + return; + } +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + + // 1. Let array be the result of calling ToObject on the this value. + JSObject* thisObject = thisValue.toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join". + JSValue function = JSValue(thisObject).get(exec, exec->propertyNames().join); + + // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2). + if (!function.isCell()) + return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(exec->vm())->className(thisObject), "]")); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(exec->vm())->className(thisObject), "]")); + + // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list. + if (!isJSArray(thisObject) || callType != CallTypeHost || callData.native.function != arrayProtoFuncJoin) + return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList())); + + ASSERT(isJSArray(thisValue)); + JSArray* thisArray = asArray(thisValue); + + unsigned length = thisArray->length(); + + StringRecursionChecker checker(exec, thisArray); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + JSStringJoiner joiner(*exec, ',', length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + for (unsigned i = 0; i < length; ++i) { + JSValue element = thisArray->tryGetIndexQuickly(i); + if (!element) { + element = thisArray->get(exec, i); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + joiner.append(*exec, element); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(joiner.join(*exec)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + + JSObject* thisObject = thisValue.toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + unsigned length = getLength(exec, thisObject); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + StringRecursionChecker checker(exec, thisObject); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + JSStringJoiner stringJoiner(*exec, ',', length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + for (unsigned i = 0; i < length; ++i) { + JSValue element = thisObject->getIndex(exec, i); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (element.isUndefinedOrNull()) + continue; + JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + CallData callData; + CallType callType = getCallData(conversionFunction, callData); + if (callType != CallTypeNone) { + element = call(exec, conversionFunction, callType, callData, element, exec->emptyList()); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + stringJoiner.append(*exec, element); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(stringJoiner.join(*exec)); +} + +static inline bool isHole(double value) +{ + return std::isnan(value); +} + +static inline bool isHole(const WriteBarrier<Unknown>& value) +{ + return !value; +} + +template<typename T> static inline bool containsHole(T* data, unsigned length) +{ + for (unsigned i = 0; i < length; ++i) { + if (isHole(data[i])) + return true; + } + return false; +} + +static inline bool holesMustForwardToPrototype(ExecState& state, JSObject* object) +{ + auto& vm = state.vm(); + return object->structure(vm)->holesMustForwardToPrototype(vm); +} + +static inline JSValue join(ExecState& state, JSObject* thisObject, StringView separator) +{ + unsigned length = getLength(&state, thisObject); + if (state.hadException()) + return jsUndefined(); + + switch (thisObject->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: { + auto& butterfly = *thisObject->butterfly(); + if (length > butterfly.publicLength()) + break; + JSStringJoiner joiner(state, separator, length); + if (state.hadException()) + return jsUndefined(); + auto data = butterfly.contiguous().data(); + bool holesKnownToBeOK = false; + for (unsigned i = 0; i < length; ++i) { + if (JSValue value = data[i].get()) { + joiner.append(state, value); + if (state.hadException()) + return jsUndefined(); + } else { + if (!holesKnownToBeOK) { + if (holesMustForwardToPrototype(state, thisObject)) + goto generalCase; + holesKnownToBeOK = true; + } + joiner.appendEmptyString(); + } + } + return joiner.join(state); + } + case ALL_DOUBLE_INDEXING_TYPES: { + auto& butterfly = *thisObject->butterfly(); + if (length > butterfly.publicLength()) + break; + JSStringJoiner joiner(state, separator, length); + if (state.hadException()) + return jsUndefined(); + auto data = butterfly.contiguousDouble().data(); + bool holesKnownToBeOK = false; + for (unsigned i = 0; i < length; ++i) { + double value = data[i]; + if (!isHole(value)) + joiner.append(state, jsDoubleNumber(value)); + else { + if (!holesKnownToBeOK) { + if (thisObject->structure(state.vm())->holesMustForwardToPrototype(state.vm())) + goto generalCase; + holesKnownToBeOK = true; + } + joiner.appendEmptyString(); + } + } + return joiner.join(state); + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + auto& storage = *thisObject->butterfly()->arrayStorage(); + if (length > storage.vectorLength()) + break; + if (storage.hasHoles() && thisObject->structure(state.vm())->holesMustForwardToPrototype(state.vm())) + break; + JSStringJoiner joiner(state, separator, length); + if (state.hadException()) + return jsUndefined(); + auto data = storage.vector().data(); + for (unsigned i = 0; i < length; ++i) { + if (JSValue value = data[i].get()) { + joiner.append(state, value); + if (state.hadException()) + return jsUndefined(); + } else + joiner.appendEmptyString(); + } + return joiner.join(state); + } + } + +generalCase: + JSStringJoiner joiner(state, separator, length); + if (state.hadException()) + return jsUndefined(); + for (unsigned i = 0; i < length; ++i) { + JSValue element = thisObject->getIndex(&state, i); + if (state.hadException()) + return jsUndefined(); + joiner.append(state, element); + if (state.hadException()) + return jsUndefined(); + } + return joiner.join(state); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) +{ + JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + + StringRecursionChecker checker(exec, thisObject); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + JSValue separatorValue = exec->argument(0); + if (separatorValue.isUndefined()) { + const LChar comma = ','; + return JSValue::encode(join(*exec, thisObject, { &comma, 1 })); + } + + JSString* separator = separatorValue.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + return JSValue::encode(join(*exec, thisObject, separator->view(exec))); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) +{ + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + unsigned argCount = exec->argumentCount(); + JSValue curArg = thisValue.toObject(exec); + Checked<unsigned, RecordOverflow> finalArraySize = 0; + + JSArray* currentArray = nullptr; + JSArray* previousArray = nullptr; + for (unsigned i = 0; ; ++i) { + previousArray = currentArray; + currentArray = jsDynamicCast<JSArray*>(curArg); + if (currentArray) { + // Can't use JSArray::length here because this might be a RuntimeArray! + finalArraySize += getLength(exec, currentArray); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else + ++finalArraySize; + if (i == argCount) + break; + curArg = exec->uncheckedArgument(i); + } + + if (finalArraySize.hasOverflowed()) + return JSValue::encode(throwOutOfMemoryError(exec)); + + if (argCount == 1 && previousArray && currentArray && finalArraySize.unsafeGet() < MIN_SPARSE_ARRAY_INDEX) { + IndexingType type = JSArray::fastConcatType(exec->vm(), *previousArray, *currentArray); + if (type != NonArray) + return previousArray->fastConcatWith(*exec, *currentArray); + } + + JSArray* arr = constructEmptyArray(exec, nullptr, finalArraySize.unsafeGet()); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + curArg = thisValue.toObject(exec); + unsigned n = 0; + for (unsigned i = 0; ; ++i) { + if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) { + // Can't use JSArray::length here because this might be a RuntimeArray! + unsigned length = getLength(exec, currentArray); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + for (unsigned k = 0; k < length; ++k) { + JSValue v = getProperty(exec, currentArray, k); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (v) + arr->putDirectIndex(exec, n, v); + n++; + } + } else { + arr->putDirectIndex(exec, n, curArg); + n++; + } + if (i == argCount) + break; + curArg = exec->uncheckedArgument(i); + } + arr->setLength(exec, n); + return JSValue::encode(arr); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec) +{ + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + + if (isJSArray(thisValue)) + return JSValue::encode(asArray(thisValue)->pop(exec)); + + JSObject* thisObj = thisValue.toObject(exec); + unsigned length = getLength(exec, thisObj); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue result; + if (length == 0) { + putLength(exec, thisObj, jsNumber(length)); + result = jsUndefined(); + } else { + result = thisObj->get(exec, length - 1); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, length - 1)) { + throwTypeError(exec, ASCIILiteral("Unable to delete property.")); + return JSValue::encode(jsUndefined()); + } + putLength(exec, thisObj, jsNumber(length - 1)); + } + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) +{ + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + + if (isJSArray(thisValue) && exec->argumentCount() == 1) { + JSArray* array = asArray(thisValue); + array->push(exec, exec->uncheckedArgument(0)); + return JSValue::encode(jsNumber(array->length())); + } + + JSObject* thisObj = thisValue.toObject(exec); + unsigned length = getLength(exec, thisObj); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + for (unsigned n = 0; n < exec->argumentCount(); n++) { + // Check for integer overflow; where safe we can do a fast put by index. + if (length + n >= length) + thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->uncheckedArgument(n), true); + else { + PutPropertySlot slot(thisObj); + Identifier propertyName = Identifier::fromString(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec)); + thisObj->methodTable()->put(thisObj, exec, propertyName, exec->uncheckedArgument(n), slot); + } + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount())); + putLength(exec, thisObj, newLength); + return JSValue::encode(newLength); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec) +{ + JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + + unsigned length = getLength(exec, thisObject); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + switch (thisObject->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: { + auto& butterfly = *thisObject->butterfly(); + if (length > butterfly.publicLength()) + break; + auto data = butterfly.contiguous().data(); + if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject)) + break; + std::reverse(data, data + length); + return JSValue::encode(thisObject); + } + case ALL_DOUBLE_INDEXING_TYPES: { + auto& butterfly = *thisObject->butterfly(); + if (length > butterfly.publicLength()) + break; + auto data = butterfly.contiguousDouble().data(); + if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject)) + break; + std::reverse(data, data + length); + return JSValue::encode(thisObject); + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + auto& storage = *thisObject->butterfly()->arrayStorage(); + if (length > storage.vectorLength()) + break; + if (storage.hasHoles() && holesMustForwardToPrototype(*exec, thisObject)) + break; + auto data = storage.vector().data(); + std::reverse(data, data + length); + return JSValue::encode(thisObject); + } + } + + unsigned middle = length / 2; + for (unsigned k = 0; k < middle; k++) { + unsigned lk1 = length - k - 1; + JSValue obj2 = getProperty(exec, thisObject, lk1); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + JSValue obj = getProperty(exec, thisObject, k); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (obj2) { + thisObject->putByIndexInline(exec, k, obj2, true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else if (!thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, k)) { + throwTypeError(exec, ASCIILiteral("Unable to delete property.")); + return JSValue::encode(jsUndefined()); + } + + if (obj) { + thisObject->putByIndexInline(exec, lk1, obj, true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else if (!thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, lk1)) { + throwTypeError(exec, ASCIILiteral("Unable to delete property.")); + return JSValue::encode(jsUndefined()); + } + } + return JSValue::encode(thisObject); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec) +{ + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + unsigned length = getLength(exec, thisObj); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue result; + if (length == 0) { + putLength(exec, thisObj, jsNumber(length)); + result = jsUndefined(); + } else { + result = thisObj->getIndex(exec, 0); + shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + putLength(exec, thisObj, jsNumber(length - 1)); + } + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) +{ + // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + unsigned length = getLength(exec, thisObj); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); + unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length); + + if (isJSArray(thisObj)) { + if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin)) + return JSValue::encode(result); + } + + JSArray* result = constructEmptyArray(exec, nullptr, end - begin); + + unsigned n = 0; + for (unsigned k = begin; k < end; k++, n++) { + JSValue v = getProperty(exec, thisObj, k); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (v) + result->putDirectIndex(exec, n, v); + } + result->setLength(exec, n); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) +{ + // 15.4.4.12 + + VM& vm = exec->vm(); + + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + unsigned length = getLength(exec, thisObj); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (!exec->argumentCount()) + return JSValue::encode(constructEmptyArray(exec, nullptr)); + + unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); + + unsigned deleteCount = length - begin; + if (exec->argumentCount() > 1) { + double deleteDouble = exec->uncheckedArgument(1).toInteger(exec); + if (deleteDouble < 0) + deleteCount = 0; + else if (deleteDouble > length - begin) + deleteCount = length - begin; + else + deleteCount = static_cast<unsigned>(deleteDouble); + } + + JSArray* result = nullptr; + + if (isJSArray(thisObj)) + result = asArray(thisObj)->fastSlice(*exec, begin, deleteCount); + + if (!result) { + result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount); + if (!result) + return JSValue::encode(throwOutOfMemoryError(exec)); + + for (unsigned k = 0; k < deleteCount; ++k) { + JSValue v = getProperty(exec, thisObj, k + begin); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + result->initializeIndex(vm, k, v); + } + } + + unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0); + if (additionalArgs < deleteCount) { + shift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else if (additionalArgs > deleteCount) { + unshift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + for (unsigned k = 0; k < additionalArgs; ++k) { + thisObj->putByIndexInline(exec, k + begin, exec->uncheckedArgument(k + 2), true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + putLength(exec, thisObj, jsNumber(length - deleteCount + additionalArgs)); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec) +{ + // 15.4.4.13 + + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + unsigned length = getLength(exec, thisObj); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + unsigned nrArgs = exec->argumentCount(); + if (nrArgs) { + unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + for (unsigned k = 0; k < nrArgs; ++k) { + thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + JSValue result = jsNumber(length + nrArgs); + putLength(exec, thisObj, result); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec) +{ + // 15.4.4.14 + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + unsigned length = getLength(exec, thisObj); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length); + JSValue searchElement = exec->argument(0); + for (; index < length; ++index) { + JSValue e = getProperty(exec, thisObj, index); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (!e) + continue; + if (JSValue::strictEqual(exec, searchElement, e)) + return JSValue::encode(jsNumber(index)); + } + + return JSValue::encode(jsNumber(-1)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec) +{ + // 15.4.4.15 + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + unsigned length = getLength(exec, thisObj); + if (!length) + return JSValue::encode(jsNumber(-1)); + + unsigned index = length - 1; + if (exec->argumentCount() >= 2) { + JSValue fromValue = exec->uncheckedArgument(1); + double fromDouble = fromValue.toInteger(exec); + if (fromDouble < 0) { + fromDouble += length; + if (fromDouble < 0) + return JSValue::encode(jsNumber(-1)); + } + if (fromDouble < length) + index = static_cast<unsigned>(fromDouble); + } + + JSValue searchElement = exec->argument(0); + do { + RELEASE_ASSERT(index < length); + JSValue e = getProperty(exec, thisObj, index); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (!e) + continue; + if (JSValue::strictEqual(exec, searchElement, e)) + return JSValue::encode(jsNumber(index)); + } while (index--); + + return JSValue::encode(jsNumber(-1)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncValues(ExecState* exec) +{ + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + return JSValue::encode(JSArrayIterator::create(exec, exec->callee()->globalObject()->arrayIteratorStructure(), ArrayIterateValue, thisObj)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncEntries(ExecState* exec) +{ + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + return JSValue::encode(JSArrayIterator::create(exec, exec->callee()->globalObject()->arrayIteratorStructure(), ArrayIterateKeyValue, thisObj)); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncKeys(ExecState* exec) +{ + JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + return JSValue::encode(JSArrayIterator::create(exec, exec->callee()->globalObject()->arrayIteratorStructure(), ArrayIterateKey, thisObj)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.h b/Source/JavaScriptCore/runtime/ArrayPrototype.h new file mode 100644 index 000000000..39412c220 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2011, 2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ArrayPrototype_h +#define ArrayPrototype_h + +#include "JSArray.h" +#include "Lookup.h" + +namespace JSC { + +class ArrayPrototype : public JSArray { +private: + ArrayPrototype(VM&, Structure*); + +public: + typedef JSArray Base; + + static ArrayPrototype* create(VM&, JSGlobalObject*, Structure*); + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass); + } + +protected: + void finishCreation(VM&, JSGlobalObject*); +}; + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncValues(ExecState*); + +} // namespace JSC + +#endif // ArrayPrototype_h diff --git a/Source/JavaScriptCore/runtime/ArrayStorage.h b/Source/JavaScriptCore/runtime/ArrayStorage.h new file mode 100644 index 000000000..c93dc3bfd --- /dev/null +++ b/Source/JavaScriptCore/runtime/ArrayStorage.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ArrayStorage_h +#define ArrayStorage_h + +#include "ArrayConventions.h" +#include "Butterfly.h" +#include "IndexingHeader.h" +#include "SparseArrayValueMap.h" +#include "WriteBarrier.h" +#include <wtf/Noncopyable.h> + +namespace JSC { + +// This struct holds the actual data values of an array. A JSArray object points to its contained ArrayStorage +// struct by pointing to m_vector. To access the contained ArrayStorage struct, use the getStorage() and +// setStorage() methods. It is important to note that there may be space before the ArrayStorage that +// is used to quick unshift / shift operation. The actual allocated pointer is available by using: +// getStorage() - m_indexBias * sizeof(JSValue) +// All slots in ArrayStorage (slots from 0 to vectorLength) are expected to be initialized to a JSValue or, +// for hole slots, JSValue(). +struct ArrayStorage { + WTF_MAKE_NONCOPYABLE(ArrayStorage); +private: + ArrayStorage() { } // Not directly instantiable. Can only be created as part of a Butterfly. +public: + + static ArrayStorage* from(Butterfly* butterfly) { return reinterpret_cast_ptr<ArrayStorage*>(butterfly); } + static ArrayStorage* from(IndexingHeader* indexingHeader) { return indexingHeader->arrayStorage(); } + + Butterfly* butterfly() { return reinterpret_cast<Butterfly*>(this); } + IndexingHeader* indexingHeader() { return IndexingHeader::from(this); } + const IndexingHeader* indexingHeader() const { return IndexingHeader::from(this); } + + // We steal two fields from the indexing header: vectorLength and length. + unsigned length() const { return indexingHeader()->publicLength(); } + void setLength(unsigned length) { indexingHeader()->setPublicLength(length); } + unsigned vectorLength() { return indexingHeader()->vectorLength(); } + void setVectorLength(unsigned length) { indexingHeader()->setVectorLength(length); } + + ALWAYS_INLINE void copyHeaderFromDuringGC(const ArrayStorage& other) + { + m_sparseMap.copyFrom(other.m_sparseMap); + m_indexBias = other.m_indexBias; + m_numValuesInVector = other.m_numValuesInVector; + } + + bool hasHoles() const + { + return m_numValuesInVector != length(); + } + + bool inSparseMode() + { + return m_sparseMap && m_sparseMap->sparseMode(); + } + + ContiguousJSValues vector() { return ContiguousJSValues(m_vector, vectorLength()); } + + WriteBarrier<SparseArrayValueMap> m_sparseMap; + unsigned m_indexBias; + unsigned m_numValuesInVector; +#if USE(JSVALUE32_64) + uintptr_t m_padding; +#endif + WriteBarrier<Unknown> m_vector[1]; + + static ptrdiff_t lengthOffset() { return Butterfly::offsetOfPublicLength(); } + static ptrdiff_t vectorLengthOffset() { return Butterfly::offsetOfVectorLength(); } + static ptrdiff_t numValuesInVectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector); } + static ptrdiff_t vectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_vector); } + static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_indexBias); } + static ptrdiff_t sparseMapOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_sparseMap); } + + static size_t sizeFor(unsigned vectorLength) + { + return ArrayStorage::vectorOffset() + vectorLength * sizeof(WriteBarrier<Unknown>); + } +}; + +} // namespace JSC + +#endif // ArrayStorage_h + diff --git a/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp b/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp new file mode 100644 index 000000000..33cc39611 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * Copyright (C) 2014 Saam Barati. <saambarati1@gmail.com> + * + * 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "BasicBlockLocation.h" + +#include "CCallHelpers.h" +#include <climits> +#include <wtf/DataLog.h> + +namespace JSC { + +BasicBlockLocation::BasicBlockLocation(int startOffset, int endOffset) + : m_startOffset(startOffset) + , m_endOffset(endOffset) + , m_hasExecuted(false) +{ +} + +void BasicBlockLocation::insertGap(int startOffset, int endOffset) +{ + std::pair<int, int> gap(startOffset, endOffset); + if (!m_gaps.contains(gap)) + m_gaps.append(gap); +} + +Vector<std::pair<int, int>> BasicBlockLocation::getExecutedRanges() const +{ + Vector<Gap> result; + Vector<Gap> gaps = m_gaps; + int nextRangeStart = m_startOffset; + while (gaps.size()) { + Gap minGap(INT_MAX, 0); + unsigned minIdx = std::numeric_limits<unsigned>::max(); + for (unsigned idx = 0; idx < gaps.size(); idx++) { + // Because we know that the Gaps inside m_gaps aren't enclosed within one another, it suffices to just check the first element to test ordering. + if (gaps[idx].first < minGap.first) { + minGap = gaps[idx]; + minIdx = idx; + } + } + result.append(Gap(nextRangeStart, minGap.first - 1)); + nextRangeStart = minGap.second + 1; + gaps.remove(minIdx); + } + + result.append(Gap(nextRangeStart, m_endOffset)); + return result; +} + +void BasicBlockLocation::dumpData() const +{ + Vector<Gap> executedRanges = getExecutedRanges(); + for (Gap gap : executedRanges) + dataLogF("\tBasicBlock: [%d, %d] hasExecuted: %s\n", gap.first, gap.second, hasExecuted() ? "true" : "false"); +} + +#if ENABLE(JIT) +void BasicBlockLocation::emitExecuteCode(CCallHelpers& jit, MacroAssembler::RegisterID ptrReg) const +{ + jit.move(CCallHelpers::TrustedImmPtr(&m_hasExecuted), ptrReg); + jit.store8(CCallHelpers::TrustedImm32(true), CCallHelpers::Address(ptrReg, 0)); +} +#endif // ENABLE(JIT) + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BasicBlockLocation.h b/Source/JavaScriptCore/runtime/BasicBlockLocation.h new file mode 100644 index 000000000..49bf51474 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BasicBlockLocation.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * Copyright (C) 2014 Saam Barati. <saambarati1@gmail.com> + * + * 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef BasicBlockLocation_h +#define BasicBlockLocation_h + +#include "MacroAssembler.h" +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace JSC { + +class CCallHelpers; +class LLIntOffsetsExtractor; + +class BasicBlockLocation { +public: + typedef std::pair<int, int> Gap; + + BasicBlockLocation(int startOffset = -1, int endOffset = -1); + + int startOffset() const { return m_startOffset; } + int endOffset() const { return m_endOffset; } + void setStartOffset(int startOffset) { m_startOffset = startOffset; } + void setEndOffset(int endOffset) { m_endOffset = endOffset; } + bool hasExecuted() const { return m_hasExecuted; } + void insertGap(int, int); + Vector<Gap> getExecutedRanges() const; + JS_EXPORT_PRIVATE void dumpData() const; +#if ENABLE(JIT) + void emitExecuteCode(CCallHelpers&, MacroAssembler::RegisterID) const; +#endif + +private: + friend class LLIntOffsetsExtractor; + + int m_startOffset; + int m_endOffset; + bool m_hasExecuted; + Vector<Gap> m_gaps; +}; + +} // namespace JSC + +#endif // BasicBlockLocation_h diff --git a/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h new file mode 100644 index 000000000..951a3286c --- /dev/null +++ b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -0,0 +1,58 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2008 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef BatchedTransitionOptimizer_h +#define BatchedTransitionOptimizer_h + +#include "JSObject.h" + +namespace JSC { + +class BatchedTransitionOptimizer { + WTF_MAKE_NONCOPYABLE(BatchedTransitionOptimizer); +public: + BatchedTransitionOptimizer(VM& vm, JSObject* object) + : m_vm(&vm) + , m_object(object) + { + if (!m_object->structure(vm)->isDictionary()) + m_object->convertToDictionary(vm); + } + + ~BatchedTransitionOptimizer() + { + if (m_object->structure()->isDictionary()) + m_object->flattenDictionaryObject(*m_vm); + } + +private: + VM* m_vm; + JSObject* m_object; +}; + +} // namespace JSC + +#endif // BatchedTransitionOptimizer_h diff --git a/Source/JavaScriptCore/runtime/BigInteger.h b/Source/JavaScriptCore/runtime/BigInteger.h new file mode 100644 index 000000000..0a0c477c2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BigInteger.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef BigInteger_h +#define BigInteger_h + +#include <wtf/MathExtras.h> + +namespace JSC { + +// This is used in converting the integer part of a number to a string. +class BigInteger { +public: + BigInteger(double number) + { + ASSERT(std::isfinite(number) && !std::signbit(number)); + ASSERT(number == floor(number)); + + bool sign; + int32_t exponent; + uint64_t mantissa; + decomposeDouble(number, sign, exponent, mantissa); + ASSERT(!sign && exponent >= 0); + + int32_t zeroBits = exponent - 52; + + if (zeroBits < 0) { + mantissa >>= -zeroBits; + zeroBits = 0; + } + + while (zeroBits >= 32) { + m_values.append(0); + zeroBits -= 32; + } + + // Left align the 53 bits of the mantissa within 96 bits. + uint32_t values[3]; + values[0] = static_cast<uint32_t>(mantissa); + values[1] = static_cast<uint32_t>(mantissa >> 32); + values[2] = 0; + // Shift based on the remainder of the exponent. + if (zeroBits) { + values[2] = values[1] >> (32 - zeroBits); + values[1] = (values[1] << zeroBits) | (values[0] >> (32 - zeroBits)); + values[0] = (values[0] << zeroBits); + } + m_values.append(values[0]); + m_values.append(values[1]); + m_values.append(values[2]); + + // Canonicalize; remove all trailing zeros. + while (m_values.size() && !m_values.last()) + m_values.removeLast(); + } + + uint32_t divide(uint32_t divisor) + { + uint32_t carry = 0; + + for (size_t i = m_values.size(); i; ) { + --i; + uint64_t dividend = (static_cast<uint64_t>(carry) << 32) + static_cast<uint64_t>(m_values[i]); + + uint64_t result = dividend / static_cast<uint64_t>(divisor); + ASSERT(result == static_cast<uint32_t>(result)); + uint64_t remainder = dividend % static_cast<uint64_t>(divisor); + ASSERT(remainder == static_cast<uint32_t>(remainder)); + + m_values[i] = static_cast<uint32_t>(result); + carry = static_cast<uint32_t>(remainder); + } + + // Canonicalize; remove all trailing zeros. + while (m_values.size() && !m_values.last()) + m_values.removeLast(); + + return carry; + } + + bool operator!() { return !m_values.size(); } + +private: + Vector<uint32_t, 36> m_values; +}; + +} + +#endif + diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.cpp b/Source/JavaScriptCore/runtime/BooleanConstructor.cpp new file mode 100644 index 000000000..0a250760d --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "BooleanConstructor.h" + +#include "BooleanPrototype.h" +#include "JSGlobalObject.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(BooleanConstructor); + +const ClassInfo BooleanConstructor::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(BooleanConstructor) }; + +BooleanConstructor::BooleanConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void BooleanConstructor::finishCreation(VM& vm, BooleanPrototype* booleanPrototype) +{ + Base::finishCreation(vm, booleanPrototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, booleanPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontDelete | DontEnum); +} + +// ECMA 15.6.2 +JSObject* constructBoolean(ExecState* exec, const ArgList& args) +{ + BooleanObject* obj = BooleanObject::create(exec->vm(), asInternalFunction(exec->callee())->globalObject()->booleanObjectStructure()); + obj->setInternalValue(exec->vm(), jsBoolean(args.at(0).toBoolean(exec))); + return obj; +} + +static EncodedJSValue JSC_HOST_CALL constructWithBooleanConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructBoolean(exec, args)); +} + +ConstructType BooleanConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithBooleanConstructor; + return ConstructTypeHost; +} + +// ECMA 15.6.1 +static EncodedJSValue JSC_HOST_CALL callBooleanConstructor(ExecState* exec) +{ + return JSValue::encode(jsBoolean(exec->argument(0).toBoolean(exec))); +} + +CallType BooleanConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callBooleanConstructor; + return CallTypeHost; +} + +JSObject* constructBooleanFromImmediateBoolean(ExecState* exec, JSGlobalObject* globalObject, JSValue immediateBooleanValue) +{ + BooleanObject* obj = BooleanObject::create(exec->vm(), globalObject->booleanObjectStructure()); + obj->setInternalValue(exec->vm(), immediateBooleanValue); + return obj; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.h b/Source/JavaScriptCore/runtime/BooleanConstructor.h new file mode 100644 index 000000000..177f69e5c --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BooleanConstructor_h +#define BooleanConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class BooleanPrototype; + +class BooleanConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + + static BooleanConstructor* create(VM& vm, Structure* structure, BooleanPrototype* booleanPrototype) + { + BooleanConstructor* constructor = new (NotNull, allocateCell<BooleanConstructor>(vm.heap)) BooleanConstructor(vm, structure); + constructor->finishCreation(vm, booleanPrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, BooleanPrototype*); + +private: + BooleanConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +JSObject* constructBooleanFromImmediateBoolean(ExecState*, JSGlobalObject*, JSValue); +JSObject* constructBoolean(ExecState*, const ArgList&); + +} // namespace JSC + +#endif // BooleanConstructor_h diff --git a/Source/JavaScriptCore/runtime/BooleanObject.cpp b/Source/JavaScriptCore/runtime/BooleanObject.cpp new file mode 100644 index 000000000..28cad6ae7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanObject.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "BooleanObject.h" + +#include "JSScope.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(BooleanObject); + +const ClassInfo BooleanObject::s_info = { "Boolean", &JSWrapperObject::s_info, 0, CREATE_METHOD_TABLE(BooleanObject) }; + +BooleanObject::BooleanObject(VM& vm, Structure* structure) + : JSWrapperObject(vm, structure) +{ +} + +void BooleanObject::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanObject.h b/Source/JavaScriptCore/runtime/BooleanObject.h new file mode 100644 index 000000000..6944db496 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanObject.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BooleanObject_h +#define BooleanObject_h + +#include "JSWrapperObject.h" + +namespace JSC { + +class BooleanObject : public JSWrapperObject { +protected: + JS_EXPORT_PRIVATE BooleanObject(VM&, Structure*); + JS_EXPORT_PRIVATE void finishCreation(VM&); + +public: + typedef JSWrapperObject Base; + + static BooleanObject* create(VM& vm, Structure* structure) + { + BooleanObject* boolean = new (NotNull, allocateCell<BooleanObject>(vm.heap)) BooleanObject(vm, structure); + boolean->finishCreation(vm); + return boolean; + } + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } +}; + +BooleanObject* asBooleanObject(JSValue); + +inline BooleanObject* asBooleanObject(JSValue value) +{ + ASSERT(asObject(value)->inherits(BooleanObject::info())); + return static_cast<BooleanObject*>(asObject(value)); +} + +} // namespace JSC + +#endif // BooleanObject_h diff --git a/Source/JavaScriptCore/runtime/BooleanPrototype.cpp b/Source/JavaScriptCore/runtime/BooleanPrototype.cpp new file mode 100644 index 000000000..be737ae5a --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "BooleanPrototype.h" + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSFunction.h" +#include "JSString.h" +#include "ObjectPrototype.h" +#include "JSCInlines.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState*); + +} + +#include "BooleanPrototype.lut.h" + +namespace JSC { + +const ClassInfo BooleanPrototype::s_info = { "Boolean", &BooleanObject::s_info, &booleanPrototypeTable, CREATE_METHOD_TABLE(BooleanPrototype) }; + +/* Source for BooleanPrototype.lut.h +@begin booleanPrototypeTable + toString booleanProtoFuncToString DontEnum|Function 0 + valueOf booleanProtoFuncValueOf DontEnum|Function 0 +@end +*/ + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(BooleanPrototype); + +BooleanPrototype::BooleanPrototype(VM& vm, Structure* structure) + : BooleanObject(vm, structure) +{ +} + +void BooleanPrototype::finishCreation(VM& vm, JSGlobalObject*) +{ + Base::finishCreation(vm); + setInternalValue(vm, jsBoolean(false)); + + ASSERT(inherits(info())); +} + +bool BooleanPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<BooleanObject>(exec, booleanPrototypeTable, jsCast<BooleanPrototype*>(object), propertyName, slot); +} + +// ------------------------------ Functions --------------------------- + +EncodedJSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState* exec) +{ + VM* vm = &exec->vm(); + JSValue thisValue = exec->thisValue(); + if (thisValue == jsBoolean(false)) + return JSValue::encode(vm->smallStrings.falseString()); + + if (thisValue == jsBoolean(true)) + return JSValue::encode(vm->smallStrings.trueString()); + + if (!thisValue.inherits(BooleanObject::info())) + return throwVMTypeError(exec); + + if (asBooleanObject(thisValue)->internalValue() == jsBoolean(false)) + return JSValue::encode(vm->smallStrings.falseString()); + + ASSERT(asBooleanObject(thisValue)->internalValue() == jsBoolean(true)); + return JSValue::encode(vm->smallStrings.trueString()); +} + +EncodedJSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (thisValue.isBoolean()) + return JSValue::encode(thisValue); + + if (!thisValue.inherits(BooleanObject::info())) + return throwVMTypeError(exec); + + return JSValue::encode(asBooleanObject(thisValue)->internalValue()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanPrototype.h b/Source/JavaScriptCore/runtime/BooleanPrototype.h new file mode 100644 index 000000000..6d8fc5e81 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BooleanPrototype_h +#define BooleanPrototype_h + +#include "BooleanObject.h" + +namespace JSC { + +class BooleanPrototype : public BooleanObject { +public: + typedef BooleanObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static BooleanPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + BooleanPrototype* prototype = new (NotNull, allocateCell<BooleanPrototype>(vm.heap)) BooleanPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, JSGlobalObject*); + +private: + BooleanPrototype(VM&, Structure*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // BooleanPrototype_h diff --git a/Source/JavaScriptCore/runtime/BundlePath.h b/Source/JavaScriptCore/runtime/BundlePath.h new file mode 100644 index 000000000..c8c694832 --- /dev/null +++ b/Source/JavaScriptCore/runtime/BundlePath.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef BundlePath_h +#define BundlePath_h + +#include <string> +#include <wtf/text/CString.h> + +namespace JSC { + +const CString& bundlePath(); + +} // namespace JSC + +#endif // BundlePath_h + diff --git a/Source/JavaScriptCore/runtime/Butterfly.h b/Source/JavaScriptCore/runtime/Butterfly.h new file mode 100644 index 000000000..20dccd9b6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Butterfly.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Butterfly_h +#define Butterfly_h + +#include "IndexingHeader.h" +#include "PropertyOffset.h" +#include "PropertyStorage.h" +#include <wtf/Noncopyable.h> + +namespace JSC { + +class VM; +class CopyVisitor; +struct ArrayStorage; + +template <typename T> struct ContiguousData { + ContiguousData() + : m_data(0) +#if !ASSERT_DISABLED + , m_length(0) +#endif + { + } + ContiguousData(T* data, size_t length) + : m_data(data) +#if !ASSERT_DISABLED + , m_length(length) +#endif + { + UNUSED_PARAM(length); + } + + const T& operator[](size_t index) const { ASSERT(index < m_length); return m_data[index]; } + T& operator[](size_t index) { ASSERT(index < m_length); return m_data[index]; } + + T* data() const { return m_data; } +#if !ASSERT_DISABLED + size_t length() const { return m_length; } +#endif + +private: + T* m_data; +#if !ASSERT_DISABLED + size_t m_length; +#endif +}; + +typedef ContiguousData<double> ContiguousDoubles; +typedef ContiguousData<WriteBarrier<Unknown>> ContiguousJSValues; + +class Butterfly { + WTF_MAKE_NONCOPYABLE(Butterfly); +private: + Butterfly() { } // Not instantiable. +public: + + static size_t totalSize(size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes) + { + ASSERT(indexingPayloadSizeInBytes ? hasIndexingHeader : true); + ASSERT(sizeof(EncodedJSValue) == sizeof(IndexingHeader)); + return (preCapacity + propertyCapacity) * sizeof(EncodedJSValue) + (hasIndexingHeader ? sizeof(IndexingHeader) : 0) + indexingPayloadSizeInBytes; + } + + static Butterfly* fromBase(void* base, size_t preCapacity, size_t propertyCapacity) + { + return reinterpret_cast<Butterfly*>(static_cast<EncodedJSValue*>(base) + preCapacity + propertyCapacity + 1); + } + + // This method is here not just because it's handy, but to remind you that + // the whole point of butterflies is to do evil pointer arithmetic. + static Butterfly* fromPointer(char* ptr) + { + return reinterpret_cast<Butterfly*>(ptr); + } + + char* pointer() { return reinterpret_cast<char*>(this); } + + static ptrdiff_t offsetOfIndexingHeader() { return IndexingHeader::offsetOfIndexingHeader(); } + static ptrdiff_t offsetOfArrayBuffer() { return offsetOfIndexingHeader() + IndexingHeader::offsetOfArrayBuffer(); } + static ptrdiff_t offsetOfPublicLength() { return offsetOfIndexingHeader() + IndexingHeader::offsetOfPublicLength(); } + static ptrdiff_t offsetOfVectorLength() { return offsetOfIndexingHeader() + IndexingHeader::offsetOfVectorLength(); } + + static Butterfly* createUninitialized(VM&, JSCell* intendedOwner, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes); + + static Butterfly* create(VM&, JSCell* intendedOwner, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, const IndexingHeader&, size_t indexingPayloadSizeInBytes); + static Butterfly* create(VM&, JSCell* intendedOwner, Structure*); + static Butterfly* createUninitializedDuringCollection(CopyVisitor&, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes); + + IndexingHeader* indexingHeader() { return IndexingHeader::from(this); } + const IndexingHeader* indexingHeader() const { return IndexingHeader::from(this); } + PropertyStorage propertyStorage() { return indexingHeader()->propertyStorage(); } + ConstPropertyStorage propertyStorage() const { return indexingHeader()->propertyStorage(); } + + uint32_t publicLength() { return indexingHeader()->publicLength(); } + uint32_t vectorLength() { return indexingHeader()->vectorLength(); } + void setPublicLength(uint32_t value) { indexingHeader()->setPublicLength(value); } + void setVectorLength(uint32_t value) { indexingHeader()->setVectorLength(value); } + + template<typename T> + T* indexingPayload() { return reinterpret_cast_ptr<T*>(this); } + ArrayStorage* arrayStorage() { return indexingPayload<ArrayStorage>(); } + ContiguousJSValues contiguousInt32() { return ContiguousJSValues(indexingPayload<WriteBarrier<Unknown>>(), vectorLength()); } + + ContiguousDoubles contiguousDouble() { return ContiguousDoubles(indexingPayload<double>(), vectorLength()); } + ContiguousJSValues contiguous() { return ContiguousJSValues(indexingPayload<WriteBarrier<Unknown>>(), vectorLength()); } + + static Butterfly* fromContiguous(WriteBarrier<Unknown>* contiguous) + { + return reinterpret_cast<Butterfly*>(contiguous); + } + static Butterfly* fromContiguous(double* contiguous) + { + return reinterpret_cast<Butterfly*>(contiguous); + } + + static ptrdiff_t offsetOfPropertyStorage() { return -static_cast<ptrdiff_t>(sizeof(IndexingHeader)); } + static int indexOfPropertyStorage() + { + ASSERT(sizeof(IndexingHeader) == sizeof(EncodedJSValue)); + return -1; + } + + void* base(size_t preCapacity, size_t propertyCapacity) { return propertyStorage() - propertyCapacity - preCapacity; } + void* base(Structure*); + + static Butterfly* createOrGrowArrayRight( + Butterfly*, VM&, JSCell* intendedOwner, Structure* oldStructure, + size_t propertyCapacity, bool hadIndexingHeader, + size_t oldIndexingPayloadSizeInBytes, size_t newIndexingPayloadSizeInBytes); + + // The butterfly reallocation methods perform the reallocation itself but do not change any + // of the meta-data to reflect that the reallocation occurred. Note that this set of + // methods is not exhaustive and is not intended to encapsulate all possible allocation + // modes of butterflies - there are code paths that allocate butterflies by calling + // directly into Heap::tryAllocateStorage. + static Butterfly* createOrGrowPropertyStorage(Butterfly*, VM&, JSCell* intendedOwner, Structure*, size_t oldPropertyCapacity, size_t newPropertyCapacity); + Butterfly* growArrayRight(VM&, JSCell* intendedOwner, Structure* oldStructure, size_t propertyCapacity, bool hadIndexingHeader, size_t oldIndexingPayloadSizeInBytes, size_t newIndexingPayloadSizeInBytes); // Assumes that preCapacity is zero, and asserts as much. + Butterfly* growArrayRight(VM&, JSCell* intendedOwner, Structure*, size_t newIndexingPayloadSizeInBytes); + Butterfly* resizeArray(VM&, JSCell* intendedOwner, size_t propertyCapacity, bool oldHasIndexingHeader, size_t oldIndexingPayloadSizeInBytes, size_t newPreCapacity, bool newHasIndexingHeader, size_t newIndexingPayloadSizeInBytes); + Butterfly* resizeArray(VM&, JSCell* intendedOwner, Structure*, size_t newPreCapacity, size_t newIndexingPayloadSizeInBytes); // Assumes that you're not changing whether or not the object has an indexing header. + Butterfly* unshift(Structure*, size_t numberOfSlots); + Butterfly* shift(Structure*, size_t numberOfSlots); +}; + +} // namespace JSC + +#endif // Butterfly_h + diff --git a/Source/JavaScriptCore/runtime/ButterflyInlines.h b/Source/JavaScriptCore/runtime/ButterflyInlines.h new file mode 100644 index 000000000..3fd8dc139 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ButterflyInlines.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ButterflyInlines_h +#define ButterflyInlines_h + +#include "ArrayStorage.h" +#include "Butterfly.h" +#include "CopiedSpaceInlines.h" +#include "CopyVisitor.h" +#include "VM.h" +#include "Structure.h" + +namespace JSC { + +inline Butterfly* Butterfly::createUninitialized(VM& vm, JSCell* intendedOwner, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes) +{ + void* temp; + size_t size = totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + RELEASE_ASSERT(vm.heap.tryAllocateStorage(intendedOwner, size, &temp)); + Butterfly* result = fromBase(temp, preCapacity, propertyCapacity); + return result; +} + +inline Butterfly* Butterfly::create(VM& vm, JSCell* intendedOwner, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, const IndexingHeader& indexingHeader, size_t indexingPayloadSizeInBytes) +{ + Butterfly* result = createUninitialized( + vm, intendedOwner, preCapacity, propertyCapacity, hasIndexingHeader, + indexingPayloadSizeInBytes); + if (hasIndexingHeader) + *result->indexingHeader() = indexingHeader; + return result; +} + +inline Butterfly* Butterfly::create(VM& vm, JSCell* intendedOwner, Structure* structure) +{ + return create( + vm, intendedOwner, 0, structure->outOfLineCapacity(), + structure->hasIndexingHeader(intendedOwner), IndexingHeader(), 0); +} + +inline Butterfly* Butterfly::createUninitializedDuringCollection(CopyVisitor& visitor, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes) +{ + size_t size = totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + Butterfly* result = fromBase( + visitor.allocateNewSpace(size), + preCapacity, propertyCapacity); + return result; +} + +inline void* Butterfly::base(Structure* structure) +{ + return base(indexingHeader()->preCapacity(structure), structure->outOfLineCapacity()); +} + +inline Butterfly* Butterfly::createOrGrowPropertyStorage( + Butterfly* oldButterfly, VM& vm, JSCell* intendedOwner, Structure* structure, size_t oldPropertyCapacity, size_t newPropertyCapacity) +{ + RELEASE_ASSERT(newPropertyCapacity > oldPropertyCapacity); + if (!oldButterfly) + return create(vm, intendedOwner, 0, newPropertyCapacity, false, IndexingHeader(), 0); + + size_t preCapacity = oldButterfly->indexingHeader()->preCapacity(structure); + size_t indexingPayloadSizeInBytes = oldButterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); + bool hasIndexingHeader = structure->hasIndexingHeader(intendedOwner); + Butterfly* result = createUninitialized( + vm, intendedOwner, preCapacity, newPropertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + memcpy( + result->propertyStorage() - oldPropertyCapacity, + oldButterfly->propertyStorage() - oldPropertyCapacity, + totalSize(0, oldPropertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes)); + return result; +} + +inline Butterfly* Butterfly::createOrGrowArrayRight( + Butterfly* oldButterfly, VM& vm, JSCell* intendedOwner, Structure* oldStructure, + size_t propertyCapacity, bool hadIndexingHeader, size_t oldIndexingPayloadSizeInBytes, + size_t newIndexingPayloadSizeInBytes) +{ + if (!oldButterfly) { + return create( + vm, intendedOwner, 0, propertyCapacity, true, IndexingHeader(), + newIndexingPayloadSizeInBytes); + } + return oldButterfly->growArrayRight( + vm, intendedOwner, oldStructure, propertyCapacity, hadIndexingHeader, + oldIndexingPayloadSizeInBytes, newIndexingPayloadSizeInBytes); +} + +inline Butterfly* Butterfly::growArrayRight( + VM& vm, JSCell* intendedOwner, Structure* oldStructure, size_t propertyCapacity, + bool hadIndexingHeader, size_t oldIndexingPayloadSizeInBytes, + size_t newIndexingPayloadSizeInBytes) +{ + ASSERT_UNUSED(oldStructure, !indexingHeader()->preCapacity(oldStructure)); + ASSERT_UNUSED(oldStructure, hadIndexingHeader == oldStructure->hasIndexingHeader(intendedOwner)); + void* theBase = base(0, propertyCapacity); + size_t oldSize = totalSize(0, propertyCapacity, hadIndexingHeader, oldIndexingPayloadSizeInBytes); + size_t newSize = totalSize(0, propertyCapacity, true, newIndexingPayloadSizeInBytes); + if (!vm.heap.tryReallocateStorage(intendedOwner, &theBase, oldSize, newSize)) + return 0; + return fromBase(theBase, 0, propertyCapacity); +} + +inline Butterfly* Butterfly::growArrayRight( + VM& vm, JSCell* intendedOwner, Structure* oldStructure, + size_t newIndexingPayloadSizeInBytes) +{ + return growArrayRight( + vm, intendedOwner, oldStructure, oldStructure->outOfLineCapacity(), + oldStructure->hasIndexingHeader(intendedOwner), + indexingHeader()->indexingPayloadSizeInBytes(oldStructure), + newIndexingPayloadSizeInBytes); +} + +inline Butterfly* Butterfly::resizeArray( + VM& vm, JSCell* intendedOwner, size_t propertyCapacity, bool oldHasIndexingHeader, + size_t oldIndexingPayloadSizeInBytes, size_t newPreCapacity, bool newHasIndexingHeader, + size_t newIndexingPayloadSizeInBytes) +{ + Butterfly* result = createUninitialized( + vm, intendedOwner, newPreCapacity, propertyCapacity, newHasIndexingHeader, + newIndexingPayloadSizeInBytes); + // FIXME: This could be made much more efficient if we used the property size, + // not the capacity. + void* to = result->propertyStorage() - propertyCapacity; + void* from = propertyStorage() - propertyCapacity; + size_t size = std::min( + totalSize(0, propertyCapacity, oldHasIndexingHeader, oldIndexingPayloadSizeInBytes), + totalSize(0, propertyCapacity, newHasIndexingHeader, newIndexingPayloadSizeInBytes)); + memcpy(to, from, size); + return result; +} + +inline Butterfly* Butterfly::resizeArray( + VM& vm, JSCell* intendedOwner, Structure* structure, size_t newPreCapacity, + size_t newIndexingPayloadSizeInBytes) +{ + bool hasIndexingHeader = structure->hasIndexingHeader(intendedOwner); + return resizeArray( + vm, intendedOwner, structure->outOfLineCapacity(), hasIndexingHeader, + indexingHeader()->indexingPayloadSizeInBytes(structure), newPreCapacity, + hasIndexingHeader, newIndexingPayloadSizeInBytes); +} + +inline Butterfly* Butterfly::unshift(Structure* structure, size_t numberOfSlots) +{ + ASSERT(hasAnyArrayStorage(structure->indexingType())); + ASSERT(numberOfSlots <= indexingHeader()->preCapacity(structure)); + unsigned propertyCapacity = structure->outOfLineCapacity(); + // FIXME: It would probably be wise to rewrite this as a loop since (1) we know in which + // direction we're moving memory so we don't need the extra check of memmove and (2) we're + // moving a small amount of memory in the common case so the throughput of memmove won't + // amortize the overhead of calling it. And no, we cannot rely on the C++ compiler to + // inline memmove (particularly since the size argument is likely to be variable), nor can + // we rely on the compiler to recognize the ordering of the pointer arguments (since + // propertyCapacity is variable and could cause wrap-around as far as the compiler knows). + memmove( + propertyStorage() - numberOfSlots - propertyCapacity, + propertyStorage() - propertyCapacity, + sizeof(EncodedJSValue) * propertyCapacity + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); + return IndexingHeader::fromEndOf(propertyStorage() - numberOfSlots)->butterfly(); +} + +inline Butterfly* Butterfly::shift(Structure* structure, size_t numberOfSlots) +{ + ASSERT(hasAnyArrayStorage(structure->indexingType())); + unsigned propertyCapacity = structure->outOfLineCapacity(); + // FIXME: See comment in unshift(), above. + memmove( + propertyStorage() - propertyCapacity + numberOfSlots, + propertyStorage() - propertyCapacity, + sizeof(EncodedJSValue) * propertyCapacity + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); + return IndexingHeader::fromEndOf(propertyStorage() + numberOfSlots)->butterfly(); +} + +} // namespace JSC + +#endif // ButterflyInlines_h + diff --git a/Source/JavaScriptCore/runtime/CallData.cpp b/Source/JavaScriptCore/runtime/CallData.cpp new file mode 100644 index 000000000..31c28c336 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CallData.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "CallData.h" + +#include "Executable.h" +#include "Interpreter.h" +#include "JSFunction.h" +#include "JSCInlines.h" + +namespace JSC { + +JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) +{ + ASSERT(callType == CallTypeJS || callType == CallTypeHost); + return exec->interpreter()->executeCall(exec, asObject(functionObject), callType, callData, thisValue, args); +} + +JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args, NakedPtr<Exception>& returnedException) +{ + JSValue result = call(exec, functionObject, callType, callData, thisValue, args); + if (exec->hadException()) { + returnedException = exec->exception(); + exec->clearException(); + return jsUndefined(); + } + RELEASE_ASSERT(result); + return result; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CallData.h b/Source/JavaScriptCore/runtime/CallData.h new file mode 100644 index 000000000..e4a918dec --- /dev/null +++ b/Source/JavaScriptCore/runtime/CallData.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef CallData_h +#define CallData_h + +#include "JSCJSValue.h" +#include <wtf/NakedPtr.h> + +namespace JSC { + +class ArgList; +class Exception; +class ExecState; +class FunctionExecutable; +class JSObject; +class JSScope; + +enum CallType { + CallTypeNone, + CallTypeHost, + CallTypeJS +}; + +typedef EncodedJSValue (JSC_HOST_CALL *NativeFunction)(ExecState*); + +union CallData { + struct { + NativeFunction function; + } native; + struct { + FunctionExecutable* functionExecutable; + JSScope* scope; + } js; +}; + +JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&); +JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&, NakedPtr<Exception>& returnedException); + +} // namespace JSC + +#endif // CallData_h diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h new file mode 100644 index 000000000..cb5058c2d --- /dev/null +++ b/Source/JavaScriptCore/runtime/ClassInfo.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef ClassInfo_h +#define ClassInfo_h + +#include "CallFrame.h" +#include "ConstructData.h" +#include "CopyToken.h" +#include "JSCell.h" + +namespace JSC { + +class JSArrayBufferView; +struct HashTable; + +struct MethodTable { + typedef void (*DestroyFunctionPtr)(JSCell*); + DestroyFunctionPtr destroy; + + typedef void (*VisitChildrenFunctionPtr)(JSCell*, SlotVisitor&); + VisitChildrenFunctionPtr visitChildren; + + typedef void (*CopyBackingStoreFunctionPtr)(JSCell*, CopyVisitor&, CopyToken); + CopyBackingStoreFunctionPtr copyBackingStore; + + typedef CallType (*GetCallDataFunctionPtr)(JSCell*, CallData&); + GetCallDataFunctionPtr getCallData; + + typedef ConstructType (*GetConstructDataFunctionPtr)(JSCell*, ConstructData&); + GetConstructDataFunctionPtr getConstructData; + + typedef void (*PutFunctionPtr)(JSCell*, ExecState*, PropertyName propertyName, JSValue, PutPropertySlot&); + PutFunctionPtr put; + + typedef void (*PutByIndexFunctionPtr)(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + PutByIndexFunctionPtr putByIndex; + + typedef bool (*DeletePropertyFunctionPtr)(JSCell*, ExecState*, PropertyName); + DeletePropertyFunctionPtr deleteProperty; + + typedef bool (*DeletePropertyByIndexFunctionPtr)(JSCell*, ExecState*, unsigned); + DeletePropertyByIndexFunctionPtr deletePropertyByIndex; + + typedef bool (*GetOwnPropertySlotFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertySlot&); + GetOwnPropertySlotFunctionPtr getOwnPropertySlot; + + typedef bool (*GetOwnPropertySlotByIndexFunctionPtr)(JSObject*, ExecState*, unsigned, PropertySlot&); + GetOwnPropertySlotByIndexFunctionPtr getOwnPropertySlotByIndex; + + typedef JSValue (*ToThisFunctionPtr)(JSCell*, ExecState*, ECMAMode); + ToThisFunctionPtr toThis; + + typedef JSValue (*DefaultValueFunctionPtr)(const JSObject*, ExecState*, PreferredPrimitiveType); + DefaultValueFunctionPtr defaultValue; + + typedef void (*GetOwnPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetOwnPropertyNamesFunctionPtr getOwnPropertyNames; + + typedef void (*GetOwnNonIndexPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetOwnNonIndexPropertyNamesFunctionPtr getOwnNonIndexPropertyNames; + + typedef void (*GetPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetPropertyNamesFunctionPtr getPropertyNames; + + typedef uint32_t (*GetEnumerableLengthFunctionPtr)(ExecState*, JSObject*); + GetEnumerableLengthFunctionPtr getEnumerableLength; + + GetPropertyNamesFunctionPtr getStructurePropertyNames; + GetPropertyNamesFunctionPtr getGenericPropertyNames; + + typedef String (*ClassNameFunctionPtr)(const JSObject*); + ClassNameFunctionPtr className; + + typedef bool (*CustomHasInstanceFunctionPtr)(JSObject*, ExecState*, JSValue); + CustomHasInstanceFunctionPtr customHasInstance; + + typedef bool (*DefineOwnPropertyFunctionPtr)(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool); + DefineOwnPropertyFunctionPtr defineOwnProperty; + + typedef ArrayBuffer* (*SlowDownAndWasteMemory)(JSArrayBufferView*); + SlowDownAndWasteMemory slowDownAndWasteMemory; + + typedef PassRefPtr<ArrayBufferView> (*GetTypedArrayImpl)(JSArrayBufferView*); + GetTypedArrayImpl getTypedArrayImpl; + + typedef void (*DumpToStreamFunctionPtr)(const JSCell*, PrintStream&); + DumpToStreamFunctionPtr dumpToStream; +}; + +#define CREATE_MEMBER_CHECKER(member) \ + template <typename T> \ + struct MemberCheck##member { \ + struct Fallback { \ + void member(...); \ + }; \ + struct Derived : T, Fallback { }; \ + template <typename U, U> struct Check; \ + typedef char Yes[2]; \ + typedef char No[1]; \ + template <typename U> \ + static No &func(Check<void (Fallback::*)(...), &U::member>*); \ + template <typename U> \ + static Yes &func(...); \ + enum { has = sizeof(func<Derived>(0)) == sizeof(Yes) }; \ + } + +#define HAS_MEMBER_NAMED(klass, name) (MemberCheck##name<klass>::has) + +#define CREATE_METHOD_TABLE(ClassName) { \ + &ClassName::destroy, \ + &ClassName::visitChildren, \ + &ClassName::copyBackingStore, \ + &ClassName::getCallData, \ + &ClassName::getConstructData, \ + &ClassName::put, \ + &ClassName::putByIndex, \ + &ClassName::deleteProperty, \ + &ClassName::deletePropertyByIndex, \ + &ClassName::getOwnPropertySlot, \ + &ClassName::getOwnPropertySlotByIndex, \ + &ClassName::toThis, \ + &ClassName::defaultValue, \ + &ClassName::getOwnPropertyNames, \ + &ClassName::getOwnNonIndexPropertyNames, \ + &ClassName::getPropertyNames, \ + &ClassName::getEnumerableLength, \ + &ClassName::getStructurePropertyNames, \ + &ClassName::getGenericPropertyNames, \ + &ClassName::className, \ + &ClassName::customHasInstance, \ + &ClassName::defineOwnProperty, \ + &ClassName::slowDownAndWasteMemory, \ + &ClassName::getTypedArrayImpl, \ + &ClassName::dumpToStream \ + }, \ + ClassName::TypedArrayStorageType + +struct ClassInfo { + // A string denoting the class name. Example: "Window". + const char* className; + + // Pointer to the class information of the base class. + // nullptrif there is none. + const ClassInfo* parentClass; + + bool isSubClassOf(const ClassInfo* other) const + { + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (ci == other) + return true; + } + return false; + } + + bool hasStaticProperties() const + { + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (ci->staticPropHashTable) + return true; + } + return false; + } + + bool hasStaticSetterOrReadonlyProperties() const; + + const HashTable* staticPropHashTable; + + MethodTable methodTable; + + TypedArrayType typedArrayStorageType; +}; + +} // namespace JSC + +#endif // ClassInfo_h diff --git a/Source/JavaScriptCore/runtime/ClonedArguments.cpp b/Source/JavaScriptCore/runtime/ClonedArguments.cpp new file mode 100644 index 000000000..8a740db6d --- /dev/null +++ b/Source/JavaScriptCore/runtime/ClonedArguments.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ClonedArguments.h" + +#include "GetterSetter.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ClonedArguments); + +const ClassInfo ClonedArguments::s_info = { "Arguments", &Base::s_info, 0, CREATE_METHOD_TABLE(ClonedArguments) }; + +ClonedArguments::ClonedArguments(VM& vm, Structure* structure) + : Base(vm, structure, nullptr) +{ +} + +ClonedArguments* ClonedArguments::createEmpty( + VM& vm, Structure* structure, JSFunction* callee) +{ + ClonedArguments* result = + new (NotNull, allocateCell<ClonedArguments>(vm.heap)) + ClonedArguments(vm, structure); + result->finishCreation(vm); + result->m_callee.set(vm, result, callee); + return result; +} + +ClonedArguments* ClonedArguments::createEmpty(ExecState* exec, JSFunction* callee) +{ + // NB. Some clients might expect that the global object of of this object is the global object + // of the callee. We don't do this for now, but maybe we should. + return createEmpty( + exec->vm(), exec->lexicalGlobalObject()->outOfBandArgumentsStructure(), callee); +} + +ClonedArguments* ClonedArguments::createWithInlineFrame(ExecState* myFrame, ExecState* targetFrame, InlineCallFrame* inlineCallFrame, ArgumentsMode mode) +{ + VM& vm = myFrame->vm(); + + JSFunction* callee; + + if (inlineCallFrame) + callee = jsCast<JSFunction*>(inlineCallFrame->calleeRecovery.recover(targetFrame)); + else + callee = jsCast<JSFunction*>(targetFrame->callee()); + + ClonedArguments* result = createEmpty(myFrame, callee); + + unsigned length = 0; // Initialize because VC needs it. + switch (mode) { + case ArgumentsMode::Cloned: { + if (inlineCallFrame) { + if (inlineCallFrame->argumentCountRegister.isValid()) + length = targetFrame->r(inlineCallFrame->argumentCountRegister).unboxedInt32(); + else + length = inlineCallFrame->arguments.size(); + length--; + + for (unsigned i = length; i--;) + result->putDirectIndex(myFrame, i, inlineCallFrame->arguments[i + 1].recover(targetFrame)); + } else { + length = targetFrame->argumentCount(); + + for (unsigned i = length; i--;) + result->putDirectIndex(myFrame, i, targetFrame->uncheckedArgument(i)); + } + break; + } + + case ArgumentsMode::FakeValues: { + length = 0; + break; + } } + + result->putDirect(vm, vm.propertyNames->length, jsNumber(length), DontEnum); + + return result; +} + +ClonedArguments* ClonedArguments::createWithMachineFrame(ExecState* myFrame, ExecState* targetFrame, ArgumentsMode mode) +{ + return createWithInlineFrame(myFrame, targetFrame, nullptr, mode); +} + +ClonedArguments* ClonedArguments::createByCopyingFrom( + ExecState* exec, Structure* structure, Register* argumentStart, unsigned length, + JSFunction* callee) +{ + VM& vm = exec->vm(); + ClonedArguments* result = createEmpty(vm, structure, callee); + + for (unsigned i = length; i--;) + result->putDirectIndex(exec, i, argumentStart[i].jsValue()); + + result->putDirect(vm, vm.propertyNames->length, jsNumber(length), DontEnum); + return result; +} + +Structure* ClonedArguments::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +bool ClonedArguments::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName ident, PropertySlot& slot) +{ + ClonedArguments* thisObject = jsCast<ClonedArguments*>(object); + VM& vm = exec->vm(); + + if (ident == vm.propertyNames->callee + || ident == vm.propertyNames->caller + || ident == vm.propertyNames->iteratorSymbol) + thisObject->materializeSpecialsIfNecessary(exec); + + if (Base::getOwnPropertySlot(thisObject, exec, ident, slot)) + return true; + + return false; +} + +void ClonedArguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) +{ + ClonedArguments* thisObject = jsCast<ClonedArguments*>(object); + thisObject->materializeSpecialsIfNecessary(exec); + Base::getOwnPropertyNames(thisObject, exec, array, mode); +} + +void ClonedArguments::put(JSCell* cell, ExecState* exec, PropertyName ident, JSValue value, PutPropertySlot& slot) +{ + ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell); + VM& vm = exec->vm(); + + if (ident == vm.propertyNames->callee + || ident == vm.propertyNames->caller + || ident == vm.propertyNames->iteratorSymbol) { + thisObject->materializeSpecialsIfNecessary(exec); + PutPropertySlot dummy = slot; // Shadow the given PutPropertySlot to prevent caching. + Base::put(thisObject, exec, ident, value, dummy); + return; + } + + Base::put(thisObject, exec, ident, value, slot); +} + +bool ClonedArguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName ident) +{ + ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell); + VM& vm = exec->vm(); + + if (ident == vm.propertyNames->callee + || ident == vm.propertyNames->caller + || ident == vm.propertyNames->iteratorSymbol) + thisObject->materializeSpecialsIfNecessary(exec); + + return Base::deleteProperty(thisObject, exec, ident); +} + +bool ClonedArguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow) +{ + ClonedArguments* thisObject = jsCast<ClonedArguments*>(object); + VM& vm = exec->vm(); + + if (ident == vm.propertyNames->callee + || ident == vm.propertyNames->caller + || ident == vm.propertyNames->iteratorSymbol) + thisObject->materializeSpecialsIfNecessary(exec); + + return Base::defineOwnProperty(object, exec, ident, descriptor, shouldThrow); +} + +void ClonedArguments::materializeSpecials(ExecState* exec) +{ + RELEASE_ASSERT(!specialsMaterialized()); + VM& vm = exec->vm(); + + FunctionExecutable* executable = jsCast<FunctionExecutable*>(m_callee->executable()); + bool isStrictMode = executable->isStrictMode(); + + if (isStrictMode) { + putDirectAccessor(exec, vm.propertyNames->callee, globalObject()->throwTypeErrorGetterSetter(vm), DontDelete | DontEnum | Accessor); + putDirectAccessor(exec, vm.propertyNames->caller, globalObject()->throwTypeErrorGetterSetter(vm), DontDelete | DontEnum | Accessor); + } else + putDirect(vm, vm.propertyNames->callee, JSValue(m_callee.get())); + + putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject()->arrayProtoValuesFunction(), DontEnum); + + m_callee.clear(); +} + +void ClonedArguments::materializeSpecialsIfNecessary(ExecState* exec) +{ + if (!specialsMaterialized()) + materializeSpecials(exec); +} + +void ClonedArguments::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_callee); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/ClonedArguments.h b/Source/JavaScriptCore/runtime/ClonedArguments.h new file mode 100644 index 000000000..8e713d5c6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ClonedArguments.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ClonedArguments_h +#define ClonedArguments_h + +#include "ArgumentsMode.h" +#include "JSObject.h" + +namespace JSC { + +// This is an Arguments-class object that we create when you do function.arguments, or you say +// "arguments" inside a function in strict mode. It behaves almpst entirely like an ordinary +// JavaScript object. All of the arguments values are simply copied from the stack (possibly via +// some sophisticated ValueRecovery's if an optimizing compiler is in play) and the appropriate +// properties of the object are populated. The only reason why we need a special class is to make +// the object claim to be "Arguments" from a toString standpoint, and to avoid materializing the +// caller/callee/@@iterator properties unless someone asks for them. +class ClonedArguments : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames; + +private: + ClonedArguments(VM&, Structure*); + +public: + static ClonedArguments* createEmpty(VM&, Structure*, JSFunction* callee); + static ClonedArguments* createEmpty(ExecState*, JSFunction* callee); + static ClonedArguments* createWithInlineFrame(ExecState* myFrame, ExecState* targetFrame, InlineCallFrame*, ArgumentsMode); + static ClonedArguments* createWithMachineFrame(ExecState* myFrame, ExecState* targetFrame, ArgumentsMode); + static ClonedArguments* createByCopyingFrom(ExecState*, Structure*, Register* argumentsStart, unsigned length, JSFunction* callee); + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + static void visitChildren(JSCell*, SlotVisitor&); + + DECLARE_INFO; + +private: + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + + bool specialsMaterialized() const { return !m_callee; } + void materializeSpecials(ExecState*); + void materializeSpecialsIfNecessary(ExecState*); + + WriteBarrier<JSFunction> m_callee; // Set to nullptr when we materialize all of our special properties. +}; + +} // namespace JSC + +#endif // ClonedArguments_h + diff --git a/Source/JavaScriptCore/runtime/CodeCache.cpp b/Source/JavaScriptCore/runtime/CodeCache.cpp new file mode 100644 index 000000000..ab70eabb9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CodeCache.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" + +#include "CodeCache.h" + +#include "BytecodeGenerator.h" +#include "CodeSpecializationKind.h" +#include "JSCInlines.h" +#include "Parser.h" +#include "StrongInlines.h" +#include "UnlinkedCodeBlock.h" + +namespace JSC { + +const double CodeCacheMap::workingSetTime = 10.0; + +void CodeCacheMap::pruneSlowCase() +{ + m_minCapacity = std::max(m_size - m_sizeAtLastPrune, static_cast<int64_t>(0)); + m_sizeAtLastPrune = m_size; + m_timeAtLastPrune = monotonicallyIncreasingTime(); + + if (m_capacity < m_minCapacity) + m_capacity = m_minCapacity; + + while (m_size > m_capacity || !canPruneQuickly()) { + MapType::iterator it = m_map.begin(); + m_size -= it->key.length(); + m_map.remove(it); + } +} + +CodeCache::CodeCache() +{ +} + +CodeCache::~CodeCache() +{ +} + +template <typename T> struct CacheTypes { }; + +template <> struct CacheTypes<UnlinkedProgramCodeBlock> { + typedef JSC::ProgramNode RootNode; + static const SourceCodeKey::CodeType codeType = SourceCodeKey::ProgramType; +}; + +template <> struct CacheTypes<UnlinkedEvalCodeBlock> { + typedef JSC::EvalNode RootNode; + static const SourceCodeKey::CodeType codeType = SourceCodeKey::EvalType; +}; + +template <class UnlinkedCodeBlockType, class ExecutableType> +UnlinkedCodeBlockType* CodeCache::getGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, + JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error, const VariableEnvironment* variablesUnderTDZ) +{ + SourceCodeKey key = SourceCodeKey(source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, builtinMode, strictMode, thisTDZMode); + SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key); + bool canCache = debuggerMode == DebuggerOff && profilerMode == ProfilerOff && !vm.typeProfiler() && !vm.controlFlowProfiler(); + if (cache && canCache) { + UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(cache->cell.get()); + unsigned firstLine = source.firstLine() + unlinkedCodeBlock->firstLine(); + unsigned lineCount = unlinkedCodeBlock->lineCount(); + unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn(); + bool endColumnIsOnStartLine = !lineCount; + unsigned endColumn = unlinkedCodeBlock->endColumn() + (endColumnIsOnStartLine ? startColumn : 1); + executable->recordParse(unlinkedCodeBlock->codeFeatures(), unlinkedCodeBlock->hasCapturedVariables(), firstLine, firstLine + lineCount, startColumn, endColumn); + return unlinkedCodeBlock; + } + + typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode; + std::unique_ptr<RootNode> rootNode = parse<RootNode>( + &vm, source, Identifier(), builtinMode, strictMode, + SourceParseMode::ProgramMode, error, nullptr, ConstructorKind::None, thisTDZMode); + if (!rootNode) + return nullptr; + + unsigned lineCount = rootNode->lastLine() - rootNode->firstLine(); + unsigned startColumn = rootNode->startColumn() + 1; + bool endColumnIsOnStartLine = !lineCount; + unsigned unlinkedEndColumn = rootNode->endColumn(); + unsigned endColumn = unlinkedEndColumn + (endColumnIsOnStartLine ? startColumn : 1); + executable->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->firstLine(), rootNode->lastLine(), startColumn, endColumn); + + UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executable->executableInfo()); + unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->firstLine() - source.firstLine(), lineCount, unlinkedEndColumn); + + auto generator = std::make_unique<BytecodeGenerator>(vm, rootNode.get(), unlinkedCodeBlock, debuggerMode, profilerMode, variablesUnderTDZ); + error = generator->generate(); + if (error.isValid()) + return nullptr; + + if (!canCache) + return unlinkedCodeBlock; + + m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age())); + return unlinkedCodeBlock; +} + +UnlinkedProgramCodeBlock* CodeCache::getProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) +{ + VariableEnvironment emptyParentTDZVariables; + return getGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, builtinMode, strictMode, ThisTDZMode::CheckIfNeeded, debuggerMode, profilerMode, error, &emptyParentTDZVariables); +} + +UnlinkedEvalCodeBlock* CodeCache::getEvalCodeBlock(VM& vm, EvalExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error, const VariableEnvironment* variablesUnderTDZ) +{ + return getGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, builtinMode, strictMode, thisTDZMode, debuggerMode, profilerMode, error, variablesUnderTDZ); +} + +// FIXME: There's no need to add the function's name to the key here. It's already in the source code. +UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(VM& vm, const Identifier& name, const SourceCode& source, ParserError& error) +{ + SourceCodeKey key = SourceCodeKey( + source, name.string(), SourceCodeKey::FunctionType, + JSParserBuiltinMode::NotBuiltin, + JSParserStrictMode::NotStrict); + SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key); + if (cache) + return jsCast<UnlinkedFunctionExecutable*>(cache->cell.get()); + + JSTextPosition positionBeforeLastNewline; + std::unique_ptr<ProgramNode> program = parse<ProgramNode>( + &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, + JSParserStrictMode::NotStrict, SourceParseMode::ProgramMode, + error, &positionBeforeLastNewline); + if (!program) { + RELEASE_ASSERT(error.isValid()); + return nullptr; + } + + // This function assumes an input string that would result in a single function declaration. + StatementNode* statement = program->singleStatement(); + ASSERT(statement); + ASSERT(statement->isBlock()); + if (!statement || !statement->isBlock()) + return nullptr; + + StatementNode* funcDecl = static_cast<BlockNode*>(statement)->singleStatement(); + ASSERT(funcDecl); + ASSERT(funcDecl->isFuncDeclNode()); + if (!funcDecl || !funcDecl->isFuncDeclNode()) + return nullptr; + + FunctionMetadataNode* metadata = static_cast<FuncDeclNode*>(funcDecl)->metadata(); + ASSERT(metadata); + if (!metadata) + return nullptr; + + metadata->setEndPosition(positionBeforeLastNewline); + // The Function constructor only has access to global variables, so no variables will be under TDZ. + VariableEnvironment emptyTDZVariables; + UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, ConstructAbility::CanConstruct, emptyTDZVariables); + functionExecutable->m_nameValue.set(vm, functionExecutable, jsString(&vm, name.string())); + + m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age())); + return functionExecutable; +} + +} diff --git a/Source/JavaScriptCore/runtime/CodeCache.h b/Source/JavaScriptCore/runtime/CodeCache.h new file mode 100644 index 000000000..af7dac7e4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CodeCache.h @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2012, 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef CodeCache_h +#define CodeCache_h + +#include "CodeSpecializationKind.h" +#include "ParserModes.h" +#include "SourceCode.h" +#include "Strong.h" +#include "VariableEnvironment.h" +#include "WeakRandom.h" +#include <wtf/CurrentTime.h> +#include <wtf/Forward.h> +#include <wtf/RandomNumber.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +class EvalExecutable; +class FunctionMetadataNode; +class Identifier; +class JSScope; +class ParserError; +class ProgramExecutable; +class UnlinkedCodeBlock; +class UnlinkedEvalCodeBlock; +class UnlinkedFunctionCodeBlock; +class UnlinkedFunctionExecutable; +class UnlinkedProgramCodeBlock; +class VM; +class SourceCode; +class SourceProvider; + +class SourceCodeKey { +public: + enum CodeType { EvalType, ProgramType, FunctionType }; + + SourceCodeKey() + { + } + + SourceCodeKey(const SourceCode& sourceCode, const String& name, CodeType codeType, JSParserBuiltinMode builtinMode, + JSParserStrictMode strictMode, ThisTDZMode thisTDZMode = ThisTDZMode::CheckIfNeeded) + : m_sourceCode(sourceCode) + , m_name(name) + , m_flags( + (static_cast<unsigned>(codeType) << 3) + | (static_cast<unsigned>(builtinMode) << 2) + | (static_cast<unsigned>(strictMode) << 1) + | static_cast<unsigned>(thisTDZMode)) + , m_hash(string().impl()->hash()) + { + } + + SourceCodeKey(WTF::HashTableDeletedValueType) + : m_sourceCode(WTF::HashTableDeletedValue) + { + } + + bool isHashTableDeletedValue() const { return m_sourceCode.isHashTableDeletedValue(); } + + unsigned hash() const { return m_hash; } + + size_t length() const { return m_sourceCode.length(); } + + bool isNull() const { return m_sourceCode.isNull(); } + + // To save memory, we compute our string on demand. It's expected that source + // providers cache their strings to make this efficient. + String string() const { return m_sourceCode.toString(); } + + bool operator==(const SourceCodeKey& other) const + { + return m_hash == other.m_hash + && length() == other.length() + && m_flags == other.m_flags + && m_name == other.m_name + && string() == other.string(); + } + +private: + SourceCode m_sourceCode; + String m_name; + unsigned m_flags; + unsigned m_hash; +}; + +struct SourceCodeKeyHash { + static unsigned hash(const SourceCodeKey& key) { return key.hash(); } + static bool equal(const SourceCodeKey& a, const SourceCodeKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +struct SourceCodeKeyHashTraits : SimpleClassHashTraits<SourceCodeKey> { + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const SourceCodeKey& sourceCodeKey) { return sourceCodeKey.isNull(); } +}; + +struct SourceCodeValue { + SourceCodeValue() + { + } + + SourceCodeValue(VM& vm, JSCell* cell, int64_t age) + : cell(vm, cell) + , age(age) + { + } + + Strong<JSCell> cell; + int64_t age; +}; + +class CodeCacheMap { +public: + typedef HashMap<SourceCodeKey, SourceCodeValue, SourceCodeKeyHash, SourceCodeKeyHashTraits> MapType; + typedef MapType::iterator iterator; + typedef MapType::AddResult AddResult; + + CodeCacheMap() + : m_size(0) + , m_sizeAtLastPrune(0) + , m_timeAtLastPrune(monotonicallyIncreasingTime()) + , m_minCapacity(0) + , m_capacity(0) + , m_age(0) + { + } + + SourceCodeValue* findCacheAndUpdateAge(const SourceCodeKey& key) + { + prune(); + + iterator findResult = m_map.find(key); + if (findResult == m_map.end()) + return nullptr; + + int64_t age = m_age - findResult->value.age; + if (age > m_capacity) { + // A requested object is older than the cache's capacity. We can + // infer that requested objects are subject to high eviction probability, + // so we grow the cache to improve our hit rate. + m_capacity += recencyBias * oldObjectSamplingMultiplier * key.length(); + } else if (age < m_capacity / 2) { + // A requested object is much younger than the cache's capacity. We can + // infer that requested objects are subject to low eviction probability, + // so we shrink the cache to save memory. + m_capacity -= recencyBias * key.length(); + if (m_capacity < m_minCapacity) + m_capacity = m_minCapacity; + } + + findResult->value.age = m_age; + m_age += key.length(); + + return &findResult->value; + } + + AddResult addCache(const SourceCodeKey& key, const SourceCodeValue& value) + { + prune(); + + AddResult addResult = m_map.add(key, value); + ASSERT(addResult.isNewEntry); + + m_size += key.length(); + m_age += key.length(); + return addResult; + } + + void remove(iterator it) + { + m_size -= it->key.length(); + m_map.remove(it); + } + + void clear() + { + m_size = 0; + m_age = 0; + m_map.clear(); + } + + int64_t age() { return m_age; } + +private: + // This constant factor biases cache capacity toward allowing a minimum + // working set to enter the cache before it starts evicting. + static const double workingSetTime; + static const int64_t workingSetMaxBytes = 16000000; + static const size_t workingSetMaxEntries = 2000; + + // This constant factor biases cache capacity toward recent activity. We + // want to adapt to changing workloads. + static const int64_t recencyBias = 4; + + // This constant factor treats a sampled event for one old object as if it + // happened for many old objects. Most old objects are evicted before we can + // sample them, so we need to extrapolate from the ones we do sample. + static const int64_t oldObjectSamplingMultiplier = 32; + + size_t numberOfEntries() const { return static_cast<size_t>(m_map.size()); } + bool canPruneQuickly() const { return numberOfEntries() < workingSetMaxEntries; } + + void pruneSlowCase(); + void prune() + { + if (m_size <= m_capacity && canPruneQuickly()) + return; + + if (monotonicallyIncreasingTime() - m_timeAtLastPrune < workingSetTime + && m_size - m_sizeAtLastPrune < workingSetMaxBytes + && canPruneQuickly()) + return; + + pruneSlowCase(); + } + + MapType m_map; + int64_t m_size; + int64_t m_sizeAtLastPrune; + double m_timeAtLastPrune; + int64_t m_minCapacity; + int64_t m_capacity; + int64_t m_age; +}; + +// Caches top-level code such as <script>, eval(), new Function, and JSEvaluateScript(). +class CodeCache { + WTF_MAKE_FAST_ALLOCATED; +public: + CodeCache(); + ~CodeCache(); + + UnlinkedProgramCodeBlock* getProgramCodeBlock(VM&, ProgramExecutable*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, DebuggerMode, ProfilerMode, ParserError&); + UnlinkedEvalCodeBlock* getEvalCodeBlock(VM&, EvalExecutable*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, ThisTDZMode, DebuggerMode, ProfilerMode, ParserError&, const VariableEnvironment*); + UnlinkedFunctionExecutable* getFunctionExecutableFromGlobalCode(VM&, const Identifier&, const SourceCode&, ParserError&); + + void clear() + { + m_sourceCode.clear(); + } + +private: + template <class UnlinkedCodeBlockType, class ExecutableType> + UnlinkedCodeBlockType* getGlobalCodeBlock(VM&, ExecutableType*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, ThisTDZMode, DebuggerMode, ProfilerMode, ParserError&, const VariableEnvironment*); + + CodeCacheMap m_sourceCode; +}; + +} + +#endif // CodeCache_h diff --git a/Source/JavaScriptCore/runtime/CodeSpecializationKind.cpp b/Source/JavaScriptCore/runtime/CodeSpecializationKind.cpp new file mode 100644 index 000000000..202a1d9c7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CodeSpecializationKind.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "CodeSpecializationKind.h" + +#include <wtf/PrintStream.h> + +namespace WTF { + +void printInternal(PrintStream& out, JSC::CodeSpecializationKind kind) +{ + if (kind == JSC::CodeForCall) { + out.print("Call"); + return; + } + + ASSERT(kind == JSC::CodeForConstruct); + out.print("Construct"); +} + +} // namespace WTF + + + diff --git a/Source/JavaScriptCore/runtime/CodeSpecializationKind.h b/Source/JavaScriptCore/runtime/CodeSpecializationKind.h new file mode 100644 index 000000000..5c408c6df --- /dev/null +++ b/Source/JavaScriptCore/runtime/CodeSpecializationKind.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef CodeSpecializationKind_h +#define CodeSpecializationKind_h + +namespace JSC { + +enum CodeSpecializationKind { CodeForCall, CodeForConstruct }; + +inline CodeSpecializationKind specializationFromIsCall(bool isCall) +{ + return isCall ? CodeForCall : CodeForConstruct; +} + +inline CodeSpecializationKind specializationFromIsConstruct(bool isConstruct) +{ + return isConstruct ? CodeForConstruct : CodeForCall; +} + +} // namespace JSC + +namespace WTF { + +class PrintStream; +void printInternal(PrintStream&, JSC::CodeSpecializationKind); + +} // namespace WTF + +#endif // CodeSpecializationKind_h + diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp new file mode 100644 index 000000000..fc6e62ca0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2003, 2007, 2009, 2012 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "CommonIdentifiers.h" + +#include "BuiltinNames.h" +#include "IdentifierInlines.h" +#include "JSCBuiltins.h" +#include "PrivateName.h" + +namespace JSC { + +#define INITIALIZE_PROPERTY_NAME(name) , name(Identifier::fromString(vm, #name)) +#define INITIALIZE_KEYWORD(name) , name##Keyword(Identifier::fromString(vm, #name)) +#define INITIALIZE_PRIVATE_NAME(name) , name##PrivateName(m_builtinNames->name##PrivateName()) +#define INITIALIZE_SYMBOL(name) , name##Symbol(m_builtinNames->name##Symbol()) + +CommonIdentifiers::CommonIdentifiers(VM* vm) + : nullIdentifier() + , emptyIdentifier(Identifier::EmptyIdentifier) + , underscoreProto(Identifier::fromString(vm, "__proto__")) + , thisIdentifier(Identifier::fromString(vm, "this")) + , useStrictIdentifier(Identifier::fromString(vm, "use strict")) + , timesIdentifier(Identifier::fromString(vm, "*")) + , m_builtinNames(new BuiltinNames(vm, this)) + JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(INITIALIZE_KEYWORD) + JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PRIVATE_NAME) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_SYMBOL) + , m_bytecodeIntrinsicRegistry(*this) +{ +} + +CommonIdentifiers::~CommonIdentifiers() +{ +} + +bool CommonIdentifiers::isPrivateName(SymbolImpl& uid) const +{ + return m_builtinNames->isPrivateName(uid); +} + +bool CommonIdentifiers::isPrivateName(UniquedStringImpl& uid) const +{ + return m_builtinNames->isPrivateName(uid); +} + +bool CommonIdentifiers::isPrivateName(const Identifier& ident) const +{ + return m_builtinNames->isPrivateName(ident); +} + +const Identifier* CommonIdentifiers::getPrivateName(const Identifier& ident) const +{ + return m_builtinNames->getPrivateName(ident); +} + +Identifier CommonIdentifiers::getPublicName(const Identifier& ident) const +{ + return m_builtinNames->getPublicName(ident); +} + + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h new file mode 100644 index 000000000..d7ae07795 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2003, 2007, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef CommonIdentifiers_h +#define CommonIdentifiers_h + +#include "BytecodeIntrinsicRegistry.h" +#include "Identifier.h" +#include <wtf/Noncopyable.h> + +// MarkedArgumentBuffer of property names, passed to a macro so we can do set them up various +// ways without repeating the list. +#define JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(macro) \ + macro(Array) \ + macro(ArrayBuffer) \ + macro(ArrayIterator) \ + macro(BYTES_PER_ELEMENT) \ + macro(Boolean) \ + macro(Collator) \ + macro(Date) \ + macro(DateTimeFormat) \ + macro(Error) \ + macro(EvalError) \ + macro(Function) \ + macro(Infinity) \ + macro(Intl) \ + macro(JSON) \ + macro(Map)\ + macro(MapIterator)\ + macro(Math) \ + macro(NaN) \ + macro(Number) \ + macro(NumberFormat) \ + macro(Object) \ + macro(Promise) \ + macro(RangeError) \ + macro(ReferenceError) \ + macro(Reflect) \ + macro(RegExp) \ + macro(Set)\ + macro(SetIterator)\ + macro(String) \ + macro(Symbol) \ + macro(SyntaxError) \ + macro(TypeError) \ + macro(URIError) \ + macro(UTC) \ + macro(WeakMap)\ + macro(WeakSet)\ + macro(__defineGetter__) \ + macro(__defineSetter__) \ + macro(__lookupGetter__) \ + macro(__lookupSetter__) \ + macro(add) \ + macro(additionalJettisonReason) \ + macro(anonymous) \ + macro(arguments) \ + macro(as) \ + macro(assign) \ + macro(back) \ + macro(bind) \ + macro(blur) \ + macro(buffer) \ + macro(byteLength) \ + macro(byteOffset) \ + macro(bytecode) \ + macro(bytecodeIndex) \ + macro(bytecodes) \ + macro(bytecodesID) \ + macro(callee) \ + macro(caller) \ + macro(clear) \ + macro(close) \ + macro(closed) \ + macro(column) \ + macro(compilationKind) \ + macro(compilations) \ + macro(compile) \ + macro(configurable) \ + macro(constructor) \ + macro(count) \ + macro(counters) \ + macro(defineProperty) \ + macro(description) \ + macro(descriptions) \ + macro(displayName) \ + macro(document) \ + macro(done) \ + macro(entries) \ + macro(enumerable) \ + macro(eval) \ + macro(exec) \ + macro(executionCount) \ + macro(exitKind) \ + macro(flags) \ + macro(focus) \ + macro(forEach) \ + macro(forward) \ + macro(from) \ + macro(fromCharCode) \ + macro(get) \ + macro(global) \ + macro(go) \ + macro(has) \ + macro(hasOwnProperty) \ + macro(hash) \ + macro(header) \ + macro(href) \ + macro(id) \ + macro(ignoreCase) \ + macro(index) \ + macro(indexedDB) \ + macro(inferredName) \ + macro(input) \ + macro(instructionCount) \ + macro(isArray) \ + macro(isPrototypeOf) \ + macro(isView) \ + macro(isWatchpoint) \ + macro(jettisonReason) \ + macro(join) \ + macro(keys) \ + macro(lastIndex) \ + macro(length) \ + macro(line) \ + macro(message) \ + macro(multiline) \ + macro(name) \ + macro(next) \ + macro(now) \ + macro(numInlinedCalls) \ + macro(numInlinedGetByIds) \ + macro(numInlinedPutByIds) \ + macro(of) \ + macro(opcode) \ + macro(origin) \ + macro(osrExitSites) \ + macro(osrExits) \ + macro(parse) \ + macro(parseInt) \ + macro(postMessage) \ + macro(profiledBytecodes) \ + macro(propertyIsEnumerable) \ + macro(prototype) \ + macro(raw) \ + macro(reload) \ + macro(replace) \ + macro(resolve) \ + macro(set) \ + macro(showModalDialog) \ + macro(size) \ + macro(slice) \ + macro(source) \ + macro(sourceURL) \ + macro(sourceCode) \ + macro(stack) \ + macro(subarray) \ + macro(target) \ + macro(test) \ + macro(then) \ + macro(toExponential) \ + macro(toFixed) \ + macro(toISOString) \ + macro(toJSON) \ + macro(toLocaleString) \ + macro(toPrecision) \ + macro(toString) \ + macro(value) \ + macro(valueOf) \ + macro(values) \ + macro(webkit) \ + macro(webkitIndexedDB) \ + macro(window) \ + macro(writable) + +#define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \ + macro(break) \ + macro(case) \ + macro(catch) \ + macro(class) \ + macro(const) \ + macro(continue) \ + macro(debugger) \ + macro(default) \ + macro(delete) \ + macro(do) \ + macro(else) \ + macro(enum) \ + macro(export) \ + macro(extends) \ + macro(false) \ + macro(finally) \ + macro(for) \ + macro(function) \ + macro(if) \ + macro(implements) \ + macro(import) \ + macro(in) \ + macro(instanceof) \ + macro(interface) \ + macro(let) \ + macro(new) \ + macro(null) \ + macro(package) \ + macro(private) \ + macro(protected) \ + macro(public) \ + macro(return) \ + macro(static) \ + macro(super) \ + macro(switch) \ + macro(this) \ + macro(throw) \ + macro(true) \ + macro(try) \ + macro(typeof) \ + macro(undefined) \ + macro(var) \ + macro(void) \ + macro(while) \ + macro(with) \ + macro(yield) + +#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL_NOT_IMPLEMENTED_YET(macro)\ + macro(hasInstance) \ + macro(isConcatSpreadable) \ + macro(match) \ + macro(replace) \ + macro(search) \ + macro(species) \ + macro(split) \ + macro(toPrimitive) \ + macro(toStringTag) + +#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \ + macro(iterator) \ + macro(unscopables) + +#define JSC_COMMON_BYTECODE_INTRINSICS_EACH_NAME(macro) \ + macro(putByValDirect) \ + macro(toString) + +#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(macro) \ + JSC_COMMON_BYTECODE_INTRINSICS_EACH_NAME(macro) \ + macro(iteratedObject) \ + macro(arrayIteratorNextIndex) \ + macro(arrayIterationKind) \ + macro(arrayIterationKindKey) \ + macro(arrayIterationKindValue) \ + macro(arrayIterationKindKeyValue) \ + macro(charCodeAt) \ + macro(iteratedString) \ + macro(stringIteratorNextIndex) \ + macro(promise) \ + macro(fulfillmentHandler) \ + macro(rejectionHandler) \ + macro(index) \ + macro(values) \ + macro(deferred) \ + macro(countdownHolder) \ + macro(Object) \ + macro(ownEnumerablePropertyKeys) \ + macro(Number) \ + macro(Array) \ + macro(String) \ + macro(Promise) \ + macro(abs) \ + macro(floor) \ + macro(isFinite) \ + macro(getPrototypeOf) \ + macro(getOwnPropertyNames) \ + macro(TypeError) \ + macro(undefined) \ + macro(BuiltinLog) \ + macro(homeObject) \ + macro(getTemplateObject) \ + macro(enqueueJob) \ + macro(handler) \ + macro(promiseState) \ + macro(promisePending) \ + macro(promiseFulfilled) \ + macro(promiseRejected) \ + macro(promiseFulfillReactions) \ + macro(promiseRejectReactions) \ + macro(promiseResult) \ + macro(capabilities) \ + macro(starDefault) \ + + +namespace JSC { + + class BuiltinNames; + + class CommonIdentifiers { + WTF_MAKE_NONCOPYABLE(CommonIdentifiers); WTF_MAKE_FAST_ALLOCATED; + private: + CommonIdentifiers(VM*); + ~CommonIdentifiers(); + friend class VM; + + public: + const BuiltinNames& builtinNames() const { return *m_builtinNames; } + const Identifier nullIdentifier; + const Identifier emptyIdentifier; + const Identifier underscoreProto; + const Identifier thisIdentifier; + const Identifier useStrictIdentifier; + const Identifier timesIdentifier; + private: + std::unique_ptr<BuiltinNames> m_builtinNames; + + public: + +#define JSC_IDENTIFIER_DECLARE_KEYWORD_NAME_GLOBAL(name) const Identifier name##Keyword; + JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(JSC_IDENTIFIER_DECLARE_KEYWORD_NAME_GLOBAL) +#undef JSC_IDENTIFIER_DECLARE_KEYWORD_NAME_GLOBAL + +#define JSC_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL(name) const Identifier name; + JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(JSC_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL) +#undef JSC_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL + +#define JSC_IDENTIFIER_DECLARE_PRIVATE_PROPERTY_NAME_GLOBAL(name) const Identifier name##PrivateName; + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(JSC_IDENTIFIER_DECLARE_PRIVATE_PROPERTY_NAME_GLOBAL) +#undef JSC_IDENTIFIER_DECLARE_PRIVATE_PROPERTY_NAME_GLOBAL + +#define JSC_IDENTIFIER_DECLARE_PRIVATE_WELL_KNOWN_SYMBOL_GLOBAL(name) const Identifier name##Symbol; + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(JSC_IDENTIFIER_DECLARE_PRIVATE_WELL_KNOWN_SYMBOL_GLOBAL) +#undef JSC_IDENTIFIER_DECLARE_PRIVATE_WELL_KNOWN_SYMBOL_GLOBAL + + bool isPrivateName(SymbolImpl& uid) const; + bool isPrivateName(UniquedStringImpl& uid) const; + bool isPrivateName(const Identifier&) const; + + const Identifier* getPrivateName(const Identifier&) const; + Identifier getPublicName(const Identifier&) const; + + const BytecodeIntrinsicRegistry& bytecodeIntrinsicRegistry() const { return m_bytecodeIntrinsicRegistry; } + + private: + BytecodeIntrinsicRegistry m_bytecodeIntrinsicRegistry; + }; + +} // namespace JSC + +#endif // CommonIdentifiers_h diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp new file mode 100644 index 000000000..acbe14629 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2011-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "CommonSlowPaths.h" +#include "ArityCheckFailReturnThunks.h" +#include "ArrayConstructor.h" +#include "CallFrame.h" +#include "ClonedArguments.h" +#include "CodeProfiling.h" +#include "CommonSlowPathsExceptions.h" +#include "DirectArguments.h" +#include "Error.h" +#include "ErrorHandlingScope.h" +#include "ExceptionFuzz.h" +#include "GetterSetter.h" +#include "HostCallReturnValue.h" +#include "Interpreter.h" +#include "JIT.h" +#include "JITStubs.h" +#include "JSCInlines.h" +#include "JSCJSValue.h" +#include "JSGlobalObjectFunctions.h" +#include "JSLexicalEnvironment.h" +#include "JSPropertyNameEnumerator.h" +#include "JSString.h" +#include "JSWithScope.h" +#include "LLIntCommon.h" +#include "LLIntExceptions.h" +#include "LowLevelInterpreter.h" +#include "ObjectConstructor.h" +#include "ScopedArguments.h" +#include "StructureRareDataInlines.h" +#include "TypeProfilerLog.h" +#include <wtf/StringPrintStream.h> + +namespace JSC { + +#define BEGIN_NO_SET_PC() \ + VM& vm = exec->vm(); \ + NativeCallFrameTracer tracer(&vm, exec) + +#ifndef NDEBUG +#define SET_PC_FOR_STUBS() do { \ + exec->codeBlock()->bytecodeOffset(pc); \ + exec->setCurrentVPC(pc + 1); \ + } while (false) +#else +#define SET_PC_FOR_STUBS() do { \ + exec->setCurrentVPC(pc + 1); \ + } while (false) +#endif + +#define RETURN_TO_THROW(exec, pc) pc = LLInt::returnToThrow(exec) + +#define BEGIN() \ + BEGIN_NO_SET_PC(); \ + SET_PC_FOR_STUBS() + +#define OP(index) (exec->uncheckedR(pc[index].u.operand)) +#define OP_C(index) (exec->r(pc[index].u.operand)) + +#define RETURN_TWO(first, second) do { \ + return encodeResult(first, second); \ + } while (false) + +#define END_IMPL() RETURN_TWO(pc, exec) + +#define THROW(exceptionToThrow) do { \ + vm.throwException(exec, exceptionToThrow); \ + RETURN_TO_THROW(exec, pc); \ + END_IMPL(); \ + } while (false) + +#define CHECK_EXCEPTION() do { \ + doExceptionFuzzingIfEnabled(exec, "CommonSlowPaths", pc); \ + if (UNLIKELY(vm.exception())) { \ + RETURN_TO_THROW(exec, pc); \ + END_IMPL(); \ + } \ + } while (false) + +#define END() do { \ + CHECK_EXCEPTION(); \ + END_IMPL(); \ + } while (false) + +#define BRANCH(opcode, condition) do { \ + bool bCondition = (condition); \ + CHECK_EXCEPTION(); \ + if (bCondition) \ + pc += pc[OPCODE_LENGTH(opcode) - 1].u.operand; \ + else \ + pc += OPCODE_LENGTH(opcode); \ + END_IMPL(); \ + } while (false) + +#define RETURN(value) do { \ + JSValue rReturnValue = (value); \ + CHECK_EXCEPTION(); \ + OP(1) = rReturnValue; \ + END_IMPL(); \ + } while (false) + +#define RETURN_PROFILED(opcode, value) do { \ + JSValue rpPeturnValue = (value); \ + CHECK_EXCEPTION(); \ + OP(1) = rpPeturnValue; \ + PROFILE_VALUE(opcode, rpPeturnValue); \ + END_IMPL(); \ + } while (false) + +#define PROFILE_VALUE(opcode, value) do { \ + pc[OPCODE_LENGTH(opcode) - 1].u.profile->m_buckets[0] = \ + JSValue::encode(value); \ + } while (false) + +#define CALL_END_IMPL(exec, callTarget) RETURN_TWO((callTarget), (exec)) + +#define CALL_THROW(exec, pc, exceptionToThrow) do { \ + ExecState* ctExec = (exec); \ + Instruction* ctPC = (pc); \ + vm.throwException(exec, exceptionToThrow); \ + CALL_END_IMPL(ctExec, LLInt::callToThrow(ctExec)); \ + } while (false) + +#define CALL_CHECK_EXCEPTION(exec, pc) do { \ + ExecState* cceExec = (exec); \ + Instruction* ccePC = (pc); \ + if (UNLIKELY(vm.exception())) \ + CALL_END_IMPL(cceExec, LLInt::callToThrow(cceExec)); \ + } while (false) + +#define CALL_RETURN(exec, pc, callTarget) do { \ + ExecState* crExec = (exec); \ + Instruction* crPC = (pc); \ + void* crCallTarget = (callTarget); \ + CALL_CHECK_EXCEPTION(crExec->callerFrame(), crPC); \ + CALL_END_IMPL(crExec, crCallTarget); \ + } while (false) + +static CommonSlowPaths::ArityCheckData* setupArityCheckData(VM& vm, int slotsToAdd) +{ + CommonSlowPaths::ArityCheckData* result = vm.arityCheckData.get(); + result->paddedStackSpace = slotsToAdd; +#if ENABLE(JIT) + if (vm.canUseJIT()) { + result->thunkToCall = vm.getCTIStub(arityFixupGenerator).code().executableAddress(); + result->returnPC = vm.arityCheckFailReturnThunks->returnPCFor(vm, slotsToAdd * stackAlignmentRegisters()).executableAddress(); + } else +#endif + { + result->thunkToCall = 0; + result->returnPC = 0; + } + return result; +} + +SLOW_PATH_DECL(slow_path_call_arityCheck) +{ + BEGIN(); + int slotsToAdd = CommonSlowPaths::arityCheckFor(exec, &vm.interpreter->stack(), CodeForCall); + if (slotsToAdd < 0) { + exec = exec->callerFrame(); + ErrorHandlingScope errorScope(exec->vm()); + CommonSlowPaths::interpreterThrowInCaller(exec, createStackOverflowError(exec)); + RETURN_TWO(bitwise_cast<void*>(static_cast<uintptr_t>(1)), exec); + } + RETURN_TWO(0, setupArityCheckData(vm, slotsToAdd)); +} + +SLOW_PATH_DECL(slow_path_construct_arityCheck) +{ + BEGIN(); + int slotsToAdd = CommonSlowPaths::arityCheckFor(exec, &vm.interpreter->stack(), CodeForConstruct); + if (slotsToAdd < 0) { + exec = exec->callerFrame(); + ErrorHandlingScope errorScope(exec->vm()); + CommonSlowPaths::interpreterThrowInCaller(exec, createStackOverflowError(exec)); + RETURN_TWO(bitwise_cast<void*>(static_cast<uintptr_t>(1)), exec); + } + RETURN_TWO(0, setupArityCheckData(vm, slotsToAdd)); +} + +SLOW_PATH_DECL(slow_path_create_direct_arguments) +{ + BEGIN(); + RETURN(DirectArguments::createByCopying(exec)); +} + +SLOW_PATH_DECL(slow_path_create_scoped_arguments) +{ + BEGIN(); + JSLexicalEnvironment* scope = jsCast<JSLexicalEnvironment*>(OP(2).jsValue()); + ScopedArgumentsTable* table = scope->symbolTable()->arguments(); + RETURN(ScopedArguments::createByCopying(exec, table, scope)); +} + +SLOW_PATH_DECL(slow_path_create_out_of_band_arguments) +{ + BEGIN(); + RETURN(ClonedArguments::createWithMachineFrame(exec, exec, ArgumentsMode::Cloned)); +} + +SLOW_PATH_DECL(slow_path_create_this) +{ + BEGIN(); + JSFunction* constructor = jsCast<JSFunction*>(OP(2).jsValue().asCell()); + +#if !ASSERT_DISABLED + ConstructData constructData; + ASSERT(constructor->methodTable()->getConstructData(constructor, constructData) == ConstructTypeJS); +#endif + + auto& cacheWriteBarrier = pc[4].u.jsCell; + if (!cacheWriteBarrier) + cacheWriteBarrier.set(exec->vm(), exec->codeBlock()->ownerExecutable(), constructor); + else if (cacheWriteBarrier.unvalidatedGet() != JSCell::seenMultipleCalleeObjects() && cacheWriteBarrier.get() != constructor) + cacheWriteBarrier.setWithoutWriteBarrier(JSCell::seenMultipleCalleeObjects()); + + size_t inlineCapacity = pc[3].u.operand; + Structure* structure = constructor->rareData(exec, inlineCapacity)->allocationProfile()->structure(); + RETURN(constructEmptyObject(exec, structure)); +} + +SLOW_PATH_DECL(slow_path_to_this) +{ + BEGIN(); + JSValue v1 = OP(1).jsValue(); + if (v1.isCell()) { + Structure* myStructure = v1.asCell()->structure(vm); + Structure* otherStructure = pc[2].u.structure.get(); + if (myStructure != otherStructure) { + if (otherStructure) + pc[3].u.toThisStatus = ToThisConflicted; + pc[2].u.structure.set(vm, exec->codeBlock()->ownerExecutable(), myStructure); + } + } else { + pc[3].u.toThisStatus = ToThisConflicted; + pc[2].u.structure.clear(); + } + RETURN(v1.toThis(exec, exec->codeBlock()->isStrictMode() ? StrictMode : NotStrictMode)); +} + +SLOW_PATH_DECL(slow_path_throw_tdz_error) +{ + BEGIN(); + THROW(createTDZError(exec)); +} + +SLOW_PATH_DECL(slow_path_not) +{ + BEGIN(); + RETURN(jsBoolean(!OP_C(2).jsValue().toBoolean(exec))); +} + +SLOW_PATH_DECL(slow_path_eq) +{ + BEGIN(); + RETURN(jsBoolean(JSValue::equal(exec, OP_C(2).jsValue(), OP_C(3).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_neq) +{ + BEGIN(); + RETURN(jsBoolean(!JSValue::equal(exec, OP_C(2).jsValue(), OP_C(3).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_stricteq) +{ + BEGIN(); + RETURN(jsBoolean(JSValue::strictEqual(exec, OP_C(2).jsValue(), OP_C(3).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_nstricteq) +{ + BEGIN(); + RETURN(jsBoolean(!JSValue::strictEqual(exec, OP_C(2).jsValue(), OP_C(3).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_less) +{ + BEGIN(); + RETURN(jsBoolean(jsLess<true>(exec, OP_C(2).jsValue(), OP_C(3).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_lesseq) +{ + BEGIN(); + RETURN(jsBoolean(jsLessEq<true>(exec, OP_C(2).jsValue(), OP_C(3).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_greater) +{ + BEGIN(); + RETURN(jsBoolean(jsLess<false>(exec, OP_C(3).jsValue(), OP_C(2).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_greatereq) +{ + BEGIN(); + RETURN(jsBoolean(jsLessEq<false>(exec, OP_C(3).jsValue(), OP_C(2).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_inc) +{ + BEGIN(); + RETURN(jsNumber(OP(1).jsValue().toNumber(exec) + 1)); +} + +SLOW_PATH_DECL(slow_path_dec) +{ + BEGIN(); + RETURN(jsNumber(OP(1).jsValue().toNumber(exec) - 1)); +} + +SLOW_PATH_DECL(slow_path_to_number) +{ + BEGIN(); + RETURN(jsNumber(OP_C(2).jsValue().toNumber(exec))); +} + +SLOW_PATH_DECL(slow_path_to_string) +{ + BEGIN(); + RETURN(OP_C(2).jsValue().toString(exec)); +} + +SLOW_PATH_DECL(slow_path_negate) +{ + BEGIN(); + RETURN(jsNumber(-OP_C(2).jsValue().toNumber(exec))); +} + +SLOW_PATH_DECL(slow_path_add) +{ + BEGIN(); + JSValue v1 = OP_C(2).jsValue(); + JSValue v2 = OP_C(3).jsValue(); + + if (v1.isString() && !v2.isObject()) + RETURN(jsString(exec, asString(v1), v2.toString(exec))); + + if (v1.isNumber() && v2.isNumber()) + RETURN(jsNumber(v1.asNumber() + v2.asNumber())); + + RETURN(jsAddSlowCase(exec, v1, v2)); +} + +// The following arithmetic and bitwise operations need to be sure to run +// toNumber() on their operands in order. (A call to toNumber() is idempotent +// if an exception is already set on the ExecState.) + +SLOW_PATH_DECL(slow_path_mul) +{ + BEGIN(); + double a = OP_C(2).jsValue().toNumber(exec); + double b = OP_C(3).jsValue().toNumber(exec); + RETURN(jsNumber(a * b)); +} + +SLOW_PATH_DECL(slow_path_sub) +{ + BEGIN(); + double a = OP_C(2).jsValue().toNumber(exec); + double b = OP_C(3).jsValue().toNumber(exec); + RETURN(jsNumber(a - b)); +} + +SLOW_PATH_DECL(slow_path_div) +{ + BEGIN(); + double a = OP_C(2).jsValue().toNumber(exec); + double b = OP_C(3).jsValue().toNumber(exec); + RETURN(jsNumber(a / b)); +} + +SLOW_PATH_DECL(slow_path_mod) +{ + BEGIN(); + double a = OP_C(2).jsValue().toNumber(exec); + double b = OP_C(3).jsValue().toNumber(exec); + RETURN(jsNumber(fmod(a, b))); +} + +SLOW_PATH_DECL(slow_path_lshift) +{ + BEGIN(); + int32_t a = OP_C(2).jsValue().toInt32(exec); + uint32_t b = OP_C(3).jsValue().toUInt32(exec); + RETURN(jsNumber(a << (b & 31))); +} + +SLOW_PATH_DECL(slow_path_rshift) +{ + BEGIN(); + int32_t a = OP_C(2).jsValue().toInt32(exec); + uint32_t b = OP_C(3).jsValue().toUInt32(exec); + RETURN(jsNumber(a >> (b & 31))); +} + +SLOW_PATH_DECL(slow_path_urshift) +{ + BEGIN(); + uint32_t a = OP_C(2).jsValue().toUInt32(exec); + uint32_t b = OP_C(3).jsValue().toUInt32(exec); + RETURN(jsNumber(static_cast<int32_t>(a >> (b & 31)))); +} + +SLOW_PATH_DECL(slow_path_unsigned) +{ + BEGIN(); + uint32_t a = OP_C(2).jsValue().toUInt32(exec); + RETURN(jsNumber(a)); +} + +SLOW_PATH_DECL(slow_path_bitand) +{ + BEGIN(); + int32_t a = OP_C(2).jsValue().toInt32(exec); + int32_t b = OP_C(3).jsValue().toInt32(exec); + RETURN(jsNumber(a & b)); +} + +SLOW_PATH_DECL(slow_path_bitor) +{ + BEGIN(); + int32_t a = OP_C(2).jsValue().toInt32(exec); + int32_t b = OP_C(3).jsValue().toInt32(exec); + RETURN(jsNumber(a | b)); +} + +SLOW_PATH_DECL(slow_path_bitxor) +{ + BEGIN(); + int32_t a = OP_C(2).jsValue().toInt32(exec); + int32_t b = OP_C(3).jsValue().toInt32(exec); + RETURN(jsNumber(a ^ b)); +} + +SLOW_PATH_DECL(slow_path_typeof) +{ + BEGIN(); + RETURN(jsTypeStringForValue(exec, OP_C(2).jsValue())); +} + +SLOW_PATH_DECL(slow_path_is_object_or_null) +{ + BEGIN(); + RETURN(jsBoolean(jsIsObjectTypeOrNull(exec, OP_C(2).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_is_function) +{ + BEGIN(); + RETURN(jsBoolean(jsIsFunctionType(OP_C(2).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_in) +{ + BEGIN(); + RETURN(jsBoolean(CommonSlowPaths::opIn(exec, OP_C(2).jsValue(), OP_C(3).jsValue()))); +} + +SLOW_PATH_DECL(slow_path_del_by_val) +{ + BEGIN(); + JSValue baseValue = OP_C(2).jsValue(); + JSObject* baseObject = baseValue.toObject(exec); + + JSValue subscript = OP_C(3).jsValue(); + + bool couldDelete; + + uint32_t i; + if (subscript.getUInt32(i)) + couldDelete = baseObject->methodTable()->deletePropertyByIndex(baseObject, exec, i); + else { + CHECK_EXCEPTION(); + auto property = subscript.toPropertyKey(exec); + CHECK_EXCEPTION(); + couldDelete = baseObject->methodTable()->deleteProperty(baseObject, exec, property); + } + + if (!couldDelete && exec->codeBlock()->isStrictMode()) + THROW(createTypeError(exec, "Unable to delete property.")); + + RETURN(jsBoolean(couldDelete)); +} + +SLOW_PATH_DECL(slow_path_strcat) +{ + BEGIN(); + RETURN(jsStringFromRegisterArray(exec, &OP(2), pc[3].u.operand)); +} + +SLOW_PATH_DECL(slow_path_to_primitive) +{ + BEGIN(); + RETURN(OP_C(2).jsValue().toPrimitive(exec)); +} + +SLOW_PATH_DECL(slow_path_enter) +{ + BEGIN(); + ScriptExecutable* ownerExecutable = exec->codeBlock()->ownerExecutable(); + Heap::heap(ownerExecutable)->writeBarrier(ownerExecutable); + END(); +} + +SLOW_PATH_DECL(slow_path_get_enumerable_length) +{ + BEGIN(); + JSValue enumeratorValue = OP(2).jsValue(); + if (enumeratorValue.isUndefinedOrNull()) + RETURN(jsNumber(0)); + + JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(enumeratorValue.asCell()); + + RETURN(jsNumber(enumerator->indexedLength())); +} + +SLOW_PATH_DECL(slow_path_has_indexed_property) +{ + BEGIN(); + JSObject* base = OP(2).jsValue().toObject(exec); + JSValue property = OP(3).jsValue(); + pc[4].u.arrayProfile->observeStructure(base->structure(vm)); + ASSERT(property.isUInt32()); + RETURN(jsBoolean(base->hasProperty(exec, property.asUInt32()))); +} + +SLOW_PATH_DECL(slow_path_has_structure_property) +{ + BEGIN(); + JSObject* base = OP(2).jsValue().toObject(exec); + JSValue property = OP(3).jsValue(); + ASSERT(property.isString()); + JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(OP(4).jsValue().asCell()); + if (base->structure(vm)->id() == enumerator->cachedStructureID()) + RETURN(jsBoolean(true)); + RETURN(jsBoolean(base->hasProperty(exec, asString(property.asCell())->toIdentifier(exec)))); +} + +SLOW_PATH_DECL(slow_path_has_generic_property) +{ + BEGIN(); + JSObject* base = OP(2).jsValue().toObject(exec); + JSValue property = OP(3).jsValue(); + bool result; + if (property.isString()) + result = base->hasProperty(exec, asString(property.asCell())->toIdentifier(exec)); + else { + ASSERT(property.isUInt32()); + result = base->hasProperty(exec, property.asUInt32()); + } + RETURN(jsBoolean(result)); +} + +SLOW_PATH_DECL(slow_path_get_direct_pname) +{ + BEGIN(); + JSValue baseValue = OP_C(2).jsValue(); + JSValue property = OP(3).jsValue(); + ASSERT(property.isString()); + RETURN(baseValue.get(exec, asString(property)->toIdentifier(exec))); +} + +SLOW_PATH_DECL(slow_path_get_property_enumerator) +{ + BEGIN(); + JSValue baseValue = OP(2).jsValue(); + if (baseValue.isUndefinedOrNull()) + RETURN(JSPropertyNameEnumerator::create(vm)); + + JSObject* base = baseValue.toObject(exec); + + RETURN(propertyNameEnumerator(exec, base)); +} + +SLOW_PATH_DECL(slow_path_next_structure_enumerator_pname) +{ + BEGIN(); + JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(OP(2).jsValue().asCell()); + uint32_t index = OP(3).jsValue().asUInt32(); + + JSString* propertyName = nullptr; + if (index < enumerator->endStructurePropertyIndex()) + propertyName = enumerator->propertyNameAtIndex(index); + RETURN(propertyName ? propertyName : jsNull()); +} + +SLOW_PATH_DECL(slow_path_next_generic_enumerator_pname) +{ + BEGIN(); + JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(OP(2).jsValue().asCell()); + uint32_t index = OP(3).jsValue().asUInt32(); + + JSString* propertyName = nullptr; + if (enumerator->endStructurePropertyIndex() <= index && index < enumerator->endGenericPropertyIndex()) + propertyName = enumerator->propertyNameAtIndex(index); + RETURN(propertyName ? propertyName : jsNull()); +} + +SLOW_PATH_DECL(slow_path_to_index_string) +{ + BEGIN(); + RETURN(jsString(exec, Identifier::from(exec, OP(2).jsValue().asUInt32()).string())); +} + +SLOW_PATH_DECL(slow_path_profile_type_clear_log) +{ + BEGIN(); + vm.typeProfilerLog()->processLogEntries(ASCIILiteral("LLInt log full.")); + END(); +} + +SLOW_PATH_DECL(slow_path_create_lexical_environment) +{ + BEGIN(); + int scopeReg = pc[2].u.operand; + JSScope* currentScope = exec->uncheckedR(scopeReg).Register::scope(); + SymbolTable* symbolTable = jsCast<SymbolTable*>(OP_C(3).jsValue()); + JSValue initialValue = OP_C(4).jsValue(); + ASSERT(initialValue == jsUndefined() || initialValue == jsTDZValue()); + JSScope* newScope = JSLexicalEnvironment::create(vm, exec->lexicalGlobalObject(), currentScope, symbolTable, initialValue); + RETURN(newScope); +} + +SLOW_PATH_DECL(slow_path_push_with_scope) +{ + BEGIN(); + JSObject* newScope = OP_C(2).jsValue().toObject(exec); + CHECK_EXCEPTION(); + + int scopeReg = pc[3].u.operand; + JSScope* currentScope = exec->uncheckedR(scopeReg).Register::scope(); + RETURN(JSWithScope::create(exec, newScope, currentScope)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h new file mode 100644 index 000000000..067df0c38 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2011-2013, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef CommonSlowPaths_h +#define CommonSlowPaths_h + +#include "CodeBlock.h" +#include "CodeSpecializationKind.h" +#include "ExceptionHelpers.h" +#include "JSStackInlines.h" +#include "StackAlignment.h" +#include "Symbol.h" +#include "VM.h" +#include <wtf/StdLibExtras.h> + +namespace JSC { + +// The purpose of this namespace is to include slow paths that are shared +// between the interpreter and baseline JIT. They are written to be agnostic +// with respect to the slow-path calling convention, but they do rely on the +// JS code being executed more-or-less directly from bytecode (so the call +// frame layout is unmodified, making it potentially awkward to use these +// from any optimizing JIT, like the DFG). + +namespace CommonSlowPaths { + +struct ArityCheckData { + unsigned paddedStackSpace; + void* thunkToCall; + void* returnPC; +}; + +ALWAYS_INLINE int arityCheckFor(ExecState* exec, JSStack* stack, CodeSpecializationKind kind) +{ + JSFunction* callee = jsCast<JSFunction*>(exec->callee()); + ASSERT(!callee->isHostFunction()); + CodeBlock* newCodeBlock = callee->jsExecutable()->codeBlockFor(kind); + int argumentCountIncludingThis = exec->argumentCountIncludingThis(); + + ASSERT(argumentCountIncludingThis < newCodeBlock->numParameters()); + int missingArgumentCount = newCodeBlock->numParameters() - argumentCountIncludingThis; + int neededStackSpace = missingArgumentCount + 1; // Allow space to save the original return PC. + int paddedStackSpace = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), neededStackSpace); + + if (!stack->ensureCapacityFor(exec->registers() - paddedStackSpace)) + return -1; + return paddedStackSpace / stackAlignmentRegisters(); +} + +inline bool opIn(ExecState* exec, JSValue propName, JSValue baseVal) +{ + if (!baseVal.isObject()) { + exec->vm().throwException(exec, createInvalidInParameterError(exec, baseVal)); + return false; + } + + JSObject* baseObj = asObject(baseVal); + + uint32_t i; + if (propName.getUInt32(i)) + return baseObj->hasProperty(exec, i); + + auto property = propName.toPropertyKey(exec); + if (exec->vm().exception()) + return false; + return baseObj->hasProperty(exec, property); +} + +inline void tryCachePutToScopeGlobal( + ExecState* exec, CodeBlock* codeBlock, Instruction* pc, JSObject* scope, + ResolveModeAndType modeAndType, PutPropertySlot& slot) +{ + // Covers implicit globals. Since they don't exist until they first execute, we didn't know how to cache them at compile time. + + if (modeAndType.type() != GlobalProperty && modeAndType.type() != GlobalPropertyWithVarInjectionChecks) + return; + + if (!slot.isCacheablePut() + || slot.base() != scope + || !scope->structure()->propertyAccessesAreCacheable()) + return; + + if (slot.type() == PutPropertySlot::NewProperty) { + // Don't cache if we've done a transition. We want to detect the first replace so that we + // can invalidate the watchpoint. + return; + } + + scope->structure()->didCachePropertyReplacement(exec->vm(), slot.cachedOffset()); + + ConcurrentJITLocker locker(codeBlock->m_lock); + pc[5].u.structure.set(exec->vm(), codeBlock->ownerExecutable(), scope->structure()); + pc[6].u.operand = slot.cachedOffset(); +} + +} // namespace CommonSlowPaths + +class ExecState; +struct Instruction; + +#if USE(JSVALUE64) +// According to C++ rules, a type used for the return signature of function with C linkage (i.e. +// 'extern "C"') needs to be POD; hence putting any constructors into it could cause either compiler +// warnings, or worse, a change in the ABI used to return these types. +struct SlowPathReturnType { + void* a; + void* b; +}; + +inline SlowPathReturnType encodeResult(void* a, void* b) +{ + SlowPathReturnType result; + result.a = a; + result.b = b; + return result; +} + +inline void decodeResult(SlowPathReturnType result, void*& a, void*& b) +{ + a = result.a; + b = result.b; +} + +#else // USE(JSVALUE32_64) +typedef int64_t SlowPathReturnType; + +typedef union { + struct { + void* a; + void* b; + } pair; + int64_t i; +} SlowPathReturnTypeEncoding; + +inline SlowPathReturnType encodeResult(void* a, void* b) +{ + SlowPathReturnTypeEncoding u; + u.pair.a = a; + u.pair.b = b; + return u.i; +} + +inline void decodeResult(SlowPathReturnType result, void*& a, void*& b) +{ + SlowPathReturnTypeEncoding u; + u.i = result; + a = u.pair.a; + b = u.pair.b; +} +#endif // USE(JSVALUE32_64) + +#define SLOW_PATH + +#define SLOW_PATH_DECL(name) \ +extern "C" SlowPathReturnType SLOW_PATH name(ExecState* exec, Instruction* pc) + +#define SLOW_PATH_HIDDEN_DECL(name) \ +SLOW_PATH_DECL(name) WTF_INTERNAL + +SLOW_PATH_HIDDEN_DECL(slow_path_call_arityCheck); +SLOW_PATH_HIDDEN_DECL(slow_path_construct_arityCheck); +SLOW_PATH_HIDDEN_DECL(slow_path_create_direct_arguments); +SLOW_PATH_HIDDEN_DECL(slow_path_create_scoped_arguments); +SLOW_PATH_HIDDEN_DECL(slow_path_create_out_of_band_arguments); +SLOW_PATH_HIDDEN_DECL(slow_path_create_this); +SLOW_PATH_HIDDEN_DECL(slow_path_enter); +SLOW_PATH_HIDDEN_DECL(slow_path_get_callee); +SLOW_PATH_HIDDEN_DECL(slow_path_to_this); +SLOW_PATH_HIDDEN_DECL(slow_path_throw_tdz_error); +SLOW_PATH_HIDDEN_DECL(slow_path_not); +SLOW_PATH_HIDDEN_DECL(slow_path_eq); +SLOW_PATH_HIDDEN_DECL(slow_path_neq); +SLOW_PATH_HIDDEN_DECL(slow_path_stricteq); +SLOW_PATH_HIDDEN_DECL(slow_path_nstricteq); +SLOW_PATH_HIDDEN_DECL(slow_path_less); +SLOW_PATH_HIDDEN_DECL(slow_path_lesseq); +SLOW_PATH_HIDDEN_DECL(slow_path_greater); +SLOW_PATH_HIDDEN_DECL(slow_path_greatereq); +SLOW_PATH_HIDDEN_DECL(slow_path_inc); +SLOW_PATH_HIDDEN_DECL(slow_path_dec); +SLOW_PATH_HIDDEN_DECL(slow_path_to_number); +SLOW_PATH_HIDDEN_DECL(slow_path_to_string); +SLOW_PATH_HIDDEN_DECL(slow_path_negate); +SLOW_PATH_HIDDEN_DECL(slow_path_add); +SLOW_PATH_HIDDEN_DECL(slow_path_mul); +SLOW_PATH_HIDDEN_DECL(slow_path_sub); +SLOW_PATH_HIDDEN_DECL(slow_path_div); +SLOW_PATH_HIDDEN_DECL(slow_path_mod); +SLOW_PATH_HIDDEN_DECL(slow_path_lshift); +SLOW_PATH_HIDDEN_DECL(slow_path_rshift); +SLOW_PATH_HIDDEN_DECL(slow_path_urshift); +SLOW_PATH_HIDDEN_DECL(slow_path_unsigned); +SLOW_PATH_HIDDEN_DECL(slow_path_bitand); +SLOW_PATH_HIDDEN_DECL(slow_path_bitor); +SLOW_PATH_HIDDEN_DECL(slow_path_bitxor); +SLOW_PATH_HIDDEN_DECL(slow_path_typeof); +SLOW_PATH_HIDDEN_DECL(slow_path_is_object); +SLOW_PATH_HIDDEN_DECL(slow_path_is_object_or_null); +SLOW_PATH_HIDDEN_DECL(slow_path_is_function); +SLOW_PATH_HIDDEN_DECL(slow_path_in); +SLOW_PATH_HIDDEN_DECL(slow_path_del_by_val); +SLOW_PATH_HIDDEN_DECL(slow_path_strcat); +SLOW_PATH_HIDDEN_DECL(slow_path_to_primitive); +SLOW_PATH_HIDDEN_DECL(slow_path_get_enumerable_length); +SLOW_PATH_HIDDEN_DECL(slow_path_has_generic_property); +SLOW_PATH_HIDDEN_DECL(slow_path_has_structure_property); +SLOW_PATH_HIDDEN_DECL(slow_path_has_indexed_property); +SLOW_PATH_HIDDEN_DECL(slow_path_get_direct_pname); +SLOW_PATH_HIDDEN_DECL(slow_path_get_property_enumerator); +SLOW_PATH_HIDDEN_DECL(slow_path_next_structure_enumerator_pname); +SLOW_PATH_HIDDEN_DECL(slow_path_next_generic_enumerator_pname); +SLOW_PATH_HIDDEN_DECL(slow_path_to_index_string); +SLOW_PATH_HIDDEN_DECL(slow_path_profile_type_clear_log); +SLOW_PATH_HIDDEN_DECL(slow_path_create_lexical_environment); +SLOW_PATH_HIDDEN_DECL(slow_path_push_with_scope); + +} // namespace JSC + +#endif // CommonSlowPaths_h diff --git a/Source/JavaScriptCore/runtime/CommonSlowPathsExceptions.cpp b/Source/JavaScriptCore/runtime/CommonSlowPathsExceptions.cpp new file mode 100644 index 000000000..f586ed089 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonSlowPathsExceptions.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "CommonSlowPathsExceptions.h" + +#include "CallFrame.h" +#include "CodeBlock.h" +#include "JITExceptions.h" +#include "LLIntCommon.h" +#include "JSCInlines.h" + +namespace JSC { namespace CommonSlowPaths { + +void interpreterThrowInCaller(ExecState* exec, JSObject* error) +{ + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + vm->throwException(exec, error); +#if LLINT_SLOW_PATH_TRACING + dataLog("Throwing exception ", vm->exception(), ".\n"); +#endif +} + +} } // namespace JSC::LLInt diff --git a/Source/JavaScriptCore/runtime/CommonSlowPathsExceptions.h b/Source/JavaScriptCore/runtime/CommonSlowPathsExceptions.h new file mode 100644 index 000000000..adcbfd47d --- /dev/null +++ b/Source/JavaScriptCore/runtime/CommonSlowPathsExceptions.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef CommonSlowPathExceptions_h +#define CommonSlowPathExceptions_h + +#include "MacroAssemblerCodeRef.h" + +namespace JSC { + +class ExecState; + +namespace CommonSlowPaths { + +// Throw the currently active exception in the context of the caller's call frame. +void interpreterThrowInCaller(ExecState* callerFrame, JSObject*); + +} } // namespace JSC::CommonSlowPaths + +#endif // CommonSlowPathExceptions_h diff --git a/Source/JavaScriptCore/runtime/CompilationResult.cpp b/Source/JavaScriptCore/runtime/CompilationResult.cpp new file mode 100644 index 000000000..878892fec --- /dev/null +++ b/Source/JavaScriptCore/runtime/CompilationResult.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "CompilationResult.h" + +namespace WTF { + +using namespace JSC; + +void printInternal(PrintStream& out, CompilationResult result) +{ + switch (result) { + case CompilationFailed: + out.print("CompilationFailed"); + return; + case CompilationInvalidated: + out.print("CompilationInvalidated"); + return; + case CompilationSuccessful: + out.print("CompilationSuccessful"); + return; + case CompilationDeferred: + out.print("CompilationDeferred"); + return; + } + + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/runtime/CompilationResult.h b/Source/JavaScriptCore/runtime/CompilationResult.h new file mode 100644 index 000000000..db6a0c46a --- /dev/null +++ b/Source/JavaScriptCore/runtime/CompilationResult.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef CompilationResult_h +#define CompilationResult_h + +#include <wtf/PrintStream.h> + +namespace JSC { + +enum CompilationResult { + // We tried to compile the code, but we couldn't compile it. This could be + // because we ran out of memory, or because the compiler encountered an + // internal error and decided to bail out gracefully. Either way, this implies + // that we shouldn't try to compile this code block again. + CompilationFailed, + + // The profiling assumptions that were fed into the compiler were invalidated + // even before we finished compiling. This means we should try again: in such + // cases the profiling will now be updated and the next compilation won't + // encounter the same problem. But it does mean that we should exercise + // exponential back-off, to get even more profiling so that new profiling + // pathologies aren't encountered. + CompilationInvalidated, + + // The compilation succeeded and the code block now has JITCode for the newly + // compiled code. However, compilation success doesn't mean that the CodeBlock + // will execute yet; you typically have to install it first, unless you plan + // on invoking it manually (something that *could* be done for some kinds of + // OSR entry). + CompilationSuccessful, + + // We decided to do the compilation asynchronously. This means that we haven't + // yet compiled the code. This only happens when you pass a + // DeferredCompilationCallback. That callback will get called with some + // interesting result, once compilation completes. + CompilationDeferred +}; + +} // namespace JSC + +namespace WTF { + +void printInternal(PrintStream&, JSC::CompilationResult); + +} // namespace WTF + +#endif // CompilationResult_h + diff --git a/Source/JavaScriptCore/runtime/Completion.cpp b/Source/JavaScriptCore/runtime/Completion.cpp new file mode 100644 index 000000000..a3daee603 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Completion.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007, 2013 Apple Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Completion.h" + +#include "CallFrame.h" +#include "CodeProfiling.h" +#include "Debugger.h" +#include "Exception.h" +#include "Interpreter.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "JSCInlines.h" +#include "ModuleAnalyzer.h" +#include "ModuleRecord.h" +#include "Parser.h" +#include <wtf/WTFThreadData.h> + +namespace JSC { + +bool checkSyntax(ExecState* exec, const SourceCode& source, JSValue* returnedException) +{ + JSLockHolder lock(exec); + RELEASE_ASSERT(exec->vm().atomicStringTable() == wtfThreadData().atomicStringTable()); + + ProgramExecutable* program = ProgramExecutable::create(exec, source); + JSObject* error = program->checkSyntax(exec); + if (error) { + if (returnedException) + *returnedException = error; + return false; + } + + return true; +} + +bool checkSyntax(VM& vm, const SourceCode& source, ParserError& error) +{ + JSLockHolder lock(vm); + RELEASE_ASSERT(vm.atomicStringTable() == wtfThreadData().atomicStringTable()); + return !!parse<ProgramNode>( + &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, + JSParserStrictMode::NotStrict, SourceParseMode::ProgramMode, error); +} + +bool checkModuleSyntax(VM& vm, const SourceCode& source, ParserError& error) +{ + JSLockHolder lock(vm); + RELEASE_ASSERT(vm.atomicStringTable() == wtfThreadData().atomicStringTable()); + std::unique_ptr<ModuleProgramNode> moduleProgramNode = parse<ModuleProgramNode>( + &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, + JSParserStrictMode::Strict, SourceParseMode::ModuleAnalyzeMode, error); + if (!moduleProgramNode) + return false; + + ModuleAnalyzer moduleAnalyzer(vm, moduleProgramNode->varDeclarations(), moduleProgramNode->lexicalVariables()); + moduleAnalyzer.analyze(*moduleProgramNode); + return true; +} + +JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException) +{ + JSLockHolder lock(exec); + RELEASE_ASSERT(exec->vm().atomicStringTable() == wtfThreadData().atomicStringTable()); + RELEASE_ASSERT(!exec->vm().isCollectorBusy()); + + CodeProfiling profile(source); + + ProgramExecutable* program = ProgramExecutable::create(exec, source); + if (!program) { + returnedException = exec->vm().exception(); + exec->vm().clearException(); + return jsUndefined(); + } + + if (!thisValue || thisValue.isUndefinedOrNull()) + thisValue = exec->vmEntryGlobalObject(); + JSObject* thisObj = jsCast<JSObject*>(thisValue.toThis(exec, NotStrictMode)); + JSValue result = exec->interpreter()->execute(program, exec, thisObj); + + if (exec->hadException()) { + returnedException = exec->exception(); + exec->clearException(); + return jsUndefined(); + } + + RELEASE_ASSERT(result); + return result; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Completion.h b/Source/JavaScriptCore/runtime/Completion.h new file mode 100644 index 000000000..702ed9fab --- /dev/null +++ b/Source/JavaScriptCore/runtime/Completion.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef Completion_h +#define Completion_h + +#include "JSCJSValue.h" +#include <wtf/NakedPtr.h> + +namespace JSC { + +class Exception; +class ExecState; +class JSScope; +class ParserError; +class SourceCode; +class VM; + +JS_EXPORT_PRIVATE bool checkSyntax(VM&, const SourceCode&, ParserError&); +JS_EXPORT_PRIVATE bool checkSyntax(ExecState*, const SourceCode&, JSValue* exception = 0); +JS_EXPORT_PRIVATE bool checkModuleSyntax(VM&, const SourceCode&, ParserError&); +JS_EXPORT_PRIVATE JSValue evaluate(ExecState*, const SourceCode&, JSValue thisValue, NakedPtr<Exception>& returnedException); +inline JSValue evaluate(ExecState* exec, const SourceCode& sourceCode, JSValue thisValue = JSValue()) +{ + NakedPtr<Exception> unused; + return evaluate(exec, sourceCode, thisValue, unused); +} + +} // namespace JSC + +#endif // Completion_h diff --git a/Source/JavaScriptCore/runtime/ConcurrentJITLock.h b/Source/JavaScriptCore/runtime/ConcurrentJITLock.h new file mode 100644 index 000000000..9a26876fc --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConcurrentJITLock.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ConcurrentJITLock_h +#define ConcurrentJITLock_h + +#include "DeferGC.h" +#include <wtf/Lock.h> +#include <wtf/NoLock.h> + +namespace JSC { + +#if ENABLE(CONCURRENT_JIT) +typedef Lock ConcurrentJITLock; +typedef LockHolder ConcurrentJITLockerImpl; +#else +typedef NoLock ConcurrentJITLock; +typedef NoLockLocker ConcurrentJITLockerImpl; +#endif + +class ConcurrentJITLockerBase { + WTF_MAKE_NONCOPYABLE(ConcurrentJITLockerBase); +public: + explicit ConcurrentJITLockerBase(ConcurrentJITLock& lockable) + : m_locker(&lockable) + { + } + explicit ConcurrentJITLockerBase(ConcurrentJITLock* lockable) + : m_locker(lockable) + { + } + + ~ConcurrentJITLockerBase() + { + } + + void unlockEarly() + { + m_locker.unlockEarly(); + } + +private: + ConcurrentJITLockerImpl m_locker; +}; + +class GCSafeConcurrentJITLocker : public ConcurrentJITLockerBase { +public: + GCSafeConcurrentJITLocker(ConcurrentJITLock& lockable, Heap& heap) + : ConcurrentJITLockerBase(lockable) + , m_deferGC(heap) + { + } + + GCSafeConcurrentJITLocker(ConcurrentJITLock* lockable, Heap& heap) + : ConcurrentJITLockerBase(lockable) + , m_deferGC(heap) + { + } + + ~GCSafeConcurrentJITLocker() + { + // We have to unlock early due to the destruction order of base + // vs. derived classes. If we didn't, then we would destroy the + // DeferGC object before unlocking the lock which could cause a GC + // and resulting deadlock. + unlockEarly(); + } + +private: +#if ENABLE(CONCURRENT_JIT) + DeferGC m_deferGC; +#else + struct NoDefer { + NoDefer(Heap& heap) : m_heap(heap) { } + Heap& m_heap; + }; + NoDefer m_deferGC; +#endif +}; + +class ConcurrentJITLocker : public ConcurrentJITLockerBase { +public: + ConcurrentJITLocker(ConcurrentJITLock& lockable) + : ConcurrentJITLockerBase(lockable) + { + } + + ConcurrentJITLocker(ConcurrentJITLock* lockable) + : ConcurrentJITLockerBase(lockable) + { + } + +#if ENABLE(CONCURRENT_JIT) && !defined(NDEBUG) +private: + DisallowGC m_disallowGC; +#endif +}; + +} // namespace JSC + +#endif // ConcurrentJITLock_h diff --git a/Source/JavaScriptCore/runtime/ConsoleClient.cpp b/Source/JavaScriptCore/runtime/ConsoleClient.cpp new file mode 100644 index 000000000..b255aa208 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConsoleClient.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ConsoleClient.h" + +#include "ScriptArguments.h" +#include "ScriptCallStack.h" +#include "ScriptCallStackFactory.h" +#include "ScriptValue.h" +#include <wtf/Assertions.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/WTFString.h> + +using namespace Inspector; + +namespace JSC { + +static void appendURLAndPosition(StringBuilder& builder, const String& url, unsigned lineNumber, unsigned columnNumber) +{ + if (url.isEmpty()) + return; + + builder.append(url); + + if (lineNumber > 0) { + builder.append(':'); + builder.appendNumber(lineNumber); + } + + if (columnNumber > 0) { + builder.append(':'); + builder.appendNumber(columnNumber); + } +} + +static void appendMessagePrefix(StringBuilder& builder, MessageSource source, MessageType type, MessageLevel level) +{ + const char* sourceString; + switch (source) { + case MessageSource::XML: + sourceString = "XML"; + break; + case MessageSource::JS: + sourceString = "JS"; + break; + case MessageSource::Network: + sourceString = "NETWORK"; + break; + case MessageSource::ConsoleAPI: + sourceString = "CONSOLE"; + break; + case MessageSource::Storage: + sourceString = "STORAGE"; + break; + case MessageSource::AppCache: + sourceString = "APPCACHE"; + break; + case MessageSource::Rendering: + sourceString = "RENDERING"; + break; + case MessageSource::CSS: + sourceString = "CSS"; + break; + case MessageSource::Security: + sourceString = "SECURITY"; + break; + case MessageSource::Other: + sourceString = "OTHER"; + break; + default: + ASSERT_NOT_REACHED(); + sourceString = "UNKNOWN"; + break; + } + + const char* levelString; + switch (level) { + case MessageLevel::Debug: + levelString = "DEBUG"; + break; + case MessageLevel::Log: + levelString = "LOG"; + break; + case MessageLevel::Info: + levelString = "INFO"; + break; + case MessageLevel::Warning: + levelString = "WARN"; + break; + case MessageLevel::Error: + levelString = "ERROR"; + break; + default: + ASSERT_NOT_REACHED(); + levelString = "UNKNOWN"; + break; + } + + if (type == MessageType::Trace) + levelString = "TRACE"; + else if (type == MessageType::Table) + levelString = "TABLE"; + + builder.append(sourceString); + builder.append(' '); + builder.append(levelString); +} + +void ConsoleClient::printConsoleMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& url, unsigned lineNumber, unsigned columnNumber) +{ + StringBuilder builder; + + if (!url.isEmpty()) { + appendURLAndPosition(builder, url, lineNumber, columnNumber); + builder.appendLiteral(": "); + } + + appendMessagePrefix(builder, source, type, level); + builder.append(' '); + builder.append(message); + + WTFLogAlways("%s", builder.toString().utf8().data()); +} + +void ConsoleClient::printConsoleMessageWithArguments(MessageSource source, MessageType type, MessageLevel level, JSC::ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + bool isTraceMessage = type == MessageType::Trace; + size_t stackSize = isTraceMessage ? ScriptCallStack::maxCallStackSizeToCapture : 1; + RefPtr<ScriptCallStack> callStack(createScriptCallStackForConsole(exec, stackSize)); + const ScriptCallFrame& lastCaller = callStack->at(0); + + StringBuilder builder; + + if (!lastCaller.sourceURL().isEmpty()) { + appendURLAndPosition(builder, lastCaller.sourceURL(), lastCaller.lineNumber(), lastCaller.columnNumber()); + builder.appendLiteral(": "); + } + + appendMessagePrefix(builder, source, type, level); + for (size_t i = 0; i < arguments->argumentCount(); ++i) { + String argAsString = arguments->argumentAt(i).toString(arguments->globalState()); + builder.append(' '); + builder.append(argAsString.utf8().data()); + } + + WTFLogAlways("%s", builder.toString().utf8().data()); + + if (isTraceMessage) { + for (size_t i = 0; i < callStack->size(); ++i) { + const ScriptCallFrame& callFrame = callStack->at(i); + String functionName = String(callFrame.functionName()); + if (functionName.isEmpty()) + functionName = ASCIILiteral("(unknown)"); + + StringBuilder callFrameBuilder; + callFrameBuilder.appendNumber(static_cast<unsigned long>(i)); + callFrameBuilder.appendLiteral(": "); + callFrameBuilder.append(functionName); + callFrameBuilder.append('('); + appendURLAndPosition(callFrameBuilder, callFrame.sourceURL(), callFrame.lineNumber(), callFrame.columnNumber()); + callFrameBuilder.append(')'); + + WTFLogAlways("%s", callFrameBuilder.toString().utf8().data()); + } + } +} + +void ConsoleClient::internalMessageWithTypeAndLevel(MessageType type, MessageLevel level, JSC::ExecState* exec, RefPtr<ScriptArguments>&& arguments, ArgumentRequirement argumentRequirement) +{ + if (argumentRequirement == ArgumentRequired && !arguments->argumentCount()) + return; + + messageWithTypeAndLevel(type, level, exec, WTF::move(arguments)); +} + +void ConsoleClient::logWithLevel(ExecState* exec, RefPtr<ScriptArguments>&& arguments, MessageLevel level) +{ + internalMessageWithTypeAndLevel(MessageType::Log, level, exec, WTF::move(arguments), ArgumentRequired); +} + +void ConsoleClient::clear(ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + internalMessageWithTypeAndLevel(MessageType::Clear, MessageLevel::Log, exec, WTF::move(arguments), ArgumentNotRequired); +} + +void ConsoleClient::dir(ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + internalMessageWithTypeAndLevel(MessageType::Dir, MessageLevel::Log, exec, WTF::move(arguments), ArgumentRequired); +} + +void ConsoleClient::dirXML(ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + internalMessageWithTypeAndLevel(MessageType::DirXML, MessageLevel::Log, exec, WTF::move(arguments), ArgumentRequired); +} + +void ConsoleClient::table(ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + internalMessageWithTypeAndLevel(MessageType::Table, MessageLevel::Log, exec, WTF::move(arguments), ArgumentRequired); +} + +void ConsoleClient::trace(ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + internalMessageWithTypeAndLevel(MessageType::Trace, MessageLevel::Log, exec, WTF::move(arguments), ArgumentNotRequired); +} + +void ConsoleClient::assertCondition(ExecState* exec, RefPtr<ScriptArguments>&& arguments, bool condition) +{ + if (condition) + return; + + internalMessageWithTypeAndLevel(MessageType::Assert, MessageLevel::Error, exec, WTF::move(arguments), ArgumentNotRequired); +} + +void ConsoleClient::group(ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + internalMessageWithTypeAndLevel(MessageType::StartGroup, MessageLevel::Log, exec, WTF::move(arguments), ArgumentNotRequired); +} + +void ConsoleClient::groupCollapsed(ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + internalMessageWithTypeAndLevel(MessageType::StartGroupCollapsed, MessageLevel::Log, exec, WTF::move(arguments), ArgumentNotRequired); +} + +void ConsoleClient::groupEnd(ExecState* exec, RefPtr<ScriptArguments>&& arguments) +{ + internalMessageWithTypeAndLevel(MessageType::EndGroup, MessageLevel::Log, exec, WTF::move(arguments), ArgumentNotRequired); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ConsoleClient.h b/Source/JavaScriptCore/runtime/ConsoleClient.h new file mode 100644 index 000000000..a16b85bb4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConsoleClient.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ConsoleClient_h +#define ConsoleClient_h + +#include "ConsoleTypes.h" +#include <wtf/Forward.h> + +namespace Inspector { +class ScriptArguments; +} + +namespace JSC { + +class ExecState; + +class ConsoleClient { +public: + virtual ~ConsoleClient() { } + + JS_EXPORT_PRIVATE static void printConsoleMessage(MessageSource, MessageType, MessageLevel, const String& message, const String& url, unsigned lineNumber, unsigned columnNumber); + JS_EXPORT_PRIVATE static void printConsoleMessageWithArguments(MessageSource, MessageType, MessageLevel, JSC::ExecState*, RefPtr<Inspector::ScriptArguments>&&); + + void logWithLevel(ExecState*, RefPtr<Inspector::ScriptArguments>&&, MessageLevel); + void clear(ExecState*, RefPtr<Inspector::ScriptArguments>&&); + void dir(ExecState*, RefPtr<Inspector::ScriptArguments>&&); + void dirXML(ExecState*, RefPtr<Inspector::ScriptArguments>&&); + void table(ExecState*, RefPtr<Inspector::ScriptArguments>&&); + void trace(ExecState*, RefPtr<Inspector::ScriptArguments>&&); + void assertCondition(ExecState*, RefPtr<Inspector::ScriptArguments>&&, bool condition); + void group(ExecState*, RefPtr<Inspector::ScriptArguments>&&); + void groupCollapsed(ExecState*, RefPtr<Inspector::ScriptArguments>&&); + void groupEnd(ExecState*, RefPtr<Inspector::ScriptArguments>&&); + + virtual void messageWithTypeAndLevel(MessageType, MessageLevel, JSC::ExecState*, RefPtr<Inspector::ScriptArguments>&&) = 0; + virtual void count(ExecState*, RefPtr<Inspector::ScriptArguments>&&) = 0; + virtual void profile(ExecState*, const String& title) = 0; + virtual void profileEnd(ExecState*, const String& title) = 0; + virtual void time(ExecState*, const String& title) = 0; + virtual void timeEnd(ExecState*, const String& title) = 0; + virtual void timeStamp(ExecState*, RefPtr<Inspector::ScriptArguments>&&) = 0; + +private: + enum ArgumentRequirement { ArgumentRequired, ArgumentNotRequired }; + void internalMessageWithTypeAndLevel(MessageType, MessageLevel, JSC::ExecState*, RefPtr<Inspector::ScriptArguments>&&, ArgumentRequirement); +}; + +} // namespace JSC + +#endif // ConsoleClient_h diff --git a/Source/JavaScriptCore/runtime/ConsolePrototype.cpp b/Source/JavaScriptCore/runtime/ConsolePrototype.cpp new file mode 100644 index 000000000..7cad2c9de --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConsolePrototype.cpp @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ConsolePrototype.h" + +#include "ConsoleClient.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSCInlines.h" +#include "JSConsole.h" +#include "ScriptArguments.h" +#include "ScriptCallStackFactory.h" + +namespace JSC { + +const ClassInfo ConsolePrototype::s_info = { "ConsolePrototype", &Base::s_info, 0, CREATE_METHOD_TABLE(ConsolePrototype) }; + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncDebug(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncError(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncLog(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncInfo(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncWarn(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncClear(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncDir(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncDirXML(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTable(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTrace(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncAssert(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncCount(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncProfile(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncProfileEnd(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTime(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTimeEnd(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTimeStamp(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroup(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroupCollapsed(ExecState*); +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroupEnd(ExecState*); + +void ConsolePrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + // For legacy reasons, console properties are enumerable, writable, deleteable, + // and all have a length of 0. This may change if Console is standardized. + + JSC_NATIVE_FUNCTION("debug", consoleProtoFuncDebug, None, 0); + JSC_NATIVE_FUNCTION("error", consoleProtoFuncError, None, 0); + JSC_NATIVE_FUNCTION("log", consoleProtoFuncLog, None, 0); + JSC_NATIVE_FUNCTION("info", consoleProtoFuncInfo, None, 0); + JSC_NATIVE_FUNCTION("warn", consoleProtoFuncWarn, None, 0); + + JSC_NATIVE_FUNCTION("clear", consoleProtoFuncClear, None, 0); + JSC_NATIVE_FUNCTION("dir", consoleProtoFuncDir, None, 0); + JSC_NATIVE_FUNCTION("dirxml", consoleProtoFuncDirXML, None, 0); + JSC_NATIVE_FUNCTION("table", consoleProtoFuncTable, None, 0); + JSC_NATIVE_FUNCTION("trace", consoleProtoFuncTrace, None, 0); + JSC_NATIVE_FUNCTION("assert", consoleProtoFuncAssert, None, 0); + JSC_NATIVE_FUNCTION("count", consoleProtoFuncCount, None, 0); + JSC_NATIVE_FUNCTION("profile", consoleProtoFuncProfile, None, 0); + JSC_NATIVE_FUNCTION("profileEnd", consoleProtoFuncProfileEnd, None, 0); + JSC_NATIVE_FUNCTION("time", consoleProtoFuncTime, None, 0); + JSC_NATIVE_FUNCTION("timeEnd", consoleProtoFuncTimeEnd, None, 0); + JSC_NATIVE_FUNCTION("timeStamp", consoleProtoFuncTimeStamp, None, 0); + JSC_NATIVE_FUNCTION("group", consoleProtoFuncGroup, None, 0); + JSC_NATIVE_FUNCTION("groupCollapsed", consoleProtoFuncGroupCollapsed, None, 0); + JSC_NATIVE_FUNCTION("groupEnd", consoleProtoFuncGroupEnd, None, 0); +} + +static String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value) +{ + if (value.isUndefinedOrNull()) + return String(); + return value.toString(exec)->value(exec); +} + +static EncodedJSValue consoleLogWithLevel(ExecState* exec, MessageLevel level) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->logWithLevel(exec, arguments.release(), level); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncDebug(ExecState* exec) +{ + return consoleLogWithLevel(exec, MessageLevel::Debug); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncError(ExecState* exec) +{ + return consoleLogWithLevel(exec, MessageLevel::Error); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncLog(ExecState* exec) +{ + return consoleLogWithLevel(exec, MessageLevel::Log); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncInfo(ExecState* exec) +{ + return consoleLogWithLevel(exec, MessageLevel::Info); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncWarn(ExecState* exec) +{ + return consoleLogWithLevel(exec, MessageLevel::Warning); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncClear(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->clear(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncDir(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->dir(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncDirXML(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->dirXML(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTable(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->table(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTrace(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->trace(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncAssert(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + bool condition(exec->argument(0).toBoolean(exec)); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 1)); + client->assertCondition(exec, arguments.release(), condition); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncCount(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->count(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncProfile(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + size_t argsCount = exec->argumentCount(); + if (argsCount <= 0) { + client->profile(exec, String()); + return JSValue::encode(jsUndefined()); + } + + const String& title(valueToStringWithUndefinedOrNullCheck(exec, exec->argument(0))); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + client->profile(exec, title); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncProfileEnd(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + size_t argsCount = exec->argumentCount(); + if (argsCount <= 0) { + client->profileEnd(exec, String()); + return JSValue::encode(jsUndefined()); + } + + const String& title(valueToStringWithUndefinedOrNullCheck(exec, exec->argument(0))); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + client->profileEnd(exec, title); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTime(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + if (exec->argumentCount() < 1) + return throwVMError(exec, createNotEnoughArgumentsError(exec)); + + const String& title(valueToStringWithUndefinedOrNullCheck(exec, exec->argument(0))); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + client->time(exec, title); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTimeEnd(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + if (exec->argumentCount() < 1) + return throwVMError(exec, createNotEnoughArgumentsError(exec)); + + const String& title(valueToStringWithUndefinedOrNullCheck(exec, exec->argument(0))); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + client->timeEnd(exec, title); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTimeStamp(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->timeStamp(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroup(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->group(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroupCollapsed(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->groupCollapsed(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroupEnd(ExecState* exec) +{ + JSConsole* castedThis = jsDynamicCast<JSConsole*>(exec->thisValue()); + if (!castedThis) + return throwVMTypeError(exec); + ASSERT_GC_OBJECT_INHERITS(castedThis, JSConsole::info()); + ConsoleClient* client = castedThis->globalObject()->consoleClient(); + if (!client) + return JSValue::encode(jsUndefined()); + + RefPtr<Inspector::ScriptArguments> arguments(Inspector::createScriptArguments(exec, 0)); + client->groupEnd(exec, arguments.release()); + return JSValue::encode(jsUndefined()); +} + +} diff --git a/Source/JavaScriptCore/runtime/ConsolePrototype.h b/Source/JavaScriptCore/runtime/ConsolePrototype.h new file mode 100644 index 000000000..a27ad24ad --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConsolePrototype.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ConsolePrototype_h +#define ConsolePrototype_h + +#include "JSObject.h" + +namespace JSC { + +class ConsolePrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + DECLARE_INFO; + + static ConsolePrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + ConsolePrototype* prototype = new (NotNull, allocateCell<ConsolePrototype>(vm.heap)) ConsolePrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + ConsolePrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); +}; + +} + +#endif // !defined(ConsolePrototype_h) diff --git a/Source/JavaScriptCore/runtime/ConsoleTypes.h b/Source/JavaScriptCore/runtime/ConsoleTypes.h new file mode 100644 index 000000000..c6ecabda9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConsoleTypes.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2012 Google Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef ConsoleTypes_h +#define ConsoleTypes_h + +namespace JSC { + +enum class MessageSource { + XML, + JS, + Network, + ConsoleAPI, + Storage, + AppCache, + Rendering, + CSS, + Security, + ContentBlocker, + Other, +}; + +enum class MessageType { + Log, + Dir, + DirXML, + Table, + Trace, + StartGroup, + StartGroupCollapsed, + EndGroup, + Clear, + Assert, + Timing, + Profile, + ProfileEnd, +}; + +enum class MessageLevel { + Log = 1, + Warning = 2, + Error = 3, + Debug = 4, + Info = 5, +}; + +} // namespace JSC + +using JSC::MessageSource; +using JSC::MessageType; +using JSC::MessageLevel; + +#endif // ConsoleTypes_h diff --git a/Source/JavaScriptCore/runtime/ConstantMode.cpp b/Source/JavaScriptCore/runtime/ConstantMode.cpp new file mode 100644 index 000000000..58ba79475 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConstantMode.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ConstantMode.h" + +namespace WTF { + +using namespace JSC; + +void printInternal(PrintStream& out, ConstantMode mode) +{ + switch (mode) { + case IsConstant: + out.print("Constant"); + return; + case IsVariable: + out.print("Variable"); + return; + } + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/runtime/ConstantMode.h b/Source/JavaScriptCore/runtime/ConstantMode.h new file mode 100644 index 000000000..a0496278a --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConstantMode.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ConstantMode_h +#define ConstantMode_h + +#include <wtf/PrintStream.h> + +namespace JSC { + +enum ConstantMode { IsConstant, IsVariable }; + +inline ConstantMode modeForIsConstant(bool isConstant) +{ + return isConstant ? IsConstant : IsVariable; +} + +} // namespace JSC + +namespace WTF { + +void printInternal(PrintStream&, JSC::ConstantMode); + +} // namespace WTF + +#endif // ConstantMode_h + diff --git a/Source/JavaScriptCore/runtime/ConstructAbility.h b/Source/JavaScriptCore/runtime/ConstructAbility.h new file mode 100644 index 000000000..3af880b60 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConstructAbility.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ConstructAbility_h +#define ConstructAbility_h + +namespace JSC { + +enum class ConstructAbility : unsigned { + CanConstruct, + CannotConstruct, +}; + +} + +#endif // ConstructAbility_h diff --git a/Source/JavaScriptCore/runtime/ConstructData.cpp b/Source/JavaScriptCore/runtime/ConstructData.cpp new file mode 100644 index 000000000..6010e6356 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConstructData.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ConstructData.h" + +#include "Executable.h" +#include "Interpreter.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSCInlines.h" + +namespace JSC { + +JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget) +{ + ASSERT(constructType == ConstructTypeJS || constructType == ConstructTypeHost); + return exec->interpreter()->executeConstruct(exec, asObject(constructorObject), constructType, constructData, args, newTarget); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ConstructData.h b/Source/JavaScriptCore/runtime/ConstructData.h new file mode 100644 index 000000000..17e1a5dae --- /dev/null +++ b/Source/JavaScriptCore/runtime/ConstructData.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef ConstructData_h +#define ConstructData_h + +#include "CallData.h" +#include "JSCJSValue.h" + +namespace JSC { + +class ArgList; +class ExecState; +class FunctionExecutable; +class JSObject; +class JSScope; + +enum ConstructType { + ConstructTypeNone, + ConstructTypeHost, + ConstructTypeJS +}; + +union ConstructData { + struct { + NativeFunction function; + } native; + struct { + FunctionExecutable* functionExecutable; + JSScope* scope; + } js; +}; + +JS_EXPORT_PRIVATE JSObject* construct(ExecState*, JSValue constructor, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget); + +ALWAYS_INLINE JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args) +{ + return construct(exec, constructorObject, constructType, constructData, args, constructorObject); +} + +} // namespace JSC + +#endif // ConstructData_h diff --git a/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp b/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp new file mode 100644 index 000000000..d80e7f3ac --- /dev/null +++ b/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * Copyright (C) 2014 Saam Barati. <saambarati1@gmail.com> + * + * 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ControlFlowProfiler.h" + +#include "VM.h" + +namespace JSC { + +ControlFlowProfiler::ControlFlowProfiler() + : m_dummyBasicBlock(BasicBlockLocation(-1, -1)) +{ +} + +ControlFlowProfiler::~ControlFlowProfiler() +{ + for (const BlockLocationCache& cache : m_sourceIDBuckets.values()) { + for (BasicBlockLocation* block : cache.values()) + delete block; + } +} + +BasicBlockLocation* ControlFlowProfiler::getBasicBlockLocation(intptr_t sourceID, int startOffset, int endOffset) +{ + auto addResult = m_sourceIDBuckets.add(sourceID, BlockLocationCache()); + BlockLocationCache& blockLocationCache = addResult.iterator->value; + BasicBlockKey key(startOffset, endOffset); + auto addResultForBasicBlock = blockLocationCache.add(key, nullptr); + if (addResultForBasicBlock.isNewEntry) + addResultForBasicBlock.iterator->value = new BasicBlockLocation(startOffset, endOffset); + return addResultForBasicBlock.iterator->value; +} + +void ControlFlowProfiler::dumpData() const +{ + auto iter = m_sourceIDBuckets.begin(); + auto end = m_sourceIDBuckets.end(); + for (; iter != end; ++iter) { + dataLog("SourceID: ", iter->key, "\n"); + for (const BasicBlockLocation* block : iter->value.values()) + block->dumpData(); + } +} + +Vector<BasicBlockRange> ControlFlowProfiler::getBasicBlocksForSourceID(intptr_t sourceID, VM& vm) const +{ + Vector<BasicBlockRange> result(0); + auto bucketFindResult = m_sourceIDBuckets.find(sourceID); + if (bucketFindResult == m_sourceIDBuckets.end()) + return result; + + const BlockLocationCache& cache = bucketFindResult->value; + for (const BasicBlockLocation* block : cache.values()) { + bool hasExecuted = block->hasExecuted(); + const Vector<BasicBlockLocation::Gap>& blockRanges = block->getExecutedRanges(); + for (BasicBlockLocation::Gap gap : blockRanges) { + BasicBlockRange range; + range.m_hasExecuted = hasExecuted; + range.m_startOffset = gap.first; + range.m_endOffset = gap.second; + result.append(range); + } + } + + const Vector<std::tuple<bool, unsigned, unsigned>>& unexecutedFunctionRanges = vm.functionHasExecutedCache()->getFunctionRanges(sourceID); + for (const auto& functionRange : unexecutedFunctionRanges) { + BasicBlockRange range; + range.m_hasExecuted = std::get<0>(functionRange); + range.m_startOffset = static_cast<int>(std::get<1>(functionRange)); + range.m_endOffset = static_cast<int>(std::get<2>(functionRange)); + result.append(range); + } + + return result; +} + +bool ControlFlowProfiler::hasBasicBlockAtTextOffsetBeenExecuted(int offset, intptr_t sourceID, VM& vm) +{ + const Vector<BasicBlockRange>& blocks = getBasicBlocksForSourceID(sourceID, vm); + int bestDistance = INT_MAX; + BasicBlockRange bestRange; + bestRange.m_startOffset = bestRange.m_endOffset = -1; + bestRange.m_hasExecuted = false; // Suppress MSVC warning. + // Because some ranges may overlap because of function boundaries, make sure to find the smallest range enclosing the offset. + for (BasicBlockRange range : blocks) { + if (range.m_startOffset <= offset && offset <= range.m_endOffset && (range.m_endOffset - range.m_startOffset) < bestDistance) { + RELEASE_ASSERT(range.m_endOffset - range.m_startOffset >= 0); + bestDistance = range.m_endOffset - range.m_startOffset; + bestRange = range; + } + } + + RELEASE_ASSERT(bestRange.m_startOffset != -1 && bestRange.m_endOffset != -1); + return bestRange.m_hasExecuted; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ControlFlowProfiler.h b/Source/JavaScriptCore/runtime/ControlFlowProfiler.h new file mode 100644 index 000000000..81749dc37 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ControlFlowProfiler.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * Copyright (C) 2014 Saam Barati. <saambarati1@gmail.com> + * + * 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ControlFlowProfiler_h +#define ControlFlowProfiler_h + +#include "BasicBlockLocation.h" +#include <wtf/HashMap.h> +#include <wtf/HashMethod.h> + +namespace JSC { + +class VM; + +struct BasicBlockKey { + BasicBlockKey() + : m_startOffset(-3) + , m_endOffset(-3) + { } + + BasicBlockKey(int startOffset, int endOffset) + : m_startOffset(startOffset) + , m_endOffset(endOffset) + { } + + BasicBlockKey(WTF::HashTableDeletedValueType) + : m_startOffset(-2) + , m_endOffset(-2) + { } + + bool isHashTableDeletedValue() const { return m_startOffset == -2 && m_endOffset == -2; } + bool operator==(const BasicBlockKey& other) const { return m_startOffset == other.m_startOffset && m_endOffset == other.m_endOffset; } + unsigned hash() const { return m_startOffset + m_endOffset + 1; } + + int m_startOffset; + int m_endOffset; +}; + +struct BasicBlockKeyHash { + static unsigned hash(const BasicBlockKey& key) { return key.hash(); } + static bool equal(const BasicBlockKey& a, const BasicBlockKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +} // namespace JSC + +namespace WTF { + +template<typename T> struct DefaultHash; +template<> struct DefaultHash<JSC::BasicBlockKey> { + typedef JSC::BasicBlockKeyHash Hash; +}; + +template<typename T> struct HashTraits; +template<> struct HashTraits<JSC::BasicBlockKey> : SimpleClassHashTraits<JSC::BasicBlockKey> { + static const bool emptyValueIsZero = false; +}; + +} // namespace WTF + +namespace JSC { + +struct BasicBlockRange { + int m_startOffset; + int m_endOffset; + bool m_hasExecuted; +}; + +class ControlFlowProfiler { + WTF_MAKE_FAST_ALLOCATED; +public: + ControlFlowProfiler(); + ~ControlFlowProfiler(); + BasicBlockLocation* getBasicBlockLocation(intptr_t sourceID, int startOffset, int endOffset); + JS_EXPORT_PRIVATE void dumpData() const; + Vector<BasicBlockRange> getBasicBlocksForSourceID(intptr_t sourceID, VM&) const; + BasicBlockLocation* dummyBasicBlock() { return &m_dummyBasicBlock; } + JS_EXPORT_PRIVATE bool hasBasicBlockAtTextOffsetBeenExecuted(int, intptr_t, VM&); // This function exists for testing. + +private: + typedef HashMap<BasicBlockKey, BasicBlockLocation*> BlockLocationCache; + typedef HashMap<intptr_t, BlockLocationCache> SourceIDBuckets; + + SourceIDBuckets m_sourceIDBuckets; + BasicBlockLocation m_dummyBasicBlock; +}; + +} // namespace JSC + +#endif // ControlFlowProfiler_h diff --git a/Source/JavaScriptCore/runtime/CustomGetterSetter.cpp b/Source/JavaScriptCore/runtime/CustomGetterSetter.cpp new file mode 100644 index 000000000..0f9e1afa9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CustomGetterSetter.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "CustomGetterSetter.h" + +#include "JSCJSValueInlines.h" +#include "SlotVisitorInlines.h" +#include <wtf/Assertions.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(CustomGetterSetter); + +const ClassInfo CustomGetterSetter::s_info = { "CustomGetterSetter", 0, 0, CREATE_METHOD_TABLE(CustomGetterSetter) }; + +void callCustomSetter(ExecState* exec, JSValue customGetterSetter, JSObject* base, JSValue thisValue, JSValue value) +{ + CustomGetterSetter::CustomSetter setter = jsCast<CustomGetterSetter*>(customGetterSetter)->setter(); + if (!setter) + return; + setter(exec, base, JSValue::encode(thisValue), JSValue::encode(value)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CustomGetterSetter.h b/Source/JavaScriptCore/runtime/CustomGetterSetter.h new file mode 100644 index 000000000..54519eba3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CustomGetterSetter.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef CustomGetterSetter_h +#define CustomGetterSetter_h + +#include "JSCell.h" +#include "PropertySlot.h" +#include "PutPropertySlot.h" +#include "Structure.h" + +namespace JSC { + +class CustomGetterSetter final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + typedef PropertySlot::GetValueFunc CustomGetter; + typedef PutPropertySlot::PutValueFunc CustomSetter; + + static CustomGetterSetter* create(VM& vm, CustomGetter customGetter, CustomSetter customSetter) + { + CustomGetterSetter* customGetterSetter = new (NotNull, allocateCell<CustomGetterSetter>(vm.heap)) CustomGetterSetter(vm, customGetter, customSetter); + customGetterSetter->finishCreation(vm); + return customGetterSetter; + } + + CustomGetterSetter::CustomGetter getter() const { return m_getter; } + CustomGetterSetter::CustomSetter setter() const { return m_setter; } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CustomGetterSetterType, StructureFlags), info()); + } + + DECLARE_EXPORT_INFO; + +private: + CustomGetterSetter(VM& vm, CustomGetter getter, CustomSetter setter) + : JSCell(vm, vm.customGetterSetterStructure.get()) + , m_getter(getter) + , m_setter(setter) + { + } + + CustomGetter m_getter; + CustomSetter m_setter; +}; + +void callCustomSetter(ExecState*, JSValue customGetterSetter, JSObject* base, JSValue thisValue, JSValue value); + +} // namespace JSC + +#endif // CustomGetterSetter_h diff --git a/Source/JavaScriptCore/runtime/DataView.cpp b/Source/JavaScriptCore/runtime/DataView.cpp new file mode 100644 index 000000000..78e743fc1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DataView.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "DataView.h" + +#include "JSDataView.h" +#include "JSGlobalObject.h" + +namespace JSC { + +DataView::DataView(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned byteLength) + : ArrayBufferView(buffer, byteOffset) + , m_byteLength(byteLength) +{ +} + +Ref<DataView> DataView::create( + PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned byteLength) +{ + return adoptRef(*new DataView(buffer, byteOffset, byteLength)); +} + +Ref<DataView> DataView::create(PassRefPtr<ArrayBuffer> passedBuffer) +{ + RefPtr<ArrayBuffer> buffer = passedBuffer; + return create(buffer, 0, buffer->byteLength()); +} + +JSArrayBufferView* DataView::wrap(ExecState* exec, JSGlobalObject* globalObject) +{ + return JSDataView::create( + exec, globalObject->typedArrayStructure(TypeDataView), buffer(), byteOffset(), + byteLength()); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/DataView.h b/Source/JavaScriptCore/runtime/DataView.h new file mode 100644 index 000000000..a01d13506 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DataView.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef DataView_h +#define DataView_h + +#include "ArrayBufferView.h" +#include <wtf/FlipBytes.h> +#include <wtf/PassRefPtr.h> + +namespace JSC { + +class DataView : public ArrayBufferView { +protected: + DataView(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned byteLength); + +public: + JS_EXPORT_PRIVATE static Ref<DataView> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + static Ref<DataView> create(PassRefPtr<ArrayBuffer>); + + virtual unsigned byteLength() const override + { + return m_byteLength; + } + + virtual TypedArrayType getType() const override + { + return TypeDataView; + } + + virtual JSArrayBufferView* wrap(ExecState*, JSGlobalObject*) override; + + template<typename T> + T get(unsigned offset, bool littleEndian, bool* status = 0) + { + if (status) { + if (offset + sizeof(T) > byteLength()) { + *status = false; + return T(); + } + *status = true; + } else + ASSERT_WITH_SECURITY_IMPLICATION(offset + sizeof(T) <= byteLength()); + return flipBytesIfLittleEndian( + *reinterpret_cast<T*>(static_cast<uint8_t*>(m_baseAddress) + offset), + littleEndian); + } + + template<typename T> + T read(unsigned& offset, bool littleEndian, bool* status = 0) + { + T result = this->template get<T>(offset, littleEndian, status); + if (!status || *status) + offset += sizeof(T); + return result; + } + + template<typename T> + void set(unsigned offset, T value, bool littleEndian, bool* status = 0) + { + if (status) { + if (offset + sizeof(T) > byteLength()) { + *status = false; + return; + } + *status = true; + } else + ASSERT_WITH_SECURITY_IMPLICATION(offset + sizeof(T) <= byteLength()); + *reinterpret_cast<T*>(static_cast<uint8_t*>(m_baseAddress) + offset) = + flipBytesIfLittleEndian(value, littleEndian); + } + +private: + unsigned m_byteLength; +}; + +} // namespace JSC + +#endif // DataView_h + diff --git a/Source/JavaScriptCore/runtime/DateConstructor.cpp b/Source/JavaScriptCore/runtime/DateConstructor.cpp new file mode 100644 index 000000000..ee656a309 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConstructor.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "DateConstructor.h" + +#include "DateConversion.h" +#include "DateInstance.h" +#include "DatePrototype.h" +#include "JSDateMath.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "ObjectPrototype.h" +#include "JSCInlines.h" +#include <math.h> +#include <time.h> +#include <wtf/MathExtras.h> + +#if ENABLE(WEB_REPLAY) +#include "InputCursor.h" +#include "JSReplayInputs.h" +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if HAVE(SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +using namespace WTF; + +namespace JSC { + +EncodedJSValue JSC_HOST_CALL dateParse(ExecState*); +EncodedJSValue JSC_HOST_CALL dateUTC(ExecState*); + +} + +#include "DateConstructor.lut.h" + +namespace JSC { + +const ClassInfo DateConstructor::s_info = { "Function", &InternalFunction::s_info, &dateConstructorTable, CREATE_METHOD_TABLE(DateConstructor) }; + +/* Source for DateConstructor.lut.h +@begin dateConstructorTable + parse dateParse DontEnum|Function 1 + UTC dateUTC DontEnum|Function 7 + now dateNow DontEnum|Function 0 +@end +*/ + +#if ENABLE(WEB_REPLAY) +static double deterministicCurrentTime(JSGlobalObject* globalObject) +{ + double currentTime = jsCurrentTime(); + InputCursor& cursor = globalObject->inputCursor(); + if (cursor.isCapturing()) + cursor.appendInput<GetCurrentTime>(currentTime); + + if (cursor.isReplaying()) { + if (GetCurrentTime* input = cursor.fetchInput<GetCurrentTime>()) + currentTime = input->currentTime(); + } + return currentTime; +} +#endif + +#if ENABLE(WEB_REPLAY) +#define NORMAL_OR_DETERMINISTIC_FUNCTION(a, b) (b) +#else +#define NORMAL_OR_DETERMINISTIC_FUNCTION(a, b) (a) +#endif + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DateConstructor); + +DateConstructor::DateConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void DateConstructor::finishCreation(VM& vm, DatePrototype* datePrototype) +{ + Base::finishCreation(vm, datePrototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, datePrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(7), ReadOnly | DontEnum | DontDelete); +} + +bool DateConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, dateConstructorTable, jsCast<DateConstructor*>(object), propertyName, slot); +} + +// ECMA 15.9.3 +JSObject* constructDate(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args) +{ + VM& vm = exec->vm(); + int numArgs = args.size(); + + double value; + + if (numArgs == 0) // new Date() ECMA 15.9.3.3 + value = NORMAL_OR_DETERMINISTIC_FUNCTION(jsCurrentTime(), deterministicCurrentTime(globalObject)); + else if (numArgs == 1) { + if (args.at(0).inherits(DateInstance::info())) + value = asDateInstance(args.at(0))->internalNumber(); + else { + JSValue primitive = args.at(0).toPrimitive(exec); + if (primitive.isString()) + value = parseDate(vm, primitive.getString(exec)); + else + value = primitive.toNumber(exec); + } + } else { + double doubleArguments[7] = { + args.at(0).toNumber(exec), + args.at(1).toNumber(exec), + args.at(2).toNumber(exec), + args.at(3).toNumber(exec), + args.at(4).toNumber(exec), + args.at(5).toNumber(exec), + args.at(6).toNumber(exec) + }; + if ((!std::isfinite(doubleArguments[0]) || (doubleArguments[0] > INT_MAX) || (doubleArguments[0] < INT_MIN)) + || (!std::isfinite(doubleArguments[1]) || (doubleArguments[1] > INT_MAX) || (doubleArguments[1] < INT_MIN)) + || (numArgs >= 3 && (!std::isfinite(doubleArguments[2]) || (doubleArguments[2] > INT_MAX) || (doubleArguments[2] < INT_MIN))) + || (numArgs >= 4 && (!std::isfinite(doubleArguments[3]) || (doubleArguments[3] > INT_MAX) || (doubleArguments[3] < INT_MIN))) + || (numArgs >= 5 && (!std::isfinite(doubleArguments[4]) || (doubleArguments[4] > INT_MAX) || (doubleArguments[4] < INT_MIN))) + || (numArgs >= 6 && (!std::isfinite(doubleArguments[5]) || (doubleArguments[5] > INT_MAX) || (doubleArguments[5] < INT_MIN))) + || (numArgs >= 7 && (!std::isfinite(doubleArguments[6]) || (doubleArguments[6] > INT_MAX) || (doubleArguments[6] < INT_MIN)))) + value = PNaN; + else { + GregorianDateTime t; + int year = JSC::toInt32(doubleArguments[0]); + t.setYear((year >= 0 && year <= 99) ? (year + 1900) : year); + t.setMonth(JSC::toInt32(doubleArguments[1])); + t.setMonthDay((numArgs >= 3) ? JSC::toInt32(doubleArguments[2]) : 1); + t.setHour(JSC::toInt32(doubleArguments[3])); + t.setMinute(JSC::toInt32(doubleArguments[4])); + t.setSecond(JSC::toInt32(doubleArguments[5])); + t.setIsDST(-1); + double ms = (numArgs >= 7) ? doubleArguments[6] : 0; + value = gregorianDateTimeToMS(vm, t, ms, WTF::LocalTime); + } + } + + return DateInstance::create(vm, globalObject->dateStructure(), value); +} + +static EncodedJSValue JSC_HOST_CALL constructWithDateConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructDate(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +ConstructType DateConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithDateConstructor; + return ConstructTypeHost; +} + +// ECMA 15.9.2 +static EncodedJSValue JSC_HOST_CALL callDate(ExecState* exec) +{ + VM& vm = exec->vm(); + GregorianDateTime ts; + msToGregorianDateTime(vm, currentTimeMS(), WTF::LocalTime, ts); + return JSValue::encode(jsNontrivialString(&vm, formatDateTime(ts, DateTimeFormatDateAndTime, false))); +} + +CallType DateConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callDate; + return CallTypeHost; +} + +EncodedJSValue JSC_HOST_CALL dateParse(ExecState* exec) +{ + return JSValue::encode(jsNumber(parseDate(exec->vm(), exec->argument(0).toString(exec)->value(exec)))); +} + +EncodedJSValue JSC_HOST_CALL dateNow(ExecState* exec) +{ +#if !ENABLE(WEB_REPLAY) + UNUSED_PARAM(exec); +#endif + + return JSValue::encode(jsNumber(NORMAL_OR_DETERMINISTIC_FUNCTION(jsCurrentTime(), deterministicCurrentTime(exec->lexicalGlobalObject())))); +} + +EncodedJSValue JSC_HOST_CALL dateUTC(ExecState* exec) +{ + double doubleArguments[7] = { + exec->argument(0).toNumber(exec), + exec->argument(1).toNumber(exec), + exec->argument(2).toNumber(exec), + exec->argument(3).toNumber(exec), + exec->argument(4).toNumber(exec), + exec->argument(5).toNumber(exec), + exec->argument(6).toNumber(exec) + }; + int n = exec->argumentCount(); + if ((std::isnan(doubleArguments[0]) || (doubleArguments[0] > INT_MAX) || (doubleArguments[0] < INT_MIN)) + || (std::isnan(doubleArguments[1]) || (doubleArguments[1] > INT_MAX) || (doubleArguments[1] < INT_MIN)) + || (n >= 3 && (std::isnan(doubleArguments[2]) || (doubleArguments[2] > INT_MAX) || (doubleArguments[2] < INT_MIN))) + || (n >= 4 && (std::isnan(doubleArguments[3]) || (doubleArguments[3] > INT_MAX) || (doubleArguments[3] < INT_MIN))) + || (n >= 5 && (std::isnan(doubleArguments[4]) || (doubleArguments[4] > INT_MAX) || (doubleArguments[4] < INT_MIN))) + || (n >= 6 && (std::isnan(doubleArguments[5]) || (doubleArguments[5] > INT_MAX) || (doubleArguments[5] < INT_MIN))) + || (n >= 7 && (std::isnan(doubleArguments[6]) || (doubleArguments[6] > INT_MAX) || (doubleArguments[6] < INT_MIN)))) + return JSValue::encode(jsNaN()); + + GregorianDateTime t; + int year = JSC::toInt32(doubleArguments[0]); + t.setYear((year >= 0 && year <= 99) ? (year + 1900) : year); + t.setMonth(JSC::toInt32(doubleArguments[1])); + t.setMonthDay((n >= 3) ? JSC::toInt32(doubleArguments[2]) : 1); + t.setHour(JSC::toInt32(doubleArguments[3])); + t.setMinute(JSC::toInt32(doubleArguments[4])); + t.setSecond(JSC::toInt32(doubleArguments[5])); + double ms = (n >= 7) ? doubleArguments[6] : 0; + return JSValue::encode(jsNumber(timeClip(gregorianDateTimeToMS(exec->vm(), t, ms, WTF::UTCTime)))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/DateConstructor.h b/Source/JavaScriptCore/runtime/DateConstructor.h new file mode 100644 index 000000000..cdac97d31 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConstructor.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DateConstructor_h +#define DateConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class DatePrototype; + +class DateConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static DateConstructor* create(VM& vm, Structure* structure, DatePrototype* datePrototype) + { + DateConstructor* constructor = new (NotNull, allocateCell<DateConstructor>(vm.heap)) DateConstructor(vm, structure); + constructor->finishCreation(vm, datePrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, DatePrototype*); + +private: + DateConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +JSObject* constructDate(ExecState*, JSGlobalObject*, const ArgList&); + +EncodedJSValue JSC_HOST_CALL dateNow(ExecState*); + +} // namespace JSC + +#endif // DateConstructor_h diff --git a/Source/JavaScriptCore/runtime/DateConversion.cpp b/Source/JavaScriptCore/runtime/DateConversion.cpp new file mode 100644 index 000000000..0b57f012d --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConversion.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012 Patrick Gansterer <paroga@paroga.com> + * + * 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "DateConversion.h" + +#include <wtf/Assertions.h> +#include <wtf/DateMath.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/WTFString.h> + +#if OS(WINDOWS) +#include <windows.h> +#endif + +using namespace WTF; + +namespace JSC { + +template<int width> +static inline void appendNumber(StringBuilder& builder, int value) +{ + int fillingZerosCount = width; + if (value < 0) { + builder.append('-'); + value = -value; + --fillingZerosCount; + } + String valueString = String::number(value); + fillingZerosCount -= valueString.length(); + for (int i = 0; i < fillingZerosCount; ++i) + builder.append('0'); + builder.append(valueString); +} + +template<> +void appendNumber<2>(StringBuilder& builder, int value) +{ + ASSERT(0 <= value && value <= 99); + builder.append(static_cast<char>('0' + value / 10)); + builder.append(static_cast<char>('0' + value % 10)); +} + +String formatDateTime(const GregorianDateTime& t, DateTimeFormat format, bool asUTCVariant) +{ + bool appendDate = format & DateTimeFormatDate; + bool appendTime = format & DateTimeFormatTime; + + StringBuilder builder; + + if (appendDate) { + builder.append(weekdayName[(t.weekDay() + 6) % 7]); + + if (asUTCVariant) { + builder.appendLiteral(", "); + appendNumber<2>(builder, t.monthDay()); + builder.append(' '); + builder.append(monthName[t.month()]); + } else { + builder.append(' '); + builder.append(monthName[t.month()]); + builder.append(' '); + appendNumber<2>(builder, t.monthDay()); + } + builder.append(' '); + appendNumber<4>(builder, t.year()); + } + + if (appendDate && appendTime) + builder.append(' '); + + if (appendTime) { + appendNumber<2>(builder, t.hour()); + builder.append(':'); + appendNumber<2>(builder, t.minute()); + builder.append(':'); + appendNumber<2>(builder, t.second()); + builder.appendLiteral(" GMT"); + + if (!asUTCVariant) { + int offset = abs(t.utcOffset()) / 60; + builder.append(t.utcOffset() < 0 ? '-' : '+'); + appendNumber<2>(builder, offset / 60); + appendNumber<2>(builder, offset % 60); + +#if OS(WINDOWS) + TIME_ZONE_INFORMATION timeZoneInformation; + GetTimeZoneInformation(&timeZoneInformation); + const WCHAR* timeZoneName = t.isDST() ? timeZoneInformation.DaylightName : timeZoneInformation.StandardName; +#else + struct tm gtm = t; + char timeZoneName[70]; + strftime(timeZoneName, sizeof(timeZoneName), "%Z", >m); +#endif + if (timeZoneName[0]) { + builder.appendLiteral(" ("); + builder.append(timeZoneName); + builder.append(')'); + } + } + } + + return builder.toString().impl(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/DateConversion.h b/Source/JavaScriptCore/runtime/DateConversion.h new file mode 100644 index 000000000..80a29c823 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateConversion.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 Patrick Gansterer <paroga@paroga.com> + * + * 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef DateConversion_h +#define DateConversion_h + +#include <wtf/GregorianDateTime.h> + +namespace WTF { +class String; +} // namespace WTF + +namespace JSC { + +enum DateTimeFormat { + DateTimeFormatDate = 1, + DateTimeFormatTime = 2, + DateTimeFormatDateAndTime = DateTimeFormatDate | DateTimeFormatTime +}; + +JS_EXPORT_PRIVATE WTF::String formatDateTime(const GregorianDateTime&, DateTimeFormat, bool asUTCVariant); + +} // namespace JSC + +#endif // DateConversion_h diff --git a/Source/JavaScriptCore/runtime/DateInstance.cpp b/Source/JavaScriptCore/runtime/DateInstance.cpp new file mode 100644 index 000000000..e095f882c --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstance.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "DateInstance.h" + +#include "JSDateMath.h" +#include "JSGlobalObject.h" +#include "JSCInlines.h" +#include <math.h> +#include <wtf/MathExtras.h> + +using namespace WTF; + +namespace JSC { + +const ClassInfo DateInstance::s_info = {"Date", &JSWrapperObject::s_info, 0, CREATE_METHOD_TABLE(DateInstance)}; + +DateInstance::DateInstance(VM& vm, Structure* structure) + : JSWrapperObject(vm, structure) +{ +} + +void DateInstance::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + setInternalValue(vm, jsNaN()); +} + +void DateInstance::finishCreation(VM& vm, double time) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + setInternalValue(vm, jsNumber(timeClip(time))); +} + +void DateInstance::destroy(JSCell* cell) +{ + static_cast<DateInstance*>(cell)->DateInstance::~DateInstance(); +} + +const GregorianDateTime* DateInstance::calculateGregorianDateTime(ExecState* exec) const +{ + double milli = internalNumber(); + if (std::isnan(milli)) + return 0; + + VM& vm = exec->vm(); + if (!m_data) + m_data = vm.dateInstanceCache.add(milli); + + if (m_data->m_gregorianDateTimeCachedForMS != milli) { + msToGregorianDateTime(vm, milli, WTF::LocalTime, m_data->m_cachedGregorianDateTime); + m_data->m_gregorianDateTimeCachedForMS = milli; + } + return &m_data->m_cachedGregorianDateTime; +} + +const GregorianDateTime* DateInstance::calculateGregorianDateTimeUTC(ExecState* exec) const +{ + double milli = internalNumber(); + if (std::isnan(milli)) + return 0; + + VM& vm = exec->vm(); + if (!m_data) + m_data = vm.dateInstanceCache.add(milli); + + if (m_data->m_gregorianDateTimeUTCCachedForMS != milli) { + msToGregorianDateTime(vm, milli, WTF::UTCTime, m_data->m_cachedGregorianDateTimeUTC); + m_data->m_gregorianDateTimeUTCCachedForMS = milli; + } + return &m_data->m_cachedGregorianDateTimeUTC; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/DateInstance.h b/Source/JavaScriptCore/runtime/DateInstance.h new file mode 100644 index 000000000..000e1a157 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstance.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DateInstance_h +#define DateInstance_h + +#include "JSWrapperObject.h" + +namespace JSC { + +class DateInstance : public JSWrapperObject { +protected: + JS_EXPORT_PRIVATE DateInstance(VM&, Structure*); + void finishCreation(VM&); + JS_EXPORT_PRIVATE void finishCreation(VM&, double); + + JS_EXPORT_PRIVATE static void destroy(JSCell*); + +public: + typedef JSWrapperObject Base; + + static DateInstance* create(VM& vm, Structure* structure, double date) + { + DateInstance* instance = new (NotNull, allocateCell<DateInstance>(vm.heap)) DateInstance(vm, structure); + instance->finishCreation(vm, date); + return instance; + } + + static DateInstance* create(VM& vm, Structure* structure) + { + DateInstance* instance = new (NotNull, allocateCell<DateInstance>(vm.heap)) DateInstance(vm, structure); + instance->finishCreation(vm); + return instance; + } + + double internalNumber() const { return internalValue().asNumber(); } + + DECLARE_EXPORT_INFO; + + const GregorianDateTime* gregorianDateTime(ExecState* exec) const + { + if (m_data && m_data->m_gregorianDateTimeCachedForMS == internalNumber()) + return &m_data->m_cachedGregorianDateTime; + return calculateGregorianDateTime(exec); + } + + const GregorianDateTime* gregorianDateTimeUTC(ExecState* exec) const + { + if (m_data && m_data->m_gregorianDateTimeUTCCachedForMS == internalNumber()) + return &m_data->m_cachedGregorianDateTimeUTC; + return calculateGregorianDateTimeUTC(exec); + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + JS_EXPORT_PRIVATE const GregorianDateTime* calculateGregorianDateTime(ExecState*) const; + JS_EXPORT_PRIVATE const GregorianDateTime* calculateGregorianDateTimeUTC(ExecState*) const; + + mutable RefPtr<DateInstanceData> m_data; +}; + +DateInstance* asDateInstance(JSValue); + +inline DateInstance* asDateInstance(JSValue value) +{ + ASSERT(asObject(value)->inherits(DateInstance::info())); + return static_cast<DateInstance*>(asObject(value)); +} + +} // namespace JSC + +#endif // DateInstance_h diff --git a/Source/JavaScriptCore/runtime/DateInstanceCache.h b/Source/JavaScriptCore/runtime/DateInstanceCache.h new file mode 100644 index 000000000..865ee8ff2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DateInstanceCache.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef DateInstanceCache_h +#define DateInstanceCache_h + +#include "JSCJSValue.h" +#include "JSDateMath.h" +#include <array> +#include <wtf/HashFunctions.h> +#include <wtf/RefCounted.h> + +namespace JSC { + +class DateInstanceData : public RefCounted<DateInstanceData> { +public: + static Ref<DateInstanceData> create() { return adoptRef(*new DateInstanceData); } + + double m_gregorianDateTimeCachedForMS; + GregorianDateTime m_cachedGregorianDateTime; + double m_gregorianDateTimeUTCCachedForMS; + GregorianDateTime m_cachedGregorianDateTimeUTC; + +private: + DateInstanceData() + : m_gregorianDateTimeCachedForMS(PNaN) + , m_gregorianDateTimeUTCCachedForMS(PNaN) + { + } +}; + +class DateInstanceCache { +public: + DateInstanceCache() + { + reset(); + } + + void reset() + { + for (size_t i = 0; i < cacheSize; ++i) + m_cache[i].key = PNaN; + } + + DateInstanceData* add(double d) + { + CacheEntry& entry = lookup(d); + if (d == entry.key) + return entry.value.get(); + + entry.key = d; + entry.value = DateInstanceData::create(); + return entry.value.get(); + } + +private: + static const size_t cacheSize = 16; + + struct CacheEntry { + double key; + RefPtr<DateInstanceData> value; + }; + + CacheEntry& lookup(double d) { return m_cache[WTF::FloatHash<double>::hash(d) & (cacheSize - 1)]; } + + std::array<CacheEntry, cacheSize> m_cache; +}; + +} // namespace JSC + +#endif // DateInstanceCache_h diff --git a/Source/JavaScriptCore/runtime/DatePrototype.cpp b/Source/JavaScriptCore/runtime/DatePrototype.cpp new file mode 100644 index 000000000..fe8747916 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DatePrototype.cpp @@ -0,0 +1,1099 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile, Inc. All rights reserved. + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "DatePrototype.h" + +#include "DateConversion.h" +#include "DateInstance.h" +#include "Error.h" +#include "JSDateMath.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "Lookup.h" +#include "ObjectPrototype.h" +#include "JSCInlines.h" +#include <limits.h> +#include <locale.h> +#include <math.h> +#include <stdlib.h> +#include <time.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> + +#if HAVE(LANGINFO_H) +#include <langinfo.h> +#endif + +#if HAVE(SYS_PARAM_H) +#include <sys/param.h> +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if HAVE(SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +#if !(OS(DARWIN) && USE(CF)) +#include <unicode/udat.h> +#endif + +#if USE(CF) +#include <CoreFoundation/CoreFoundation.h> +#endif + +using namespace WTF; + +namespace JSC { + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetDate(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetFullYear(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetHours(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMilliSeconds(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMinutes(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMonth(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetSeconds(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCDate(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCHours(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMilliseconds(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMinutes(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMonth(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCSeconds(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState*); +EncodedJSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState*); + +} + +#include "DatePrototype.lut.h" + +namespace JSC { + +enum LocaleDateTimeFormat { LocaleDateAndTime, LocaleDate, LocaleTime }; + +#if OS(DARWIN) && USE(CF) + +// FIXME: Since this is superior to the strftime-based version, why limit this to OS(DARWIN)? +// Instead we should consider using this whenever USE(CF) is true. + +static CFDateFormatterStyle styleFromArgString(const String& string, CFDateFormatterStyle defaultStyle) +{ + if (string == "short") + return kCFDateFormatterShortStyle; + if (string == "medium") + return kCFDateFormatterMediumStyle; + if (string == "long") + return kCFDateFormatterLongStyle; + if (string == "full") + return kCFDateFormatterFullStyle; + return defaultStyle; +} + +static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMilliseconds, LocaleDateTimeFormat format) +{ + CFDateFormatterStyle dateStyle = (format != LocaleTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); + CFDateFormatterStyle timeStyle = (format != LocaleDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); + + bool useCustomFormat = false; + String customFormatString; + + String arg0String = exec->argument(0).toString(exec)->value(exec); + if (arg0String == "custom" && !exec->argument(1).isUndefined()) { + useCustomFormat = true; + customFormatString = exec->argument(1).toString(exec)->value(exec); + } else if (format == LocaleDateAndTime && !exec->argument(1).isUndefined()) { + dateStyle = styleFromArgString(arg0String, dateStyle); + timeStyle = styleFromArgString(exec->argument(1).toString(exec)->value(exec), timeStyle); + } else if (format != LocaleTime && !exec->argument(0).isUndefined()) + dateStyle = styleFromArgString(arg0String, dateStyle); + else if (format != LocaleDate && !exec->argument(0).isUndefined()) + timeStyle = styleFromArgString(arg0String, timeStyle); + + CFAbsoluteTime absoluteTime = floor(timeInMilliseconds / msPerSecond) - kCFAbsoluteTimeIntervalSince1970; + + auto formatter = adoptCF(CFDateFormatterCreate(kCFAllocatorDefault, adoptCF(CFLocaleCopyCurrent()).get(), dateStyle, timeStyle)); + if (useCustomFormat) + CFDateFormatterSetFormat(formatter.get(), customFormatString.createCFString().get()); + return jsNontrivialString(exec, adoptCF(CFDateFormatterCreateStringWithAbsoluteTime(kCFAllocatorDefault, formatter.get(), absoluteTime)).get()); +} + +#elif !UCONFIG_NO_FORMATTING + +static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMilliseconds, LocaleDateTimeFormat format) +{ + UDateFormatStyle timeStyle = (format != LocaleDate ? UDAT_LONG : UDAT_NONE); + UDateFormatStyle dateStyle = (format != LocaleTime ? UDAT_LONG : UDAT_NONE); + + UErrorCode status = U_ZERO_ERROR; + UDateFormat* df = udat_open(timeStyle, dateStyle, 0, 0, -1, 0, 0, &status); + if (!df) + return jsEmptyString(exec); + + UChar buffer[128]; + int32_t length; + length = udat_format(df, timeInMilliseconds, buffer, 128, 0, &status); + udat_close(df); + if (status != U_ZERO_ERROR) + return jsEmptyString(exec); + + return jsNontrivialString(exec, String(buffer, length)); +} + +#else + +static JSCell* formatLocaleDate(ExecState* exec, const GregorianDateTime& gdt, LocaleDateTimeFormat format) +{ +#if OS(WINDOWS) + SYSTEMTIME systemTime; + memset(&systemTime, 0, sizeof(systemTime)); + systemTime.wYear = gdt.year(); + systemTime.wMonth = gdt.month() + 1; + systemTime.wDay = gdt.monthDay(); + systemTime.wDayOfWeek = gdt.weekDay(); + systemTime.wHour = gdt.hour(); + systemTime.wMinute = gdt.minute(); + systemTime.wSecond = gdt.second(); + + Vector<UChar, 128> buffer; + size_t length = 0; + + if (format == LocaleDate) { + buffer.resize(GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systemTime, 0, 0, 0)); + length = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systemTime, 0, buffer.data(), buffer.size()); + } else if (format == LocaleTime) { + buffer.resize(GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systemTime, 0, 0, 0)); + length = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systemTime, 0, buffer.data(), buffer.size()); + } else if (format == LocaleDateAndTime) { + buffer.resize(GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systemTime, 0, 0, 0) + GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systemTime, 0, 0, 0)); + length = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &systemTime, 0, buffer.data(), buffer.size()); + if (length) { + buffer[length - 1] = ' '; + length += GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systemTime, 0, buffer.data() + length, buffer.size() - length); + } + } else + RELEASE_ASSERT_NOT_REACHED(); + + // Remove terminating null character. + if (length) + length--; + + return jsNontrivialString(exec, String(buffer.data(), length)); + +#else // OS(WINDOWS) + +#if HAVE(LANGINFO_H) + static const nl_item formats[] = { D_T_FMT, D_FMT, T_FMT }; +#else + static const char* const formatStrings[] = { "%#c", "%#x", "%X" }; +#endif + + // Offset year if needed + struct tm localTM = gdt; + int year = gdt.year(); + bool yearNeedsOffset = year < 1900 || year > 2038; + if (yearNeedsOffset) + localTM.tm_year = equivalentYearForDST(year) - 1900; + +#if HAVE(LANGINFO_H) + // We do not allow strftime to generate dates with 2-digits years, + // both to avoid ambiguity, and a crash in strncpy, for years that + // need offset. + char* formatString = strdup(nl_langinfo(formats[format])); + char* yPos = strchr(formatString, 'y'); + if (yPos) + *yPos = 'Y'; +#endif + + // Do the formatting + const int bufsize = 128; + char timebuffer[bufsize]; + +#if HAVE(LANGINFO_H) + size_t ret = strftime(timebuffer, bufsize, formatString, &localTM); + free(formatString); +#else + size_t ret = strftime(timebuffer, bufsize, formatStrings[format], &localTM); +#endif + + if (ret == 0) + return jsEmptyString(exec); + + // Copy original into the buffer + if (yearNeedsOffset && format != LocaleTime) { + static const int yearLen = 5; // FIXME will be a problem in the year 10,000 + char yearString[yearLen]; + + snprintf(yearString, yearLen, "%d", localTM.tm_year + 1900); + char* yearLocation = strstr(timebuffer, yearString); + snprintf(yearString, yearLen, "%d", year); + + strncpy(yearLocation, yearString, yearLen - 1); + } + + // Convert multi-byte result to UNICODE. + // If __STDC_ISO_10646__ is defined, wide character represents + // UTF-16 (or UTF-32) code point. In most modern Unix like system + // (e.g. Linux with glibc 2.2 and above) the macro is defined, + // and wide character represents UTF-32 code point. + // Here we static_cast potential UTF-32 to UTF-16, it should be + // safe because date and (or) time related characters in different languages + // should be in UNICODE BMP. If mbstowcs fails, we just fall + // back on using multi-byte result as-is. +#ifdef __STDC_ISO_10646__ + UChar buffer[bufsize]; + wchar_t tempbuffer[bufsize]; + size_t length = mbstowcs(tempbuffer, timebuffer, bufsize - 1); + if (length != static_cast<size_t>(-1)) { + for (size_t i = 0; i < length; ++i) + buffer[i] = static_cast<UChar>(tempbuffer[i]); + return jsNontrivialString(exec, String(buffer, length)); + } +#endif + + return jsNontrivialString(exec, timebuffer); +#endif // OS(WINDOWS) +} + +static JSCell* formatLocaleDate(ExecState* exec, DateInstance* dateObject, double, LocaleDateTimeFormat format) +{ + const GregorianDateTime* gregorianDateTime = dateObject->gregorianDateTime(exec); + if (!gregorianDateTime) + return jsNontrivialString(exec, ASCIILiteral("Invalid Date")); + return formatLocaleDate(exec, *gregorianDateTime, format); +} + +#endif // OS(DARWIN) && USE(CF) + +static EncodedJSValue formateDateInstance(ExecState* exec, DateTimeFormat format, bool asUTCVariant) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = asUTCVariant + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, String(ASCIILiteral("Invalid Date")))); + + return JSValue::encode(jsNontrivialString(exec, formatDateTime(*gregorianDateTime, format, asUTCVariant))); +} + +// Converts a list of arguments sent to a Date member function into milliseconds, updating +// ms (representing milliseconds) and t (representing the rest of the date structure) appropriately. +// +// Format of member function: f([hour,] [min,] [sec,] [ms]) +static bool fillStructuresUsingTimeArgs(ExecState* exec, int maxArgs, double* ms, GregorianDateTime* t) +{ + double milliseconds = 0; + bool ok = true; + int idx = 0; + int numArgs = exec->argumentCount(); + + // JS allows extra trailing arguments -- ignore them + if (numArgs > maxArgs) + numArgs = maxArgs; + + // hours + if (maxArgs >= 4 && idx < numArgs) { + t->setHour(0); + double hours = exec->uncheckedArgument(idx++).toIntegerPreserveNaN(exec); + ok = std::isfinite(hours); + milliseconds += hours * msPerHour; + } + + // minutes + if (maxArgs >= 3 && idx < numArgs && ok) { + t->setMinute(0); + double minutes = exec->uncheckedArgument(idx++).toIntegerPreserveNaN(exec); + ok = std::isfinite(minutes); + milliseconds += minutes * msPerMinute; + } + + // seconds + if (maxArgs >= 2 && idx < numArgs && ok) { + t->setSecond(0); + double seconds = exec->uncheckedArgument(idx++).toIntegerPreserveNaN(exec); + ok = std::isfinite(seconds); + milliseconds += seconds * msPerSecond; + } + + if (!ok) + return false; + + // milliseconds + if (idx < numArgs) { + double millis = exec->uncheckedArgument(idx).toIntegerPreserveNaN(exec); + ok = std::isfinite(millis); + milliseconds += millis; + } else + milliseconds += *ms; + + *ms = milliseconds; + return ok; +} + +// Converts a list of arguments sent to a Date member function into years, months, and milliseconds, updating +// ms (representing milliseconds) and t (representing the rest of the date structure) appropriately. +// +// Format of member function: f([years,] [months,] [days]) +static bool fillStructuresUsingDateArgs(ExecState *exec, int maxArgs, double *ms, GregorianDateTime *t) +{ + int idx = 0; + bool ok = true; + int numArgs = exec->argumentCount(); + + // JS allows extra trailing arguments -- ignore them + if (numArgs > maxArgs) + numArgs = maxArgs; + + // years + if (maxArgs >= 3 && idx < numArgs) { + double years = exec->uncheckedArgument(idx++).toIntegerPreserveNaN(exec); + ok = std::isfinite(years); + t->setYear(toInt32(years)); + } + // months + if (maxArgs >= 2 && idx < numArgs && ok) { + double months = exec->uncheckedArgument(idx++).toIntegerPreserveNaN(exec); + ok = std::isfinite(months); + t->setMonth(toInt32(months)); + } + // days + if (idx < numArgs && ok) { + double days = exec->uncheckedArgument(idx++).toIntegerPreserveNaN(exec); + ok = std::isfinite(days); + t->setMonthDay(0); + *ms += days * msPerDay; + } + + return ok; +} + +const ClassInfo DatePrototype::s_info = {"Date", &DateInstance::s_info, &dateTable, CREATE_METHOD_TABLE(DatePrototype)}; + +/* Source for DatePrototype.lut.h +@begin dateTable + toString dateProtoFuncToString DontEnum|Function 0 + toISOString dateProtoFuncToISOString DontEnum|Function 0 + toUTCString dateProtoFuncToUTCString DontEnum|Function 0 + toDateString dateProtoFuncToDateString DontEnum|Function 0 + toTimeString dateProtoFuncToTimeString DontEnum|Function 0 + toLocaleString dateProtoFuncToLocaleString DontEnum|Function 0 + toLocaleDateString dateProtoFuncToLocaleDateString DontEnum|Function 0 + toLocaleTimeString dateProtoFuncToLocaleTimeString DontEnum|Function 0 + valueOf dateProtoFuncGetTime DontEnum|Function 0 + getTime dateProtoFuncGetTime DontEnum|Function 0 + getFullYear dateProtoFuncGetFullYear DontEnum|Function 0 + getUTCFullYear dateProtoFuncGetUTCFullYear DontEnum|Function 0 + toGMTString dateProtoFuncToGMTString DontEnum|Function 0 + getMonth dateProtoFuncGetMonth DontEnum|Function 0 + getUTCMonth dateProtoFuncGetUTCMonth DontEnum|Function 0 + getDate dateProtoFuncGetDate DontEnum|Function 0 + getUTCDate dateProtoFuncGetUTCDate DontEnum|Function 0 + getDay dateProtoFuncGetDay DontEnum|Function 0 + getUTCDay dateProtoFuncGetUTCDay DontEnum|Function 0 + getHours dateProtoFuncGetHours DontEnum|Function 0 + getUTCHours dateProtoFuncGetUTCHours DontEnum|Function 0 + getMinutes dateProtoFuncGetMinutes DontEnum|Function 0 + getUTCMinutes dateProtoFuncGetUTCMinutes DontEnum|Function 0 + getSeconds dateProtoFuncGetSeconds DontEnum|Function 0 + getUTCSeconds dateProtoFuncGetUTCSeconds DontEnum|Function 0 + getMilliseconds dateProtoFuncGetMilliSeconds DontEnum|Function 0 + getUTCMilliseconds dateProtoFuncGetUTCMilliseconds DontEnum|Function 0 + getTimezoneOffset dateProtoFuncGetTimezoneOffset DontEnum|Function 0 + setTime dateProtoFuncSetTime DontEnum|Function 1 + setMilliseconds dateProtoFuncSetMilliSeconds DontEnum|Function 1 + setUTCMilliseconds dateProtoFuncSetUTCMilliseconds DontEnum|Function 1 + setSeconds dateProtoFuncSetSeconds DontEnum|Function 2 + setUTCSeconds dateProtoFuncSetUTCSeconds DontEnum|Function 2 + setMinutes dateProtoFuncSetMinutes DontEnum|Function 3 + setUTCMinutes dateProtoFuncSetUTCMinutes DontEnum|Function 3 + setHours dateProtoFuncSetHours DontEnum|Function 4 + setUTCHours dateProtoFuncSetUTCHours DontEnum|Function 4 + setDate dateProtoFuncSetDate DontEnum|Function 1 + setUTCDate dateProtoFuncSetUTCDate DontEnum|Function 1 + setMonth dateProtoFuncSetMonth DontEnum|Function 2 + setUTCMonth dateProtoFuncSetUTCMonth DontEnum|Function 2 + setFullYear dateProtoFuncSetFullYear DontEnum|Function 3 + setUTCFullYear dateProtoFuncSetUTCFullYear DontEnum|Function 3 + setYear dateProtoFuncSetYear DontEnum|Function 1 + getYear dateProtoFuncGetYear DontEnum|Function 0 + toJSON dateProtoFuncToJSON DontEnum|Function 1 +@end +*/ + +// ECMA 15.9.4 + +DatePrototype::DatePrototype(VM& vm, Structure* structure) + : DateInstance(vm, structure) +{ +} + +void DatePrototype::finishCreation(VM& vm, JSGlobalObject*) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + + // The constructor will be added later, after DateConstructor has been built. +} + +bool DatePrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, dateTable, jsCast<DatePrototype*>(object), propertyName, slot); +} + +// Functions + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec) +{ + const bool asUTCVariant = false; + return formateDateInstance(exec, DateTimeFormatDateAndTime, asUTCVariant); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec) +{ + const bool asUTCVariant = true; + return formateDateInstance(exec, DateTimeFormatDateAndTime, asUTCVariant); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + if (!std::isfinite(thisDateObj->internalNumber())) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("Invalid Date"))); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNontrivialString(exec, String(ASCIILiteral("Invalid Date")))); + // Maximum amount of space we need in buffer: 7 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + 4 (. + 3 digits for milliseconds) + // 6 for formatting and one for null termination = 28. We add one extra character to allow us to force null termination. + char buffer[28]; + // If the year is outside the bounds of 0 and 9999 inclusive we want to use the extended year format (ES 15.9.1.15.1). + int ms = static_cast<int>(fmod(thisDateObj->internalNumber(), msPerSecond)); + if (ms < 0) + ms += msPerSecond; + + int charactersWritten; + if (gregorianDateTime->year() > 9999 || gregorianDateTime->year() < 0) + charactersWritten = snprintf(buffer, sizeof(buffer), "%+07d-%02d-%02dT%02d:%02d:%02d.%03dZ", gregorianDateTime->year(), gregorianDateTime->month() + 1, gregorianDateTime->monthDay(), gregorianDateTime->hour(), gregorianDateTime->minute(), gregorianDateTime->second(), ms); + else + charactersWritten = snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", gregorianDateTime->year(), gregorianDateTime->month() + 1, gregorianDateTime->monthDay(), gregorianDateTime->hour(), gregorianDateTime->minute(), gregorianDateTime->second(), ms); + + ASSERT(charactersWritten > 0 && static_cast<unsigned>(charactersWritten) < sizeof(buffer)); + if (static_cast<unsigned>(charactersWritten) >= sizeof(buffer)) + return JSValue::encode(jsEmptyString(exec)); + + return JSValue::encode(jsNontrivialString(exec, String(buffer, charactersWritten))); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec) +{ + const bool asUTCVariant = false; + return formateDateInstance(exec, DateTimeFormatDate, asUTCVariant); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec) +{ + const bool asUTCVariant = false; + return formateDateInstance(exec, DateTimeFormatTime, asUTCVariant); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleDateAndTime)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleDate)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleTime)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + return JSValue::encode(asDateInstance(thisValue)->internalValue()); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->year())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->year())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec) +{ + const bool asUTCVariant = true; + return formateDateInstance(exec, DateTimeFormatDateAndTime, asUTCVariant); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->month())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->month())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->monthDay())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->monthDay())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->weekDay())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->weekDay())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->hour())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->hour())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->minute())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->minute())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->second())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->second())); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + double milli = thisDateObj->internalNumber(); + if (std::isnan(milli)) + return JSValue::encode(jsNaN()); + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + return JSValue::encode(jsNumber(ms)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + double milli = thisDateObj->internalNumber(); + if (std::isnan(milli)) + return JSValue::encode(jsNaN()); + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + return JSValue::encode(jsNumber(ms)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(-gregorianDateTime->utcOffset() / minutesPerHour)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + double milli = timeClip(exec->argument(0).toNumber(exec)); + JSValue result = jsNumber(milli); + thisDateObj->setInternalValue(exec->vm(), result); + return JSValue::encode(result); +} + +static EncodedJSValue setNewValueFromTimeArgs(ExecState* exec, int numArgsToUse, WTF::TimeType inputTimeType) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + double milli = thisDateObj->internalNumber(); + VM& vm = exec->vm(); + + if (!exec->argumentCount() || std::isnan(milli)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(vm, result); + return JSValue::encode(result); + } + + double secs = floor(milli / msPerSecond); + double ms = milli - secs * msPerSecond; + + const GregorianDateTime* other = inputTimeType == WTF::UTCTime + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!other) + return JSValue::encode(jsNaN()); + + GregorianDateTime gregorianDateTime; + gregorianDateTime.copyFrom(*other); + if (!fillStructuresUsingTimeArgs(exec, numArgsToUse, &ms, &gregorianDateTime)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(vm, result); + return JSValue::encode(result); + } + + JSValue result = jsNumber(gregorianDateTimeToMS(vm, gregorianDateTime, ms, inputTimeType)); + thisDateObj->setInternalValue(vm, result); + return JSValue::encode(result); +} + +static EncodedJSValue setNewValueFromDateArgs(ExecState* exec, int numArgsToUse, WTF::TimeType inputTimeType) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + if (!exec->argumentCount()) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(exec->vm(), result); + return JSValue::encode(result); + } + + VM& vm = exec->vm(); + double milli = thisDateObj->internalNumber(); + double ms = 0; + + GregorianDateTime gregorianDateTime; + if (numArgsToUse == 3 && std::isnan(milli)) + msToGregorianDateTime(vm, 0, WTF::UTCTime, gregorianDateTime); + else { + ms = milli - floor(milli / msPerSecond) * msPerSecond; + const GregorianDateTime* other = inputTimeType == WTF::UTCTime + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!other) + return JSValue::encode(jsNaN()); + gregorianDateTime.copyFrom(*other); + } + + if (!fillStructuresUsingDateArgs(exec, numArgsToUse, &ms, &gregorianDateTime)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(vm, result); + return JSValue::encode(result); + } + + JSValue result = jsNumber(gregorianDateTimeToMS(vm, gregorianDateTime, ms, inputTimeType)); + thisDateObj->setInternalValue(vm, result); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMilliSeconds(ExecState* exec) +{ + return setNewValueFromTimeArgs(exec, 1, WTF::LocalTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMilliseconds(ExecState* exec) +{ + return setNewValueFromTimeArgs(exec, 1, WTF::UTCTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetSeconds(ExecState* exec) +{ + return setNewValueFromTimeArgs(exec, 2, WTF::LocalTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCSeconds(ExecState* exec) +{ + return setNewValueFromTimeArgs(exec, 2, WTF::UTCTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMinutes(ExecState* exec) +{ + return setNewValueFromTimeArgs(exec, 3, WTF::LocalTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMinutes(ExecState* exec) +{ + return setNewValueFromTimeArgs(exec, 3, WTF::UTCTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetHours(ExecState* exec) +{ + return setNewValueFromTimeArgs(exec, 4, WTF::LocalTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCHours(ExecState* exec) +{ + return setNewValueFromTimeArgs(exec, 4, WTF::UTCTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetDate(ExecState* exec) +{ + return setNewValueFromDateArgs(exec, 1, WTF::LocalTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCDate(ExecState* exec) +{ + return setNewValueFromDateArgs(exec, 1, WTF::UTCTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMonth(ExecState* exec) +{ + return setNewValueFromDateArgs(exec, 2, WTF::LocalTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMonth(ExecState* exec) +{ + return setNewValueFromDateArgs(exec, 2, WTF::UTCTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetFullYear(ExecState* exec) +{ + return setNewValueFromDateArgs(exec, 3, WTF::LocalTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState* exec) +{ + return setNewValueFromDateArgs(exec, 3, WTF::UTCTime); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + VM& vm = exec->vm(); + DateInstance* thisDateObj = asDateInstance(thisValue); + if (!exec->argumentCount()) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(vm, result); + return JSValue::encode(result); + } + + double milli = thisDateObj->internalNumber(); + double ms = 0; + + GregorianDateTime gregorianDateTime; + if (std::isnan(milli)) + // Based on ECMA 262 B.2.5 (setYear) + // the time must be reset to +0 if it is NaN. + msToGregorianDateTime(vm, 0, WTF::UTCTime, gregorianDateTime); + else { + double secs = floor(milli / msPerSecond); + ms = milli - secs * msPerSecond; + if (const GregorianDateTime* other = thisDateObj->gregorianDateTime(exec)) + gregorianDateTime.copyFrom(*other); + } + + double year = exec->argument(0).toIntegerPreserveNaN(exec); + if (!std::isfinite(year)) { + JSValue result = jsNaN(); + thisDateObj->setInternalValue(vm, result); + return JSValue::encode(result); + } + + gregorianDateTime.setYear(toInt32((year >= 0 && year <= 99) ? (year + 1900) : year)); + JSValue result = jsNumber(gregorianDateTimeToMS(vm, gregorianDateTime, ms, WTF::LocalTime)); + thisDateObj->setInternalValue(vm, result); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(DateInstance::info())) + return throwVMTypeError(exec); + + DateInstance* thisDateObj = asDateInstance(thisValue); + + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) + return JSValue::encode(jsNaN()); + + // NOTE: IE returns the full year even in getYear. + return JSValue::encode(jsNumber(gregorianDateTime->year() - 1900)); +} + +EncodedJSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + JSObject* object = jsCast<JSObject*>(thisValue.toThis(exec, NotStrictMode)); + if (exec->hadException()) + return JSValue::encode(jsNull()); + + JSValue timeValue = object->toPrimitive(exec, PreferNumber); + if (exec->hadException()) + return JSValue::encode(jsNull()); + if (timeValue.isNumber() && !(timeValue.isInt32() || std::isfinite(timeValue.asDouble()))) + return JSValue::encode(jsNull()); + + JSValue toISOValue = object->get(exec, exec->vm().propertyNames->toISOString); + if (exec->hadException()) + return JSValue::encode(jsNull()); + + CallData callData; + CallType callType = getCallData(toISOValue, callData); + if (callType == CallTypeNone) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("toISOString is not a function"))); + + JSValue result = call(exec, asObject(toISOValue), callType, callData, object, exec->emptyList()); + if (exec->hadException()) + return JSValue::encode(jsNull()); + if (result.isObject()) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("toISOString did not return a primitive value"))); + return JSValue::encode(result); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/DatePrototype.h b/Source/JavaScriptCore/runtime/DatePrototype.h new file mode 100644 index 000000000..6714d7e36 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DatePrototype.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DatePrototype_h +#define DatePrototype_h + +#include "DateInstance.h" + +namespace JSC { + +class ObjectPrototype; + +class DatePrototype : public DateInstance { +private: + DatePrototype(VM&, Structure*); + +public: + typedef DateInstance Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static DatePrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + DatePrototype* prototype = new (NotNull, allocateCell<DatePrototype>(vm.heap)) DatePrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, JSGlobalObject*); +}; + +} // namespace JSC + +#endif // DatePrototype_h diff --git a/Source/JavaScriptCore/runtime/DirectArguments.cpp b/Source/JavaScriptCore/runtime/DirectArguments.cpp new file mode 100644 index 000000000..bb61e4faf --- /dev/null +++ b/Source/JavaScriptCore/runtime/DirectArguments.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "DirectArguments.h" + +#include "CopyVisitorInlines.h" +#include "GenericArgumentsInlines.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DirectArguments); + +const ClassInfo DirectArguments::s_info = { "Arguments", &Base::s_info, 0, CREATE_METHOD_TABLE(DirectArguments) }; + +DirectArguments::DirectArguments(VM& vm, Structure* structure, unsigned length, unsigned capacity) + : GenericArguments(vm, structure) + , m_length(length) + , m_minCapacity(capacity) +{ + // When we construct the object from C++ code, we expect the capacity to be at least as large as + // length. JIT-allocated DirectArguments objects play evil tricks, though. + ASSERT(capacity >= length); +} + +DirectArguments* DirectArguments::createUninitialized( + VM& vm, Structure* structure, unsigned length, unsigned capacity) +{ + DirectArguments* result = + new (NotNull, allocateCell<DirectArguments>(vm.heap, allocationSize(capacity))) + DirectArguments(vm, structure, length, capacity); + result->finishCreation(vm); + return result; +} + +DirectArguments* DirectArguments::create(VM& vm, Structure* structure, unsigned length, unsigned capacity) +{ + DirectArguments* result = createUninitialized(vm, structure, length, capacity); + + for (unsigned i = capacity; i--;) + result->storage()[i].clear(); + + return result; +} + +DirectArguments* DirectArguments::createByCopying(ExecState* exec) +{ + VM& vm = exec->vm(); + + unsigned length = exec->argumentCount(); + unsigned capacity = std::max(length, static_cast<unsigned>(exec->codeBlock()->numParameters() - 1)); + DirectArguments* result = createUninitialized( + vm, exec->lexicalGlobalObject()->directArgumentsStructure(), length, capacity); + + for (unsigned i = capacity; i--;) + result->storage()[i].set(vm, result, exec->getArgumentUnsafe(i)); + + result->callee().set(vm, result, jsCast<JSFunction*>(exec->callee())); + + return result; +} + +void DirectArguments::visitChildren(JSCell* thisCell, SlotVisitor& visitor) +{ + DirectArguments* thisObject = static_cast<DirectArguments*>(thisCell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + visitor.appendValues(thisObject->storage(), std::max(thisObject->m_length, thisObject->m_minCapacity)); + visitor.append(&thisObject->m_callee); + + if (thisObject->m_overrides) { + visitor.copyLater( + thisObject, DirectArgumentsOverridesCopyToken, + thisObject->m_overrides.get(), thisObject->overridesSize()); + } +} + +void DirectArguments::copyBackingStore(JSCell* thisCell, CopyVisitor& visitor, CopyToken token) +{ + DirectArguments* thisObject = static_cast<DirectArguments*>(thisCell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + RELEASE_ASSERT(token == DirectArgumentsOverridesCopyToken); + + bool* oldOverrides = thisObject->m_overrides.get(); + if (!oldOverrides) + return; + + if (visitor.checkIfShouldCopy(oldOverrides)) { + bool* newOverrides = static_cast<bool*>(visitor.allocateNewSpace(thisObject->overridesSize())); + memcpy(newOverrides, oldOverrides, thisObject->m_length); + thisObject->m_overrides.setWithoutWriteBarrier(newOverrides); + visitor.didCopy(oldOverrides, thisObject->overridesSize()); + } +} + +Structure* DirectArguments::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(DirectArgumentsType, StructureFlags), info()); +} + +void DirectArguments::overrideThings(VM& vm) +{ + RELEASE_ASSERT(!m_overrides); + + putDirect(vm, vm.propertyNames->length, jsNumber(m_length), DontEnum); + putDirect(vm, vm.propertyNames->callee, m_callee.get(), DontEnum); + putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject()->arrayProtoValuesFunction(), DontEnum); + + void* backingStore; + RELEASE_ASSERT(vm.heap.tryAllocateStorage(this, overridesSize(), &backingStore)); + m_overrides.set(vm, this, static_cast<bool*>(backingStore)); + for (unsigned i = m_length; i--;) + m_overrides.get()[i] = false; +} + +void DirectArguments::overrideThingsIfNecessary(VM& vm) +{ + if (!m_overrides) + overrideThings(vm); +} + +void DirectArguments::overrideArgument(VM& vm, unsigned index) +{ + overrideThingsIfNecessary(vm); + m_overrides.get()[index] = true; +} + +void DirectArguments::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length) +{ + if (!m_overrides) { + unsigned limit = std::min(length + offset, m_length); + unsigned i; + VirtualRegister start = firstElementDest - offset; + for (i = offset; i < limit; ++i) + exec->r(start + i) = storage()[i].get(); + for (; i < length; ++i) + exec->r(start + i) = get(exec, i); + return; + } + + GenericArguments::copyToArguments(exec, firstElementDest, offset, length); +} + +unsigned DirectArguments::overridesSize() +{ + // We always allocate something; in the relatively uncommon case of overriding an empty argument we + // still allocate so that m_overrides is non-null. We use that to indicate that the other properties + // (length, etc) are overridden. + return WTF::roundUpToMultipleOf<8>(m_length ? m_length : 1); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/DirectArguments.h b/Source/JavaScriptCore/runtime/DirectArguments.h new file mode 100644 index 000000000..14bb8bbb3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/DirectArguments.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef DirectArguments_h +#define DirectArguments_h + +#include "DirectArgumentsOffset.h" +#include "GenericArguments.h" + +namespace JSC { + +// This is an Arguments-class object that we create when you say "arguments" inside a function, +// and none of the arguments are captured in the function's activation. The function will copy all +// of its arguments into this object, and all subsequent accesses to the arguments will go through +// this object thereafter. Special support is in place for mischevious events like the arguments +// being deleted (something like "delete arguments[0]") or reconfigured (broadly, we say deletions +// and reconfigurations mean that the respective argument was "overridden"). +// +// To speed allocation, this object will hold all of the arguments in-place. The arguments as well +// as a table of flags saying which arguments were overridden. +class DirectArguments : public GenericArguments<DirectArguments> { +private: + DirectArguments(VM&, Structure*, unsigned length, unsigned capacity); + +public: + // Creates an arguments object but leaves it uninitialized. This is dangerous if we GC right + // after allocation. + static DirectArguments* createUninitialized(VM&, Structure*, unsigned length, unsigned capacity); + + // Creates an arguments object and initializes everything to the empty value. Use this if you + // cannot guarantee that you'll immediately initialize all of the elements. + static DirectArguments* create(VM&, Structure*, unsigned length, unsigned capacity); + + // Creates an arguments object by copying the argumnets from the stack. + static DirectArguments* createByCopying(ExecState*); + + static void visitChildren(JSCell*, SlotVisitor&); + static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); + + uint32_t internalLength() const + { + return m_length; + } + + uint32_t length(ExecState* exec) const + { + if (UNLIKELY(m_overrides)) + return get(exec, exec->propertyNames().length).toUInt32(exec); + return m_length; + } + + bool canAccessIndexQuickly(uint32_t i) const + { + return i < m_length && (!m_overrides || !m_overrides.get()[i]); + } + + bool canAccessArgumentIndexQuicklyInDFG(uint32_t i) const + { + return i < m_length && !overrodeThings(); + } + + JSValue getIndexQuickly(uint32_t i) const + { + ASSERT_WITH_SECURITY_IMPLICATION(canAccessIndexQuickly(i)); + return const_cast<DirectArguments*>(this)->storage()[i].get(); + } + + void setIndexQuickly(VM& vm, uint32_t i, JSValue value) + { + ASSERT_WITH_SECURITY_IMPLICATION(canAccessIndexQuickly(i)); + storage()[i].set(vm, this, value); + } + + WriteBarrier<JSFunction>& callee() + { + return m_callee; + } + + WriteBarrier<Unknown>& argument(DirectArgumentsOffset offset) + { + ASSERT(offset); + ASSERT_WITH_SECURITY_IMPLICATION(offset.offset() < std::max(m_length, m_minCapacity)); + return storage()[offset.offset()]; + } + + // Methods intended for use by the GenericArguments mixin. + bool overrodeThings() const { return !!m_overrides; } + void overrideThings(VM&); + void overrideThingsIfNecessary(VM&); + void overrideArgument(VM&, unsigned index); + + void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length); + + DECLARE_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + static ptrdiff_t offsetOfCallee() { return OBJECT_OFFSETOF(DirectArguments, m_callee); } + static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(DirectArguments, m_length); } + static ptrdiff_t offsetOfMinCapacity() { return OBJECT_OFFSETOF(DirectArguments, m_minCapacity); } + static ptrdiff_t offsetOfOverrides() { return OBJECT_OFFSETOF(DirectArguments, m_overrides); } + + static size_t storageOffset() + { + return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<Unknown>)>(sizeof(DirectArguments)); + } + + static size_t offsetOfSlot(uint32_t index) + { + return storageOffset() + sizeof(WriteBarrier<Unknown>) * index; + } + + static size_t allocationSize(uint32_t capacity) + { + return offsetOfSlot(capacity); + } + +private: + WriteBarrier<Unknown>* storage() + { + return bitwise_cast<WriteBarrier<Unknown>*>(bitwise_cast<char*>(this) + storageOffset()); + } + + unsigned overridesSize(); + + WriteBarrier<JSFunction> m_callee; + uint32_t m_length; // Always the actual length of captured arguments and never what was stored into the length property. + uint32_t m_minCapacity; // The max of this and length determines the capacity of this object. It may be the actual capacity, or maybe something smaller. We arrange it this way to be kind to the JITs. + CopyWriteBarrier<bool> m_overrides; // If non-null, it means that length, callee, and caller are fully materialized properties. +}; + +} // namespace JSC + +#endif // DirectArguments_h + diff --git a/Source/JavaScriptCore/runtime/DirectArgumentsOffset.cpp b/Source/JavaScriptCore/runtime/DirectArgumentsOffset.cpp new file mode 100644 index 000000000..9fc4c42df --- /dev/null +++ b/Source/JavaScriptCore/runtime/DirectArgumentsOffset.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "DirectArgumentsOffset.h" + +namespace JSC { + +void DirectArgumentsOffset::dump(PrintStream& out) const +{ + if (!*this) { + out.print("capturedArgumentInvalid"); + return; + } + + out.print("capturedArgument", offset()); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/DirectArgumentsOffset.h b/Source/JavaScriptCore/runtime/DirectArgumentsOffset.h new file mode 100644 index 000000000..88a51874e --- /dev/null +++ b/Source/JavaScriptCore/runtime/DirectArgumentsOffset.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef DirectArgumentsOffset_h +#define DirectArgumentsOffset_h + +#include "GenericOffset.h" +#include <wtf/PrintStream.h> + +namespace JSC { + +// This is an offset into the special arguments object, which captures the arguments to a +// function. It only comes into play if the arguments aren't also lifted into the activation. +// If they were then accesses to the arguments would resolve to a ScopeOffset and not a +// DirectArgumentsOffset. +class DirectArgumentsOffset : public GenericOffset<DirectArgumentsOffset> { +public: + DirectArgumentsOffset() { } + + explicit DirectArgumentsOffset(unsigned offset) + : GenericOffset(offset) + { + } + + void dump(PrintStream&) const; +}; + +} // namespace JSC + +#endif // DirectArgumentsOffset_h + diff --git a/Source/JavaScriptCore/runtime/DumpContext.cpp b/Source/JavaScriptCore/runtime/DumpContext.cpp new file mode 100644 index 000000000..f07c2b25d --- /dev/null +++ b/Source/JavaScriptCore/runtime/DumpContext.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "DumpContext.h" + +namespace JSC { + +DumpContext::DumpContext() + : graph(0) +{ +} + +DumpContext::~DumpContext() { } + +bool DumpContext::isEmpty() const +{ + return structures.isEmpty(); +} + +void DumpContext::dump(PrintStream& out, const char* prefix) const +{ + structures.dump(out, prefix); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/DumpContext.h b/Source/JavaScriptCore/runtime/DumpContext.h new file mode 100644 index 000000000..ddd6ba06e --- /dev/null +++ b/Source/JavaScriptCore/runtime/DumpContext.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef DumpContext_h +#define DumpContext_h + +#include "Structure.h" +#include <wtf/PrintStream.h> +#include <wtf/StringHashDumpContext.h> + +namespace JSC { + +namespace DFG { class Graph; } + +struct DumpContext { + DumpContext(); + ~DumpContext(); + + bool isEmpty() const; + + void dump(PrintStream&, const char* prefix = "") const; + + StringHashDumpContext<Structure> structures; + DFG::Graph* graph; +}; + +} // namespace JSC + +#endif // DumpContext_h diff --git a/Source/JavaScriptCore/runtime/EnumerationMode.h b/Source/JavaScriptCore/runtime/EnumerationMode.h new file mode 100644 index 000000000..86d53e049 --- /dev/null +++ b/Source/JavaScriptCore/runtime/EnumerationMode.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef EnumerationMode_h +#define EnumerationMode_h + +namespace JSC { + +enum class PropertyNameMode { + Symbols = 1 << 0, + Strings = 1 << 1, + StringsAndSymbols = Symbols | Strings, +}; + +enum class DontEnumPropertiesMode { + Include, + Exclude +}; + +enum class JSObjectPropertiesMode { + Include, + Exclude +}; + +class EnumerationMode { +public: + EnumerationMode(DontEnumPropertiesMode dontEnumPropertiesMode = DontEnumPropertiesMode::Exclude, JSObjectPropertiesMode jsObjectPropertiesMode = JSObjectPropertiesMode::Include) + : m_dontEnumPropertiesMode(dontEnumPropertiesMode) + , m_jsObjectPropertiesMode(jsObjectPropertiesMode) + { + } + + EnumerationMode(const EnumerationMode& mode, JSObjectPropertiesMode jsObjectPropertiesMode) + : m_dontEnumPropertiesMode(mode.m_dontEnumPropertiesMode) + , m_jsObjectPropertiesMode(jsObjectPropertiesMode) + { + } + + // Add other constructors as needed for convenience + + bool includeDontEnumProperties() + { + return m_dontEnumPropertiesMode == DontEnumPropertiesMode::Include; + } + + bool includeJSObjectProperties() + { + return m_jsObjectPropertiesMode == JSObjectPropertiesMode::Include; + } + +private: + DontEnumPropertiesMode m_dontEnumPropertiesMode; + JSObjectPropertiesMode m_jsObjectPropertiesMode; +}; + +} // namespace JSC + +#endif // EnumerationMode_h diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp new file mode 100644 index 000000000..00285a675 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Error.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel (eric@webkit.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Error.h" + +#include "ConstructData.h" +#include "ErrorConstructor.h" +#include "ExceptionHelpers.h" +#include "FunctionPrototype.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSObject.h" +#include "JSString.h" +#include "NativeErrorConstructor.h" +#include "JSCInlines.h" +#include "SourceCode.h" + +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +static const char* linePropertyName = "line"; +static const char* sourceURLPropertyName = "sourceURL"; + +JSObject* createError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + return ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(), message, appender, TypeNothing, true); +} + +JSObject* createEvalError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + return ErrorInstance::create(exec, globalObject->vm(), globalObject->evalErrorConstructor()->errorStructure(), message, appender, TypeNothing, true); +} + +JSObject* createRangeError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + return ErrorInstance::create(exec, globalObject->vm(), globalObject->rangeErrorConstructor()->errorStructure(), message, appender, TypeNothing, true); +} + +JSObject* createReferenceError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + return ErrorInstance::create(exec, globalObject->vm(), globalObject->referenceErrorConstructor()->errorStructure(), message, appender, TypeNothing, true); +} + +JSObject* createSyntaxError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + return ErrorInstance::create(exec, globalObject->vm(), globalObject->syntaxErrorConstructor()->errorStructure(), message, appender, TypeNothing, true); +} + +JSObject* createTypeError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender, RuntimeType type) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + return ErrorInstance::create(exec, globalObject->vm(), globalObject->typeErrorConstructor()->errorStructure(), message, appender, type, true); +} + +JSObject* createNotEnoughArgumentsError(ExecState* exec, ErrorInstance::SourceAppender appender) +{ + return createTypeError(exec, ASCIILiteral("Not enough arguments"), appender, TypeNothing); +} + +JSObject* createURIError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + return ErrorInstance::create(exec, globalObject->vm(), globalObject->URIErrorConstructor()->errorStructure(), message, appender, TypeNothing, true); +} + +JSObject* createOutOfMemoryError(ExecState* exec, ErrorInstance::SourceAppender appender) +{ + return createError(exec, ASCIILiteral("Out of memory"), appender); +} + + +class FindFirstCallerFrameWithCodeblockFunctor { +public: + FindFirstCallerFrameWithCodeblockFunctor(CallFrame* startCallFrame) + : m_startCallFrame(startCallFrame) + , m_foundCallFrame(nullptr) + , m_foundStartCallFrame(false) + , m_index(0) + { } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + if (!m_foundStartCallFrame && (visitor->callFrame() == m_startCallFrame)) + m_foundStartCallFrame = true; + + if (m_foundStartCallFrame) { + if (visitor->callFrame()->codeBlock()) { + m_foundCallFrame = visitor->callFrame(); + return StackVisitor::Done; + } + m_index++; + } + + return StackVisitor::Continue; + } + + CallFrame* foundCallFrame() const { return m_foundCallFrame; } + unsigned index() const { return m_index; } + +private: + CallFrame* m_startCallFrame; + CallFrame* m_foundCallFrame; + bool m_foundStartCallFrame; + unsigned m_index; +}; + +bool addErrorInfoAndGetBytecodeOffset(ExecState* exec, VM& vm, JSObject* obj, bool useCurrentFrame, CallFrame*& callFrame, unsigned &bytecodeOffset) +{ + Vector<StackFrame> stackTrace = Vector<StackFrame>(); + + if (exec && stackTrace.isEmpty()) + vm.interpreter->getStackTrace(stackTrace); + + if (!stackTrace.isEmpty()) { + + ASSERT(exec == vm.topCallFrame || exec == exec->lexicalGlobalObject()->globalExec() || exec == exec->vmEntryGlobalObject()->globalExec()); + + StackFrame* stackFrame; + for (unsigned i = 0 ; i < stackTrace.size(); ++i) { + stackFrame = &stackTrace.at(i); + if (stackFrame->bytecodeOffset) + break; + } + + if (bytecodeOffset) { + FindFirstCallerFrameWithCodeblockFunctor functor(exec); + vm.topCallFrame->iterate(functor); + callFrame = functor.foundCallFrame(); + unsigned stackIndex = functor.index(); + bytecodeOffset = stackTrace.at(stackIndex).bytecodeOffset; + } + + unsigned line; + unsigned column; + stackFrame->computeLineAndColumn(line, column); + obj->putDirect(vm, vm.propertyNames->line, jsNumber(line), ReadOnly | DontDelete); + obj->putDirect(vm, vm.propertyNames->column, jsNumber(column), ReadOnly | DontDelete); + + if (!stackFrame->sourceURL.isEmpty()) + obj->putDirect(vm, vm.propertyNames->sourceURL, jsString(&vm, stackFrame->sourceURL), ReadOnly | DontDelete); + + if (!useCurrentFrame) + stackTrace.remove(0); + obj->putDirect(vm, vm.propertyNames->stack, vm.interpreter->stackTraceAsString(vm.topCallFrame, stackTrace), DontEnum); + + return true; + } + return false; +} + +void addErrorInfo(ExecState* exec, JSObject* obj, bool useCurrentFrame) +{ + CallFrame* callFrame = nullptr; + unsigned bytecodeOffset = 0; + addErrorInfoAndGetBytecodeOffset(exec, exec->vm(), obj, useCurrentFrame, callFrame, bytecodeOffset); +} + +JSObject* addErrorInfo(CallFrame* callFrame, JSObject* error, int line, const SourceCode& source) +{ + VM* vm = &callFrame->vm(); + const String& sourceURL = source.provider()->url(); + + if (line != -1) + error->putDirect(*vm, Identifier::fromString(vm, linePropertyName), jsNumber(line), ReadOnly | DontDelete); + if (!sourceURL.isNull()) + error->putDirect(*vm, Identifier::fromString(vm, sourceURLPropertyName), jsString(vm, sourceURL), ReadOnly | DontDelete); + return error; +} + + +bool hasErrorInfo(ExecState* exec, JSObject* error) +{ + return error->hasProperty(exec, Identifier::fromString(exec, linePropertyName)) + || error->hasProperty(exec, Identifier::fromString(exec, sourceURLPropertyName)); +} + +JSObject* throwTypeError(ExecState* exec) +{ + return exec->vm().throwException(exec, createTypeError(exec)); +} + +JSObject* throwSyntaxError(ExecState* exec) +{ + return exec->vm().throwException(exec, createSyntaxError(exec, ASCIILiteral("Syntax error"))); +} + + +JSObject* createError(ExecState* exec, const String& message) +{ + return createError(exec, message, nullptr); +} + +JSObject* createEvalError(ExecState* exec, const String& message) +{ + return createEvalError(exec, message, nullptr); +} + +JSObject* createRangeError(ExecState* exec, const String& message) +{ + return createRangeError(exec, message, nullptr); +} + +JSObject* createReferenceError(ExecState* exec, const String& message) +{ + return createReferenceError(exec, message, nullptr); +} + +JSObject* createSyntaxError(ExecState* exec, const String& message) +{ + return createSyntaxError(exec, message, nullptr); +} + +JSObject* createTypeError(ExecState* exec) +{ + return createTypeError(exec, ASCIILiteral("Type error")); +} + +JSObject* createTypeError(ExecState* exec, const String& message) +{ + return createTypeError(exec, message, nullptr, TypeNothing); +} + +JSObject* createNotEnoughArgumentsError(ExecState* exec) +{ + return createNotEnoughArgumentsError(exec, nullptr); +} + +JSObject* createURIError(ExecState* exec, const String& message) +{ + return createURIError(exec, message, nullptr); +} + +JSObject* createOutOfMemoryError(ExecState* exec) +{ + return createOutOfMemoryError(exec, nullptr); +} + + +const ClassInfo StrictModeTypeErrorFunction::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(StrictModeTypeErrorFunction) }; + +void StrictModeTypeErrorFunction::destroy(JSCell* cell) +{ + static_cast<StrictModeTypeErrorFunction*>(cell)->StrictModeTypeErrorFunction::~StrictModeTypeErrorFunction(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Error.h b/Source/JavaScriptCore/runtime/Error.h new file mode 100644 index 000000000..ec30a3dc4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Error.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef Error_h +#define Error_h + +#include "ErrorInstance.h" +#include "InternalFunction.h" +#include "Interpreter.h" +#include "JSObject.h" +#include <stdint.h> + + +namespace JSC { + +class ExecState; +class VM; +class JSGlobalObject; +class JSObject; +class SourceCode; +class Structure; + +// ExecState wrappers. +JSObject* createError(ExecState*, const String&, ErrorInstance::SourceAppender); +JSObject* createEvalError(ExecState*, const String&, ErrorInstance::SourceAppender); +JSObject* createRangeError(ExecState*, const String&, ErrorInstance::SourceAppender); +JSObject* createReferenceError(ExecState*, const String&, ErrorInstance::SourceAppender); +JSObject* createSyntaxError(ExecState*, const String&, ErrorInstance::SourceAppender); +JSObject* createTypeError(ExecState*, const String&, ErrorInstance::SourceAppender, RuntimeType); +JSObject* createNotEnoughArgumentsError(ExecState*, ErrorInstance::SourceAppender); +JSObject* createURIError(ExecState*, const String&, ErrorInstance::SourceAppender); +JSObject* createOutOfMemoryError(ExecState*, ErrorInstance::SourceAppender); + + +JS_EXPORT_PRIVATE JSObject* createError(ExecState*, const String&); +JS_EXPORT_PRIVATE JSObject* createEvalError(ExecState*, const String&); +JS_EXPORT_PRIVATE JSObject* createRangeError(ExecState*, const String&); +JS_EXPORT_PRIVATE JSObject* createReferenceError(ExecState*, const String&); +JS_EXPORT_PRIVATE JSObject* createSyntaxError(ExecState*, const String&); +JS_EXPORT_PRIVATE JSObject* createTypeError(ExecState*); +JS_EXPORT_PRIVATE JSObject* createTypeError(ExecState*, const String&); +JS_EXPORT_PRIVATE JSObject* createNotEnoughArgumentsError(ExecState*); +JS_EXPORT_PRIVATE JSObject* createURIError(ExecState*, const String&); +JS_EXPORT_PRIVATE JSObject* createOutOfMemoryError(ExecState*); + + +bool addErrorInfoAndGetBytecodeOffset(ExecState*, VM&, JSObject*, bool, CallFrame*&, unsigned&); + +bool hasErrorInfo(ExecState*, JSObject* error); +JS_EXPORT_PRIVATE void addErrorInfo(ExecState*, JSObject*, bool); +JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&); + +// Methods to throw Errors. + +// Convenience wrappers, create an throw an exception with a default message. +JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*); +JS_EXPORT_PRIVATE JSObject* throwSyntaxError(ExecState*); +inline JSObject* throwRangeError(ExecState* state, const String& errorMessage) { return state->vm().throwException(state, createRangeError(state, errorMessage)); } + +// Convenience wrappers, wrap result as an EncodedJSValue. +inline void throwVMError(ExecState* exec, Exception* exception) { exec->vm().throwException(exec, exception); } +inline EncodedJSValue throwVMError(ExecState* exec, JSValue error) { return JSValue::encode(exec->vm().throwException(exec, error)); } +inline EncodedJSValue throwVMTypeError(ExecState* exec) { return JSValue::encode(throwTypeError(exec)); } +inline EncodedJSValue throwVMTypeError(ExecState* exec, const String& errorMessage) { return JSValue::encode(throwTypeError(exec, errorMessage)); } +inline EncodedJSValue throwVMRangeError(ExecState* state, const String& errorMessage) { return JSValue::encode(throwRangeError(state, errorMessage)); } + +class StrictModeTypeErrorFunction : public InternalFunction { +private: + StrictModeTypeErrorFunction(VM& vm, Structure* structure, const String& message) + : InternalFunction(vm, structure) + , m_message(message) + { + } + + static void destroy(JSCell*); + +public: + typedef InternalFunction Base; + + static StrictModeTypeErrorFunction* create(VM& vm, Structure* structure, const String& message) + { + StrictModeTypeErrorFunction* function = new (NotNull, allocateCell<StrictModeTypeErrorFunction>(vm.heap)) StrictModeTypeErrorFunction(vm, structure, message); + function->finishCreation(vm, String()); + return function; + } + + static EncodedJSValue JSC_HOST_CALL constructThrowTypeError(ExecState* exec) + { + throwTypeError(exec, static_cast<StrictModeTypeErrorFunction*>(exec->callee())->m_message); + return JSValue::encode(jsNull()); + } + + static ConstructType getConstructData(JSCell*, ConstructData& constructData) + { + constructData.native.function = constructThrowTypeError; + return ConstructTypeHost; + } + + static EncodedJSValue JSC_HOST_CALL callThrowTypeError(ExecState* exec) + { + throwTypeError(exec, static_cast<StrictModeTypeErrorFunction*>(exec->callee())->m_message); + return JSValue::encode(jsNull()); + } + + static CallType getCallData(JSCell*, CallData& callData) + { + callData.native.function = callThrowTypeError; + return CallTypeHost; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + String m_message; +}; + +} // namespace JSC + +#endif // Error_h diff --git a/Source/JavaScriptCore/runtime/ErrorConstructor.cpp b/Source/JavaScriptCore/runtime/ErrorConstructor.cpp new file mode 100644 index 000000000..30c4faae5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorConstructor.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ErrorConstructor.h" + +#include "ErrorPrototype.h" +#include "Interpreter.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ErrorConstructor); + +const ClassInfo ErrorConstructor::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(ErrorConstructor) }; + +ErrorConstructor::ErrorConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void ErrorConstructor::finishCreation(VM& vm, ErrorPrototype* errorPrototype) +{ + Base::finishCreation(vm, errorPrototype->classInfo()->className); + // ECMA 15.11.3.1 Error.prototype + putDirectWithoutTransition(vm, vm.propertyNames->prototype, errorPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), DontDelete | ReadOnly | DontEnum); +} + +// ECMA 15.9.3 + +EncodedJSValue JSC_HOST_CALL Interpreter::constructWithErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = asInternalFunction(exec->callee())->globalObject()->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message, nullptr, TypeNothing, false)); +} + +ConstructType ErrorConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = Interpreter::constructWithErrorConstructor; + return ConstructTypeHost; +} + +EncodedJSValue JSC_HOST_CALL Interpreter::callErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = asInternalFunction(exec->callee())->globalObject()->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message, nullptr, TypeNothing, false)); +} + +CallType ErrorConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = Interpreter::callErrorConstructor; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorConstructor.h b/Source/JavaScriptCore/runtime/ErrorConstructor.h new file mode 100644 index 000000000..b11894dbe --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorConstructor.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ErrorConstructor_h +#define ErrorConstructor_h + +#include "ErrorInstance.h" +#include "InternalFunction.h" + +namespace JSC { + +class ErrorPrototype; + +class ErrorConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + + static ErrorConstructor* create(VM& vm, Structure* structure, ErrorPrototype* errorPrototype) + { + ErrorConstructor* constructor = new (NotNull, allocateCell<ErrorConstructor>(vm.heap)) ErrorConstructor(vm, structure); + constructor->finishCreation(vm, errorPrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, ErrorPrototype*); + +private: + ErrorConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} // namespace JSC + +#endif // ErrorConstructor_h diff --git a/Source/JavaScriptCore/runtime/ErrorHandlingScope.cpp b/Source/JavaScriptCore/runtime/ErrorHandlingScope.cpp new file mode 100644 index 000000000..beb52a334 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorHandlingScope.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ErrorHandlingScope.h" + +#include "Interpreter.h" +#include "Options.h" +#include "VM.h" + +namespace JSC { + +ErrorHandlingScope::ErrorHandlingScope(VM& vm) + : m_vm(vm) +{ + RELEASE_ASSERT(m_vm.stackPointerAtVMEntry()); + size_t newReservedZoneSize = Options::errorModeReservedZoneSize(); + m_savedReservedZoneSize = m_vm.updateReservedZoneSize(newReservedZoneSize); +#if !ENABLE(JIT) + m_vm.interpreter->stack().setReservedZoneSize(newReservedZoneSize); +#endif +} + +ErrorHandlingScope::~ErrorHandlingScope() +{ + RELEASE_ASSERT(m_vm.stackPointerAtVMEntry()); + m_vm.updateReservedZoneSize(m_savedReservedZoneSize); +#if !ENABLE(JIT) + m_vm.interpreter->stack().setReservedZoneSize(m_savedReservedZoneSize); +#endif +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorHandlingScope.h b/Source/JavaScriptCore/runtime/ErrorHandlingScope.h new file mode 100644 index 000000000..451b241e4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorHandlingScope.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ErrorHandlingScope_h +#define ErrorHandlingScope_h + +namespace JSC { + +class VM; + +class ErrorHandlingScope { +public: + JS_EXPORT_PRIVATE ErrorHandlingScope(VM&); + JS_EXPORT_PRIVATE ~ErrorHandlingScope(); +private: + VM& m_vm; + size_t m_savedReservedZoneSize; +}; + +} // namespace JSC + +#endif // ErrorHandlingScope_h + diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.cpp b/Source/JavaScriptCore/runtime/ErrorInstance.cpp new file mode 100644 index 000000000..2bf1493bd --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorInstance.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ErrorInstance.h" + +#include "JSScope.h" +#include "JSCInlines.h" +#include "JSGlobalObjectFunctions.h" +#include <wtf/Vector.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ErrorInstance); + +const ClassInfo ErrorInstance::s_info = { "Error", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(ErrorInstance) }; + +ErrorInstance::ErrorInstance(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} + +static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception, unsigned bytecodeOffset) +{ + ErrorInstance::SourceAppender appender = exception->sourceAppender(); + exception->clearSourceAppender(); + RuntimeType type = exception->runtimeTypeForCause(); + exception->clearRuntimeTypeForCause(); + + if (!callFrame->codeBlock()->hasExpressionInfo()) + return; + + int startOffset = 0; + int endOffset = 0; + int divotPoint = 0; + unsigned line = 0; + unsigned column = 0; + + CodeBlock* codeBlock = callFrame->codeBlock(); + codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divotPoint, startOffset, endOffset, line, column); + + int expressionStart = divotPoint - startOffset; + int expressionStop = divotPoint + endOffset; + + const String& sourceString = codeBlock->source()->source(); + if (!expressionStop || expressionStart > static_cast<int>(sourceString.length())) + return; + + VM* vm = &callFrame->vm(); + JSValue jsMessage = exception->getDirect(*vm, vm->propertyNames->message); + if (!jsMessage || !jsMessage.isString()) + return; + + String message = asString(jsMessage)->value(callFrame); + if (expressionStart < expressionStop) + message = appender(message, codeBlock->source()->getRange(expressionStart, expressionStop) , type, ErrorInstance::FoundExactSource); + else { + // No range information, so give a few characters of context. + const StringImpl* data = sourceString.impl(); + int dataLength = sourceString.length(); + int start = expressionStart; + int stop = expressionStart; + // Get up to 20 characters of context to the left and right of the divot, clamping to the line. + // Then strip whitespace. + while (start > 0 && (expressionStart - start < 20) && (*data)[start - 1] != '\n') + start--; + while (start < (expressionStart - 1) && isStrWhiteSpace((*data)[start])) + start++; + while (stop < dataLength && (stop - expressionStart < 20) && (*data)[stop] != '\n') + stop++; + while (stop > expressionStart && isStrWhiteSpace((*data)[stop - 1])) + stop--; + message = appender(message, codeBlock->source()->getRange(start, stop), type, ErrorInstance::FoundApproximateSource); + } + exception->putDirect(*vm, vm->propertyNames->message, jsString(vm, message)); + +} + +class FindFirstCallerFrameWithCodeblockFunctor { +public: + FindFirstCallerFrameWithCodeblockFunctor(CallFrame* startCallFrame) + : m_startCallFrame(startCallFrame) + , m_foundCallFrame(nullptr) + , m_foundStartCallFrame(false) + , m_index(0) + { } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + if (!m_foundStartCallFrame && (visitor->callFrame() == m_startCallFrame)) + m_foundStartCallFrame = true; + + if (m_foundStartCallFrame) { + if (visitor->callFrame()->codeBlock()) { + m_foundCallFrame = visitor->callFrame(); + return StackVisitor::Done; + } + m_index++; + } + + return StackVisitor::Continue; + } + + CallFrame* foundCallFrame() const { return m_foundCallFrame; } + unsigned index() const { return m_index; } + +private: + CallFrame* m_startCallFrame; + CallFrame* m_foundCallFrame; + bool m_foundStartCallFrame; + unsigned m_index; +}; + +static bool addErrorInfoAndGetBytecodeOffset(ExecState* exec, VM& vm, JSObject* obj, bool useCurrentFrame, CallFrame*& callFrame, unsigned &bytecodeOffset) +{ + Vector<StackFrame> stackTrace = Vector<StackFrame>(); + + if (exec && stackTrace.isEmpty()) + vm.interpreter->getStackTrace(stackTrace); + + if (!stackTrace.isEmpty()) { + + ASSERT(exec == vm.topCallFrame || exec == exec->lexicalGlobalObject()->globalExec() || exec == exec->vmEntryGlobalObject()->globalExec()); + + StackFrame* stackFrame; + for (unsigned i = 0 ; i < stackTrace.size(); ++i) { + stackFrame = &stackTrace.at(i); + if (stackFrame->bytecodeOffset) + break; + } + + if (bytecodeOffset) { + FindFirstCallerFrameWithCodeblockFunctor functor(exec); + vm.topCallFrame->iterate(functor); + callFrame = functor.foundCallFrame(); + unsigned stackIndex = functor.index(); + bytecodeOffset = stackTrace.at(stackIndex).bytecodeOffset; + } + + unsigned line; + unsigned column; + stackFrame->computeLineAndColumn(line, column); + obj->putDirect(vm, vm.propertyNames->line, jsNumber(line), ReadOnly | DontDelete); + obj->putDirect(vm, vm.propertyNames->column, jsNumber(column), ReadOnly | DontDelete); + + if (!stackFrame->sourceURL.isEmpty()) + obj->putDirect(vm, vm.propertyNames->sourceURL, jsString(&vm, stackFrame->sourceURL), ReadOnly | DontDelete); + + if (!useCurrentFrame) + stackTrace.remove(0); + obj->putDirect(vm, vm.propertyNames->stack, vm.interpreter->stackTraceAsString(vm.topCallFrame, stackTrace), DontEnum); + + return true; + } + return false; +} + +void ErrorInstance::finishCreation(ExecState* exec, VM& vm, const String& message, bool useCurrentFrame) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + if (!message.isNull()) + putDirect(vm, vm.propertyNames->message, jsString(&vm, message), DontEnum); + + unsigned bytecodeOffset = hasSourceAppender(); + CallFrame* callFrame = nullptr; + bool hasTrace = addErrorInfoAndGetBytecodeOffset(exec, vm, this, useCurrentFrame, callFrame, bytecodeOffset); + + if (hasTrace && callFrame && hasSourceAppender()) { + if (callFrame && callFrame->codeBlock()) + appendSourceToError(callFrame, this, bytecodeOffset); + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.h b/Source/JavaScriptCore/runtime/ErrorInstance.h new file mode 100644 index 000000000..3f584a99f --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorInstance.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ErrorInstance_h +#define ErrorInstance_h + +#include "Interpreter.h" +#include "RuntimeType.h" +#include "SourceProvider.h" +#include <wtf/Vector.h> + +namespace JSC { + +class ErrorInstance : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + enum SourceTextWhereErrorOccurred { FoundExactSource, FoundApproximateSource }; + typedef String (*SourceAppender) (const String& originalMessage, const String& sourceText, RuntimeType, SourceTextWhereErrorOccurred); + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ErrorInstanceType, StructureFlags), info()); + } + + static ErrorInstance* create(ExecState* exec, VM& vm, Structure* structure, const String& message, SourceAppender appender = nullptr, RuntimeType type = TypeNothing, bool useCurrentFrame = true) + { + ErrorInstance* instance = new (NotNull, allocateCell<ErrorInstance>(vm.heap)) ErrorInstance(vm, structure); + instance->m_sourceAppender = appender; + instance->m_runtimeTypeForCause = type; + instance->finishCreation(exec, vm, message, useCurrentFrame); + return instance; + } + + static ErrorInstance* create(ExecState* exec, Structure* structure, JSValue message, SourceAppender appender = nullptr, RuntimeType type = TypeNothing, bool useCurrentFrame = true) + { + return create(exec, exec->vm(), structure, message.isUndefined() ? String() : message.toString(exec)->value(exec), appender, type, useCurrentFrame); + } + + static void addErrorInfo(ExecState*, VM&, JSObject*, bool = true); + + bool hasSourceAppender() const { return !!m_sourceAppender; } + SourceAppender sourceAppender() const { return m_sourceAppender; } + void setSourceAppender(SourceAppender appender) { m_sourceAppender = appender; } + void clearSourceAppender() { m_sourceAppender = nullptr; } + void setRuntimeTypeForCause(RuntimeType type) { m_runtimeTypeForCause = type; } + RuntimeType runtimeTypeForCause() const { return m_runtimeTypeForCause; } + void clearRuntimeTypeForCause() { m_runtimeTypeForCause = TypeNothing; } + +protected: + explicit ErrorInstance(VM&, Structure*); + + void finishCreation(ExecState*, VM&, const String&, bool useCurrentFrame = true); + + SourceAppender m_sourceAppender { nullptr }; + RuntimeType m_runtimeTypeForCause { TypeNothing }; +}; + +} // namespace JSC + +#endif // ErrorInstance_h diff --git a/Source/JavaScriptCore/runtime/ErrorPrototype.cpp b/Source/JavaScriptCore/runtime/ErrorPrototype.cpp new file mode 100644 index 000000000..5bc2ec3c8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorPrototype.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ErrorPrototype.h" + +#include "Error.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "ObjectPrototype.h" +#include "JSCInlines.h" +#include "StringRecursionChecker.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ErrorPrototype); + +static EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState*); + +} + +#include "ErrorPrototype.lut.h" + +namespace JSC { + +const ClassInfo ErrorPrototype::s_info = { "Error", &ErrorInstance::s_info, &errorPrototypeTable, CREATE_METHOD_TABLE(ErrorPrototype) }; + +/* Source for ErrorPrototype.lut.h +@begin errorPrototypeTable + toString errorProtoFuncToString DontEnum|Function 0 +@end +*/ + +ErrorPrototype::ErrorPrototype(VM& vm, Structure* structure) + : ErrorInstance(vm, structure) +{ +} + +void ErrorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(globalObject->globalExec(), vm, ""); + ASSERT(inherits(info())); + putDirect(vm, vm.propertyNames->name, jsNontrivialString(&vm, String(ASCIILiteral("Error"))), DontEnum); +} + +bool ErrorPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<ErrorInstance>(exec, errorPrototypeTable, jsCast<ErrorPrototype*>(object), propertyName, slot); +} + +// ------------------------------ Functions --------------------------- + +// ECMA-262 5.1, 15.11.4.4 +EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec) +{ + // 1. Let O be the this value. + JSValue thisValue = exec->thisValue(); + + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisValue.isObject()) + return throwVMTypeError(exec); + JSObject* thisObj = asObject(thisValue); + + // Guard against recursion! + StringRecursionChecker checker(exec, thisObj); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + // 3. Let name be the result of calling the [[Get]] internal method of O with argument "name". + JSValue name = thisObj->get(exec, exec->propertyNames().name); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 4. If name is undefined, then let name be "Error"; else let name be ToString(name). + String nameString; + if (name.isUndefined()) + nameString = ASCIILiteral("Error"); + else { + nameString = name.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + // 5. Let msg be the result of calling the [[Get]] internal method of O with argument "message". + JSValue message = thisObj->get(exec, exec->propertyNames().message); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // (sic) + // 6. If msg is undefined, then let msg be the empty String; else let msg be ToString(msg). + // 7. If msg is undefined, then let msg be the empty String; else let msg be ToString(msg). + String messageString; + if (message.isUndefined()) + messageString = String(); + else { + messageString = message.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + // 8. If name is the empty String, return msg. + if (!nameString.length()) + return JSValue::encode(message.isString() ? message : jsString(exec, messageString)); + + // 9. If msg is the empty String, return name. + if (!messageString.length()) + return JSValue::encode(name.isString() ? name : jsNontrivialString(exec, nameString)); + + // 10. Return the result of concatenating name, ":", a single space character, and msg. + return JSValue::encode(jsMakeNontrivialString(exec, nameString, ": ", messageString)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ErrorPrototype.h b/Source/JavaScriptCore/runtime/ErrorPrototype.h new file mode 100644 index 000000000..29cd6a948 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ErrorPrototype.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ErrorPrototype_h +#define ErrorPrototype_h + +#include "ErrorInstance.h" + +namespace JSC { + +class ObjectPrototype; + +class ErrorPrototype : public ErrorInstance { +public: + typedef ErrorInstance Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static ErrorPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + ErrorPrototype* prototype = new (NotNull, allocateCell<ErrorPrototype>(vm.heap)) ErrorPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ErrorInstanceType, StructureFlags), info()); + } + +protected: + ErrorPrototype(VM&, Structure*); + void finishCreation(VM&, JSGlobalObject*); + +private: + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // ErrorPrototype_h diff --git a/Source/JavaScriptCore/runtime/Exception.cpp b/Source/JavaScriptCore/runtime/Exception.cpp new file mode 100644 index 000000000..4f5fcb981 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Exception.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "Exception.h" + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo Exception::s_info = { "Exception", &Base::s_info, 0, CREATE_METHOD_TABLE(Exception) }; + +Exception* Exception::create(VM& vm, JSValue thrownValue, StackCaptureAction action) +{ + Exception* result = new (NotNull, allocateCell<Exception>(vm.heap)) Exception(vm); + result->finishCreation(vm, thrownValue, action); + return result; +} + +void Exception::destroy(JSCell* cell) +{ + Exception* exception = static_cast<Exception*>(cell); + exception->~Exception(); +} + +Structure* Exception::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void Exception::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Exception* thisObject = jsCast<Exception*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_value); +} + +Exception::Exception(VM& vm) + : Base(vm, vm.exceptionStructure.get()) +{ +} + +Exception::~Exception() +{ +} + +void Exception::finishCreation(VM& vm, JSValue thrownValue, StackCaptureAction action) +{ + Base::finishCreation(vm); + + m_value.set(vm, this, thrownValue); + + Vector<StackFrame> stackTrace; + if (action == StackCaptureAction::CaptureStack) + vm.interpreter->getStackTrace(stackTrace); + m_stack = RefCountedArray<StackFrame>(stackTrace); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Exception.h b/Source/JavaScriptCore/runtime/Exception.h new file mode 100644 index 000000000..491cb4994 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Exception.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Exception_h +#define Exception_h + +#include "Interpreter.h" +#include <wtf/RefCountedArray.h> + +namespace JSC { + +class Exception : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags; + + enum StackCaptureAction { + CaptureStack, + DoNotCaptureStack + }; + JS_EXPORT_PRIVATE static Exception* create(VM&, JSValue thrownValue, StackCaptureAction = CaptureStack); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + static void visitChildren(JSCell*, SlotVisitor&); + + DECLARE_EXPORT_INFO; + + static ptrdiff_t valueOffset() + { + return OBJECT_OFFSETOF(Exception, m_value); + } + + JSValue value() const { return m_value.get(); } + const RefCountedArray<StackFrame>& stack() const { return m_stack; } + + bool didNotifyInspectorOfThrow() const { return m_didNotifyInspectorOfThrow; } + void setDidNotifyInspectorOfThrow() { m_didNotifyInspectorOfThrow = true; } + + ~Exception(); + +private: + Exception(VM&); + void finishCreation(VM&, JSValue thrownValue, StackCaptureAction); + + WriteBarrier<Unknown> m_value; + RefCountedArray<StackFrame> m_stack; + bool m_didNotifyInspectorOfThrow { false }; + + friend class LLIntOffsetsExtractor; +}; + +} // namespace JSC + +#endif // Exception_h diff --git a/Source/JavaScriptCore/runtime/ExceptionFuzz.cpp b/Source/JavaScriptCore/runtime/ExceptionFuzz.cpp new file mode 100644 index 000000000..744b1a60d --- /dev/null +++ b/Source/JavaScriptCore/runtime/ExceptionFuzz.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ExceptionFuzz.h" + +#include "Error.h" +#include "JSCInlines.h" +#include "TestRunnerUtils.h" + +namespace JSC { + +static unsigned s_numberOfExceptionFuzzChecks; +unsigned numberOfExceptionFuzzChecks() { return s_numberOfExceptionFuzzChecks; } + +// Call this only if you know that exception fuzzing is enabled. +void doExceptionFuzzing(ExecState* exec, const char* where, void* returnPC) +{ + ASSERT(Options::enableExceptionFuzz()); + + DeferGCForAWhile deferGC(exec->vm().heap); + + s_numberOfExceptionFuzzChecks++; + + unsigned fireTarget = Options::fireExceptionFuzzAt(); + if (fireTarget == s_numberOfExceptionFuzzChecks) { + printf("JSC EXCEPTION FUZZ: Throwing fuzz exception with call frame %p, seen in %s and return address %p.\n", exec, where, returnPC); + exec->vm().throwException( + exec, createError(exec, ASCIILiteral("Exception Fuzz"))); + } +} + +} // namespace JSC + + diff --git a/Source/JavaScriptCore/runtime/ExceptionFuzz.h b/Source/JavaScriptCore/runtime/ExceptionFuzz.h new file mode 100644 index 000000000..d9a6b4b82 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ExceptionFuzz.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ExceptionFuzz_h +#define ExceptionFuzz_h + +#include "Options.h" + +namespace JSC { + +class ExecState; + +// Call this only if you know that exception fuzzing is enabled. +void doExceptionFuzzing(ExecState* exec, const char* where, void* returnPC); + +// This is what you should call if you don't know if fuzzing is enabled. +ALWAYS_INLINE void doExceptionFuzzingIfEnabled(ExecState* exec, const char* where, void* returnPC) +{ + if (LIKELY(!Options::enableExceptionFuzz())) + return; + doExceptionFuzzing(exec, where, returnPC); +} + +} // namespace JSC + +#endif // ExceptionFuzz_h + diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp new file mode 100644 index 000000000..46c0cef93 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "ExceptionHelpers.h" + +#include "CodeBlock.h" +#include "CallFrame.h" +#include "ErrorHandlingScope.h" +#include "Exception.h" +#include "JSGlobalObjectFunctions.h" +#include "JSNotAnObject.h" +#include "Interpreter.h" +#include "Nodes.h" +#include "JSCInlines.h" +#include "RuntimeType.h" +#include <wtf/text/StringBuilder.h> +#include <wtf/text/StringView.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(TerminatedExecutionError); + +const ClassInfo TerminatedExecutionError::s_info = { "TerminatedExecutionError", &Base::s_info, 0, CREATE_METHOD_TABLE(TerminatedExecutionError) }; + +JSValue TerminatedExecutionError::defaultValue(const JSObject*, ExecState* exec, PreferredPrimitiveType hint) +{ + if (hint == PreferString) + return jsNontrivialString(exec, String(ASCIILiteral("JavaScript execution terminated."))); + return JSValue(PNaN); +} + +JSObject* createTerminatedExecutionException(VM* vm) +{ + return TerminatedExecutionError::create(*vm); +} + +bool isTerminatedExecutionException(Exception* exception) +{ + return exception->value().inherits(TerminatedExecutionError::info()); +} + +JSObject* createStackOverflowError(ExecState* exec) +{ + return createRangeError(exec, ASCIILiteral("Maximum call stack size exceeded.")); +} + +JSObject* createUndefinedVariableError(ExecState* exec, const Identifier& ident) +{ + if (exec->propertyNames().isPrivateName(ident)) { + String message(makeString("Can't find private variable: @", exec->propertyNames().getPublicName(ident).string())); + return createReferenceError(exec, message); + } + String message(makeString("Can't find variable: ", ident.string())); + return createReferenceError(exec, message); +} + +JSString* errorDescriptionForValue(ExecState* exec, JSValue v) +{ + if (v.isString()) + return jsNontrivialString(exec, makeString('"', asString(v)->value(exec), '"')); + if (v.isObject()) { + CallData callData; + JSObject* object = asObject(v); + if (object->methodTable()->getCallData(object, callData) != CallTypeNone) + return exec->vm().smallStrings.functionString(); + return jsString(exec, JSObject::calculatedClassName(object)); + } + return v.toString(exec); +} + +static String defaultApproximateSourceError(const String& originalMessage, const String& sourceText) +{ + return makeString(originalMessage, " (near '...", sourceText, "...')"); +} + +static String defaultSourceAppender(const String& originalMessage, const String& sourceText, RuntimeType, ErrorInstance::SourceTextWhereErrorOccurred occurrence) +{ + if (occurrence == ErrorInstance::FoundApproximateSource) + return defaultApproximateSourceError(originalMessage, sourceText); + + ASSERT(occurrence == ErrorInstance::FoundExactSource); + return makeString(originalMessage, " (evaluating '", sourceText, "')"); +} + +static String functionCallBase(const String& sourceText) +{ + // This function retrieves the 'foo.bar' substring from 'foo.bar(baz)'. + // FIXME: This function has simple processing of /* */ style comments. + // It doesn't properly handle embedded comments of string literals that contain + // parenthesis or comment constructs, e.g. foo.bar("/abc\)*/"). + // https://bugs.webkit.org/show_bug.cgi?id=146304 + + unsigned sourceLength = sourceText.length(); + unsigned idx = sourceLength - 1; + if (sourceLength < 2 || sourceText[idx] != ')') { + // For function calls that have many new lines in between their open parenthesis + // and their closing parenthesis, the text range passed into the message appender + // will not inlcude the text in between these parentheses, it will just be the desired + // text that precedes the parentheses. + return sourceText; + } + + unsigned parenStack = 1; + bool isInMultiLineComment = false; + idx -= 1; + // Note that we're scanning text right to left instead of the more common left to right, + // so syntax detection is backwards. + while (parenStack > 0) { + UChar curChar = sourceText[idx]; + if (isInMultiLineComment) { + if (idx > 0 && curChar == '*' && sourceText[idx - 1] == '/') { + isInMultiLineComment = false; + idx -= 1; + } + } else if (curChar == '(') + parenStack -= 1; + else if (curChar == ')') + parenStack += 1; + else if (idx > 0 && curChar == '/' && sourceText[idx - 1] == '*') { + isInMultiLineComment = true; + idx -= 1; + } + + if (!idx) + break; + + idx -= 1; + } + + return sourceText.left(idx + 1); +} + +static String notAFunctionSourceAppender(const String& originalMessage, const String& sourceText, RuntimeType type, ErrorInstance::SourceTextWhereErrorOccurred occurrence) +{ + ASSERT(type != TypeFunction); + + if (occurrence == ErrorInstance::FoundApproximateSource) + return defaultApproximateSourceError(originalMessage, sourceText); + + ASSERT(occurrence == ErrorInstance::FoundExactSource); + auto notAFunctionIndex = originalMessage.reverseFind("is not a function"); + RELEASE_ASSERT(notAFunctionIndex != notFound); + StringView displayValue; + if (originalMessage.is8Bit()) + displayValue = StringView(originalMessage.characters8(), notAFunctionIndex - 1); + else + displayValue = StringView(originalMessage.characters16(), notAFunctionIndex - 1); + + String base = functionCallBase(sourceText); + StringBuilder builder; + builder.append(base); + builder.appendLiteral(" is not a function. (In '"); + builder.append(sourceText); + builder.appendLiteral("', '"); + builder.append(base); + builder.appendLiteral("' is "); + if (type == TypeObject) + builder.appendLiteral("an instance of "); + builder.append(displayValue); + builder.appendLiteral(")"); + + return builder.toString(); +} + +static String invalidParameterInSourceAppender(const String& originalMessage, const String& sourceText, RuntimeType type, ErrorInstance::SourceTextWhereErrorOccurred occurrence) +{ + ASSERT_UNUSED(type, type != TypeObject); + + if (occurrence == ErrorInstance::FoundApproximateSource) + return defaultApproximateSourceError(originalMessage, sourceText); + + ASSERT(occurrence == ErrorInstance::FoundExactSource); + auto inIndex = sourceText.reverseFind("in"); + RELEASE_ASSERT(inIndex != notFound); + if (sourceText.find("in") != inIndex) + return makeString(originalMessage, " (evaluating '", sourceText, "')"); + + static const unsigned inLength = 2; + String rightHandSide = sourceText.substring(inIndex + inLength).simplifyWhiteSpace(); + return makeString(rightHandSide, " is not an Object. (evaluating '", sourceText, "')"); +} + +static String invalidParameterInstanceofSourceAppender(const String& originalMessage, const String& sourceText, RuntimeType, ErrorInstance::SourceTextWhereErrorOccurred occurrence) +{ + if (occurrence == ErrorInstance::FoundApproximateSource) + return defaultApproximateSourceError(originalMessage, sourceText); + + ASSERT(occurrence == ErrorInstance::FoundExactSource); + auto instanceofIndex = sourceText.reverseFind("instanceof"); + RELEASE_ASSERT(instanceofIndex != notFound); + if (sourceText.find("instanceof") != instanceofIndex) + return makeString(originalMessage, " (evaluating '", sourceText, "')"); + + static const unsigned instanceofLength = 10; + String rightHandSide = sourceText.substring(instanceofIndex + instanceofLength).simplifyWhiteSpace(); + return makeString(rightHandSide, " is not a function. (evaluating '", sourceText, "')"); +} + +JSObject* createError(ExecState* exec, JSValue value, const String& message, ErrorInstance::SourceAppender appender) +{ + String errorMessage = makeString(errorDescriptionForValue(exec, value)->value(exec), ' ', message); + JSObject* exception = createTypeError(exec, errorMessage, appender, runtimeTypeForValue(value)); + ASSERT(exception->isErrorInstance()); + return exception; +} + +JSObject* createInvalidFunctionApplyParameterError(ExecState* exec, JSValue value) +{ + JSObject* exception = createTypeError(exec, makeString("second argument to Function.prototype.apply must be an Array-like object"), defaultSourceAppender, runtimeTypeForValue(value)); + ASSERT(exception->isErrorInstance()); + return exception; +} + +JSObject* createInvalidInParameterError(ExecState* exec, JSValue value) +{ + return createError(exec, value, makeString("is not an Object."), invalidParameterInSourceAppender); +} + +JSObject* createInvalidInstanceofParameterError(ExecState* exec, JSValue value) +{ + return createError(exec, value, makeString("is not a function."), invalidParameterInstanceofSourceAppender); +} + +JSObject* createNotAConstructorError(ExecState* exec, JSValue value) +{ + return createError(exec, value, ASCIILiteral("is not a constructor"), defaultSourceAppender); +} + +JSObject* createNotAFunctionError(ExecState* exec, JSValue value) +{ + return createError(exec, value, ASCIILiteral("is not a function"), notAFunctionSourceAppender); +} + +JSObject* createNotAnObjectError(ExecState* exec, JSValue value) +{ + return createError(exec, value, ASCIILiteral("is not an object"), defaultSourceAppender); +} + +JSObject* createErrorForInvalidGlobalAssignment(ExecState* exec, const String& propertyName) +{ + return createReferenceError(exec, makeString("Strict mode forbids implicit creation of global property '", propertyName, '\'')); +} + +JSObject* createTDZError(ExecState* exec) +{ + return createReferenceError(exec, "Cannot access uninitialized variable."); +} + +JSObject* throwOutOfMemoryError(ExecState* exec) +{ + return exec->vm().throwException(exec, createOutOfMemoryError(exec)); +} + +JSObject* throwStackOverflowError(ExecState* exec) +{ + VM& vm = exec->vm(); + ErrorHandlingScope errorScope(vm); + return vm.throwException(exec, createStackOverflowError(exec)); +} + +JSObject* throwTerminatedExecutionException(ExecState* exec) +{ + VM& vm = exec->vm(); + ErrorHandlingScope errorScope(vm); + return vm.throwException(exec, createTerminatedExecutionException(&vm)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.h b/Source/JavaScriptCore/runtime/ExceptionHelpers.h new file mode 100644 index 000000000..c6ba02e3f --- /dev/null +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef ExceptionHelpers_h +#define ExceptionHelpers_h + +#include "ErrorInstance.h" +#include "JSObject.h" + +namespace JSC { + +typedef JSObject* (*ErrorFactory)(ExecState*, const String&, ErrorInstance::SourceAppender); + +JSObject* createTerminatedExecutionException(VM*); +JS_EXPORT_PRIVATE bool isTerminatedExecutionException(Exception*); +JS_EXPORT_PRIVATE JSObject* createError(ExecState*, JSValue, const String&, ErrorInstance::SourceAppender); +JS_EXPORT_PRIVATE JSObject* createStackOverflowError(ExecState*); +JSObject* createUndefinedVariableError(ExecState*, const Identifier&); +JSObject* createTDZError(ExecState*); +JSObject* createNotAnObjectError(ExecState*, JSValue); +JSObject* createInvalidFunctionApplyParameterError(ExecState*, JSValue); +JSObject* createInvalidInParameterError(ExecState*, JSValue); +JSObject* createInvalidInstanceofParameterError(ExecState*, JSValue); +JSObject* createNotAConstructorError(ExecState*, JSValue); +JSObject* createNotAFunctionError(ExecState*, JSValue); +JSObject* createErrorForInvalidGlobalAssignment(ExecState*, const String&); +JSString* errorDescriptionForValue(ExecState*, JSValue); + +JS_EXPORT_PRIVATE JSObject* throwOutOfMemoryError(ExecState*); +JS_EXPORT_PRIVATE JSObject* throwStackOverflowError(ExecState*); +JS_EXPORT_PRIVATE JSObject* throwTerminatedExecutionException(ExecState*); + + +class TerminatedExecutionError final : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static TerminatedExecutionError* create(VM& vm) + { + TerminatedExecutionError* error = new (NotNull, allocateCell<TerminatedExecutionError>(vm.heap)) TerminatedExecutionError(vm); + error->finishCreation(vm); + return error; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + DECLARE_EXPORT_INFO; + +private: + explicit TerminatedExecutionError(VM& vm) + : JSNonFinalObject(vm, vm.terminatedExecutionErrorStructure.get()) + { + } + + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + +}; + +} // namespace JSC + +#endif // ExceptionHelpers_h diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp new file mode 100644 index 000000000..0ab167211 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -0,0 +1,662 @@ +/* + * Copyright (C) 2009, 2010, 2013, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "Executable.h" + +#include "BatchedTransitionOptimizer.h" +#include "CodeBlock.h" +#include "DFGDriver.h" +#include "JIT.h" +#include "JSCInlines.h" +#include "LLIntEntrypoint.h" +#include "Parser.h" +#include "ProfilerDatabase.h" +#include "TypeProfiler.h" +#include <wtf/CommaPrinter.h> +#include <wtf/Vector.h> +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +const ClassInfo ExecutableBase::s_info = { "Executable", 0, 0, CREATE_METHOD_TABLE(ExecutableBase) }; + +void ExecutableBase::destroy(JSCell* cell) +{ + static_cast<ExecutableBase*>(cell)->ExecutableBase::~ExecutableBase(); +} + +void ExecutableBase::clearCode() +{ +#if ENABLE(JIT) + m_jitCodeForCall = nullptr; + m_jitCodeForConstruct = nullptr; + m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr(); + m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr(); + m_jitCodeForCallWithArityCheckAndPreserveRegs = MacroAssemblerCodePtr(); + m_jitCodeForConstructWithArityCheckAndPreserveRegs = MacroAssemblerCodePtr(); +#endif + m_numParametersForCall = NUM_PARAMETERS_NOT_COMPILED; + m_numParametersForConstruct = NUM_PARAMETERS_NOT_COMPILED; +} + +#if ENABLE(DFG_JIT) +Intrinsic ExecutableBase::intrinsic() const +{ + if (const NativeExecutable* nativeExecutable = jsDynamicCast<const NativeExecutable*>(this)) + return nativeExecutable->intrinsic(); + return NoIntrinsic; +} +#else +Intrinsic ExecutableBase::intrinsic() const +{ + return NoIntrinsic; +} +#endif + +const ClassInfo NativeExecutable::s_info = { "NativeExecutable", &ExecutableBase::s_info, 0, CREATE_METHOD_TABLE(NativeExecutable) }; + +void NativeExecutable::destroy(JSCell* cell) +{ + static_cast<NativeExecutable*>(cell)->NativeExecutable::~NativeExecutable(); +} + +#if ENABLE(DFG_JIT) +Intrinsic NativeExecutable::intrinsic() const +{ + return m_intrinsic; +} +#endif + +const ClassInfo ScriptExecutable::s_info = { "ScriptExecutable", &ExecutableBase::s_info, 0, CREATE_METHOD_TABLE(ScriptExecutable) }; + +ScriptExecutable::ScriptExecutable(Structure* structure, VM& vm, const SourceCode& source, bool isInStrictContext) + : ExecutableBase(vm, structure, NUM_PARAMETERS_NOT_COMPILED) + , m_source(source) + , m_features(isInStrictContext ? StrictModeFeature : 0) + , m_hasCapturedVariables(false) + , m_neverInline(false) + , m_didTryToEnterInLoop(false) + , m_overrideLineNumber(-1) + , m_firstLine(-1) + , m_lastLine(-1) + , m_startColumn(UINT_MAX) + , m_endColumn(UINT_MAX) + , m_typeProfilingStartOffset(UINT_MAX) + , m_typeProfilingEndOffset(UINT_MAX) +{ +} + +void ScriptExecutable::destroy(JSCell* cell) +{ + static_cast<ScriptExecutable*>(cell)->ScriptExecutable::~ScriptExecutable(); +} + +void ScriptExecutable::installCode(CodeBlock* genericCodeBlock) +{ + RELEASE_ASSERT(genericCodeBlock->ownerExecutable() == this); + RELEASE_ASSERT(JITCode::isExecutableScript(genericCodeBlock->jitType())); + + if (Options::verboseOSR()) + dataLog("Installing ", *genericCodeBlock, "\n"); + + VM& vm = *genericCodeBlock->vm(); + + if (vm.m_perBytecodeProfiler) + vm.m_perBytecodeProfiler->ensureBytecodesFor(genericCodeBlock); + + ASSERT(vm.heap.isDeferred()); + + CodeSpecializationKind kind = genericCodeBlock->specializationKind(); + + RefPtr<CodeBlock> oldCodeBlock; + + switch (kind) { + case CodeForCall: + m_jitCodeForCall = genericCodeBlock->jitCode(); + m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr(); + m_jitCodeForCallWithArityCheckAndPreserveRegs = MacroAssemblerCodePtr(); + m_numParametersForCall = genericCodeBlock->numParameters(); + break; + case CodeForConstruct: + m_jitCodeForConstruct = genericCodeBlock->jitCode(); + m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr(); + m_jitCodeForConstructWithArityCheckAndPreserveRegs = MacroAssemblerCodePtr(); + m_numParametersForConstruct = genericCodeBlock->numParameters(); + break; + } + + switch (genericCodeBlock->codeType()) { + case GlobalCode: { + ProgramExecutable* executable = jsCast<ProgramExecutable*>(this); + ProgramCodeBlock* codeBlock = static_cast<ProgramCodeBlock*>(genericCodeBlock); + + ASSERT(kind == CodeForCall); + + oldCodeBlock = executable->m_programCodeBlock; + executable->m_programCodeBlock = codeBlock; + break; + } + + case EvalCode: { + EvalExecutable* executable = jsCast<EvalExecutable*>(this); + EvalCodeBlock* codeBlock = static_cast<EvalCodeBlock*>(genericCodeBlock); + + ASSERT(kind == CodeForCall); + + oldCodeBlock = executable->m_evalCodeBlock; + executable->m_evalCodeBlock = codeBlock; + break; + } + + case FunctionCode: { + FunctionExecutable* executable = jsCast<FunctionExecutable*>(this); + FunctionCodeBlock* codeBlock = static_cast<FunctionCodeBlock*>(genericCodeBlock); + + switch (kind) { + case CodeForCall: + oldCodeBlock = executable->m_codeBlockForCall; + executable->m_codeBlockForCall = codeBlock; + break; + case CodeForConstruct: + oldCodeBlock = executable->m_codeBlockForConstruct; + executable->m_codeBlockForConstruct = codeBlock; + break; + } + break; + } } + + if (oldCodeBlock) + oldCodeBlock->unlinkIncomingCalls(); + + Debugger* debugger = genericCodeBlock->globalObject()->debugger(); + if (debugger) + debugger->registerCodeBlock(genericCodeBlock); + + Heap::heap(this)->writeBarrier(this); +} + +RefPtr<CodeBlock> ScriptExecutable::newCodeBlockFor( + CodeSpecializationKind kind, JSFunction* function, JSScope* scope, JSObject*& exception) +{ + VM* vm = scope->vm(); + + ASSERT(vm->heap.isDeferred()); + ASSERT(startColumn() != UINT_MAX); + ASSERT(endColumn() != UINT_MAX); + + if (classInfo() == EvalExecutable::info()) { + EvalExecutable* executable = jsCast<EvalExecutable*>(this); + RELEASE_ASSERT(kind == CodeForCall); + RELEASE_ASSERT(!executable->m_evalCodeBlock); + RELEASE_ASSERT(!function); + return adoptRef(new EvalCodeBlock( + executable, executable->m_unlinkedEvalCodeBlock.get(), scope, + executable->source().provider())); + } + + if (classInfo() == ProgramExecutable::info()) { + ProgramExecutable* executable = jsCast<ProgramExecutable*>(this); + RELEASE_ASSERT(kind == CodeForCall); + RELEASE_ASSERT(!executable->m_programCodeBlock); + RELEASE_ASSERT(!function); + return adoptRef(new ProgramCodeBlock( + executable, executable->m_unlinkedProgramCodeBlock.get(), scope, + executable->source().provider(), executable->source().startColumn())); + } + + RELEASE_ASSERT(classInfo() == FunctionExecutable::info()); + RELEASE_ASSERT(function); + FunctionExecutable* executable = jsCast<FunctionExecutable*>(this); + RELEASE_ASSERT(!executable->codeBlockFor(kind)); + JSGlobalObject* globalObject = scope->globalObject(); + ParserError error; + DebuggerMode debuggerMode = globalObject->hasDebugger() ? DebuggerOn : DebuggerOff; + ProfilerMode profilerMode = globalObject->hasProfiler() ? ProfilerOn : ProfilerOff; + UnlinkedFunctionCodeBlock* unlinkedCodeBlock = + executable->m_unlinkedExecutable->codeBlockFor( + *vm, executable->m_source, kind, debuggerMode, profilerMode, error); + recordParse(executable->m_unlinkedExecutable->features(), executable->m_unlinkedExecutable->hasCapturedVariables(), firstLine(), lastLine(), startColumn(), endColumn()); + if (!unlinkedCodeBlock) { + exception = vm->throwException( + globalObject->globalExec(), + error.toErrorObject(globalObject, executable->m_source)); + return nullptr; + } + + SourceProvider* provider = executable->source().provider(); + unsigned sourceOffset = executable->source().startOffset(); + unsigned startColumn = executable->source().startColumn(); + + return adoptRef(new FunctionCodeBlock( + executable, unlinkedCodeBlock, scope, provider, sourceOffset, startColumn)); +} + +PassRefPtr<CodeBlock> ScriptExecutable::newReplacementCodeBlockFor( + CodeSpecializationKind kind) +{ + if (classInfo() == EvalExecutable::info()) { + RELEASE_ASSERT(kind == CodeForCall); + EvalExecutable* executable = jsCast<EvalExecutable*>(this); + EvalCodeBlock* baseline = static_cast<EvalCodeBlock*>( + executable->m_evalCodeBlock->baselineVersion()); + RefPtr<EvalCodeBlock> result = adoptRef(new EvalCodeBlock( + CodeBlock::CopyParsedBlock, *baseline)); + result->setAlternative(baseline); + return result; + } + + if (classInfo() == ProgramExecutable::info()) { + RELEASE_ASSERT(kind == CodeForCall); + ProgramExecutable* executable = jsCast<ProgramExecutable*>(this); + ProgramCodeBlock* baseline = static_cast<ProgramCodeBlock*>( + executable->m_programCodeBlock->baselineVersion()); + RefPtr<ProgramCodeBlock> result = adoptRef(new ProgramCodeBlock( + CodeBlock::CopyParsedBlock, *baseline)); + result->setAlternative(baseline); + return result; + } + + RELEASE_ASSERT(classInfo() == FunctionExecutable::info()); + FunctionExecutable* executable = jsCast<FunctionExecutable*>(this); + FunctionCodeBlock* baseline = static_cast<FunctionCodeBlock*>( + executable->codeBlockFor(kind)->baselineVersion()); + RefPtr<FunctionCodeBlock> result = adoptRef(new FunctionCodeBlock( + CodeBlock::CopyParsedBlock, *baseline)); + result->setAlternative(baseline); + return result; +} + +static void setupLLInt(VM& vm, CodeBlock* codeBlock) +{ + LLInt::setEntrypoint(vm, codeBlock); +} + +static void setupJIT(VM& vm, CodeBlock* codeBlock) +{ +#if ENABLE(JIT) + CompilationResult result = JIT::compile(&vm, codeBlock, JITCompilationMustSucceed); + RELEASE_ASSERT(result == CompilationSuccessful); +#else + UNUSED_PARAM(vm); + UNUSED_PARAM(codeBlock); + UNREACHABLE_FOR_PLATFORM(); +#endif +} + +JSObject* ScriptExecutable::prepareForExecutionImpl( + ExecState* exec, JSFunction* function, JSScope* scope, CodeSpecializationKind kind) +{ + VM& vm = exec->vm(); + DeferGC deferGC(vm.heap); + + JSObject* exception = 0; + RefPtr<CodeBlock> codeBlock = newCodeBlockFor(kind, function, scope, exception); + if (!codeBlock) { + RELEASE_ASSERT(exception); + return exception; + } + + if (Options::validateBytecode()) + codeBlock->validate(); + + if (Options::useLLInt()) + setupLLInt(vm, codeBlock.get()); + else + setupJIT(vm, codeBlock.get()); + + installCode(codeBlock.get()); + return 0; +} + +const ClassInfo EvalExecutable::s_info = { "EvalExecutable", &ScriptExecutable::s_info, 0, CREATE_METHOD_TABLE(EvalExecutable) }; + +EvalExecutable* EvalExecutable::create(ExecState* exec, const SourceCode& source, bool isInStrictContext, ThisTDZMode thisTDZMode, const VariableEnvironment* variablesUnderTDZ) +{ + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + if (!globalObject->evalEnabled()) { + exec->vm().throwException(exec, createEvalError(exec, globalObject->evalDisabledErrorMessage())); + return 0; + } + + EvalExecutable* executable = new (NotNull, allocateCell<EvalExecutable>(*exec->heap())) EvalExecutable(exec, source, isInStrictContext); + executable->finishCreation(exec->vm()); + + UnlinkedEvalCodeBlock* unlinkedEvalCode = globalObject->createEvalCodeBlock(exec, executable, thisTDZMode, variablesUnderTDZ); + if (!unlinkedEvalCode) + return 0; + + executable->m_unlinkedEvalCodeBlock.set(exec->vm(), executable, unlinkedEvalCode); + + return executable; +} + +EvalExecutable::EvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext) + : ScriptExecutable(exec->vm().evalExecutableStructure.get(), exec->vm(), source, inStrictContext) +{ +} + +void EvalExecutable::destroy(JSCell* cell) +{ + static_cast<EvalExecutable*>(cell)->EvalExecutable::~EvalExecutable(); +} + +const ClassInfo ProgramExecutable::s_info = { "ProgramExecutable", &ScriptExecutable::s_info, 0, CREATE_METHOD_TABLE(ProgramExecutable) }; + +ProgramExecutable::ProgramExecutable(ExecState* exec, const SourceCode& source) + : ScriptExecutable(exec->vm().programExecutableStructure.get(), exec->vm(), source, false) +{ + m_typeProfilingStartOffset = 0; + m_typeProfilingEndOffset = source.length() - 1; + if (exec->vm().typeProfiler() || exec->vm().controlFlowProfiler()) + exec->vm().functionHasExecutedCache()->insertUnexecutedRange(sourceID(), m_typeProfilingStartOffset, m_typeProfilingEndOffset); +} + +void ProgramExecutable::destroy(JSCell* cell) +{ + static_cast<ProgramExecutable*>(cell)->ProgramExecutable::~ProgramExecutable(); +} + +const ClassInfo FunctionExecutable::s_info = { "FunctionExecutable", &ScriptExecutable::s_info, 0, CREATE_METHOD_TABLE(FunctionExecutable) }; + +FunctionExecutable::FunctionExecutable(VM& vm, const SourceCode& source, + UnlinkedFunctionExecutable* unlinkedExecutable, unsigned firstLine, + unsigned lastLine, unsigned startColumn, unsigned endColumn) + : ScriptExecutable(vm.functionExecutableStructure.get(), vm, source, unlinkedExecutable->isInStrictContext()) + , m_unlinkedExecutable(vm, this, unlinkedExecutable) +{ + RELEASE_ASSERT(!source.isNull()); + ASSERT(source.length()); + m_firstLine = firstLine; + m_lastLine = lastLine; + ASSERT(startColumn != UINT_MAX); + ASSERT(endColumn != UINT_MAX); + m_startColumn = startColumn; + m_endColumn = endColumn; + m_parametersStartOffset = unlinkedExecutable->parametersStartOffset(); + m_typeProfilingStartOffset = unlinkedExecutable->typeProfilingStartOffset(); + m_typeProfilingEndOffset = unlinkedExecutable->typeProfilingEndOffset(); +} + +void FunctionExecutable::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + m_singletonFunction.set(vm, this, InferredValue::create(vm)); +} + +void FunctionExecutable::destroy(JSCell* cell) +{ + static_cast<FunctionExecutable*>(cell)->FunctionExecutable::~FunctionExecutable(); +} + +inline const char* samplingDescription(JITCode::JITType jitType) +{ + switch (jitType) { + case JITCode::InterpreterThunk: + return "Interpreter Compilation (TOTAL)"; + case JITCode::BaselineJIT: + return "Baseline Compilation (TOTAL)"; + case JITCode::DFGJIT: + return "DFG Compilation (TOTAL)"; + case JITCode::FTLJIT: + return "FTL Compilation (TOTAL)"; + default: + RELEASE_ASSERT_NOT_REACHED(); + return 0; + } +} + +void EvalExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + EvalExecutable* thisObject = jsCast<EvalExecutable*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + ScriptExecutable::visitChildren(thisObject, visitor); + if (thisObject->m_evalCodeBlock) + thisObject->m_evalCodeBlock->visitAggregate(visitor); + visitor.append(&thisObject->m_unlinkedEvalCodeBlock); +} + +void EvalExecutable::unlinkCalls() +{ +#if ENABLE(JIT) + if (!m_jitCodeForCall) + return; + RELEASE_ASSERT(m_evalCodeBlock); + m_evalCodeBlock->unlinkCalls(); +#endif +} + +void EvalExecutable::clearCode() +{ + m_evalCodeBlock = nullptr; + m_unlinkedEvalCodeBlock.clear(); + Base::clearCode(); +} + +JSObject* ProgramExecutable::checkSyntax(ExecState* exec) +{ + ParserError error; + VM* vm = &exec->vm(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + std::unique_ptr<ProgramNode> programNode = parse<ProgramNode>( + vm, m_source, Identifier(), JSParserBuiltinMode::NotBuiltin, + JSParserStrictMode::NotStrict, SourceParseMode::ProgramMode, error); + if (programNode) + return 0; + ASSERT(error.isValid()); + return error.toErrorObject(lexicalGlobalObject, m_source); +} + +void ProgramExecutable::unlinkCalls() +{ +#if ENABLE(JIT) + if (!m_jitCodeForCall) + return; + RELEASE_ASSERT(m_programCodeBlock); + m_programCodeBlock->unlinkCalls(); +#endif +} + +JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callFrame, JSScope* scope) +{ + RELEASE_ASSERT(scope); + JSGlobalObject* globalObject = scope->globalObject(); + RELEASE_ASSERT(globalObject); + ASSERT(&globalObject->vm() == &vm); + + JSObject* exception = 0; + UnlinkedProgramCodeBlock* unlinkedCodeBlock = globalObject->createProgramCodeBlock(callFrame, this, &exception); + if (exception) + return exception; + + m_unlinkedProgramCodeBlock.set(vm, this, unlinkedCodeBlock); + + BatchedTransitionOptimizer optimizer(vm, globalObject); + + for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) { + UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i); + ASSERT(!unlinkedFunctionExecutable->name().isEmpty()); + globalObject->addFunction(callFrame, unlinkedFunctionExecutable->name()); + if (vm.typeProfiler() || vm.controlFlowProfiler()) { + vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(), + unlinkedFunctionExecutable->typeProfilingStartOffset(), + unlinkedFunctionExecutable->typeProfilingEndOffset()); + } + } + + const VariableEnvironment& variableDeclarations = unlinkedCodeBlock->variableDeclarations(); + for (auto& entry : variableDeclarations) { + ASSERT(entry.value.isVar()); + globalObject->addVar(callFrame, Identifier::fromUid(&vm, entry.key.get())); + } + return 0; +} + +void ProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + ProgramExecutable* thisObject = jsCast<ProgramExecutable*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + ScriptExecutable::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_unlinkedProgramCodeBlock); + if (thisObject->m_programCodeBlock) + thisObject->m_programCodeBlock->visitAggregate(visitor); +} + +void ProgramExecutable::clearCode() +{ + m_programCodeBlock = nullptr; + m_unlinkedProgramCodeBlock.clear(); + Base::clearCode(); +} + +FunctionCodeBlock* FunctionExecutable::baselineCodeBlockFor(CodeSpecializationKind kind) +{ + FunctionCodeBlock* result; + if (kind == CodeForCall) + result = m_codeBlockForCall.get(); + else { + RELEASE_ASSERT(kind == CodeForConstruct); + result = m_codeBlockForConstruct.get(); + } + if (!result) + return 0; + return static_cast<FunctionCodeBlock*>(result->baselineAlternative()); +} + +void FunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + FunctionExecutable* thisObject = jsCast<FunctionExecutable*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + ScriptExecutable::visitChildren(thisObject, visitor); + if (thisObject->m_codeBlockForCall) + thisObject->m_codeBlockForCall->visitAggregate(visitor); + if (thisObject->m_codeBlockForConstruct) + thisObject->m_codeBlockForConstruct->visitAggregate(visitor); + visitor.append(&thisObject->m_unlinkedExecutable); + visitor.append(&thisObject->m_singletonFunction); +} + +void FunctionExecutable::clearUnlinkedCodeForRecompilation() +{ + m_unlinkedExecutable->clearCodeForRecompilation(); +} + +void FunctionExecutable::clearCode() +{ + m_codeBlockForCall = nullptr; + m_codeBlockForConstruct = nullptr; + Base::clearCode(); +} + +void FunctionExecutable::unlinkCalls() +{ +#if ENABLE(JIT) + if (!!m_jitCodeForCall) { + RELEASE_ASSERT(m_codeBlockForCall); + m_codeBlockForCall->unlinkCalls(); + } + if (!!m_jitCodeForConstruct) { + RELEASE_ASSERT(m_codeBlockForConstruct); + m_codeBlockForConstruct->unlinkCalls(); + } +#endif +} + +FunctionExecutable* FunctionExecutable::fromGlobalCode( + const Identifier& name, ExecState& exec, const SourceCode& source, + JSObject*& exception, int overrideLineNumber) +{ + UnlinkedFunctionExecutable* unlinkedExecutable = + UnlinkedFunctionExecutable::fromGlobalCode( + name, exec, source, exception, overrideLineNumber); + if (!unlinkedExecutable) + return nullptr; + + return unlinkedExecutable->link(exec.vm(), source, overrideLineNumber); +} + +void ExecutableBase::dump(PrintStream& out) const +{ + ExecutableBase* realThis = const_cast<ExecutableBase*>(this); + + if (classInfo() == NativeExecutable::info()) { + NativeExecutable* native = jsCast<NativeExecutable*>(realThis); + out.print("NativeExecutable:", RawPointer(bitwise_cast<void*>(native->function())), "/", RawPointer(bitwise_cast<void*>(native->constructor()))); + return; + } + + if (classInfo() == EvalExecutable::info()) { + EvalExecutable* eval = jsCast<EvalExecutable*>(realThis); + if (CodeBlock* codeBlock = eval->codeBlock()) + out.print(*codeBlock); + else + out.print("EvalExecutable w/o CodeBlock"); + return; + } + + if (classInfo() == ProgramExecutable::info()) { + ProgramExecutable* eval = jsCast<ProgramExecutable*>(realThis); + if (CodeBlock* codeBlock = eval->codeBlock()) + out.print(*codeBlock); + else + out.print("ProgramExecutable w/o CodeBlock"); + return; + } + + FunctionExecutable* function = jsCast<FunctionExecutable*>(realThis); + if (!function->eitherCodeBlock()) + out.print("FunctionExecutable w/o CodeBlock"); + else { + CommaPrinter comma("/"); + if (function->codeBlockForCall()) + out.print(comma, *function->codeBlockForCall()); + if (function->codeBlockForConstruct()) + out.print(comma, *function->codeBlockForConstruct()); + } +} + +CodeBlockHash ExecutableBase::hashFor(CodeSpecializationKind kind) const +{ + if (this->classInfo() == NativeExecutable::info()) + return jsCast<const NativeExecutable*>(this)->hashFor(kind); + + return jsCast<const ScriptExecutable*>(this)->hashFor(kind); +} + +CodeBlockHash NativeExecutable::hashFor(CodeSpecializationKind kind) const +{ + if (kind == CodeForCall) + return CodeBlockHash(static_cast<unsigned>(bitwise_cast<size_t>(m_function))); + + RELEASE_ASSERT(kind == CodeForConstruct); + return CodeBlockHash(static_cast<unsigned>(bitwise_cast<size_t>(m_constructor))); +} + +CodeBlockHash ScriptExecutable::hashFor(CodeSpecializationKind kind) const +{ + return CodeBlockHash(source(), kind); +} + +} diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h new file mode 100644 index 000000000..bce2428cb --- /dev/null +++ b/Source/JavaScriptCore/runtime/Executable.h @@ -0,0 +1,713 @@ +/* + * Copyright (C) 2009, 2010, 2013-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Executable_h +#define Executable_h + +#include "ArityCheckMode.h" +#include "CallData.h" +#include "CodeBlockHash.h" +#include "CodeSpecializationKind.h" +#include "CompilationResult.h" +#include "DFGPlan.h" +#include "ExecutableInfo.h" +#include "HandlerInfo.h" +#include "InferredValue.h" +#include "JITCode.h" +#include "JSGlobalObject.h" +#include "RegisterPreservationMode.h" +#include "SamplingTool.h" +#include "SourceCode.h" +#include "TypeSet.h" +#include "UnlinkedCodeBlock.h" +#include "UnlinkedFunctionExecutable.h" + +namespace JSC { + +class CodeBlock; +class Debugger; +class EvalCodeBlock; +class FunctionCodeBlock; +class LLIntOffsetsExtractor; +class ProgramCodeBlock; +class JSScope; + +enum CompilationKind { FirstCompilation, OptimizingCompilation }; + +inline bool isCall(CodeSpecializationKind kind) +{ + if (kind == CodeForCall) + return true; + ASSERT(kind == CodeForConstruct); + return false; +} + +class ExecutableBase : public JSCell { + friend class JIT; + +protected: + static const int NUM_PARAMETERS_IS_HOST = 0; + static const int NUM_PARAMETERS_NOT_COMPILED = -1; + + ExecutableBase(VM& vm, Structure* structure, int numParameters) + : JSCell(vm, structure) + , m_numParametersForCall(numParameters) + , m_numParametersForConstruct(numParameters) + { + } + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + } + +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags; + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + CodeBlockHash hashFor(CodeSpecializationKind) const; + + bool isEvalExecutable() + { + return type() == EvalExecutableType; + } + bool isFunctionExecutable() + { + return type() == FunctionExecutableType; + } + bool isProgramExecutable() + { + return type() == ProgramExecutableType; + } + + bool isHostFunction() const + { + ASSERT((m_numParametersForCall == NUM_PARAMETERS_IS_HOST) == (m_numParametersForConstruct == NUM_PARAMETERS_IS_HOST)); + return m_numParametersForCall == NUM_PARAMETERS_IS_HOST; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(vm, globalObject, proto, TypeInfo(CellType, StructureFlags), info()); } + + void clearCode(); + + DECLARE_EXPORT_INFO; + +protected: + int m_numParametersForCall; + int m_numParametersForConstruct; + +public: + static void clearCodeVirtual(ExecutableBase*); + + PassRefPtr<JITCode> generatedJITCodeForCall() + { + ASSERT(m_jitCodeForCall); + return m_jitCodeForCall; + } + + PassRefPtr<JITCode> generatedJITCodeForConstruct() + { + ASSERT(m_jitCodeForConstruct); + return m_jitCodeForConstruct; + } + + PassRefPtr<JITCode> generatedJITCodeFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return generatedJITCodeForCall(); + ASSERT(kind == CodeForConstruct); + return generatedJITCodeForConstruct(); + } + + MacroAssemblerCodePtr entrypointFor( + VM& vm, CodeSpecializationKind kind, ArityCheckMode arity, RegisterPreservationMode registers) + { + // Check if we have a cached result. We only have it for arity check because we use the + // no-arity entrypoint in non-virtual calls, which will "cache" this value directly in + // machine code. + if (arity == MustCheckArity) { + switch (kind) { + case CodeForCall: + switch (registers) { + case RegisterPreservationNotRequired: + if (MacroAssemblerCodePtr result = m_jitCodeForCallWithArityCheck) + return result; + break; + case MustPreserveRegisters: + if (MacroAssemblerCodePtr result = m_jitCodeForCallWithArityCheckAndPreserveRegs) + return result; + break; + } + break; + case CodeForConstruct: + switch (registers) { + case RegisterPreservationNotRequired: + if (MacroAssemblerCodePtr result = m_jitCodeForConstructWithArityCheck) + return result; + break; + case MustPreserveRegisters: + if (MacroAssemblerCodePtr result = m_jitCodeForConstructWithArityCheckAndPreserveRegs) + return result; + break; + } + break; + } + } + MacroAssemblerCodePtr result = + generatedJITCodeFor(kind)->addressForCall(vm, this, arity, registers); + if (arity == MustCheckArity) { + // Cache the result; this is necessary for the JIT's virtual call optimizations. + switch (kind) { + case CodeForCall: + switch (registers) { + case RegisterPreservationNotRequired: + m_jitCodeForCallWithArityCheck = result; + break; + case MustPreserveRegisters: + m_jitCodeForCallWithArityCheckAndPreserveRegs = result; + break; + } + break; + case CodeForConstruct: + switch (registers) { + case RegisterPreservationNotRequired: + m_jitCodeForConstructWithArityCheck = result; + break; + case MustPreserveRegisters: + m_jitCodeForConstructWithArityCheckAndPreserveRegs = result; + break; + } + break; + } + } + return result; + } + + static ptrdiff_t offsetOfJITCodeWithArityCheckFor( + CodeSpecializationKind kind, RegisterPreservationMode registers) + { + switch (kind) { + case CodeForCall: + switch (registers) { + case RegisterPreservationNotRequired: + return OBJECT_OFFSETOF(ExecutableBase, m_jitCodeForCallWithArityCheck); + case MustPreserveRegisters: + return OBJECT_OFFSETOF(ExecutableBase, m_jitCodeForCallWithArityCheckAndPreserveRegs); + } + case CodeForConstruct: + switch (registers) { + case RegisterPreservationNotRequired: + return OBJECT_OFFSETOF(ExecutableBase, m_jitCodeForConstructWithArityCheck); + case MustPreserveRegisters: + return OBJECT_OFFSETOF(ExecutableBase, m_jitCodeForConstructWithArityCheckAndPreserveRegs); + } + } + RELEASE_ASSERT_NOT_REACHED(); + return 0; + } + + static ptrdiff_t offsetOfNumParametersFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return OBJECT_OFFSETOF(ExecutableBase, m_numParametersForCall); + ASSERT(kind == CodeForConstruct); + return OBJECT_OFFSETOF(ExecutableBase, m_numParametersForConstruct); + } + + bool hasJITCodeForCall() const + { + return m_numParametersForCall >= 0; + } + + bool hasJITCodeForConstruct() const + { + return m_numParametersForConstruct >= 0; + } + + bool hasJITCodeFor(CodeSpecializationKind kind) const + { + if (kind == CodeForCall) + return hasJITCodeForCall(); + ASSERT(kind == CodeForConstruct); + return hasJITCodeForConstruct(); + } + + // Intrinsics are only for calls, currently. + Intrinsic intrinsic() const; + + Intrinsic intrinsicFor(CodeSpecializationKind kind) const + { + if (isCall(kind)) + return intrinsic(); + return NoIntrinsic; + } + + void dump(PrintStream&) const; + +protected: + RefPtr<JITCode> m_jitCodeForCall; + RefPtr<JITCode> m_jitCodeForConstruct; + MacroAssemblerCodePtr m_jitCodeForCallWithArityCheck; + MacroAssemblerCodePtr m_jitCodeForConstructWithArityCheck; + MacroAssemblerCodePtr m_jitCodeForCallWithArityCheckAndPreserveRegs; + MacroAssemblerCodePtr m_jitCodeForConstructWithArityCheckAndPreserveRegs; +}; + +class NativeExecutable final : public ExecutableBase { + friend class JIT; + friend class LLIntOffsetsExtractor; +public: + typedef ExecutableBase Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static NativeExecutable* create(VM& vm, PassRefPtr<JITCode> callThunk, NativeFunction function, PassRefPtr<JITCode> constructThunk, NativeFunction constructor, Intrinsic intrinsic) + { + NativeExecutable* executable; + executable = new (NotNull, allocateCell<NativeExecutable>(vm.heap)) NativeExecutable(vm, function, constructor); + executable->finishCreation(vm, callThunk, constructThunk, intrinsic); + return executable; + } + + static void destroy(JSCell*); + + CodeBlockHash hashFor(CodeSpecializationKind) const; + + NativeFunction function() { return m_function; } + NativeFunction constructor() { return m_constructor; } + + NativeFunction nativeFunctionFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return function(); + ASSERT(kind == CodeForConstruct); + return constructor(); + } + + static ptrdiff_t offsetOfNativeFunctionFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return OBJECT_OFFSETOF(NativeExecutable, m_function); + ASSERT(kind == CodeForConstruct); + return OBJECT_OFFSETOF(NativeExecutable, m_constructor); + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(vm, globalObject, proto, TypeInfo(CellType, StructureFlags), info()); } + + DECLARE_INFO; + + Intrinsic intrinsic() const; + +protected: + void finishCreation(VM& vm, PassRefPtr<JITCode> callThunk, PassRefPtr<JITCode> constructThunk, Intrinsic intrinsic) + { + Base::finishCreation(vm); + m_jitCodeForCall = callThunk; + m_jitCodeForConstruct = constructThunk; + m_intrinsic = intrinsic; + } + +private: + NativeExecutable(VM& vm, NativeFunction function, NativeFunction constructor) + : ExecutableBase(vm, vm.nativeExecutableStructure.get(), NUM_PARAMETERS_IS_HOST) + , m_function(function) + , m_constructor(constructor) + { + } + + NativeFunction m_function; + NativeFunction m_constructor; + + Intrinsic m_intrinsic; +}; + +class ScriptExecutable : public ExecutableBase { +public: + typedef ExecutableBase Base; + static const unsigned StructureFlags = Base::StructureFlags; + + static void destroy(JSCell*); + + CodeBlockHash hashFor(CodeSpecializationKind) const; + + const SourceCode& source() const { return m_source; } + intptr_t sourceID() const { return m_source.providerID(); } + const String& sourceURL() const { return m_source.provider()->url(); } + int firstLine() const { return m_firstLine; } + void setOverrideLineNumber(int overrideLineNumber) { m_overrideLineNumber = overrideLineNumber; } + bool hasOverrideLineNumber() const { return m_overrideLineNumber != -1; } + int overrideLineNumber() const { return m_overrideLineNumber; } + int lastLine() const { return m_lastLine; } + unsigned startColumn() const { return m_startColumn; } + unsigned endColumn() const { return m_endColumn; } + unsigned typeProfilingStartOffset() const { return m_typeProfilingStartOffset; } + unsigned typeProfilingEndOffset() const { return m_typeProfilingEndOffset; } + + bool usesEval() const { return m_features & EvalFeature; } + bool usesArguments() const { return m_features & ArgumentsFeature; } + bool needsActivation() const { return m_hasCapturedVariables || m_features & (EvalFeature | WithFeature | CatchFeature); } + bool isStrictMode() const { return m_features & StrictModeFeature; } + ECMAMode ecmaMode() const { return isStrictMode() ? StrictMode : NotStrictMode; } + + void setNeverInline(bool value) { m_neverInline = value; } + void setDidTryToEnterInLoop(bool value) { m_didTryToEnterInLoop = value; } + bool neverInline() const { return m_neverInline; } + bool didTryToEnterInLoop() const { return m_didTryToEnterInLoop; } + bool isInliningCandidate() const { return !neverInline(); } + + bool* addressOfDidTryToEnterInLoop() { return &m_didTryToEnterInLoop; } + + void unlinkCalls(); + + CodeFeatures features() const { return m_features; } + + DECLARE_INFO; + + void recordParse(CodeFeatures features, bool hasCapturedVariables, int firstLine, int lastLine, unsigned startColumn, unsigned endColumn) + { + m_features = features; + m_hasCapturedVariables = hasCapturedVariables; + m_firstLine = firstLine; + m_lastLine = lastLine; + ASSERT(startColumn != UINT_MAX); + m_startColumn = startColumn; + ASSERT(endColumn != UINT_MAX); + m_endColumn = endColumn; + } + + void installCode(CodeBlock*); + RefPtr<CodeBlock> newCodeBlockFor(CodeSpecializationKind, JSFunction*, JSScope*, JSObject*& exception); + PassRefPtr<CodeBlock> newReplacementCodeBlockFor(CodeSpecializationKind); + + JSObject* prepareForExecution(ExecState* exec, JSFunction* function, JSScope* scope, CodeSpecializationKind kind) + { + if (hasJITCodeFor(kind)) + return 0; + return prepareForExecutionImpl(exec, function, scope, kind); + } + + template <typename Functor> void forEachCodeBlock(Functor&&); + +private: + JSObject* prepareForExecutionImpl(ExecState*, JSFunction*, JSScope*, CodeSpecializationKind); + +protected: + ScriptExecutable(Structure* structure, VM& vm, const SourceCode& source, bool isInStrictContext); + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + vm.heap.addCompiledCode(this); // Balanced by Heap::deleteUnmarkedCompiledCode(). + +#if ENABLE(CODEBLOCK_SAMPLING) + if (SamplingTool* sampler = vm.interpreter->sampler()) + sampler->notifyOfScope(vm, this); +#endif + } + + SourceCode m_source; + CodeFeatures m_features; + bool m_hasCapturedVariables; + bool m_neverInline; + bool m_didTryToEnterInLoop; + int m_overrideLineNumber; + int m_firstLine; + int m_lastLine; + unsigned m_startColumn; + unsigned m_endColumn; + unsigned m_typeProfilingStartOffset; + unsigned m_typeProfilingEndOffset; +}; + +class EvalExecutable final : public ScriptExecutable { + friend class LLIntOffsetsExtractor; +public: + typedef ScriptExecutable Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static void destroy(JSCell*); + + EvalCodeBlock* codeBlock() + { + return m_evalCodeBlock.get(); + } + + static EvalExecutable* create(ExecState*, const SourceCode&, bool isInStrictContext, ThisTDZMode, const VariableEnvironment*); + + PassRefPtr<JITCode> generatedJITCode() + { + return generatedJITCodeForCall(); + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(EvalExecutableType, StructureFlags), info()); + } + + DECLARE_INFO; + + void unlinkCalls(); + + void clearCode(); + + ExecutableInfo executableInfo() const { return ExecutableInfo(needsActivation(), usesEval(), isStrictMode(), false, false, ConstructorKind::None); } + + unsigned numVariables() { return m_unlinkedEvalCodeBlock->numVariables(); } + unsigned numberOfFunctionDecls() { return m_unlinkedEvalCodeBlock->numberOfFunctionDecls(); } + +private: + friend class ScriptExecutable; + EvalExecutable(ExecState*, const SourceCode&, bool); + + static void visitChildren(JSCell*, SlotVisitor&); + + RefPtr<EvalCodeBlock> m_evalCodeBlock; + WriteBarrier<UnlinkedEvalCodeBlock> m_unlinkedEvalCodeBlock; +}; + +class ProgramExecutable final : public ScriptExecutable { + friend class LLIntOffsetsExtractor; +public: + typedef ScriptExecutable Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static ProgramExecutable* create(ExecState* exec, const SourceCode& source) + { + ProgramExecutable* executable = new (NotNull, allocateCell<ProgramExecutable>(*exec->heap())) ProgramExecutable(exec, source); + executable->finishCreation(exec->vm()); + return executable; + } + + + JSObject* initializeGlobalProperties(VM&, CallFrame*, JSScope*); + + static void destroy(JSCell*); + + ProgramCodeBlock* codeBlock() + { + return m_programCodeBlock.get(); + } + + JSObject* checkSyntax(ExecState*); + + PassRefPtr<JITCode> generatedJITCode() + { + return generatedJITCodeForCall(); + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(ProgramExecutableType, StructureFlags), info()); + } + + DECLARE_INFO; + + void unlinkCalls(); + + void clearCode(); + + ExecutableInfo executableInfo() const { return ExecutableInfo(needsActivation(), usesEval(), isStrictMode(), false, false, ConstructorKind::None); } + +private: + friend class ScriptExecutable; + + ProgramExecutable(ExecState*, const SourceCode&); + + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<UnlinkedProgramCodeBlock> m_unlinkedProgramCodeBlock; + RefPtr<ProgramCodeBlock> m_programCodeBlock; +}; + +class FunctionExecutable final : public ScriptExecutable { + friend class JIT; + friend class LLIntOffsetsExtractor; +public: + typedef ScriptExecutable Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static FunctionExecutable* create( + VM& vm, const SourceCode& source, UnlinkedFunctionExecutable* unlinkedExecutable, + unsigned firstLine, unsigned lastLine, unsigned startColumn, unsigned endColumn) + { + FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(vm.heap)) FunctionExecutable(vm, source, unlinkedExecutable, firstLine, lastLine, startColumn, endColumn); + executable->finishCreation(vm); + return executable; + } + static FunctionExecutable* fromGlobalCode( + const Identifier& name, ExecState&, const SourceCode&, + JSObject*& exception, int overrideLineNumber); + + static void destroy(JSCell*); + + UnlinkedFunctionExecutable* unlinkedExecutable() + { + return m_unlinkedExecutable.get(); + } + + // Returns either call or construct bytecode. This can be appropriate + // for answering questions that that don't vary between call and construct -- + // for example, argumentsRegister(). + FunctionCodeBlock* eitherCodeBlock() + { + if (m_codeBlockForCall) + return m_codeBlockForCall.get(); + return m_codeBlockForConstruct.get(); + } + + bool isGeneratedForCall() const + { + return m_codeBlockForCall; + } + + FunctionCodeBlock* codeBlockForCall() + { + return m_codeBlockForCall.get(); + } + + bool isGeneratedForConstruct() const + { + return m_codeBlockForConstruct; + } + + FunctionCodeBlock* codeBlockForConstruct() + { + return m_codeBlockForConstruct.get(); + } + + bool isGeneratedFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return isGeneratedForCall(); + ASSERT(kind == CodeForConstruct); + return isGeneratedForConstruct(); + } + + FunctionCodeBlock* codeBlockFor(CodeSpecializationKind kind) + { + if (kind == CodeForCall) + return codeBlockForCall(); + ASSERT(kind == CodeForConstruct); + return codeBlockForConstruct(); + } + + FunctionCodeBlock* baselineCodeBlockFor(CodeSpecializationKind); + + FunctionCodeBlock* profiledCodeBlockFor(CodeSpecializationKind kind) + { + return baselineCodeBlockFor(kind); + } + + RefPtr<TypeSet> returnStatementTypeSet() + { + if (!m_returnStatementTypeSet) + m_returnStatementTypeSet = TypeSet::create(); + + return m_returnStatementTypeSet; + } + + FunctionMode functionMode() { return m_unlinkedExecutable->functionMode(); } + bool isBuiltinFunction() const { return m_unlinkedExecutable->isBuiltinFunction(); } + ConstructAbility constructAbility() const { return m_unlinkedExecutable->constructAbility(); } + bool isClassConstructorFunction() const { return m_unlinkedExecutable->isClassConstructorFunction(); } + const Identifier& name() { return m_unlinkedExecutable->name(); } + const Identifier& inferredName() { return m_unlinkedExecutable->inferredName(); } + JSString* nameValue() const { return m_unlinkedExecutable->nameValue(); } + size_t parameterCount() const { return m_unlinkedExecutable->parameterCount(); } // Excluding 'this'! + + void clearUnlinkedCodeForRecompilation(); + static void visitChildren(JSCell*, SlotVisitor&); + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(FunctionExecutableType, StructureFlags), info()); + } + + unsigned parametersStartOffset() const { return m_parametersStartOffset; } + + void overrideParameterAndTypeProfilingStartEndOffsets(unsigned parametersStartOffset, unsigned typeProfilingStartOffset, unsigned typeProfilingEndOffset) + { + m_parametersStartOffset = parametersStartOffset; + m_typeProfilingStartOffset = typeProfilingStartOffset; + m_typeProfilingEndOffset = typeProfilingEndOffset; + } + + DECLARE_INFO; + + void unlinkCalls(); + + void clearCode(); + + InferredValue* singletonFunction() { return m_singletonFunction.get(); } + +private: + FunctionExecutable( + VM&, const SourceCode&, UnlinkedFunctionExecutable*, unsigned firstLine, + unsigned lastLine, unsigned startColumn, unsigned endColumn); + + void finishCreation(VM&); + + friend class ScriptExecutable; + + WriteBarrier<UnlinkedFunctionExecutable> m_unlinkedExecutable; + RefPtr<FunctionCodeBlock> m_codeBlockForCall; + RefPtr<FunctionCodeBlock> m_codeBlockForConstruct; + RefPtr<TypeSet> m_returnStatementTypeSet; + unsigned m_parametersStartOffset; + WriteBarrier<InferredValue> m_singletonFunction; +}; + +inline void ExecutableBase::clearCodeVirtual(ExecutableBase* executable) +{ + switch (executable->type()) { + case EvalExecutableType: + return jsCast<EvalExecutable*>(executable)->clearCode(); + case ProgramExecutableType: + return jsCast<ProgramExecutable*>(executable)->clearCode(); + case FunctionExecutableType: + return jsCast<FunctionExecutable*>(executable)->clearCode(); + default: + return jsCast<NativeExecutable*>(executable)->clearCode(); + } +} + +inline void ScriptExecutable::unlinkCalls() +{ + switch (type()) { + case EvalExecutableType: + return jsCast<EvalExecutable*>(this)->unlinkCalls(); + case ProgramExecutableType: + return jsCast<ProgramExecutable*>(this)->unlinkCalls(); + case FunctionExecutableType: + return jsCast<FunctionExecutable*>(this)->unlinkCalls(); + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/Float32Array.h b/Source/JavaScriptCore/runtime/Float32Array.h new file mode 100644 index 000000000..4eccbb84b --- /dev/null +++ b/Source/JavaScriptCore/runtime/Float32Array.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Float32Array_h +#define Float32Array_h + +#include "TypedArrays.h" + +using JSC::Float32Array; + +#endif // Float32Array_h + diff --git a/Source/JavaScriptCore/runtime/Float64Array.h b/Source/JavaScriptCore/runtime/Float64Array.h new file mode 100644 index 000000000..5b39326f4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Float64Array.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Float64Array_h +#define Float64Array_h + +#include "TypedArrays.h" + +using JSC::Float64Array; + +#endif // Float64Array_h + diff --git a/Source/JavaScriptCore/runtime/FunctionConstructor.cpp b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp new file mode 100644 index 000000000..dbe42fa5f --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "FunctionConstructor.h" + +#include "Debugger.h" +#include "ExceptionHelpers.h" +#include "FunctionPrototype.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "Lexer.h" +#include "Nodes.h" +#include "JSCInlines.h" +#include "Parser.h" +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(FunctionConstructor); + +const ClassInfo FunctionConstructor::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(FunctionConstructor) }; + +FunctionConstructor::FunctionConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void FunctionConstructor::finishCreation(VM& vm, FunctionPrototype* functionPrototype) +{ + Base::finishCreation(vm, functionPrototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, functionPrototype, DontEnum | DontDelete | ReadOnly); + + // Number of arguments for constructor + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontDelete | DontEnum); +} + +static EncodedJSValue JSC_HOST_CALL constructWithFunctionConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructFunction(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +ConstructType FunctionConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithFunctionConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callFunctionConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructFunction(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +// ECMA 15.3.1 The Function Constructor Called as a Function +CallType FunctionConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callFunctionConstructor; + return CallTypeHost; +} + +// ECMA 15.3.2 The Function Constructor +JSObject* constructFunction(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, const Identifier& functionName, const String& sourceURL, const TextPosition& position) +{ + if (!globalObject->evalEnabled()) + return exec->vm().throwException(exec, createEvalError(exec, globalObject->evalDisabledErrorMessage())); + return constructFunctionSkippingEvalEnabledCheck(exec, globalObject, args, functionName, sourceURL, position); +} + +JSObject* constructFunctionSkippingEvalEnabledCheck( + ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, + const Identifier& functionName, const String& sourceURL, + const TextPosition& position, int overrideLineNumber) +{ + // How we stringify functions is sometimes important for web compatibility. + // See https://bugs.webkit.org/show_bug.cgi?id=24350. + String program; + if (args.isEmpty()) + program = makeString("{function ", functionName.string(), "() {\n\n}}"); + else if (args.size() == 1) + program = makeString("{function ", functionName.string(), "() {\n", args.at(0).toString(exec)->value(exec), "\n}}"); + else { + StringBuilder builder; + builder.appendLiteral("{function "); + builder.append(functionName.string()); + builder.append('('); + builder.append(args.at(0).toString(exec)->view(exec)); + for (size_t i = 1; i < args.size() - 1; i++) { + builder.appendLiteral(", "); + builder.append(args.at(i).toString(exec)->view(exec)); + } + builder.appendLiteral(") {\n"); + builder.append(args.at(args.size() - 1).toString(exec)->view(exec)); + builder.appendLiteral("\n}}"); + program = builder.toString(); + } + + SourceCode source = makeSource(program, sourceURL, position); + JSObject* exception = nullptr; + FunctionExecutable* function = FunctionExecutable::fromGlobalCode(functionName, *exec, source, exception, overrideLineNumber); + if (!function) { + ASSERT(exception); + return exec->vm().throwException(exec, exception); + } + + return JSFunction::create(exec->vm(), function, globalObject); +} + +// ECMA 15.3.2 The Function Constructor +JSObject* constructFunction(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args) +{ + return constructFunction(exec, globalObject, args, exec->propertyNames().anonymous, String(), TextPosition::minimumPosition()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/FunctionConstructor.h b/Source/JavaScriptCore/runtime/FunctionConstructor.h new file mode 100644 index 000000000..22ecb57b9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionConstructor.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FunctionConstructor_h +#define FunctionConstructor_h + +#include "InternalFunction.h" + +namespace WTF { +class TextPosition; +} + +namespace JSC { + +class FunctionPrototype; + +class FunctionConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + + static FunctionConstructor* create(VM& vm, Structure* structure, FunctionPrototype* functionPrototype) + { + FunctionConstructor* constructor = new (NotNull, allocateCell<FunctionConstructor>(vm.heap)) FunctionConstructor(vm, structure); + constructor->finishCreation(vm, functionPrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + FunctionConstructor(VM&, Structure*); + void finishCreation(VM&, FunctionPrototype*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +JSObject* constructFunction(ExecState*, JSGlobalObject*, const ArgList&, const Identifier& functionName, const String& sourceURL, const WTF::TextPosition&); +JSObject* constructFunction(ExecState*, JSGlobalObject*, const ArgList&); + +JS_EXPORT_PRIVATE JSObject* constructFunctionSkippingEvalEnabledCheck( + ExecState*, JSGlobalObject*, const ArgList&, const Identifier&, + const String&, const WTF::TextPosition&, int overrideLineNumber = -1); + +} // namespace JSC + +#endif // FunctionConstructor_h diff --git a/Source/JavaScriptCore/runtime/FunctionExecutableDump.cpp b/Source/JavaScriptCore/runtime/FunctionExecutableDump.cpp new file mode 100644 index 000000000..d80a8e701 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionExecutableDump.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "FunctionExecutableDump.h" +#include "JSCellInlines.h" + +#include "CodeBlock.h" + +namespace JSC { + +void FunctionExecutableDump::dump(PrintStream& out) const +{ + out.print(m_executable->inferredName().string(), "#"); + if (m_executable->isGeneratedForCall()) + out.print(m_executable->codeBlockForCall()->hashAsStringIfPossible()); + else + out.print("<nogen>"); + out.print("/"); + if (m_executable->isGeneratedForConstruct()) + out.print(m_executable->codeBlockForConstruct()->hashAsStringIfPossible()); + else + out.print("<nogen>"); + out.print(":[", RawPointer(m_executable), "]"); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/FunctionExecutableDump.h b/Source/JavaScriptCore/runtime/FunctionExecutableDump.h new file mode 100644 index 000000000..732ec458f --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionExecutableDump.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef FunctionExecutableDump_h +#define FunctionExecutableDump_h + +#include "Executable.h" +#include <wtf/PrintStream.h> + +namespace JSC { + +class FunctionExecutableDump { +public: + explicit FunctionExecutableDump(FunctionExecutable* executable) + : m_executable(executable) + { + } + + void dump(PrintStream&) const; +private: + FunctionExecutable* m_executable; +}; + +} // namespace JSC + +#endif // FunctionExecutableDump_h + diff --git a/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp b/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp new file mode 100644 index 000000000..3d7e31b2e --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "FunctionHasExecutedCache.h" + +#include <limits.h> + +namespace JSC { + +bool FunctionHasExecutedCache::hasExecutedAtOffset(intptr_t id, unsigned offset) +{ + if (m_rangeMap.find(id) == m_rangeMap.end()) + return false; + + RangeMap& map = m_rangeMap.find(id)->second; + unsigned distance = UINT_MAX; + bool hasExecuted = false; + for (auto iter = map.begin(), end = map.end(); iter != end; ++iter) { + const FunctionRange& range = iter->first; + if (range.m_start <= offset && offset <= range.m_end && range.m_end - range.m_start < distance) { + hasExecuted = iter->second; + distance = range.m_end - range.m_start; + } + } + + return hasExecuted; +} + +void FunctionHasExecutedCache::insertUnexecutedRange(intptr_t id, unsigned start, unsigned end) +{ + if (m_rangeMap.find(id) == m_rangeMap.end()) { + RangeMap map; + m_rangeMap[id] = map; + } + + RangeMap& map = m_rangeMap.find(id)->second; + FunctionRange range; + range.m_start = start; + range.m_end = end; + // Only insert unexecuted ranges once for a given sourceID because we may run into a situation where an executable executes, then is GCed, and then is allocated again, + // and tries to reinsert itself, claiming it has never run, but this is false because it indeed already executed. + if (map.find(range) == map.end()) + map[range] = false; +} + +void FunctionHasExecutedCache::removeUnexecutedRange(intptr_t id, unsigned start, unsigned end) +{ + // FIXME: We should never have an instance where we return here, but currently do in some situations. Find out why. + if (m_rangeMap.find(id) == m_rangeMap.end()) + return; + + RangeMap& map = m_rangeMap.find(id)->second; + + FunctionRange range; + range.m_start = start; + range.m_end = end; + map[range] = true; +} + +Vector<std::tuple<bool, unsigned, unsigned>> FunctionHasExecutedCache::getFunctionRanges(intptr_t id) +{ + Vector<std::tuple<bool, unsigned, unsigned>> ranges(0); + auto findResult = m_rangeMap.find(id); + if (findResult == m_rangeMap.end()) + return ranges; + + RangeMap& map = m_rangeMap.find(id)->second; + for (auto iter = map.begin(), end = map.end(); iter != end; ++iter) { + const FunctionRange& range = iter->first; + bool hasExecuted = iter->second; + ranges.append(std::tuple<bool, unsigned, unsigned>(hasExecuted, range.m_start, range.m_end)); + } + + return ranges; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h b/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h new file mode 100644 index 000000000..7f3eb9784 --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionHasExecutedCache.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef FunctionHasExecutedCache_h +#define FunctionHasExecutedCache_h + +#include <unordered_map> +#include <wtf/HashMethod.h> +#include <wtf/Vector.h> + +namespace JSC { + +class FunctionHasExecutedCache { +public: + struct FunctionRange { + FunctionRange() {} + bool operator==(const FunctionRange& other) const + { + return m_start == other.m_start && m_end == other.m_end; + } + unsigned hash() const + { + return m_start * m_end; + } + + unsigned m_start; + unsigned m_end; + }; + + bool hasExecutedAtOffset(intptr_t id, unsigned offset); + void insertUnexecutedRange(intptr_t id, unsigned start, unsigned end); + void removeUnexecutedRange(intptr_t id, unsigned start, unsigned end); + Vector<std::tuple<bool, unsigned, unsigned>> getFunctionRanges(intptr_t id); + +private: + typedef std::unordered_map<FunctionRange, bool, HashMethod<FunctionRange>> RangeMap; + typedef std::unordered_map<intptr_t, RangeMap> SourceIDToRangeMap; + SourceIDToRangeMap m_rangeMap; +}; + +} // namespace JSC + +#endif // FunctionHasExecutedCache_h diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp new file mode 100644 index 000000000..d082b12cf --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "FunctionPrototype.h" + +#include "BuiltinExecutables.h" +#include "BuiltinNames.h" +#include "Error.h" +#include "JSArray.h" +#include "JSBoundFunction.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "Interpreter.h" +#include "Lexer.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(FunctionPrototype); + +const ClassInfo FunctionPrototype::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(FunctionPrototype) }; + +static EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState*); + +FunctionPrototype::FunctionPrototype(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void FunctionPrototype::finishCreation(VM& vm, const String& name) +{ + Base::finishCreation(vm, name); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), DontDelete | ReadOnly | DontEnum); +} + +void FunctionPrototype::addFunctionProperties(ExecState* exec, JSGlobalObject* globalObject, JSFunction** callFunction, JSFunction** applyFunction) +{ + VM& vm = exec->vm(); + + JSFunction* toStringFunction = JSFunction::create(vm, globalObject, 0, vm.propertyNames->toString.string(), functionProtoFuncToString); + putDirectWithoutTransition(vm, vm.propertyNames->toString, toStringFunction, DontEnum); + + *applyFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().applyPublicName(), functionPrototypeApplyCodeGenerator(vm), DontEnum); + *callFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().callPublicName(), functionPrototypeCallCodeGenerator(vm), DontEnum); + + JSFunction* bindFunction = JSFunction::create(vm, globalObject, 1, vm.propertyNames->bind.string(), functionProtoFuncBind); + putDirectWithoutTransition(vm, vm.propertyNames->bind, bindFunction, DontEnum); +} + +static EncodedJSValue JSC_HOST_CALL callFunctionPrototype(ExecState*) +{ + return JSValue::encode(jsUndefined()); +} + +// ECMA 15.3.4 +CallType FunctionPrototype::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callFunctionPrototype; + return CallTypeHost; +} + +EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (thisValue.inherits(JSFunction::info())) { + JSFunction* function = jsCast<JSFunction*>(thisValue); + if (function->isHostOrBuiltinFunction()) + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); + + FunctionExecutable* executable = function->jsExecutable(); + String source = executable->source().provider()->getRange( + executable->parametersStartOffset(), + executable->typeProfilingEndOffset() + 1); // Type profiling end offset is the character before the '}'. + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), source)); + } + + if (thisValue.inherits(InternalFunction::info())) { + InternalFunction* function = asInternalFunction(thisValue); + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); + } + + return throwVMTypeError(exec); +} + +// 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]]) +EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec) +{ + JSGlobalObject* globalObject = exec->callee()->globalObject(); + + // Let Target be the this value. + JSValue target = exec->thisValue(); + + // If IsCallable(Target) is false, throw a TypeError exception. + CallData callData; + CallType callType = getCallData(target, callData); + if (callType == CallTypeNone) + return throwVMTypeError(exec); + // Primitive values are not callable. + ASSERT(target.isObject()); + JSObject* targetObject = asObject(target); + VM& vm = exec->vm(); + + // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order. + size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0; + JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), numBoundArgs); + if (!boundArgs) + return JSValue::encode(throwOutOfMemoryError(exec)); + + for (size_t i = 0; i < numBoundArgs; ++i) + boundArgs->initializeIndex(vm, i, exec->argument(i + 1)); + + // If the [[Class]] internal property of Target is "Function", then ... + // Else set the length own property of F to 0. + unsigned length = 0; + if (targetObject->inherits(JSFunction::info())) { + ASSERT(target.get(exec, exec->propertyNames().length).isNumber()); + // a. Let L be the length property of Target minus the length of A. + // b. Set the length own property of F to either 0 or L, whichever is larger. + unsigned targetLength = (unsigned)target.get(exec, exec->propertyNames().length).asNumber(); + if (targetLength > numBoundArgs) + length = targetLength - numBoundArgs; + } + + JSString* name = target.get(exec, exec->propertyNames().name).toString(exec); + return JSValue::encode(JSBoundFunction::create(vm, globalObject, targetObject, exec->argument(0), boundArgs, length, name->value(exec))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.h b/Source/JavaScriptCore/runtime/FunctionPrototype.h new file mode 100644 index 000000000..52ce8f11c --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FunctionPrototype_h +#define FunctionPrototype_h + +#include "InternalFunction.h" + +namespace JSC { + +class FunctionPrototype : public InternalFunction { +public: + typedef InternalFunction Base; + + static FunctionPrototype* create(VM& vm, Structure* structure) + { + FunctionPrototype* prototype = new (NotNull, allocateCell<FunctionPrototype>(vm.heap)) FunctionPrototype(vm, structure); + prototype->finishCreation(vm, String()); + return prototype; + } + + void addFunctionProperties(ExecState*, JSGlobalObject*, JSFunction** callFunction, JSFunction** applyFunction); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), info()); + } + + DECLARE_INFO; + +protected: + void finishCreation(VM&, const String& name); + +private: + FunctionPrototype(VM&, Structure*); + static CallType getCallData(JSCell*, CallData&); +}; + +} // namespace JSC + +#endif // FunctionPrototype_h diff --git a/Source/JavaScriptCore/runtime/FunctionRareData.cpp b/Source/JavaScriptCore/runtime/FunctionRareData.cpp new file mode 100644 index 000000000..d44c3234c --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionRareData.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "FunctionRareData.h" + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo FunctionRareData::s_info = { "FunctionRareData", 0, 0, CREATE_METHOD_TABLE(FunctionRareData) }; + +FunctionRareData* FunctionRareData::create(VM& vm, JSObject* prototype, size_t inlineCapacity) +{ + FunctionRareData* rareData = new (NotNull, allocateCell<FunctionRareData>(vm.heap)) FunctionRareData(vm); + rareData->finishCreation(vm, prototype, inlineCapacity); + return rareData; +} + +void FunctionRareData::destroy(JSCell* cell) +{ + FunctionRareData* rareData = static_cast<FunctionRareData*>(cell); + rareData->FunctionRareData::~FunctionRareData(); +} + +Structure* FunctionRareData::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); +} + +void FunctionRareData::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + FunctionRareData* rareData = jsCast<FunctionRareData*>(cell); + + rareData->m_allocationProfile.visitAggregate(visitor); +} + +FunctionRareData::FunctionRareData(VM& vm) + : Base(vm, vm.functionRareDataStructure.get()) + , m_allocationProfile() + // We initialize blind so that changes to the prototype after function creation but before + // the optimizer kicks in don't disable optimizations. Once the optimizer kicks in, the + // watchpoint will start watching and any changes will both force deoptimization and disable + // future attempts to optimize. This is necessary because we are guaranteed that the + // allocation profile is changed exactly once prior to optimizations kicking in. We could be + // smarter and count the number of times the prototype is clobbered and only optimize if it + // was clobbered exactly once, but that seems like overkill. In almost all cases it will be + // clobbered once, and if it's clobbered more than once, that will probably only occur + // before we started optimizing, anyway. + , m_allocationProfileWatchpoint(ClearWatchpoint) +{ +} + +FunctionRareData::~FunctionRareData() +{ +} + +void FunctionRareData::finishCreation(VM& vm, JSObject* prototype, size_t inlineCapacity) +{ + Base::finishCreation(vm); + initialize(vm, prototype, inlineCapacity); +} + +void FunctionRareData::initialize(VM& vm, JSObject* prototype, size_t inlineCapacity) +{ + m_allocationProfile.initialize(vm, this, prototype, inlineCapacity); +} + +void FunctionRareData::clear(const char* reason) +{ + m_allocationProfile.clear(); + m_allocationProfileWatchpoint.fireAll(reason); +} + +} diff --git a/Source/JavaScriptCore/runtime/FunctionRareData.h b/Source/JavaScriptCore/runtime/FunctionRareData.h new file mode 100644 index 000000000..f0a2db2da --- /dev/null +++ b/Source/JavaScriptCore/runtime/FunctionRareData.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef FunctionRareData_h +#define FunctionRareData_h + +#include "JSCell.h" +#include "ObjectAllocationProfile.h" +#include "Watchpoint.h" + +namespace JSC { + +class JSGlobalObject; +class LLIntOffsetsExtractor; +namespace DFG { +class SpeculativeJIT; +class JITCompiler; +} + +class FunctionRareData : public JSCell { + friend class JIT; + friend class DFG::SpeculativeJIT; + friend class DFG::JITCompiler; + friend class VM; + +public: + typedef JSCell Base; + static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags; + + static FunctionRareData* create(VM&, JSObject* prototype, size_t inlineCapacity); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + static void visitChildren(JSCell*, SlotVisitor&); + + DECLARE_INFO; + + static inline ptrdiff_t offsetOfAllocationProfile() + { + return OBJECT_OFFSETOF(FunctionRareData, m_allocationProfile); + } + + ObjectAllocationProfile* allocationProfile() + { + return &m_allocationProfile; + } + + Structure* allocationStructure() { return m_allocationProfile.structure(); } + + InlineWatchpointSet& allocationProfileWatchpointSet() + { + return m_allocationProfileWatchpoint; + } + + void clear(const char* reason); + + void initialize(VM&, JSObject* prototype, size_t inlineCapacity); + + bool isInitialized() { return !m_allocationProfile.isNull(); } + +protected: + FunctionRareData(VM&); + ~FunctionRareData(); + + void finishCreation(VM&, JSObject* prototype, size_t inlineCapacity); + using Base::finishCreation; + +private: + + friend class LLIntOffsetsExtractor; + + ObjectAllocationProfile m_allocationProfile; + InlineWatchpointSet m_allocationProfileWatchpoint; +}; + +} // namespace JSC + +#endif // FunctionRareData_h diff --git a/Source/JavaScriptCore/runtime/GenericArguments.h b/Source/JavaScriptCore/runtime/GenericArguments.h new file mode 100644 index 000000000..67f1b0345 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GenericArguments.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef GenericArguments_h +#define GenericArguments_h + +#include "JSObject.h" + +namespace JSC { + +// This is a mixin for the two kinds of Arguments-class objects that arise when you say +// "arguments" inside a function. This class doesn't show up in the JSCell inheritance hierarchy. +template<typename Type> +class GenericArguments : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames; + +protected: + GenericArguments(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + + void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length); +}; + +} // namespace JSC + +#endif // GenericArguments_h + diff --git a/Source/JavaScriptCore/runtime/GenericArgumentsInlines.h b/Source/JavaScriptCore/runtime/GenericArgumentsInlines.h new file mode 100644 index 000000000..9b1800185 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GenericArgumentsInlines.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef GenericArgumentsInlines_h +#define GenericArgumentsInlines_h + +#include "GenericArguments.h" +#include "JSCInlines.h" + +namespace JSC { + +template<typename Type> +bool GenericArguments<Type>::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName ident, PropertySlot& slot) +{ + Type* thisObject = jsCast<Type*>(object); + VM& vm = exec->vm(); + + if (!thisObject->overrodeThings()) { + if (ident == vm.propertyNames->length) { + slot.setValue(thisObject, DontEnum, jsNumber(thisObject->internalLength())); + return true; + } + if (ident == vm.propertyNames->callee) { + slot.setValue(thisObject, DontEnum, thisObject->callee().get()); + return true; + } + if (ident == vm.propertyNames->iteratorSymbol) { + slot.setValue(thisObject, DontEnum, thisObject->globalObject()->arrayProtoValuesFunction()); + return true; + } + } + + Optional<uint32_t> index = parseIndex(ident); + if (index && thisObject->canAccessIndexQuickly(index.value())) { + slot.setValue(thisObject, None, thisObject->getIndexQuickly(index.value())); + return true; + } + + return Base::getOwnPropertySlot(thisObject, exec, ident, slot); +} + +template<typename Type> +bool GenericArguments<Type>::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned index, PropertySlot& slot) +{ + Type* thisObject = jsCast<Type*>(object); + + if (thisObject->canAccessIndexQuickly(index)) { + slot.setValue(thisObject, None, thisObject->getIndexQuickly(index)); + return true; + } + + return Base::getOwnPropertySlotByIndex(object, exec, index, slot); +} + +template<typename Type> +void GenericArguments<Type>::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) +{ + Type* thisObject = jsCast<Type*>(object); + + if (array.includeStringProperties()) { + for (unsigned i = 0; i < thisObject->internalLength(); ++i) { + if (!thisObject->canAccessIndexQuickly(i)) + continue; + array.add(Identifier::from(exec, i)); + } + } + + if (mode.includeDontEnumProperties() && !thisObject->overrodeThings()) { + array.add(exec->propertyNames().length); + array.add(exec->propertyNames().callee); + if (array.includeSymbolProperties()) + array.add(exec->propertyNames().iteratorSymbol); + } + Base::getOwnPropertyNames(thisObject, exec, array, mode); +} + +template<typename Type> +void GenericArguments<Type>::put(JSCell* cell, ExecState* exec, PropertyName ident, JSValue value, PutPropertySlot& slot) +{ + Type* thisObject = jsCast<Type*>(cell); + VM& vm = exec->vm(); + + if (!thisObject->overrodeThings() + && (ident == vm.propertyNames->length + || ident == vm.propertyNames->callee + || ident == vm.propertyNames->iteratorSymbol)) { + thisObject->overrideThings(vm); + PutPropertySlot dummy = slot; // This put is not cacheable, so we shadow the slot that was given to us. + Base::put(thisObject, exec, ident, value, dummy); + return; + } + + Optional<uint32_t> index = parseIndex(ident); + if (index && thisObject->canAccessIndexQuickly(index.value())) { + thisObject->setIndexQuickly(vm, index.value(), value); + return; + } + + Base::put(thisObject, exec, ident, value, slot); +} + +template<typename Type> +void GenericArguments<Type>::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow) +{ + Type* thisObject = jsCast<Type*>(cell); + VM& vm = exec->vm(); + + if (thisObject->canAccessIndexQuickly(index)) { + thisObject->setIndexQuickly(vm, index, value); + return; + } + + return Base::putByIndex(cell, exec, index, value, shouldThrow); +} + +template<typename Type> +bool GenericArguments<Type>::deleteProperty(JSCell* cell, ExecState* exec, PropertyName ident) +{ + Type* thisObject = jsCast<Type*>(cell); + VM& vm = exec->vm(); + + if (!thisObject->overrodeThings() + && (ident == vm.propertyNames->length + || ident == vm.propertyNames->callee + || ident == vm.propertyNames->iteratorSymbol)) + thisObject->overrideThings(vm); + + Optional<uint32_t> index = parseIndex(ident); + if (index && thisObject->canAccessIndexQuickly(index.value())) { + thisObject->overrideArgument(vm, index.value()); + return true; + } + + return Base::deleteProperty(thisObject, exec, ident); +} + +template<typename Type> +bool GenericArguments<Type>::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned index) +{ + Type* thisObject = jsCast<Type*>(cell); + VM& vm = exec->vm(); + + if (thisObject->canAccessIndexQuickly(index)) { + thisObject->overrideArgument(vm, index); + return true; + } + + return Base::deletePropertyByIndex(cell, exec, index); +} + +template<typename Type> +bool GenericArguments<Type>::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow) +{ + Type* thisObject = jsCast<Type*>(object); + VM& vm = exec->vm(); + + if (ident == vm.propertyNames->length + || ident == vm.propertyNames->callee + || ident == vm.propertyNames->iteratorSymbol) + thisObject->overrideThingsIfNecessary(vm); + else { + Optional<uint32_t> optionalIndex = parseIndex(ident); + if (optionalIndex && thisObject->canAccessIndexQuickly(optionalIndex.value())) { + uint32_t index = optionalIndex.value(); + if (!descriptor.isAccessorDescriptor()) { + // If the property is not deleted and we are using a non-accessor descriptor, then + // make sure that the aliased argument sees the value. + if (descriptor.value()) + thisObject->setIndexQuickly(vm, index, descriptor.value()); + + // If the property is not deleted and we are using a non-accessor, writable + // descriptor, then we are done. The argument continues to be aliased. Note that we + // ignore the request to change enumerability. We appear to have always done so, in + // cases where the argument was still aliased. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=141952 + if (descriptor.writable()) + return true; + } + + // If the property is a non-deleted argument, then move it into the base object and + // then delete it. + JSValue value = thisObject->getIndexQuickly(index); + ASSERT(value); + object->putDirectMayBeIndex(exec, ident, value); + thisObject->overrideArgument(vm, index); + } + } + + // Now just let the normal object machinery do its thing. + return Base::defineOwnProperty(object, exec, ident, descriptor, shouldThrow); +} + +template<typename Type> +void GenericArguments<Type>::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length) +{ + Type* thisObject = static_cast<Type*>(this); + for (unsigned i = 0; i < length; ++i) { + if (thisObject->canAccessIndexQuickly(i + offset)) + exec->r(firstElementDest + i) = thisObject->getIndexQuickly(i + offset); + else { + exec->r(firstElementDest + i) = get(exec, i + offset); + if (UNLIKELY(exec->vm().exception())) + return; + } + } +} + +} // namespace JSC + +#endif // GenericArgumentsInlines_h + diff --git a/Source/JavaScriptCore/runtime/GenericOffset.h b/Source/JavaScriptCore/runtime/GenericOffset.h new file mode 100644 index 000000000..a6bfe5655 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GenericOffset.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef GenericOffset_h +#define GenericOffset_h + +#include <limits.h> +#include <wtf/Assertions.h> + +namespace JSC { + +// A mixin for creating the various kinds of variable offsets that our engine supports. +template<typename T> +class GenericOffset { +public: + static const unsigned invalidOffset = UINT_MAX; + + GenericOffset() + : m_offset(invalidOffset) + { + } + + explicit GenericOffset(unsigned offset) + : m_offset(offset) + { + } + + bool operator!() const { return m_offset == invalidOffset; } + + unsigned offsetUnchecked() const + { + return m_offset; + } + + unsigned offset() const + { + ASSERT(m_offset != invalidOffset); + return m_offset; + } + + bool operator==(const T& other) const + { + return m_offset == other.offsetUnchecked(); + } + bool operator!=(const T& other) const + { + return m_offset != other.offsetUnchecked(); + } + bool operator<(const T& other) const + { + return m_offset < other.offsetUnchecked(); + } + bool operator>(const T& other) const + { + return m_offset > other.offsetUnchecked(); + } + bool operator<=(const T& other) const + { + return m_offset <= other.offsetUnchecked(); + } + bool operator>=(const T& other) const + { + return m_offset >= other.offsetUnchecked(); + } + + T operator+(int value) const + { + return T(offset() + value); + } + T operator-(int value) const + { + return T(offset() - value); + } + T& operator+=(int value) + { + return *this = *this + value; + } + T& operator-=(int value) + { + return *this = *this - value; + } + +private: + unsigned m_offset; +}; + +} // namespace JSC + +#endif // GenericOffset_h + diff --git a/Source/JavaScriptCore/runtime/GenericTypedArrayView.h b/Source/JavaScriptCore/runtime/GenericTypedArrayView.h new file mode 100644 index 000000000..36e37ca32 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GenericTypedArrayView.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef GenericTypedArrayView_h +#define GenericTypedArrayView_h + +#include "ArrayBuffer.h" +#include "ArrayBufferView.h" + +namespace JSC { + +template<typename Adaptor> +class GenericTypedArrayView : public ArrayBufferView { +protected: + GenericTypedArrayView(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + +public: + static RefPtr<GenericTypedArrayView> create(unsigned length); + static RefPtr<GenericTypedArrayView> create(const typename Adaptor::Type* array, unsigned length); + static RefPtr<GenericTypedArrayView> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + + static RefPtr<GenericTypedArrayView> createUninitialized(unsigned length); + + typename Adaptor::Type* data() const { return static_cast<typename Adaptor::Type*>(baseAddress()); } + + bool set(GenericTypedArrayView<Adaptor>* array, unsigned offset) + { + return setImpl(array, offset * sizeof(typename Adaptor::Type)); + } + + bool setRange(const typename Adaptor::Type* data, size_t dataLength, unsigned offset) + { + return setRangeImpl( + reinterpret_cast<const char*>(data), + dataLength * sizeof(typename Adaptor::Type), + offset * sizeof(typename Adaptor::Type)); + } + + bool zeroRange(unsigned offset, size_t length) + { + return zeroRangeImpl(offset * sizeof(typename Adaptor::Type), length * sizeof(typename Adaptor::Type)); + } + + void zeroFill() { zeroRange(0, length()); } + + unsigned length() const + { + if (isNeutered()) + return 0; + return m_length; + } + + virtual unsigned byteLength() const override + { + return length() * sizeof(typename Adaptor::Type); + } + + typename Adaptor::Type item(unsigned index) const + { + ASSERT_WITH_SECURITY_IMPLICATION(index < this->length()); + return data()[index]; + } + + void set(unsigned index, double value) const + { + ASSERT_WITH_SECURITY_IMPLICATION(index < this->length()); + data()[index] = Adaptor::toNativeFromDouble(value); + } + + bool checkInboundData(unsigned offset, unsigned pos) const + { + unsigned length = this->length(); + return (offset <= length + && offset + pos <= length + // check overflow + && offset + pos >= offset); + } + + RefPtr<GenericTypedArrayView> subarray(int start) const; + RefPtr<GenericTypedArrayView> subarray(int start, int end) const; + + virtual TypedArrayType getType() const override + { + return Adaptor::typeValue; + } + + virtual JSArrayBufferView* wrap(ExecState*, JSGlobalObject*) override; + +private: + unsigned m_length; +}; + +} // namespace JSC + +#endif // GenericTypedArrayView_h + diff --git a/Source/JavaScriptCore/runtime/GenericTypedArrayViewInlines.h b/Source/JavaScriptCore/runtime/GenericTypedArrayViewInlines.h new file mode 100644 index 000000000..253726965 --- /dev/null +++ b/Source/JavaScriptCore/runtime/GenericTypedArrayViewInlines.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef GenericTypedArrayViewInlines_h +#define GenericTypedArrayViewInlines_h + +#include "GenericTypedArrayView.h" +#include "JSGlobalObject.h" + +namespace JSC { + +template<typename Adaptor> +GenericTypedArrayView<Adaptor>::GenericTypedArrayView( + PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : ArrayBufferView(buffer, byteOffset) + , m_length(length) +{ +} + +template<typename Adaptor> +RefPtr<GenericTypedArrayView<Adaptor>> GenericTypedArrayView<Adaptor>::create(unsigned length) +{ + RefPtr<ArrayBuffer> buffer = ArrayBuffer::create(length, sizeof(typename Adaptor::Type)); + if (!buffer) + return nullptr; + return create(buffer.release(), 0, length); +} + +template<typename Adaptor> +RefPtr<GenericTypedArrayView<Adaptor>> GenericTypedArrayView<Adaptor>::create( + const typename Adaptor::Type* array, unsigned length) +{ + RefPtr<GenericTypedArrayView> result = create(length); + memcpy(result->data(), array, length * sizeof(typename Adaptor::Type)); + return result; +} + +template<typename Adaptor> +RefPtr<GenericTypedArrayView<Adaptor>> GenericTypedArrayView<Adaptor>::create( + PassRefPtr<ArrayBuffer> passedBuffer, unsigned byteOffset, unsigned length) +{ + RefPtr<ArrayBuffer> buffer = passedBuffer; + if (!verifySubRangeLength(buffer, byteOffset, length, sizeof(typename Adaptor::Type)) + || !verifyByteOffsetAlignment(byteOffset, sizeof(typename Adaptor::Type))) { + return nullptr; + } + + return adoptRef(new GenericTypedArrayView(buffer.release(), byteOffset, length)); +} + +template<typename Adaptor> +RefPtr<GenericTypedArrayView<Adaptor>> +GenericTypedArrayView<Adaptor>::createUninitialized(unsigned length) +{ + RefPtr<ArrayBuffer> buffer = + ArrayBuffer::createUninitialized(length, sizeof(typename Adaptor::Type)); + if (!buffer) + return nullptr; + return create(buffer.release(), 0, length); +} + +template<typename Adaptor> +RefPtr<GenericTypedArrayView<Adaptor>> +GenericTypedArrayView<Adaptor>::subarray(int start) const +{ + return subarray(start, length()); +} + +template<typename Adaptor> +RefPtr<GenericTypedArrayView<Adaptor>> +GenericTypedArrayView<Adaptor>::subarray(int start, int end) const +{ + unsigned offset, length; + calculateOffsetAndLength(start, end, this->length(), &offset, &length); + clampOffsetAndNumElements<Adaptor::Type>(buffer(), byteOffset(), &offset, &length); + return create(buffer(), offset, length); +} + +template<typename Adaptor> +JSArrayBufferView* GenericTypedArrayView<Adaptor>::wrap( + ExecState* exec, JSGlobalObject* globalObject) +{ + return Adaptor::JSViewType::create( + exec->vm(), globalObject->typedArrayStructure(Adaptor::typeValue), this); +} + +} // namespace JSC + +#endif // GenericTypedArrayViewInlines_h + diff --git a/Source/JavaScriptCore/runtime/GetterSetter.cpp b/Source/JavaScriptCore/runtime/GetterSetter.cpp new file mode 100644 index 000000000..2cd66e5cd --- /dev/null +++ b/Source/JavaScriptCore/runtime/GetterSetter.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2007, 2008, 2009, 2014 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "GetterSetter.h" + +#include "Error.h" +#include "Exception.h" +#include "JSObject.h" +#include "JSCInlines.h" +#include <wtf/Assertions.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(GetterSetter); + +const ClassInfo GetterSetter::s_info = { "GetterSetter", 0, 0, CREATE_METHOD_TABLE(GetterSetter) }; + +void GetterSetter::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + GetterSetter* thisObject = jsCast<GetterSetter*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + JSCell::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_getter); + visitor.append(&thisObject->m_setter); +} + +GetterSetter* GetterSetter::withGetter(VM& vm, JSGlobalObject* globalObject, JSObject* newGetter) +{ + if (isGetterNull()) { + setGetter(vm, globalObject, newGetter); + return this; + } + + GetterSetter* result = GetterSetter::create(vm, globalObject); + result->setGetter(vm, globalObject, newGetter); + result->setSetter(vm, globalObject, setter()); + return result; +} + +GetterSetter* GetterSetter::withSetter(VM& vm, JSGlobalObject* globalObject, JSObject* newSetter) +{ + if (isSetterNull()) { + setSetter(vm, globalObject, newSetter); + return this; + } + + GetterSetter* result = GetterSetter::create(vm, globalObject); + result->setGetter(vm, globalObject, getter()); + result->setSetter(vm, globalObject, newSetter); + return result; +} + +JSValue callGetter(ExecState* exec, JSValue base, JSValue getterSetter) +{ + // FIXME: Some callers may invoke get() without checking for an exception first. + // We work around that by checking here. + if (exec->hadException()) + return exec->exception()->value(); + + JSObject* getter = jsCast<GetterSetter*>(getterSetter)->getter(); + + CallData callData; + CallType callType = getter->methodTable(exec->vm())->getCallData(getter, callData); + return call(exec, getter, callType, callData, base, ArgList()); +} + +void callSetter(ExecState* exec, JSValue base, JSValue getterSetter, JSValue value, ECMAMode ecmaMode) +{ + GetterSetter* getterSetterObj = jsCast<GetterSetter*>(getterSetter); + + if (getterSetterObj->isSetterNull()) { + if (ecmaMode == StrictMode) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + + JSObject* setter = getterSetterObj->setter(); + + MarkedArgumentBuffer args; + args.append(value); + + CallData callData; + CallType callType = setter->methodTable(exec->vm())->getCallData(setter, callData); + call(exec, setter, callType, callData, base, args); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/GetterSetter.h b/Source/JavaScriptCore/runtime/GetterSetter.h new file mode 100644 index 000000000..b983f043d --- /dev/null +++ b/Source/JavaScriptCore/runtime/GetterSetter.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2014 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef GetterSetter_h +#define GetterSetter_h + +#include "JSCell.h" + +#include "CallFrame.h" +#include "JSGlobalObject.h" +#include "NullGetterFunction.h" +#include "NullSetterFunction.h" +#include "Structure.h" + +namespace JSC { + +class JSObject; + +// This is an internal value object which stores getter and setter functions +// for a property. Instances of this class have the property that once a getter +// or setter is set to a non-null value, then they cannot be changed. This means +// that if a property holding a GetterSetter reference is constant-inferred and +// that constant is observed to have a non-null setter (or getter) then we can +// constant fold that setter (or getter). +class GetterSetter final : public JSCell { + friend class JIT; + +private: + GetterSetter(VM& vm, JSGlobalObject* globalObject) + : JSCell(vm, vm.getterSetterStructure.get()) + { + m_getter.set(vm, this, globalObject->nullGetterFunction()); + m_setter.set(vm, this, globalObject->nullSetterFunction()); + } + +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static GetterSetter* create(VM& vm, JSGlobalObject* globalObject) + { + GetterSetter* getterSetter = new (NotNull, allocateCell<GetterSetter>(vm.heap)) GetterSetter(vm, globalObject); + getterSetter->finishCreation(vm); + return getterSetter; + } + + static void visitChildren(JSCell*, SlotVisitor&); + + JSObject* getter() const { return m_getter.get(); } + + JSObject* getterConcurrently() const + { + JSObject* result = getter(); + WTF::loadLoadFence(); + return result; + } + + bool isGetterNull() const { return !!jsDynamicCast<NullGetterFunction*>(m_getter.get()); } + bool isSetterNull() const { return !!jsDynamicCast<NullSetterFunction*>(m_setter.get()); } + + // Set the getter. It's only valid to call this if you've never set the getter on this + // object. + void setGetter(VM& vm, JSGlobalObject* globalObject, JSObject* getter) + { + if (!getter) + getter = jsCast<JSObject*>(globalObject->nullGetterFunction()); + + RELEASE_ASSERT(isGetterNull()); + WTF::storeStoreFence(); + m_getter.set(vm, this, getter); + } + + JSObject* setter() const { return m_setter.get(); } + + JSObject* setterConcurrently() const + { + JSObject* result = setter(); + WTF::loadLoadFence(); + return result; + } + + // Set the setter. It's only valid to call this if you've never set the setter on this + // object. + void setSetter(VM& vm, JSGlobalObject* globalObject, JSObject* setter) + { + if (!setter) + setter = jsCast<JSObject*>(globalObject->nullSetterFunction()); + + RELEASE_ASSERT(isSetterNull()); + WTF::storeStoreFence(); + m_setter.set(vm, this, setter); + } + + GetterSetter* withGetter(VM&, JSGlobalObject*, JSObject* getter); + GetterSetter* withSetter(VM&, JSGlobalObject*, JSObject* setter); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(GetterSetterType), info()); + } + + static ptrdiff_t offsetOfGetter() + { + return OBJECT_OFFSETOF(GetterSetter, m_getter); + } + + static ptrdiff_t offsetOfSetter() + { + return OBJECT_OFFSETOF(GetterSetter, m_setter); + } + + DECLARE_INFO; + +private: + WriteBarrier<JSObject> m_getter; + WriteBarrier<JSObject> m_setter; +}; + +GetterSetter* asGetterSetter(JSValue); + +inline GetterSetter* asGetterSetter(JSValue value) +{ + ASSERT_WITH_SECURITY_IMPLICATION(value.asCell()->isGetterSetter()); + return static_cast<GetterSetter*>(value.asCell()); +} + +JSValue callGetter(ExecState*, JSValue base, JSValue getterSetter); +void callSetter(ExecState*, JSValue base, JSValue getterSetter, JSValue, ECMAMode); + +} // namespace JSC + +#endif // GetterSetter_h diff --git a/Source/JavaScriptCore/runtime/Identifier.cpp b/Source/JavaScriptCore/runtime/Identifier.cpp new file mode 100644 index 000000000..d26d9567c --- /dev/null +++ b/Source/JavaScriptCore/runtime/Identifier.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2012 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Identifier.h" + +#include "CallFrame.h" +#include "JSObject.h" +#include "JSScope.h" +#include "NumericStrings.h" +#include "JSCInlines.h" +#include <new> +#include <string.h> +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> +#include <wtf/HashSet.h> +#include <wtf/text/ASCIIFastPath.h> +#include <wtf/text/StringHash.h> + +using WTF::ThreadSpecific; + +namespace JSC { + +Ref<StringImpl> Identifier::add(VM* vm, const char* c) +{ + ASSERT(c); + ASSERT(c[0]); + if (!c[1]) + return *vm->smallStrings.singleCharacterStringRep(c[0]); + + return *AtomicStringImpl::add(c); +} + +Ref<StringImpl> Identifier::add(ExecState* exec, const char* c) +{ + return add(&exec->vm(), c); +} + +Ref<StringImpl> Identifier::add8(VM* vm, const UChar* s, int length) +{ + if (length == 1) { + UChar c = s[0]; + ASSERT(c <= 0xff); + if (canUseSingleCharacterString(c)) + return *vm->smallStrings.singleCharacterStringRep(c); + } + if (!length) + return *StringImpl::empty(); + + return *AtomicStringImpl::add(s, length); +} + +Identifier Identifier::from(ExecState* exec, unsigned value) +{ + return Identifier(exec, exec->vm().numericStrings.add(value)); +} + +Identifier Identifier::from(ExecState* exec, int value) +{ + return Identifier(exec, exec->vm().numericStrings.add(value)); +} + +Identifier Identifier::from(ExecState* exec, double value) +{ + return Identifier(exec, exec->vm().numericStrings.add(value)); +} + +Identifier Identifier::from(VM* vm, unsigned value) +{ + return Identifier(vm, vm->numericStrings.add(value)); +} + +Identifier Identifier::from(VM* vm, int value) +{ + return Identifier(vm, vm->numericStrings.add(value)); +} + +Identifier Identifier::from(VM* vm, double value) +{ + return Identifier(vm, vm->numericStrings.add(value)); +} + +void Identifier::dump(PrintStream& out) const +{ + if (impl()) + out.print(impl()); + else + out.print("<null identifier>"); +} + +#ifndef NDEBUG + +void Identifier::checkCurrentAtomicStringTable(VM* vm) +{ + // Check the identifier table accessible through the threadspecific matches the + // vm's identifier table. + ASSERT_UNUSED(vm, vm->atomicStringTable() == wtfThreadData().atomicStringTable()); +} + +void Identifier::checkCurrentAtomicStringTable(ExecState* exec) +{ + checkCurrentAtomicStringTable(&exec->vm()); +} + +#else + +// These only exists so that our exports are the same for debug and release builds. +// This would be an RELEASE_ASSERT_NOT_REACHED(), but we're in NDEBUG only code here! +NO_RETURN_DUE_TO_CRASH void Identifier::checkCurrentAtomicStringTable(VM*) { CRASH(); } +NO_RETURN_DUE_TO_CRASH void Identifier::checkCurrentAtomicStringTable(ExecState*) { CRASH(); } + +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Identifier.h b/Source/JavaScriptCore/runtime/Identifier.h new file mode 100644 index 000000000..61de6536a --- /dev/null +++ b/Source/JavaScriptCore/runtime/Identifier.h @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef Identifier_h +#define Identifier_h + +#include "VM.h" +#include <wtf/Optional.h> +#include <wtf/ThreadSpecific.h> +#include <wtf/WTFThreadData.h> +#include <wtf/text/CString.h> +#include <wtf/text/UniquedStringImpl.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +class ExecState; + +ALWAYS_INLINE bool isIndex(uint32_t index) +{ + return index != 0xFFFFFFFFU; +} + +template <typename CharType> +ALWAYS_INLINE Optional<uint32_t> parseIndex(const CharType* characters, unsigned length) +{ + // An empty string is not a number. + if (!length) + return Nullopt; + + // Get the first character, turning it into a digit. + uint32_t value = characters[0] - '0'; + if (value > 9) + return Nullopt; + + // Check for leading zeros. If the first characher is 0, then the + // length of the string must be one - e.g. "042" is not equal to "42". + if (!value && length > 1) + return Nullopt; + + while (--length) { + // Multiply value by 10, checking for overflow out of 32 bits. + if (value > 0xFFFFFFFFU / 10) + return Nullopt; + value *= 10; + + // Get the next character, turning it into a digit. + uint32_t newValue = *(++characters) - '0'; + if (newValue > 9) + return Nullopt; + + // Add in the old value, checking for overflow out of 32 bits. + newValue += value; + if (newValue < value) + return Nullopt; + value = newValue; + } + + if (!isIndex(value)) + return Nullopt; + return value; +} + +ALWAYS_INLINE Optional<uint32_t> parseIndex(StringImpl& impl) +{ + if (impl.is8Bit()) + return parseIndex(impl.characters8(), impl.length()); + return parseIndex(impl.characters16(), impl.length()); +} + +class Identifier { + friend class Structure; +public: + Identifier() { } + enum EmptyIdentifierFlag { EmptyIdentifier }; + Identifier(EmptyIdentifierFlag) : m_string(StringImpl::empty()) { ASSERT(m_string.impl()->isAtomic()); } + + const String& string() const { return m_string; } + UniquedStringImpl* impl() const { return static_cast<UniquedStringImpl*>(m_string.impl()); } + + int length() const { return m_string.length(); } + + CString ascii() const { return m_string.ascii(); } + CString utf8() const { return m_string.utf8(); } + + // There's 2 functions to construct Identifier from string, (1) fromString and (2) fromUid. + // They have different meanings in keeping or discarding symbol-ness of strings. + // (1): fromString + // Just construct Identifier from string. String held by Identifier is always atomized. + // Symbol-ness of StringImpl*, which represents that the string is inteded to be used for ES6 Symbols, is discarded. + // So a constructed Identifier never represents a symbol. + // (2): fromUid + // `StringImpl* uid` represents ether String or Symbol property. + // fromUid keeps symbol-ness of provided StringImpl* while fromString discards it. + // Use fromUid when constructing Identifier from StringImpl* which may represent symbols. + + // Only to be used with string literals. + template<unsigned charactersCount> + static Identifier fromString(VM*, const char (&characters)[charactersCount]); + template<unsigned charactersCount> + static Identifier fromString(ExecState*, const char (&characters)[charactersCount]); + static Identifier fromString(VM*, const LChar*, int length); + static Identifier fromString(VM*, const UChar*, int length); + static Identifier fromString(VM*, const String&); + static Identifier fromString(ExecState*, AtomicStringImpl*); + static Identifier fromString(ExecState*, const AtomicString&); + static Identifier fromString(ExecState*, const String&); + static Identifier fromString(ExecState*, const char*); + + static Identifier fromUid(VM*, UniquedStringImpl* uid); + static Identifier fromUid(ExecState*, UniquedStringImpl* uid); + static Identifier fromUid(const PrivateName&); + + static Identifier createLCharFromUChar(VM* vm, const UChar* s, int length) { return Identifier(vm, add8(vm, s, length)); } + + JS_EXPORT_PRIVATE static Identifier from(ExecState*, unsigned y); + JS_EXPORT_PRIVATE static Identifier from(ExecState*, int y); + static Identifier from(ExecState*, double y); + static Identifier from(VM*, unsigned y); + static Identifier from(VM*, int y); + static Identifier from(VM*, double y); + + bool isNull() const { return m_string.isNull(); } + bool isEmpty() const { return m_string.isEmpty(); } + bool isSymbol() const { return !isNull() && impl()->isSymbol(); } + + friend bool operator==(const Identifier&, const Identifier&); + friend bool operator!=(const Identifier&, const Identifier&); + + friend bool operator==(const Identifier&, const LChar*); + friend bool operator==(const Identifier&, const char*); + friend bool operator!=(const Identifier&, const LChar*); + friend bool operator!=(const Identifier&, const char*); + + static bool equal(const StringImpl*, const LChar*); + static inline bool equal(const StringImpl*a, const char*b) { return Identifier::equal(a, reinterpret_cast<const LChar*>(b)); }; + static bool equal(const StringImpl*, const LChar*, unsigned length); + static bool equal(const StringImpl*, const UChar*, unsigned length); + static bool equal(const StringImpl* a, const StringImpl* b) { return ::equal(a, b); } + + // Only to be used with string literals. + JS_EXPORT_PRIVATE static Ref<StringImpl> add(VM*, const char*); + JS_EXPORT_PRIVATE static Ref<StringImpl> add(ExecState*, const char*); + + void dump(PrintStream&) const; + +private: + String m_string; + + // Only to be used with string literals. + template<unsigned charactersCount> + Identifier(VM* vm, const char (&characters)[charactersCount]) : m_string(add(vm, characters)) { ASSERT(m_string.impl()->isAtomic()); } + + Identifier(VM* vm, const LChar* s, int length) : m_string(add(vm, s, length)) { ASSERT(m_string.impl()->isAtomic()); } + Identifier(VM* vm, const UChar* s, int length) : m_string(add(vm, s, length)) { ASSERT(m_string.impl()->isAtomic()); } + Identifier(ExecState*, AtomicStringImpl*); + Identifier(ExecState*, const AtomicString&); + Identifier(VM* vm, const String& string) : m_string(add(vm, string.impl())) { ASSERT(m_string.impl()->isAtomic()); } + Identifier(VM* vm, StringImpl* rep) : m_string(add(vm, rep)) { ASSERT(m_string.impl()->isAtomic()); } + + Identifier(SymbolImpl& uid) + : m_string(&uid) + { + } + + template <typename CharType> + ALWAYS_INLINE static uint32_t toUInt32FromCharacters(const CharType* characters, unsigned length, bool& ok); + + static bool equal(const Identifier& a, const Identifier& b) { return a.m_string.impl() == b.m_string.impl(); } + static bool equal(const Identifier& a, const LChar* b) { return equal(a.m_string.impl(), b); } + + template <typename T> static Ref<StringImpl> add(VM*, const T*, int length); + static Ref<StringImpl> add8(VM*, const UChar*, int length); + template <typename T> ALWAYS_INLINE static bool canUseSingleCharacterString(T); + + static Ref<StringImpl> add(ExecState*, StringImpl*); + static Ref<StringImpl> add(VM*, StringImpl*); + +#ifndef NDEBUG + JS_EXPORT_PRIVATE static void checkCurrentAtomicStringTable(ExecState*); + JS_EXPORT_PRIVATE static void checkCurrentAtomicStringTable(VM*); +#else + JS_EXPORT_PRIVATE NO_RETURN_DUE_TO_CRASH static void checkCurrentAtomicStringTable(ExecState*); + JS_EXPORT_PRIVATE NO_RETURN_DUE_TO_CRASH static void checkCurrentAtomicStringTable(VM*); +#endif +}; + +template <> ALWAYS_INLINE bool Identifier::canUseSingleCharacterString(LChar) +{ + ASSERT(maxSingleCharacterString == 0xff); + return true; +} + +template <> ALWAYS_INLINE bool Identifier::canUseSingleCharacterString(UChar c) +{ + return (c <= maxSingleCharacterString); +} + +template <typename T> +Ref<StringImpl> Identifier::add(VM* vm, const T* s, int length) +{ + if (length == 1) { + T c = s[0]; + if (canUseSingleCharacterString(c)) + return *vm->smallStrings.singleCharacterStringRep(c); + } + if (!length) + return *StringImpl::empty(); + + return *AtomicStringImpl::add(s, length); +} + +inline bool operator==(const Identifier& a, const Identifier& b) +{ + return Identifier::equal(a, b); +} + +inline bool operator!=(const Identifier& a, const Identifier& b) +{ + return !Identifier::equal(a, b); +} + +inline bool operator==(const Identifier& a, const LChar* b) +{ + return Identifier::equal(a, b); +} + +inline bool operator==(const Identifier& a, const char* b) +{ + return Identifier::equal(a, reinterpret_cast<const LChar*>(b)); +} + +inline bool operator!=(const Identifier& a, const LChar* b) +{ + return !Identifier::equal(a, b); +} + +inline bool operator!=(const Identifier& a, const char* b) +{ + return !Identifier::equal(a, reinterpret_cast<const LChar*>(b)); +} + +inline bool Identifier::equal(const StringImpl* r, const LChar* s) +{ + return WTF::equal(r, s); +} + +inline bool Identifier::equal(const StringImpl* r, const LChar* s, unsigned length) +{ + return WTF::equal(r, s, length); +} + +inline bool Identifier::equal(const StringImpl* r, const UChar* s, unsigned length) +{ + return WTF::equal(r, s, length); +} + +ALWAYS_INLINE Optional<uint32_t> parseIndex(const Identifier& identifier) +{ + auto uid = identifier.impl(); + if (!uid) + return Nullopt; + if (uid->isSymbol()) + return Nullopt; + return parseIndex(*uid); +} + +struct IdentifierRepHash : PtrHash<RefPtr<UniquedStringImpl>> { + static unsigned hash(const RefPtr<UniquedStringImpl>& key) { return key->existingSymbolAwareHash(); } + static unsigned hash(UniquedStringImpl* key) { return key->existingSymbolAwareHash(); } +}; + +struct IdentifierMapIndexHashTraits : HashTraits<int> { + static int emptyValue() { return std::numeric_limits<int>::max(); } + static const bool emptyValueIsZero = false; +}; + +typedef HashMap<RefPtr<UniquedStringImpl>, int, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, IdentifierMapIndexHashTraits> IdentifierMap; +typedef HashMap<UniquedStringImpl*, int, IdentifierRepHash, HashTraits<UniquedStringImpl*>, IdentifierMapIndexHashTraits> BorrowedIdentifierMap; + +} // namespace JSC + +namespace WTF { + +template <> struct VectorTraits<JSC::Identifier> : SimpleClassVectorTraits { }; + +} // namespace WTF + +#endif // Identifier_h diff --git a/Source/JavaScriptCore/runtime/IdentifierInlines.h b/Source/JavaScriptCore/runtime/IdentifierInlines.h new file mode 100644 index 000000000..6b50748e7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IdentifierInlines.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef IdentifierInlines_h +#define IdentifierInlines_h + +#include "CallFrame.h" +#include "Identifier.h" + +namespace JSC { + +inline Identifier::Identifier(ExecState* exec, AtomicStringImpl* string) + : m_string(string) +{ +#ifndef NDEBUG + checkCurrentAtomicStringTable(exec); + if (string) + ASSERT_WITH_MESSAGE(!string->length() || string->isSymbol() || AtomicStringImpl::isInAtomicStringTable(string), "The atomic string comes from an other thread!"); +#else + UNUSED_PARAM(exec); +#endif +} + +inline Identifier::Identifier(ExecState* exec, const AtomicString& string) + : m_string(string.string()) +{ +#ifndef NDEBUG + checkCurrentAtomicStringTable(exec); + if (!string.isNull()) + ASSERT_WITH_MESSAGE(!string.length() || string.impl()->isSymbol() || AtomicStringImpl::isInAtomicStringTable(string.impl()), "The atomic string comes from an other thread!"); +#else + UNUSED_PARAM(exec); +#endif +} + +inline Ref<StringImpl> Identifier::add(ExecState* exec, StringImpl* r) +{ +#ifndef NDEBUG + checkCurrentAtomicStringTable(exec); +#endif + return *AtomicStringImpl::addWithStringTableProvider(*exec, r); +} +inline Ref<StringImpl> Identifier::add(VM* vm, StringImpl* r) +{ +#ifndef NDEBUG + checkCurrentAtomicStringTable(vm); +#endif + return *AtomicStringImpl::addWithStringTableProvider(*vm, r); +} + +inline Identifier Identifier::fromUid(VM* vm, UniquedStringImpl* uid) +{ + if (!uid || !uid->isSymbol()) + return Identifier(vm, uid); + return static_cast<SymbolImpl&>(*uid); +} + +inline Identifier Identifier::fromUid(ExecState* exec, UniquedStringImpl* uid) +{ + return fromUid(&exec->vm(), uid); +} + +inline Identifier Identifier::fromUid(const PrivateName& name) +{ + return *name.uid(); +} + +template<unsigned charactersCount> +inline Identifier Identifier::fromString(VM* vm, const char (&characters)[charactersCount]) +{ + return Identifier(vm, characters); +} + +template<unsigned charactersCount> +inline Identifier Identifier::fromString(ExecState* exec, const char (&characters)[charactersCount]) +{ + return Identifier(&exec->vm(), characters); +} + +inline Identifier Identifier::fromString(VM* vm, const LChar* s, int length) +{ + return Identifier(vm, s, length); +} + +inline Identifier Identifier::fromString(VM* vm, const UChar* s, int length) +{ + return Identifier(vm, s, length); +} + +inline Identifier Identifier::fromString(VM* vm, const String& string) +{ + return Identifier(vm, string.impl()); +} + +inline Identifier Identifier::fromString(ExecState* exec, const String& string) +{ + return Identifier(&exec->vm(), string.impl()); +} + +inline Identifier Identifier::fromString(ExecState* exec, AtomicStringImpl* atomicString) +{ + return Identifier(exec, atomicString); +} + +inline Identifier Identifier::fromString(ExecState* exec, const AtomicString& atomicString) +{ + return Identifier(exec, atomicString); +} + +inline Identifier Identifier::fromString(ExecState* exec, const char* s) +{ + return Identifier(exec, AtomicString(s)); +} + +} // namespace JSC + +#endif // IdentifierInlines_h diff --git a/Source/JavaScriptCore/runtime/IndexingHeader.h b/Source/JavaScriptCore/runtime/IndexingHeader.h new file mode 100644 index 000000000..a88643239 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IndexingHeader.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2012, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef IndexingHeader_h +#define IndexingHeader_h + +#include "PropertyStorage.h" + +namespace JSC { + +class ArrayBuffer; +class Butterfly; +class LLIntOffsetsExtractor; +class Structure; +struct ArrayStorage; + +class IndexingHeader { +public: + // Define the maximum storage vector length to be 2^32 / sizeof(JSValue) / 2 to ensure that + // there is no risk of overflow. + enum { maximumLength = 0x10000000 }; + + static ptrdiff_t offsetOfIndexingHeader() { return -static_cast<ptrdiff_t>(sizeof(IndexingHeader)); } + + static ptrdiff_t offsetOfArrayBuffer() { return OBJECT_OFFSETOF(IndexingHeader, u.typedArray.buffer); } + static ptrdiff_t offsetOfPublicLength() { return OBJECT_OFFSETOF(IndexingHeader, u.lengths.publicLength); } + static ptrdiff_t offsetOfVectorLength() { return OBJECT_OFFSETOF(IndexingHeader, u.lengths.vectorLength); } + + IndexingHeader() + { + u.lengths.publicLength = 0; + u.lengths.vectorLength = 0; + } + + uint32_t vectorLength() const { return u.lengths.vectorLength; } + + void setVectorLength(uint32_t length) + { + RELEASE_ASSERT(length <= maximumLength); + u.lengths.vectorLength = length; + } + + uint32_t publicLength() const { return u.lengths.publicLength; } + void setPublicLength(uint32_t auxWord) { u.lengths.publicLength = auxWord; } + + ArrayBuffer* arrayBuffer() { return u.typedArray.buffer; } + void setArrayBuffer(ArrayBuffer* buffer) { u.typedArray.buffer = buffer; } + + static IndexingHeader* from(Butterfly* butterfly) + { + return reinterpret_cast<IndexingHeader*>(butterfly) - 1; + } + + static const IndexingHeader* from(const Butterfly* butterfly) + { + return reinterpret_cast<const IndexingHeader*>(butterfly) - 1; + } + + static IndexingHeader* from(ArrayStorage* arrayStorage) + { + return const_cast<IndexingHeader*>(from(const_cast<const ArrayStorage*>(arrayStorage))); + } + + static const IndexingHeader* from(const ArrayStorage* arrayStorage) + { + return reinterpret_cast<const IndexingHeader*>(arrayStorage) - 1; + } + + static IndexingHeader* fromEndOf(PropertyStorage propertyStorage) + { + return reinterpret_cast<IndexingHeader*>(propertyStorage); + } + + PropertyStorage propertyStorage() + { + return reinterpret_cast_ptr<PropertyStorage>(this); + } + + ConstPropertyStorage propertyStorage() const + { + return reinterpret_cast_ptr<ConstPropertyStorage>(this); + } + + ArrayStorage* arrayStorage() + { + return reinterpret_cast<ArrayStorage*>(this + 1); + } + + Butterfly* butterfly() + { + return reinterpret_cast<Butterfly*>(this + 1); + } + + // These methods are not standalone in the sense that they cannot be + // used on a copy of the IndexingHeader. + size_t preCapacity(Structure*); + size_t indexingPayloadSizeInBytes(Structure*); + +private: + friend class LLIntOffsetsExtractor; + + union { + struct { + uint32_t publicLength; // The meaning of this field depends on the array type, but for all JSArrays we rely on this being the publicly visible length (array.length). + uint32_t vectorLength; // The length of the indexed property storage. The actual size of the storage depends on this, and the type. + } lengths; + + struct { + ArrayBuffer* buffer; + } typedArray; + } u; +}; + +} // namespace JSC + +#endif // IndexingHeader_h + diff --git a/Source/JavaScriptCore/runtime/IndexingHeaderInlines.h b/Source/JavaScriptCore/runtime/IndexingHeaderInlines.h new file mode 100644 index 000000000..b188741c3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IndexingHeaderInlines.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef IndexingHeaderInlines_h +#define IndexingHeaderInlines_h + +#include "ArrayStorage.h" +#include "IndexingHeader.h" +#include "Structure.h" + +namespace JSC { + +inline size_t IndexingHeader::preCapacity(Structure* structure) +{ + if (LIKELY(!hasAnyArrayStorage(structure->indexingType()))) + return 0; + + return arrayStorage()->m_indexBias; +} + +inline size_t IndexingHeader::indexingPayloadSizeInBytes(Structure* structure) +{ + switch (structure->indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return vectorLength() * sizeof(EncodedJSValue); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return ArrayStorage::sizeFor(arrayStorage()->vectorLength()); + + default: + ASSERT(!hasIndexedProperties(structure->indexingType())); + return 0; + } +} + +} // namespace JSC + +#endif // IndexingHeaderInlines_h + diff --git a/Source/JavaScriptCore/runtime/IndexingType.cpp b/Source/JavaScriptCore/runtime/IndexingType.cpp new file mode 100644 index 000000000..f7d54754d --- /dev/null +++ b/Source/JavaScriptCore/runtime/IndexingType.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "IndexingType.h" + +#include <wtf/StringExtras.h> + +namespace JSC { + +IndexingType leastUpperBoundOfIndexingTypes(IndexingType a, IndexingType b) +{ + // It doesn't make sense to LUB something that is an array with something that isn't. + ASSERT((a & IsArray) == (b & IsArray)); + + // Boy, this sure is easy right now. + return std::max(a, b); +} + +IndexingType leastUpperBoundOfIndexingTypeAndType(IndexingType indexingType, SpeculatedType type) +{ + if (!type) + return indexingType; + switch (indexingType) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + if (isInt32Speculation(type)) + return (indexingType & ~IndexingShapeMask) | Int32Shape; + if (isFullNumberSpeculation(type)) + return (indexingType & ~IndexingShapeMask) | DoubleShape; + return (indexingType & ~IndexingShapeMask) | ContiguousShape; + case ALL_DOUBLE_INDEXING_TYPES: + if (isFullNumberSpeculation(type)) + return indexingType; + return (indexingType & ~IndexingShapeMask) | ContiguousShape; + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return indexingType; + default: + CRASH(); + return 0; + } +} + +IndexingType leastUpperBoundOfIndexingTypeAndValue(IndexingType indexingType, JSValue value) +{ + return leastUpperBoundOfIndexingTypeAndType(indexingType, speculationFromValue(value)); +} + +void dumpIndexingType(PrintStream& out, IndexingType indexingType) +{ + const char* basicName; + switch (indexingType & AllArrayTypes) { + case NonArray: + basicName = "NonArray"; + break; + case NonArrayWithInt32: + basicName = "NonArrayWithInt32"; + break; + case NonArrayWithDouble: + basicName = "NonArrayWithDouble"; + break; + case NonArrayWithContiguous: + basicName = "NonArrayWithContiguous"; + break; + case NonArrayWithArrayStorage: + basicName = "NonArrayWithArrayStorage"; + break; + case NonArrayWithSlowPutArrayStorage: + basicName = "NonArrayWithSlowPutArrayStorage"; + break; + case ArrayClass: + basicName = "ArrayClass"; + break; + case ArrayWithUndecided: + basicName = "ArrayWithUndecided"; + break; + case ArrayWithInt32: + basicName = "ArrayWithInt32"; + break; + case ArrayWithDouble: + basicName = "ArrayWithDouble"; + break; + case ArrayWithContiguous: + basicName = "ArrayWithContiguous"; + break; + case ArrayWithArrayStorage: + basicName = "ArrayWithArrayStorage"; + break; + case ArrayWithSlowPutArrayStorage: + basicName = "ArrayWithSlowPutArrayStorage"; + break; + default: + basicName = "Unknown!"; + break; + } + + out.printf("%s%s", basicName, (indexingType & MayHaveIndexedAccessors) ? "|MayHaveIndexedAccessors" : ""); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/IndexingType.h b/Source/JavaScriptCore/runtime/IndexingType.h new file mode 100644 index 000000000..3f9fdd1ed --- /dev/null +++ b/Source/JavaScriptCore/runtime/IndexingType.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2012, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef IndexingType_h +#define IndexingType_h + +#include "SpeculatedType.h" +#include <wtf/StdLibExtras.h> + +namespace JSC { + +/* + Structure of the IndexingType + ============================= + Conceptually, the IndexingType looks like this: + + struct IndexingType { + uint8_t isArray:1; // bit 0 + uint8_t shape:4; // bit 1 - 3 + uint8_t mayHaveIndexedAccessors:1; // bit 4 + }; + + The shape values (e.g. Int32Shape, ContiguousShape, etc) are an enumeration of + various shapes (though not necessarily sequential in terms of their values). + Hence, shape values are not bitwise exclusive with respect to each other. +*/ + +typedef uint8_t IndexingType; + +// Flags for testing the presence of capabilities. +static const IndexingType IsArray = 0x01; + +// The shape of the indexed property storage. +static const IndexingType IndexingShapeMask = 0x0E; +static const IndexingType NoIndexingShape = 0x00; +static const IndexingType UndecidedShape = 0x02; // Only useful for arrays. +static const IndexingType Int32Shape = 0x04; +static const IndexingType DoubleShape = 0x06; +static const IndexingType ContiguousShape = 0x08; +static const IndexingType ArrayStorageShape = 0x0A; +static const IndexingType SlowPutArrayStorageShape = 0x0C; + +static const IndexingType IndexingShapeShift = 1; +static const IndexingType NumberOfIndexingShapes = 7; + +// Additional flags for tracking the history of the type. These are usually +// masked off unless you ask for them directly. +static const IndexingType MayHaveIndexedAccessors = 0x10; + +// List of acceptable array types. +static const IndexingType NonArray = 0x0; +static const IndexingType NonArrayWithInt32 = Int32Shape; +static const IndexingType NonArrayWithDouble = DoubleShape; +static const IndexingType NonArrayWithContiguous = ContiguousShape; +static const IndexingType NonArrayWithArrayStorage = ArrayStorageShape; +static const IndexingType NonArrayWithSlowPutArrayStorage = SlowPutArrayStorageShape; +static const IndexingType ArrayClass = IsArray; // I'd want to call this "Array" but this would lead to disastrous namespace pollution. +static const IndexingType ArrayWithUndecided = IsArray | UndecidedShape; +static const IndexingType ArrayWithInt32 = IsArray | Int32Shape; +static const IndexingType ArrayWithDouble = IsArray | DoubleShape; +static const IndexingType ArrayWithContiguous = IsArray | ContiguousShape; +static const IndexingType ArrayWithArrayStorage = IsArray | ArrayStorageShape; +static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | SlowPutArrayStorageShape; + +#define ALL_BLANK_INDEXING_TYPES \ + NonArray: \ + case ArrayClass + +#define ALL_UNDECIDED_INDEXING_TYPES \ + ArrayWithUndecided + +#define ALL_INT32_INDEXING_TYPES \ + NonArrayWithInt32: \ + case ArrayWithInt32 + +#define ALL_DOUBLE_INDEXING_TYPES \ + NonArrayWithDouble: \ + case ArrayWithDouble + +#define ALL_CONTIGUOUS_INDEXING_TYPES \ + NonArrayWithContiguous: \ + case ArrayWithContiguous + +#define ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES \ + ArrayWithArrayStorage: \ + case ArrayWithSlowPutArrayStorage + +#define ALL_ARRAY_STORAGE_INDEXING_TYPES \ + NonArrayWithArrayStorage: \ + case NonArrayWithSlowPutArrayStorage: \ + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES + +static inline bool hasIndexedProperties(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) != NoIndexingShape; +} + +static inline bool hasUndecided(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == UndecidedShape; +} + +static inline bool hasInt32(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == Int32Shape; +} + +static inline bool hasDouble(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == DoubleShape; +} + +static inline bool hasContiguous(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == ContiguousShape; +} + +static inline bool hasArrayStorage(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == ArrayStorageShape; +} + +static inline bool hasAnyArrayStorage(IndexingType indexingType) +{ + return static_cast<uint8_t>(indexingType & IndexingShapeMask) >= ArrayStorageShape; +} + +static inline bool shouldUseSlowPut(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == SlowPutArrayStorageShape; +} + +// Return an indexing type that can handle all of the elements of both indexing types. +IndexingType leastUpperBoundOfIndexingTypes(IndexingType, IndexingType); + +IndexingType leastUpperBoundOfIndexingTypeAndType(IndexingType, SpeculatedType); +IndexingType leastUpperBoundOfIndexingTypeAndValue(IndexingType, JSValue); + +void dumpIndexingType(PrintStream&, IndexingType); +MAKE_PRINT_ADAPTOR(IndexingTypeDump, IndexingType, dumpIndexingType); + +// Mask of all possible types. +static const IndexingType AllArrayTypes = IndexingShapeMask | IsArray; + +// Mask of all possible types including the history. +static const IndexingType AllArrayTypesAndHistory = AllArrayTypes | MayHaveIndexedAccessors; + +} // namespace JSC + +#endif // IndexingType_h + diff --git a/Source/JavaScriptCore/runtime/InferredValue.cpp b/Source/JavaScriptCore/runtime/InferredValue.cpp new file mode 100644 index 000000000..73c6bc76b --- /dev/null +++ b/Source/JavaScriptCore/runtime/InferredValue.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "InferredValue.h" + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo InferredValue::s_info = { "InferredValue", 0, 0, CREATE_METHOD_TABLE(InferredValue) }; + +InferredValue* InferredValue::create(VM& vm) +{ + InferredValue* result = new (NotNull, allocateCell<InferredValue>(vm.heap)) InferredValue(vm); + result->finishCreation(vm); + return result; +} + +void InferredValue::destroy(JSCell* cell) +{ + InferredValue* inferredValue = static_cast<InferredValue*>(cell); + inferredValue->InferredValue::~InferredValue(); +} + +Structure* InferredValue::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); +} + +void InferredValue::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + InferredValue* inferredValue = jsCast<InferredValue*>(cell); + + if (inferredValue->m_set.hasBeenInvalidated()) { + inferredValue->m_cleanup = nullptr; + return; + } + + if (!inferredValue->m_value) + return; + if (!inferredValue->m_value.get().isCell()) + return; + + if (!inferredValue->m_cleanup) + inferredValue->m_cleanup = std::make_unique<ValueCleanup>(inferredValue); + visitor.addUnconditionalFinalizer(inferredValue->m_cleanup.get()); +} + +InferredValue::InferredValue(VM& vm) + : Base(vm, vm.inferredValueStructure.get()) + , m_set(ClearWatchpoint) +{ +} + +InferredValue::~InferredValue() +{ +} + +void InferredValue::notifyWriteSlow(VM& vm, JSValue value, const FireDetail& detail) +{ + ASSERT(!!value); + switch (m_set.state()) { + case ClearWatchpoint: + m_value.set(vm, this, value); + m_set.startWatching(); + return; + + case IsWatched: + ASSERT(!!m_value); + if (m_value.get() == value) + return; + invalidate(detail); + return; + + case IsInvalidated: + ASSERT_NOT_REACHED(); + return; + } + + ASSERT_NOT_REACHED(); +} + +void InferredValue::notifyWriteSlow(VM& vm, JSValue value, const char* reason) +{ + notifyWriteSlow(vm, value, StringFireDetail(reason)); +} + +InferredValue::ValueCleanup::ValueCleanup(InferredValue* owner) + : m_owner(owner) +{ +} + +InferredValue::ValueCleanup::~ValueCleanup() +{ +} + +void InferredValue::ValueCleanup::finalizeUnconditionally() +{ + ASSERT(m_owner->m_value); + ASSERT(m_owner->m_value.get().isCell()); + + if (Heap::isMarked(m_owner->m_value.get().asCell())) + return; + + m_owner->invalidate(StringFireDetail("InferredValue clean-up during GC")); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/InferredValue.h b/Source/JavaScriptCore/runtime/InferredValue.h new file mode 100644 index 000000000..28318e992 --- /dev/null +++ b/Source/JavaScriptCore/runtime/InferredValue.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef InferredValue_h +#define InferredValue_h + +#include "JSCell.h" +#include "Watchpoint.h" +#include "WriteBarrier.h" + +namespace JSC { + +// Allocate one of these if you'd like to infer a constant value. Writes to the value should use +// notifyWrite(). So long as exactly one value had ever been written and invalidate() has never been +// called, and you register a watchpoint, you can rely on the inferredValue() being the one true +// value. +// +// Commonly used for inferring singletons - in that case each allocation does notifyWrite(). But you +// can use it for other things as well. + +class InferredValue : public JSCell { +public: + typedef JSCell Base; + + static InferredValue* create(VM&); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + static void visitChildren(JSCell*, SlotVisitor&); + + DECLARE_INFO; + + // For the purpose of deciding whether or not to watch this variable, you only need + // to inspect inferredValue(). If this returns something other than the empty + // value, then it means that at all future safepoints, this watchpoint set will be + // in one of these states: + // + // IsWatched: in this case, the variable's value must still be the + // inferredValue. + // + // IsInvalidated: in this case the variable's value may be anything but you'll + // either notice that it's invalidated and not install the watchpoint, or + // you will have been notified that the watchpoint was fired. + JSValue inferredValue() { return m_value.get(); } + + // Forwards some WatchpointSet methods. + WatchpointState state() const { return m_set.state(); } + bool isStillValid() const { return m_set.isStillValid(); } + bool hasBeenInvalidated() const { return m_set.hasBeenInvalidated(); } + void add(Watchpoint* watchpoint) { m_set.add(watchpoint); } + + void notifyWrite(VM& vm, JSValue value, const FireDetail& detail) + { + if (LIKELY(m_set.stateOnJSThread() == IsInvalidated)) + return; + notifyWriteSlow(vm, value, detail); + } + + void notifyWrite(VM& vm, JSValue value, const char* reason) + { + if (LIKELY(m_set.stateOnJSThread() == IsInvalidated)) + return; + notifyWriteSlow(vm, value, reason); + } + + void invalidate(const FireDetail& detail) + { + m_value.clear(); + m_set.invalidate(detail); + } + + static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags; + + // We could have used Weak<>. But we want arbitrary JSValues, not just cells. It's also somewhat + // convenient to have eager notification of death. + // + // Also note that this should be a private class, but it isn't because Windows. + class ValueCleanup : public UnconditionalFinalizer { + WTF_MAKE_FAST_ALLOCATED; + + public: + ValueCleanup(InferredValue*); + virtual ~ValueCleanup(); + + protected: + void finalizeUnconditionally() override; + + private: + InferredValue* m_owner; + }; + +private: + InferredValue(VM&); + ~InferredValue(); + + JS_EXPORT_PRIVATE void notifyWriteSlow(VM&, JSValue, const FireDetail&); + JS_EXPORT_PRIVATE void notifyWriteSlow(VM&, JSValue, const char* reason); + + friend class ValueCleanup; + + InlineWatchpointSet m_set; + WriteBarrier<Unknown> m_value; + std::unique_ptr<ValueCleanup> m_cleanup; +}; + +// FIXME: We could have an InlineInferredValue, which only allocates the InferredValue object when +// a notifyWrite() transitions us towards watching, and then clears the reference (allowing the object +// to die) when we get invalidated. + +} // namespace JSC + +#endif // InferredValue_h + diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp new file mode 100644 index 000000000..d74460f20 --- /dev/null +++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008, 2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "InitializeThreading.h" + +#include "ExecutableAllocator.h" +#include "Heap.h" +#include "HeapStatistics.h" +#include "Options.h" +#include "Identifier.h" +#include "JSDateMath.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "LLIntData.h" +#include "StructureIDTable.h" +#include "WriteBarrier.h" +#include <mutex> +#include <wtf/dtoa.h> +#include <wtf/Threading.h> +#include <wtf/dtoa/cached-powers.h> + +using namespace WTF; + +namespace JSC { + +void initializeThreading() +{ + static std::once_flag initializeThreadingOnceFlag; + + std::call_once(initializeThreadingOnceFlag, []{ + WTF::double_conversion::initialize(); + WTF::initializeThreading(); + Options::initialize(); + if (Options::recordGCPauseTimes()) + HeapStatistics::initialize(); +#if ENABLE(WRITE_BARRIER_PROFILING) + WriteBarrierCounters::initialize(); +#endif +#if ENABLE(ASSEMBLER) + ExecutableAllocator::initializeAllocator(); +#endif + LLInt::initialize(); +#ifndef NDEBUG + DisallowGC::initialize(); +#endif + WTFThreadData& threadData = wtfThreadData(); + threadData.setSavedLastStackTop(threadData.stack().origin()); + }); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.h b/Source/JavaScriptCore/runtime/InitializeThreading.h new file mode 100644 index 000000000..39845c243 --- /dev/null +++ b/Source/JavaScriptCore/runtime/InitializeThreading.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef InitializeThreading_h +#define InitializeThreading_h + +namespace JSC { + +// This function must be called from the main thread. It is safe to call it repeatedly. +// Darwin is an exception to this rule: it is OK to call this function from any thread, even reentrantly. +JS_EXPORT_PRIVATE void initializeThreading(); + +} + +#endif // InitializeThreading_h diff --git a/Source/JavaScriptCore/runtime/Int16Array.h b/Source/JavaScriptCore/runtime/Int16Array.h new file mode 100644 index 000000000..610c15d65 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Int16Array.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Int16Array_h +#define Int16Array_h + +#include "TypedArrays.h" + +using JSC::Int16Array; + +#endif // Int16Array_h + diff --git a/Source/JavaScriptCore/runtime/Int32Array.h b/Source/JavaScriptCore/runtime/Int32Array.h new file mode 100644 index 000000000..120061fbc --- /dev/null +++ b/Source/JavaScriptCore/runtime/Int32Array.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Int32Array_h +#define Int32Array_h + +#include "TypedArrays.h" + +using JSC::Int32Array; + +#endif // Int32Array_h + diff --git a/Source/JavaScriptCore/runtime/Int8Array.h b/Source/JavaScriptCore/runtime/Int8Array.h new file mode 100644 index 000000000..527a9a1db --- /dev/null +++ b/Source/JavaScriptCore/runtime/Int8Array.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Int8Array_h +#define Int8Array_h + +#include "TypedArrays.h" + +using JSC::Int8Array; + +#endif // Int8Array_h + diff --git a/Source/JavaScriptCore/runtime/IntegralTypedArrayBase.h b/Source/JavaScriptCore/runtime/IntegralTypedArrayBase.h new file mode 100644 index 000000000..466ff8cde --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntegralTypedArrayBase.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef IntegralTypedArrayBase_h +#define IntegralTypedArrayBase_h + +#include "TypedArrayBase.h" +#include <limits> +#include <wtf/MathExtras.h> + +// Base class for all WebGL<T>Array types holding integral +// (non-floating-point) values. + +namespace JSC { + +template <typename T> +class IntegralTypedArrayBase : public TypedArrayBase<T> { +public: + void set(unsigned index, double value) + { + if (index >= TypedArrayBase<T>::m_length) + return; + if (std::isnan(value)) // Clamp NaN to 0 + value = 0; + // The double cast is necessary to get the correct wrapping + // for out-of-range values with Int32Array and Uint32Array. + TypedArrayBase<T>::data()[index] = static_cast<T>(static_cast<int64_t>(value)); + } + +protected: + IntegralTypedArrayBase(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : TypedArrayBase<T>(buffer, byteOffset, length) + { + } + +}; + +} // namespace JSC + +#endif // IntegralTypedArrayBase_h diff --git a/Source/JavaScriptCore/runtime/InternalFunction.cpp b/Source/JavaScriptCore/runtime/InternalFunction.cpp new file mode 100644 index 000000000..6f53bc01f --- /dev/null +++ b/Source/JavaScriptCore/runtime/InternalFunction.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "InternalFunction.h" + +#include "FunctionPrototype.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(InternalFunction); + +const ClassInfo InternalFunction::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(InternalFunction) }; + +InternalFunction::InternalFunction(VM& vm, Structure* structure) + : JSDestructibleObject(vm, structure) +{ +} + +void InternalFunction::finishCreation(VM& vm, const String& name) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + ASSERT(methodTable()->getCallData != InternalFunction::info()->methodTable.getCallData); + putDirect(vm, vm.propertyNames->name, jsString(&vm, name), DontDelete | ReadOnly | DontEnum); +} + +const String& InternalFunction::name(ExecState* exec) +{ + return asString(getDirect(exec->vm(), exec->vm().propertyNames->name))->tryGetValue(); +} + +const String InternalFunction::displayName(ExecState* exec) +{ + JSValue displayName = getDirect(exec->vm(), exec->vm().propertyNames->displayName); + + if (displayName && isJSString(displayName)) + return asString(displayName)->tryGetValue(); + + return String(); +} + +CallType InternalFunction::getCallData(JSCell*, CallData&) +{ + RELEASE_ASSERT_NOT_REACHED(); + return CallTypeNone; +} + +const String InternalFunction::calculatedDisplayName(ExecState* exec) +{ + const String explicitName = displayName(exec); + + if (!explicitName.isEmpty()) + return explicitName; + + return name(exec); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/InternalFunction.h b/Source/JavaScriptCore/runtime/InternalFunction.h new file mode 100644 index 000000000..8b0d09f13 --- /dev/null +++ b/Source/JavaScriptCore/runtime/InternalFunction.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef InternalFunction_h +#define InternalFunction_h + +#include "Identifier.h" +#include "JSDestructibleObject.h" + +namespace JSC { + +class FunctionPrototype; + +class InternalFunction : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + static const unsigned StructureFlags = Base::StructureFlags | ImplementsHasInstance | TypeOfShouldCallGetCallData; + + DECLARE_EXPORT_INFO; + + JS_EXPORT_PRIVATE const String& name(ExecState*); + const String displayName(ExecState*); + const String calculatedDisplayName(ExecState*); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + JS_EXPORT_PRIVATE InternalFunction(VM&, Structure*); + + JS_EXPORT_PRIVATE void finishCreation(VM&, const String& name); + + static CallType getCallData(JSCell*, CallData&); +}; + +InternalFunction* asInternalFunction(JSValue); + +inline InternalFunction* asInternalFunction(JSValue value) +{ + ASSERT(asObject(value)->inherits(InternalFunction::info())); + return static_cast<InternalFunction*>(asObject(value)); +} + +} // namespace JSC + +#endif // InternalFunction_h diff --git a/Source/JavaScriptCore/runtime/IntlCollator.cpp b/Source/JavaScriptCore/runtime/IntlCollator.cpp new file mode 100644 index 000000000..98108fc18 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlCollator.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlCollator.h" + +#if ENABLE(INTL) + +#include "Error.h" +#include "IntlCollatorConstructor.h" +#include "IntlObject.h" +#include "JSBoundFunction.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo IntlCollator::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(IntlCollator) }; + +IntlCollator* IntlCollator::create(VM& vm, IntlCollatorConstructor* constructor) +{ + IntlCollator* format = new (NotNull, allocateCell<IntlCollator>(vm.heap)) IntlCollator(vm, constructor->collatorStructure()); + format->finishCreation(vm); + return format; +} + +Structure* IntlCollator::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlCollator::IntlCollator(VM& vm, Structure* structure) + : JSDestructibleObject(vm, structure) +{ +} + +void IntlCollator::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +void IntlCollator::destroy(JSCell* cell) +{ + static_cast<IntlCollator*>(cell)->IntlCollator::~IntlCollator(); +} + +void IntlCollator::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + IntlCollator* thisObject = jsCast<IntlCollator*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_boundCompare); +} + +void IntlCollator::setBoundCompare(VM& vm, JSBoundFunction* format) +{ + m_boundCompare.set(vm, this, format); +} + +EncodedJSValue JSC_HOST_CALL IntlCollatorFuncCompare(ExecState* exec) +{ + // 10.3.4 Collator Compare Functions (ECMA-402 2.0) + // 1. Let collator be the this value. + IntlCollator* collator = jsDynamicCast<IntlCollator*>(exec->thisValue()); + + // 2. Assert: Type(collator) is Object and collator has an [[initializedCollator]] internal slot whose value is true. + if (!collator) + return JSValue::encode(throwTypeError(exec)); + + // 3. If x is not provided, let x be undefined. + // 4. If y is not provided, let y be undefined. + // 5. Let X be ToString(x). + JSString* a = exec->argument(0).toString(exec); + // 6. ReturnIfAbrupt(X). + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 7. Let Y be ToString(y). + JSString* b = exec->argument(1).toString(exec); + // 8. ReturnIfAbrupt(Y). + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 9. Return CompareStrings(collator, X, Y). + + // 10.3.4 CompareStrings abstract operation (ECMA-402 2.0) + // FIXME: Implement CompareStrings. + + // Return simple check until properly implemented. + return JSValue::encode(jsNumber(codePointCompare(a->value(exec).impl(), b->value(exec).impl()))); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlCollator.h b/Source/JavaScriptCore/runtime/IntlCollator.h new file mode 100644 index 000000000..ff2251494 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlCollator.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlCollator_h +#define IntlCollator_h + +#if ENABLE(INTL) + +#include "JSDestructibleObject.h" + +namespace JSC { + +class IntlCollatorConstructor; +class JSBoundFunction; + +class IntlCollator : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static IntlCollator* create(VM&, IntlCollatorConstructor*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + JSBoundFunction* boundCompare() const { return m_boundCompare.get(); } + void setBoundCompare(VM&, JSBoundFunction*); + +protected: + IntlCollator(VM&, Structure*); + void finishCreation(VM&); + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + +private: + WriteBarrier<JSBoundFunction> m_boundCompare; +}; + +EncodedJSValue JSC_HOST_CALL IntlCollatorFuncCompare(ExecState*); + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlCollator_h diff --git a/Source/JavaScriptCore/runtime/IntlCollatorConstructor.cpp b/Source/JavaScriptCore/runtime/IntlCollatorConstructor.cpp new file mode 100644 index 000000000..d41dd17cd --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlCollatorConstructor.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlCollatorConstructor.h" + +#if ENABLE(INTL) + +#include "Error.h" +#include "IntlCollator.h" +#include "IntlCollatorPrototype.h" +#include "IntlObject.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "Lookup.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(IntlCollatorConstructor); + +static EncodedJSValue JSC_HOST_CALL IntlCollatorConstructorFuncSupportedLocalesOf(ExecState*); + +} + +#include "IntlCollatorConstructor.lut.h" + +namespace JSC { + +const ClassInfo IntlCollatorConstructor::s_info = { "Function", &InternalFunction::s_info, &collatorConstructorTable, CREATE_METHOD_TABLE(IntlCollatorConstructor) }; + +/* Source for IntlCollatorConstructor.lut.h +@begin collatorConstructorTable + supportedLocalesOf IntlCollatorConstructorFuncSupportedLocalesOf DontEnum|Function 1 +@end +*/ + +IntlCollatorConstructor* IntlCollatorConstructor::create(VM& vm, Structure* structure, IntlCollatorPrototype* collatorPrototype, Structure* collatorStructure) +{ + IntlCollatorConstructor* constructor = new (NotNull, allocateCell<IntlCollatorConstructor>(vm.heap)) IntlCollatorConstructor(vm, structure); + constructor->finishCreation(vm, collatorPrototype, collatorStructure); + return constructor; +} + +Structure* IntlCollatorConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlCollatorConstructor::IntlCollatorConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void IntlCollatorConstructor::finishCreation(VM& vm, IntlCollatorPrototype* collatorPrototype, Structure* collatorStructure) +{ + Base::finishCreation(vm, ASCIILiteral("Collator")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, collatorPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), ReadOnly | DontEnum | DontDelete); + m_collatorStructure.set(vm, this, collatorStructure); +} + +EncodedJSValue JSC_HOST_CALL constructIntlCollator(ExecState* exec) +{ + // 10.1.2 Intl.Collator ([locales [, options]]) (ECMA-402 2.0) + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + JSValue newTarget = exec->newTarget(); + if (!newTarget || newTarget.isUndefined()) + newTarget = exec->callee(); + + // 2. Let collator be OrdinaryCreateFromConstructor(newTarget, %CollatorPrototype%). + VM& vm = exec->vm(); + IntlCollator* collator = IntlCollator::create(vm, jsCast<IntlCollatorConstructor*>(exec->callee())); + if (collator && !jsDynamicCast<IntlCollatorConstructor*>(newTarget)) { + JSValue proto = asObject(newTarget)->getDirect(vm, vm.propertyNames->prototype); + asObject(collator)->setPrototypeWithCycleCheck(exec, proto); + } + + // 3. ReturnIfAbrupt(collator). + ASSERT(collator); + + // 4. Return InitializeCollator(collator, locales, options). + // FIXME: return JSValue::encode(InitializeCollator(collator, locales, options)); + + return JSValue::encode(collator); +} + +EncodedJSValue JSC_HOST_CALL callIntlCollator(ExecState* exec) +{ + // 10.1.2 Intl.Collator ([locales [, options]]) (ECMA-402 2.0) + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + // NewTarget is always undefined when called as a function. + + // 2. Let collator be OrdinaryCreateFromConstructor(newTarget, %CollatorPrototype%). + VM& vm = exec->vm(); + IntlCollator* collator = IntlCollator::create(vm, jsCast<IntlCollatorConstructor*>(exec->callee())); + + // 3. ReturnIfAbrupt(collator). + ASSERT(collator); + + // 4. Return InitializeCollator(collator, locales, options). + // FIXME: return JSValue::encode(InitializeCollator(collator, locales, options)); + + return JSValue::encode(collator); +} + +ConstructType IntlCollatorConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructIntlCollator; + return ConstructTypeHost; +} + +CallType IntlCollatorConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callIntlCollator; + return CallTypeHost; +} + +bool IntlCollatorConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, collatorConstructorTable, jsCast<IntlCollatorConstructor*>(object), propertyName, slot); +} + +EncodedJSValue JSC_HOST_CALL IntlCollatorConstructorFuncSupportedLocalesOf(ExecState* exec) +{ + // 10.2.2 Intl.Collator.supportedLocalesOf(locales [, options]) (ECMA-402 2.0) + + // 1. Let requestedLocales be CanonicalizeLocaleList(locales). + // FIXME: requested = CanonicalizeLocaleList(locales); + + // 2. ReturnIfAbrupt(requestedLocales). + // if (exec->hadException()) + // return JSValue::encode(jsUndefined()); + + // 3. Return SupportedLocales(%Collator%.[[availableLocales]], requestedLocales, options). + // FIXME: return JSValue::encode(SupportedLocales(available, requested, options)); + + // Return empty array until properly implemented. + VM& vm = exec->vm(); + JSGlobalObject* globalObject = exec->callee()->globalObject(); + JSArray* supportedLocales = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0); + if (!supportedLocales) + return JSValue::encode(throwOutOfMemoryError(exec)); + + return JSValue::encode(supportedLocales); +} + +void IntlCollatorConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + IntlCollatorConstructor* thisObject = jsCast<IntlCollatorConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_collatorStructure); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlCollatorConstructor.h b/Source/JavaScriptCore/runtime/IntlCollatorConstructor.h new file mode 100644 index 000000000..4aa98f2fa --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlCollatorConstructor.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlCollatorConstructor_h +#define IntlCollatorConstructor_h + +#if ENABLE(INTL) + +#include "InternalFunction.h" + +namespace JSC { + +class IntlCollator; +class IntlCollatorPrototype; + +class IntlCollatorConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static IntlCollatorConstructor* create(VM&, Structure*, IntlCollatorPrototype*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + Structure* collatorStructure() const { return m_collatorStructure.get(); } + +protected: + void finishCreation(VM&, IntlCollatorPrototype*, Structure*); + +private: + IntlCollatorConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<Structure> m_collatorStructure; +}; + +EncodedJSValue JSC_HOST_CALL constructIntlCollator(ExecState*); +EncodedJSValue JSC_HOST_CALL callIntlCollator(ExecState*); + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlCollatorConstructor_h diff --git a/Source/JavaScriptCore/runtime/IntlCollatorPrototype.cpp b/Source/JavaScriptCore/runtime/IntlCollatorPrototype.cpp new file mode 100644 index 000000000..d46ddbfc8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlCollatorPrototype.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlCollatorPrototype.h" + +#if ENABLE(INTL) + +#include "Error.h" +#include "IntlCollator.h" +#include "JSBoundFunction.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSObject.h" +#include "ObjectConstructor.h" +#include "StructureInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(IntlCollatorPrototype); + +static EncodedJSValue JSC_HOST_CALL IntlCollatorPrototypeGetterCompare(ExecState*); +static EncodedJSValue JSC_HOST_CALL IntlCollatorPrototypeFuncResolvedOptions(ExecState*); + +} + +#include "IntlCollatorPrototype.lut.h" + +namespace JSC { + +const ClassInfo IntlCollatorPrototype::s_info = { "Object", &IntlCollator::s_info, &collatorPrototypeTable, CREATE_METHOD_TABLE(IntlCollatorPrototype) }; + +/* Source for IntlCollatorPrototype.lut.h +@begin collatorPrototypeTable + compare IntlCollatorPrototypeGetterCompare DontEnum|Accessor + resolvedOptions IntlCollatorPrototypeFuncResolvedOptions DontEnum|Function 0 +@end +*/ + +IntlCollatorPrototype* IntlCollatorPrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + IntlCollatorPrototype* object = new (NotNull, allocateCell<IntlCollatorPrototype>(vm.heap)) IntlCollatorPrototype(vm, structure); + object->finishCreation(vm); + return object; +} + +Structure* IntlCollatorPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlCollatorPrototype::IntlCollatorPrototype(VM& vm, Structure* structure) + : IntlCollator(vm, structure) +{ +} + +void IntlCollatorPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +bool IntlCollatorPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, collatorPrototypeTable, jsCast<IntlCollatorPrototype*>(object), propertyName, slot); +} + +EncodedJSValue JSC_HOST_CALL IntlCollatorPrototypeGetterCompare(ExecState* exec) +{ + // 10.3.3 Intl.Collator.prototype.compare (ECMA-402 2.0) + // 1. Let collator be this Collator object. + IntlCollator* collator = jsDynamicCast<IntlCollator*>(exec->thisValue()); + if (!collator) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Intl.Collator.prototype.compare called on value that's not an object initialized as a Collator"))); + + JSBoundFunction* boundCompare = collator->boundCompare(); + // 2. If collator.[[boundCompare]] is undefined, + if (!boundCompare) { + VM& vm = exec->vm(); + JSGlobalObject* globalObject = collator->globalObject(); + // a. Let F be a new built-in function object as defined in 11.3.4. + // b. The value of F’s length property is 2. + JSFunction* targetObject = JSFunction::create(vm, globalObject, 2, ASCIILiteral("compare"), IntlCollatorFuncCompare, NoIntrinsic); + JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0); + if (!boundArgs) + return JSValue::encode(throwOutOfMemoryError(exec)); + + // c. Let bc be BoundFunctionCreate(F, «this value»). + boundCompare = JSBoundFunction::create(vm, globalObject, targetObject, collator, boundArgs, 2, ASCIILiteral("compare")); + // d. Set collator.[[boundCompare]] to bc. + collator->setBoundCompare(vm, boundCompare); + } + // 3. Return collator.[[boundCompare]]. + return JSValue::encode(boundCompare); +} + +EncodedJSValue JSC_HOST_CALL IntlCollatorPrototypeFuncResolvedOptions(ExecState* exec) +{ + // 10.3.5 Intl.Collator.prototype.resolvedOptions() (ECMA-402 2.0) + IntlCollator* collator = jsDynamicCast<IntlCollator*>(exec->thisValue()); + if (!collator) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Intl.Collator.prototype.resolvedOptions called on value that's not an object initialized as a Collator"))); + + // The function returns a new object whose properties and attributes are set as if constructed by an object literal assigning to each of the following properties the value of the corresponding internal slot of this Collator object (see 10.4): locale, usage, sensitivity, ignorePunctuation, collation, as well as those properties shown in Table 1 whose keys are included in the %Collator%[[relevantExtensionKeys]] internal slot of the standard built-in object that is the initial value of Intl.Collator. + + JSObject* options = constructEmptyObject(exec); + + // FIXME: Populate object from internal slots. + + return JSValue::encode(options); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlCollatorPrototype.h b/Source/JavaScriptCore/runtime/IntlCollatorPrototype.h new file mode 100644 index 000000000..f81642679 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlCollatorPrototype.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlCollatorPrototype_h +#define IntlCollatorPrototype_h + +#if ENABLE(INTL) + +#include "IntlCollator.h" +#include "JSObject.h" + +namespace JSC { + +class IntlCollatorPrototype : public IntlCollator { +public: + typedef IntlCollator Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static IntlCollatorPrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&); + +private: + IntlCollatorPrototype(VM&, Structure*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlCollatorPrototype_h diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp b/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp new file mode 100644 index 000000000..7839badc3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlDateTimeFormat.h" + +#if ENABLE(INTL) + +#include "DateConstructor.h" +#include "DateInstance.h" +#include "Error.h" +#include "IntlDateTimeFormatConstructor.h" +#include "IntlObject.h" +#include "JSBoundFunction.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo IntlDateTimeFormat::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(IntlDateTimeFormat) }; + +IntlDateTimeFormat* IntlDateTimeFormat::create(VM& vm, IntlDateTimeFormatConstructor* constructor) +{ + IntlDateTimeFormat* format = new (NotNull, allocateCell<IntlDateTimeFormat>(vm.heap)) IntlDateTimeFormat(vm, constructor->dateTimeFormatStructure()); + format->finishCreation(vm); + return format; +} + +Structure* IntlDateTimeFormat::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlDateTimeFormat::IntlDateTimeFormat(VM& vm, Structure* structure) + : JSDestructibleObject(vm, structure) +{ +} + +void IntlDateTimeFormat::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +void IntlDateTimeFormat::destroy(JSCell* cell) +{ + static_cast<IntlDateTimeFormat*>(cell)->IntlDateTimeFormat::~IntlDateTimeFormat(); +} + +void IntlDateTimeFormat::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + IntlDateTimeFormat* thisObject = jsCast<IntlDateTimeFormat*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_boundFormat); +} + +void IntlDateTimeFormat::setBoundFormat(VM& vm, JSBoundFunction* format) +{ + m_boundFormat.set(vm, this, format); +} + +EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatFuncFormatDateTime(ExecState* exec) +{ + // 12.3.4 DateTime Format Functions (ECMA-402 2.0) + // 1. Let dtf be the this value. + IntlDateTimeFormat* format = jsDynamicCast<IntlDateTimeFormat*>(exec->thisValue()); + // 2. Assert: Type(dtf) is Object and dtf has an [[initializedDateTimeFormat]] internal slot whose value is true. + if (!format) + return JSValue::encode(throwTypeError(exec)); + + JSValue date = exec->argument(0); + double value; + + // 3. If date is not provided or is undefined, then + if (date.isUndefined()) { + // a. Let x be %Date_now%(). + value = JSValue::decode(dateNow(exec)).toNumber(exec); + } else { + // 4. Else + // a. Let x be ToNumber(date). + value = date.toNumber(exec); + // b. ReturnIfAbrupt(x). + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + // 5. Return FormatDateTime(dtf, x). + + // 12.3.4 FormatDateTime abstract operation (ECMA-402 2.0) + + // 1. If x is not a finite Number, then throw a RangeError exception. + if (!std::isfinite(value)) + return JSValue::encode(throwRangeError(exec, ASCIILiteral("date value is not finite in DateTimeFormat.format()"))); + + // FIXME: implement 2 - 9 + + // Return new Date(value).toString() until properly implemented. + VM& vm = exec->vm(); + JSGlobalObject* globalObject = exec->callee()->globalObject(); + DateInstance* d = DateInstance::create(vm, globalObject->dateStructure(), value); + return JSValue::encode(JSValue(d).toString(exec)); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormat.h b/Source/JavaScriptCore/runtime/IntlDateTimeFormat.h new file mode 100644 index 000000000..7daed138f --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormat.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlDateTimeFormat_h +#define IntlDateTimeFormat_h + +#if ENABLE(INTL) + +#include "JSDestructibleObject.h" + +namespace JSC { + +class IntlDateTimeFormatConstructor; +class JSBoundFunction; + +class IntlDateTimeFormat : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static IntlDateTimeFormat* create(VM&, IntlDateTimeFormatConstructor*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + JSBoundFunction* boundFormat() const { return m_boundFormat.get(); } + void setBoundFormat(VM&, JSBoundFunction*); + +protected: + IntlDateTimeFormat(VM&, Structure*); + void finishCreation(VM&); + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<JSBoundFunction> m_boundFormat; +}; + +EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatFuncFormatDateTime(ExecState*); + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlDateTimeFormat_h diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormatConstructor.cpp b/Source/JavaScriptCore/runtime/IntlDateTimeFormatConstructor.cpp new file mode 100644 index 000000000..0b8d460d2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormatConstructor.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlDateTimeFormatConstructor.h" + +#if ENABLE(INTL) + +#include "Error.h" +#include "IntlDateTimeFormat.h" +#include "IntlDateTimeFormatPrototype.h" +#include "IntlObject.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "Lookup.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(IntlDateTimeFormatConstructor); + +static EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatConstructorFuncSupportedLocalesOf(ExecState*); + +} + +#include "IntlDateTimeFormatConstructor.lut.h" + +namespace JSC { + +const ClassInfo IntlDateTimeFormatConstructor::s_info = { "Function", &InternalFunction::s_info, &dateTimeFormatConstructorTable, CREATE_METHOD_TABLE(IntlDateTimeFormatConstructor) }; + +/* Source for IntlDateTimeFormatConstructor.lut.h +@begin dateTimeFormatConstructorTable + supportedLocalesOf IntlDateTimeFormatConstructorFuncSupportedLocalesOf DontEnum|Function 1 +@end +*/ + +IntlDateTimeFormatConstructor* IntlDateTimeFormatConstructor::create(VM& vm, Structure* structure, IntlDateTimeFormatPrototype* dateTimeFormatPrototype, Structure* dateTimeFormatStructure) +{ + IntlDateTimeFormatConstructor* constructor = new (NotNull, allocateCell<IntlDateTimeFormatConstructor>(vm.heap)) IntlDateTimeFormatConstructor(vm, structure); + constructor->finishCreation(vm, dateTimeFormatPrototype, dateTimeFormatStructure); + return constructor; +} + +Structure* IntlDateTimeFormatConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlDateTimeFormatConstructor::IntlDateTimeFormatConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void IntlDateTimeFormatConstructor::finishCreation(VM& vm, IntlDateTimeFormatPrototype* dateTimeFormatPrototype, Structure* dateTimeFormatStructure) +{ + Base::finishCreation(vm, ASCIILiteral("DateTimeFormat")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, dateTimeFormatPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), ReadOnly | DontEnum | DontDelete); + m_dateTimeFormatStructure.set(vm, this, dateTimeFormatStructure); +} + +EncodedJSValue JSC_HOST_CALL constructIntlDateTimeFormat(ExecState* exec) +{ + // 12.1.2 Intl.DateTimeFormat ([locales [, options]]) (ECMA-402 2.0) + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + JSValue newTarget = exec->newTarget(); + if (!newTarget || newTarget.isUndefined()) + newTarget = exec->callee(); + + // 2. Let dateTimeFormat be OrdinaryCreateFromConstructor(newTarget, %DateTimeFormatPrototype%). + VM& vm = exec->vm(); + IntlDateTimeFormat* dateTimeFormat = IntlDateTimeFormat::create(vm, jsCast<IntlDateTimeFormatConstructor*>(exec->callee())); + if (dateTimeFormat && !jsDynamicCast<IntlDateTimeFormatConstructor*>(newTarget)) { + JSValue proto = asObject(newTarget)->getDirect(vm, vm.propertyNames->prototype); + asObject(dateTimeFormat)->setPrototypeWithCycleCheck(exec, proto); + } + + // 3. ReturnIfAbrupt(dateTimeFormat). + ASSERT(dateTimeFormat); + + // 4. Return InitializeDateTimeFormat(dateTimeFormat, locales, options). + // FIXME: return JSValue::encode(InitializeDateTimeFormat(dateTimeFormat, locales, options)); + + return JSValue::encode(dateTimeFormat); +} + +EncodedJSValue JSC_HOST_CALL callIntlDateTimeFormat(ExecState* exec) +{ + // 12.1.2 Intl.DateTimeFormat ([locales [, options]]) (ECMA-402 2.0) + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + // NewTarget is always undefined when called as a function. + + // 2. Let dateTimeFormat be OrdinaryCreateFromConstructor(newTarget, %DateTimeFormatPrototype%). + VM& vm = exec->vm(); + IntlDateTimeFormat* dateTimeFormat = IntlDateTimeFormat::create(vm, jsCast<IntlDateTimeFormatConstructor*>(exec->callee())); + + // 3. ReturnIfAbrupt(dateTimeFormat). + ASSERT(dateTimeFormat); + + // 4. Return InitializeDateTimeFormat(dateTimeFormat, locales, options). + // FIXME: return JSValue::encode(InitializeDateTimeFormat(dateTimeFormat, locales, options)); + + return JSValue::encode(dateTimeFormat); +} + +ConstructType IntlDateTimeFormatConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructIntlDateTimeFormat; + return ConstructTypeHost; +} + +CallType IntlDateTimeFormatConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callIntlDateTimeFormat; + return CallTypeHost; +} + +bool IntlDateTimeFormatConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, dateTimeFormatConstructorTable, jsCast<IntlDateTimeFormatConstructor*>(object), propertyName, slot); +} + +EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatConstructorFuncSupportedLocalesOf(ExecState* exec) +{ + // 12.2.2 Intl.DateTimeFormat.supportedLocalesOf(locales [, options]) (ECMA-402 2.0) + + // 1. Let availableLocales be %DateTimeFormat%.[[availableLocales]]. + // FIXME: available = IntlDateTimeFormatConstructor::getAvailableLocales() + + // 2. Let requestedLocales be CanonicalizeLocaleList(locales). + // FIXME: requested = CanonicalizeLocaleList(locales) + + // 3. Return SupportedLocales(availableLocales, requestedLocales, options). + // FIXME: return JSValue::encode(SupportedLocales(available, requested, options)); + + // Return empty array until properly implemented. + VM& vm = exec->vm(); + JSGlobalObject* globalObject = exec->callee()->globalObject(); + JSArray* supportedLocales = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0); + if (!supportedLocales) + return JSValue::encode(throwOutOfMemoryError(exec)); + + return JSValue::encode(supportedLocales); +} + +void IntlDateTimeFormatConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + IntlDateTimeFormatConstructor* thisObject = jsCast<IntlDateTimeFormatConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_dateTimeFormatStructure); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormatConstructor.h b/Source/JavaScriptCore/runtime/IntlDateTimeFormatConstructor.h new file mode 100644 index 000000000..cf043a7e7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormatConstructor.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlDateTimeFormatConstructor_h +#define IntlDateTimeFormatConstructor_h + +#if ENABLE(INTL) + +#include "InternalFunction.h" + +namespace JSC { + +class IntlDateTimeFormat; +class IntlDateTimeFormatPrototype; + +class IntlDateTimeFormatConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static IntlDateTimeFormatConstructor* create(VM&, Structure*, IntlDateTimeFormatPrototype*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + Structure* dateTimeFormatStructure() const { return m_dateTimeFormatStructure.get(); } + +protected: + void finishCreation(VM&, IntlDateTimeFormatPrototype*, Structure*); + +private: + IntlDateTimeFormatConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<Structure> m_dateTimeFormatStructure; +}; + +EncodedJSValue JSC_HOST_CALL constructIntlDateTimeFormat(ExecState*); +EncodedJSValue JSC_HOST_CALL callIntlDateTimeFormat(ExecState*); + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlDateTimeFormatConstructor_h diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp b/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp new file mode 100644 index 000000000..079b9295c --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlDateTimeFormatPrototype.h" + +#if ENABLE(INTL) + +#include "Error.h" +#include "IntlDateTimeFormat.h" +#include "JSBoundFunction.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSObject.h" +#include "ObjectConstructor.h" +#include "StructureInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(IntlDateTimeFormatPrototype); + +static EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeGetterFormat(ExecState*); +static EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeFuncResolvedOptions(ExecState*); + +} + +#include "IntlDateTimeFormatPrototype.lut.h" + +namespace JSC { + +const ClassInfo IntlDateTimeFormatPrototype::s_info = { "Object", &IntlDateTimeFormat::s_info, &dateTimeFormatPrototypeTable, CREATE_METHOD_TABLE(IntlDateTimeFormatPrototype) }; + +/* Source for IntlDateTimeFormatPrototype.lut.h +@begin dateTimeFormatPrototypeTable + format IntlDateTimeFormatPrototypeGetterFormat DontEnum|Accessor + resolvedOptions IntlDateTimeFormatPrototypeFuncResolvedOptions DontEnum|Function 0 +@end +*/ + +IntlDateTimeFormatPrototype* IntlDateTimeFormatPrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + IntlDateTimeFormatPrototype* object = new (NotNull, allocateCell<IntlDateTimeFormatPrototype>(vm.heap)) IntlDateTimeFormatPrototype(vm, structure); + object->finishCreation(vm, structure); + return object; +} + +Structure* IntlDateTimeFormatPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlDateTimeFormatPrototype::IntlDateTimeFormatPrototype(VM& vm, Structure* structure) + : IntlDateTimeFormat(vm, structure) +{ +} + +void IntlDateTimeFormatPrototype::finishCreation(VM& vm, Structure*) +{ + Base::finishCreation(vm); +} + +bool IntlDateTimeFormatPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, dateTimeFormatPrototypeTable, jsCast<IntlDateTimeFormatPrototype*>(object), propertyName, slot); +} + +EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeGetterFormat(ExecState* exec) +{ + // 12.3.3 Intl.DateTimeFormat.prototype.format (ECMA-402 2.0) + // 1. Let dtf be this DateTimeFormat object. + IntlDateTimeFormat* dtf = jsDynamicCast<IntlDateTimeFormat*>(exec->thisValue()); + // 2. ReturnIfAbrupt(dtf). + if (!dtf) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Intl.DateTimeFormat.prototype.format called on value that's not an object initialized as a DateTimeFormat"))); + + JSBoundFunction* boundFormat = dtf->boundFormat(); + // 3. If the [[boundFormat]] internal slot of this DateTimeFormat object is undefined, + if (!boundFormat) { + VM& vm = exec->vm(); + JSGlobalObject* globalObject = dtf->globalObject(); + // a. Let F be a new built-in function object as defined in 12.3.4. + // b. The value of F’s length property is 1. (Note: F’s length property was 0 in ECMA-402 1.0) + JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, ASCIILiteral("format"), IntlDateTimeFormatFuncFormatDateTime, NoIntrinsic); + JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0); + if (!boundArgs) + return JSValue::encode(throwOutOfMemoryError(exec)); + + // c. Let bf be BoundFunctionCreate(F, «this value»). + boundFormat = JSBoundFunction::create(vm, globalObject, targetObject, dtf, boundArgs, 1, ASCIILiteral("format")); + // d. Set dtf.[[boundFormat]] to bf. + dtf->setBoundFormat(vm, boundFormat); + } + // 4. Return dtf.[[boundFormat]]. + return JSValue::encode(boundFormat); +} + +EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeFuncResolvedOptions(ExecState* exec) +{ + // 12.3.5 Intl.DateTimeFormat.prototype.resolvedOptions() (ECMA-402 2.0) + IntlDateTimeFormat* dtf = jsDynamicCast<IntlDateTimeFormat*>(exec->thisValue()); + if (!dtf) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Intl.DateTimeFormat.prototype.resolvedOptions called on value that's not an object initialized as a DateTimeFormat"))); + + // The function returns a new object whose properties and attributes are set as if constructed by an object literal assigning to each of the following properties the value of the corresponding internal slot of this DateTimeFormat object (see 12.4): locale, calendar, numberingSystem, timeZone, hour12, weekday, era, year, month, day, hour, minute, second, and timeZoneName. Properties whose corresponding internal slots are not present are not assigned. + // Note: In this version of the ECMAScript 2015 Internationalization API, the timeZone property will be the name of the default time zone if no timeZone property was provided in the options object provided to the Intl.DateTimeFormat constructor. The previous version left the timeZone property undefined in this case. + + JSObject* options = constructEmptyObject(exec); + + // FIXME: Populate object from internal slots. + + return JSValue::encode(options); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.h b/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.h new file mode 100644 index 000000000..a2fd5e34e --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlDateTimeFormatPrototype_h +#define IntlDateTimeFormatPrototype_h + +#if ENABLE(INTL) + +#include "IntlDateTimeFormat.h" +#include "JSObject.h" + +namespace JSC { + +class IntlDateTimeFormatPrototype : public IntlDateTimeFormat { +public: + typedef IntlDateTimeFormat Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static IntlDateTimeFormatPrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, Structure*); + +private: + IntlDateTimeFormatPrototype(VM&, Structure*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlDateTimeFormatPrototype_h diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp b/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp new file mode 100644 index 000000000..139dc5b19 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlNumberFormat.h" + +#if ENABLE(INTL) + +#include "Error.h" +#include "IntlNumberFormatConstructor.h" +#include "IntlObject.h" +#include "JSBoundFunction.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo IntlNumberFormat::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(IntlNumberFormat) }; + +IntlNumberFormat* IntlNumberFormat::create(VM& vm, IntlNumberFormatConstructor* constructor) +{ + IntlNumberFormat* format = new (NotNull, allocateCell<IntlNumberFormat>(vm.heap)) IntlNumberFormat(vm, constructor->numberFormatStructure()); + format->finishCreation(vm); + return format; +} + +Structure* IntlNumberFormat::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlNumberFormat::IntlNumberFormat(VM& vm, Structure* structure) + : JSDestructibleObject(vm, structure) +{ +} + +void IntlNumberFormat::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +void IntlNumberFormat::destroy(JSCell* cell) +{ + static_cast<IntlNumberFormat*>(cell)->IntlNumberFormat::~IntlNumberFormat(); +} + +void IntlNumberFormat::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + IntlNumberFormat* thisObject = jsCast<IntlNumberFormat*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_boundFormat); +} + +void IntlNumberFormat::setBoundFormat(VM& vm, JSBoundFunction* format) +{ + m_boundFormat.set(vm, this, format); +} + +EncodedJSValue JSC_HOST_CALL IntlNumberFormatFuncFormatNumber(ExecState* exec) +{ + // 11.3.4 Format Number Functions (ECMA-402 2.0) + // 1. Let nf be the this value. + IntlNumberFormat* format = jsDynamicCast<IntlNumberFormat*>(exec->thisValue()); + // 2. Assert: Type(nf) is Object and nf has an [[initializedNumberFormat]] internal slot whose value is true. + if (!format) + return JSValue::encode(throwTypeError(exec)); + + // 3. If value is not provided, let value be undefined. + // 4. Let x be ToNumber(value). + double value = exec->argument(0).toNumber(exec); + // 5. ReturnIfAbrupt(x). + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 6. Return FormatNumber(nf, x). + + // 11.3.4 FormatNumber abstract operation (ECMA-402 2.0) + // FIXME: Implement FormatNumber. + + return JSValue::encode(jsNumber(value).toString(exec)); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormat.h b/Source/JavaScriptCore/runtime/IntlNumberFormat.h new file mode 100644 index 000000000..cc2538f9b --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlNumberFormat.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlNumberFormat_h +#define IntlNumberFormat_h + +#if ENABLE(INTL) + +#include "JSDestructibleObject.h" + +namespace JSC { + +class IntlNumberFormatConstructor; +class JSBoundFunction; + +class IntlNumberFormat : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static IntlNumberFormat* create(VM&, IntlNumberFormatConstructor*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + JSBoundFunction* boundFormat() const { return m_boundFormat.get(); } + void setBoundFormat(VM&, JSBoundFunction*); + +protected: + IntlNumberFormat(VM&, Structure*); + void finishCreation(VM&); + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<JSBoundFunction> m_boundFormat; +}; + +EncodedJSValue JSC_HOST_CALL IntlNumberFormatFuncFormatNumber(ExecState*); + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlNumberFormat_h diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormatConstructor.cpp b/Source/JavaScriptCore/runtime/IntlNumberFormatConstructor.cpp new file mode 100644 index 000000000..b8639d6b1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlNumberFormatConstructor.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlNumberFormatConstructor.h" + +#if ENABLE(INTL) + +#include "Error.h" +#include "IntlNumberFormat.h" +#include "IntlNumberFormatPrototype.h" +#include "IntlObject.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "Lookup.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(IntlNumberFormatConstructor); + +static EncodedJSValue JSC_HOST_CALL IntlNumberFormatConstructorFuncSupportedLocalesOf(ExecState*); + +} + +#include "IntlNumberFormatConstructor.lut.h" + +namespace JSC { + +const ClassInfo IntlNumberFormatConstructor::s_info = { "Function", &Base::s_info, &numberFormatConstructorTable, CREATE_METHOD_TABLE(IntlNumberFormatConstructor) }; + +/* Source for IntlNumberFormatConstructor.lut.h +@begin numberFormatConstructorTable + supportedLocalesOf IntlNumberFormatConstructorFuncSupportedLocalesOf DontEnum|Function 1 +@end +*/ + +IntlNumberFormatConstructor* IntlNumberFormatConstructor::create(VM& vm, Structure* structure, IntlNumberFormatPrototype* numberFormatPrototype, Structure* numberFormatStructure) +{ + IntlNumberFormatConstructor* constructor = new (NotNull, allocateCell<IntlNumberFormatConstructor>(vm.heap)) IntlNumberFormatConstructor(vm, structure); + constructor->finishCreation(vm, numberFormatPrototype, numberFormatStructure); + return constructor; +} + +Structure* IntlNumberFormatConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlNumberFormatConstructor::IntlNumberFormatConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void IntlNumberFormatConstructor::finishCreation(VM& vm, IntlNumberFormatPrototype* numberFormatPrototype, Structure* numberFormatStructure) +{ + Base::finishCreation(vm, ASCIILiteral("NumberFormat")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, numberFormatPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), ReadOnly | DontEnum | DontDelete); + m_numberFormatStructure.set(vm, this, numberFormatStructure); +} + +EncodedJSValue JSC_HOST_CALL constructIntlNumberFormat(ExecState* exec) +{ + // 11.1.2 Intl.NumberFormat ([locales [, options]]) (ECMA-402 2.0) + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + JSValue newTarget = exec->newTarget(); + if (!newTarget || newTarget.isUndefined()) + newTarget = exec->callee(); + + // 2. Let numberFormat be OrdinaryCreateFromConstructor(newTarget, %NumberFormatPrototype%). + VM& vm = exec->vm(); + IntlNumberFormat* numberFormat = IntlNumberFormat::create(vm, jsCast<IntlNumberFormatConstructor*>(exec->callee())); + if (numberFormat && !jsDynamicCast<IntlNumberFormatConstructor*>(newTarget)) { + JSValue proto = asObject(newTarget)->getDirect(vm, vm.propertyNames->prototype); + asObject(numberFormat)->setPrototypeWithCycleCheck(exec, proto); + } + + // 3. ReturnIfAbrupt(numberFormat). + ASSERT(numberFormat); + + // 4. Return InitializeNumberFormat(numberFormat, locales, options). + // FIXME: return JSValue::encode(InitializeNumberFormat(numberFormat, locales, options)); + + return JSValue::encode(numberFormat); +} + +EncodedJSValue JSC_HOST_CALL callIntlNumberFormat(ExecState* exec) +{ + // 11.1.2 Intl.NumberFormat ([locales [, options]]) (ECMA-402 2.0) + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + // NewTarget is always undefined when called as a function. + + // 2. Let numberFormat be OrdinaryCreateFromConstructor(newTarget, %NumberFormatPrototype%). + VM& vm = exec->vm(); + IntlNumberFormat* numberFormat = IntlNumberFormat::create(vm, jsCast<IntlNumberFormatConstructor*>(exec->callee())); + + // 3. ReturnIfAbrupt(numberFormat). + ASSERT(numberFormat); + + // 4. Return InitializeNumberFormat(numberFormat, locales, options). + // FIXME: return JSValue::encode(InitializeNumberFormat(numberFormat, locales, options)); + + return JSValue::encode(numberFormat); +} + +ConstructType IntlNumberFormatConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructIntlNumberFormat; + return ConstructTypeHost; +} + +CallType IntlNumberFormatConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callIntlNumberFormat; + return CallTypeHost; +} + +bool IntlNumberFormatConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, numberFormatConstructorTable, jsCast<IntlNumberFormatConstructor*>(object), propertyName, slot); +} + +EncodedJSValue JSC_HOST_CALL IntlNumberFormatConstructorFuncSupportedLocalesOf(ExecState* exec) +{ + // 11.2.2 Intl.NumberFormat.supportedLocalesOf(locales [, options]) (ECMA-402 2.0) + + // 1. Let availableLocales be %NumberFormat%.[[availableLocales]]. + // FIXME: available = IntlNumberFormatConstructor::getAvailableLocales() + + // 2. Let requestedLocales be CanonicalizeLocaleList(locales). + // FIXME: requested = CanonicalizeLocaleList(locales) + + // 3. Return SupportedLocales(availableLocales, requestedLocales, options). + // FIXME: return JSValue::encode(SupportedLocales(available, requested, options)); + + // Return empty array until properly implemented. + VM& vm = exec->vm(); + JSGlobalObject* globalObject = exec->callee()->globalObject(); + JSArray* supportedLocales = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0); + if (!supportedLocales) + return JSValue::encode(throwOutOfMemoryError(exec)); + + return JSValue::encode(supportedLocales); +} + +void IntlNumberFormatConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + IntlNumberFormatConstructor* thisObject = jsCast<IntlNumberFormatConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_numberFormatStructure); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormatConstructor.h b/Source/JavaScriptCore/runtime/IntlNumberFormatConstructor.h new file mode 100644 index 000000000..bb75f1021 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlNumberFormatConstructor.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlNumberFormatConstructor_h +#define IntlNumberFormatConstructor_h + +#if ENABLE(INTL) + +#include "InternalFunction.h" + +namespace JSC { + +class IntlNumberFormat; +class IntlNumberFormatPrototype; + +class IntlNumberFormatConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static IntlNumberFormatConstructor* create(VM&, Structure*, IntlNumberFormatPrototype*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + Structure* numberFormatStructure() const { return m_numberFormatStructure.get(); } + +protected: + void finishCreation(VM&, IntlNumberFormatPrototype*, Structure*); + +private: + IntlNumberFormatConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<Structure> m_numberFormatStructure; +}; + +EncodedJSValue JSC_HOST_CALL constructIntlNumberFormat(ExecState*); +EncodedJSValue JSC_HOST_CALL callIntlNumberFormat(ExecState*); + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlNumberFormatConstructor_h diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp b/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp new file mode 100644 index 000000000..8a3492294 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlNumberFormatPrototype.h" + +#if ENABLE(INTL) + +#include "Error.h" +#include "IntlNumberFormat.h" +#include "JSBoundFunction.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSObject.h" +#include "ObjectConstructor.h" +#include "StructureInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(IntlNumberFormatPrototype); + +static EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeGetterFormat(ExecState*); +static EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeFuncResolvedOptions(ExecState*); + +} + +#include "IntlNumberFormatPrototype.lut.h" + +namespace JSC { + +const ClassInfo IntlNumberFormatPrototype::s_info = { "Object", &IntlNumberFormat::s_info, &numberFormatPrototypeTable, CREATE_METHOD_TABLE(IntlNumberFormatPrototype) }; + +/* Source for IntlNumberFormatPrototype.lut.h +@begin numberFormatPrototypeTable + format IntlNumberFormatPrototypeGetterFormat DontEnum|Accessor + resolvedOptions IntlNumberFormatPrototypeFuncResolvedOptions DontEnum|Function 0 +@end +*/ + +IntlNumberFormatPrototype* IntlNumberFormatPrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + IntlNumberFormatPrototype* object = new (NotNull, allocateCell<IntlNumberFormatPrototype>(vm.heap)) IntlNumberFormatPrototype(vm, structure); + object->finishCreation(vm, structure); + return object; +} + +Structure* IntlNumberFormatPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +IntlNumberFormatPrototype::IntlNumberFormatPrototype(VM& vm, Structure* structure) + : IntlNumberFormat(vm, structure) +{ +} + +void IntlNumberFormatPrototype::finishCreation(VM& vm, Structure*) +{ + Base::finishCreation(vm); +} + +bool IntlNumberFormatPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, numberFormatPrototypeTable, jsCast<IntlNumberFormatPrototype*>(object), propertyName, slot); +} + +EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeGetterFormat(ExecState* exec) +{ + // 11.3.3 Intl.NumberFormat.prototype.format (ECMA-402 2.0) + // 1. Let nf be this NumberFormat object. + IntlNumberFormat* nf = jsDynamicCast<IntlNumberFormat*>(exec->thisValue()); + if (!nf) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Intl.NumberFormat.prototype.format called on value that's not an object initialized as a NumberFormat"))); + + JSBoundFunction* boundFormat = nf->boundFormat(); + // 2. If nf.[[boundFormat]] is undefined, + if (!boundFormat) { + VM& vm = exec->vm(); + JSGlobalObject* globalObject = nf->globalObject(); + // a. Let F be a new built-in function object as defined in 11.3.4. + // b. The value of F’s length property is 1. + JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, ASCIILiteral("format"), IntlNumberFormatFuncFormatNumber, NoIntrinsic); + JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0); + if (!boundArgs) + return JSValue::encode(throwOutOfMemoryError(exec)); + + // c. Let bf be BoundFunctionCreate(F, «this value»). + boundFormat = JSBoundFunction::create(vm, globalObject, targetObject, nf, boundArgs, 1, ASCIILiteral("format")); + // d. Set nf.[[boundFormat]] to bf. + nf->setBoundFormat(vm, boundFormat); + } + // 3. Return nf.[[boundFormat]]. + return JSValue::encode(boundFormat); +} + +EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeFuncResolvedOptions(ExecState* exec) +{ + // 11.3.5 Intl.NumberFormat.prototype.resolvedOptions() (ECMA-402 2.0) + IntlNumberFormat* nf = jsDynamicCast<IntlNumberFormat*>(exec->thisValue()); + if (!nf) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Intl.NumberFormat.prototype.resolvedOptions called on value that's not an object initialized as a NumberFormat"))); + + // The function returns a new object whose properties and attributes are set as if constructed by an object literal assigning to each of the following properties the value of the corresponding internal slot of this NumberFormat object (see 11.4): locale, numberingSystem, style, currency, currencyDisplay, minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, minimumSignificantDigits, maximumSignificantDigits, and useGrouping. Properties whose corresponding internal slots are not present are not assigned. + + JSObject* options = constructEmptyObject(exec); + + // FIXME: Populate object from internal slots. + + return JSValue::encode(options); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.h b/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.h new file mode 100644 index 000000000..78bc051fd --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlNumberFormatPrototype_h +#define IntlNumberFormatPrototype_h + +#if ENABLE(INTL) + +#include "IntlNumberFormat.h" +#include "JSObject.h" + +namespace JSC { + +class IntlNumberFormatPrototype : public IntlNumberFormat { +public: + typedef IntlNumberFormat Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static IntlNumberFormatPrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, Structure*); + +private: + IntlNumberFormatPrototype(VM&, Structure*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlNumberFormatPrototype_h diff --git a/Source/JavaScriptCore/runtime/IntlObject.cpp b/Source/JavaScriptCore/runtime/IntlObject.cpp new file mode 100644 index 000000000..ba457e725 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlObject.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "IntlObject.h" + +#if ENABLE(INTL) + +#include "FunctionPrototype.h" +#include "IntlCollator.h" +#include "IntlCollatorConstructor.h" +#include "IntlCollatorPrototype.h" +#include "IntlDateTimeFormat.h" +#include "IntlDateTimeFormatConstructor.h" +#include "IntlDateTimeFormatPrototype.h" +#include "IntlNumberFormat.h" +#include "IntlNumberFormatConstructor.h" +#include "IntlNumberFormatPrototype.h" +#include "JSCInlines.h" +#include "Lookup.h" +#include "ObjectPrototype.h" +#include <wtf/Assertions.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(IntlObject); + +} + +namespace JSC { + +const ClassInfo IntlObject::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(IntlObject) }; + +IntlObject::IntlObject(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} + +IntlObject* IntlObject::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + IntlObject* object = new (NotNull, allocateCell<IntlObject>(vm.heap)) IntlObject(vm, structure); + object->finishCreation(vm, globalObject); + return object; +} + +void IntlObject::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + + // Set up Collator. + IntlCollatorPrototype* collatorPrototype = IntlCollatorPrototype::create(vm, globalObject, IntlCollatorPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); + Structure* collatorStructure = IntlCollator::createStructure(vm, globalObject, collatorPrototype); + IntlCollatorConstructor* collatorConstructor = IntlCollatorConstructor::create(vm, IntlCollatorConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), collatorPrototype, collatorStructure); + + collatorPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, collatorConstructor, DontEnum); + + // Set up NumberFormat. + IntlNumberFormatPrototype* numberFormatPrototype = IntlNumberFormatPrototype::create(vm, globalObject, IntlNumberFormatPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); + Structure* numberFormatStructure = IntlNumberFormat::createStructure(vm, globalObject, numberFormatPrototype); + IntlNumberFormatConstructor* numberFormatConstructor = IntlNumberFormatConstructor::create(vm, IntlNumberFormatConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), numberFormatPrototype, numberFormatStructure); + + numberFormatPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, numberFormatConstructor, DontEnum); + + // Set up DateTimeFormat. + IntlDateTimeFormatPrototype* dateTimeFormatPrototype = IntlDateTimeFormatPrototype::create(vm, globalObject, IntlDateTimeFormatPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); + Structure* dateTimeFormatStructure = IntlDateTimeFormat::createStructure(vm, globalObject, dateTimeFormatPrototype); + IntlDateTimeFormatConstructor* dateTimeFormatConstructor = IntlDateTimeFormatConstructor::create(vm, IntlDateTimeFormatConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), dateTimeFormatPrototype, dateTimeFormatStructure); + + dateTimeFormatPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, dateTimeFormatConstructor, DontEnum); + + // 8.1 Properties of the Intl Object (ECMA-402 2.0) + putDirectWithoutTransition(vm, vm.propertyNames->Collator, collatorConstructor, DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->NumberFormat, numberFormatConstructor, DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->DateTimeFormat, dateTimeFormatConstructor, DontEnum); +} + +Structure* IntlObject::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +} // namespace JSC + +#endif // ENABLE(INTL) diff --git a/Source/JavaScriptCore/runtime/IntlObject.h b/Source/JavaScriptCore/runtime/IntlObject.h new file mode 100644 index 000000000..83428542b --- /dev/null +++ b/Source/JavaScriptCore/runtime/IntlObject.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 Andy VanWagoner (thetalecrafter@gmail.com) + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef IntlObject_h +#define IntlObject_h + +#if ENABLE(INTL) + +#include "JSObject.h" + +namespace JSC { + +class IntlCollatorConstructor; +class IntlCollatorPrototype; +class IntlDateTimeFormatConstructor; +class IntlDateTimeFormatPrototype; +class IntlNumberFormatConstructor; +class IntlNumberFormatPrototype; + +class IntlObject : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static IntlObject* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, JSGlobalObject*); + +private: + IntlObject(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(INTL) + +#endif // IntlObject_h diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h new file mode 100644 index 000000000..fe552a781 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Intrinsic.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Intrinsic_h +#define Intrinsic_h + +namespace JSC { + +enum Intrinsic { + NoIntrinsic, + AbsIntrinsic, + MinIntrinsic, + MaxIntrinsic, + SqrtIntrinsic, + SinIntrinsic, + Clz32Intrinsic, + CosIntrinsic, + ArrayPushIntrinsic, + ArrayPopIntrinsic, + CharCodeAtIntrinsic, + CharAtIntrinsic, + FromCharCodeIntrinsic, + PowIntrinsic, + FloorIntrinsic, + CeilIntrinsic, + RoundIntrinsic, + ExpIntrinsic, + LogIntrinsic, + RegExpExecIntrinsic, + RegExpTestIntrinsic, + StringPrototypeValueOfIntrinsic, + IMulIntrinsic, + FRoundIntrinsic, + + // Debugging intrinsics. These are meant to be used as testing hacks within + // jsc.cpp and should never be exposed to users. + DFGTrueIntrinsic, + OSRExitIntrinsic, + IsFinalTierIntrinsic, + SetInt32HeapPredictionIntrinsic, + CheckInt32Intrinsic, + FiatInt52Intrinsic, +}; + +} // namespace JSC + +#endif // Intrinsic_h diff --git a/Source/JavaScriptCore/runtime/IterationStatus.h b/Source/JavaScriptCore/runtime/IterationStatus.h new file mode 100644 index 000000000..fb8b5fb75 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IterationStatus.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef IterationStatus_h +#define IterationStatus_h + +namespace JSC { + +enum class IterationStatus { + Continue, + Done +}; + +} // namespace JSC + +#endif // IterationStatus_h diff --git a/Source/JavaScriptCore/runtime/IteratorOperations.cpp b/Source/JavaScriptCore/runtime/IteratorOperations.cpp new file mode 100644 index 000000000..2e886bf96 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IteratorOperations.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "IteratorOperations.h" + +#include "Error.h" +#include "JSCInlines.h" +#include "ObjectConstructor.h" + +using namespace WTF; + +namespace JSC { + +JSValue iteratorNext(ExecState* exec, JSValue iterator, JSValue value) +{ + JSValue nextFunction = iterator.get(exec, exec->vm().propertyNames->next); + if (exec->hadException()) + return jsUndefined(); + + CallData nextFunctionCallData; + CallType nextFunctionCallType = getCallData(nextFunction, nextFunctionCallData); + if (nextFunctionCallType == CallTypeNone) + return throwTypeError(exec); + + MarkedArgumentBuffer nextFunctionArguments; + if (!value.isEmpty()) + nextFunctionArguments.append(value); + JSValue result = call(exec, nextFunction, nextFunctionCallType, nextFunctionCallData, iterator, nextFunctionArguments); + if (exec->hadException()) + return jsUndefined(); + + if (!result.isObject()) + return throwTypeError(exec, ASCIILiteral("Iterator result interface is not an object.")); + + return result; +} + +JSValue iteratorNext(ExecState* exec, JSValue iterator) +{ + return iteratorNext(exec, iterator, JSValue()); +} + +JSValue iteratorValue(ExecState* exec, JSValue iterResult) +{ + return iterResult.get(exec, exec->vm().propertyNames->value); +} + +bool iteratorComplete(ExecState* exec, JSValue iterResult) +{ + JSValue done = iterResult.get(exec, exec->vm().propertyNames->done); + return done.toBoolean(exec); +} + +JSValue iteratorStep(ExecState* exec, JSValue iterator) +{ + JSValue result = iteratorNext(exec, iterator); + if (exec->hadException()) + return jsUndefined(); + bool done = iteratorComplete(exec, result); + if (exec->hadException()) + return jsUndefined(); + if (done) + return jsBoolean(false); + return result; +} + +void iteratorClose(ExecState* exec, JSValue iterator) +{ + Exception* exception = nullptr; + if (exec->hadException()) { + exception = exec->exception(); + exec->clearException(); + } + JSValue returnFunction = iterator.get(exec, exec->vm().propertyNames->returnKeyword); + if (exec->hadException()) + return; + + if (returnFunction.isUndefined()) { + if (exception) + exec->vm().throwException(exec, exception); + return; + } + + CallData returnFunctionCallData; + CallType returnFunctionCallType = getCallData(returnFunction, returnFunctionCallData); + if (returnFunctionCallType == CallTypeNone) { + if (exception) + exec->vm().throwException(exec, exception); + else + throwTypeError(exec); + return; + } + + MarkedArgumentBuffer returnFunctionArguments; + JSValue innerResult = call(exec, returnFunction, returnFunctionCallType, returnFunctionCallData, iterator, returnFunctionArguments); + + if (exception) { + exec->vm().throwException(exec, exception); + return; + } + + if (exec->hadException()) + return; + + if (!innerResult.isObject()) { + throwTypeError(exec, ASCIILiteral("Iterator result interface is not an object.")); + return; + } +} + +JSObject* createIteratorResultObject(ExecState* exec, JSValue value, bool done) +{ + JSObject* resultObject = constructEmptyObject(exec); + resultObject->putDirect(exec->vm(), exec->propertyNames().done, jsBoolean(done)); + resultObject->putDirect(exec->vm(), exec->propertyNames().value, value); + return resultObject; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/IteratorOperations.h b/Source/JavaScriptCore/runtime/IteratorOperations.h new file mode 100644 index 000000000..a9a300c94 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IteratorOperations.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef IteratorOperations_h +#define IteratorOperations_h + +#include "JSCJSValue.h" +#include "JSObject.h" + +namespace JSC { + +JSValue iteratorNext(ExecState*, JSValue iterator, JSValue); +JSValue iteratorNext(ExecState*, JSValue iterator); +JSValue iteratorValue(ExecState*, JSValue iterator); +bool iteratorComplete(ExecState*, JSValue iterator); +JSValue iteratorStep(ExecState*, JSValue iterator); +void iteratorClose(ExecState*, JSValue iterator); +JS_EXPORT_PRIVATE JSObject* createIteratorResultObject(ExecState*, JSValue, bool done); + +} + +#endif // !defined(IteratorOperations_h) diff --git a/Source/JavaScriptCore/runtime/IteratorPrototype.cpp b/Source/JavaScriptCore/runtime/IteratorPrototype.cpp new file mode 100644 index 000000000..3e4ec9303 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IteratorPrototype.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "IteratorPrototype.h" + +#include "JSCBuiltins.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "ObjectConstructor.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo IteratorPrototype::s_info = { "Iterator", &Base::s_info, nullptr, CREATE_METHOD_TABLE(IteratorPrototype) }; + +void IteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + JSFunction* iteratorPrototypeFunction = JSFunction::createBuiltinFunction(vm, iteratorPrototypeSymbolIteratorCodeGenerator(vm), globalObject, "[Symbol.iterator]"); + putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, iteratorPrototypeFunction, DontEnum); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/IteratorPrototype.h b/Source/JavaScriptCore/runtime/IteratorPrototype.h new file mode 100644 index 000000000..c15971dab --- /dev/null +++ b/Source/JavaScriptCore/runtime/IteratorPrototype.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef IteratorPrototype_h +#define IteratorPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class IteratorPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags; + + static IteratorPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + IteratorPrototype* prototype = new (NotNull, allocateCell<IteratorPrototype>(vm.heap)) IteratorPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + IteratorPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); +}; + +} + +#endif // !defined(IteratorPrototype_h) diff --git a/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp new file mode 100644 index 000000000..6e5b53618 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "JSAPIValueWrapper.h" + +#include "NumberObject.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSAPIValueWrapper); + +const ClassInfo JSAPIValueWrapper::s_info = { "API Wrapper", 0, 0, CREATE_METHOD_TABLE(JSAPIValueWrapper) }; + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSAPIValueWrapper.h b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.h new file mode 100644 index 000000000..66e61c70d --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSAPIValueWrapper.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSAPIValueWrapper_h +#define JSAPIValueWrapper_h + +#include "JSCJSValue.h" +#include "JSCell.h" +#include "CallFrame.h" +#include "Structure.h" + +namespace JSC { + +class JSAPIValueWrapper : public JSCell { + friend JSValue jsAPIValueWrapper(ExecState*, JSValue); +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + JSValue value() const { return m_value.get(); } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(APIValueWrapperType, OverridesGetPropertyNames), info()); + } + + DECLARE_EXPORT_INFO; + + static JSAPIValueWrapper* create(ExecState* exec, JSValue value) + { + VM& vm = exec->vm(); + JSAPIValueWrapper* wrapper = new (NotNull, allocateCell<JSAPIValueWrapper>(vm.heap)) JSAPIValueWrapper(exec); + wrapper->finishCreation(vm, value); + return wrapper; + } + +protected: + void finishCreation(VM& vm, JSValue value) + { + Base::finishCreation(vm); + m_value.set(vm, this, value); + ASSERT(!value.isCell()); + } + +private: + JSAPIValueWrapper(ExecState* exec) + : JSCell(exec->vm(), exec->vm().apiWrapperStructure.get()) + { + } + + WriteBarrier<Unknown> m_value; +}; + +inline JSValue jsAPIValueWrapper(ExecState* exec, JSValue value) +{ + return JSAPIValueWrapper::create(exec, value); +} + +} // namespace JSC + +#endif // JSAPIValueWrapper_h diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp new file mode 100644 index 000000000..2c5a19ae6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -0,0 +1,1203 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2009, 2012, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2003 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "JSArray.h" + +#include "ArrayPrototype.h" +#include "ButterflyInlines.h" +#include "CachedCall.h" +#include "CopiedSpace.h" +#include "Error.h" +#include "Executable.h" +#include "GetterSetter.h" +#include "IndexingHeaderInlines.h" +#include "JSCInlines.h" +#include "PropertyNameArray.h" +#include "Reject.h" +#include <wtf/Assertions.h> + +using namespace std; +using namespace WTF; + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray); + +const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(JSArray)}; + +Butterfly* createArrayButterflyInDictionaryIndexingMode( + VM& vm, JSCell* intendedOwner, unsigned initialLength) +{ + Butterfly* butterfly = Butterfly::create( + vm, intendedOwner, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0)); + ArrayStorage* storage = butterfly->arrayStorage(); + storage->setLength(initialLength); + storage->setVectorLength(0); + storage->m_indexBias = 0; + storage->m_sparseMap.clear(); + storage->m_numValuesInVector = 0; + return butterfly; +} + +void JSArray::setLengthWritable(ExecState* exec, bool writable) +{ + ASSERT(isLengthWritable() || !writable); + if (!isLengthWritable() || writable) + return; + + enterDictionaryIndexingMode(exec->vm()); + + SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get(); + ASSERT(map); + map->setLengthIsReadOnly(); +} + +// Defined in ES5.1 15.4.5.1 +bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) +{ + JSArray* array = jsCast<JSArray*>(object); + + // 3. If P is "length", then + if (propertyName == exec->propertyNames().length) { + // All paths through length definition call the default [[DefineOwnProperty]], hence: + // from ES5.1 8.12.9 7.a. + if (descriptor.configurablePresent() && descriptor.configurable()) + return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property."); + // from ES5.1 8.12.9 7.b. + if (descriptor.enumerablePresent() && descriptor.enumerable()) + return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property."); + + // a. If the [[Value]] field of Desc is absent, then + // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments. + if (descriptor.isAccessorDescriptor()) + return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property."); + // from ES5.1 8.12.9 10.a. + if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable()) + return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property."); + // This descriptor is either just making length read-only, or changing nothing! + if (!descriptor.value()) { + if (descriptor.writablePresent()) + array->setLengthWritable(exec, descriptor.writable()); + return true; + } + + // b. Let newLenDesc be a copy of Desc. + // c. Let newLen be ToUint32(Desc.[[Value]]). + unsigned newLen = descriptor.value().toUInt32(exec); + // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception. + if (newLen != descriptor.value().toNumber(exec)) { + exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); + return false; + } + + // Based on SameValue check in 8.12.9, this is always okay. + // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case. + if (newLen == array->length()) { + if (descriptor.writablePresent()) + array->setLengthWritable(exec, descriptor.writable()); + return true; + } + + // e. Set newLenDesc.[[Value] to newLen. + // f. If newLen >= oldLen, then + // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments. + // g. Reject if oldLenDesc.[[Writable]] is false. + if (!array->isLengthWritable()) + return reject(exec, throwException, "Attempting to change value of a readonly property."); + + // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true. + // i. Else, + // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted. + // i.ii. Let newWritable be false. + // i.iii. Set newLenDesc.[[Writable] to true. + // j. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments. + // k. If succeeded is false, return false. + // l. While newLen < oldLen repeat, + // l.i. Set oldLen to oldLen – 1. + // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments. + // l.iii. If deleteSucceeded is false, then + if (!array->setLength(exec, newLen, throwException)) { + // 1. Set newLenDesc.[[Value] to oldLen+1. + // 2. If newWritable is false, set newLenDesc.[[Writable] to false. + // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments. + // 4. Reject. + if (descriptor.writablePresent()) + array->setLengthWritable(exec, descriptor.writable()); + return false; + } + + // m. If newWritable is false, then + // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", + // Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always + // return true. + if (descriptor.writablePresent()) + array->setLengthWritable(exec, descriptor.writable()); + // n. Return true. + return true; + } + + // 4. Else if P is an array index (15.4), then + // a. Let index be ToUint32(P). + if (Optional<uint32_t> optionalIndex = parseIndex(propertyName)) { + // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false. + uint32_t index = optionalIndex.value(); + // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case. + if (index >= array->length() && !array->isLengthWritable()) + return reject(exec, throwException, "Attempting to define numeric property on array with non-writable length property."); + // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments. + // d. Reject if succeeded is false. + // e. If index >= oldLen + // e.i. Set oldLenDesc.[[Value]] to index + 1. + // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true. + // f. Return true. + return array->defineOwnIndexedProperty(exec, index, descriptor, throwException); + } + + return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); +} + +bool JSArray::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSArray* thisObject = jsCast<JSArray*>(object); + if (propertyName == exec->propertyNames().length) { + unsigned attributes = thisObject->isLengthWritable() ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly; + slot.setValue(thisObject, attributes, jsNumber(thisObject->length())); + return true; + } + + return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +// ECMA 15.4.5.1 +void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + + if (propertyName == exec->propertyNames().length) { + unsigned newLength = value.toUInt32(exec); + if (value.toNumber(exec) != static_cast<double>(newLength)) { + exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); + return; + } + thisObject->setLength(exec, newLength, slot.isStrictMode()); + return; + } + + JSObject::put(thisObject, exec, propertyName, value, slot); +} + +bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSArray* thisObject = jsCast<JSArray*>(cell); + + if (propertyName == exec->propertyNames().length) + return false; + + return JSObject::deleteProperty(thisObject, exec, propertyName); +} + +static int compareKeysForQSort(const void* a, const void* b) +{ + unsigned da = *static_cast<const unsigned*>(a); + unsigned db = *static_cast<const unsigned*>(b); + return (da > db) - (da < db); +} + +void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSArray* thisObject = jsCast<JSArray*>(object); + + if (mode.includeDontEnumProperties()) + propertyNames.add(exec->propertyNames().length); + + JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); +} + +// This method makes room in the vector, but leaves the new space for count slots uncleared. +bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count) +{ + ArrayStorage* storage = ensureArrayStorage(vm); + Butterfly* butterfly = storage->butterfly(); + unsigned propertyCapacity = structure(vm)->outOfLineCapacity(); + unsigned propertySize = structure(vm)->outOfLineSize(); + + // If not, we should have handled this on the fast path. + ASSERT(!addToFront || count > storage->m_indexBias); + + // Step 1: + // Gather 4 key metrics: + // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors). + // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more. + // * currentCapacity - what is the current size of the vector, including any pre-capacity. + // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength. + + unsigned length = storage->length(); + unsigned usedVectorLength = min(storage->vectorLength(), length); + ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH); + // Check that required vector length is possible, in an overflow-safe fashion. + if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength) + return false; + unsigned requiredVectorLength = usedVectorLength + count; + ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH); + // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. + ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias); + unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias; + // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH. + unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1); + + // Step 2: + // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one. + + DeferGC deferGC(vm.heap); + void* newAllocBase = 0; + unsigned newStorageCapacity; + // If the current storage array is sufficiently large (but not too large!) then just keep using it. + if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) { + newAllocBase = butterfly->base(structure(vm)); + newStorageCapacity = currentCapacity; + } else { + size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity)); + if (!vm.heap.tryAllocateStorage(this, newSize, &newAllocBase)) + return false; + newStorageCapacity = desiredCapacity; + } + + // Step 3: + // Work out where we're going to move things to. + + // Determine how much of the vector to use as pre-capacity, and how much as post-capacity. + // If we're adding to the end, we'll add all the new space to the end. + // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any. + // If it did, we calculate the amount that will remain based on an atomic decay - leave the + // vector with half the post-capacity it had previously. + unsigned postCapacity = 0; + if (!addToFront) + postCapacity = max(newStorageCapacity - requiredVectorLength, count); + else if (length < storage->vectorLength()) { + // Atomic decay, + the post-capacity cannot be greater than what is available. + postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength); + // If we're moving contents within the same allocation, the post-capacity is being reduced. + ASSERT(newAllocBase != butterfly->base(structure(vm)) || postCapacity < storage->vectorLength() - length); + } + + unsigned newVectorLength = requiredVectorLength + postCapacity; + unsigned newIndexBias = newStorageCapacity - newVectorLength; + + Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity); + + if (addToFront) { + ASSERT(count + usedVectorLength <= newVectorLength); + memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength); + memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); + } else if ((newAllocBase != butterfly->base(structure(vm))) || (newIndexBias != storage->m_indexBias)) { + memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); + memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength); + + WriteBarrier<Unknown>* newVector = newButterfly->arrayStorage()->m_vector; + for (unsigned i = requiredVectorLength; i < newVectorLength; i++) + newVector[i].clear(); + } + + newButterfly->arrayStorage()->setVectorLength(newVectorLength); + newButterfly->arrayStorage()->m_indexBias = newIndexBias; + setButterflyWithoutChangingStructure(vm, newButterfly); + + return true; +} + +bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage) +{ + unsigned length = storage->length(); + + // If the length is read only then we enter sparse mode, so should enter the following 'if'. + ASSERT(isLengthWritable() || storage->m_sparseMap); + + if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { + // Fail if the length is not writable. + if (map->lengthIsReadOnly()) + return reject(exec, throwException, StrictModeReadonlyPropertyWriteError); + + if (newLength < length) { + // Copy any keys we might be interested in into a vector. + Vector<unsigned, 0, UnsafeVectorOverflow> keys; + keys.reserveInitialCapacity(min(map->size(), static_cast<size_t>(length - newLength))); + SparseArrayValueMap::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { + unsigned index = static_cast<unsigned>(it->key); + if (index < length && index >= newLength) + keys.append(index); + } + + // Check if the array is in sparse mode. If so there may be non-configurable + // properties, so we have to perform deletion with caution, if not we can + // delete values in any order. + if (map->sparseMode()) { + qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort); + unsigned i = keys.size(); + while (i) { + unsigned index = keys[--i]; + SparseArrayValueMap::iterator it = map->find(index); + ASSERT(it != map->notFound()); + if (it->value.attributes & DontDelete) { + storage->setLength(index + 1); + return reject(exec, throwException, "Unable to delete property."); + } + map->remove(it); + } + } else { + for (unsigned i = 0; i < keys.size(); ++i) + map->remove(keys[i]); + if (map->isEmpty()) + deallocateSparseIndexMap(); + } + } + } + + if (newLength < length) { + // Delete properties from the vector. + unsigned usedVectorLength = min(length, storage->vectorLength()); + for (unsigned i = newLength; i < usedVectorLength; ++i) { + WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; + bool hadValue = !!valueSlot; + valueSlot.clear(); + storage->m_numValuesInVector -= hadValue; + } + } + + storage->setLength(newLength); + + return true; +} + +bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException) +{ + switch (indexingType()) { + case ArrayClass: + if (!newLength) + return true; + if (newLength >= MIN_SPARSE_ARRAY_INDEX) { + return setLengthWithArrayStorage( + exec, newLength, throwException, + convertContiguousToArrayStorage(exec->vm())); + } + createInitialUndecided(exec->vm(), newLength); + return true; + + case ArrayWithUndecided: + case ArrayWithInt32: + case ArrayWithDouble: + case ArrayWithContiguous: { + if (newLength == m_butterfly->publicLength()) + return true; + if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push. + || (newLength >= MIN_SPARSE_ARRAY_INDEX + && !isDenseEnoughForVector(newLength, countElements()))) { + return setLengthWithArrayStorage( + exec, newLength, throwException, + ensureArrayStorage(exec->vm())); + } + if (newLength > m_butterfly->publicLength()) { + ensureLength(exec->vm(), newLength); + return true; + } + + unsigned lengthToClear = m_butterfly->publicLength() - newLength; + unsigned costToAllocateNewButterfly = 64; // a heuristic. + if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) { + reallocateAndShrinkButterfly(exec->vm(), newLength); + return true; + } + + if (indexingType() == ArrayWithDouble) { + for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) + m_butterfly->contiguousDouble()[i] = PNaN; + } else { + for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) + m_butterfly->contiguous()[i].clear(); + } + m_butterfly->setPublicLength(newLength); + return true; + } + + case ArrayWithArrayStorage: + case ArrayWithSlowPutArrayStorage: + return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage()); + + default: + CRASH(); + return false; + } +} + +JSValue JSArray::pop(ExecState* exec) +{ + switch (indexingType()) { + case ArrayClass: + return jsUndefined(); + + case ArrayWithUndecided: + if (!m_butterfly->publicLength()) + return jsUndefined(); + // We have nothing but holes. So, drop down to the slow version. + break; + + case ArrayWithInt32: + case ArrayWithContiguous: { + unsigned length = m_butterfly->publicLength(); + + if (!length--) + return jsUndefined(); + + RELEASE_ASSERT(length < m_butterfly->vectorLength()); + JSValue value = m_butterfly->contiguous()[length].get(); + if (value) { + m_butterfly->contiguous()[length].clear(); + m_butterfly->setPublicLength(length); + return value; + } + break; + } + + case ArrayWithDouble: { + unsigned length = m_butterfly->publicLength(); + + if (!length--) + return jsUndefined(); + + RELEASE_ASSERT(length < m_butterfly->vectorLength()); + double value = m_butterfly->contiguousDouble()[length]; + if (value == value) { + m_butterfly->contiguousDouble()[length] = PNaN; + m_butterfly->setPublicLength(length); + return JSValue(JSValue::EncodeAsDouble, value); + } + break; + } + + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = m_butterfly->arrayStorage(); + + unsigned length = storage->length(); + if (!length) { + if (!isLengthWritable()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return jsUndefined(); + } + + unsigned index = length - 1; + if (index < storage->vectorLength()) { + WriteBarrier<Unknown>& valueSlot = storage->m_vector[index]; + if (valueSlot) { + --storage->m_numValuesInVector; + JSValue element = valueSlot.get(); + valueSlot.clear(); + + RELEASE_ASSERT(isLengthWritable()); + storage->setLength(index); + return element; + } + } + break; + } + + default: + CRASH(); + return JSValue(); + } + + unsigned index = getArrayLength() - 1; + // Let element be the result of calling the [[Get]] internal method of O with argument indx. + JSValue element = get(exec, index); + if (exec->hadException()) + return jsUndefined(); + // Call the [[Delete]] internal method of O with arguments indx and true. + if (!deletePropertyByIndex(this, exec, index)) { + throwTypeError(exec, ASCIILiteral("Unable to delete property.")); + return jsUndefined(); + } + // Call the [[Put]] internal method of O with arguments "length", indx, and true. + setLength(exec, index, true); + // Return element. + return element; +} + +// Push & putIndex are almost identical, with two small differences. +// - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector. +// - pushing to an array of length 2^32-1 stores the property, but throws a range error. +void JSArray::push(ExecState* exec, JSValue value) +{ + switch (indexingType()) { + case ArrayClass: { + createInitialUndecided(exec->vm(), 0); + FALLTHROUGH; + } + + case ArrayWithUndecided: { + convertUndecidedForValue(exec->vm(), value); + push(exec, value); + return; + } + + case ArrayWithInt32: { + if (!value.isInt32()) { + convertInt32ForValue(exec->vm(), value); + push(exec, value); + return; + } + + unsigned length = m_butterfly->publicLength(); + ASSERT(length <= m_butterfly->vectorLength()); + if (length < m_butterfly->vectorLength()) { + m_butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value); + m_butterfly->setPublicLength(length + 1); + return; + } + + if (length > MAX_ARRAY_INDEX) { + methodTable(exec->vm())->putByIndex(this, exec, length, value, true); + if (!exec->hadException()) + exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); + return; + } + + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value); + return; + } + + case ArrayWithContiguous: { + unsigned length = m_butterfly->publicLength(); + ASSERT(length <= m_butterfly->vectorLength()); + if (length < m_butterfly->vectorLength()) { + m_butterfly->contiguous()[length].set(exec->vm(), this, value); + m_butterfly->setPublicLength(length + 1); + return; + } + + if (length > MAX_ARRAY_INDEX) { + methodTable(exec->vm())->putByIndex(this, exec, length, value, true); + if (!exec->hadException()) + exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); + return; + } + + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value); + return; + } + + case ArrayWithDouble: { + if (!value.isNumber()) { + convertDoubleToContiguous(exec->vm()); + push(exec, value); + return; + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + convertDoubleToContiguous(exec->vm()); + push(exec, value); + return; + } + + unsigned length = m_butterfly->publicLength(); + ASSERT(length <= m_butterfly->vectorLength()); + if (length < m_butterfly->vectorLength()) { + m_butterfly->contiguousDouble()[length] = valueAsDouble; + m_butterfly->setPublicLength(length + 1); + return; + } + + if (length > MAX_ARRAY_INDEX) { + methodTable(exec->vm())->putByIndex(this, exec, length, value, true); + if (!exec->hadException()) + exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); + return; + } + + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value); + break; + } + + case ArrayWithSlowPutArrayStorage: { + unsigned oldLength = length(); + if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) { + if (!exec->hadException() && oldLength < 0xFFFFFFFFu) + setLength(exec, oldLength + 1, true); + return; + } + FALLTHROUGH; + } + + case ArrayWithArrayStorage: { + ArrayStorage* storage = m_butterfly->arrayStorage(); + + // Fast case - push within vector, always update m_length & m_numValuesInVector. + unsigned length = storage->length(); + if (length < storage->vectorLength()) { + storage->m_vector[length].set(exec->vm(), this, value); + storage->setLength(length + 1); + ++storage->m_numValuesInVector; + return; + } + + // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error. + if (storage->length() > MAX_ARRAY_INDEX) { + methodTable(exec->vm())->putByIndex(this, exec, storage->length(), value, true); + // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. + if (!exec->hadException()) + exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); + return; + } + + // Handled the same as putIndex. + putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage); + break; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count) +{ + auto arrayType = indexingType(); + switch (arrayType) { + case ArrayWithDouble: + case ArrayWithInt32: + case ArrayWithContiguous: { + VM& vm = exec.vm(); + if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm)) + return nullptr; + + Structure* resultStructure = exec.lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(arrayType); + JSArray* resultArray = JSArray::tryCreateUninitialized(vm, resultStructure, count); + if (!resultArray) + return nullptr; + + auto& resultButterfly = *resultArray->butterfly(); + if (arrayType == ArrayWithDouble) + memcpy(resultButterfly.contiguousDouble().data(), m_butterfly->contiguousDouble().data() + startIndex, sizeof(JSValue) * count); + else + memcpy(resultButterfly.contiguous().data(), m_butterfly->contiguous().data() + startIndex, sizeof(JSValue) * count); + resultButterfly.setPublicLength(count); + + return resultArray; + } + default: + return nullptr; + } +} + +EncodedJSValue JSArray::fastConcatWith(ExecState& exec, JSArray& otherArray) +{ + auto newArrayType = indexingType(); + + VM& vm = exec.vm(); + ASSERT(newArrayType == fastConcatType(vm, *this, otherArray)); + + unsigned thisArraySize = m_butterfly->publicLength(); + unsigned otherArraySize = otherArray.m_butterfly->publicLength(); + ASSERT(thisArraySize + otherArraySize < MIN_SPARSE_ARRAY_INDEX); + + Structure* resultStructure = exec.lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(newArrayType); + JSArray* resultArray = JSArray::tryCreateUninitialized(vm, resultStructure, thisArraySize + otherArraySize); + if (!resultArray) + return JSValue::encode(throwOutOfMemoryError(&exec)); + + auto& resultButterfly = *resultArray->butterfly(); + auto& otherButterfly = *otherArray.butterfly(); + if (newArrayType == ArrayWithDouble) { + auto buffer = resultButterfly.contiguousDouble().data(); + memcpy(buffer, m_butterfly->contiguousDouble().data(), sizeof(JSValue) * thisArraySize); + memcpy(buffer + thisArraySize, otherButterfly.contiguousDouble().data(), sizeof(JSValue) * otherArraySize); + } else { + auto buffer = resultButterfly.contiguous().data(); + memcpy(buffer, m_butterfly->contiguous().data(), sizeof(JSValue) * thisArraySize); + memcpy(buffer + thisArraySize, otherButterfly.contiguous().data(), sizeof(JSValue) * otherArraySize); + } + + resultButterfly.setPublicLength(thisArraySize + otherArraySize); + return JSValue::encode(resultArray); +} + +bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage) +{ + unsigned oldLength = storage->length(); + RELEASE_ASSERT(count <= oldLength); + + // If the array contains holes or is otherwise in an abnormal state, + // use the generic algorithm in ArrayPrototype. + if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm)) + || hasSparseMap() + || shouldUseSlowPut(indexingType())) { + return false; + } + + if (!oldLength) + return true; + + unsigned length = oldLength - count; + + storage->m_numValuesInVector -= count; + storage->setLength(length); + + unsigned vectorLength = storage->vectorLength(); + if (!vectorLength) + return true; + + if (startIndex >= vectorLength) + return true; + + if (startIndex + count > vectorLength) + count = vectorLength - startIndex; + + unsigned usedVectorLength = min(vectorLength, oldLength); + + unsigned numElementsBeforeShiftRegion = startIndex; + unsigned firstIndexAfterShiftRegion = startIndex + count; + unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion; + ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength); + + // The point of this comparison seems to be to minimize the amount of elements that have to + // be moved during a shift operation. + if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) { + // The number of elements before the shift region is less than the number of elements + // after the shift region, so we move the elements before to the right. + if (numElementsBeforeShiftRegion) { + RELEASE_ASSERT(count + startIndex <= vectorLength); + if (storage->hasHoles()) { + for (unsigned i = startIndex; i-- > 0;) { + unsigned destinationIndex = count + i; + JSValue source = storage->m_vector[i].get(); + JSValue dest = storage->m_vector[destinationIndex].get(); + // Any time we overwrite a hole we know we overcounted the number of values we removed + // when we subtracted count from m_numValuesInVector above. + if (!dest && destinationIndex >= firstIndexAfterShiftRegion) + storage->m_numValuesInVector++; + storage->m_vector[count + i].setWithoutWriteBarrier(source); + } + } else { + memmove(storage->m_vector + count, + storage->m_vector, + sizeof(JSValue) * startIndex); + } + } + // Adjust the Butterfly and the index bias. We only need to do this here because we're changing + // the start of the Butterfly, which needs to point at the first indexed property in the used + // portion of the vector. + m_butterfly.setWithoutWriteBarrier(m_butterfly->shift(structure(), count)); + storage = m_butterfly->arrayStorage(); + storage->m_indexBias += count; + + // Since we're consuming part of the vector by moving its beginning to the left, + // we need to modify the vector length appropriately. + storage->setVectorLength(vectorLength - count); + } else { + // The number of elements before the shift region is greater than or equal to the number + // of elements after the shift region, so we move the elements after the shift region to the left. + if (storage->hasHoles()) { + for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) { + unsigned destinationIndex = startIndex + i; + JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get(); + JSValue dest = storage->m_vector[destinationIndex].get(); + // Any time we overwrite a hole we know we overcounted the number of values we removed + // when we subtracted count from m_numValuesInVector above. + if (!dest && destinationIndex < firstIndexAfterShiftRegion) + storage->m_numValuesInVector++; + storage->m_vector[startIndex + i].setWithoutWriteBarrier(source); + } + } else { + memmove(storage->m_vector + startIndex, + storage->m_vector + firstIndexAfterShiftRegion, + sizeof(JSValue) * numElementsAfterShiftRegion); + } + // Clear the slots of the elements we just moved. + unsigned startOfEmptyVectorTail = usedVectorLength - count; + for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i) + storage->m_vector[i].clear(); + // We don't modify the index bias or the Butterfly pointer in this case because we're not changing + // the start of the Butterfly, which needs to point at the first indexed property in the used + // portion of the vector. We also don't modify the vector length because we're not actually changing + // its length; we're just using less of it. + } + + return true; +} + +bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count) +{ + VM& vm = exec->vm(); + RELEASE_ASSERT(count > 0); + + switch (indexingType()) { + case ArrayClass: + return true; + + case ArrayWithUndecided: + // Don't handle this because it's confusing and it shouldn't come up. + return false; + + case ArrayWithInt32: + case ArrayWithContiguous: { + unsigned oldLength = m_butterfly->publicLength(); + RELEASE_ASSERT(count <= oldLength); + + // We may have to walk the entire array to do the shift. We're willing to do + // so only if it's not horribly slow. + if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) + return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm)); + + // Storing to a hole is fine since we're still having a good time. But reading from a hole + // is totally not fine, since we might have to read from the proto chain. + // We have to check for holes before we start moving things around so that we don't get halfway + // through shifting and then realize we should have been in ArrayStorage mode. + unsigned end = oldLength - count; + if (this->structure(vm)->holesMustForwardToPrototype(vm)) { + for (unsigned i = startIndex; i < end; ++i) { + JSValue v = m_butterfly->contiguous()[i + count].get(); + if (UNLIKELY(!v)) { + startIndex = i; + return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm)); + } + m_butterfly->contiguous()[i].setWithoutWriteBarrier(v); + } + } else { + memmove(m_butterfly->contiguous().data() + startIndex, + m_butterfly->contiguous().data() + startIndex + count, + sizeof(JSValue) * (end - startIndex)); + } + + for (unsigned i = end; i < oldLength; ++i) + m_butterfly->contiguous()[i].clear(); + + m_butterfly->setPublicLength(oldLength - count); + return true; + } + + case ArrayWithDouble: { + unsigned oldLength = m_butterfly->publicLength(); + RELEASE_ASSERT(count <= oldLength); + + // We may have to walk the entire array to do the shift. We're willing to do + // so only if it's not horribly slow. + if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) + return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm)); + + // Storing to a hole is fine since we're still having a good time. But reading from a hole + // is totally not fine, since we might have to read from the proto chain. + // We have to check for holes before we start moving things around so that we don't get halfway + // through shifting and then realize we should have been in ArrayStorage mode. + unsigned end = oldLength - count; + if (this->structure(vm)->holesMustForwardToPrototype(vm)) { + for (unsigned i = startIndex; i < end; ++i) { + double v = m_butterfly->contiguousDouble()[i + count]; + if (UNLIKELY(v != v)) { + startIndex = i; + return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm)); + } + m_butterfly->contiguousDouble()[i] = v; + } + } else { + memmove(m_butterfly->contiguousDouble().data() + startIndex, + m_butterfly->contiguousDouble().data() + startIndex + count, + sizeof(JSValue) * (end - startIndex)); + } + for (unsigned i = end; i < oldLength; ++i) + m_butterfly->contiguousDouble()[i] = PNaN; + + m_butterfly->setPublicLength(oldLength - count); + return true; + } + + case ArrayWithArrayStorage: + case ArrayWithSlowPutArrayStorage: + return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage()); + + default: + CRASH(); + return false; + } +} + +// Returns true if the unshift can be handled, false to fallback. +bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage) +{ + unsigned length = storage->length(); + + RELEASE_ASSERT(startIndex <= length); + + // If the array contains holes or is otherwise in an abnormal state, + // use the generic algorithm in ArrayPrototype. + if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType())) + return false; + + bool moveFront = !startIndex || startIndex < length / 2; + + unsigned vectorLength = storage->vectorLength(); + + if (moveFront && storage->m_indexBias >= count) { + Butterfly* newButterfly = storage->butterfly()->unshift(structure(), count); + storage = newButterfly->arrayStorage(); + storage->m_indexBias -= count; + storage->setVectorLength(vectorLength + count); + setButterflyWithoutChangingStructure(exec->vm(), newButterfly); + } else if (!moveFront && vectorLength - length >= count) + storage = storage->butterfly()->arrayStorage(); + else if (unshiftCountSlowCase(exec->vm(), moveFront, count)) + storage = arrayStorage(); + else { + throwOutOfMemoryError(exec); + return true; + } + + WriteBarrier<Unknown>* vector = storage->m_vector; + + if (startIndex) { + if (moveFront) + memmove(vector, vector + count, startIndex * sizeof(JSValue)); + else if (length - startIndex) + memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue)); + } + + for (unsigned i = 0; i < count; i++) + vector[i + startIndex].clear(); + return true; +} + +bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count) +{ + switch (indexingType()) { + case ArrayClass: + case ArrayWithUndecided: + // We could handle this. But it shouldn't ever come up, so we won't. + return false; + + case ArrayWithInt32: + case ArrayWithContiguous: { + unsigned oldLength = m_butterfly->publicLength(); + + // We may have to walk the entire array to do the unshift. We're willing to do so + // only if it's not horribly slow. + if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm())); + + ensureLength(exec->vm(), oldLength + count); + + // We have to check for holes before we start moving things around so that we don't get halfway + // through shifting and then realize we should have been in ArrayStorage mode. + for (unsigned i = oldLength; i-- > startIndex;) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (UNLIKELY(!v)) + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm())); + } + + for (unsigned i = oldLength; i-- > startIndex;) { + JSValue v = m_butterfly->contiguous()[i].get(); + ASSERT(v); + m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v); + } + + // NOTE: we're leaving being garbage in the part of the array that we shifted out + // of. This is fine because the caller is required to store over that area, and + // in contiguous mode storing into a hole is guaranteed to behave exactly the same + // as storing over an existing element. + + return true; + } + + case ArrayWithDouble: { + unsigned oldLength = m_butterfly->publicLength(); + + // We may have to walk the entire array to do the unshift. We're willing to do so + // only if it's not horribly slow. + if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm())); + + ensureLength(exec->vm(), oldLength + count); + + // We have to check for holes before we start moving things around so that we don't get halfway + // through shifting and then realize we should have been in ArrayStorage mode. + for (unsigned i = oldLength; i-- > startIndex;) { + double v = m_butterfly->contiguousDouble()[i]; + if (UNLIKELY(v != v)) + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm())); + } + + for (unsigned i = oldLength; i-- > startIndex;) { + double v = m_butterfly->contiguousDouble()[i]; + ASSERT(v == v); + m_butterfly->contiguousDouble()[i + count] = v; + } + + // NOTE: we're leaving being garbage in the part of the array that we shifted out + // of. This is fine because the caller is required to store over that area, and + // in contiguous mode storing into a hole is guaranteed to behave exactly the same + // as storing over an existing element. + + return true; + } + + case ArrayWithArrayStorage: + case ArrayWithSlowPutArrayStorage: + return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage()); + + default: + CRASH(); + return false; + } +} + +void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) +{ + unsigned i = 0; + unsigned vectorEnd; + WriteBarrier<Unknown>* vector; + + switch (indexingType()) { + case ArrayClass: + return; + + case ArrayWithUndecided: { + vector = 0; + vectorEnd = 0; + break; + } + + case ArrayWithInt32: + case ArrayWithContiguous: { + vectorEnd = m_butterfly->publicLength(); + vector = m_butterfly->contiguous().data(); + break; + } + + case ArrayWithDouble: { + vector = 0; + vectorEnd = 0; + for (; i < m_butterfly->publicLength(); ++i) { + double v = butterfly()->contiguousDouble()[i]; + if (v != v) + break; + args.append(JSValue(JSValue::EncodeAsDouble, v)); + } + break; + } + + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = m_butterfly->arrayStorage(); + + vector = storage->m_vector; + vectorEnd = min(storage->length(), storage->vectorLength()); + break; + } + + default: + CRASH(); +#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) + vector = 0; + vectorEnd = 0; + break; +#endif + } + + for (; i < vectorEnd; ++i) { + WriteBarrier<Unknown>& v = vector[i]; + if (!v) + break; + args.append(v.get()); + } + + // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case. + for (; i < length(); ++i) + args.append(get(exec, i)); +} + +void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length) +{ + unsigned i = offset; + WriteBarrier<Unknown>* vector; + unsigned vectorEnd; + length += offset; // We like to think of the length as being our length, rather than the output length. + + // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case. + ASSERT(length == this->length()); + + switch (indexingType()) { + case ArrayClass: + return; + + case ArrayWithUndecided: { + vector = 0; + vectorEnd = 0; + break; + } + + case ArrayWithInt32: + case ArrayWithContiguous: { + vector = m_butterfly->contiguous().data(); + vectorEnd = m_butterfly->publicLength(); + break; + } + + case ArrayWithDouble: { + vector = 0; + vectorEnd = 0; + for (; i < m_butterfly->publicLength(); ++i) { + ASSERT(i < butterfly()->vectorLength()); + double v = m_butterfly->contiguousDouble()[i]; + if (v != v) + break; + exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v); + } + break; + } + + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = m_butterfly->arrayStorage(); + vector = storage->m_vector; + vectorEnd = min(length, storage->vectorLength()); + break; + } + + default: + CRASH(); +#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) + vector = 0; + vectorEnd = 0; + break; +#endif + } + + for (; i < vectorEnd; ++i) { + WriteBarrier<Unknown>& v = vector[i]; + if (!v) + break; + exec->r(firstElementDest + i - offset) = v.get(); + } + + for (; i < length; ++i) { + exec->r(firstElementDest + i - offset) = get(exec, i); + if (UNLIKELY(exec->vm().exception())) + return; + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h new file mode 100644 index 000000000..45b558b20 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -0,0 +1,347 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2009, 2012, 2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef JSArray_h +#define JSArray_h + +#include "ArrayConventions.h" +#include "ButterflyInlines.h" +#include "JSObject.h" + +namespace JSC { + +class JSArray; +class LLIntOffsetsExtractor; + +class JSArray : public JSNonFinalObject { + friend class LLIntOffsetsExtractor; + friend class Walker; + friend class JIT; + +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames; + + static size_t allocationSize(size_t inlineCapacity) + { + ASSERT_UNUSED(inlineCapacity, !inlineCapacity); + return sizeof(JSArray); + } + +protected: + explicit JSArray(VM& vm, Structure* structure, Butterfly* butterfly) + : JSNonFinalObject(vm, structure, butterfly) + { + } + +public: + static JSArray* create(VM&, Structure*, unsigned initialLength = 0); + static JSArray* createWithButterfly(VM&, Structure*, Butterfly*); + + // tryCreateUninitialized is used for fast construction of arrays whose size and + // contents are known at time of creation. Clients of this interface must: + // - null-check the result (indicating out of memory, or otherwise unable to allocate vector). + // - call 'initializeIndex' for all properties in sequence, for 0 <= i < initialLength. + static JSArray* tryCreateUninitialized(VM&, Structure*, unsigned initialLength); + + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool throwException); + + JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + + DECLARE_EXPORT_INFO; + + // OK if we know this is a JSArray, but not if it could be an object of a derived class; for RuntimeArray this always returns 0. + unsigned length() const { return getArrayLength(); } + + // OK to use on new arrays, but not if it might be a RegExpMatchArray or RuntimeArray. + JS_EXPORT_PRIVATE bool setLength(ExecState*, unsigned, bool throwException = false); + + JS_EXPORT_PRIVATE void push(ExecState*, JSValue); + JS_EXPORT_PRIVATE JSValue pop(ExecState*); + + JSArray* fastSlice(ExecState&, unsigned startIndex, unsigned count); + + static IndexingType fastConcatType(VM& vm, JSArray& firstArray, JSArray& secondArray) + { + IndexingType type = firstArray.indexingType(); + if (type != secondArray.indexingType()) + return NonArray; + if (type != ArrayWithDouble && type != ArrayWithInt32 && type != ArrayWithContiguous) + return NonArray; + if (firstArray.structure(vm)->holesMustForwardToPrototype(vm) + || secondArray.structure(vm)->holesMustForwardToPrototype(vm)) + return NonArray; + return type; + } + EncodedJSValue fastConcatWith(ExecState&, JSArray&); + + enum ShiftCountMode { + // This form of shift hints that we're doing queueing. With this assumption in hand, + // we convert to ArrayStorage, which has queue optimizations. + ShiftCountForShift, + + // This form of shift hints that we're just doing care and feeding on an array that + // is probably typically used for ordinary accesses. With this assumption in hand, + // we try to preserve whatever indexing type it has already. + ShiftCountForSplice + }; + + bool shiftCountForShift(ExecState* exec, unsigned startIndex, unsigned count) + { + return shiftCountWithArrayStorage(exec->vm(), startIndex, count, ensureArrayStorage(exec->vm())); + } + bool shiftCountForSplice(ExecState* exec, unsigned& startIndex, unsigned count) + { + return shiftCountWithAnyIndexingType(exec, startIndex, count); + } + template<ShiftCountMode shiftCountMode> + bool shiftCount(ExecState* exec, unsigned& startIndex, unsigned count) + { + switch (shiftCountMode) { + case ShiftCountForShift: + return shiftCountForShift(exec, startIndex, count); + case ShiftCountForSplice: + return shiftCountForSplice(exec, startIndex, count); + default: + CRASH(); + return false; + } + } + + bool unshiftCountForShift(ExecState* exec, unsigned startIndex, unsigned count) + { + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm())); + } + bool unshiftCountForSplice(ExecState* exec, unsigned startIndex, unsigned count) + { + return unshiftCountWithAnyIndexingType(exec, startIndex, count); + } + template<ShiftCountMode shiftCountMode> + bool unshiftCount(ExecState* exec, unsigned startIndex, unsigned count) + { + switch (shiftCountMode) { + case ShiftCountForShift: + return unshiftCountForShift(exec, startIndex, count); + case ShiftCountForSplice: + return unshiftCountForSplice(exec, startIndex, count); + default: + CRASH(); + return false; + } + } + + JS_EXPORT_PRIVATE void fillArgList(ExecState*, MarkedArgumentBuffer&); + JS_EXPORT_PRIVATE void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, IndexingType indexingType) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), indexingType); + } + +protected: + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + +private: + bool isLengthWritable() + { + ArrayStorage* storage = arrayStorageOrNull(); + if (!storage) + return true; + SparseArrayValueMap* map = storage->m_sparseMap.get(); + return !map || !map->lengthIsReadOnly(); + } + + bool shiftCountWithAnyIndexingType(ExecState*, unsigned& startIndex, unsigned count); + JS_EXPORT_PRIVATE bool shiftCountWithArrayStorage(VM&, unsigned startIndex, unsigned count, ArrayStorage*); + + bool unshiftCountWithAnyIndexingType(ExecState*, unsigned startIndex, unsigned count); + bool unshiftCountWithArrayStorage(ExecState*, unsigned startIndex, unsigned count, ArrayStorage*); + bool unshiftCountSlowCase(VM&, bool, unsigned); + + bool setLengthWithArrayStorage(ExecState*, unsigned newLength, bool throwException, ArrayStorage*); + void setLengthWritable(ExecState*, bool writable); +}; + +inline Butterfly* createContiguousArrayButterfly(VM& vm, JSCell* intendedOwner, unsigned length, unsigned& vectorLength) +{ + IndexingHeader header; + vectorLength = std::max(length, BASE_VECTOR_LEN); + header.setVectorLength(vectorLength); + header.setPublicLength(length); + Butterfly* result = Butterfly::create( + vm, intendedOwner, 0, 0, true, header, vectorLength * sizeof(EncodedJSValue)); + return result; +} + +inline Butterfly* createArrayButterfly(VM& vm, JSCell* intendedOwner, unsigned initialLength) +{ + Butterfly* butterfly = Butterfly::create( + vm, intendedOwner, 0, 0, true, baseIndexingHeaderForArray(initialLength), + ArrayStorage::sizeFor(BASE_VECTOR_LEN)); + ArrayStorage* storage = butterfly->arrayStorage(); + storage->m_indexBias = 0; + storage->m_sparseMap.clear(); + storage->m_numValuesInVector = 0; + return butterfly; +} + +Butterfly* createArrayButterflyInDictionaryIndexingMode( + VM&, JSCell* intendedOwner, unsigned initialLength); + +inline JSArray* JSArray::create(VM& vm, Structure* structure, unsigned initialLength) +{ + Butterfly* butterfly; + if (LIKELY(!hasAnyArrayStorage(structure->indexingType()))) { + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); + unsigned vectorLength; + butterfly = createContiguousArrayButterfly(vm, 0, initialLength, vectorLength); + ASSERT(initialLength < MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH); + if (hasDouble(structure->indexingType())) { + for (unsigned i = 0; i < vectorLength; ++i) + butterfly->contiguousDouble()[i] = PNaN; + } + } else { + ASSERT( + structure->indexingType() == ArrayWithSlowPutArrayStorage + || structure->indexingType() == ArrayWithArrayStorage); + butterfly = createArrayButterfly(vm, 0, initialLength); + } + + return createWithButterfly(vm, structure, butterfly); +} + +inline JSArray* JSArray::tryCreateUninitialized(VM& vm, Structure* structure, unsigned initialLength) +{ + unsigned vectorLength = std::max(BASE_VECTOR_LEN, initialLength); + if (vectorLength > MAX_STORAGE_VECTOR_LENGTH) + return 0; + + Butterfly* butterfly; + if (LIKELY(!hasAnyArrayStorage(structure->indexingType()))) { + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); + + void* temp; + if (!vm.heap.tryAllocateStorage(0, Butterfly::totalSize(0, 0, true, vectorLength * sizeof(EncodedJSValue)), &temp)) + return 0; + butterfly = Butterfly::fromBase(temp, 0, 0); + butterfly->setVectorLength(vectorLength); + butterfly->setPublicLength(initialLength); + if (hasDouble(structure->indexingType())) { + for (unsigned i = initialLength; i < vectorLength; ++i) + butterfly->contiguousDouble()[i] = PNaN; + } + } else { + void* temp; + if (!vm.heap.tryAllocateStorage(0, Butterfly::totalSize(0, 0, true, ArrayStorage::sizeFor(vectorLength)), &temp)) + return 0; + butterfly = Butterfly::fromBase(temp, 0, 0); + *butterfly->indexingHeader() = indexingHeaderForArray(initialLength, vectorLength); + ArrayStorage* storage = butterfly->arrayStorage(); + storage->m_indexBias = 0; + storage->m_sparseMap.clear(); + storage->m_numValuesInVector = initialLength; + } + + return createWithButterfly(vm, structure, butterfly); +} + +inline JSArray* JSArray::createWithButterfly(VM& vm, Structure* structure, Butterfly* butterfly) +{ + JSArray* array = new (NotNull, allocateCell<JSArray>(vm.heap)) JSArray(vm, structure, butterfly); + array->finishCreation(vm); + return array; +} + +JSArray* asArray(JSValue); + +inline JSArray* asArray(JSCell* cell) +{ + ASSERT(cell->inherits(JSArray::info())); + return jsCast<JSArray*>(cell); +} + +inline JSArray* asArray(JSValue value) +{ + return asArray(value.asCell()); +} + +inline bool isJSArray(JSCell* cell) { return cell->classInfo() == JSArray::info(); } +inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); } + +inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values) +{ + VM& vm = exec->vm(); + unsigned length = values.size(); + JSArray* array = JSArray::tryCreateUninitialized(vm, arrayStructure, length); + + // FIXME: we should probably throw an out of memory error here, but + // when making this change we should check that all clients of this + // function will correctly handle an exception being thrown from here. + RELEASE_ASSERT(array); + + for (unsigned i = 0; i < length; ++i) + array->initializeIndex(vm, i, values.at(i)); + return array; +} + +inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length) +{ + VM& vm = exec->vm(); + JSArray* array = JSArray::tryCreateUninitialized(vm, arrayStructure, length); + + // FIXME: we should probably throw an out of memory error here, but + // when making this change we should check that all clients of this + // function will correctly handle an exception being thrown from here. + RELEASE_ASSERT(array); + + for (unsigned i = 0; i < length; ++i) + array->initializeIndex(vm, i, values[i]); + return array; +} + +inline JSArray* constructArrayNegativeIndexed(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length) +{ + VM& vm = exec->vm(); + JSArray* array = JSArray::tryCreateUninitialized(vm, arrayStructure, length); + + // FIXME: we should probably throw an out of memory error here, but + // when making this change we should check that all clients of this + // function will correctly handle an exception being thrown from here. + RELEASE_ASSERT(array); + + for (int i = 0; i < static_cast<int>(length); ++i) + array->initializeIndex(vm, i, values[-i]); + return array; +} + +} // namespace JSC + +#endif // JSArray_h diff --git a/Source/JavaScriptCore/runtime/JSArrayBuffer.cpp b/Source/JavaScriptCore/runtime/JSArrayBuffer.cpp new file mode 100644 index 000000000..62ab89a3d --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBuffer.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSArrayBuffer.h" + +#include "JSCInlines.h" +#include "Reject.h" + +namespace JSC { + +const ClassInfo JSArrayBuffer::s_info = { + "ArrayBuffer", &Base::s_info, 0, CREATE_METHOD_TABLE(JSArrayBuffer)}; + +JSArrayBuffer::JSArrayBuffer(VM& vm, Structure* structure, PassRefPtr<ArrayBuffer> arrayBuffer) + : Base(vm, structure) + , m_impl(arrayBuffer.get()) +{ +} + +void JSArrayBuffer::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + vm.heap.addReference(this, m_impl); +} + +JSArrayBuffer* JSArrayBuffer::create( + VM& vm, Structure* structure, PassRefPtr<ArrayBuffer> passedBuffer) +{ + RefPtr<ArrayBuffer> buffer = passedBuffer; + JSArrayBuffer* result = + new (NotNull, allocateCell<JSArrayBuffer>(vm.heap)) + JSArrayBuffer(vm, structure, buffer); + result->finishCreation(vm); + return result; +} + +Structure* JSArrayBuffer::createStructure( + VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create( + vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), + NonArray); +} + +bool JSArrayBuffer::getOwnPropertySlot( + JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(object); + + if (propertyName == exec->propertyNames().byteLength) { + slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->impl()->byteLength())); + return true; + } + + return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +void JSArrayBuffer::put( + JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, + PutPropertySlot& slot) +{ + JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(cell); + + if (propertyName == exec->propertyNames().byteLength) { + reject(exec, slot.isStrictMode(), "Attempting to write to a read-only array buffer property."); + return; + } + + Base::put(thisObject, exec, propertyName, value, slot); +} + +bool JSArrayBuffer::defineOwnProperty( + JSObject* object, ExecState* exec, PropertyName propertyName, + const PropertyDescriptor& descriptor, bool shouldThrow) +{ + JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(object); + + if (propertyName == exec->propertyNames().byteLength) + return reject(exec, shouldThrow, "Attempting to define read-only array buffer property."); + + return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); +} + +bool JSArrayBuffer::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(cell); + + if (propertyName == exec->propertyNames().byteLength) + return false; + + return Base::deleteProperty(thisObject, exec, propertyName); +} + +void JSArrayBuffer::getOwnNonIndexPropertyNames( + JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) +{ + JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(object); + + if (mode.includeDontEnumProperties()) + array.add(exec->propertyNames().byteLength); + + Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSArrayBuffer.h b/Source/JavaScriptCore/runtime/JSArrayBuffer.h new file mode 100644 index 000000000..7529e3634 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBuffer.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSArrayBuffer_h +#define JSArrayBuffer_h + +#include "ArrayBuffer.h" +#include "JSObject.h" + +namespace JSC { + +class JSArrayBuffer : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetPropertyNames | OverridesGetOwnPropertySlot; + +protected: + JSArrayBuffer(VM&, Structure*, PassRefPtr<ArrayBuffer>); + void finishCreation(VM&); + +public: + JS_EXPORT_PRIVATE static JSArrayBuffer* create(VM&, Structure*, PassRefPtr<ArrayBuffer>); + + ArrayBuffer* impl() const { return m_impl; } + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + DECLARE_EXPORT_INFO; + +protected: + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + + static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + +private: + ArrayBuffer* m_impl; +}; + +inline ArrayBuffer* toArrayBuffer(JSValue value) +{ + JSArrayBuffer* wrapper = jsDynamicCast<JSArrayBuffer*>(value); + if (!wrapper) + return 0; + return wrapper->impl(); +} + +} // namespace JSC + +#endif // JSArrayBuffer_h + diff --git a/Source/JavaScriptCore/runtime/JSArrayBufferConstructor.cpp b/Source/JavaScriptCore/runtime/JSArrayBufferConstructor.cpp new file mode 100644 index 000000000..3d14a9ef5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBufferConstructor.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSArrayBufferConstructor.h" + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArrayBuffer.h" +#include "JSArrayBufferPrototype.h" +#include "JSGlobalObject.h" +#include "JSCInlines.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL arrayBufferFuncIsView(ExecState*); + +const ClassInfo JSArrayBufferConstructor::s_info = { + "Function", &Base::s_info, 0, + CREATE_METHOD_TABLE(JSArrayBufferConstructor) +}; + +JSArrayBufferConstructor::JSArrayBufferConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +void JSArrayBufferConstructor::finishCreation(VM& vm, JSArrayBufferPrototype* prototype) +{ + Base::finishCreation(vm, ASCIILiteral("ArrayBuffer")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), DontEnum | DontDelete | ReadOnly); + + JSGlobalObject* globalObject = this->globalObject(); + JSC_NATIVE_FUNCTION(vm.propertyNames->isView, arrayBufferFuncIsView, DontEnum, 1); +} + +JSArrayBufferConstructor* JSArrayBufferConstructor::create(VM& vm, Structure* structure, JSArrayBufferPrototype* prototype) +{ + JSArrayBufferConstructor* result = + new (NotNull, allocateCell<JSArrayBufferConstructor>(vm.heap)) + JSArrayBufferConstructor(vm, structure); + result->finishCreation(vm, prototype); + return result; +} + +Structure* JSArrayBufferConstructor::createStructure( + VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create( + vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +static EncodedJSValue JSC_HOST_CALL constructArrayBuffer(ExecState* exec) +{ + JSArrayBufferConstructor* constructor = + jsCast<JSArrayBufferConstructor*>(exec->callee()); + + unsigned length; + if (exec->argumentCount()) { + length = exec->uncheckedArgument(0).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else { + // Although the documentation doesn't say so, it is in fact correct to say + // "new ArrayBuffer()". The result is the same as allocating an array buffer + // with a zero length. + length = 0; + } + + RefPtr<ArrayBuffer> buffer = ArrayBuffer::create(length, 1); + if (!buffer) + return throwVMError(exec, createOutOfMemoryError(exec)); + + JSArrayBuffer* result = JSArrayBuffer::create( + exec->vm(), constructor->globalObject()->arrayBufferStructure(), buffer.release()); + + return JSValue::encode(result); +} + +ConstructType JSArrayBufferConstructor::getConstructData( + JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructArrayBuffer; + return ConstructTypeHost; +} + +CallType JSArrayBufferConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = constructArrayBuffer; + return CallTypeHost; +} + +// ------------------------------ Functions -------------------------------- + +// ECMA 24.1.3.1 +EncodedJSValue JSC_HOST_CALL arrayBufferFuncIsView(ExecState* exec) +{ + return JSValue::encode(jsBoolean(jsDynamicCast<JSArrayBufferView*>(exec->argument(0)))); +} + + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSArrayBufferConstructor.h b/Source/JavaScriptCore/runtime/JSArrayBufferConstructor.h new file mode 100644 index 000000000..b65b523b6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBufferConstructor.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSArrayBufferConstructor_h +#define JSArrayBufferConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class JSArrayBufferPrototype; + +class JSArrayBufferConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + +protected: + JSArrayBufferConstructor(VM&, Structure*); + void finishCreation(VM&, JSArrayBufferPrototype*); + +public: + static JSArrayBufferConstructor* create(VM&, Structure*, JSArrayBufferPrototype*); + + DECLARE_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + +protected: + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} // namespace JSC + +#endif // JSArrayBufferConstructor_h + diff --git a/Source/JavaScriptCore/runtime/JSArrayBufferPrototype.cpp b/Source/JavaScriptCore/runtime/JSArrayBufferPrototype.cpp new file mode 100644 index 000000000..ef650f6eb --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBufferPrototype.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSArrayBufferPrototype.h" + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArrayBuffer.h" +#include "JSFunction.h" +#include "JSCInlines.h" +#include "TypedArrayAdaptors.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL arrayBufferProtoFuncSlice(ExecState* exec) +{ + JSFunction* callee = jsCast<JSFunction*>(exec->callee()); + + JSArrayBuffer* thisObject = jsDynamicCast<JSArrayBuffer*>(exec->thisValue()); + if (!thisObject) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Receiver of slice must be an array buffer."))); + + if (!exec->argumentCount()) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Slice requires at least one argument."))); + + int32_t begin = exec->argument(0).toInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + int32_t end; + if (exec->argumentCount() >= 2) { + end = exec->uncheckedArgument(1).toInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else + end = thisObject->impl()->byteLength(); + + RefPtr<ArrayBuffer> newBuffer = thisObject->impl()->slice(begin, end); + if (!newBuffer) + return throwVMError(exec, createOutOfMemoryError(exec)); + + Structure* structure = callee->globalObject()->arrayBufferStructure(); + + JSArrayBuffer* result = JSArrayBuffer::create(exec->vm(), structure, newBuffer); + + return JSValue::encode(result); +} + +const ClassInfo JSArrayBufferPrototype::s_info = { + "ArrayBufferPrototype", &Base::s_info, 0, CREATE_METHOD_TABLE(JSArrayBufferPrototype) +}; + +JSArrayBufferPrototype::JSArrayBufferPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +void JSArrayBufferPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + + JSC_NATIVE_FUNCTION(vm.propertyNames->slice, arrayBufferProtoFuncSlice, DontEnum, 2); +} + +JSArrayBufferPrototype* JSArrayBufferPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + JSArrayBufferPrototype* prototype = + new (NotNull, allocateCell<JSArrayBufferPrototype>(vm.heap)) + JSArrayBufferPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; +} + +Structure* JSArrayBufferPrototype::createStructure( + VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create( + vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSArrayBufferPrototype.h b/Source/JavaScriptCore/runtime/JSArrayBufferPrototype.h new file mode 100644 index 000000000..9acd65bbb --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBufferPrototype.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSArrayBufferPrototype_h +#define JSArrayBufferPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class JSArrayBufferPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + +protected: + JSArrayBufferPrototype(VM&, Structure*); + void finishCreation(VM&, JSGlobalObject*); + +public: + static JSArrayBufferPrototype* create(VM&, JSGlobalObject*, Structure*); + + DECLARE_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); +}; + +} // namespace JSC + +#endif // JSArrayBufferPrototype_h + diff --git a/Source/JavaScriptCore/runtime/JSArrayBufferView.cpp b/Source/JavaScriptCore/runtime/JSArrayBufferView.cpp new file mode 100644 index 000000000..a3398dbaa --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBufferView.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSArrayBufferView.h" + +#include "JSArrayBuffer.h" +#include "JSCInlines.h" +#include "Reject.h" + +namespace JSC { + +const ClassInfo JSArrayBufferView::s_info = { + "ArrayBufferView", &Base::s_info, 0, CREATE_METHOD_TABLE(JSArrayBufferView) +}; + +JSArrayBufferView::ConstructionContext::ConstructionContext( + VM& vm, Structure* structure, uint32_t length, uint32_t elementSize, + InitializationMode mode) + : m_structure(0) + , m_length(length) + , m_butterfly(0) +{ + if (length <= fastSizeLimit) { + // Attempt GC allocation. + void* temp = 0; + size_t size = sizeOf(length, elementSize); + // CopiedSpace only allows non-zero size allocations. + if (size && !vm.heap.tryAllocateStorage(0, size, &temp)) + return; + + m_structure = structure; + m_vector = temp; + m_mode = FastTypedArray; + +#if USE(JSVALUE32_64) + if (mode == ZeroFill) { + uint64_t* asWords = static_cast<uint64_t*>(m_vector); + for (unsigned i = size / sizeof(uint64_t); i--;) + asWords[i] = 0; + } +#endif // USE(JSVALUE32_64) + + return; + } + + // Don't allow a typed array to use more than 2GB. + if (length > static_cast<unsigned>(INT_MAX) / elementSize) + return; + + if (mode == ZeroFill) { + if (!tryFastCalloc(length, elementSize).getValue(m_vector)) + return; + } else { + if (!tryFastMalloc(length * elementSize).getValue(m_vector)) + return; + } + + vm.heap.reportExtraMemoryAllocated(static_cast<size_t>(length) * elementSize); + + m_structure = structure; + m_mode = OversizeTypedArray; +} + +JSArrayBufferView::ConstructionContext::ConstructionContext( + VM& vm, Structure* structure, PassRefPtr<ArrayBuffer> arrayBuffer, + unsigned byteOffset, unsigned length) + : m_structure(structure) + , m_vector(static_cast<uint8_t*>(arrayBuffer->data()) + byteOffset) + , m_length(length) + , m_mode(WastefulTypedArray) +{ + IndexingHeader indexingHeader; + indexingHeader.setArrayBuffer(arrayBuffer.get()); + m_butterfly = Butterfly::create(vm, 0, 0, 0, true, indexingHeader, 0); +} + +JSArrayBufferView::ConstructionContext::ConstructionContext( + Structure* structure, PassRefPtr<ArrayBuffer> arrayBuffer, + unsigned byteOffset, unsigned length, DataViewTag) + : m_structure(structure) + , m_vector(static_cast<uint8_t*>(arrayBuffer->data()) + byteOffset) + , m_length(length) + , m_mode(DataViewMode) + , m_butterfly(0) +{ +} + +JSArrayBufferView::JSArrayBufferView(VM& vm, ConstructionContext& context) + : Base(vm, context.structure(), context.butterfly()) + , m_vector(context.vector()) + , m_length(context.length()) + , m_mode(context.mode()) +{ +} + +void JSArrayBufferView::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + switch (m_mode) { + case FastTypedArray: + return; + case OversizeTypedArray: + vm.heap.addFinalizer(this, finalize); + return; + case WastefulTypedArray: + vm.heap.addReference(this, butterfly()->indexingHeader()->arrayBuffer()); + return; + case DataViewMode: + ASSERT(!butterfly()); + vm.heap.addReference(this, jsCast<JSDataView*>(this)->buffer()); + return; + } + RELEASE_ASSERT_NOT_REACHED(); +} + +bool JSArrayBufferView::getOwnPropertySlot( + JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(object); + if (propertyName == exec->propertyNames().byteOffset) { + slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->byteOffset())); + return true; + } + + if (propertyName == exec->propertyNames().buffer) { + slot.setValue( + thisObject, DontDelete | ReadOnly, exec->vm().m_typedArrayController->toJS( + exec, thisObject->globalObject(), thisObject->buffer())); + return true; + } + + return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +void JSArrayBufferView::put( + JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, + PutPropertySlot& slot) +{ + JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell); + if (propertyName == exec->propertyNames().byteLength + || propertyName == exec->propertyNames().byteOffset + || propertyName == exec->propertyNames().buffer) { + reject(exec, slot.isStrictMode(), "Attempting to write to read-only typed array property."); + return; + } + + Base::put(thisObject, exec, propertyName, value, slot); +} + +bool JSArrayBufferView::defineOwnProperty( + JSObject* object, ExecState* exec, PropertyName propertyName, + const PropertyDescriptor& descriptor, bool shouldThrow) +{ + JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(object); + if (propertyName == exec->propertyNames().byteLength + || propertyName == exec->propertyNames().byteOffset + || propertyName == exec->propertyNames().buffer) + return reject(exec, shouldThrow, "Attempting to define read-only typed array property."); + + return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); +} + +bool JSArrayBufferView::deleteProperty( + JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell); + if (propertyName == exec->propertyNames().byteLength + || propertyName == exec->propertyNames().byteOffset + || propertyName == exec->propertyNames().buffer) + return false; + + return Base::deleteProperty(thisObject, exec, propertyName); +} + +void JSArrayBufferView::getOwnNonIndexPropertyNames( + JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) +{ + JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(object); + + // length/byteOffset/byteLength are DontEnum, at least in Firefox. + if (mode.includeDontEnumProperties()) { + array.add(exec->propertyNames().byteOffset); + array.add(exec->propertyNames().byteLength); + array.add(exec->propertyNames().buffer); + } + + Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode); +} + +void JSArrayBufferView::finalize(JSCell* cell) +{ + JSArrayBufferView* thisObject = static_cast<JSArrayBufferView*>(cell); + ASSERT(thisObject->m_mode == OversizeTypedArray || thisObject->m_mode == WastefulTypedArray); + if (thisObject->m_mode == OversizeTypedArray) + fastFree(thisObject->m_vector); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSArrayBufferView.h b/Source/JavaScriptCore/runtime/JSArrayBufferView.h new file mode 100644 index 000000000..051445299 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBufferView.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSArrayBufferView_h +#define JSArrayBufferView_h + +#include "JSObject.h" + +namespace JSC { + +// This class serves two purposes: +// +// 1) It provides those parts of JSGenericTypedArrayView that don't depend +// on template parameters. +// +// 2) It represents the DOM/WebCore-visible "JSArrayBufferView" type, which +// C++ code uses when it wants to pass around a view of an array buffer +// without concern for the actual type of the view. +// +// These two roles are quite different. (1) is just a matter of optimizing +// compile and link times by having as much code and data as possible not +// be subject to template specialization. (2) is *almost* a matter of +// semantics; indeed at the very least it is a matter of obeying a contract +// that we have with WebCore right now. +// +// One convenient thing that saves us from too much crazy is that +// ArrayBufferView is not instantiable. + +// Typed array views have different modes depending on how big they are and +// whether the user has done anything that requires a separate backing +// buffer or the DOM-specified neutering capabilities. +enum TypedArrayMode { + // Small and fast typed array. B is unused, V points to a vector + // allocated in copied space, and M = FastTypedArray. V's liveness is + // determined entirely by the view's liveness. + FastTypedArray, + + // A large typed array that still attempts not to waste too much + // memory. B is initialized to point to a slot that could hold a + // buffer pointer, V points to a vector allocated using fastCalloc(), + // and M = OversizeTypedArray. V's liveness is determined entirely by + // the view's liveness, and the view will add a finalizer to delete V. + OversizeTypedArray, + + // A typed array that was used in some crazy way. B's IndexingHeader + // is hijacked to contain a reference to the native array buffer. The + // native typed array view points back to the JS view. V points to a + // vector allocated using who-knows-what, and M = WastefulTypedArray. + // The view does not own the vector. + WastefulTypedArray, + + // A data view. B is unused, V points to a vector allocated using who- + // knows-what, and M = DataViewMode. The view does not own the vector. + // There is an extra field (in JSDataView) that points to the + // ArrayBuffer. + DataViewMode +}; + +inline bool hasArrayBuffer(TypedArrayMode mode) +{ + return mode >= WastefulTypedArray; +} + +// When WebCore uses a JSArrayBufferView, it expects to be able to get the native +// ArrayBuffer and little else. This requires slowing down and wasting memory, +// and then accessing things via the Butterfly. When JS uses a JSArrayBufferView +// it is always via the usual methods in the MethodTable, so this class's +// implementation of those has no need to even exist - we could at any time sink +// code into JSGenericTypedArrayView if it was convenient. + +class JSArrayBufferView : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetPropertyNames | OverridesGetOwnPropertySlot; + + static const unsigned fastSizeLimit = 1000; + + static size_t sizeOf(uint32_t length, uint32_t elementSize) + { + return (length * elementSize + sizeof(EncodedJSValue) - 1) + & ~(sizeof(EncodedJSValue) - 1); + } + + static size_t allocationSize(size_t inlineCapacity) + { + ASSERT_UNUSED(inlineCapacity, !inlineCapacity); + return sizeof(JSArrayBufferView); + } + +protected: + class ConstructionContext { + WTF_MAKE_NONCOPYABLE(ConstructionContext); + + public: + enum InitializationMode { ZeroFill, DontInitialize }; + + JS_EXPORT_PRIVATE ConstructionContext(VM&, Structure*, uint32_t length, uint32_t elementSize, InitializationMode = ZeroFill); + + JS_EXPORT_PRIVATE ConstructionContext( + VM&, Structure*, PassRefPtr<ArrayBuffer>, + unsigned byteOffset, unsigned length); + + enum DataViewTag { DataView }; + ConstructionContext( + Structure*, PassRefPtr<ArrayBuffer>, + unsigned byteOffset, unsigned length, DataViewTag); + + bool operator!() const { return !m_structure; } + + Structure* structure() const { return m_structure; } + void* vector() const { return m_vector; } + uint32_t length() const { return m_length; } + TypedArrayMode mode() const { return m_mode; } + Butterfly* butterfly() const { return m_butterfly; } + + private: + Structure* m_structure; + void* m_vector; + uint32_t m_length; + TypedArrayMode m_mode; + Butterfly* m_butterfly; + }; + + JS_EXPORT_PRIVATE JSArrayBufferView(VM&, ConstructionContext&); + JS_EXPORT_PRIVATE void finishCreation(VM&); + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + + static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + +public: + TypedArrayMode mode() const { return m_mode; } + bool hasArrayBuffer() const { return JSC::hasArrayBuffer(mode()); } + + ArrayBuffer* buffer(); + PassRefPtr<ArrayBufferView> impl(); + void neuter(); + + void* vector() { return m_vector; } + unsigned byteOffset(); + unsigned length() const { return m_length; } + + DECLARE_EXPORT_INFO; + + static ptrdiff_t offsetOfVector() { return OBJECT_OFFSETOF(JSArrayBufferView, m_vector); } + static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(JSArrayBufferView, m_length); } + static ptrdiff_t offsetOfMode() { return OBJECT_OFFSETOF(JSArrayBufferView, m_mode); } + +private: + static void finalize(JSCell*); + +protected: + ArrayBuffer* existingBufferInButterfly(); + + void* m_vector; + uint32_t m_length; + TypedArrayMode m_mode; +}; + +} // namespace JSC + +#endif // JSArrayBufferView_h + diff --git a/Source/JavaScriptCore/runtime/JSArrayBufferViewInlines.h b/Source/JavaScriptCore/runtime/JSArrayBufferViewInlines.h new file mode 100644 index 000000000..de4e8587c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayBufferViewInlines.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSArrayBufferViewInlines_h +#define JSArrayBufferViewInlines_h + +#include "ArrayBufferView.h" +#include "JSArrayBufferView.h" +#include "JSDataView.h" + +namespace JSC { + +inline ArrayBuffer* JSArrayBufferView::buffer() +{ + switch (m_mode) { + case WastefulTypedArray: + return existingBufferInButterfly(); + case DataViewMode: + return jsCast<JSDataView*>(this)->buffer(); + default: + return methodTable()->slowDownAndWasteMemory(this); + } +} + +inline ArrayBuffer* JSArrayBufferView::existingBufferInButterfly() +{ + ASSERT(m_mode == WastefulTypedArray); + return butterfly()->indexingHeader()->arrayBuffer(); +} + +inline PassRefPtr<ArrayBufferView> JSArrayBufferView::impl() +{ + return methodTable()->getTypedArrayImpl(this); +} + +inline void JSArrayBufferView::neuter() +{ + ASSERT(hasArrayBuffer()); + m_length = 0; + m_vector = 0; +} + +inline unsigned JSArrayBufferView::byteOffset() +{ + if (!hasArrayBuffer()) + return 0; + + ptrdiff_t delta = + static_cast<uint8_t*>(m_vector) - static_cast<uint8_t*>(buffer()->data()); + + unsigned result = static_cast<unsigned>(delta); + ASSERT(static_cast<ptrdiff_t>(result) == delta); + return result; +} + +} // namespace JSC + +#endif // JSArrayBufferViewInlines_h + diff --git a/Source/JavaScriptCore/runtime/JSArrayIterator.cpp b/Source/JavaScriptCore/runtime/JSArrayIterator.cpp new file mode 100644 index 000000000..0abc9d120 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayIterator.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSArrayIterator.h" + +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSArrayIterator::s_info = { "Array Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(JSArrayIterator) }; + +void JSArrayIterator::finishCreation(VM& vm, JSGlobalObject*, ArrayIterationKind kind, JSObject* iteratedObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + + putDirect(vm, vm.propertyNames->iteratedObjectPrivateName, iteratedObject); + putDirect(vm, vm.propertyNames->arrayIteratorNextIndexPrivateName, jsNumber(0)); + putDirect(vm, vm.propertyNames->arrayIterationKindPrivateName, jsNumber(kind)); +} + +ArrayIterationKind JSArrayIterator::kind(ExecState* exec) const +{ + JSValue kindValue = getDirect(exec->vm(), exec->vm().propertyNames->arrayIterationKindPrivateName); + return static_cast<ArrayIterationKind>(kindValue.asInt32()); +} + +JSValue JSArrayIterator::iteratedValue(ExecState* exec) const +{ + return getDirect(exec->vm(), exec->vm().propertyNames->iteratedObjectPrivateName); +} + +JSArrayIterator* JSArrayIterator::clone(ExecState* exec) +{ + VM& vm = exec->vm(); + JSValue iteratedObject = getDirect(vm, vm.propertyNames->iteratedObjectPrivateName); + JSValue nextIndex = getDirect(vm, vm.propertyNames->arrayIteratorNextIndexPrivateName); + + auto clone = JSArrayIterator::create(exec, exec->callee()->globalObject()->arrayIteratorStructure(), kind(exec), asObject(iteratedObject)); + clone->putDirect(vm, vm.propertyNames->arrayIteratorNextIndexPrivateName, nextIndex); + return clone; +} + +} diff --git a/Source/JavaScriptCore/runtime/JSArrayIterator.h b/Source/JavaScriptCore/runtime/JSArrayIterator.h new file mode 100644 index 000000000..0166bd2e5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSArrayIterator.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSArrayIterator_h +#define JSArrayIterator_h + +#include "JSObject.h" + +namespace JSC { + +enum ArrayIterationKind : uint32_t { + ArrayIterateKey, + ArrayIterateValue, + ArrayIterateKeyValue +}; + +class JSArrayIterator : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSArrayIterator* create(ExecState* exec, Structure* structure, ArrayIterationKind kind, JSObject* iteratedObject) + { + VM& vm = exec->vm(); + JSArrayIterator* instance = new (NotNull, allocateCell<JSArrayIterator>(vm.heap)) JSArrayIterator(vm, structure); + instance->finishCreation(vm, structure->globalObject(), kind, iteratedObject); + return instance; + } + + ArrayIterationKind kind(ExecState*) const; + JSValue iteratedValue(ExecState*) const; + JSArrayIterator* clone(ExecState*); + + using JSNonFinalObject::arrayStorageOrNull; +private: + JSArrayIterator(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(VM&, JSGlobalObject*, ArrayIterationKind, JSObject* iteratedObject); +}; + +} + +#endif // !defined(JSArrayIterator_h) diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp new file mode 100644 index 000000000..8c5cc2ed3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2011 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSBoundFunction.h" + +#include "GetterSetter.h" +#include "JSGlobalObject.h" +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(JSBoundFunction) }; + +EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec) +{ + JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee()); + + ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true! + JSArray* boundArgs = asArray(boundFunction->boundArgs()); + + MarkedArgumentBuffer args; + for (unsigned i = 0; i < boundArgs->length(); ++i) + args.append(boundArgs->getIndexQuickly(i)); + for (unsigned i = 0; i < exec->argumentCount(); ++i) + args.append(exec->uncheckedArgument(i)); + + JSObject* targetFunction = boundFunction->targetFunction(); + CallData callData; + CallType callType = getCallData(targetFunction, callData); + ASSERT(callType != CallTypeNone); + return JSValue::encode(call(exec, targetFunction, callType, callData, boundFunction->boundThis(), args)); +} + +EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState* exec) +{ + JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee()); + + ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true! + JSArray* boundArgs = asArray(boundFunction->boundArgs()); + + MarkedArgumentBuffer args; + for (unsigned i = 0; i < boundArgs->length(); ++i) + args.append(boundArgs->getIndexQuickly(i)); + for (unsigned i = 0; i < exec->argumentCount(); ++i) + args.append(exec->uncheckedArgument(i)); + + JSObject* targetFunction = boundFunction->targetFunction(); + ConstructData constructData; + ConstructType constructType = getConstructData(targetFunction, constructData); + ASSERT(constructType != ConstructTypeNone); + return JSValue::encode(construct(exec, targetFunction, constructType, constructData, args)); +} + +JSBoundFunction* JSBoundFunction::create(VM& vm, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int length, const String& name) +{ + ConstructData constructData; + ConstructType constructType = JSC::getConstructData(targetFunction, constructData); + bool canConstruct = constructType != ConstructTypeNone; + NativeExecutable* executable = vm.getHostFunction(boundFunctionCall, canConstruct ? boundFunctionConstruct : callHostFunctionAsConstructor); + JSBoundFunction* function = new (NotNull, allocateCell<JSBoundFunction>(vm.heap)) JSBoundFunction(vm, globalObject, globalObject->boundFunctionStructure(), targetFunction, boundThis, boundArgs); + + function->finishCreation(vm, executable, length, name); + return function; +} + +bool JSBoundFunction::customHasInstance(JSObject* object, ExecState* exec, JSValue value) +{ + return jsCast<JSBoundFunction*>(object)->m_targetFunction->hasInstance(exec, value); +} + +JSBoundFunction::JSBoundFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs) + : Base(vm, globalObject, structure) + , m_targetFunction(vm, this, targetFunction) + , m_boundThis(vm, this, boundThis) + , m_boundArgs(vm, this, boundArgs) +{ +} + +void JSBoundFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name) +{ + Base::finishCreation(vm, executable, length, name); + ASSERT(inherits(info())); + + putDirectNonIndexAccessor(vm, vm.propertyNames->arguments, globalObject()->throwTypeErrorGetterSetter(vm), DontDelete | DontEnum | Accessor); + putDirectNonIndexAccessor(vm, vm.propertyNames->caller, globalObject()->throwTypeErrorGetterSetter(vm), DontDelete | DontEnum | Accessor); +} + +void JSBoundFunction::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSBoundFunction* thisObject = jsCast<JSBoundFunction*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_targetFunction); + visitor.append(&thisObject->m_boundThis); + visitor.append(&thisObject->m_boundArgs); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.h b/Source/JavaScriptCore/runtime/JSBoundFunction.h new file mode 100644 index 000000000..af2a6323d --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSBoundFunction_h +#define JSBoundFunction_h + +#include "JSFunction.h" + +namespace JSC { + +EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState*); +EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState*); + +class JSBoundFunction : public JSFunction { +public: + typedef JSFunction Base; + const static unsigned StructureFlags = OverridesHasInstance | Base::StructureFlags; + + static JSBoundFunction* create(VM&, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int, const String&); + + static bool customHasInstance(JSObject*, ExecState*, JSValue); + + JSObject* targetFunction() { return m_targetFunction.get(); } + JSValue boundThis() { return m_boundThis.get(); } + JSValue boundArgs() { return m_boundArgs.get(); } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + ASSERT(globalObject); + return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info()); + } + + DECLARE_INFO; + +protected: + static void visitChildren(JSCell*, SlotVisitor&); + +private: + JSBoundFunction(VM&, JSGlobalObject*, Structure*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs); + + void finishCreation(VM&, NativeExecutable*, int, const String&); + + WriteBarrier<JSObject> m_targetFunction; + WriteBarrier<Unknown> m_boundThis; + WriteBarrier<Unknown> m_boundArgs; +}; + +} // namespace JSC + +#endif // JSFunction_h diff --git a/Source/JavaScriptCore/runtime/JSCInlines.h b/Source/JavaScriptCore/runtime/JSCInlines.h new file mode 100644 index 000000000..e9fabb53e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCInlines.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSCInlines_h +#define JSCInlines_h + +// This file's only purpose is to collect commonly used *Inlines.h files, so that you don't +// have to include all of them in every .cpp file. Instead you just include this. It's good +// style to make sure that every .cpp file includes JSCInlines.h. +// +// JSC should never include this file, or any *Inline.h file, from interface headers, since +// this could lead to a circular dependency. +// +// WebCore, or any other downstream client of JSC, is allowed to include this file in headers. +// In fact, it can make a lot of sense: outside of JSC, this file becomes a kind of umbrella +// header that pulls in most (all?) of the interesting things in JSC. + +#include "CallFrameInlines.h" +#include "ExceptionHelpers.h" +#include "GCIncomingRefCountedInlines.h" +#include "HeapInlines.h" +#include "IdentifierInlines.h" +#include "Interpreter.h" +#include "JSArrayBufferViewInlines.h" +#include "JSCJSValueInlines.h" +#include "JSFunctionInlines.h" +#include "JSProxy.h" +#include "JSString.h" +#include "Operations.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" +#include "WeakGCMapInlines.h" + +#endif // JSCInlines_h diff --git a/Source/JavaScriptCore/runtime/JSCJSValue.cpp b/Source/JavaScriptCore/runtime/JSCJSValue.cpp new file mode 100644 index 000000000..9e0aab177 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCJSValue.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "JSCJSValue.h" + +#include "BooleanConstructor.h" +#include "BooleanPrototype.h" +#include "CustomGetterSetter.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "GetterSetter.h" +#include "JSCJSValueInlines.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSNotAnObject.h" +#include "NumberObject.h" +#include "StructureInlines.h" +#include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> + +namespace JSC { + +// ECMA 9.4 +double JSValue::toInteger(ExecState* exec) const +{ + if (isInt32()) + return asInt32(); + double d = toNumber(exec); + return std::isnan(d) ? 0.0 : trunc(d); +} + +double JSValue::toIntegerPreserveNaN(ExecState* exec) const +{ + if (isInt32()) + return asInt32(); + return trunc(toNumber(exec)); +} + +double JSValue::toNumberSlowCase(ExecState* exec) const +{ + ASSERT(!isInt32() && !isDouble()); + if (isCell()) + return asCell()->toNumber(exec); + if (isTrue()) + return 1.0; + return isUndefined() ? PNaN : 0; // null and false both convert to 0. +} + +JSObject* JSValue::toObjectSlowCase(ExecState* exec, JSGlobalObject* globalObject) const +{ + ASSERT(!isCell()); + + if (isInt32() || isDouble()) + return constructNumber(exec, globalObject, asValue()); + if (isTrue() || isFalse()) + return constructBooleanFromImmediateBoolean(exec, globalObject, asValue()); + + ASSERT(isUndefinedOrNull()); + VM& vm = exec->vm(); + vm.throwException(exec, createNotAnObjectError(exec, *this)); + return JSNotAnObject::create(vm); +} + +JSValue JSValue::toThisSlowCase(ExecState* exec, ECMAMode ecmaMode) const +{ + ASSERT(!isCell()); + + if (ecmaMode == StrictMode) + return *this; + + if (isInt32() || isDouble()) + return constructNumber(exec, exec->lexicalGlobalObject(), asValue()); + if (isTrue() || isFalse()) + return constructBooleanFromImmediateBoolean(exec, exec->lexicalGlobalObject(), asValue()); + ASSERT(isUndefinedOrNull()); + return exec->globalThisValue(); +} + +JSObject* JSValue::synthesizePrototype(ExecState* exec) const +{ + if (isCell()) { + if (isString()) + return exec->lexicalGlobalObject()->stringPrototype(); + ASSERT(isSymbol()); + return exec->lexicalGlobalObject()->symbolPrototype(); + } + + if (isNumber()) + return exec->lexicalGlobalObject()->numberPrototype(); + if (isBoolean()) + return exec->lexicalGlobalObject()->booleanPrototype(); + + ASSERT(isUndefinedOrNull()); + VM& vm = exec->vm(); + vm.throwException(exec, createNotAnObjectError(exec, *this)); + return JSNotAnObject::create(vm); +} + +// ECMA 8.7.2 +void JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + VM& vm = exec->vm(); + + if (Optional<uint32_t> index = parseIndex(propertyName)) { + putToPrimitiveByIndex(exec, index.value(), value, slot.isStrictMode()); + return; + } + + // Check if there are any setters or getters in the prototype chain + JSObject* obj = synthesizePrototype(exec); + JSValue prototype; + if (propertyName != exec->propertyNames().underscoreProto) { + for (; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { + prototype = obj->prototype(); + if (prototype.isNull()) { + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + } + } + + for (; ; obj = asObject(prototype)) { + unsigned attributes; + PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes); + if (offset != invalidOffset) { + if (attributes & ReadOnly) { + if (slot.isStrictMode()) + exec->vm().throwException(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); + return; + } + + JSValue gs = obj->getDirect(offset); + if (gs.isGetterSetter()) { + callSetter(exec, *this, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode); + return; + } + + if (gs.isCustomGetterSetter()) { + callCustomSetter(exec, gs, obj, slot.thisValue(), value); + return; + } + + // If there's an existing property on the object or one of its + // prototypes it should be replaced, so break here. + break; + } + + prototype = obj->prototype(); + if (prototype.isNull()) + break; + } + + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; +} + +void JSValue::putToPrimitiveByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) +{ + if (propertyName > MAX_ARRAY_INDEX) { + PutPropertySlot slot(*this, shouldThrow); + putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot); + return; + } + + if (synthesizePrototype(exec)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, *this, propertyName, value, shouldThrow)) + return; + + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); +} + +void JSValue::dump(PrintStream& out) const +{ + dumpInContext(out, 0); +} + +void JSValue::dumpInContext(PrintStream& out, DumpContext* context) const +{ + dumpInContextAssumingStructure( + out, context, (!!*this && isCell()) ? asCell()->structure() : nullptr); +} + +void JSValue::dumpInContextAssumingStructure( + PrintStream& out, DumpContext* context, Structure* structure) const +{ + if (!*this) + out.print("<JSValue()>"); + else if (isInt32()) + out.printf("Int32: %d", asInt32()); + else if (isDouble()) { +#if USE(JSVALUE64) + out.printf("Double: %lld, %lf", (long long)reinterpretDoubleToInt64(asDouble()), asDouble()); +#else + union { + double asDouble; + uint32_t asTwoInt32s[2]; + } u; + u.asDouble = asDouble(); + out.printf("Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble()); +#endif + } else if (isCell()) { + if (structure->classInfo()->isSubClassOf(JSString::info())) { + JSString* string = jsCast<JSString*>(asCell()); + out.print("String"); + if (string->isRope()) + out.print(" (rope)"); + const StringImpl* impl = string->tryGetValueImpl(); + if (impl) { + if (impl->isAtomic()) + out.print(" (atomic)"); + if (impl->isAtomic()) + out.print(" (identifier)"); + if (impl->isSymbol()) + out.print(" (symbol)"); + } else + out.print(" (unresolved)"); + out.print(": ", impl); + } else if (structure->classInfo()->isSubClassOf(Structure::info())) + out.print("Structure: ", inContext(*jsCast<Structure*>(asCell()), context)); + else { + out.print("Cell: ", RawPointer(asCell())); + out.print(" (", inContext(*structure, context), ")"); + } +#if USE(JSVALUE64) + out.print(", ID: ", asCell()->structureID()); +#endif + } else if (isTrue()) + out.print("True"); + else if (isFalse()) + out.print("False"); + else if (isNull()) + out.print("Null"); + else if (isUndefined()) + out.print("Undefined"); + else + out.print("INVALID"); +} + +void JSValue::dumpForBacktrace(PrintStream& out) const +{ + if (!*this) + out.print("<JSValue()>"); + else if (isInt32()) + out.printf("%d", asInt32()); + else if (isDouble()) + out.printf("%lf", asDouble()); + else if (isCell()) { + if (asCell()->inherits(JSString::info())) { + JSString* string = jsCast<JSString*>(asCell()); + const StringImpl* impl = string->tryGetValueImpl(); + if (impl) + out.print("\"", impl, "\""); + else + out.print("(unresolved string)"); + } else if (asCell()->inherits(Structure::info())) { + out.print("Structure[ ", asCell()->structure()->classInfo()->className); +#if USE(JSVALUE64) + out.print(" ID: ", asCell()->structureID()); +#endif + out.print("]: ", RawPointer(asCell())); + } else { + out.print("Cell[", asCell()->structure()->classInfo()->className); +#if USE(JSVALUE64) + out.print(" ID: ", asCell()->structureID()); +#endif + out.print("]: ", RawPointer(asCell())); + } + } else if (isTrue()) + out.print("True"); + else if (isFalse()) + out.print("False"); + else if (isNull()) + out.print("Null"); + else if (isUndefined()) + out.print("Undefined"); + else + out.print("INVALID"); +} + +// This in the ToInt32 operation is defined in section 9.5 of the ECMA-262 spec. +// Note that this operation is identical to ToUInt32 other than to interpretation +// of the resulting bit-pattern (as such this metod is also called to implement +// ToUInt32). +// +// The operation can be descibed as round towards zero, then select the 32 least +// bits of the resulting value in 2s-complement representation. +int32_t toInt32(double number) +{ + int64_t bits = WTF::bitwise_cast<int64_t>(number); + int32_t exp = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff; + + // If exponent < 0 there will be no bits to the left of the decimal point + // after rounding; if the exponent is > 83 then no bits of precision can be + // left in the low 32-bit range of the result (IEEE-754 doubles have 52 bits + // of fractional precision). + // Note this case handles 0, -0, and all infinte, NaN, & denormal value. + if (exp < 0 || exp > 83) + return 0; + + // Select the appropriate 32-bits from the floating point mantissa. If the + // exponent is 52 then the bits we need to select are already aligned to the + // lowest bits of the 64-bit integer representation of tghe number, no need + // to shift. If the exponent is greater than 52 we need to shift the value + // left by (exp - 52), if the value is less than 52 we need to shift right + // accordingly. + int32_t result = (exp > 52) + ? static_cast<int32_t>(bits << (exp - 52)) + : static_cast<int32_t>(bits >> (52 - exp)); + + // IEEE-754 double precision values are stored omitting an implicit 1 before + // the decimal point; we need to reinsert this now. We may also the shifted + // invalid bits into the result that are not a part of the mantissa (the sign + // and exponent bits from the floatingpoint representation); mask these out. + if (exp < 32) { + int32_t missingOne = 1 << exp; + result &= missingOne - 1; + result += missingOne; + } + + // If the input value was negative (we could test either 'number' or 'bits', + // but testing 'bits' is likely faster) invert the result appropriately. + return bits < 0 ? -result : result; +} + +bool JSValue::isValidCallee() +{ + return asObject(asCell())->globalObject(); +} + +JSString* JSValue::toStringSlowCase(ExecState* exec) const +{ + VM& vm = exec->vm(); + ASSERT(!isString()); + if (isInt32()) { + auto integer = asInt32(); + if (static_cast<unsigned>(integer) <= 9) + return vm.smallStrings.singleCharacterString(integer + '0'); + return jsNontrivialString(&vm, vm.numericStrings.add(integer)); + } + if (isDouble()) + return jsString(&vm, vm.numericStrings.add(asDouble())); + if (isTrue()) + return vm.smallStrings.trueString(); + if (isFalse()) + return vm.smallStrings.falseString(); + if (isNull()) + return vm.smallStrings.nullString(); + if (isUndefined()) + return vm.smallStrings.undefinedString(); + if (isSymbol()) { + throwTypeError(exec); + return jsEmptyString(exec); + } + + ASSERT(isCell()); + JSValue value = asCell()->toPrimitive(exec, PreferString); + if (exec->hadException()) + return jsEmptyString(exec); + ASSERT(!value.isObject()); + return value.toString(exec); +} + +String JSValue::toWTFStringSlowCase(ExecState* exec) const +{ + VM& vm = exec->vm(); + if (isInt32()) + return vm.numericStrings.add(asInt32()); + if (isDouble()) + return vm.numericStrings.add(asDouble()); + if (isTrue()) + return vm.propertyNames->trueKeyword.string(); + if (isFalse()) + return vm.propertyNames->falseKeyword.string(); + if (isNull()) + return vm.propertyNames->nullKeyword.string(); + if (isUndefined()) + return vm.propertyNames->undefinedKeyword.string(); + return toString(exec)->value(exec); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSCJSValue.h b/Source/JavaScriptCore/runtime/JSCJSValue.h new file mode 100644 index 000000000..b7eaad2da --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCJSValue.h @@ -0,0 +1,577 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2012, 2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSCJSValue_h +#define JSCJSValue_h + +#include "JSExportMacros.h" +#include "PureNaN.h" +#include <functional> +#include <math.h> +#include <stddef.h> +#include <stdint.h> +#include <wtf/Assertions.h> +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/HashTraits.h> +#include <wtf/MathExtras.h> +#include <wtf/MediaTime.h> +#include <wtf/StdLibExtras.h> +#include <wtf/TriState.h> + +namespace JSC { + +class AssemblyHelpers; +class ExecState; +class JSCell; +class JSValueSource; +class VM; +class JSGlobalObject; +class JSObject; +class JSString; +class Identifier; +class PropertyName; +class PropertySlot; +class PutPropertySlot; +class Structure; +#if ENABLE(DFG_JIT) +namespace DFG { +class JITCompiler; +class OSRExitCompiler; +class SpeculativeJIT; +} +#endif +#if !ENABLE(JIT) +namespace LLInt { +class CLoop; +} +#endif + +struct ClassInfo; +struct DumpContext; +struct Instruction; +struct MethodTable; + +template <class T> class WriteBarrierBase; + +enum PreferredPrimitiveType { NoPreference, PreferNumber, PreferString }; +enum ECMAMode { StrictMode, NotStrictMode }; + +typedef int64_t EncodedJSValue; + +union EncodedValueDescriptor { + int64_t asInt64; +#if USE(JSVALUE32_64) + double asDouble; +#elif USE(JSVALUE64) + JSCell* ptr; +#endif + +#if CPU(BIG_ENDIAN) + struct { + int32_t tag; + int32_t payload; + } asBits; +#else + struct { + int32_t payload; + int32_t tag; + } asBits; +#endif +}; + +#define TagOffset (OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)) +#define PayloadOffset (OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)) + +#if USE(JSVALUE64) +#define CellPayloadOffset 0 +#else +#define CellPayloadOffset PayloadOffset +#endif + +enum WhichValueWord { + TagWord, + PayloadWord +}; + +// This implements ToInt32, defined in ECMA-262 9.5. +JS_EXPORT_PRIVATE int32_t toInt32(double); + +// This implements ToUInt32, defined in ECMA-262 9.6. +inline uint32_t toUInt32(double number) +{ + // As commented in the spec, the operation of ToInt32 and ToUint32 only differ + // in how the result is interpreted; see NOTEs in sections 9.5 and 9.6. + return toInt32(number); +} + +int64_t tryConvertToInt52(double); +bool isInt52(double); + +enum class SourceCodeRepresentation { + Other, + Integer, + Double +}; + +class JSValue { + friend struct EncodedJSValueHashTraits; + friend struct EncodedJSValueWithRepresentationHashTraits; + friend class AssemblyHelpers; + friend class JIT; + friend class JITSlowPathCall; + friend class JITStubs; + friend class JITStubCall; + friend class JSInterfaceJIT; + friend class JSValueSource; + friend class SpecializedThunkJIT; +#if ENABLE(DFG_JIT) + friend class DFG::JITCompiler; + friend class DFG::OSRExitCompiler; + friend class DFG::SpeculativeJIT; +#endif +#if !ENABLE(JIT) + friend class LLInt::CLoop; +#endif + +public: +#if USE(JSVALUE32_64) + enum { Int32Tag = 0xffffffff }; + enum { BooleanTag = 0xfffffffe }; + enum { NullTag = 0xfffffffd }; + enum { UndefinedTag = 0xfffffffc }; + enum { CellTag = 0xfffffffb }; + enum { EmptyValueTag = 0xfffffffa }; + enum { DeletedValueTag = 0xfffffff9 }; + + enum { LowestTag = DeletedValueTag }; +#endif + + static EncodedJSValue encode(JSValue); + static JSValue decode(EncodedJSValue); + + enum JSNullTag { JSNull }; + enum JSUndefinedTag { JSUndefined }; + enum JSTrueTag { JSTrue }; + enum JSFalseTag { JSFalse }; + enum EncodeAsDoubleTag { EncodeAsDouble }; + + JSValue(); + JSValue(JSNullTag); + JSValue(JSUndefinedTag); + JSValue(JSTrueTag); + JSValue(JSFalseTag); + JSValue(JSCell* ptr); + JSValue(const JSCell* ptr); + + // Numbers + JSValue(EncodeAsDoubleTag, double); + explicit JSValue(double); + explicit JSValue(char); + explicit JSValue(unsigned char); + explicit JSValue(short); + explicit JSValue(unsigned short); + explicit JSValue(int); + explicit JSValue(unsigned); + explicit JSValue(long); + explicit JSValue(unsigned long); + explicit JSValue(long long); + explicit JSValue(unsigned long long); + + explicit operator bool() const; + bool operator==(const JSValue& other) const; + bool operator!=(const JSValue& other) const; + + bool isInt32() const; + bool isUInt32() const; + bool isDouble() const; + bool isTrue() const; + bool isFalse() const; + + int32_t asInt32() const; + uint32_t asUInt32() const; + int64_t asMachineInt() const; + double asDouble() const; + bool asBoolean() const; + double asNumber() const; + + int32_t asInt32ForArithmetic() const; // Boolean becomes an int, but otherwise like asInt32(). + + // Querying the type. + bool isEmpty() const; + bool isFunction() const; + bool isUndefined() const; + bool isNull() const; + bool isUndefinedOrNull() const; + bool isBoolean() const; + bool isMachineInt() const; + bool isNumber() const; + bool isString() const; + bool isSymbol() const; + bool isPrimitive() const; + bool isGetterSetter() const; + bool isCustomGetterSetter() const; + bool isObject() const; + bool inherits(const ClassInfo*) const; + + // Extracting the value. + bool getString(ExecState*, WTF::String&) const; + WTF::String getString(ExecState*) const; // null string if not a string + JSObject* getObject() const; // 0 if not an object + + // Extracting integer values. + bool getUInt32(uint32_t&) const; + + // Basic conversions. + JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&); + + bool toBoolean(ExecState*) const; + TriState pureToBoolean() const; + + // toNumber conversion is expected to be side effect free if an exception has + // been set in the ExecState already. + double toNumber(ExecState*) const; + JSString* toString(ExecState*) const; + Identifier toPropertyKey(ExecState*) const; + WTF::String toWTFString(ExecState*) const; + JSObject* toObject(ExecState*) const; + JSObject* toObject(ExecState*, JSGlobalObject*) const; + + // Integer conversions. + JS_EXPORT_PRIVATE double toInteger(ExecState*) const; + JS_EXPORT_PRIVATE double toIntegerPreserveNaN(ExecState*) const; + int32_t toInt32(ExecState*) const; + uint32_t toUInt32(ExecState*) const; + + // Floating point conversions (this is a convenience function for WebCore; + // single precision float is not a representation used in JS or JSC). + float toFloat(ExecState* exec) const { return static_cast<float>(toNumber(exec)); } + + // Object operations, with the toObject operation included. + JSValue get(ExecState*, PropertyName) const; + JSValue get(ExecState*, PropertyName, PropertySlot&) const; + JSValue get(ExecState*, unsigned propertyName) const; + JSValue get(ExecState*, unsigned propertyName, PropertySlot&) const; + + bool getPropertySlot(ExecState*, PropertyName, PropertySlot&) const; + + void put(ExecState*, PropertyName, JSValue, PutPropertySlot&); + JS_EXPORT_PRIVATE void putToPrimitive(ExecState*, PropertyName, JSValue, PutPropertySlot&); + JS_EXPORT_PRIVATE void putToPrimitiveByIndex(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + void putByIndex(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + + JSValue toThis(ExecState*, ECMAMode) const; + + static bool equal(ExecState*, JSValue v1, JSValue v2); + static bool equalSlowCase(ExecState*, JSValue v1, JSValue v2); + static bool equalSlowCaseInline(ExecState*, JSValue v1, JSValue v2); + static bool strictEqual(ExecState*, JSValue v1, JSValue v2); + static bool strictEqualSlowCase(ExecState*, JSValue v1, JSValue v2); + static bool strictEqualSlowCaseInline(ExecState*, JSValue v1, JSValue v2); + static TriState pureStrictEqual(JSValue v1, JSValue v2); + + bool isCell() const; + JSCell* asCell() const; + JS_EXPORT_PRIVATE bool isValidCallee(); + + JSValue structureOrUndefined() const; + + JS_EXPORT_PRIVATE void dump(PrintStream&) const; + void dumpInContext(PrintStream&, DumpContext*) const; + void dumpInContextAssumingStructure(PrintStream&, DumpContext*, Structure*) const; + void dumpForBacktrace(PrintStream&) const; + + JS_EXPORT_PRIVATE JSObject* synthesizePrototype(ExecState*) const; + bool requireObjectCoercible(ExecState*) const; + + // Constants used for Int52. Int52 isn't part of JSValue right now, but JSValues may be + // converted to Int52s and back again. + static const unsigned numberOfInt52Bits = 52; + static const int64_t notInt52 = static_cast<int64_t>(1) << numberOfInt52Bits; + static const unsigned int52ShiftAmount = 12; + + static ptrdiff_t offsetOfPayload() { return OBJECT_OFFSETOF(JSValue, u.asBits.payload); } + static ptrdiff_t offsetOfTag() { return OBJECT_OFFSETOF(JSValue, u.asBits.tag); } + +#if USE(JSVALUE32_64) + /* + * On 32-bit platforms USE(JSVALUE32_64) should be defined, and we use a NaN-encoded + * form for immediates. + * + * The encoding makes use of unused NaN space in the IEEE754 representation. Any value + * with the top 13 bits set represents a QNaN (with the sign bit set). QNaN values + * can encode a 51-bit payload. Hardware produced and C-library payloads typically + * have a payload of zero. We assume that non-zero payloads are available to encode + * pointer and integer values. Since any 64-bit bit pattern where the top 15 bits are + * all set represents a NaN with a non-zero payload, we can use this space in the NaN + * ranges to encode other values (however there are also other ranges of NaN space that + * could have been selected). + * + * For JSValues that do not contain a double value, the high 32 bits contain the tag + * values listed in the enums below, which all correspond to NaN-space. In the case of + * cell, integer and bool values the lower 32 bits (the 'payload') contain the pointer + * integer or boolean value; in the case of all other tags the payload is 0. + */ + uint32_t tag() const; + int32_t payload() const; + +#if !ENABLE(JIT) + // This should only be used by the LLInt C Loop interpreter who needs + // synthesize JSValue from its "register"s holding tag and payload + // values. + explicit JSValue(int32_t tag, int32_t payload); +#endif + +#elif USE(JSVALUE64) + /* + * On 64-bit platforms USE(JSVALUE64) should be defined, and we use a NaN-encoded + * form for immediates. + * + * The encoding makes use of unused NaN space in the IEEE754 representation. Any value + * with the top 13 bits set represents a QNaN (with the sign bit set). QNaN values + * can encode a 51-bit payload. Hardware produced and C-library payloads typically + * have a payload of zero. We assume that non-zero payloads are available to encode + * pointer and integer values. Since any 64-bit bit pattern where the top 15 bits are + * all set represents a NaN with a non-zero payload, we can use this space in the NaN + * ranges to encode other values (however there are also other ranges of NaN space that + * could have been selected). + * + * This range of NaN space is represented by 64-bit numbers begining with the 16-bit + * hex patterns 0xFFFE and 0xFFFF - we rely on the fact that no valid double-precision + * numbers will fall in these ranges. + * + * The top 16-bits denote the type of the encoded JSValue: + * + * Pointer { 0000:PPPP:PPPP:PPPP + * / 0001:****:****:**** + * Double { ... + * \ FFFE:****:****:**** + * Integer { FFFF:0000:IIII:IIII + * + * The scheme we have implemented encodes double precision values by performing a + * 64-bit integer addition of the value 2^48 to the number. After this manipulation + * no encoded double-precision value will begin with the pattern 0x0000 or 0xFFFF. + * Values must be decoded by reversing this operation before subsequent floating point + * operations may be peformed. + * + * 32-bit signed integers are marked with the 16-bit tag 0xFFFF. + * + * The tag 0x0000 denotes a pointer, or another form of tagged immediate. Boolean, + * null and undefined values are represented by specific, invalid pointer values: + * + * False: 0x06 + * True: 0x07 + * Undefined: 0x0a + * Null: 0x02 + * + * These values have the following properties: + * - Bit 1 (TagBitTypeOther) is set for all four values, allowing real pointers to be + * quickly distinguished from all immediate values, including these invalid pointers. + * - With bit 3 is masked out (TagBitUndefined) Undefined and Null share the + * same value, allowing null & undefined to be quickly detected. + * + * No valid JSValue will have the bit pattern 0x0, this is used to represent array + * holes, and as a C++ 'no value' result (e.g. JSValue() has an internal value of 0). + */ + + // These values are #defines since using static const integers here is a ~1% regression! + + // This value is 2^48, used to encode doubles such that the encoded value will begin + // with a 16-bit pattern within the range 0x0001..0xFFFE. + #define DoubleEncodeOffset 0x1000000000000ll + // If all bits in the mask are set, this indicates an integer number, + // if any but not all are set this value is a double precision number. + #define TagTypeNumber 0xffff000000000000ll + + // All non-numeric (bool, null, undefined) immediates have bit 2 set. + #define TagBitTypeOther 0x2ll + #define TagBitBool 0x4ll + #define TagBitUndefined 0x8ll + // Combined integer value for non-numeric immediates. + #define ValueFalse (TagBitTypeOther | TagBitBool | false) + #define ValueTrue (TagBitTypeOther | TagBitBool | true) + #define ValueUndefined (TagBitTypeOther | TagBitUndefined) + #define ValueNull (TagBitTypeOther) + + // TagMask is used to check for all types of immediate values (either number or 'other'). + #define TagMask (TagTypeNumber | TagBitTypeOther) + + // These special values are never visible to JavaScript code; Empty is used to represent + // Array holes, and for uninitialized JSValues. Deleted is used in hash table code. + // These values would map to cell types in the JSValue encoding, but not valid GC cell + // pointer should have either of these values (Empty is null, deleted is at an invalid + // alignment for a GC cell, and in the zero page). + #define ValueEmpty 0x0ll + #define ValueDeleted 0x4ll +#endif + +private: + template <class T> JSValue(WriteBarrierBase<T>); + + enum HashTableDeletedValueTag { HashTableDeletedValue }; + JSValue(HashTableDeletedValueTag); + + inline const JSValue asValue() const { return *this; } + JS_EXPORT_PRIVATE double toNumberSlowCase(ExecState*) const; + JS_EXPORT_PRIVATE JSString* toStringSlowCase(ExecState*) const; + JS_EXPORT_PRIVATE WTF::String toWTFStringSlowCase(ExecState*) const; + JS_EXPORT_PRIVATE JSObject* toObjectSlowCase(ExecState*, JSGlobalObject*) const; + JS_EXPORT_PRIVATE JSValue toThisSlowCase(ExecState*, ECMAMode) const; + + EncodedValueDescriptor u; +}; + +typedef IntHash<EncodedJSValue> EncodedJSValueHash; + +#if USE(JSVALUE32_64) +struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> { + static const bool emptyValueIsZero = false; + static EncodedJSValue emptyValue() { return JSValue::encode(JSValue()); } + static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } + static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } +}; +#else +struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> { + static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } + static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } +}; +#endif + +typedef std::pair<EncodedJSValue, SourceCodeRepresentation> EncodedJSValueWithRepresentation; + +struct EncodedJSValueWithRepresentationHashTraits : HashTraits<EncodedJSValueWithRepresentation> { + static const bool emptyValueIsZero = false; + static EncodedJSValueWithRepresentation emptyValue() { return std::make_pair(JSValue::encode(JSValue()), SourceCodeRepresentation::Other); } + static void constructDeletedValue(EncodedJSValueWithRepresentation& slot) { slot = std::make_pair(JSValue::encode(JSValue(JSValue::HashTableDeletedValue)), SourceCodeRepresentation::Other); } + static bool isDeletedValue(EncodedJSValueWithRepresentation value) { return value == std::make_pair(JSValue::encode(JSValue(JSValue::HashTableDeletedValue)), SourceCodeRepresentation::Other); } +}; + +struct EncodedJSValueWithRepresentationHash { + static unsigned hash(const EncodedJSValueWithRepresentation& value) + { + return WTF::pairIntHash(EncodedJSValueHash::hash(value.first), IntHash<SourceCodeRepresentation>::hash(value.second)); + } + static bool equal(const EncodedJSValueWithRepresentation& a, const EncodedJSValueWithRepresentation& b) + { + return a == b; + } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +// Stand-alone helper functions. +inline JSValue jsNull() +{ + return JSValue(JSValue::JSNull); +} + +inline JSValue jsUndefined() +{ + return JSValue(JSValue::JSUndefined); +} + +inline JSValue jsTDZValue() +{ + return JSValue(); +} + +inline JSValue jsBoolean(bool b) +{ + return b ? JSValue(JSValue::JSTrue) : JSValue(JSValue::JSFalse); +} + +ALWAYS_INLINE JSValue jsDoubleNumber(double d) +{ + ASSERT(JSValue(JSValue::EncodeAsDouble, d).isNumber()); + return JSValue(JSValue::EncodeAsDouble, d); +} + +ALWAYS_INLINE JSValue jsNumber(double d) +{ + ASSERT(JSValue(d).isNumber()); + return JSValue(d); +} + +ALWAYS_INLINE JSValue jsNumber(MediaTime t) +{ + return jsNumber(t.toDouble()); +} + +ALWAYS_INLINE JSValue jsNumber(char i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(unsigned char i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(short i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(unsigned short i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(int i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(unsigned i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(long i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(unsigned long i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(long long i) +{ + return JSValue(i); +} + +ALWAYS_INLINE JSValue jsNumber(unsigned long long i) +{ + return JSValue(i); +} + +inline bool operator==(const JSValue a, const JSCell* b) { return a == JSValue(b); } +inline bool operator==(const JSCell* a, const JSValue b) { return JSValue(a) == b; } + +inline bool operator!=(const JSValue a, const JSCell* b) { return a != JSValue(b); } +inline bool operator!=(const JSCell* a, const JSValue b) { return JSValue(a) != b; } + +} // namespace JSC + +#endif // JSCJSValue_h diff --git a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h new file mode 100644 index 000000000..71842c21c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h @@ -0,0 +1,931 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSValueInlines_h +#define JSValueInlines_h + +#include "ExceptionHelpers.h" +#include "Identifier.h" +#include "InternalFunction.h" +#include "JSCJSValue.h" +#include "JSCellInlines.h" +#include "JSFunction.h" +#include <wtf/text/StringImpl.h> + +namespace JSC { + +ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const +{ + if (isInt32()) + return asInt32(); + return JSC::toInt32(toNumber(exec)); +} + +inline uint32_t JSValue::toUInt32(ExecState* exec) const +{ + // See comment on JSC::toUInt32, above. + return toInt32(exec); +} + +inline bool JSValue::isUInt32() const +{ + return isInt32() && asInt32() >= 0; +} + +inline uint32_t JSValue::asUInt32() const +{ + ASSERT(isUInt32()); + return asInt32(); +} + +inline double JSValue::asNumber() const +{ + ASSERT(isNumber()); + return isInt32() ? asInt32() : asDouble(); +} + +inline JSValue jsNaN() +{ + return JSValue(PNaN); +} + +inline JSValue::JSValue(char i) +{ + *this = JSValue(static_cast<int32_t>(i)); +} + +inline JSValue::JSValue(unsigned char i) +{ + *this = JSValue(static_cast<int32_t>(i)); +} + +inline JSValue::JSValue(short i) +{ + *this = JSValue(static_cast<int32_t>(i)); +} + +inline JSValue::JSValue(unsigned short i) +{ + *this = JSValue(static_cast<int32_t>(i)); +} + +inline JSValue::JSValue(unsigned i) +{ + if (static_cast<int32_t>(i) < 0) { + *this = JSValue(EncodeAsDouble, static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<int32_t>(i)); +} + +inline JSValue::JSValue(long i) +{ + if (static_cast<int32_t>(i) != i) { + *this = JSValue(EncodeAsDouble, static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<int32_t>(i)); +} + +inline JSValue::JSValue(unsigned long i) +{ + if (static_cast<uint32_t>(i) != i) { + *this = JSValue(EncodeAsDouble, static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<uint32_t>(i)); +} + +inline JSValue::JSValue(long long i) +{ + if (static_cast<int32_t>(i) != i) { + *this = JSValue(EncodeAsDouble, static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<int32_t>(i)); +} + +inline JSValue::JSValue(unsigned long long i) +{ + if (static_cast<uint32_t>(i) != i) { + *this = JSValue(EncodeAsDouble, static_cast<double>(i)); + return; + } + *this = JSValue(static_cast<uint32_t>(i)); +} + +inline JSValue::JSValue(double d) +{ + const int32_t asInt32 = static_cast<int32_t>(d); + if (asInt32 != d || (!asInt32 && std::signbit(d))) { // true for -0.0 + *this = JSValue(EncodeAsDouble, d); + return; + } + *this = JSValue(static_cast<int32_t>(d)); +} + +inline EncodedJSValue JSValue::encode(JSValue value) +{ + return value.u.asInt64; +} + +inline JSValue JSValue::decode(EncodedJSValue encodedJSValue) +{ + JSValue v; + v.u.asInt64 = encodedJSValue; + return v; +} + +#if USE(JSVALUE32_64) +inline JSValue::JSValue() +{ + u.asBits.tag = EmptyValueTag; + u.asBits.payload = 0; +} + +inline JSValue::JSValue(JSNullTag) +{ + u.asBits.tag = NullTag; + u.asBits.payload = 0; +} + +inline JSValue::JSValue(JSUndefinedTag) +{ + u.asBits.tag = UndefinedTag; + u.asBits.payload = 0; +} + +inline JSValue::JSValue(JSTrueTag) +{ + u.asBits.tag = BooleanTag; + u.asBits.payload = 1; +} + +inline JSValue::JSValue(JSFalseTag) +{ + u.asBits.tag = BooleanTag; + u.asBits.payload = 0; +} + +inline JSValue::JSValue(HashTableDeletedValueTag) +{ + u.asBits.tag = DeletedValueTag; + u.asBits.payload = 0; +} + +inline JSValue::JSValue(JSCell* ptr) +{ + if (ptr) + u.asBits.tag = CellTag; + else + u.asBits.tag = EmptyValueTag; + u.asBits.payload = reinterpret_cast<int32_t>(ptr); +} + +inline JSValue::JSValue(const JSCell* ptr) +{ + if (ptr) + u.asBits.tag = CellTag; + else + u.asBits.tag = EmptyValueTag; + u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); +} + +inline JSValue::operator bool() const +{ + ASSERT(tag() != DeletedValueTag); + return tag() != EmptyValueTag; +} + +inline bool JSValue::operator==(const JSValue& other) const +{ + return u.asInt64 == other.u.asInt64; +} + +inline bool JSValue::operator!=(const JSValue& other) const +{ + return u.asInt64 != other.u.asInt64; +} + +inline bool JSValue::isEmpty() const +{ + return tag() == EmptyValueTag; +} + +inline bool JSValue::isUndefined() const +{ + return tag() == UndefinedTag; +} + +inline bool JSValue::isNull() const +{ + return tag() == NullTag; +} + +inline bool JSValue::isUndefinedOrNull() const +{ + return isUndefined() || isNull(); +} + +inline bool JSValue::isCell() const +{ + return tag() == CellTag; +} + +inline bool JSValue::isInt32() const +{ + return tag() == Int32Tag; +} + +inline bool JSValue::isDouble() const +{ + return tag() < LowestTag; +} + +inline bool JSValue::isTrue() const +{ + return tag() == BooleanTag && payload(); +} + +inline bool JSValue::isFalse() const +{ + return tag() == BooleanTag && !payload(); +} + +inline uint32_t JSValue::tag() const +{ + return u.asBits.tag; +} + +inline int32_t JSValue::payload() const +{ + return u.asBits.payload; +} + +inline int32_t JSValue::asInt32() const +{ + ASSERT(isInt32()); + return u.asBits.payload; +} + +inline double JSValue::asDouble() const +{ + ASSERT(isDouble()); + return u.asDouble; +} + +ALWAYS_INLINE JSCell* JSValue::asCell() const +{ + ASSERT(isCell()); + return reinterpret_cast<JSCell*>(u.asBits.payload); +} + +ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) +{ + ASSERT(!isImpureNaN(d)); + u.asDouble = d; +} + +inline JSValue::JSValue(int i) +{ + u.asBits.tag = Int32Tag; + u.asBits.payload = i; +} + +#if !ENABLE(JIT) +inline JSValue::JSValue(int32_t tag, int32_t payload) +{ + u.asBits.tag = tag; + u.asBits.payload = payload; +} +#endif + +inline bool JSValue::isNumber() const +{ + return isInt32() || isDouble(); +} + +inline bool JSValue::isBoolean() const +{ + return tag() == BooleanTag; +} + +inline bool JSValue::asBoolean() const +{ + ASSERT(isBoolean()); + return payload(); +} + +#else // !USE(JSVALUE32_64) i.e. USE(JSVALUE64) + +// 0x0 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x0, which is in the (invalid) zero page. +inline JSValue::JSValue() +{ + u.asInt64 = ValueEmpty; +} + +// 0x4 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x4, which is in the (invalid) zero page. +inline JSValue::JSValue(HashTableDeletedValueTag) +{ + u.asInt64 = ValueDeleted; +} + +inline JSValue::JSValue(JSCell* ptr) +{ + u.asInt64 = reinterpret_cast<uintptr_t>(ptr); +} + +inline JSValue::JSValue(const JSCell* ptr) +{ + u.asInt64 = reinterpret_cast<uintptr_t>(const_cast<JSCell*>(ptr)); +} + +inline JSValue::operator bool() const +{ + return u.asInt64; +} + +inline bool JSValue::operator==(const JSValue& other) const +{ + return u.asInt64 == other.u.asInt64; +} + +inline bool JSValue::operator!=(const JSValue& other) const +{ + return u.asInt64 != other.u.asInt64; +} + +inline bool JSValue::isEmpty() const +{ + return u.asInt64 == ValueEmpty; +} + +inline bool JSValue::isUndefined() const +{ + return asValue() == JSValue(JSUndefined); +} + +inline bool JSValue::isNull() const +{ + return asValue() == JSValue(JSNull); +} + +inline bool JSValue::isTrue() const +{ + return asValue() == JSValue(JSTrue); +} + +inline bool JSValue::isFalse() const +{ + return asValue() == JSValue(JSFalse); +} + +inline bool JSValue::asBoolean() const +{ + ASSERT(isBoolean()); + return asValue() == JSValue(JSTrue); +} + +inline int32_t JSValue::asInt32() const +{ + ASSERT(isInt32()); + return static_cast<int32_t>(u.asInt64); +} + +inline bool JSValue::isDouble() const +{ + return isNumber() && !isInt32(); +} + +inline JSValue::JSValue(JSNullTag) +{ + u.asInt64 = ValueNull; +} + +inline JSValue::JSValue(JSUndefinedTag) +{ + u.asInt64 = ValueUndefined; +} + +inline JSValue::JSValue(JSTrueTag) +{ + u.asInt64 = ValueTrue; +} + +inline JSValue::JSValue(JSFalseTag) +{ + u.asInt64 = ValueFalse; +} + +inline bool JSValue::isUndefinedOrNull() const +{ + // Undefined and null share the same value, bar the 'undefined' bit in the extended tag. + return (u.asInt64 & ~TagBitUndefined) == ValueNull; +} + +inline bool JSValue::isBoolean() const +{ + return (u.asInt64 & ~1) == ValueFalse; +} + +inline bool JSValue::isCell() const +{ + return !(u.asInt64 & TagMask); +} + +inline bool JSValue::isInt32() const +{ + return (u.asInt64 & TagTypeNumber) == TagTypeNumber; +} + +inline int64_t reinterpretDoubleToInt64(double value) +{ + return bitwise_cast<int64_t>(value); +} +inline double reinterpretInt64ToDouble(int64_t value) +{ + return bitwise_cast<double>(value); +} + +ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) +{ + ASSERT(!isImpureNaN(d)); + u.asInt64 = reinterpretDoubleToInt64(d) + DoubleEncodeOffset; +} + +inline JSValue::JSValue(int i) +{ + u.asInt64 = TagTypeNumber | static_cast<uint32_t>(i); +} + +inline double JSValue::asDouble() const +{ + ASSERT(isDouble()); + return reinterpretInt64ToDouble(u.asInt64 - DoubleEncodeOffset); +} + +inline bool JSValue::isNumber() const +{ + return u.asInt64 & TagTypeNumber; +} + +ALWAYS_INLINE JSCell* JSValue::asCell() const +{ + ASSERT(isCell()); + return u.ptr; +} + +#endif // USE(JSVALUE64) + +inline int64_t tryConvertToInt52(double number) +{ + if (number != number) + return JSValue::notInt52; +#if OS(WINDOWS) && CPU(X86) + // The VS Compiler for 32-bit builds generates a floating point error when attempting to cast + // from an infinity to a 64-bit integer. We leave this routine with the floating point error + // left in a register, causing undefined behavior in later floating point operations. + // + // To avoid this issue, we check for infinity here, and return false in that case. + if (std::isinf(number)) + return JSValue::notInt52; +#endif + int64_t asInt64 = static_cast<int64_t>(number); + if (asInt64 != number) + return JSValue::notInt52; + if (!asInt64 && std::signbit(number)) + return JSValue::notInt52; + if (asInt64 >= (static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) + return JSValue::notInt52; + if (asInt64 < -(static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) + return JSValue::notInt52; + return asInt64; +} + +inline bool isInt52(double number) +{ + return tryConvertToInt52(number) != JSValue::notInt52; +} + +inline bool JSValue::isMachineInt() const +{ + if (isInt32()) + return true; + if (!isNumber()) + return false; + return isInt52(asDouble()); +} + +inline int64_t JSValue::asMachineInt() const +{ + ASSERT(isMachineInt()); + if (isInt32()) + return asInt32(); + return static_cast<int64_t>(asDouble()); +} + +inline bool JSValue::isString() const +{ + return isCell() && asCell()->isString(); +} + +inline bool JSValue::isSymbol() const +{ + return isCell() && asCell()->isSymbol(); +} + +inline bool JSValue::isPrimitive() const +{ + return !isCell() || asCell()->isString() || asCell()->isSymbol(); +} + +inline bool JSValue::isGetterSetter() const +{ + return isCell() && asCell()->isGetterSetter(); +} + +inline bool JSValue::isCustomGetterSetter() const +{ + return isCell() && asCell()->isCustomGetterSetter(); +} + +inline bool JSValue::isObject() const +{ + return isCell() && asCell()->isObject(); +} + +inline bool JSValue::getString(ExecState* exec, String& s) const +{ + return isCell() && asCell()->getString(exec, s); +} + +inline String JSValue::getString(ExecState* exec) const +{ + return isCell() ? asCell()->getString(exec) : String(); +} + +template <typename Base> String HandleConverter<Base, Unknown>::getString(ExecState* exec) const +{ + return jsValue().getString(exec); +} + +inline JSObject* JSValue::getObject() const +{ + return isCell() ? asCell()->getObject() : 0; +} + +ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const +{ + if (isInt32()) { + int32_t i = asInt32(); + v = static_cast<uint32_t>(i); + return i >= 0; + } + if (isDouble()) { + double d = asDouble(); + v = static_cast<uint32_t>(d); + return v == d; + } + return false; +} + +ALWAYS_INLINE Identifier JSValue::toPropertyKey(ExecState* exec) const +{ + if (isString()) + return asString(*this)->toIdentifier(exec); + + JSValue primitive = toPrimitive(exec, PreferString); + if (primitive.isSymbol()) + return Identifier::fromUid(asSymbol(primitive)->privateName()); + return primitive.toString(exec)->toIdentifier(exec); +} + +inline JSValue JSValue::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const +{ + return isCell() ? asCell()->toPrimitive(exec, preferredType) : asValue(); +} + +inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue& value) +{ + if (isInt32()) { + number = asInt32(); + value = *this; + return true; + } + if (isDouble()) { + number = asDouble(); + value = *this; + return true; + } + if (isCell()) + return asCell()->getPrimitiveNumber(exec, number, value); + if (isTrue()) { + number = 1.0; + value = *this; + return true; + } + if (isFalse() || isNull()) { + number = 0.0; + value = *this; + return true; + } + ASSERT(isUndefined()); + number = PNaN; + value = *this; + return true; +} + +ALWAYS_INLINE double JSValue::toNumber(ExecState* exec) const +{ + if (isInt32()) + return asInt32(); + if (isDouble()) + return asDouble(); + return toNumberSlowCase(exec); +} + +inline JSObject* JSValue::toObject(ExecState* exec) const +{ + return isCell() ? asCell()->toObject(exec, exec->lexicalGlobalObject()) : toObjectSlowCase(exec, exec->lexicalGlobalObject()); +} + +inline JSObject* JSValue::toObject(ExecState* exec, JSGlobalObject* globalObject) const +{ + return isCell() ? asCell()->toObject(exec, globalObject) : toObjectSlowCase(exec, globalObject); +} + +inline bool JSValue::isFunction() const +{ + return isCell() && (asCell()->inherits(JSFunction::info()) || asCell()->inherits(InternalFunction::info())); +} + +// this method is here to be after the inline declaration of JSCell::inherits +inline bool JSValue::inherits(const ClassInfo* classInfo) const +{ + return isCell() && asCell()->inherits(classInfo); +} + +inline JSValue JSValue::toThis(ExecState* exec, ECMAMode ecmaMode) const +{ + return isCell() ? asCell()->methodTable(exec->vm())->toThis(asCell(), exec, ecmaMode) : toThisSlowCase(exec, ecmaMode); +} + +ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, PropertyName propertyName) const +{ + PropertySlot slot(asValue()); + return get(exec, propertyName, slot); +} + +ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const +{ + return getPropertySlot(exec, propertyName, slot) ? + slot.getValue(exec, propertyName) : jsUndefined(); +} + +ALWAYS_INLINE bool JSValue::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const +{ + // If this is a primitive, we'll need to synthesize the prototype - + // and if it's a string there are special properties to check first. + JSObject* object; + if (UNLIKELY(!isObject())) { + if (isString() && asString(*this)->getStringPropertySlot(exec, propertyName, slot)) + return true; + object = synthesizePrototype(exec); + } else + object = asObject(asCell()); + + return object->getPropertySlot(exec, propertyName, slot); +} + +ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, unsigned propertyName) const +{ + PropertySlot slot(asValue()); + return get(exec, propertyName, slot); +} + +ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const +{ + // If this is a primitive, we'll need to synthesize the prototype - + // and if it's a string there are special properties to check first. + JSObject* object; + if (UNLIKELY(!isObject())) { + if (isString() && asString(*this)->getStringPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, propertyName); + object = synthesizePrototype(exec); + } else + object = asObject(asCell()); + + if (object->getPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, propertyName); + return jsUndefined(); +} + +inline void JSValue::put(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + if (UNLIKELY(!isCell())) { + putToPrimitive(exec, propertyName, value, slot); + return; + } + asCell()->methodTable(exec->vm())->put(asCell(), exec, propertyName, value, slot); +} + +inline void JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) +{ + if (UNLIKELY(!isCell())) { + putToPrimitiveByIndex(exec, propertyName, value, shouldThrow); + return; + } + asCell()->methodTable(exec->vm())->putByIndex(asCell(), exec, propertyName, value, shouldThrow); +} + +inline JSValue JSValue::structureOrUndefined() const +{ + if (isCell()) + return JSValue(asCell()->structure()); + return jsUndefined(); +} + +// ECMA 11.9.3 +inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) +{ + if (v1.isInt32() && v2.isInt32()) + return v1 == v2; + + return equalSlowCase(exec, v1, v2); +} + +ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) +{ + VM& vm = exec->vm(); + do { + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() == v2.asNumber(); + + bool s1 = v1.isString(); + bool s2 = v2.isString(); + if (s1 && s2) + return WTF::equal(*asString(v1)->value(exec).impl(), *asString(v2)->value(exec).impl()); + + if (v1.isUndefinedOrNull()) { + if (v2.isUndefinedOrNull()) + return true; + if (!v2.isCell()) + return false; + return v2.asCell()->structure(vm)->masqueradesAsUndefined(exec->lexicalGlobalObject()); + } + + if (v2.isUndefinedOrNull()) { + if (!v1.isCell()) + return false; + return v1.asCell()->structure(vm)->masqueradesAsUndefined(exec->lexicalGlobalObject()); + } + + if (v1.isObject()) { + if (v2.isObject()) + return v1 == v2; + JSValue p1 = v1.toPrimitive(exec); + if (exec->hadException()) + return false; + v1 = p1; + if (v1.isInt32() && v2.isInt32()) + return v1 == v2; + continue; + } + + if (v2.isObject()) { + JSValue p2 = v2.toPrimitive(exec); + if (exec->hadException()) + return false; + v2 = p2; + if (v1.isInt32() && v2.isInt32()) + return v1 == v2; + continue; + } + + bool sym1 = v1.isSymbol(); + bool sym2 = v2.isSymbol(); + if (sym1 || sym2) { + if (sym1 && sym2) + return asSymbol(v1)->privateName() == asSymbol(v2)->privateName(); + return false; + } + + if (s1 || s2) { + double d1 = v1.toNumber(exec); + double d2 = v2.toNumber(exec); + return d1 == d2; + } + + if (v1.isBoolean()) { + if (v2.isNumber()) + return static_cast<double>(v1.asBoolean()) == v2.asNumber(); + } else if (v2.isBoolean()) { + if (v1.isNumber()) + return v1.asNumber() == static_cast<double>(v2.asBoolean()); + } + + return v1 == v2; + } while (true); +} + +// ECMA 11.9.3 +ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) +{ + ASSERT(v1.isCell() && v2.isCell()); + + if (v1.asCell()->isString() && v2.asCell()->isString()) + return WTF::equal(*asString(v1)->value(exec).impl(), *asString(v2)->value(exec).impl()); + if (v1.asCell()->isSymbol() && v2.asCell()->isSymbol()) + return asSymbol(v1)->privateName() == asSymbol(v2)->privateName(); + + return v1 == v2; +} + +inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) +{ + if (v1.isInt32() && v2.isInt32()) + return v1 == v2; + + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() == v2.asNumber(); + + if (!v1.isCell() || !v2.isCell()) + return v1 == v2; + + return strictEqualSlowCaseInline(exec, v1, v2); +} + +inline int32_t JSValue::asInt32ForArithmetic() const +{ + if (isBoolean()) + return asBoolean(); + return asInt32(); +} + +inline TriState JSValue::pureStrictEqual(JSValue v1, JSValue v2) +{ + if (v1.isInt32() && v2.isInt32()) + return triState(v1 == v2); + + if (v1.isNumber() && v2.isNumber()) + return triState(v1.asNumber() == v2.asNumber()); + + if (!v1.isCell() || !v2.isCell()) + return triState(v1 == v2); + + if (v1.asCell()->isString() && v2.asCell()->isString()) { + const StringImpl* v1String = asString(v1)->tryGetValueImpl(); + const StringImpl* v2String = asString(v2)->tryGetValueImpl(); + if (!v1String || !v2String) + return MixedTriState; + return triState(WTF::equal(*v1String, *v2String)); + } + + return triState(v1 == v2); +} + +inline TriState JSValue::pureToBoolean() const +{ + if (isInt32()) + return asInt32() ? TrueTriState : FalseTriState; + if (isDouble()) + return isNotZeroAndOrdered(asDouble()) ? TrueTriState : FalseTriState; // false for NaN + if (isCell()) + return asCell()->pureToBoolean(); + return isTrue() ? TrueTriState : FalseTriState; +} + +ALWAYS_INLINE bool JSValue::requireObjectCoercible(ExecState* exec) const +{ + if (!isUndefinedOrNull()) + return true; + exec->vm().throwException(exec, createNotAnObjectError(exec, *this)); + return false; +} + +} // namespace JSC + +#endif // JSValueInlines_h + diff --git a/Source/JavaScriptCore/runtime/JSCallee.cpp b/Source/JavaScriptCore/runtime/JSCallee.cpp new file mode 100644 index 000000000..d303296cc --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCallee.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSCallee.h" + +#include "GetterSetter.h" +#include "JSCJSValueInlines.h" +#include "JSCell.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "SlotVisitorInlines.h" +#include "StackVisitor.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSCallee::s_info = { "Callee", &Base::s_info, 0, CREATE_METHOD_TABLE(JSCallee) }; + +JSCallee::JSCallee(VM& vm, JSGlobalObject* globalObject, Structure* structure) + : Base(vm, structure) + , m_scope(vm, this, globalObject) +{ +} + +JSCallee::JSCallee(VM& vm, JSScope* scope, Structure* structure) + : Base(vm, structure) +{ + setScope(vm, scope); +} + +void JSCallee::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +void JSCallee::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSCallee* thisObject = jsCast<JSCallee*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_scope); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSCallee.h b/Source/JavaScriptCore/runtime/JSCallee.h new file mode 100644 index 000000000..1545eddc7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCallee.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSCallee_h +#define JSCallee_h + +#include "JSGlobalObject.h" +#include "JSObject.h" +#include "JSScope.h" + +namespace JSC { + +class JSGlobalObject; +class LLIntOffsetsExtractor; + + +class JSCallee : public JSNonFinalObject { + friend class JIT; +#if ENABLE(DFG_JIT) + friend class DFG::SpeculativeJIT; + friend class DFG::JITCompiler; +#endif + friend class VM; + +public: + typedef JSNonFinalObject Base; + const static unsigned StructureFlags = Base::StructureFlags | ImplementsHasInstance; + + static JSCallee* create(VM& vm, JSGlobalObject* globalObject, JSScope* scope) + { + JSCallee* callee = new (NotNull, allocateCell<JSCallee>(vm.heap)) JSCallee(vm, scope, globalObject->calleeStructure()); + callee->finishCreation(vm); + return callee; + } + + JSScope* scope() + { + return m_scope.get(); + } + + // This method may be called for host functions, in which case it + // will return an arbitrary value. This should only be used for + // optimized paths in which the return value does not matter for + // host functions, and checking whether the function is a host + // function is deemed too expensive. + JSScope* scopeUnchecked() + { + return m_scope.get(); + } + + void setScope(VM& vm, JSScope* scope) + { + m_scope.set(vm, this, scope); + } + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + ASSERT(globalObject); + return Structure::create(vm, globalObject, prototype, TypeInfo(JSCalleeType, StructureFlags), info()); + } + + static inline ptrdiff_t offsetOfScopeChain() + { + return OBJECT_OFFSETOF(JSCallee, m_scope); + } + +protected: + JS_EXPORT_PRIVATE JSCallee(VM&, JSGlobalObject*, Structure*); + JSCallee(VM&, JSScope*, Structure*); + + void finishCreation(VM&); + using Base::finishCreation; + + static void visitChildren(JSCell*, SlotVisitor&); + +private: + friend class LLIntOffsetsExtractor; + + WriteBarrier<JSScope> m_scope; +}; + +} // namespace JSC + +#endif // JSCallee_h diff --git a/Source/JavaScriptCore/runtime/JSCell.cpp b/Source/JavaScriptCore/runtime/JSCell.cpp new file mode 100644 index 000000000..2c403c813 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCell.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "JSCell.h" + +#include "ArrayBufferView.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSObject.h" +#include "NumberObject.h" +#include "JSCInlines.h" +#include <wtf/MathExtras.h> + +namespace JSC { + +COMPILE_ASSERT(sizeof(JSCell) == sizeof(uint64_t), jscell_is_eight_bytes); +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSCell); + +void JSCell::destroy(JSCell* cell) +{ + cell->JSCell::~JSCell(); +} + +void JSCell::dump(PrintStream& out) const +{ + methodTable()->dumpToStream(this, out); +} + +void JSCell::dumpToStream(const JSCell* cell, PrintStream& out) +{ + out.printf("<%p, %s>", cell, cell->className()); +} + +void JSCell::copyBackingStore(JSCell*, CopyVisitor&, CopyToken) +{ +} + +bool JSCell::getString(ExecState* exec, String& stringValue) const +{ + if (!isString()) + return false; + stringValue = static_cast<const JSString*>(this)->value(exec); + return true; +} + +String JSCell::getString(ExecState* exec) const +{ + return isString() ? static_cast<const JSString*>(this)->value(exec) : String(); +} + +JSObject* JSCell::getObject() +{ + return isObject() ? asObject(this) : 0; +} + +const JSObject* JSCell::getObject() const +{ + return isObject() ? static_cast<const JSObject*>(this) : 0; +} + +CallType JSCell::getCallData(JSCell*, CallData& callData) +{ + callData.js.functionExecutable = 0; + callData.js.scope = 0; + callData.native.function = 0; + return CallTypeNone; +} + +ConstructType JSCell::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.js.functionExecutable = 0; + constructData.js.scope = 0; + constructData.native.function = 0; + return ConstructTypeNone; +} + +void JSCell::put(JSCell* cell, ExecState* exec, PropertyName identifier, JSValue value, PutPropertySlot& slot) +{ + if (cell->isString() || cell->isSymbol()) { + JSValue(cell).putToPrimitive(exec, identifier, value, slot); + return; + } + JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); + thisObject->methodTable(exec->vm())->put(thisObject, exec, identifier, value, slot); +} + +void JSCell::putByIndex(JSCell* cell, ExecState* exec, unsigned identifier, JSValue value, bool shouldThrow) +{ + if (cell->isString() || cell->isSymbol()) { + PutPropertySlot slot(cell, shouldThrow); + JSValue(cell).putToPrimitive(exec, Identifier::from(exec, identifier), value, slot); + return; + } + JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); + thisObject->methodTable(exec->vm())->putByIndex(thisObject, exec, identifier, value, shouldThrow); +} + +bool JSCell::deleteProperty(JSCell* cell, ExecState* exec, PropertyName identifier) +{ + JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); + return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, identifier); +} + +bool JSCell::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned identifier) +{ + JSObject* thisObject = cell->toObject(exec, exec->lexicalGlobalObject()); + return thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, identifier); +} + +JSValue JSCell::toThis(JSCell* cell, ExecState* exec, ECMAMode ecmaMode) +{ + if (ecmaMode == StrictMode) + return cell; + return cell->toObject(exec, exec->lexicalGlobalObject()); +} + +JSValue JSCell::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const +{ + if (isString()) + return static_cast<const JSString*>(this)->toPrimitive(exec, preferredType); + if (isSymbol()) + return static_cast<const Symbol*>(this)->toPrimitive(exec, preferredType); + return static_cast<const JSObject*>(this)->toPrimitive(exec, preferredType); +} + +bool JSCell::getPrimitiveNumber(ExecState* exec, double& number, JSValue& value) const +{ + if (isString()) + return static_cast<const JSString*>(this)->getPrimitiveNumber(exec, number, value); + if (isSymbol()) + return static_cast<const Symbol*>(this)->getPrimitiveNumber(exec, number, value); + return static_cast<const JSObject*>(this)->getPrimitiveNumber(exec, number, value); +} + +double JSCell::toNumber(ExecState* exec) const +{ + if (isString()) + return static_cast<const JSString*>(this)->toNumber(exec); + if (isSymbol()) + return static_cast<const Symbol*>(this)->toNumber(exec); + return static_cast<const JSObject*>(this)->toNumber(exec); +} + +JSObject* JSCell::toObject(ExecState* exec, JSGlobalObject* globalObject) const +{ + if (isString()) + return static_cast<const JSString*>(this)->toObject(exec, globalObject); + if (isSymbol()) + return static_cast<const Symbol*>(this)->toObject(exec, globalObject); + ASSERT(isObject()); + return jsCast<JSObject*>(const_cast<JSCell*>(this)); +} + +void slowValidateCell(JSCell* cell) +{ + ASSERT_GC_OBJECT_LOOKS_VALID(cell); +} + +JSValue JSCell::defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType) +{ + RELEASE_ASSERT_NOT_REACHED(); + return jsUndefined(); +} + +bool JSCell::getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&) +{ + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +bool JSCell::getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned, PropertySlot&) +{ + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +void JSCell::getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) +{ + RELEASE_ASSERT_NOT_REACHED(); +} + +void JSCell::getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) +{ + RELEASE_ASSERT_NOT_REACHED(); +} + +String JSCell::className(const JSObject*) +{ + RELEASE_ASSERT_NOT_REACHED(); + return String(); +} + +const char* JSCell::className() const +{ + return classInfo()->className; +} + +void JSCell::getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) +{ + RELEASE_ASSERT_NOT_REACHED(); +} + +bool JSCell::customHasInstance(JSObject*, ExecState*, JSValue) +{ + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +bool JSCell::defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool) +{ + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +ArrayBuffer* JSCell::slowDownAndWasteMemory(JSArrayBufferView*) +{ + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} + +PassRefPtr<ArrayBufferView> JSCell::getTypedArrayImpl(JSArrayBufferView*) +{ + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} + +uint32_t JSCell::getEnumerableLength(ExecState*, JSObject*) +{ + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} + +void JSCell::getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) +{ + RELEASE_ASSERT_NOT_REACHED(); +} + +void JSCell::getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) +{ + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h new file mode 100644 index 000000000..6d648ad26 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -0,0 +1,279 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSCell_h +#define JSCell_h + +#include "CallData.h" +#include "ConstructData.h" +#include "EnumerationMode.h" +#include "Heap.h" +#include "IndexingType.h" +#include "JSLock.h" +#include "JSTypeInfo.h" +#include "SlotVisitor.h" +#include "TypedArrayType.h" +#include "WriteBarrier.h" +#include <wtf/Noncopyable.h> + +namespace JSC { + +class CopyVisitor; +class ExecState; +class Identifier; +class JSArrayBufferView; +class JSDestructibleObject; +class JSGlobalObject; +class LLIntOffsetsExtractor; +class PropertyDescriptor; +class PropertyNameArray; +class Structure; + +template<typename T> void* allocateCell(Heap&); +template<typename T> void* allocateCell(Heap&, size_t); + +#define DECLARE_EXPORT_INFO \ + protected: \ + static JS_EXPORTDATA const ::JSC::ClassInfo s_info; \ + public: \ + static const ::JSC::ClassInfo* info() { return &s_info; } + +#define DECLARE_INFO \ + protected: \ + static const ::JSC::ClassInfo s_info; \ + public: \ + static const ::JSC::ClassInfo* info() { return &s_info; } + +class JSCell { + friend class JSValue; + friend class MarkedBlock; + template<typename T> friend void* allocateCell(Heap&); + template<typename T> friend void* allocateCell(Heap&, size_t); + +public: + static const unsigned StructureFlags = 0; + + static const bool needsDestruction = false; + + static JSCell* seenMultipleCalleeObjects() { return bitwise_cast<JSCell*>(static_cast<uintptr_t>(1)); } + + enum CreatingEarlyCellTag { CreatingEarlyCell }; + JSCell(CreatingEarlyCellTag); + +protected: + JSCell(VM&, Structure*); + JS_EXPORT_PRIVATE static void destroy(JSCell*); + +public: + // Querying the type. + bool isString() const; + bool isSymbol() const; + bool isObject() const; + bool isGetterSetter() const; + bool isCustomGetterSetter() const; + bool isProxy() const; + bool inherits(const ClassInfo*) const; + bool isAPIValueWrapper() const; + + JSType type() const; + IndexingType indexingType() const; + StructureID structureID() const { return m_structureID; } + Structure* structure() const; + Structure* structure(VM&) const; + void setStructure(VM&, Structure*); + void clearStructure() { m_structureID = 0; } + + TypeInfo::InlineTypeFlags inlineTypeFlags() const { return m_flags; } + + const char* className() const; + + VM* vm() const; + + // Extracting the value. + JS_EXPORT_PRIVATE bool getString(ExecState*, String&) const; + JS_EXPORT_PRIVATE String getString(ExecState*) const; // null string if not a string + JS_EXPORT_PRIVATE JSObject* getObject(); // NULL if not an object + const JSObject* getObject() const; // NULL if not an object + + // Returns information about how to call/construct this cell as a function/constructor. May tell + // you that the cell is not callable or constructor (default is that it's not either). If it + // says that the function is callable, and the TypeOfShouldCallGetCallData type flag is set, and + // this is an object, then typeof will return "function" instead of "object". These methods + // cannot change their minds and must be thread-safe. They are sometimes called from compiler + // threads. + JS_EXPORT_PRIVATE static CallType getCallData(JSCell*, CallData&); + JS_EXPORT_PRIVATE static ConstructType getConstructData(JSCell*, ConstructData&); + + // Basic conversions. + JS_EXPORT_PRIVATE JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + bool toBoolean(ExecState*) const; + TriState pureToBoolean() const; + JS_EXPORT_PRIVATE double toNumber(ExecState*) const; + JS_EXPORT_PRIVATE JSObject* toObject(ExecState*, JSGlobalObject*) const; + + void dump(PrintStream&) const; + JS_EXPORT_PRIVATE static void dumpToStream(const JSCell*, PrintStream&); + static void visitChildren(JSCell*, SlotVisitor&); + JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); + + // Object operations, with the toObject operation included. + const ClassInfo* classInfo() const; + const MethodTable* methodTable() const; + const MethodTable* methodTable(VM&) const; + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + + static JSValue toThis(JSCell*, ExecState*, ECMAMode); + + void zap() { *reinterpret_cast<uintptr_t**>(this) = 0; } + bool isZapped() const { return !*reinterpret_cast<uintptr_t* const*>(this); } + + static bool canUseFastGetOwnProperty(const Structure&); + JSValue fastGetOwnProperty(VM&, Structure&, PropertyName); + + enum GCData : uint8_t { + Marked = 0, // The object has survived a GC and is in the old gen. + NotMarked = 1, // The object is new and in the eden gen. + MarkedAndRemembered = 2, // The object is in the GC's remembered set. + + // The object being in the GC's remembered set implies that it is also + // Marked. This is because objects are only added to the remembered sets + // by write barriers, and write barriers are only interested in old gen + // objects that point to potential eden gen objects. + }; + + void setMarked() { m_gcData = Marked; } + void setRemembered(bool remembered) + { + ASSERT(m_gcData == (remembered ? Marked : MarkedAndRemembered)); + m_gcData = remembered ? MarkedAndRemembered : Marked; + } + bool isMarked() const + { + switch (m_gcData) { + case Marked: + case MarkedAndRemembered: + return true; + case NotMarked: + return false; + } + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + bool isRemembered() const { return m_gcData == MarkedAndRemembered; } + + static ptrdiff_t structureIDOffset() + { + return OBJECT_OFFSETOF(JSCell, m_structureID); + } + + static ptrdiff_t typeInfoFlagsOffset() + { + return OBJECT_OFFSETOF(JSCell, m_flags); + } + + static ptrdiff_t typeInfoTypeOffset() + { + return OBJECT_OFFSETOF(JSCell, m_type); + } + + static ptrdiff_t indexingTypeOffset() + { + return OBJECT_OFFSETOF(JSCell, m_indexingType); + } + + static ptrdiff_t gcDataOffset() + { + return OBJECT_OFFSETOF(JSCell, m_gcData); + } + + static const TypedArrayType TypedArrayStorageType = NotTypedArray; +protected: + + void finishCreation(VM&); + void finishCreation(VM&, Structure*, CreatingEarlyCellTag); + + // Dummy implementations of override-able static functions for classes to put in their MethodTable + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + static NO_RETURN_DUE_TO_CRASH void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static NO_RETURN_DUE_TO_CRASH void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static NO_RETURN_DUE_TO_CRASH void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + static uint32_t getEnumerableLength(ExecState*, JSObject*); + static NO_RETURN_DUE_TO_CRASH void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static NO_RETURN_DUE_TO_CRASH void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + static String className(const JSObject*); + JS_EXPORT_PRIVATE static bool customHasInstance(JSObject*, ExecState*, JSValue); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); + JS_EXPORT_PRIVATE static ArrayBuffer* slowDownAndWasteMemory(JSArrayBufferView*); + JS_EXPORT_PRIVATE static PassRefPtr<ArrayBufferView> getTypedArrayImpl(JSArrayBufferView*); + +private: + friend class LLIntOffsetsExtractor; + + StructureID m_structureID; + IndexingType m_indexingType; + JSType m_type; + TypeInfo::InlineTypeFlags m_flags; + uint8_t m_gcData; +}; + +template<typename To, typename From> +inline To jsCast(From* from) +{ + ASSERT_WITH_SECURITY_IMPLICATION(!from || from->JSCell::inherits(std::remove_pointer<To>::type::info())); + return static_cast<To>(from); +} + +template<typename To> +inline To jsCast(JSValue from) +{ + ASSERT_WITH_SECURITY_IMPLICATION(from.isCell() && from.asCell()->JSCell::inherits(std::remove_pointer<To>::type::info())); + return static_cast<To>(from.asCell()); +} + +template<typename To, typename From> +inline To jsDynamicCast(From* from) +{ + if (LIKELY(from->inherits(std::remove_pointer<To>::type::info()))) + return static_cast<To>(from); + return nullptr; +} + +template<typename To> +inline To jsDynamicCast(JSValue from) +{ + if (LIKELY(from.isCell() && from.asCell()->inherits(std::remove_pointer<To>::type::info()))) + return static_cast<To>(from.asCell()); + return nullptr; +} + +} // namespace JSC + +#endif // JSCell_h diff --git a/Source/JavaScriptCore/runtime/JSCellInlines.h b/Source/JavaScriptCore/runtime/JSCellInlines.h new file mode 100644 index 000000000..c4019ff4e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSCellInlines.h @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2012, 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSCellInlines_h +#define JSCellInlines_h + +#include "CallFrame.h" +#include "DeferGC.h" +#include "Handle.h" +#include "JSCell.h" +#include "JSDestructibleObject.h" +#include "JSObject.h" +#include "JSString.h" +#include "MarkedBlock.h" +#include "Structure.h" +#include "Symbol.h" +#include <wtf/CompilationThread.h> + +namespace JSC { + +inline JSCell::JSCell(CreatingEarlyCellTag) + : m_gcData(NotMarked) +{ + ASSERT(!isCompilationThread()); +} + +inline JSCell::JSCell(VM&, Structure* structure) + : m_structureID(structure->id()) + , m_indexingType(structure->indexingType()) + , m_type(structure->typeInfo().type()) + , m_flags(structure->typeInfo().inlineTypeFlags()) + , m_gcData(NotMarked) +{ + ASSERT(!isCompilationThread()); +} + +inline void JSCell::finishCreation(VM& vm) +{ +#if ENABLE(GC_VALIDATION) + ASSERT(vm.isInitializingObject()); + vm.setInitializingObjectClass(0); +#else + UNUSED_PARAM(vm); +#endif + ASSERT(m_structureID); +} + +inline void JSCell::finishCreation(VM& vm, Structure* structure, CreatingEarlyCellTag) +{ +#if ENABLE(GC_VALIDATION) + ASSERT(vm.isInitializingObject()); + vm.setInitializingObjectClass(0); + if (structure) { +#endif + m_structureID = structure->id(); + m_indexingType = structure->indexingType(); + m_type = structure->typeInfo().type(); + m_flags = structure->typeInfo().inlineTypeFlags(); +#if ENABLE(GC_VALIDATION) + } +#else + UNUSED_PARAM(vm); +#endif + // Very first set of allocations won't have a real structure. + ASSERT(m_structureID || !vm.structureStructure); +} + +inline JSType JSCell::type() const +{ + return m_type; +} + +inline IndexingType JSCell::indexingType() const +{ + return m_indexingType; +} + +inline Structure* JSCell::structure() const +{ + return Heap::heap(this)->structureIDTable().get(m_structureID); +} + +inline Structure* JSCell::structure(VM& vm) const +{ + return vm.heap.structureIDTable().get(m_structureID); +} + +inline void JSCell::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Structure* structure = cell->structure(visitor.vm()); + visitor.appendUnbarrieredPointer(&structure); +} + +inline VM* JSCell::vm() const +{ + return MarkedBlock::blockFor(this)->vm(); +} + +inline VM& ExecState::vm() const +{ + ASSERT(callee()); + ASSERT(callee()->vm()); + return *calleeAsValue().asCell()->vm(); +} + +template<typename T> +void* allocateCell(Heap& heap, size_t size) +{ + ASSERT(!DisallowGC::isGCDisallowedOnCurrentThread()); + ASSERT(size >= sizeof(T)); + JSCell* result = static_cast<JSCell*>(heap.allocateObjectOfType<T>(size)); +#if ENABLE(GC_VALIDATION) + ASSERT(!heap.vm()->isInitializingObject()); + heap.vm()->setInitializingObjectClass(T::info()); +#endif + result->clearStructure(); + return result; +} + +template<typename T> +void* allocateCell(Heap& heap) +{ + return allocateCell<T>(heap, sizeof(T)); +} + +inline bool isZapped(const JSCell* cell) +{ + return cell->isZapped(); +} + +inline bool JSCell::isObject() const +{ + return TypeInfo::isObject(m_type); +} + +inline bool JSCell::isString() const +{ + return m_type == StringType; +} + +inline bool JSCell::isSymbol() const +{ + return m_type == SymbolType; +} + +inline bool JSCell::isGetterSetter() const +{ + return m_type == GetterSetterType; +} + +inline bool JSCell::isCustomGetterSetter() const +{ + return m_type == CustomGetterSetterType; +} + +inline bool JSCell::isProxy() const +{ + return m_type == ImpureProxyType || m_type == PureForwardingProxyType; +} + +inline bool JSCell::isAPIValueWrapper() const +{ + return m_type == APIValueWrapperType; +} + +inline void JSCell::setStructure(VM& vm, Structure* structure) +{ + ASSERT(structure->classInfo() == this->structure()->classInfo()); + ASSERT(!this->structure() + || this->structure()->transitionWatchpointSetHasBeenInvalidated() + || Heap::heap(this)->structureIDTable().get(structure->id()) == structure); + vm.heap.writeBarrier(this, structure); + m_structureID = structure->id(); + m_flags = structure->typeInfo().inlineTypeFlags(); + m_type = structure->typeInfo().type(); + m_indexingType = structure->indexingType(); +} + +inline const MethodTable* JSCell::methodTable() const +{ + VM& vm = *Heap::heap(this)->vm(); + Structure* structure = this->structure(vm); + if (Structure* rootStructure = structure->structure(vm)) + RELEASE_ASSERT(rootStructure == rootStructure->structure(vm)); + + return &structure->classInfo()->methodTable; +} + +inline const MethodTable* JSCell::methodTable(VM& vm) const +{ + Structure* structure = this->structure(vm); + if (Structure* rootStructure = structure->structure(vm)) + RELEASE_ASSERT(rootStructure == rootStructure->structure(vm)); + + return &structure->classInfo()->methodTable; +} + +inline bool JSCell::inherits(const ClassInfo* info) const +{ + return classInfo()->isSubClassOf(info); +} + +ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(VM& vm, Structure& structure, PropertyName name) +{ + ASSERT(canUseFastGetOwnProperty(structure)); + PropertyOffset offset = structure.get(vm, name); + if (offset != invalidOffset) + return asObject(this)->locationForOffset(offset)->get(); + return JSValue(); +} + +inline bool JSCell::canUseFastGetOwnProperty(const Structure& structure) +{ + return !structure.hasGetterSetterProperties() + && !structure.hasCustomGetterSetterProperties() + && !structure.typeInfo().overridesGetOwnPropertySlot(); +} + +inline const ClassInfo* JSCell::classInfo() const +{ + MarkedBlock* block = MarkedBlock::blockFor(this); + if (block->needsDestruction() && !(inlineTypeFlags() & StructureIsImmortal)) + return static_cast<const JSDestructibleObject*>(this)->classInfo(); + return structure(*block->vm())->classInfo(); +} + +inline bool JSCell::toBoolean(ExecState* exec) const +{ + if (isString()) + return static_cast<const JSString*>(this)->toBoolean(); + return !structure()->masqueradesAsUndefined(exec->lexicalGlobalObject()); +} + +inline TriState JSCell::pureToBoolean() const +{ + if (isString()) + return static_cast<const JSString*>(this)->toBoolean() ? TrueTriState : FalseTriState; + if (isSymbol()) + return TrueTriState; + return MixedTriState; +} + +} // namespace JSC + +#endif // JSCellInlines_h diff --git a/Source/JavaScriptCore/runtime/JSConsole.cpp b/Source/JavaScriptCore/runtime/JSConsole.cpp new file mode 100644 index 000000000..7d04abf58 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSConsole.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSConsole.h" + +#include "JSCJSValueInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSConsole::s_info = { "Console", &Base::s_info, 0, CREATE_METHOD_TABLE(JSConsole) }; + +} diff --git a/Source/JavaScriptCore/runtime/JSConsole.h b/Source/JavaScriptCore/runtime/JSConsole.h new file mode 100644 index 000000000..de611ec76 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSConsole.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSConsole_h +#define JSConsole_h + +#include "JSObject.h" + +namespace JSC { + +class JSConsole : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSConsole* create(VM& vm, Structure* structure) + { + JSConsole* instance = new (NotNull, allocateCell<JSConsole>(vm.heap)) JSConsole(vm, structure); + instance->finishCreation(vm); + return instance; + } + + static JSConsole* create(ExecState* exec, Structure* structure) + { + return create(exec->vm(), structure); + } + +private: + JSConsole(VM& vm, Structure* structure) + : Base(vm, structure) + { + } +}; + +} + +#endif // !defined(JSConsole_h) diff --git a/Source/JavaScriptCore/runtime/JSDataView.cpp b/Source/JavaScriptCore/runtime/JSDataView.cpp new file mode 100644 index 000000000..513d4c6b3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDataView.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSDataView.h" + +#include "ArrayBufferView.h" +#include "DataView.h" +#include "Error.h" +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo JSDataView::s_info = { + "DataView", &Base::s_info, 0, CREATE_METHOD_TABLE(JSDataView)}; + +JSDataView::JSDataView(VM& vm, ConstructionContext& context, ArrayBuffer* buffer) + : Base(vm, context) + , m_buffer(buffer) +{ +} + +JSDataView* JSDataView::create( + ExecState* exec, Structure* structure, PassRefPtr<ArrayBuffer> passedBuffer, + unsigned byteOffset, unsigned byteLength) +{ + RefPtr<ArrayBuffer> buffer = passedBuffer; + if (!ArrayBufferView::verifySubRangeLength(buffer, byteOffset, byteLength, sizeof(uint8_t))) { + throwVMError(exec, createRangeError(exec, ASCIILiteral("Length out of range of buffer"))); + return nullptr; + } + if (!ArrayBufferView::verifyByteOffsetAlignment(byteOffset, sizeof(uint8_t))) { + exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Byte offset is not aligned"))); + return nullptr; + } + VM& vm = exec->vm(); + ConstructionContext context( + structure, buffer, byteOffset, byteLength, ConstructionContext::DataView); + ASSERT(context); + JSDataView* result = + new (NotNull, allocateCell<JSDataView>(vm.heap)) JSDataView(vm, context, buffer.get()); + result->finishCreation(vm); + return result; +} + +JSDataView* JSDataView::createUninitialized(ExecState*, Structure*, unsigned) +{ + UNREACHABLE_FOR_PLATFORM(); + return 0; +} + +JSDataView* JSDataView::create(ExecState*, Structure*, unsigned) +{ + UNREACHABLE_FOR_PLATFORM(); + return 0; +} + +bool JSDataView::set(ExecState*, JSObject*, unsigned, unsigned) +{ + UNREACHABLE_FOR_PLATFORM(); + return false; +} + +PassRefPtr<DataView> JSDataView::typedImpl() +{ + return DataView::create(buffer(), byteOffset(), length()); +} + +bool JSDataView::getOwnPropertySlot( + JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSDataView* thisObject = jsCast<JSDataView*>(object); + if (propertyName == exec->propertyNames().byteLength) { + slot.setValue(thisObject, DontEnum | ReadOnly, jsNumber(thisObject->m_length)); + return true; + } + + return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +ArrayBuffer* JSDataView::slowDownAndWasteMemory(JSArrayBufferView*) +{ + UNREACHABLE_FOR_PLATFORM(); + return 0; +} + +PassRefPtr<ArrayBufferView> JSDataView::getTypedArrayImpl(JSArrayBufferView* object) +{ + JSDataView* thisObject = jsCast<JSDataView*>(object); + return thisObject->typedImpl(); +} + +Structure* JSDataView::createStructure( + VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create( + vm, globalObject, prototype, TypeInfo(DataViewType, StructureFlags), info(), + NonArray); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSDataView.h b/Source/JavaScriptCore/runtime/JSDataView.h new file mode 100644 index 000000000..950d1d678 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDataView.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSDataView_h +#define JSDataView_h + +#include "DataView.h" +#include "JSArrayBufferView.h" + +namespace JSC { + +class JSDataView : public JSArrayBufferView { +public: + typedef JSArrayBufferView Base; + static const unsigned elementSize = 1; + +protected: + JSDataView(VM&, ConstructionContext&, ArrayBuffer*); + +public: + static JSDataView* create( + ExecState*, Structure*, PassRefPtr<ArrayBuffer>, unsigned byteOffset, + unsigned byteLength); + + // Dummy methods, which don't actually work; these are just in place to + // placate some template specialization we do elsewhere. + static JSDataView* createUninitialized(ExecState*, Structure*, unsigned length); + static JSDataView* create(ExecState*, Structure*, unsigned length); + bool set(ExecState*, JSObject*, unsigned offset, unsigned length); + + ArrayBuffer* buffer() const { return m_buffer; } + + PassRefPtr<DataView> typedImpl(); + + static const TypedArrayType TypedArrayStorageType = TypeDataView; + +protected: + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + + static ArrayBuffer* slowDownAndWasteMemory(JSArrayBufferView*); + static PassRefPtr<ArrayBufferView> getTypedArrayImpl(JSArrayBufferView*); + +public: + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + DECLARE_EXPORT_INFO; + +private: + ArrayBuffer* m_buffer; +}; + +} // namespace JSC + +#endif // JSDataView_h + diff --git a/Source/JavaScriptCore/runtime/JSDataViewPrototype.cpp b/Source/JavaScriptCore/runtime/JSDataViewPrototype.cpp new file mode 100644 index 000000000..dd5b44e5b --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDataViewPrototype.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSDataViewPrototype.h" + +#include "Error.h" +#include "JSDataView.h" +#include "Lookup.h" +#include "JSCInlines.h" +#include "ToNativeFromValue.h" +#include "TypedArrayAdaptors.h" +#include <wtf/FlipBytes.h> + +namespace JSC { + +/* Source for JSDataViewPrototype.lut.h +@begin dataViewTable + getInt8 dataViewProtoFuncGetInt8 DontEnum|Function 0 + getUint8 dataViewProtoFuncGetUint8 DontEnum|Function 0 + getInt16 dataViewProtoFuncGetInt16 DontEnum|Function 0 + getUint16 dataViewProtoFuncGetUint16 DontEnum|Function 0 + getInt32 dataViewProtoFuncGetInt32 DontEnum|Function 0 + getUint32 dataViewProtoFuncGetUint32 DontEnum|Function 0 + getFloat32 dataViewProtoFuncGetFloat32 DontEnum|Function 0 + getFloat64 dataViewProtoFuncGetFloat64 DontEnum|Function 0 + setInt8 dataViewProtoFuncSetInt8 DontEnum|Function 0 + setUint8 dataViewProtoFuncSetUint8 DontEnum|Function 0 + setInt16 dataViewProtoFuncSetInt16 DontEnum|Function 0 + setUint16 dataViewProtoFuncSetUint16 DontEnum|Function 0 + setInt32 dataViewProtoFuncSetInt32 DontEnum|Function 0 + setUint32 dataViewProtoFuncSetUint32 DontEnum|Function 0 + setFloat32 dataViewProtoFuncSetFloat32 DontEnum|Function 0 + setFloat64 dataViewProtoFuncSetFloat64 DontEnum|Function 0 +@end +*/ + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetInt8(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetInt16(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetInt32(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetUint8(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetUint16(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetUint32(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetFloat32(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetFloat64(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetInt8(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetInt16(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetInt32(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetUint8(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetUint16(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetUint32(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetFloat32(ExecState*); +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetFloat64(ExecState*); + +} + +#include "JSDataViewPrototype.lut.h" + +namespace JSC { + +const ClassInfo JSDataViewPrototype::s_info = { + "DataViewPrototype", &Base::s_info, &dataViewTable, + CREATE_METHOD_TABLE(JSDataViewPrototype) +}; + +JSDataViewPrototype::JSDataViewPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +JSDataViewPrototype* JSDataViewPrototype::create(VM& vm, Structure* structure) +{ + JSDataViewPrototype* prototype = + new (NotNull, allocateCell<JSDataViewPrototype>(vm.heap)) + JSDataViewPrototype(vm, structure); + prototype->finishCreation(vm); + return prototype; +} + +Structure* JSDataViewPrototype::createStructure( + VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create( + vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +bool JSDataViewPrototype::getOwnPropertySlot( + JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>( + exec, dataViewTable, jsCast<JSDataViewPrototype*>(object), + propertyName, slot); +} + +template<typename Adaptor> +EncodedJSValue getData(ExecState* exec) +{ + JSDataView* dataView = jsDynamicCast<JSDataView*>(exec->thisValue()); + if (!dataView) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Receiver of DataView method must be a DataView"))); + + if (!exec->argumentCount()) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Need at least one argument (the byteOffset)"))); + + unsigned byteOffset = exec->uncheckedArgument(0).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + bool littleEndian = false; + unsigned elementSize = sizeof(typename Adaptor::Type); + if (elementSize > 1 && exec->argumentCount() >= 2) { + littleEndian = exec->uncheckedArgument(1).toBoolean(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + unsigned byteLength = dataView->length(); + if (elementSize > byteLength || byteOffset > byteLength - elementSize) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("Out of bounds access"))); + + const unsigned dataSize = sizeof(typename Adaptor::Type); + union { + typename Adaptor::Type value; + uint8_t rawBytes[dataSize]; + } u = { }; + + uint8_t* dataPtr = static_cast<uint8_t*>(dataView->vector()) + byteOffset; + + if (needToFlipBytesIfLittleEndian(littleEndian)) { + for (unsigned i = dataSize; i--;) + u.rawBytes[i] = *dataPtr++; + } else { + for (unsigned i = 0; i < dataSize; i++) + u.rawBytes[i] = *dataPtr++; + } + + return JSValue::encode(Adaptor::toJSValue(u.value)); +} + +template<typename Adaptor> +EncodedJSValue setData(ExecState* exec) +{ + JSDataView* dataView = jsDynamicCast<JSDataView*>(exec->thisValue()); + if (!dataView) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Receiver of DataView method must be a DataView"))); + + if (exec->argumentCount() < 2) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Need at least two argument (the byteOffset and value)"))); + + unsigned byteOffset = exec->uncheckedArgument(0).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + const unsigned dataSize = sizeof(typename Adaptor::Type); + union { + typename Adaptor::Type value; + uint8_t rawBytes[dataSize]; + } u; + + u.value = toNativeFromValue<Adaptor>(exec, exec->uncheckedArgument(1)); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + bool littleEndian = false; + unsigned elementSize = sizeof(typename Adaptor::Type); + if (elementSize > 1 && exec->argumentCount() >= 3) { + littleEndian = exec->uncheckedArgument(2).toBoolean(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + unsigned byteLength = dataView->length(); + if (elementSize > byteLength || byteOffset > byteLength - elementSize) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("Out of bounds access"))); + + uint8_t* dataPtr = static_cast<uint8_t*>(dataView->vector()) + byteOffset; + + if (needToFlipBytesIfLittleEndian(littleEndian)) { + for (unsigned i = dataSize; i--;) + *dataPtr++ = u.rawBytes[i]; + } else { + for (unsigned i = 0; i < dataSize; i++) + *dataPtr++ = u.rawBytes[i]; + } + + return JSValue::encode(jsUndefined()); +} + +#if COMPILER(CLANG) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#endif + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetInt8(ExecState* exec) +{ + return getData<Int8Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetInt16(ExecState* exec) +{ + return getData<Int16Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetInt32(ExecState* exec) +{ + return getData<Int32Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetUint8(ExecState* exec) +{ + return getData<Uint8Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetUint16(ExecState* exec) +{ + return getData<Uint16Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetUint32(ExecState* exec) +{ + return getData<Uint32Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetFloat32(ExecState* exec) +{ + return getData<Float32Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncGetFloat64(ExecState* exec) +{ + return getData<Float64Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetInt8(ExecState* exec) +{ + return setData<Int8Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetInt16(ExecState* exec) +{ + return setData<Int16Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetInt32(ExecState* exec) +{ + return setData<Int32Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetUint8(ExecState* exec) +{ + return setData<Uint8Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetUint16(ExecState* exec) +{ + return setData<Uint16Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetUint32(ExecState* exec) +{ + return setData<Uint32Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetFloat32(ExecState* exec) +{ + return setData<Float32Adaptor>(exec); +} + +EncodedJSValue JSC_HOST_CALL dataViewProtoFuncSetFloat64(ExecState* exec) +{ + return setData<Float64Adaptor>(exec); +} +#if COMPILER(CLANG) +#pragma clang diagnostic pop +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSDataViewPrototype.h b/Source/JavaScriptCore/runtime/JSDataViewPrototype.h new file mode 100644 index 000000000..bc72455cc --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDataViewPrototype.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSDataViewPrototype_h +#define JSDataViewPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class JSDataViewPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + +protected: + JSDataViewPrototype(VM&, Structure*); + +public: + static JSDataViewPrototype* create(VM&, Structure*); + + DECLARE_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + +protected: + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // JSDataViewPrototype_h + diff --git a/Source/JavaScriptCore/runtime/JSDateMath.cpp b/Source/JavaScriptCore/runtime/JSDateMath.cpp new file mode 100644 index 000000000..1482e312f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDateMath.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2010 &yet, LLC. (nate@andyet.net) + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + + * Copyright 2006-2008 the V8 project authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 + * OWNER 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 "config.h" +#include "JSDateMath.h" + +#include "JSObject.h" +#include "JSScope.h" +#include "JSCInlines.h" + +#include <algorithm> +#include <limits.h> +#include <limits> +#include <stdint.h> +#include <time.h> +#include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> +#include <wtf/CurrentTime.h> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/text/StringBuilder.h> + +#if HAVE(ERRNO_H) +#include <errno.h> +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if HAVE(SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +using namespace WTF; + +namespace JSC { + +static inline double timeToMS(double hour, double min, double sec, double ms) +{ + return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms); +} + +static inline int msToSeconds(double ms) +{ + double result = fmod(floor(ms / msPerSecond), secondsPerMinute); + if (result < 0) + result += secondsPerMinute; + return static_cast<int>(result); +} + +// 0: Sunday, 1: Monday, etc. +static inline int msToWeekDay(double ms) +{ + int wd = (static_cast<int>(msToDays(ms)) + 4) % 7; + if (wd < 0) + wd += 7; + return wd; +} + +// Get the combined UTC + DST offset for the time passed in. +// +// NOTE: The implementation relies on the fact that no time zones have +// more than one daylight savings offset change per month. +// If this function is called with NaN it returns NaN. +static LocalTimeOffset localTimeOffset(VM& vm, double ms, WTF::TimeType inputTimeType = WTF::UTCTime) +{ + LocalTimeOffsetCache& cache = vm.localTimeOffsetCache; + double start = cache.start; + double end = cache.end; + WTF::TimeType cachedTimeType = cache.timeType; + + if (cachedTimeType == inputTimeType && start <= ms) { + // If the time fits in the cached interval, return the cached offset. + if (ms <= end) return cache.offset; + + // Compute a possible new interval end. + double newEnd = end + cache.increment; + + if (ms <= newEnd) { + LocalTimeOffset endOffset = calculateLocalTimeOffset(newEnd, inputTimeType); + if (cache.offset == endOffset) { + // If the offset at the end of the new interval still matches + // the offset in the cache, we grow the cached time interval + // and return the offset. + cache.end = newEnd; + cache.increment = msPerMonth; + return endOffset; + } + LocalTimeOffset offset = calculateLocalTimeOffset(ms, inputTimeType); + if (offset == endOffset) { + // The offset at the given time is equal to the offset at the + // new end of the interval, so that means that we've just skipped + // the point in time where the DST offset change occurred. Updated + // the interval to reflect this and reset the increment. + cache.start = ms; + cache.end = newEnd; + cache.increment = msPerMonth; + } else { + // The interval contains a DST offset change and the given time is + // before it. Adjust the increment to avoid a linear search for + // the offset change point and change the end of the interval. + cache.increment /= 3; + cache.end = ms; + } + // Update the offset in the cache and return it. + cache.offset = offset; + return offset; + } + } + + // Compute the DST offset for the time and shrink the cache interval + // to only contain the time. This allows fast repeated DST offset + // computations for the same time. + LocalTimeOffset offset = calculateLocalTimeOffset(ms, inputTimeType); + cache.offset = offset; + cache.start = ms; + cache.end = ms; + cache.increment = msPerMonth; + cache.timeType = inputTimeType; + return offset; +} + +double gregorianDateTimeToMS(VM& vm, const GregorianDateTime& t, double milliSeconds, WTF::TimeType inputTimeType) +{ + double day = dateToDaysFrom1970(t.year(), t.month(), t.monthDay()); + double ms = timeToMS(t.hour(), t.minute(), t.second(), milliSeconds); + double localTimeResult = (day * WTF::msPerDay) + ms; + double localToUTCTimeOffset = inputTimeType == LocalTime + ? localTimeOffset(vm, localTimeResult, inputTimeType).offset : 0; + + return localTimeResult - localToUTCTimeOffset; +} + +// input is UTC +void msToGregorianDateTime(VM& vm, double ms, WTF::TimeType outputTimeType, GregorianDateTime& tm) +{ + LocalTimeOffset localTime; + if (outputTimeType == WTF::LocalTime) { + localTime = localTimeOffset(vm, ms); + ms += localTime.offset; + } + + const int year = msToYear(ms); + tm.setSecond(msToSeconds(ms)); + tm.setMinute(msToMinutes(ms)); + tm.setHour(msToHours(ms)); + tm.setWeekDay(msToWeekDay(ms)); + tm.setYearDay(dayInYear(ms, year)); + tm.setMonthDay(dayInMonthFromDayInYear(tm.yearDay(), isLeapYear(year))); + tm.setMonth(monthFromDayInYear(tm.yearDay(), isLeapYear(year))); + tm.setYear(year); + tm.setIsDST(localTime.isDST); + tm.setUtcOffset(localTime.offset / WTF::msPerSecond); +} + +double parseDateFromNullTerminatedCharacters(VM& vm, const char* dateString) +{ + bool haveTZ; + int offset; + double localTimeMS = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); + if (std::isnan(localTimeMS)) + return std::numeric_limits<double>::quiet_NaN(); + + // fall back to local timezone. + if (!haveTZ) + offset = localTimeOffset(vm, localTimeMS, WTF::LocalTime).offset / WTF::msPerMinute; + + return localTimeMS - (offset * WTF::msPerMinute); +} + +double parseDate(VM& vm, const String& date) +{ + if (date == vm.cachedDateString) + return vm.cachedDateStringValue; + double value = parseES5DateFromNullTerminatedCharacters(date.utf8().data()); + if (std::isnan(value)) + value = parseDateFromNullTerminatedCharacters(vm, date.utf8().data()); + vm.cachedDateString = date; + vm.cachedDateStringValue = value; + return value; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSDateMath.h b/Source/JavaScriptCore/runtime/JSDateMath.h new file mode 100644 index 000000000..4e4d16ff5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDateMath.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 Research In Motion Limited. All rights reserved. + * + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + */ + +#ifndef JSDateMath_h +#define JSDateMath_h + +#include <wtf/DateMath.h> +#include <wtf/GregorianDateTime.h> + +namespace JSC { + +class VM; + +JS_EXPORT_PRIVATE void msToGregorianDateTime(VM&, double, WTF::TimeType outputTimeType, GregorianDateTime&); +JS_EXPORT_PRIVATE double gregorianDateTimeToMS(VM&, const GregorianDateTime&, double, WTF::TimeType inputTimeType); +JS_EXPORT_PRIVATE double getUTCOffset(VM&); +JS_EXPORT_PRIVATE double parseDateFromNullTerminatedCharacters(VM&, const char* dateString); +JS_EXPORT_PRIVATE double parseDate(VM&, const WTF::String&); + +} // namespace JSC + +#endif // JSDateMath_h diff --git a/Source/JavaScriptCore/runtime/JSDestructibleObject.h b/Source/JavaScriptCore/runtime/JSDestructibleObject.h new file mode 100644 index 000000000..d687b8420 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDestructibleObject.h @@ -0,0 +1,34 @@ +#ifndef JSDestructibleObject_h +#define JSDestructibleObject_h + +#include "JSObject.h" + +namespace JSC { + +struct ClassInfo; + +class JSDestructibleObject : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static const bool needsDestruction = true; + + const ClassInfo* classInfo() const { return m_classInfo; } + + static ptrdiff_t classInfoOffset() { return OBJECT_OFFSETOF(JSDestructibleObject, m_classInfo); } + +protected: + JSDestructibleObject(VM& vm, Structure* structure, Butterfly* butterfly = 0) + : JSNonFinalObject(vm, structure, butterfly) + , m_classInfo(structure->classInfo()) + { + ASSERT(m_classInfo); + } + +private: + const ClassInfo* m_classInfo; +}; + +} // namespace JSC + +#endif diff --git a/Source/JavaScriptCore/runtime/JSEnvironmentRecord.cpp b/Source/JavaScriptCore/runtime/JSEnvironmentRecord.cpp new file mode 100644 index 000000000..f1d769592 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSEnvironmentRecord.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007, 2008, 2012, 2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "JSEnvironmentRecord.h" + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo JSEnvironmentRecord::s_info = { "EnvironmentRecord", &Base::s_info, 0, CREATE_METHOD_TABLE(JSEnvironmentRecord) }; + +void JSEnvironmentRecord::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSEnvironmentRecord* thisObject = jsCast<JSEnvironmentRecord*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.appendValues(thisObject->variables(), thisObject->symbolTable()->scopeSize()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSEnvironmentRecord.h b/Source/JavaScriptCore/runtime/JSEnvironmentRecord.h new file mode 100644 index 000000000..fae8f6acf --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSEnvironmentRecord.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2007, 2008, 2012, 2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef JSEnvironmentRecord_h +#define JSEnvironmentRecord_h + +#include "JSObject.h" +#include "JSSymbolTableObject.h" +#include "Register.h" +#include "SymbolTable.h" + +namespace JSC { + +class LLIntOffsetsExtractor; +class Register; + +class JSEnvironmentRecord : public JSSymbolTableObject { + friend class JIT; + friend class LLIntOffsetsExtractor; + +public: + typedef JSSymbolTableObject Base; + static const unsigned StructureFlags = Base::StructureFlags; + + WriteBarrierBase<Unknown>* variables() + { + return bitwise_cast<WriteBarrierBase<Unknown>*>(bitwise_cast<char*>(this) + offsetOfVariables()); + } + + bool isValid(ScopeOffset offset) + { + return !!offset && offset.offset() < symbolTable()->scopeSize(); + } + + WriteBarrierBase<Unknown>& variableAt(ScopeOffset offset) + { + ASSERT(isValid(offset)); + return variables()[offset.offset()]; + } + + static size_t offsetOfVariables() + { + return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<Unknown>)>(sizeof(JSEnvironmentRecord)); + } + + static ptrdiff_t offsetOfVariable(ScopeOffset offset) + { + return offsetOfVariables() + offset.offset() * sizeof(WriteBarrier<Unknown>); + } + + DECLARE_INFO; + + static size_t allocationSizeForScopeSize(unsigned scopeSize) + { + return offsetOfVariables() + scopeSize * sizeof(WriteBarrier<Unknown>); + } + + static size_t allocationSize(SymbolTable* symbolTable) + { + return allocationSizeForScopeSize(symbolTable->scopeSize()); + } + +protected: + JSEnvironmentRecord( + VM& vm, + Structure* structure, + JSScope* scope, + SymbolTable* symbolTable) + : Base(vm, structure, scope, symbolTable) + { + } + + void finishCreationUninitialized(VM& vm) + { + Base::finishCreation(vm); + } + + void finishCreation(VM& vm, JSValue value) + { + finishCreationUninitialized(vm); + ASSERT(value == jsUndefined() || value == jsTDZValue()); + for (unsigned i = symbolTable()->scopeSize(); i--;) { + // Filling this with undefined/TDZEmptyValue is useful because that's what variables start out as. + variableAt(ScopeOffset(i)).setStartingValue(value); + } + } + + static void visitChildren(JSCell*, SlotVisitor&); +}; + +} // namespace JSC + +#endif // JSEnvironmentRecord_h diff --git a/Source/JavaScriptCore/runtime/JSExportMacros.h b/Source/JavaScriptCore/runtime/JSExportMacros.h new file mode 100644 index 000000000..bd46591b4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSExportMacros.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + * + * This file handles shared library symbol export decorations. It is recommended + * that all WebKit projects use these definitions so that symbol exports work + * properly on all platforms and compilers that WebKit builds under. + */ + +#ifndef JSExportMacros_h +#define JSExportMacros_h + +#include <wtf/ExportMacros.h> + +// See note in wtf/Platform.h for more info on EXPORT_MACROS. +#if USE(EXPORT_MACROS) + +#if defined(BUILDING_JavaScriptCore) || defined(STATICALLY_LINKED_WITH_JavaScriptCore) +#define JS_EXPORT_PRIVATE WTF_EXPORT +#else +#define JS_EXPORT_PRIVATE WTF_IMPORT +#endif + +#define JS_EXPORT_HIDDEN WTF_HIDDEN +#define JS_EXPORTDATA JS_EXPORT_PRIVATE +#define JS_EXPORTCLASS JS_EXPORT_PRIVATE + +#else // !USE(EXPORT_MACROS) + +#if OS(WINDOWS) && !COMPILER(GCC_OR_CLANG) + +#if defined(BUILDING_JavaScriptCore) || defined(STATICALLY_LINKED_WITH_JavaScriptCore) +#define JS_EXPORTDATA __declspec(dllexport) +#else +#define JS_EXPORTDATA __declspec(dllimport) +#endif + +#define JS_EXPORTCLASS JS_EXPORTDATA + +#else // !PLATFORM... + +#define JS_EXPORTDATA +#define JS_EXPORTCLASS + +#endif // !PLATFORM... + +#define JS_EXPORT_PRIVATE +#define JS_EXPORT_HIDDEN + +#endif // USE(EXPORT_MACROS) + +#endif // JSExportMacros_h diff --git a/Source/JavaScriptCore/runtime/JSFloat32Array.h b/Source/JavaScriptCore/runtime/JSFloat32Array.h new file mode 100644 index 000000000..ca98e0ab2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFloat32Array.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSFloat32Array_h +#define JSFloat32Array_h + +#include "JSTypedArrays.h" + +#endif // JSFloat32Array_h + diff --git a/Source/JavaScriptCore/runtime/JSFloat64Array.h b/Source/JavaScriptCore/runtime/JSFloat64Array.h new file mode 100644 index 000000000..8c143e470 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFloat64Array.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSFloat64Array_h +#define JSFloat64Array_h + +#include "JSTypedArrays.h" + +#endif // JSFloat64Array_h + diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp new file mode 100644 index 000000000..0f7354ed7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFunction.cpp @@ -0,0 +1,578 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * Copyright (C) 2015 Canon Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "JSFunction.h" + +#include "ClonedArguments.h" +#include "CodeBlock.h" +#include "CommonIdentifiers.h" +#include "CallFrame.h" +#include "ExceptionHelpers.h" +#include "FunctionPrototype.h" +#include "GetterSetter.h" +#include "JSArray.h" +#include "JSBoundFunction.h" +#include "JSCInlines.h" +#include "JSFunctionInlines.h" +#include "JSGlobalObject.h" +#include "JSNotAnObject.h" +#include "Interpreter.h" +#include "ObjectConstructor.h" +#include "ObjectPrototype.h" +#include "Parser.h" +#include "PropertyNameArray.h" +#include "StackVisitor.h" + +namespace JSC { + +EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) +{ + return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); +} + +const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(JSFunction) }; + +bool JSFunction::isHostFunctionNonInline() const +{ + return isHostFunction(); +} + +JSFunction* JSFunction::create(VM& vm, FunctionExecutable* executable, JSScope* scope) +{ + JSFunction* result = createImpl(vm, executable, scope); + executable->singletonFunction()->notifyWrite(vm, result, "Allocating a function"); + return result; +} + +static inline NativeExecutable* getNativeExecutable(VM& vm, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor) +{ +#if !ENABLE(JIT) + UNUSED_PARAM(intrinsic); +#else + if (intrinsic != NoIntrinsic && vm.canUseJIT()) { + ASSERT(nativeConstructor == callHostFunctionAsConstructor); + return vm.getHostFunction(nativeFunction, intrinsic); + } +#endif + return vm.getHostFunction(nativeFunction, nativeConstructor); +} + +JSFunction* JSFunction::create(VM& vm, JSGlobalObject* globalObject, int length, const String& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor) +{ + NativeExecutable* executable = getNativeExecutable(vm, nativeFunction, intrinsic, nativeConstructor); + JSFunction* function = new (NotNull, allocateCell<JSFunction>(vm.heap)) JSFunction(vm, globalObject, globalObject->functionStructure()); + // Can't do this during initialization because getHostFunction might do a GC allocation. + function->finishCreation(vm, executable, length, name); + return function; +} + +class JSStdFunction : public JSFunction { +public: + JSStdFunction(VM& vm, JSGlobalObject* object, Structure* structure, NativeStdFunction&& function) + : JSFunction(vm, object, structure) + , stdFunction(WTF::move(function)) { } + + NativeStdFunction stdFunction; +}; + +static EncodedJSValue JSC_HOST_CALL runStdFunction(ExecState* state) +{ + JSStdFunction* jsFunction = jsCast<JSStdFunction*>(state->callee()); + ASSERT(jsFunction); + return jsFunction->stdFunction(state); +} + +JSFunction* JSFunction::create(VM& vm, JSGlobalObject* globalObject, int length, const String& name, NativeStdFunction&& nativeStdFunction, Intrinsic intrinsic, NativeFunction nativeConstructor) +{ + NativeExecutable* executable = getNativeExecutable(vm, runStdFunction, intrinsic, nativeConstructor); + JSStdFunction* function = new (NotNull, allocateCell<JSStdFunction>(vm.heap)) JSStdFunction(vm, globalObject, globalObject->functionStructure(), WTF::move(nativeStdFunction)); + // Can't do this during initialization because getHostFunction might do a GC allocation. + function->finishCreation(vm, executable, length, name); + return function; +} + +JSFunction::JSFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure) + : Base(vm, globalObject, structure) + , m_executable() +{ +} + +void JSFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + m_executable.set(vm, this, executable); + putDirect(vm, vm.propertyNames->name, jsString(&vm, name), DontDelete | ReadOnly | DontEnum); + putDirect(vm, vm.propertyNames->length, jsNumber(length), DontDelete | ReadOnly | DontEnum); +} + +JSFunction* JSFunction::createBuiltinFunction(VM& vm, FunctionExecutable* executable, JSGlobalObject* globalObject) +{ + JSFunction* function = create(vm, executable, globalObject); + function->putDirect(vm, vm.propertyNames->name, jsString(&vm, executable->name().string()), DontDelete | ReadOnly | DontEnum); + function->putDirect(vm, vm.propertyNames->length, jsNumber(executable->parameterCount()), DontDelete | ReadOnly | DontEnum); + return function; +} + +JSFunction* JSFunction::createBuiltinFunction(VM& vm, FunctionExecutable* executable, JSGlobalObject* globalObject, const String& name) +{ + JSFunction* function = create(vm, executable, globalObject); + function->putDirect(vm, vm.propertyNames->name, jsString(&vm, name), DontDelete | ReadOnly | DontEnum); + function->putDirect(vm, vm.propertyNames->length, jsNumber(executable->parameterCount()), DontDelete | ReadOnly | DontEnum); + return function; +} + +FunctionRareData* JSFunction::allocateAndInitializeRareData(ExecState* exec, size_t inlineCapacity) +{ + ASSERT(!m_rareData); + VM& vm = exec->vm(); + JSObject* prototype = jsDynamicCast<JSObject*>(get(exec, vm.propertyNames->prototype)); + if (!prototype) + prototype = globalObject()->objectPrototype(); + FunctionRareData* rareData = FunctionRareData::create(vm, prototype, inlineCapacity); + + // A DFG compilation thread may be trying to read the rare data + // We want to ensure that it sees it properly allocated + WTF::storeStoreFence(); + + m_rareData.set(vm, this, rareData); + return m_rareData.get(); +} + +FunctionRareData* JSFunction::initializeRareData(ExecState* exec, size_t inlineCapacity) +{ + ASSERT(!!m_rareData); + VM& vm = exec->vm(); + JSObject* prototype = jsDynamicCast<JSObject*>(get(exec, vm.propertyNames->prototype)); + if (!prototype) + prototype = globalObject()->objectPrototype(); + m_rareData->initialize(globalObject()->vm(), prototype, inlineCapacity); + return m_rareData.get(); +} + +String JSFunction::name(ExecState* exec) +{ + return get(exec, exec->vm().propertyNames->name).toWTFString(exec); +} + +String JSFunction::displayName(ExecState* exec) +{ + JSValue displayName = getDirect(exec->vm(), exec->vm().propertyNames->displayName); + + if (displayName && isJSString(displayName)) + return asString(displayName)->tryGetValue(); + + return String(); +} + +const String JSFunction::calculatedDisplayName(ExecState* exec) +{ + const String explicitName = displayName(exec); + + if (!explicitName.isEmpty()) + return explicitName; + + const String actualName = name(exec); + if (!actualName.isEmpty() || isHostOrBuiltinFunction()) + return actualName; + + return jsExecutable()->inferredName().string(); +} + +const SourceCode* JSFunction::sourceCode() const +{ + if (isHostOrBuiltinFunction()) + return 0; + return &jsExecutable()->source(); +} + +void JSFunction::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_executable); + if (thisObject->m_rareData) + visitor.append(&thisObject->m_rareData); +} + +CallType JSFunction::getCallData(JSCell* cell, CallData& callData) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + if (thisObject->isHostFunction()) { + callData.native.function = thisObject->nativeFunction(); + return CallTypeHost; + } + callData.js.functionExecutable = thisObject->jsExecutable(); + callData.js.scope = thisObject->scope(); + return CallTypeJS; +} + +class RetrieveArgumentsFunctor { +public: + RetrieveArgumentsFunctor(JSFunction* functionObj) + : m_targetCallee(jsDynamicCast<JSObject*>(functionObj)) + , m_result(jsNull()) + { + } + + JSValue result() const { return m_result; } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + JSObject* callee = visitor->callee(); + if (callee != m_targetCallee) + return StackVisitor::Continue; + + m_result = JSValue(visitor->createArguments()); + return StackVisitor::Done; + } + +private: + JSObject* m_targetCallee; + JSValue m_result; +}; + +static JSValue retrieveArguments(ExecState* exec, JSFunction* functionObj) +{ + RetrieveArgumentsFunctor functor(functionObj); + exec->iterate(functor); + return functor.result(); +} + +EncodedJSValue JSFunction::argumentsGetter(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + JSFunction* thisObj = jsCast<JSFunction*>(slotBase); + ASSERT(!thisObj->isHostFunction()); + + return JSValue::encode(retrieveArguments(exec, thisObj)); +} + +class RetrieveCallerFunctionFunctor { +public: + RetrieveCallerFunctionFunctor(JSFunction* functionObj) + : m_targetCallee(jsDynamicCast<JSObject*>(functionObj)) + , m_hasFoundFrame(false) + , m_hasSkippedToCallerFrame(false) + , m_result(jsNull()) + { + } + + JSValue result() const { return m_result; } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + JSObject* callee = visitor->callee(); + + if (callee && callee->inherits(JSBoundFunction::info())) + return StackVisitor::Continue; + + if (!m_hasFoundFrame && (callee != m_targetCallee)) + return StackVisitor::Continue; + + m_hasFoundFrame = true; + if (!m_hasSkippedToCallerFrame) { + m_hasSkippedToCallerFrame = true; + return StackVisitor::Continue; + } + + if (callee) + m_result = callee; + return StackVisitor::Done; + } + +private: + JSObject* m_targetCallee; + bool m_hasFoundFrame; + bool m_hasSkippedToCallerFrame; + JSValue m_result; +}; + +static JSValue retrieveCallerFunction(ExecState* exec, JSFunction* functionObj) +{ + RetrieveCallerFunctionFunctor functor(functionObj); + exec->iterate(functor); + return functor.result(); +} + +EncodedJSValue JSFunction::callerGetter(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + JSFunction* thisObj = jsCast<JSFunction*>(slotBase); + ASSERT(!thisObj->isHostFunction()); + JSValue caller = retrieveCallerFunction(exec, thisObj); + + // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller. + if (!caller.isObject() || !asObject(caller)->inherits(JSFunction::info())) { + // It isn't a JSFunction, but if it is a JSCallee from a program or call eval, return null. + if (jsDynamicCast<JSCallee*>(caller)) + return JSValue::encode(jsNull()); + return JSValue::encode(caller); + } + JSFunction* function = jsCast<JSFunction*>(caller); + if (function->isHostOrBuiltinFunction() || !function->jsExecutable()->isStrictMode()) + return JSValue::encode(caller); + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Function.caller used to retrieve strict caller"))); +} + +EncodedJSValue JSFunction::lengthGetter(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + JSFunction* thisObj = jsCast<JSFunction*>(slotBase); + ASSERT(!thisObj->isHostFunction()); + return JSValue::encode(jsNumber(thisObj->jsExecutable()->parameterCount())); +} + +EncodedJSValue JSFunction::nameGetter(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + JSFunction* thisObj = jsCast<JSFunction*>(slotBase); + ASSERT(!thisObj->isHostFunction()); + return JSValue::encode(thisObj->jsExecutable()->nameValue()); +} + +bool JSFunction::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSFunction* thisObject = jsCast<JSFunction*>(object); + if (thisObject->isHostOrBuiltinFunction()) + return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + + if (propertyName == exec->propertyNames().prototype) { + VM& vm = exec->vm(); + unsigned attributes; + PropertyOffset offset = thisObject->getDirectOffset(vm, propertyName, attributes); + if (!isValidOffset(offset)) { + JSObject* prototype = constructEmptyObject(exec); + prototype->putDirect(vm, exec->propertyNames().constructor, thisObject, DontEnum); + thisObject->putDirect(vm, exec->propertyNames().prototype, prototype, DontDelete | DontEnum); + offset = thisObject->getDirectOffset(vm, exec->propertyNames().prototype, attributes); + ASSERT(isValidOffset(offset)); + } + + slot.setValue(thisObject, attributes, thisObject->getDirect(offset), offset); + } + + if (propertyName == exec->propertyNames().arguments) { + if (thisObject->jsExecutable()->isStrictMode()) { + bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + if (!result) { + thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); + result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + ASSERT(result); + } + return result; + } + slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, argumentsGetter); + return true; + } + + if (propertyName == exec->propertyNames().length) { + slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, lengthGetter); + return true; + } + + if (propertyName == exec->propertyNames().name) { + slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, nameGetter); + return true; + } + + if (propertyName == exec->propertyNames().caller) { + if (thisObject->jsExecutable()->isStrictMode()) { + bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + if (!result) { + thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); + result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + ASSERT(result); + } + return result; + } + slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, callerGetter); + return true; + } + + return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSFunction* thisObject = jsCast<JSFunction*>(object); + if (!thisObject->isHostOrBuiltinFunction() && mode.includeDontEnumProperties()) { + VM& vm = exec->vm(); + // Make sure prototype has been reified. + PropertySlot slot(thisObject); + thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, exec, vm.propertyNames->prototype, slot); + + propertyNames.add(vm.propertyNames->arguments); + propertyNames.add(vm.propertyNames->caller); + propertyNames.add(vm.propertyNames->length); + propertyNames.add(vm.propertyNames->name); + } + Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); +} + +void JSFunction::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + if (thisObject->isHostOrBuiltinFunction()) { + Base::put(thisObject, exec, propertyName, value, slot); + return; + } + if (propertyName == exec->propertyNames().prototype) { + // Make sure prototype has been reified, such that it can only be overwritten + // following the rules set out in ECMA-262 8.12.9. + PropertySlot slot(thisObject); + thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, propertyName, slot); + if (thisObject->m_rareData) + thisObject->m_rareData->clear("Store to prototype property of a function"); + // Don't allow this to be cached, since a [[Put]] must clear m_rareData. + PutPropertySlot dontCache(thisObject); + Base::put(thisObject, exec, propertyName, value, dontCache); + return; + } + if (thisObject->jsExecutable()->isStrictMode() && (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().caller)) { + // This will trigger the property to be reified, if this is not already the case! + bool okay = thisObject->hasProperty(exec, propertyName); + ASSERT_UNUSED(okay, okay); + Base::put(thisObject, exec, propertyName, value, slot); + return; + } + if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().name || propertyName == exec->propertyNames().caller) { + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + Base::put(thisObject, exec, propertyName, value, slot); +} + +bool JSFunction::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty. + if (!thisObject->isHostOrBuiltinFunction() && !exec->vm().isInDefineOwnProperty() + && (propertyName == exec->propertyNames().arguments + || propertyName == exec->propertyNames().length + || propertyName == exec->propertyNames().name + || propertyName == exec->propertyNames().prototype + || propertyName == exec->propertyNames().caller)) + return false; + return Base::deleteProperty(thisObject, exec, propertyName); +} + +bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) +{ + JSFunction* thisObject = jsCast<JSFunction*>(object); + if (thisObject->isHostOrBuiltinFunction()) + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + + if (propertyName == exec->propertyNames().prototype) { + // Make sure prototype has been reified, such that it can only be overwritten + // following the rules set out in ECMA-262 8.12.9. + PropertySlot slot(thisObject); + thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, propertyName, slot); + if (thisObject->m_rareData) + thisObject->m_rareData->clear("Store to prototype property of a function"); + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + } + + bool valueCheck; + if (propertyName == exec->propertyNames().arguments) { + if (thisObject->jsExecutable()->isStrictMode()) { + PropertySlot slot(thisObject); + if (!Base::getOwnPropertySlot(thisObject, exec, propertyName, slot)) + thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + } + valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), retrieveArguments(exec, thisObject)); + } else if (propertyName == exec->propertyNames().caller) { + if (thisObject->jsExecutable()->isStrictMode()) { + PropertySlot slot(thisObject); + if (!Base::getOwnPropertySlot(thisObject, exec, propertyName, slot)) + thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + } + valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), retrieveCallerFunction(exec, thisObject)); + } else if (propertyName == exec->propertyNames().length) + valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), jsNumber(thisObject->jsExecutable()->parameterCount())); + else if (propertyName == exec->propertyNames().name) + valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), thisObject->jsExecutable()->nameValue()); + else + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); + + if (descriptor.configurablePresent() && descriptor.configurable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property."))); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property."))); + return false; + } + if (descriptor.isAccessorDescriptor()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); + return false; + } + if (descriptor.writablePresent() && descriptor.writable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property."))); + return false; + } + if (!valueCheck) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property."))); + return false; + } + return true; +} + +// ECMA 13.2.2 [[Construct]] +ConstructType JSFunction::getConstructData(JSCell* cell, ConstructData& constructData) +{ + JSFunction* thisObject = jsCast<JSFunction*>(cell); + + if (thisObject->isHostFunction()) { + constructData.native.function = thisObject->nativeConstructor(); + return ConstructTypeHost; + } + + FunctionExecutable* functionExecutable = thisObject->jsExecutable(); + if (functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) + return ConstructTypeNone; + + constructData.js.functionExecutable = functionExecutable; + constructData.js.scope = thisObject->scope(); + return ConstructTypeJS; +} + +String getCalculatedDisplayName(CallFrame* callFrame, JSObject* object) +{ + if (JSFunction* function = jsDynamicCast<JSFunction*>(object)) + return function->calculatedDisplayName(callFrame); + if (InternalFunction* function = jsDynamicCast<InternalFunction*>(object)) + return function->calculatedDisplayName(callFrame); + return ""; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h new file mode 100644 index 000000000..0b87d75c9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFunction.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSFunction_h +#define JSFunction_h + +#include "FunctionRareData.h" +#include "InternalFunction.h" +#include "JSCallee.h" +#include "JSScope.h" +#include "Watchpoint.h" + +namespace JSC { + +class ExecutableBase; +class FunctionExecutable; +class FunctionPrototype; +class JSLexicalEnvironment; +class JSGlobalObject; +class LLIntOffsetsExtractor; +class NativeExecutable; +class SourceCode; +namespace DFG { +class SpeculativeJIT; +class JITCompiler; +} + +typedef std::function<EncodedJSValue (ExecState*)> NativeStdFunction; + +JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState*); + +JS_EXPORT_PRIVATE String getCalculatedDisplayName(CallFrame*, JSObject*); + +class JSFunction : public JSCallee { + friend class JIT; + friend class DFG::SpeculativeJIT; + friend class DFG::JITCompiler; + friend class VM; + +public: + typedef JSCallee Base; + const static unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames; + + static size_t allocationSize(size_t inlineCapacity) + { + ASSERT_UNUSED(inlineCapacity, !inlineCapacity); + return sizeof(JSFunction); + } + + JS_EXPORT_PRIVATE static JSFunction* create(VM&, JSGlobalObject*, int length, const String& name, NativeFunction, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor); + + static JSFunction* createWithInvalidatedReallocationWatchpoint(VM&, FunctionExecutable*, JSScope*); + JS_EXPORT_PRIVATE static JSFunction* create(VM&, JSGlobalObject*, int length, const String& name, NativeStdFunction&&, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor); + + static JSFunction* create(VM&, FunctionExecutable*, JSScope*); + + static JSFunction* createBuiltinFunction(VM&, FunctionExecutable*, JSGlobalObject*); + static JSFunction* createBuiltinFunction(VM&, FunctionExecutable*, JSGlobalObject*, const String& name); + + JS_EXPORT_PRIVATE String name(ExecState*); + JS_EXPORT_PRIVATE String displayName(ExecState*); + const String calculatedDisplayName(ExecState*); + + ExecutableBase* executable() const { return m_executable.get(); } + + // To call either of these methods include Executable.h + bool isHostFunction() const; + FunctionExecutable* jsExecutable() const; + + JS_EXPORT_PRIVATE const SourceCode* sourceCode() const; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + ASSERT(globalObject); + return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info()); + } + + NativeFunction nativeFunction(); + NativeFunction nativeConstructor(); + + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + static inline ptrdiff_t offsetOfExecutable() + { + return OBJECT_OFFSETOF(JSFunction, m_executable); + } + + static inline ptrdiff_t offsetOfRareData() + { + return OBJECT_OFFSETOF(JSFunction, m_rareData); + } + + FunctionRareData* rareData(ExecState* exec, unsigned inlineCapacity) + { + if (UNLIKELY(!m_rareData)) + return allocateAndInitializeRareData(exec, inlineCapacity); + if (UNLIKELY(!m_rareData->isInitialized())) + return initializeRareData(exec, inlineCapacity); + return m_rareData.get(); + } + + FunctionRareData* rareData() + { + FunctionRareData* rareData = m_rareData.get(); + + // The JS thread may be concurrently creating the rare data + // If we see it, we want to ensure it has been properly created + WTF::loadLoadFence(); + + return rareData; + } + + bool isHostOrBuiltinFunction() const; + bool isBuiltinFunction() const; + JS_EXPORT_PRIVATE bool isHostFunctionNonInline() const; + bool isClassConstructorFunction() const; + +protected: + JS_EXPORT_PRIVATE JSFunction(VM&, JSGlobalObject*, Structure*); + JSFunction(VM&, FunctionExecutable*, JSScope*); + + void finishCreation(VM&, NativeExecutable*, int length, const String& name); + using Base::finishCreation; + + FunctionRareData* allocateAndInitializeRareData(ExecState*, size_t inlineCapacity); + FunctionRareData* initializeRareData(ExecState*, size_t inlineCapacity); + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = EnumerationMode()); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + + static void visitChildren(JSCell*, SlotVisitor&); + +private: + static JSFunction* createImpl(VM& vm, FunctionExecutable* executable, JSScope* scope) + { + JSFunction* function = new (NotNull, allocateCell<JSFunction>(vm.heap)) JSFunction(vm, executable, scope); + ASSERT(function->structure()->globalObject()); + function->finishCreation(vm); + return function; + } + + friend class LLIntOffsetsExtractor; + + static EncodedJSValue argumentsGetter(ExecState*, JSObject*, EncodedJSValue, PropertyName); + static EncodedJSValue callerGetter(ExecState*, JSObject*, EncodedJSValue, PropertyName); + static EncodedJSValue lengthGetter(ExecState*, JSObject*, EncodedJSValue, PropertyName); + static EncodedJSValue nameGetter(ExecState*, JSObject*, EncodedJSValue, PropertyName); + + WriteBarrier<ExecutableBase> m_executable; + WriteBarrier<FunctionRareData> m_rareData; +}; + +} // namespace JSC + +#endif // JSFunction_h diff --git a/Source/JavaScriptCore/runtime/JSFunctionInlines.h b/Source/JavaScriptCore/runtime/JSFunctionInlines.h new file mode 100644 index 000000000..f6c6d58ec --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSFunctionInlines.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2013, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSFunctionInlines_h +#define JSFunctionInlines_h + +#include "Executable.h" +#include "JSFunction.h" + +namespace JSC { + +inline JSFunction* JSFunction::createWithInvalidatedReallocationWatchpoint( + VM& vm, FunctionExecutable* executable, JSScope* scope) +{ + ASSERT(executable->singletonFunction()->hasBeenInvalidated()); + return createImpl(vm, executable, scope); +} + +inline JSFunction::JSFunction(VM& vm, FunctionExecutable* executable, JSScope* scope) + : Base(vm, scope, scope->globalObject()->functionStructure()) + , m_executable(vm, this, executable) + , m_rareData() +{ +} + +inline FunctionExecutable* JSFunction::jsExecutable() const +{ + ASSERT(!isHostFunctionNonInline()); + return static_cast<FunctionExecutable*>(m_executable.get()); +} + +inline bool JSFunction::isHostFunction() const +{ + ASSERT(m_executable); + return m_executable->isHostFunction(); +} + +inline bool JSFunction::isBuiltinFunction() const +{ + return !isHostFunction() && jsExecutable()->isBuiltinFunction(); +} + +inline bool JSFunction::isHostOrBuiltinFunction() const +{ + return isHostFunction() || isBuiltinFunction(); +} + +inline bool JSFunction::isClassConstructorFunction() const +{ + return !isHostFunction() && jsExecutable()->isClassConstructorFunction(); +} + +inline NativeFunction JSFunction::nativeFunction() +{ + ASSERT(isHostFunctionNonInline()); + return static_cast<NativeExecutable*>(m_executable.get())->function(); +} + +inline NativeFunction JSFunction::nativeConstructor() +{ + ASSERT(isHostFunctionNonInline()); + return static_cast<NativeExecutable*>(m_executable.get())->constructor(); +} + +inline bool isHostFunction(JSValue value, NativeFunction nativeFunction) +{ + JSFunction* function = jsCast<JSFunction*>(getJSFunction(value)); + if (!function || !function->isHostFunction()) + return false; + return function->nativeFunction() == nativeFunction; +} + +} // namespace JSC + +#endif // JSFunctionInlines_h + diff --git a/Source/JavaScriptCore/runtime/JSGenericTypedArrayView.h b/Source/JavaScriptCore/runtime/JSGenericTypedArrayView.h new file mode 100644 index 000000000..a3a756cf5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGenericTypedArrayView.h @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSGenericTypedArrayView_h +#define JSGenericTypedArrayView_h + +#include "JSArrayBufferView.h" +#include "ToNativeFromValue.h" + +namespace JSC { + +JS_EXPORT_PRIVATE const ClassInfo* getInt8ArrayClassInfo(); +JS_EXPORT_PRIVATE const ClassInfo* getInt16ArrayClassInfo(); +JS_EXPORT_PRIVATE const ClassInfo* getInt32ArrayClassInfo(); +JS_EXPORT_PRIVATE const ClassInfo* getUint8ArrayClassInfo(); +JS_EXPORT_PRIVATE const ClassInfo* getUint8ClampedArrayClassInfo(); +JS_EXPORT_PRIVATE const ClassInfo* getUint16ArrayClassInfo(); +JS_EXPORT_PRIVATE const ClassInfo* getUint32ArrayClassInfo(); +JS_EXPORT_PRIVATE const ClassInfo* getFloat32ArrayClassInfo(); +JS_EXPORT_PRIVATE const ClassInfo* getFloat64ArrayClassInfo(); + +// A typed array view is our representation of a typed array object as seen +// from JavaScript. For example: +// +// var o = new Int8Array(100); +// +// Here, 'o' points to a JSGenericTypedArrayView<int8_t>. +// +// Views contain five fields: +// +// Structure* S // from JSCell +// Butterfly* B // from JSObject +// ElementType* V +// uint32_t L +// TypedArrayMode M +// +// These fields take up a total of four pointer-width words. FIXME: Make +// it take less words! +// +// B is usually unused but may stored some additional "overflow" data for +// one of the modes. V always points to the base of the typed array's data, +// and may point to either GC-managed copied space, or data in the C heap; +// which of those things it points to is governed by the mode although for +// simple accesses to the view you can just read from the pointer either +// way. M specifies the mode of the view. L is the length, in units that +// depend on the view's type. + +// The JSGenericTypedArrayView is templatized by an Adaptor that controls +// the element type and how it's converted; it should obey the following +// interface; I use int8_t as an example: +// +// struct Adaptor { +// typedef int8_t Type; +// typedef Int8Array ViewType; +// typedef JSInt8Array JSViewType; +// static int8_t toNativeFromInt32(int32_t); +// static int8_t toNativeFromUint32(uint32_t); +// static int8_t toNativeFromDouble(double); +// static JSValue toJSValue(int8_t); +// static double toDouble(int8_t); +// template<T> static T::Type convertTo(uint8_t); +// }; + +template<typename Adaptor> +class JSGenericTypedArrayView : public JSArrayBufferView { +public: + typedef JSArrayBufferView Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetPropertyNames | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero; + + static const unsigned elementSize = sizeof(typename Adaptor::Type); + +protected: + JSGenericTypedArrayView(VM&, ConstructionContext&); + +public: + static JSGenericTypedArrayView* create(ExecState*, Structure*, unsigned length); + static JSGenericTypedArrayView* createUninitialized(ExecState*, Structure*, unsigned length); + static JSGenericTypedArrayView* create(ExecState*, Structure*, PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned length); + static JSGenericTypedArrayView* create(VM&, Structure*, PassRefPtr<typename Adaptor::ViewType> impl); + static JSGenericTypedArrayView* create(Structure*, JSGlobalObject*, PassRefPtr<typename Adaptor::ViewType> impl); + + unsigned byteLength() const { return m_length * sizeof(typename Adaptor::Type); } + size_t byteSize() const { return sizeOf(m_length, sizeof(typename Adaptor::Type)); } + + const typename Adaptor::Type* typedVector() const + { + return static_cast<const typename Adaptor::Type*>(m_vector); + } + typename Adaptor::Type* typedVector() + { + return static_cast<typename Adaptor::Type*>(m_vector); + } + + // These methods are meant to match indexed access methods that JSObject + // supports - hence the slight redundancy. + bool canGetIndexQuickly(unsigned i) + { + return i < m_length; + } + bool canSetIndexQuickly(unsigned i) + { + return i < m_length; + } + + typename Adaptor::Type getIndexQuicklyAsNativeValue(unsigned i) + { + ASSERT(i < m_length); + return typedVector()[i]; + } + + double getIndexQuicklyAsDouble(unsigned i) + { + return Adaptor::toDouble(getIndexQuicklyAsNativeValue(i)); + } + + JSValue getIndexQuickly(unsigned i) + { + return Adaptor::toJSValue(getIndexQuicklyAsNativeValue(i)); + } + + void setIndexQuicklyToNativeValue(unsigned i, typename Adaptor::Type value) + { + ASSERT(i < m_length); + typedVector()[i] = value; + } + + void setIndexQuicklyToDouble(unsigned i, double value) + { + setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(value)); + } + + void setIndexQuickly(unsigned i, JSValue value) + { + setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(value)); + } + + bool setIndex(ExecState* exec, unsigned i, JSValue jsValue) + { + typename Adaptor::Type value = toNativeFromValue<Adaptor>(exec, jsValue); + if (exec->hadException()) + return false; + + if (i >= m_length) + return false; + + setIndexQuicklyToNativeValue(i, value); + return true; + } + + bool canAccessRangeQuickly(unsigned offset, unsigned length) + { + return offset <= m_length + && offset + length <= m_length + // check overflow + && offset + length >= offset; + } + + // Like canSetQuickly, except: if it returns false, it will throw the + // appropriate exception. + bool validateRange(ExecState*, unsigned offset, unsigned length); + + // Returns true if successful, and false on error; if it returns false + // then it will have thrown an exception. + bool set(ExecState*, JSObject*, unsigned offset, unsigned length); + + PassRefPtr<typename Adaptor::ViewType> typedImpl() + { + return Adaptor::ViewType::create(buffer(), byteOffset(), length()); + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(typeForTypedArrayType(Adaptor::typeValue), StructureFlags), info(), NonArray); + } + + static const ClassInfo s_info; // This is never accessed directly, since that would break linkage on some compilers. + + static const ClassInfo* info() + { + switch (Adaptor::typeValue) { + case TypeInt8: + return getInt8ArrayClassInfo(); + case TypeInt16: + return getInt16ArrayClassInfo(); + case TypeInt32: + return getInt32ArrayClassInfo(); + case TypeUint8: + return getUint8ArrayClassInfo(); + case TypeUint8Clamped: + return getUint8ClampedArrayClassInfo(); + case TypeUint16: + return getUint16ArrayClassInfo(); + case TypeUint32: + return getUint32ArrayClassInfo(); + case TypeFloat32: + return getFloat32ArrayClassInfo(); + case TypeFloat64: + return getFloat64ArrayClassInfo(); + default: + RELEASE_ASSERT_NOT_REACHED(); + return 0; + } + } + + ArrayBuffer* existingBuffer(); + + static const TypedArrayType TypedArrayStorageType = Adaptor::typeValue; + +protected: + friend struct TypedArrayClassInfos; + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + + static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + + static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + static void visitChildren(JSCell*, SlotVisitor&); + static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); + + // Allocates the full-on native buffer and moves data into the C heap if + // necessary. Note that this never allocates in the GC heap. + static ArrayBuffer* slowDownAndWasteMemory(JSArrayBufferView*); + static PassRefPtr<ArrayBufferView> getTypedArrayImpl(JSArrayBufferView*); + +private: + // Returns true if successful, and false on error; it will throw on error. + template<typename OtherAdaptor> + bool setWithSpecificType( + ExecState*, JSGenericTypedArrayView<OtherAdaptor>*, + unsigned offset, unsigned length); +}; + +template<typename Adaptor> +inline RefPtr<typename Adaptor::ViewType> toNativeTypedView(JSValue value) +{ + typename Adaptor::JSViewType* wrapper = jsDynamicCast<typename Adaptor::JSViewType*>(value); + if (!wrapper) + return nullptr; + return wrapper->typedImpl(); +} + +} // namespace JSC + +#endif // JSGenericTypedArrayView_h + diff --git a/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructor.h b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructor.h new file mode 100644 index 000000000..3dffe3c23 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructor.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSGenericTypedArrayViewConstructor_h +#define JSGenericTypedArrayViewConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +template<typename ViewClass> +class JSGenericTypedArrayViewConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + +protected: + JSGenericTypedArrayViewConstructor(VM&, Structure*); + void finishCreation(VM&, JSObject* prototype, const String& name); + +public: + static JSGenericTypedArrayViewConstructor* create( + VM&, Structure*, JSObject* prototype, const String& name); + + DECLARE_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + +protected: + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} // namespace JSC + +#endif // JSGenericTypedArrayViewConstructor_h + diff --git a/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h new file mode 100644 index 000000000..2ddc29f71 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSGenericTypedArrayViewConstructorInlines_h +#define JSGenericTypedArrayViewConstructorInlines_h + +#include "Error.h" +#include "JSArrayBuffer.h" +#include "JSGenericTypedArrayViewConstructor.h" +#include "JSGlobalObject.h" + +namespace JSC { + +template<typename ViewClass> +JSGenericTypedArrayViewConstructor<ViewClass>::JSGenericTypedArrayViewConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +template<typename ViewClass> +void JSGenericTypedArrayViewConstructor<ViewClass>::finishCreation(VM& vm, JSObject* prototype, const String& name) +{ + Base::finishCreation(vm, name); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(3), DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->BYTES_PER_ELEMENT, jsNumber(ViewClass::elementSize), DontEnum | ReadOnly | DontDelete); +} + +template<typename ViewClass> +JSGenericTypedArrayViewConstructor<ViewClass>* +JSGenericTypedArrayViewConstructor<ViewClass>::create( + VM& vm, Structure* structure, JSObject* prototype, + const String& name) +{ + JSGenericTypedArrayViewConstructor* result = + new (NotNull, allocateCell<JSGenericTypedArrayViewConstructor>(vm.heap)) + JSGenericTypedArrayViewConstructor(vm, structure); + result->finishCreation(vm, prototype, name); + return result; +} + +template<typename ViewClass> +Structure* JSGenericTypedArrayViewConstructor<ViewClass>::createStructure( + VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create( + vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +template<typename ViewClass> +static EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState* exec) +{ + Structure* structure = + asInternalFunction(exec->callee())->globalObject()->typedArrayStructure( + ViewClass::TypedArrayStorageType); + + if (!exec->argumentCount()) { + if (ViewClass::TypedArrayStorageType == TypeDataView) + return throwVMError(exec, createTypeError(exec, "DataView constructor requires at least one argument.")); + + // Even though the documentation doesn't say so, it's correct to say + // "new Int8Array()". This is the same as allocating an array of zero + // length. + return JSValue::encode(ViewClass::create(exec, structure, 0)); + } + + if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(exec->argument(0))) { + RefPtr<ArrayBuffer> buffer = jsBuffer->impl(); + + unsigned offset = (exec->argumentCount() > 1) ? exec->uncheckedArgument(1).toUInt32(exec) : 0; + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + unsigned length = 0; + if (exec->argumentCount() > 2) { + length = exec->uncheckedArgument(2).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else { + if ((buffer->byteLength() - offset) % ViewClass::elementSize) + return throwVMError(exec, createRangeError(exec, "ArrayBuffer length minus the byteOffset is not a multiple of the element size")); + length = (buffer->byteLength() - offset) / ViewClass::elementSize; + } + return JSValue::encode(ViewClass::create(exec, structure, buffer, offset, length)); + } + + if (ViewClass::TypedArrayStorageType == TypeDataView) + return throwVMError(exec, createTypeError(exec, "Expected ArrayBuffer for the first argument.")); + + // For everything but DataView, we allow construction with any of: + // - Another array. This creates a copy of the of that array. + // - An integer. This creates a new typed array of that length and zero-initializes it. + + if (JSObject* object = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0))) { + unsigned length = + object->get(exec, exec->vm().propertyNames->length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + ViewClass* result = ViewClass::createUninitialized(exec, structure, length); + if (!result) { + ASSERT(exec->hadException()); + return JSValue::encode(jsUndefined()); + } + + if (!result->set(exec, object, 0, length)) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(result); + } + + int length; + if (exec->uncheckedArgument(0).isInt32()) + length = exec->uncheckedArgument(0).asInt32(); + else if (!exec->uncheckedArgument(0).isNumber()) + return throwVMError(exec, createTypeError(exec, "Invalid array length argument")); + else { + length = static_cast<int>(exec->uncheckedArgument(0).asNumber()); + if (length != exec->uncheckedArgument(0).asNumber()) + return throwVMError(exec, createTypeError(exec, "Invalid array length argument (fractional lengths not allowed)")); + } + + if (length < 0) + return throwVMError(exec, createRangeError(exec, "Requested length is negative")); + return JSValue::encode(ViewClass::create(exec, structure, length)); +} + +template<typename ViewClass> +ConstructType JSGenericTypedArrayViewConstructor<ViewClass>::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructGenericTypedArrayView<ViewClass>; + return ConstructTypeHost; +} + +template<typename ViewClass> +CallType JSGenericTypedArrayViewConstructor<ViewClass>::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = constructGenericTypedArrayView<ViewClass>; + return CallTypeNone; +} + +} // namespace JSC + +#endif // JSGenericTypedArrayViewConstructorInlines_h diff --git a/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewInlines.h b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewInlines.h new file mode 100644 index 000000000..e9ed1e998 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewInlines.h @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSGenericTypedArrayViewInlines_h +#define JSGenericTypedArrayViewInlines_h + +#include "ArrayBufferView.h" +#include "DeferGC.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSGenericTypedArrayView.h" +#include "Reject.h" +#include "TypedArrays.h" + +namespace JSC { + +template<typename Adaptor> +JSGenericTypedArrayView<Adaptor>::JSGenericTypedArrayView( + VM& vm, ConstructionContext& context) + : Base(vm, context) +{ +} + +template<typename Adaptor> +JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( + ExecState* exec, Structure* structure, unsigned length) +{ + ConstructionContext context(exec->vm(), structure, length, sizeof(typename Adaptor::Type)); + if (!context) { + exec->vm().throwException(exec, createOutOfMemoryError(exec)); + return 0; + } + JSGenericTypedArrayView* result = + new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap)) + JSGenericTypedArrayView(exec->vm(), context); + result->finishCreation(exec->vm()); + return result; +} + +template<typename Adaptor> +JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::createUninitialized( + ExecState* exec, Structure* structure, unsigned length) +{ + ConstructionContext context( + exec->vm(), structure, length, sizeof(typename Adaptor::Type), + ConstructionContext::DontInitialize); + if (!context) { + exec->vm().throwException(exec, createOutOfMemoryError(exec)); + return 0; + } + JSGenericTypedArrayView* result = + new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap)) + JSGenericTypedArrayView(exec->vm(), context); + result->finishCreation(exec->vm()); + return result; +} + +template<typename Adaptor> +JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( + ExecState* exec, Structure* structure, PassRefPtr<ArrayBuffer> passedBuffer, + unsigned byteOffset, unsigned length) +{ + RefPtr<ArrayBuffer> buffer = passedBuffer; + size_t size = sizeof(typename Adaptor::Type); + if (!ArrayBufferView::verifySubRangeLength(buffer, byteOffset, length, size)) { + exec->vm().throwException(exec, createRangeError(exec, "Length out of range of buffer")); + return nullptr; + } + if (!ArrayBufferView::verifyByteOffsetAlignment(byteOffset, size)) { + exec->vm().throwException(exec, createRangeError(exec, "Byte offset is not aligned")); + return nullptr; + } + ConstructionContext context(exec->vm(), structure, buffer, byteOffset, length); + ASSERT(context); + JSGenericTypedArrayView* result = + new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap)) + JSGenericTypedArrayView(exec->vm(), context); + result->finishCreation(exec->vm()); + return result; +} + +template<typename Adaptor> +JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( + VM& vm, Structure* structure, PassRefPtr<typename Adaptor::ViewType> impl) +{ + RefPtr<ArrayBuffer> buffer = impl->buffer(); + ConstructionContext context(vm, structure, buffer, impl->byteOffset(), impl->length()); + ASSERT(context); + JSGenericTypedArrayView* result = + new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap)) + JSGenericTypedArrayView(vm, context); + result->finishCreation(vm); + return result; +} + +template<typename Adaptor> +JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( + Structure* structure, JSGlobalObject* globalObject, + PassRefPtr<typename Adaptor::ViewType> impl) +{ + return create(globalObject->vm(), structure, impl); +} + +template<typename Adaptor> +bool JSGenericTypedArrayView<Adaptor>::validateRange( + ExecState* exec, unsigned offset, unsigned length) +{ + if (canAccessRangeQuickly(offset, length)) + return true; + + exec->vm().throwException(exec, createRangeError(exec, "Range consisting of offset and length are out of bounds")); + return false; +} + +template<typename Adaptor> +template<typename OtherAdaptor> +bool JSGenericTypedArrayView<Adaptor>::setWithSpecificType( + ExecState* exec, JSGenericTypedArrayView<OtherAdaptor>* other, + unsigned offset, unsigned length) +{ + // Handle the hilarious case: the act of getting the length could have resulted + // in neutering. Well, no. That'll never happen because there cannot be + // side-effects on getting the length of a typed array. But predicting where there + // are, or aren't, side-effects is a fool's game so we resort to this cheap + // check. Worst case, if we're wrong, people start seeing less things get copied + // but we won't have a security vulnerability. + length = std::min(length, other->length()); + + if (!validateRange(exec, offset, length)) + return false; + + if (other->length() != length) { + exec->vm().throwException(exec, createRangeError(exec, "Length of incoming array changed unexpectedly.")); + return false; + } + + // This method doesn't support copying between the same array. Note that + // set() will only call this if the types differ, which implicitly guarantees + // that we can't be the same array. This is relevant because the way we detect + // non-overlapping is by checking if either (a) either array doesn't have a + // backing buffer or (b) the backing buffers are different, but that doesn't + // catch the case where it's the *same* array - fortunately though, this code + // path never needs to worry about that case. + ASSERT(static_cast<JSCell*>(this) != static_cast<JSCell*>(other)); + + // 1) If the two arrays are non-overlapping, we can copy in any order we like + // and we don't need an intermediate buffer. Arrays are definitely + // non-overlapping if either one of them has no backing buffer (that means + // that it *owns* its philosophical backing buffer) or if they have + // different backing buffers. + // 2) If the two arrays overlap but have the same element size, we can do a + // memmove-like copy where we flip-flop direction based on which vector + // starts before the other: + // A) If the destination vector is before the source vector, then a forward + // copy is in order. + // B) If the destination vector is after the source vector, then a backward + // copy is in order. + // 3) If we have different element sizes and there is a chance of overlap then + // we need an intermediate vector. + + // NB. Comparisons involving elementSize will be constant-folded by template + // specialization. + + unsigned otherElementSize = sizeof(typename OtherAdaptor::Type); + + // Handle cases (1) and (2B). + if (!hasArrayBuffer() || !other->hasArrayBuffer() + || existingBuffer() != other->existingBuffer() + || (elementSize == otherElementSize && vector() > other->vector())) { + for (unsigned i = length; i--;) { + setIndexQuicklyToNativeValue( + offset + i, OtherAdaptor::template convertTo<Adaptor>( + other->getIndexQuicklyAsNativeValue(i))); + } + return true; + } + + // Now we either have (2A) or (3) - so first we try to cover (2A). + if (elementSize == otherElementSize) { + for (unsigned i = 0; i < length; ++i) { + setIndexQuicklyToNativeValue( + offset + i, OtherAdaptor::template convertTo<Adaptor>( + other->getIndexQuicklyAsNativeValue(i))); + } + return true; + } + + // Fail: we need an intermediate transfer buffer (i.e. case (3)). + Vector<typename Adaptor::Type, 32> transferBuffer(length); + for (unsigned i = length; i--;) { + transferBuffer[i] = OtherAdaptor::template convertTo<Adaptor>( + other->getIndexQuicklyAsNativeValue(i)); + } + for (unsigned i = length; i--;) + setIndexQuicklyToNativeValue(offset + i, transferBuffer[i]); + + return true; +} + +template<typename Adaptor> +bool JSGenericTypedArrayView<Adaptor>::set( + ExecState* exec, JSObject* object, unsigned offset, unsigned length) +{ + const ClassInfo* ci = object->classInfo(); + if (ci->typedArrayStorageType == Adaptor::typeValue) { + // The super fast case: we can just memcpy since we're the same type. + JSGenericTypedArrayView* other = jsCast<JSGenericTypedArrayView*>(object); + length = std::min(length, other->length()); + + if (!validateRange(exec, offset, length)) + return false; + + memmove(typedVector() + offset, other->typedVector(), other->byteLength()); + return true; + } + + switch (ci->typedArrayStorageType) { + case TypeInt8: + return setWithSpecificType<Int8Adaptor>( + exec, jsCast<JSInt8Array*>(object), offset, length); + case TypeInt16: + return setWithSpecificType<Int16Adaptor>( + exec, jsCast<JSInt16Array*>(object), offset, length); + case TypeInt32: + return setWithSpecificType<Int32Adaptor>( + exec, jsCast<JSInt32Array*>(object), offset, length); + case TypeUint8: + return setWithSpecificType<Uint8Adaptor>( + exec, jsCast<JSUint8Array*>(object), offset, length); + case TypeUint8Clamped: + return setWithSpecificType<Uint8ClampedAdaptor>( + exec, jsCast<JSUint8ClampedArray*>(object), offset, length); + case TypeUint16: + return setWithSpecificType<Uint16Adaptor>( + exec, jsCast<JSUint16Array*>(object), offset, length); + case TypeUint32: + return setWithSpecificType<Uint32Adaptor>( + exec, jsCast<JSUint32Array*>(object), offset, length); + case TypeFloat32: + return setWithSpecificType<Float32Adaptor>( + exec, jsCast<JSFloat32Array*>(object), offset, length); + case TypeFloat64: + return setWithSpecificType<Float64Adaptor>( + exec, jsCast<JSFloat64Array*>(object), offset, length); + case NotTypedArray: + case TypeDataView: { + if (!validateRange(exec, offset, length)) + return false; + // We could optimize this case. But right now, we don't. + for (unsigned i = 0; i < length; ++i) { + JSValue value = object->get(exec, i); + if (!setIndex(exec, offset + i, value)) + return false; + } + return true; + } } + + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +template<typename Adaptor> +ArrayBuffer* JSGenericTypedArrayView<Adaptor>::existingBuffer() +{ + return existingBufferInButterfly(); +} + +template<typename Adaptor> +bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot( + JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); + if (propertyName == exec->propertyNames().length) { + slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->length())); + return true; + } + + if (propertyName == exec->propertyNames().byteLength) { + slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->byteLength())); + return true; + } + + Optional<uint32_t> index = parseIndex(propertyName); + if (index && thisObject->canGetIndexQuickly(index.value())) { + slot.setValue(thisObject, DontDelete | ReadOnly, thisObject->getIndexQuickly(index.value())); + return true; + } + + return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +template<typename Adaptor> +void JSGenericTypedArrayView<Adaptor>::put( + JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, + PutPropertySlot& slot) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); + + if (propertyName == exec->propertyNames().length) { + // Firefox appears to simply ignore attempts to store to the length property. + // Even in strict mode. I will do the same. + return; + } + + if (Optional<uint32_t> index = parseIndex(propertyName)) { + putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode()); + return; + } + + Base::put(thisObject, exec, propertyName, value, slot); +} + +template<typename Adaptor> +bool JSGenericTypedArrayView<Adaptor>::defineOwnProperty( + JSObject* object, ExecState* exec, PropertyName propertyName, + const PropertyDescriptor& descriptor, bool shouldThrow) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); + + // This is matching Firefox behavior. In particular, it rejects all attempts to + // defineOwnProperty for indexed properties on typed arrays, even if they're out + // of bounds. + if (propertyName == exec->propertyNames().length || parseIndex(propertyName)) + return reject(exec, shouldThrow, "Attempting to write to a read-only typed array property."); + + return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); +} + +template<typename Adaptor> +bool JSGenericTypedArrayView<Adaptor>::deleteProperty( + JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); + + if (propertyName == exec->propertyNames().length || parseIndex(propertyName)) + return false; + + return Base::deleteProperty(thisObject, exec, propertyName); +} + +template<typename Adaptor> +bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlotByIndex( + JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); + + if (propertyName > MAX_ARRAY_INDEX) { + return thisObject->methodTable()->getOwnPropertySlot( + thisObject, exec, Identifier::from(exec, propertyName), slot); + } + + if (!thisObject->canGetIndexQuickly(propertyName)) + return false; + + slot.setValue(thisObject, None, thisObject->getIndexQuickly(propertyName)); + return true; +} + +template<typename Adaptor> +void JSGenericTypedArrayView<Adaptor>::putByIndex( + JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); + + if (propertyName > MAX_ARRAY_INDEX) { + PutPropertySlot slot(JSValue(thisObject), shouldThrow); + thisObject->methodTable()->put( + thisObject, exec, Identifier::from(exec, propertyName), value, slot); + return; + } + + thisObject->setIndex(exec, propertyName, value); +} + +template<typename Adaptor> +bool JSGenericTypedArrayView<Adaptor>::deletePropertyByIndex( + JSCell* cell, ExecState* exec, unsigned propertyName) +{ + if (propertyName > MAX_ARRAY_INDEX) { + return cell->methodTable()->deleteProperty( + cell, exec, Identifier::from(exec, propertyName)); + } + + return false; +} + +template<typename Adaptor> +void JSGenericTypedArrayView<Adaptor>::getOwnNonIndexPropertyNames( + JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); + + if (mode.includeDontEnumProperties()) + array.add(exec->propertyNames().length); + + Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode); +} + +template<typename Adaptor> +void JSGenericTypedArrayView<Adaptor>::getOwnPropertyNames( + JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); + + if (array.includeStringProperties()) { + for (unsigned i = 0; i < thisObject->m_length; ++i) + array.add(Identifier::from(exec, i)); + } + + return Base::getOwnPropertyNames(object, exec, array, mode); +} + +template<typename Adaptor> +void JSGenericTypedArrayView<Adaptor>::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); + + switch (thisObject->m_mode) { + case FastTypedArray: { + if (thisObject->m_vector) + visitor.copyLater(thisObject, TypedArrayVectorCopyToken, thisObject->m_vector, thisObject->byteSize()); + break; + } + + case OversizeTypedArray: { + visitor.reportExtraMemoryVisited(thisObject, thisObject->byteSize()); + break; + } + + case WastefulTypedArray: + break; + + case DataViewMode: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + + Base::visitChildren(thisObject, visitor); +} + +template<typename Adaptor> +void JSGenericTypedArrayView<Adaptor>::copyBackingStore( + JSCell* cell, CopyVisitor& visitor, CopyToken token) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); + + if (token == TypedArrayVectorCopyToken + && visitor.checkIfShouldCopy(thisObject->m_vector)) { + ASSERT(thisObject->m_vector); + void* oldVector = thisObject->m_vector; + void* newVector = visitor.allocateNewSpace(thisObject->byteSize()); + memcpy(newVector, oldVector, thisObject->byteSize()); + thisObject->m_vector = newVector; + visitor.didCopy(oldVector, thisObject->byteSize()); + } + + Base::copyBackingStore(thisObject, visitor, token); +} + +template<typename Adaptor> +ArrayBuffer* JSGenericTypedArrayView<Adaptor>::slowDownAndWasteMemory(JSArrayBufferView* object) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); + + // We play this game because we want this to be callable even from places that + // don't have access to ExecState* or the VM, and we only allocate so little + // memory here that it's not necessary to trigger a GC - just accounting what + // we have done is good enough. The sort of bizarro exception to the "allocating + // little memory" is when we transfer a backing buffer into the C heap; this + // will temporarily get counted towards heap footprint (incorrectly, in the case + // of adopting an oversize typed array) but we don't GC here anyway. That's + // almost certainly fine. The worst case is if you created a ton of fast typed + // arrays, and did nothing but caused all of them to slow down and waste memory. + // In that case, your memory footprint will double before the GC realizes what's + // up. But if you do *anything* to trigger a GC watermark check, it will know + // that you *had* done those allocations and it will GC appropriately. + Heap* heap = Heap::heap(thisObject); + DeferGCForAWhile deferGC(*heap); + + ASSERT(!thisObject->hasIndexingHeader()); + + size_t size = thisObject->byteSize(); + + if (thisObject->m_mode == FastTypedArray + && !thisObject->butterfly() && size >= sizeof(IndexingHeader)) { + ASSERT(thisObject->m_vector); + // Reuse already allocated memory if at all possible. + thisObject->m_butterfly.setWithoutWriteBarrier( + static_cast<IndexingHeader*>(thisObject->m_vector)->butterfly()); + } else { + VM& vm = *heap->vm(); + thisObject->m_butterfly.set(vm, thisObject, Butterfly::createOrGrowArrayRight( + thisObject->butterfly(), vm, thisObject, thisObject->structure(), + thisObject->structure()->outOfLineCapacity(), false, 0, 0)); + } + + RefPtr<ArrayBuffer> buffer; + + switch (thisObject->m_mode) { + case FastTypedArray: + buffer = ArrayBuffer::create(thisObject->m_vector, thisObject->byteLength()); + break; + + case OversizeTypedArray: + // FIXME: consider doing something like "subtracting" from extra memory + // cost, since right now this case will cause the GC to think that we reallocated + // the whole buffer. + buffer = ArrayBuffer::createAdopted(thisObject->m_vector, thisObject->byteLength()); + break; + + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + + thisObject->butterfly()->indexingHeader()->setArrayBuffer(buffer.get()); + thisObject->m_vector = buffer->data(); + thisObject->m_mode = WastefulTypedArray; + heap->addReference(thisObject, buffer.get()); + + return buffer.get(); +} + +template<typename Adaptor> +PassRefPtr<ArrayBufferView> +JSGenericTypedArrayView<Adaptor>::getTypedArrayImpl(JSArrayBufferView* object) +{ + JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); + return thisObject->typedImpl(); +} + +} // namespace JSC + +#endif // JSGenericTypedArrayViewInlines_h + diff --git a/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototype.h b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototype.h new file mode 100644 index 000000000..7ef77490a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSGenericTypedArrayViewPrototype_h +#define JSGenericTypedArrayViewPrototype_h + +#include "JSObject.h" + +namespace JSC { + +template<typename ViewClass> +class JSGenericTypedArrayViewPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + +protected: + JSGenericTypedArrayViewPrototype(VM&, Structure*); + void finishCreation(VM&, JSGlobalObject*); + +public: + static JSGenericTypedArrayViewPrototype* create( + VM&, JSGlobalObject*, Structure*); + + DECLARE_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); +}; + +} // namespace JSC + +#endif // JSGenericTypedArrayViewPrototype_h + diff --git a/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototypeInlines.h b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototypeInlines.h new file mode 100644 index 000000000..459997a46 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototypeInlines.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSGenericTypedArrayViewPrototypeInlines_h +#define JSGenericTypedArrayViewPrototypeInlines_h + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSFunction.h" +#include "JSGenericTypedArrayViewPrototype.h" +#include <wtf/StdLibExtras.h> + +namespace JSC { + +template<typename ViewClass> +EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncSet(ExecState* exec) +{ + ViewClass* thisObject = jsDynamicCast<ViewClass*>(exec->thisValue()); + if (!thisObject) + return throwVMError(exec, createTypeError(exec, "Receiver should be a typed array view")); + + if (!exec->argumentCount()) + return throwVMError(exec, createTypeError(exec, "Expected at least one argument")); + + JSObject* sourceArray = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0)); + if (!sourceArray) + return throwVMError(exec, createTypeError(exec, "First argument should be an object")); + + unsigned offset; + if (exec->argumentCount() >= 2) { + offset = exec->uncheckedArgument(1).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else + offset = 0; + + unsigned length = sourceArray->get(exec, exec->vm().propertyNames->length).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + thisObject->set(exec, sourceArray, offset, length); + return JSValue::encode(jsUndefined()); +} + +template<typename ViewClass> +EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncSubarray(ExecState* exec) +{ + JSFunction* callee = jsCast<JSFunction*>(exec->callee()); + + ViewClass* thisObject = jsDynamicCast<ViewClass*>(exec->thisValue()); + if (!thisObject) + return throwVMError(exec, createTypeError(exec, "Receiver should be a typed array view")); + + if (!exec->argumentCount()) + return throwVMError(exec, createTypeError(exec, "Expected at least one argument")); + + int32_t begin = exec->uncheckedArgument(0).toInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + int32_t end; + if (exec->argumentCount() >= 2) { + end = exec->uncheckedArgument(1).toInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } else + end = thisObject->length(); + + // Get the length here; later assert that the length didn't change. + unsigned thisLength = thisObject->length(); + + // Handle negative indices: -x => length - x + if (begin < 0) + begin = std::max(static_cast<int>(thisLength + begin), 0); + if (end < 0) + end = std::max(static_cast<int>(thisLength + end), 0); + + // Clamp the indices to the bounds of the array. + ASSERT(begin >= 0); + ASSERT(end >= 0); + begin = std::min(begin, static_cast<int32_t>(thisLength)); + end = std::min(end, static_cast<int32_t>(thisLength)); + + // Clamp end to begin. + end = std::max(begin, end); + + ASSERT(end >= begin); + unsigned offset = begin; + unsigned length = end - begin; + + RefPtr<ArrayBuffer> arrayBuffer = thisObject->buffer(); + RELEASE_ASSERT(thisLength == thisObject->length()); + + Structure* structure = + callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType); + + ViewClass* result = ViewClass::create( + exec, structure, arrayBuffer, + thisObject->byteOffset() + offset * ViewClass::elementSize, + length); + + return JSValue::encode(result); +} + +template<typename ViewClass> +JSGenericTypedArrayViewPrototype<ViewClass>::JSGenericTypedArrayViewPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +template<typename ViewClass> +void JSGenericTypedArrayViewPrototype<ViewClass>::finishCreation( + VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + + ASSERT(inherits(info())); + + JSC_NATIVE_FUNCTION(vm.propertyNames->set, genericTypedArrayViewProtoFuncSet<ViewClass>, DontEnum, 2); + JSC_NATIVE_FUNCTION(vm.propertyNames->subarray, genericTypedArrayViewProtoFuncSubarray<ViewClass>, DontEnum, 2); + putDirect(vm, vm.propertyNames->BYTES_PER_ELEMENT, jsNumber(ViewClass::elementSize), DontEnum | ReadOnly | DontDelete); +} + +template<typename ViewClass> +JSGenericTypedArrayViewPrototype<ViewClass>* +JSGenericTypedArrayViewPrototype<ViewClass>::create( + VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + JSGenericTypedArrayViewPrototype* prototype = + new (NotNull, allocateCell<JSGenericTypedArrayViewPrototype>(vm.heap)) + JSGenericTypedArrayViewPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; +} + +template<typename ViewClass> +Structure* JSGenericTypedArrayViewPrototype<ViewClass>::createStructure( + VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create( + vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +} // namespace JSC + +#endif // JSGenericTypedArrayViewPrototypeInlines_h diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp new file mode 100644 index 000000000..cee9bbd94 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -0,0 +1,972 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2014, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2008 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * + * 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "JSGlobalObject.h" + +#include "ArrayConstructor.h" +#include "ArrayIteratorPrototype.h" +#include "ArrayPrototype.h" +#include "BooleanConstructor.h" +#include "BooleanPrototype.h" +#include "BuiltinNames.h" +#include "ClonedArguments.h" +#include "CodeBlock.h" +#include "CodeCache.h" +#include "ConsolePrototype.h" +#include "DateConstructor.h" +#include "DatePrototype.h" +#include "Debugger.h" +#include "DebuggerScope.h" +#include "DirectArguments.h" +#include "Error.h" +#include "ErrorConstructor.h" +#include "ErrorPrototype.h" +#include "FunctionConstructor.h" +#include "FunctionPrototype.h" +#include "GetterSetter.h" +#include "HeapIterationScope.h" +#include "Interpreter.h" +#include "IteratorPrototype.h" +#include "JSAPIWrapperObject.h" +#include "JSArrayBuffer.h" +#include "JSArrayBufferConstructor.h" +#include "JSArrayBufferPrototype.h" +#include "JSArrayIterator.h" +#include "JSBoundFunction.h" +#include "JSCInlines.h" +#include "JSCallbackConstructor.h" +#include "JSCallbackFunction.h" +#include "JSCallbackObject.h" +#include "JSConsole.h" +#include "JSDataView.h" +#include "JSDataViewPrototype.h" +#include "JSDollarVM.h" +#include "JSDollarVMPrototype.h" +#include "JSFunction.h" +#include "JSGenericTypedArrayViewConstructorInlines.h" +#include "JSGenericTypedArrayViewInlines.h" +#include "JSGenericTypedArrayViewPrototypeInlines.h" +#include "JSGlobalObjectFunctions.h" +#include "JSJob.h" +#include "JSLexicalEnvironment.h" +#include "JSLock.h" +#include "JSMap.h" +#include "JSMapIterator.h" +#include "JSONObject.h" +#include "JSPromise.h" +#include "JSPromiseConstructor.h" +#include "JSPromisePrototype.h" +#include "JSPropertyNameIterator.h" +#include "JSSet.h" +#include "JSSetIterator.h" +#include "JSStringIterator.h" +#include "JSTemplateRegistryKey.h" +#include "JSTypedArrayConstructors.h" +#include "JSTypedArrayPrototypes.h" +#include "JSTypedArrays.h" +#include "JSWASMModule.h" +#include "JSWeakMap.h" +#include "JSWeakSet.h" +#include "JSWithScope.h" +#include "LegacyProfiler.h" +#include "Lookup.h" +#include "MapConstructor.h" +#include "MapIteratorPrototype.h" +#include "MapPrototype.h" +#include "MathObject.h" +#include "Microtask.h" +#include "NativeErrorConstructor.h" +#include "NativeErrorPrototype.h" +#include "NullGetterFunction.h" +#include "NullSetterFunction.h" +#include "NumberConstructor.h" +#include "NumberPrototype.h" +#include "ObjCCallbackFunction.h" +#include "ObjectConstructor.h" +#include "ObjectPrototype.h" +#include "ParserError.h" +#include "ReflectObject.h" +#include "RegExpConstructor.h" +#include "RegExpMatchesArray.h" +#include "RegExpObject.h" +#include "RegExpPrototype.h" +#include "ScopedArguments.h" +#include "SetConstructor.h" +#include "SetIteratorPrototype.h" +#include "SetPrototype.h" +#include "StrictEvalActivation.h" +#include "StringConstructor.h" +#include "StringIteratorPrototype.h" +#include "StringPrototype.h" +#include "Symbol.h" +#include "SymbolConstructor.h" +#include "SymbolPrototype.h" +#include "VariableWriteFireDetail.h" +#include "WeakGCMapInlines.h" +#include "WeakMapConstructor.h" +#include "WeakMapPrototype.h" +#include "WeakSetConstructor.h" +#include "WeakSetPrototype.h" + +#if ENABLE(INTL) +#include "IntlObject.h" +#endif // ENABLE(INTL) + +#if ENABLE(REMOTE_INSPECTOR) +#include "JSGlobalObjectDebuggable.h" +#include "JSGlobalObjectInspectorController.h" +#endif + +#if ENABLE(WEB_REPLAY) +#include "EmptyInputCursor.h" +#include "JSReplayInputs.h" +#endif + +#include "JSGlobalObject.lut.h" + +namespace JSC { + +const ClassInfo JSGlobalObject::s_info = { "GlobalObject", &Base::s_info, &globalObjectTable, CREATE_METHOD_TABLE(JSGlobalObject) }; + +const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, nullptr, &shouldInterruptScriptBeforeTimeout }; + +/* Source for JSGlobalObject.lut.h +@begin globalObjectTable + parseFloat globalFuncParseFloat DontEnum|Function 1 + isNaN globalFuncIsNaN DontEnum|Function 1 + isFinite globalFuncIsFinite DontEnum|Function 1 + escape globalFuncEscape DontEnum|Function 1 + unescape globalFuncUnescape DontEnum|Function 1 + decodeURI globalFuncDecodeURI DontEnum|Function 1 + decodeURIComponent globalFuncDecodeURIComponent DontEnum|Function 1 + encodeURI globalFuncEncodeURI DontEnum|Function 1 + encodeURIComponent globalFuncEncodeURIComponent DontEnum|Function 1 +@end +*/ + +static EncodedJSValue JSC_HOST_CALL getTemplateObject(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + ASSERT(thisValue.inherits(JSTemplateRegistryKey::info())); + return JSValue::encode(exec->lexicalGlobalObject()->templateRegistry().getTemplateObject(exec, jsCast<JSTemplateRegistryKey*>(thisValue)->templateRegistryKey())); +} + + +static EncodedJSValue JSC_HOST_CALL enqueueJob(ExecState* exec) +{ + VM& vm = exec->vm(); + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + + JSValue job = exec->argument(0); + JSValue arguments = exec->argument(1); + ASSERT(arguments.inherits(JSArray::info())); + + globalObject->queueMicrotask(createJSJob(vm, job, jsCast<JSArray*>(arguments))); + + return JSValue::encode(jsUndefined()); +} + +JSGlobalObject::JSGlobalObject(VM& vm, Structure* structure, const GlobalObjectMethodTable* globalObjectMethodTable) + : Base(vm, structure, 0) + , m_vm(vm) +#if ENABLE(WEB_REPLAY) + , m_inputCursor(EmptyInputCursor::create()) +#endif + , m_masqueradesAsUndefinedWatchpoint(adoptRef(new WatchpointSet(IsWatched))) + , m_havingABadTimeWatchpoint(adoptRef(new WatchpointSet(IsWatched))) + , m_varInjectionWatchpoint(adoptRef(new WatchpointSet(IsWatched))) + , m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0))) + , m_templateRegistry(vm) + , m_evalEnabled(true) + , m_runtimeFlags() + , m_consoleClient(nullptr) + , m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable) +{ +} + +JSGlobalObject::~JSGlobalObject() +{ +#if ENABLE(REMOTE_INSPECTOR) + m_inspectorController->globalObjectDestroyed(); +#endif + + if (m_debugger) + m_debugger->detach(this, Debugger::GlobalObjectIsDestructing); + + if (LegacyProfiler* profiler = vm().enabledProfiler()) + profiler->stopProfiling(this); +} + +void JSGlobalObject::destroy(JSCell* cell) +{ + static_cast<JSGlobalObject*>(cell)->JSGlobalObject::~JSGlobalObject(); +} + +void JSGlobalObject::setGlobalThis(VM& vm, JSObject* globalThis) +{ + m_globalThis.set(vm, this, globalThis); +} + +void JSGlobalObject::init(VM& vm) +{ + ASSERT(vm.currentThreadIsHoldingAPILock()); + + JSGlobalObject::globalExec()->init(0, 0, CallFrame::noCaller(), 0, 0); + + m_debugger = 0; + +#if ENABLE(REMOTE_INSPECTOR) + m_inspectorController = std::make_unique<Inspector::JSGlobalObjectInspectorController>(*this); + m_inspectorDebuggable = std::make_unique<JSGlobalObjectDebuggable>(*this); + m_inspectorDebuggable->init(); + m_consoleClient = m_inspectorController->consoleClient(); +#endif + + ExecState* exec = JSGlobalObject::globalExec(); + + m_functionPrototype.set(vm, this, FunctionPrototype::create(vm, FunctionPrototype::createStructure(vm, this, jsNull()))); // The real prototype will be set once ObjectPrototype is created. + m_calleeStructure.set(vm, this, JSCallee::createStructure(vm, this, jsNull())); + + // Need to create the callee structure (above) before creating the callee. + m_globalCallee.set(vm, this, JSCallee::create(vm, this, this)); + exec->setCallee(m_globalCallee.get()); + + m_functionStructure.set(vm, this, JSFunction::createStructure(vm, this, m_functionPrototype.get())); + m_boundFunctionStructure.set(vm, this, JSBoundFunction::createStructure(vm, this, m_functionPrototype.get())); + m_namedFunctionStructure.set(vm, this, Structure::addPropertyTransition(vm, m_functionStructure.get(), vm.propertyNames->name, DontDelete | ReadOnly | DontEnum, m_functionNameOffset)); + m_internalFunctionStructure.set(vm, this, InternalFunction::createStructure(vm, this, m_functionPrototype.get())); + JSFunction* callFunction = 0; + JSFunction* applyFunction = 0; + m_functionPrototype->addFunctionProperties(exec, this, &callFunction, &applyFunction); + m_callFunction.set(vm, this, callFunction); + m_applyFunction.set(vm, this, applyFunction); + m_arrayProtoValuesFunction.set(vm, this, JSFunction::create(vm, this, 0, vm.propertyNames->values.string(), arrayProtoFuncValues)); + m_initializePromiseFunction.set(vm, this, JSFunction::createBuiltinFunction(vm, operationsPromiseInitializePromiseCodeGenerator(vm), this)); + m_newPromiseDeferredFunction.set(vm, this, JSFunction::createBuiltinFunction(vm, operationsPromiseNewPromiseDeferredCodeGenerator(vm), this)); + m_nullGetterFunction.set(vm, this, NullGetterFunction::create(vm, NullGetterFunction::createStructure(vm, this, m_functionPrototype.get()))); + m_nullSetterFunction.set(vm, this, NullSetterFunction::create(vm, NullSetterFunction::createStructure(vm, this, m_functionPrototype.get()))); + m_objectPrototype.set(vm, this, ObjectPrototype::create(vm, this, ObjectPrototype::createStructure(vm, this, jsNull()))); + GetterSetter* protoAccessor = GetterSetter::create(vm, this); + protoAccessor->setGetter(vm, this, JSFunction::create(vm, this, 0, String(), globalFuncProtoGetter)); + protoAccessor->setSetter(vm, this, JSFunction::create(vm, this, 0, String(), globalFuncProtoSetter)); + m_objectPrototype->putDirectNonIndexAccessor(vm, vm.propertyNames->underscoreProto, protoAccessor, Accessor | DontEnum); + m_functionPrototype->structure()->setPrototypeWithoutTransition(vm, m_objectPrototype.get()); + + m_typedArrays[toIndex(TypeInt8)].prototype.set(vm, this, JSInt8ArrayPrototype::create(vm, this, JSInt8ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeInt16)].prototype.set(vm, this, JSInt16ArrayPrototype::create(vm, this, JSInt16ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeInt32)].prototype.set(vm, this, JSInt32ArrayPrototype::create(vm, this, JSInt32ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeUint8)].prototype.set(vm, this, JSUint8ArrayPrototype::create(vm, this, JSUint8ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeUint8Clamped)].prototype.set(vm, this, JSUint8ClampedArrayPrototype::create(vm, this, JSUint8ClampedArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeUint16)].prototype.set(vm, this, JSUint16ArrayPrototype::create(vm, this, JSUint16ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeUint32)].prototype.set(vm, this, JSUint32ArrayPrototype::create(vm, this, JSUint32ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeFloat32)].prototype.set(vm, this, JSFloat32ArrayPrototype::create(vm, this, JSFloat32ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeFloat64)].prototype.set(vm, this, JSFloat64ArrayPrototype::create(vm, this, JSFloat64ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_typedArrays[toIndex(TypeDataView)].prototype.set(vm, this, JSDataViewPrototype::create(vm, JSDataViewPrototype::createStructure(vm, this, m_objectPrototype.get()))); + + m_typedArrays[toIndex(TypeInt8)].structure.set(vm, this, JSInt8Array::createStructure(vm, this, m_typedArrays[toIndex(TypeInt8)].prototype.get())); + m_typedArrays[toIndex(TypeInt16)].structure.set(vm, this, JSInt16Array::createStructure(vm, this, m_typedArrays[toIndex(TypeInt16)].prototype.get())); + m_typedArrays[toIndex(TypeInt32)].structure.set(vm, this, JSInt32Array::createStructure(vm, this, m_typedArrays[toIndex(TypeInt32)].prototype.get())); + m_typedArrays[toIndex(TypeUint8)].structure.set(vm, this, JSUint8Array::createStructure(vm, this, m_typedArrays[toIndex(TypeUint8)].prototype.get())); + m_typedArrays[toIndex(TypeUint8Clamped)].structure.set(vm, this, JSUint8ClampedArray::createStructure(vm, this, m_typedArrays[toIndex(TypeUint8Clamped)].prototype.get())); + m_typedArrays[toIndex(TypeUint16)].structure.set(vm, this, JSUint16Array::createStructure(vm, this, m_typedArrays[toIndex(TypeUint16)].prototype.get())); + m_typedArrays[toIndex(TypeUint32)].structure.set(vm, this, JSUint32Array::createStructure(vm, this, m_typedArrays[toIndex(TypeUint32)].prototype.get())); + m_typedArrays[toIndex(TypeFloat32)].structure.set(vm, this, JSFloat32Array::createStructure(vm, this, m_typedArrays[toIndex(TypeFloat32)].prototype.get())); + m_typedArrays[toIndex(TypeFloat64)].structure.set(vm, this, JSFloat64Array::createStructure(vm, this, m_typedArrays[toIndex(TypeFloat64)].prototype.get())); + m_typedArrays[toIndex(TypeDataView)].structure.set(vm, this, JSDataView::createStructure(vm, this, m_typedArrays[toIndex(TypeDataView)].prototype.get())); + + m_lexicalEnvironmentStructure.set(vm, this, JSLexicalEnvironment::createStructure(vm, this)); + m_strictEvalActivationStructure.set(vm, this, StrictEvalActivation::createStructure(vm, this, jsNull())); + m_debuggerScopeStructure.set(m_vm, this, DebuggerScope::createStructure(m_vm, this)); + m_withScopeStructure.set(vm, this, JSWithScope::createStructure(vm, this, jsNull())); + + m_nullPrototypeObjectStructure.set(vm, this, JSFinalObject::createStructure(vm, this, jsNull(), JSFinalObject::defaultInlineCapacity())); + + m_callbackFunctionStructure.set(vm, this, JSCallbackFunction::createStructure(vm, this, m_functionPrototype.get())); + m_directArgumentsStructure.set(vm, this, DirectArguments::createStructure(vm, this, m_objectPrototype.get())); + m_scopedArgumentsStructure.set(vm, this, ScopedArguments::createStructure(vm, this, m_objectPrototype.get())); + m_outOfBandArgumentsStructure.set(vm, this, ClonedArguments::createStructure(vm, this, m_objectPrototype.get())); + m_callbackConstructorStructure.set(vm, this, JSCallbackConstructor::createStructure(vm, this, m_objectPrototype.get())); + m_callbackObjectStructure.set(vm, this, JSCallbackObject<JSDestructibleObject>::createStructure(vm, this, m_objectPrototype.get())); +#if JSC_OBJC_API_ENABLED + m_objcCallbackFunctionStructure.set(vm, this, ObjCCallbackFunction::createStructure(vm, this, m_functionPrototype.get())); + m_objcWrapperObjectStructure.set(vm, this, JSCallbackObject<JSAPIWrapperObject>::createStructure(vm, this, m_objectPrototype.get())); +#endif + + m_arrayPrototype.set(vm, this, ArrayPrototype::create(vm, this, ArrayPrototype::createStructure(vm, this, m_objectPrototype.get()))); + + m_originalArrayStructureForIndexingShape[UndecidedShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithUndecided)); + m_originalArrayStructureForIndexingShape[Int32Shape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithInt32)); + m_originalArrayStructureForIndexingShape[DoubleShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithDouble)); + m_originalArrayStructureForIndexingShape[ContiguousShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithContiguous)); + m_originalArrayStructureForIndexingShape[ArrayStorageShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithArrayStorage)); + m_originalArrayStructureForIndexingShape[SlowPutArrayStorageShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithSlowPutArrayStorage)); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + m_arrayStructureForIndexingShapeDuringAllocation[i] = m_originalArrayStructureForIndexingShape[i]; + + RegExp* emptyRegex = RegExp::create(vm, "", NoFlags); + + m_regExpPrototype.set(vm, this, RegExpPrototype::create(vm, RegExpPrototype::createStructure(vm, this, m_objectPrototype.get()), emptyRegex)); + m_regExpStructure.set(vm, this, RegExpObject::createStructure(vm, this, m_regExpPrototype.get())); + m_regExpMatchesArrayStructure.set(vm, this, createRegExpMatchesArrayStructure(vm, *this)); + + m_promisePrototype.set(vm, this, JSPromisePrototype::create(exec, this, JSPromisePrototype::createStructure(vm, this, m_objectPrototype.get()))); + m_promiseStructure.set(vm, this, JSPromise::createStructure(vm, this, m_promisePrototype.get())); + +#if ENABLE(WEBASSEMBLY) + m_wasmModuleStructure.set(vm, this, JSWASMModule::createStructure(vm, this)); +#endif + + m_parseIntFunction.set(vm, this, JSFunction::create(vm, this, 2, vm.propertyNames->parseInt.string(), globalFuncParseInt, NoIntrinsic)); + putDirectWithoutTransition(vm, vm.propertyNames->parseInt, m_parseIntFunction.get(), DontEnum | Function); + +#define CREATE_PROTOTYPE_FOR_SIMPLE_TYPE(capitalName, lowerName, properName, instanceType, jsName) \ +m_ ## lowerName ## Prototype.set(vm, this, capitalName##Prototype::create(vm, this, capitalName##Prototype::createStructure(vm, this, m_objectPrototype.get()))); \ +m_ ## properName ## Structure.set(vm, this, instanceType::createStructure(vm, this, m_ ## lowerName ## Prototype.get())); + + FOR_EACH_SIMPLE_BUILTIN_TYPE(CREATE_PROTOTYPE_FOR_SIMPLE_TYPE) + +#undef CREATE_PROTOTYPE_FOR_SIMPLE_TYPE + + m_iteratorPrototype.set(vm, this, IteratorPrototype::create(vm, this, IteratorPrototype::createStructure(vm, this, m_objectPrototype.get()))); + +#define CREATE_PROTOTYPE_FOR_DERIVED_ITERATOR_TYPE(capitalName, lowerName, properName, instanceType, jsName) \ +m_ ## lowerName ## Prototype.set(vm, this, capitalName##Prototype::create(vm, this, capitalName##Prototype::createStructure(vm, this, m_iteratorPrototype.get()))); \ +m_ ## properName ## Structure.set(vm, this, instanceType::createStructure(vm, this, m_ ## lowerName ## Prototype.get())); + + FOR_EACH_BUILTIN_DERIVED_ITERATOR_TYPE(CREATE_PROTOTYPE_FOR_DERIVED_ITERATOR_TYPE) + m_propertyNameIteratorStructure.set(vm, this, JSPropertyNameIterator::createStructure(vm, this, m_iteratorPrototype.get())); + +#undef CREATE_PROTOTYPE_FOR_DERIVED_ITERATOR_TYPE + + // Constructors + + ObjectConstructor* objectConstructor = ObjectConstructor::create(vm, this, ObjectConstructor::createStructure(vm, this, m_functionPrototype.get()), m_objectPrototype.get()); + m_objectConstructor.set(vm, this, objectConstructor); + + JSFunction* definePropertyFunction = m_objectConstructor->addDefineProperty(exec, this); + m_definePropertyFunction.set(vm, this, definePropertyFunction); + + JSCell* functionConstructor = FunctionConstructor::create(vm, FunctionConstructor::createStructure(vm, this, m_functionPrototype.get()), m_functionPrototype.get()); + JSCell* arrayConstructor = ArrayConstructor::create(vm, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get()); + + m_regExpConstructor.set(vm, this, RegExpConstructor::create(vm, RegExpConstructor::createStructure(vm, this, m_functionPrototype.get()), m_regExpPrototype.get())); + +#define CREATE_CONSTRUCTOR_FOR_SIMPLE_TYPE(capitalName, lowerName, properName, instanceType, jsName) \ +capitalName ## Constructor* lowerName ## Constructor = capitalName ## Constructor::create(vm, capitalName ## Constructor::createStructure(vm, this, m_functionPrototype.get()), m_ ## lowerName ## Prototype.get()); \ +m_ ## lowerName ## Prototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, lowerName ## Constructor, DontEnum); \ + + FOR_EACH_SIMPLE_BUILTIN_TYPE(CREATE_CONSTRUCTOR_FOR_SIMPLE_TYPE) + +#undef CREATE_CONSTRUCTOR_FOR_SIMPLE_TYPE + + m_errorConstructor.set(vm, this, errorConstructor); + + Structure* nativeErrorPrototypeStructure = NativeErrorPrototype::createStructure(vm, this, m_errorPrototype.get()); + Structure* nativeErrorStructure = NativeErrorConstructor::createStructure(vm, this, m_functionPrototype.get()); + m_evalErrorConstructor.set(vm, this, NativeErrorConstructor::create(vm, this, nativeErrorStructure, nativeErrorPrototypeStructure, ASCIILiteral("EvalError"))); + m_rangeErrorConstructor.set(vm, this, NativeErrorConstructor::create(vm, this, nativeErrorStructure, nativeErrorPrototypeStructure, ASCIILiteral("RangeError"))); + m_referenceErrorConstructor.set(vm, this, NativeErrorConstructor::create(vm, this, nativeErrorStructure, nativeErrorPrototypeStructure, ASCIILiteral("ReferenceError"))); + m_syntaxErrorConstructor.set(vm, this, NativeErrorConstructor::create(vm, this, nativeErrorStructure, nativeErrorPrototypeStructure, ASCIILiteral("SyntaxError"))); + m_typeErrorConstructor.set(vm, this, NativeErrorConstructor::create(vm, this, nativeErrorStructure, nativeErrorPrototypeStructure, ASCIILiteral("TypeError"))); + m_URIErrorConstructor.set(vm, this, NativeErrorConstructor::create(vm, this, nativeErrorStructure, nativeErrorPrototypeStructure, ASCIILiteral("URIError"))); + m_promiseConstructor.set(vm, this, JSPromiseConstructor::create(vm, JSPromiseConstructor::createStructure(vm, this, m_functionPrototype.get()), m_promisePrototype.get())); + + m_objectPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, objectConstructor, DontEnum); + m_functionPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, functionConstructor, DontEnum); + m_arrayPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, arrayConstructor, DontEnum); + m_regExpPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, m_regExpConstructor.get(), DontEnum); + m_promisePrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, m_promiseConstructor.get(), DontEnum); + + putDirectWithoutTransition(vm, vm.propertyNames->Object, objectConstructor, DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->Function, functionConstructor, DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->Array, arrayConstructor, DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->RegExp, m_regExpConstructor.get(), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->EvalError, m_evalErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->RangeError, m_rangeErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->ReferenceError, m_referenceErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->SyntaxError, m_syntaxErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->TypeError, m_typeErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->URIError, m_URIErrorConstructor.get(), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->Promise, m_promiseConstructor.get(), DontEnum); + + +#define PUT_CONSTRUCTOR_FOR_SIMPLE_TYPE(capitalName, lowerName, properName, instanceType, jsName) \ +putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Constructor, DontEnum); \ + + FOR_EACH_SIMPLE_BUILTIN_TYPE_WITH_CONSTRUCTOR(PUT_CONSTRUCTOR_FOR_SIMPLE_TYPE) + +#undef PUT_CONSTRUCTOR_FOR_SIMPLE_TYPE + PrototypeMap& prototypeMap = vm.prototypeMap; + Structure* iteratorResultStructure = prototypeMap.emptyObjectStructureForPrototype(m_objectPrototype.get(), JSFinalObject::defaultInlineCapacity()); + PropertyOffset offset; + iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->done, 0, offset); + iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->value, 0, offset); + m_iteratorResultStructure.set(vm, this, iteratorResultStructure); + + m_evalFunction.set(vm, this, JSFunction::create(vm, this, 1, vm.propertyNames->eval.string(), globalFuncEval)); + putDirectWithoutTransition(vm, vm.propertyNames->eval, m_evalFunction.get(), DontEnum); + +#if ENABLE(INTL) + putDirectWithoutTransition(vm, vm.propertyNames->Intl, IntlObject::create(vm, this, IntlObject::createStructure(vm, this, m_objectPrototype.get())), DontEnum); +#endif // ENABLE(INTL) + putDirectWithoutTransition(vm, vm.propertyNames->JSON, JSONObject::create(vm, JSONObject::createStructure(vm, this, m_objectPrototype.get())), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->Math, MathObject::create(vm, this, MathObject::createStructure(vm, this, m_objectPrototype.get())), DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->Reflect, ReflectObject::create(vm, this, ReflectObject::createStructure(vm, this, m_objectPrototype.get())), DontEnum); + + std::array<InternalFunction*, NUMBER_OF_TYPED_ARRAY_TYPES> typedArrayConstructors; + typedArrayConstructors[toIndex(TypeInt8)] = JSInt8ArrayConstructor::create(vm, JSInt8ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeInt8)].prototype.get(), ASCIILiteral("Int8Array")); + typedArrayConstructors[toIndex(TypeInt16)] = JSInt16ArrayConstructor::create(vm, JSInt16ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeInt16)].prototype.get(), ASCIILiteral("Int16Array")); + typedArrayConstructors[toIndex(TypeInt32)] = JSInt32ArrayConstructor::create(vm, JSInt32ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeInt32)].prototype.get(), ASCIILiteral("Int32Array")); + typedArrayConstructors[toIndex(TypeUint8)] = JSUint8ArrayConstructor::create(vm, JSUint8ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeUint8)].prototype.get(), ASCIILiteral("Uint8Array")); + typedArrayConstructors[toIndex(TypeUint8Clamped)] = JSUint8ClampedArrayConstructor::create(vm, JSUint8ClampedArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeUint8Clamped)].prototype.get(), ASCIILiteral("Uint8ClampedArray")); + typedArrayConstructors[toIndex(TypeUint16)] = JSUint16ArrayConstructor::create(vm, JSUint16ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeUint16)].prototype.get(), ASCIILiteral("Uint16Array")); + typedArrayConstructors[toIndex(TypeUint32)] = JSUint32ArrayConstructor::create(vm, JSUint32ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeUint32)].prototype.get(), ASCIILiteral("Uint32Array")); + typedArrayConstructors[toIndex(TypeFloat32)] = JSFloat32ArrayConstructor::create(vm, JSFloat32ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeFloat32)].prototype.get(), ASCIILiteral("Float32Array")); + typedArrayConstructors[toIndex(TypeFloat64)] = JSFloat64ArrayConstructor::create(vm, JSFloat64ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeFloat64)].prototype.get(), ASCIILiteral("Float64Array")); + typedArrayConstructors[toIndex(TypeDataView)] = JSDataViewConstructor::create(vm, JSDataViewConstructor::createStructure(vm, this, m_functionPrototype.get()), m_typedArrays[toIndex(TypeDataView)].prototype.get(), ASCIILiteral("DataView")); + + for (unsigned typedArrayIndex = NUMBER_OF_TYPED_ARRAY_TYPES; typedArrayIndex--;) { + m_typedArrays[typedArrayIndex].prototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, typedArrayConstructors[typedArrayIndex], DontEnum); + putDirectWithoutTransition(vm, Identifier::fromString(exec, typedArrayConstructors[typedArrayIndex]->name(exec)), typedArrayConstructors[typedArrayIndex], DontEnum); + } + + JSFunction* builtinLog = JSFunction::create(vm, this, 1, vm.propertyNames->emptyIdentifier.string(), globalFuncBuiltinLog); + + JSFunction* privateFuncAbs = JSFunction::create(vm, this, 0, String(), mathProtoFuncAbs, AbsIntrinsic); + JSFunction* privateFuncFloor = JSFunction::create(vm, this, 0, String(), mathProtoFuncFloor, FloorIntrinsic); + JSFunction* privateFuncIsFinite = JSFunction::create(vm, this, 0, String(), globalFuncIsFinite); + + JSFunction* privateFuncGetTemplateObject = JSFunction::create(vm, this, 0, String(), getTemplateObject); + JSFunction* privateFuncToLength = JSFunction::createBuiltinFunction(vm, globalObjectToLengthCodeGenerator(vm), this); + JSFunction* privateFuncToInteger = JSFunction::createBuiltinFunction(vm, globalObjectToIntegerCodeGenerator(vm), this); + + GlobalPropertyInfo staticGlobals[] = { + GlobalPropertyInfo(vm.propertyNames->NaN, jsNaN(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->Infinity, jsNumber(std::numeric_limits<double>::infinity()), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->undefinedKeyword, jsUndefined(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->undefinedPrivateName, jsUndefined(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->ObjectPrivateName, objectConstructor, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->ownEnumerablePropertyKeysPrivateName, JSFunction::create(vm, this, 0, String(), ownEnumerablePropertyKeys), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->getTemplateObjectPrivateName, privateFuncGetTemplateObject, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->enqueueJobPrivateName, JSFunction::create(vm, this, 0, String(), enqueueJob), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->TypeErrorPrivateName, m_typeErrorConstructor.get(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->BuiltinLogPrivateName, builtinLog, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->ArrayPrivateName, arrayConstructor, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->NumberPrivateName, numberConstructor, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->StringPrivateName, stringConstructor, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->absPrivateName, privateFuncAbs, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->floorPrivateName, privateFuncFloor, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->getPrototypeOfPrivateName, privateFuncFloor, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->getOwnPropertyNamesPrivateName, privateFuncFloor, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->isFinitePrivateName, privateFuncIsFinite, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->arrayIterationKindKeyPrivateName, jsNumber(ArrayIterateKey), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->arrayIterationKindValuePrivateName, jsNumber(ArrayIterateValue), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->arrayIterationKindKeyValuePrivateName, jsNumber(ArrayIterateKeyValue), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().symbolIteratorPrivateName(), Symbol::create(vm, static_cast<SymbolImpl&>(*vm.propertyNames->iteratorSymbol.impl())), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->PromisePrivateName, m_promiseConstructor.get(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->promisePendingPrivateName, jsNumber(static_cast<unsigned>(JSPromise::Status::Pending)), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->promiseFulfilledPrivateName, jsNumber(static_cast<unsigned>(JSPromise::Status::Fulfilled)), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->promiseRejectedPrivateName, jsNumber(static_cast<unsigned>(JSPromise::Status::Rejected)), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().toLengthPrivateName(), privateFuncToLength, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().toIntegerPrivateName(), privateFuncToInteger, DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().isObjectPrivateName(), JSFunction::createBuiltinFunction(vm, globalObjectIsObjectCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().isPromisePrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromiseIsPromiseCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().newPromiseReactionPrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromiseNewPromiseReactionCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().newPromiseCapabilityPrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromiseNewPromiseCapabilityCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().triggerPromiseReactionsPrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromiseTriggerPromiseReactionsCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().rejectPromisePrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromiseRejectPromiseCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().fulfillPromisePrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromiseFulfillPromiseCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().createResolvingFunctionsPrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromiseCreateResolvingFunctionsCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().promiseReactionJobPrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromisePromiseReactionJobCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(vm.propertyNames->builtinNames().promiseResolveThenableJobPrivateName(), JSFunction::createBuiltinFunction(vm, operationsPromisePromiseResolveThenableJobCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly), + }; + addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals)); + + m_specialPointers[Special::CallFunction] = m_callFunction.get(); + m_specialPointers[Special::ApplyFunction] = m_applyFunction.get(); + m_specialPointers[Special::ObjectConstructor] = objectConstructor; + m_specialPointers[Special::ArrayConstructor] = arrayConstructor; + + m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::DefinePropertyFunction)] = m_definePropertyFunction.get(); + + ConsolePrototype* consolePrototype = ConsolePrototype::create(vm, this, ConsolePrototype::createStructure(vm, this, m_objectPrototype.get())); + m_consoleStructure.set(vm, this, JSConsole::createStructure(vm, this, consolePrototype)); + JSConsole* consoleObject = JSConsole::create(vm, m_consoleStructure.get()); + putDirectWithoutTransition(vm, Identifier::fromString(exec, "console"), consoleObject, DontEnum); + + if (UNLIKELY(Options::enableDollarVM())) { + JSDollarVMPrototype* dollarVMPrototype = JSDollarVMPrototype::create(vm, this, JSDollarVMPrototype::createStructure(vm, this, m_objectPrototype.get())); + m_dollarVMStructure.set(vm, this, JSDollarVM::createStructure(vm, this, dollarVMPrototype)); + JSDollarVM* dollarVM = JSDollarVM::create(vm, m_dollarVMStructure.get()); + putDirectWithoutTransition(vm, Identifier::fromString(exec, "$vm"), dollarVM, DontEnum); + } + + resetPrototype(vm, prototype()); +} + +void JSGlobalObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(cell); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); + + if (symbolTablePut(thisObject, exec, propertyName, value, slot.isStrictMode())) + return; + Base::put(thisObject, exec, propertyName, value, slot); +} + +bool JSGlobalObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(object); + PropertySlot slot(thisObject); + // silently ignore attempts to add accessors aliasing vars. + if (descriptor.isAccessorDescriptor() && symbolTableGet(thisObject, propertyName, slot)) + return false; + return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); +} + +void JSGlobalObject::addGlobalVar(const Identifier& ident) +{ + ConcurrentJITLocker locker(symbolTable()->m_lock); + SymbolTableEntry entry = symbolTable()->get(locker, ident.impl()); + if (!entry.isNull()) + return; + + ScopeOffset offset = symbolTable()->takeNextScopeOffset(locker); + SymbolTableEntry newEntry(VarOffset(offset), 0); + newEntry.prepareToWatch(); + symbolTable()->add(locker, ident.impl(), newEntry); + + ScopeOffset offsetForAssert = addVariables(1); + RELEASE_ASSERT(offsetForAssert == offset); +} + +void JSGlobalObject::addFunction(ExecState* exec, const Identifier& propertyName) +{ + VM& vm = exec->vm(); + removeDirect(vm, propertyName); // Newly declared functions overwrite existing properties. + addGlobalVar(propertyName); +} + +static inline JSObject* lastInPrototypeChain(JSObject* object) +{ + JSObject* o = object; + while (o->prototype().isObject()) + o = asObject(o->prototype()); + return o; +} + +// Private namespace for helpers for JSGlobalObject::haveABadTime() +namespace { + +class ObjectsWithBrokenIndexingFinder : public MarkedBlock::VoidFunctor { +public: + ObjectsWithBrokenIndexingFinder(MarkedArgumentBuffer&, JSGlobalObject*); + IterationStatus operator()(JSCell*); + +private: + void visit(JSCell*); + + MarkedArgumentBuffer& m_foundObjects; + JSGlobalObject* m_globalObject; +}; + +ObjectsWithBrokenIndexingFinder::ObjectsWithBrokenIndexingFinder( + MarkedArgumentBuffer& foundObjects, JSGlobalObject* globalObject) + : m_foundObjects(foundObjects) + , m_globalObject(globalObject) +{ +} + +inline bool hasBrokenIndexing(JSObject* object) +{ + // This will change if we have more indexing types. + IndexingType type = object->indexingType(); + // This could be made obviously more efficient, but isn't made so right now, because + // we expect this to be an unlikely slow path anyway. + return hasUndecided(type) || hasInt32(type) || hasDouble(type) || hasContiguous(type) || hasArrayStorage(type); +} + +inline void ObjectsWithBrokenIndexingFinder::visit(JSCell* cell) +{ + if (!cell->isObject()) + return; + + JSObject* object = asObject(cell); + + // Run this filter first, since it's cheap, and ought to filter out a lot of objects. + if (!hasBrokenIndexing(object)) + return; + + // We only want to have a bad time in the affected global object, not in the entire + // VM. But we have to be careful, since there may be objects that claim to belong to + // a different global object that have prototypes from our global object. + bool foundGlobalObject = false; + for (JSObject* current = object; ;) { + if (current->globalObject() == m_globalObject) { + foundGlobalObject = true; + break; + } + + JSValue prototypeValue = current->prototype(); + if (prototypeValue.isNull()) + break; + current = asObject(prototypeValue); + } + if (!foundGlobalObject) + return; + + m_foundObjects.append(object); +} + +IterationStatus ObjectsWithBrokenIndexingFinder::operator()(JSCell* cell) +{ + visit(cell); + return IterationStatus::Continue; +} + +} // end private namespace for helpers for JSGlobalObject::haveABadTime() + +void JSGlobalObject::haveABadTime(VM& vm) +{ + ASSERT(&vm == &this->vm()); + + if (isHavingABadTime()) + return; + + // Make sure that all allocations or indexed storage transitions that are inlining + // the assumption that it's safe to transition to a non-SlowPut array storage don't + // do so anymore. + m_havingABadTimeWatchpoint->fireAll("Having a bad time"); + ASSERT(isHavingABadTime()); // The watchpoint is what tells us that we're having a bad time. + + // Make sure that all JSArray allocations that load the appropriate structure from + // this object now load a structure that uses SlowPut. + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + m_arrayStructureForIndexingShapeDuringAllocation[i].set(vm, this, originalArrayStructureForIndexingType(ArrayWithSlowPutArrayStorage)); + + // Make sure that all objects that have indexed storage switch to the slow kind of + // indexed storage. + MarkedArgumentBuffer foundObjects; // Use MarkedArgumentBuffer because switchToSlowPutArrayStorage() may GC. + ObjectsWithBrokenIndexingFinder finder(foundObjects, this); + { + HeapIterationScope iterationScope(vm.heap); + vm.heap.objectSpace().forEachLiveCell(iterationScope, finder); + } + while (!foundObjects.isEmpty()) { + JSObject* object = asObject(foundObjects.last()); + foundObjects.removeLast(); + ASSERT(hasBrokenIndexing(object)); + object->switchToSlowPutArrayStorage(vm); + } +} + +bool JSGlobalObject::objectPrototypeIsSane() +{ + return !hasIndexedProperties(m_objectPrototype->indexingType()) + && m_objectPrototype->prototype().isNull(); +} + +bool JSGlobalObject::arrayPrototypeChainIsSane() +{ + return !hasIndexedProperties(m_arrayPrototype->indexingType()) + && m_arrayPrototype->prototype() == m_objectPrototype.get() + && objectPrototypeIsSane(); +} + +bool JSGlobalObject::stringPrototypeChainIsSane() +{ + return !hasIndexedProperties(m_stringPrototype->indexingType()) + && m_stringPrototype->prototype() == m_objectPrototype.get() + && objectPrototypeIsSane(); +} + +void JSGlobalObject::createThrowTypeError(VM& vm) +{ + JSFunction* thrower = JSFunction::create(vm, this, 0, String(), globalFuncThrowTypeError); + GetterSetter* getterSetter = GetterSetter::create(vm, this); + getterSetter->setGetter(vm, this, thrower); + getterSetter->setSetter(vm, this, thrower); + m_throwTypeErrorGetterSetter.set(vm, this, getterSetter); +} + +// Set prototype, and also insert the object prototype at the end of the chain. +void JSGlobalObject::resetPrototype(VM& vm, JSValue prototype) +{ + setPrototype(vm, prototype); + + JSObject* oldLastInPrototypeChain = lastInPrototypeChain(this); + JSObject* objectPrototype = m_objectPrototype.get(); + if (oldLastInPrototypeChain != objectPrototype) + oldLastInPrototypeChain->setPrototype(vm, objectPrototype); + + // Whenever we change the prototype of the global object, we need to create a new JSProxy with the correct prototype. + setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, this, prototype, PureForwardingProxyType), this)); +} + +void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_globalThis); + + visitor.append(&thisObject->m_globalCallee); + visitor.append(&thisObject->m_regExpConstructor); + visitor.append(&thisObject->m_errorConstructor); + visitor.append(&thisObject->m_evalErrorConstructor); + visitor.append(&thisObject->m_rangeErrorConstructor); + visitor.append(&thisObject->m_referenceErrorConstructor); + visitor.append(&thisObject->m_syntaxErrorConstructor); + visitor.append(&thisObject->m_typeErrorConstructor); + visitor.append(&thisObject->m_URIErrorConstructor); + visitor.append(&thisObject->m_objectConstructor); + visitor.append(&thisObject->m_promiseConstructor); + + visitor.append(&thisObject->m_nullGetterFunction); + visitor.append(&thisObject->m_nullSetterFunction); + + visitor.append(&thisObject->m_parseIntFunction); + visitor.append(&thisObject->m_evalFunction); + visitor.append(&thisObject->m_callFunction); + visitor.append(&thisObject->m_applyFunction); + visitor.append(&thisObject->m_definePropertyFunction); + visitor.append(&thisObject->m_arrayProtoValuesFunction); + visitor.append(&thisObject->m_initializePromiseFunction); + visitor.append(&thisObject->m_newPromiseDeferredFunction); + visitor.append(&thisObject->m_throwTypeErrorGetterSetter); + + visitor.append(&thisObject->m_objectPrototype); + visitor.append(&thisObject->m_functionPrototype); + visitor.append(&thisObject->m_arrayPrototype); + visitor.append(&thisObject->m_errorPrototype); + visitor.append(&thisObject->m_iteratorPrototype); + visitor.append(&thisObject->m_promisePrototype); + + visitor.append(&thisObject->m_debuggerScopeStructure); + visitor.append(&thisObject->m_withScopeStructure); + visitor.append(&thisObject->m_strictEvalActivationStructure); + visitor.append(&thisObject->m_lexicalEnvironmentStructure); + visitor.append(&thisObject->m_directArgumentsStructure); + visitor.append(&thisObject->m_scopedArgumentsStructure); + visitor.append(&thisObject->m_outOfBandArgumentsStructure); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + visitor.append(&thisObject->m_originalArrayStructureForIndexingShape[i]); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + visitor.append(&thisObject->m_arrayStructureForIndexingShapeDuringAllocation[i]); + visitor.append(&thisObject->m_booleanObjectStructure); + visitor.append(&thisObject->m_callbackConstructorStructure); + visitor.append(&thisObject->m_callbackFunctionStructure); + visitor.append(&thisObject->m_callbackObjectStructure); + visitor.append(&thisObject->m_propertyNameIteratorStructure); +#if JSC_OBJC_API_ENABLED + visitor.append(&thisObject->m_objcCallbackFunctionStructure); + visitor.append(&thisObject->m_objcWrapperObjectStructure); +#endif + visitor.append(&thisObject->m_nullPrototypeObjectStructure); + visitor.append(&thisObject->m_errorStructure); + visitor.append(&thisObject->m_calleeStructure); + visitor.append(&thisObject->m_functionStructure); + visitor.append(&thisObject->m_boundFunctionStructure); + visitor.append(&thisObject->m_namedFunctionStructure); + visitor.append(&thisObject->m_symbolObjectStructure); + visitor.append(&thisObject->m_regExpStructure); + visitor.append(&thisObject->m_regExpMatchesArrayStructure); + visitor.append(&thisObject->m_consoleStructure); + visitor.append(&thisObject->m_dollarVMStructure); + visitor.append(&thisObject->m_internalFunctionStructure); + visitor.append(&thisObject->m_promiseStructure); +#if ENABLE(WEBASSEMBLY) + visitor.append(&thisObject->m_wasmModuleStructure); +#endif + +#define VISIT_SIMPLE_TYPE(CapitalName, lowerName, properName, instanceType, jsName) \ + visitor.append(&thisObject->m_ ## lowerName ## Prototype); \ + visitor.append(&thisObject->m_ ## properName ## Structure); \ + + FOR_EACH_SIMPLE_BUILTIN_TYPE(VISIT_SIMPLE_TYPE) + FOR_EACH_BUILTIN_DERIVED_ITERATOR_TYPE(VISIT_SIMPLE_TYPE) + +#undef VISIT_SIMPLE_TYPE + + for (unsigned i = NUMBER_OF_TYPED_ARRAY_TYPES; i--;) { + visitor.append(&thisObject->m_typedArrays[i].prototype); + visitor.append(&thisObject->m_typedArrays[i].structure); + } +} + +JSValue JSGlobalObject::toThis(JSCell*, ExecState* exec, ECMAMode ecmaMode) +{ + if (ecmaMode == StrictMode) + return jsUndefined(); + return exec->globalThisValue(); +} + +ExecState* JSGlobalObject::globalExec() +{ + return CallFrame::create(m_globalCallFrame); +} + +void JSGlobalObject::addStaticGlobals(GlobalPropertyInfo* globals, int count) +{ + ScopeOffset startOffset = addVariables(count); + + for (int i = 0; i < count; ++i) { + GlobalPropertyInfo& global = globals[i]; + ASSERT(global.attributes & DontDelete); + + ScopeOffset offset; + { + ConcurrentJITLocker locker(symbolTable()->m_lock); + offset = symbolTable()->takeNextScopeOffset(locker); + RELEASE_ASSERT(offset = startOffset + i); + SymbolTableEntry newEntry(VarOffset(offset), global.attributes); + symbolTable()->add(locker, global.identifier.impl(), newEntry); + } + variableAt(offset).set(vm(), this, global.value); + } +} + +bool JSGlobalObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(object); + if (getStaticFunctionSlot<Base>(exec, globalObjectTable, thisObject, propertyName, slot)) + return true; + return symbolTableGet(thisObject, propertyName, slot); +} + +void JSGlobalObject::clearRareData(JSCell* cell) +{ + jsCast<JSGlobalObject*>(cell)->m_rareData = nullptr; +} + +void slowValidateCell(JSGlobalObject* globalObject) +{ + RELEASE_ASSERT(globalObject->isGlobalObject()); + ASSERT_GC_OBJECT_INHERITS(globalObject, JSGlobalObject::info()); +} + +UnlinkedProgramCodeBlock* JSGlobalObject::createProgramCodeBlock(CallFrame* callFrame, ProgramExecutable* executable, JSObject** exception) +{ + ParserError error; + JSParserStrictMode strictMode = executable->isStrictMode() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict; + DebuggerMode debuggerMode = hasDebugger() ? DebuggerOn : DebuggerOff; + ProfilerMode profilerMode = hasProfiler() ? ProfilerOn : ProfilerOff; + UnlinkedProgramCodeBlock* unlinkedCodeBlock = vm().codeCache()->getProgramCodeBlock( + vm(), executable, executable->source(), JSParserBuiltinMode::NotBuiltin, strictMode, + debuggerMode, profilerMode, error); + + if (hasDebugger()) + debugger()->sourceParsed(callFrame, executable->source().provider(), error.line(), error.message()); + + if (error.isValid()) { + *exception = error.toErrorObject(this, executable->source()); + return nullptr; + } + + return unlinkedCodeBlock; +} + +UnlinkedEvalCodeBlock* JSGlobalObject::createEvalCodeBlock(CallFrame* callFrame, EvalExecutable* executable, ThisTDZMode thisTDZMode, const VariableEnvironment* variablesUnderTDZ) +{ + ParserError error; + JSParserStrictMode strictMode = executable->isStrictMode() ? JSParserStrictMode::Strict : JSParserStrictMode::NotStrict; + DebuggerMode debuggerMode = hasDebugger() ? DebuggerOn : DebuggerOff; + ProfilerMode profilerMode = hasProfiler() ? ProfilerOn : ProfilerOff; + UnlinkedEvalCodeBlock* unlinkedCodeBlock = vm().codeCache()->getEvalCodeBlock( + vm(), executable, executable->source(), JSParserBuiltinMode::NotBuiltin, strictMode, thisTDZMode, debuggerMode, profilerMode, error, variablesUnderTDZ); + + if (hasDebugger()) + debugger()->sourceParsed(callFrame, executable->source().provider(), error.line(), error.message()); + + if (error.isValid()) { + throwVMError(callFrame, error.toErrorObject(this, executable->source())); + return nullptr; + } + + return unlinkedCodeBlock; +} + +void JSGlobalObject::setRemoteDebuggingEnabled(bool enabled) +{ +#if ENABLE(REMOTE_INSPECTOR) + m_inspectorDebuggable->setRemoteDebuggingAllowed(enabled); +#else + UNUSED_PARAM(enabled); +#endif +} + +bool JSGlobalObject::remoteDebuggingEnabled() const +{ +#if ENABLE(REMOTE_INSPECTOR) + return m_inspectorDebuggable->remoteDebuggingAllowed(); +#else + return false; +#endif +} + +#if ENABLE(WEB_REPLAY) +void JSGlobalObject::setInputCursor(PassRefPtr<InputCursor> prpCursor) +{ + m_inputCursor = prpCursor; + ASSERT(m_inputCursor); + + InputCursor& cursor = inputCursor(); + // Save or set the random seed. This performed here rather than the constructor + // to avoid threading the input cursor through all the abstraction layers. + if (cursor.isCapturing()) + cursor.appendInput<SetRandomSeed>(m_weakRandom.seedUnsafe()); + else if (cursor.isReplaying()) { + if (SetRandomSeed* input = cursor.fetchInput<SetRandomSeed>()) + m_weakRandom.initializeSeed(static_cast<unsigned>(input->randomSeed())); + } +} +#endif + +void JSGlobalObject::setName(const String& name) +{ + m_name = name; + +#if ENABLE(REMOTE_INSPECTOR) + m_inspectorDebuggable->update(); +#endif +} + +void JSGlobalObject::queueMicrotask(PassRefPtr<Microtask> task) +{ + if (globalObjectMethodTable()->queueTaskToEventLoop) { + globalObjectMethodTable()->queueTaskToEventLoop(this, task); + return; + } + + vm().queueMicrotask(this, task); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h new file mode 100644 index 000000000..9986bcf62 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -0,0 +1,731 @@ +/* + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * Copyright (C) 2007, 2008, 2009, 2014, 2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSGlobalObject_h +#define JSGlobalObject_h + +#include "ArrayAllocationProfile.h" +#include "JSArray.h" +#include "JSArrayBufferPrototype.h" +#include "JSClassRef.h" +#include "JSProxy.h" +#include "JSSegmentedVariableObject.h" +#include "JSWeakObjectMapRefInternal.h" +#include "NumberPrototype.h" +#include "RuntimeFlags.h" +#include "SpecialPointer.h" +#include "StringPrototype.h" +#include "StructureChain.h" +#include "StructureRareDataInlines.h" +#include "SymbolPrototype.h" +#include "TemplateRegistry.h" +#include "VM.h" +#include "VariableEnvironment.h" +#include "Watchpoint.h" +#include <JavaScriptCore/JSBase.h> +#include <array> +#include <wtf/HashSet.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RandomNumber.h> + +struct OpaqueJSClass; +struct OpaqueJSClassContextData; + +namespace Inspector { +class JSGlobalObjectInspectorController; +} + +namespace JSC { + +class ArrayPrototype; +class BooleanPrototype; +class ConsoleClient; +class Debugger; +class ErrorConstructor; +class ErrorPrototype; +class EvalCodeBlock; +class EvalExecutable; +class FunctionCodeBlock; +class FunctionExecutable; +class FunctionPrototype; +class GetterSetter; +class GlobalCodeBlock; +class InputCursor; +class JSGlobalObjectDebuggable; +class JSPromiseConstructor; +class JSPromisePrototype; +class JSStack; +class LLIntOffsetsExtractor; +class Microtask; +class NativeErrorConstructor; +class ObjectConstructor; +class ProgramCodeBlock; +class ProgramExecutable; +class RegExpConstructor; +class RegExpPrototype; +class SourceCode; +class NullGetterFunction; +class NullSetterFunction; +enum class ThisTDZMode; +struct ActivationStackNode; +struct HashTable; + +#define DEFINE_STANDARD_BUILTIN(macro, upperName, lowerName) macro(upperName, lowerName, lowerName, JS ## upperName, upperName) + +#define FOR_EACH_SIMPLE_BUILTIN_TYPE_WITH_CONSTRUCTOR(macro) \ + macro(Set, set, set, JSSet, Set) \ + macro(Map, map, map, JSMap, Map) \ + macro(Date, date, date, DateInstance, Date) \ + macro(String, string, stringObject, StringObject, String) \ + macro(Symbol, symbol, symbolObject, SymbolObject, Symbol) \ + macro(Boolean, boolean, booleanObject, BooleanObject, Boolean) \ + macro(Number, number, numberObject, NumberObject, Number) \ + macro(Error, error, error, ErrorInstance, Error) \ + macro(JSArrayBuffer, arrayBuffer, arrayBuffer, JSArrayBuffer, ArrayBuffer) \ + DEFINE_STANDARD_BUILTIN(macro, WeakMap, weakMap) \ + DEFINE_STANDARD_BUILTIN(macro, WeakSet, weakSet) \ + +#define FOR_EACH_BUILTIN_DERIVED_ITERATOR_TYPE(macro) \ + DEFINE_STANDARD_BUILTIN(macro, ArrayIterator, arrayIterator) \ + DEFINE_STANDARD_BUILTIN(macro, MapIterator, mapIterator) \ + DEFINE_STANDARD_BUILTIN(macro, SetIterator, setIterator) \ + DEFINE_STANDARD_BUILTIN(macro, StringIterator, stringIterator) \ + +#define FOR_EACH_BUILTIN_ITERATOR_TYPE(macro) \ + DEFINE_STANDARD_BUILTIN(macro, Iterator, iterator) \ + FOR_EACH_BUILTIN_DERIVED_ITERATOR_TYPE(macro) \ + +#define FOR_EACH_SIMPLE_BUILTIN_TYPE(macro) \ + FOR_EACH_SIMPLE_BUILTIN_TYPE_WITH_CONSTRUCTOR(macro) \ + +#define DECLARE_SIMPLE_BUILTIN_TYPE(capitalName, lowerName, properName, instanceType, jsName) \ + class JS ## capitalName; \ + class capitalName ## Prototype; \ + class capitalName ## Constructor; + +class IteratorPrototype; +FOR_EACH_SIMPLE_BUILTIN_TYPE(DECLARE_SIMPLE_BUILTIN_TYPE) +FOR_EACH_BUILTIN_DERIVED_ITERATOR_TYPE(DECLARE_SIMPLE_BUILTIN_TYPE) + +#undef DECLARE_SIMPLE_BUILTIN_TYPE + +typedef Vector<ExecState*, 16> ExecStateStack; + +struct GlobalObjectMethodTable { + typedef bool (*AllowsAccessFromFunctionPtr)(const JSGlobalObject*, ExecState*); + AllowsAccessFromFunctionPtr allowsAccessFrom; + + typedef bool (*SupportsProfilingFunctionPtr)(const JSGlobalObject*); + SupportsProfilingFunctionPtr supportsProfiling; + + typedef bool (*SupportsRichSourceInfoFunctionPtr)(const JSGlobalObject*); + SupportsRichSourceInfoFunctionPtr supportsRichSourceInfo; + + typedef bool (*ShouldInterruptScriptFunctionPtr)(const JSGlobalObject*); + ShouldInterruptScriptFunctionPtr shouldInterruptScript; + + typedef RuntimeFlags (*JavaScriptRuntimeFlagsFunctionPtr)(const JSGlobalObject*); + JavaScriptRuntimeFlagsFunctionPtr javaScriptRuntimeFlags; + + typedef void (*QueueTaskToEventLoopFunctionPtr)(const JSGlobalObject*, PassRefPtr<Microtask>); + QueueTaskToEventLoopFunctionPtr queueTaskToEventLoop; + + typedef bool (*ShouldInterruptScriptBeforeTimeoutPtr)(const JSGlobalObject*); + ShouldInterruptScriptBeforeTimeoutPtr shouldInterruptScriptBeforeTimeout; +}; + +class JSGlobalObject : public JSSegmentedVariableObject { +private: + typedef HashSet<RefPtr<OpaqueJSWeakObjectMap>> WeakMapSet; + typedef HashMap<OpaqueJSClass*, std::unique_ptr<OpaqueJSClassContextData>> OpaqueJSClassDataMap; + + struct JSGlobalObjectRareData { + JSGlobalObjectRareData() + : profileGroup(0) + { + } + + WeakMapSet weakMaps; + unsigned profileGroup; + + OpaqueJSClassDataMap opaqueJSClassData; + }; + +protected: + Register m_globalCallFrame[JSStack::CallFrameHeaderSize]; + + WriteBarrier<JSObject> m_globalThis; + + WriteBarrier<JSObject> m_globalCallee; + WriteBarrier<RegExpConstructor> m_regExpConstructor; + WriteBarrier<ErrorConstructor> m_errorConstructor; + WriteBarrier<NativeErrorConstructor> m_evalErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_rangeErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_referenceErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_syntaxErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_typeErrorConstructor; + WriteBarrier<NativeErrorConstructor> m_URIErrorConstructor; + WriteBarrier<JSPromiseConstructor> m_promiseConstructor; + WriteBarrier<ObjectConstructor> m_objectConstructor; + + WriteBarrier<NullGetterFunction> m_nullGetterFunction; + WriteBarrier<NullSetterFunction> m_nullSetterFunction; + + WriteBarrier<JSFunction> m_parseIntFunction; + + WriteBarrier<JSFunction> m_evalFunction; + WriteBarrier<JSFunction> m_callFunction; + WriteBarrier<JSFunction> m_applyFunction; + WriteBarrier<JSFunction> m_definePropertyFunction; + WriteBarrier<JSFunction> m_arrayProtoValuesFunction; + WriteBarrier<JSFunction> m_initializePromiseFunction; + WriteBarrier<JSFunction> m_newPromiseDeferredFunction; + WriteBarrier<GetterSetter> m_throwTypeErrorGetterSetter; + + WriteBarrier<ObjectPrototype> m_objectPrototype; + WriteBarrier<FunctionPrototype> m_functionPrototype; + WriteBarrier<ArrayPrototype> m_arrayPrototype; + WriteBarrier<RegExpPrototype> m_regExpPrototype; + WriteBarrier<IteratorPrototype> m_iteratorPrototype; + WriteBarrier<JSPromisePrototype> m_promisePrototype; + + WriteBarrier<Structure> m_debuggerScopeStructure; + WriteBarrier<Structure> m_withScopeStructure; + WriteBarrier<Structure> m_strictEvalActivationStructure; + WriteBarrier<Structure> m_lexicalEnvironmentStructure; + WriteBarrier<Structure> m_directArgumentsStructure; + WriteBarrier<Structure> m_scopedArgumentsStructure; + WriteBarrier<Structure> m_outOfBandArgumentsStructure; + + // Lists the actual structures used for having these particular indexing shapes. + WriteBarrier<Structure> m_originalArrayStructureForIndexingShape[NumberOfIndexingShapes]; + // Lists the structures we should use during allocation for these particular indexing shapes. + WriteBarrier<Structure> m_arrayStructureForIndexingShapeDuringAllocation[NumberOfIndexingShapes]; + + WriteBarrier<Structure> m_callbackConstructorStructure; + WriteBarrier<Structure> m_callbackFunctionStructure; + WriteBarrier<Structure> m_callbackObjectStructure; + WriteBarrier<Structure> m_propertyNameIteratorStructure; +#if JSC_OBJC_API_ENABLED + WriteBarrier<Structure> m_objcCallbackFunctionStructure; + WriteBarrier<Structure> m_objcWrapperObjectStructure; +#endif + WriteBarrier<Structure> m_nullPrototypeObjectStructure; + WriteBarrier<Structure> m_calleeStructure; + WriteBarrier<Structure> m_functionStructure; + WriteBarrier<Structure> m_boundFunctionStructure; + WriteBarrier<Structure> m_namedFunctionStructure; + PropertyOffset m_functionNameOffset; + WriteBarrier<Structure> m_privateNameStructure; + WriteBarrier<Structure> m_regExpStructure; + WriteBarrier<Structure> m_consoleStructure; + WriteBarrier<Structure> m_dollarVMStructure; + WriteBarrier<Structure> m_internalFunctionStructure; + WriteBarrier<Structure> m_iteratorResultStructure; + WriteBarrier<Structure> m_regExpMatchesArrayStructure; + WriteBarrier<Structure> m_promiseStructure; +#if ENABLE(WEBASSEMBLY) + WriteBarrier<Structure> m_wasmModuleStructure; +#endif + +#define DEFINE_STORAGE_FOR_SIMPLE_TYPE(capitalName, lowerName, properName, instanceType, jsName) \ + WriteBarrier<capitalName ## Prototype> m_ ## lowerName ## Prototype; \ + WriteBarrier<Structure> m_ ## properName ## Structure; + + FOR_EACH_SIMPLE_BUILTIN_TYPE(DEFINE_STORAGE_FOR_SIMPLE_TYPE) + FOR_EACH_BUILTIN_DERIVED_ITERATOR_TYPE(DEFINE_STORAGE_FOR_SIMPLE_TYPE) + +#undef DEFINE_STORAGE_FOR_SIMPLE_TYPE + + struct TypedArrayData { + WriteBarrier<JSObject> prototype; + WriteBarrier<Structure> structure; + }; + + std::array<TypedArrayData, NUMBER_OF_TYPED_ARRAY_TYPES> m_typedArrays; + + JSCell* m_specialPointers[Special::TableSize]; // Special pointers used by the LLInt and JIT. + JSCell* m_linkTimeConstants[LinkTimeConstantCount]; + + String m_name; + + Debugger* m_debugger; + + VM& m_vm; + +#if ENABLE(WEB_REPLAY) + RefPtr<InputCursor> m_inputCursor; +#endif + +#if ENABLE(REMOTE_INSPECTOR) + std::unique_ptr<Inspector::JSGlobalObjectInspectorController> m_inspectorController; + std::unique_ptr<JSGlobalObjectDebuggable> m_inspectorDebuggable; +#endif + + RefPtr<WatchpointSet> m_masqueradesAsUndefinedWatchpoint; + RefPtr<WatchpointSet> m_havingABadTimeWatchpoint; + RefPtr<WatchpointSet> m_varInjectionWatchpoint; + + std::unique_ptr<JSGlobalObjectRareData> m_rareData; + + WeakRandom m_weakRandom; + + TemplateRegistry m_templateRegistry; + + bool m_evalEnabled; + String m_evalDisabledErrorMessage; + RuntimeFlags m_runtimeFlags; + ConsoleClient* m_consoleClient; + + static JS_EXPORTDATA const GlobalObjectMethodTable s_globalObjectMethodTable; + const GlobalObjectMethodTable* m_globalObjectMethodTable; + + void createRareDataIfNeeded() + { + if (m_rareData) + return; + m_rareData = std::make_unique<JSGlobalObjectRareData>(); + } + +public: + typedef JSSegmentedVariableObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames; + + static JSGlobalObject* create(VM& vm, Structure* structure) + { + JSGlobalObject* globalObject = new (NotNull, allocateCell<JSGlobalObject>(vm.heap)) JSGlobalObject(vm, structure); + globalObject->finishCreation(vm); + vm.heap.addFinalizer(globalObject, destroy); + return globalObject; + } + + DECLARE_EXPORT_INFO; + + bool hasDebugger() const { return m_debugger; } + bool hasProfiler() const { return globalObjectMethodTable()->supportsProfiling(this); } + const RuntimeFlags& runtimeFlags() const { return m_runtimeFlags; } + +protected: + JS_EXPORT_PRIVATE explicit JSGlobalObject(VM&, Structure*, const GlobalObjectMethodTable* = 0); + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + structure()->setGlobalObject(vm, this); + m_runtimeFlags = m_globalObjectMethodTable->javaScriptRuntimeFlags(this); + init(vm); + setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, this, prototype(), PureForwardingProxyType), this)); + } + + void finishCreation(VM& vm, JSObject* thisValue) + { + Base::finishCreation(vm); + structure()->setGlobalObject(vm, this); + m_runtimeFlags = m_globalObjectMethodTable->javaScriptRuntimeFlags(this); + init(vm); + setGlobalThis(vm, thisValue); + } + + void addGlobalVar(const Identifier&); + +public: + JS_EXPORT_PRIVATE ~JSGlobalObject(); + JS_EXPORT_PRIVATE static void destroy(JSCell*); + // We don't need a destructor because we use a finalizer instead. + static const bool needsDestruction = false; + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + + JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + bool hasOwnPropertyForWrite(ExecState*, PropertyName); + JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + + JS_EXPORT_PRIVATE static void defineGetter(JSObject*, ExecState*, PropertyName, JSObject* getterFunc, unsigned attributes); + JS_EXPORT_PRIVATE static void defineSetter(JSObject*, ExecState*, PropertyName, JSObject* setterFunc, unsigned attributes); + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + + // We use this in the code generator as we perform symbol table + // lookups prior to initializing the properties + bool symbolTableHasProperty(PropertyName); + + void addVar(ExecState* exec, const Identifier& propertyName) + { + if (!hasProperty(exec, propertyName)) + addGlobalVar(propertyName); + } + void addFunction(ExecState*, const Identifier&); + + // The following accessors return pristine values, even if a script + // replaces the global object's associated property. + + RegExpConstructor* regExpConstructor() const { return m_regExpConstructor.get(); } + + ErrorConstructor* errorConstructor() const { return m_errorConstructor.get(); } + ObjectConstructor* objectConstructor() const { return m_objectConstructor.get(); } + NativeErrorConstructor* evalErrorConstructor() const { return m_evalErrorConstructor.get(); } + NativeErrorConstructor* rangeErrorConstructor() const { return m_rangeErrorConstructor.get(); } + NativeErrorConstructor* referenceErrorConstructor() const { return m_referenceErrorConstructor.get(); } + NativeErrorConstructor* syntaxErrorConstructor() const { return m_syntaxErrorConstructor.get(); } + NativeErrorConstructor* typeErrorConstructor() const { return m_typeErrorConstructor.get(); } + NativeErrorConstructor* URIErrorConstructor() const { return m_URIErrorConstructor.get(); } + JSPromiseConstructor* promiseConstructor() const { return m_promiseConstructor.get(); } + + NullGetterFunction* nullGetterFunction() const { return m_nullGetterFunction.get(); } + NullSetterFunction* nullSetterFunction() const { return m_nullSetterFunction.get(); } + + JSFunction* parseIntFunction() const { return m_parseIntFunction.get(); } + + JSFunction* evalFunction() const { return m_evalFunction.get(); } + JSFunction* callFunction() const { return m_callFunction.get(); } + JSFunction* applyFunction() const { return m_applyFunction.get(); } + JSFunction* definePropertyFunction() const { return m_definePropertyFunction.get(); } + JSFunction* arrayProtoValuesFunction() const { return m_arrayProtoValuesFunction.get(); } + JSFunction* initializePromiseFunction() const { return m_initializePromiseFunction.get(); } + JSFunction* newPromiseDeferredFunction() const { return m_newPromiseDeferredFunction.get(); } + GetterSetter* throwTypeErrorGetterSetter(VM& vm) + { + if (!m_throwTypeErrorGetterSetter) + createThrowTypeError(vm); + return m_throwTypeErrorGetterSetter.get(); + } + + ObjectPrototype* objectPrototype() const { return m_objectPrototype.get(); } + FunctionPrototype* functionPrototype() const { return m_functionPrototype.get(); } + ArrayPrototype* arrayPrototype() const { return m_arrayPrototype.get(); } + BooleanPrototype* booleanPrototype() const { return m_booleanPrototype.get(); } + StringPrototype* stringPrototype() const { return m_stringPrototype.get(); } + SymbolPrototype* symbolPrototype() const { return m_symbolPrototype.get(); } + NumberPrototype* numberPrototype() const { return m_numberPrototype.get(); } + DatePrototype* datePrototype() const { return m_datePrototype.get(); } + RegExpPrototype* regExpPrototype() const { return m_regExpPrototype.get(); } + ErrorPrototype* errorPrototype() const { return m_errorPrototype.get(); } + IteratorPrototype* iteratorPrototype() const { return m_iteratorPrototype.get(); } + JSPromisePrototype* promisePrototype() const { return m_promisePrototype.get(); } + + Structure* debuggerScopeStructure() const { return m_debuggerScopeStructure.get(); } + Structure* withScopeStructure() const { return m_withScopeStructure.get(); } + Structure* strictEvalActivationStructure() const { return m_strictEvalActivationStructure.get(); } + Structure* activationStructure() const { return m_lexicalEnvironmentStructure.get(); } + Structure* directArgumentsStructure() const { return m_directArgumentsStructure.get(); } + Structure* scopedArgumentsStructure() const { return m_scopedArgumentsStructure.get(); } + Structure* outOfBandArgumentsStructure() const { return m_outOfBandArgumentsStructure.get(); } + Structure* originalArrayStructureForIndexingType(IndexingType indexingType) const + { + ASSERT(indexingType & IsArray); + return m_originalArrayStructureForIndexingShape[(indexingType & IndexingShapeMask) >> IndexingShapeShift].get(); + } + Structure* arrayStructureForIndexingTypeDuringAllocation(IndexingType indexingType) const + { + ASSERT(indexingType & IsArray); + return m_arrayStructureForIndexingShapeDuringAllocation[(indexingType & IndexingShapeMask) >> IndexingShapeShift].get(); + } + Structure* arrayStructureForProfileDuringAllocation(ArrayAllocationProfile* profile) const + { + return arrayStructureForIndexingTypeDuringAllocation(ArrayAllocationProfile::selectIndexingTypeFor(profile)); + } + + bool isOriginalArrayStructure(Structure* structure) + { + return originalArrayStructureForIndexingType(structure->indexingType() | IsArray) == structure; + } + + Structure* booleanObjectStructure() const { return m_booleanObjectStructure.get(); } + Structure* callbackConstructorStructure() const { return m_callbackConstructorStructure.get(); } + Structure* callbackFunctionStructure() const { return m_callbackFunctionStructure.get(); } + Structure* callbackObjectStructure() const { return m_callbackObjectStructure.get(); } + Structure* propertyNameIteratorStructure() const { return m_propertyNameIteratorStructure.get(); } +#if JSC_OBJC_API_ENABLED + Structure* objcCallbackFunctionStructure() const { return m_objcCallbackFunctionStructure.get(); } + Structure* objcWrapperObjectStructure() const { return m_objcWrapperObjectStructure.get(); } +#endif + Structure* dateStructure() const { return m_dateStructure.get(); } + Structure* nullPrototypeObjectStructure() const { return m_nullPrototypeObjectStructure.get(); } + Structure* errorStructure() const { return m_errorStructure.get(); } + Structure* calleeStructure() const { return m_calleeStructure.get(); } + Structure* functionStructure() const { return m_functionStructure.get(); } + Structure* boundFunctionStructure() const { return m_boundFunctionStructure.get(); } + Structure* namedFunctionStructure() const { return m_namedFunctionStructure.get(); } + PropertyOffset functionNameOffset() const { return m_functionNameOffset; } + Structure* numberObjectStructure() const { return m_numberObjectStructure.get(); } + Structure* privateNameStructure() const { return m_privateNameStructure.get(); } + Structure* internalFunctionStructure() const { return m_internalFunctionStructure.get(); } + Structure* mapStructure() const { return m_mapStructure.get(); } + Structure* regExpStructure() const { return m_regExpStructure.get(); } + Structure* setStructure() const { return m_setStructure.get(); } + Structure* stringObjectStructure() const { return m_stringObjectStructure.get(); } + Structure* symbolObjectStructure() const { return m_symbolObjectStructure.get(); } + Structure* iteratorResultStructure() const { return m_iteratorResultStructure.get(); } + static ptrdiff_t iteratorResultStructureOffset() { return OBJECT_OFFSETOF(JSGlobalObject, m_iteratorResultStructure); } + Structure* regExpMatchesArrayStructure() const { return m_regExpMatchesArrayStructure.get(); } + Structure* promiseStructure() const { return m_promiseStructure.get(); } +#if ENABLE(WEBASSEMBLY) + Structure* wasmModuleStructure() const { return m_wasmModuleStructure.get(); } +#endif + + JS_EXPORT_PRIVATE void setRemoteDebuggingEnabled(bool); + JS_EXPORT_PRIVATE bool remoteDebuggingEnabled() const; + +#if ENABLE(WEB_REPLAY) + JS_EXPORT_PRIVATE void setInputCursor(PassRefPtr<InputCursor>); + InputCursor& inputCursor() const { return *m_inputCursor; } +#endif + +#if ENABLE(REMOTE_INSPECTOR) + Inspector::JSGlobalObjectInspectorController& inspectorController() const { return *m_inspectorController.get(); } + JSGlobalObjectDebuggable& inspectorDebuggable() { return *m_inspectorDebuggable.get(); } +#endif + + void setConsoleClient(ConsoleClient* consoleClient) { m_consoleClient = consoleClient; } + ConsoleClient* consoleClient() const { return m_consoleClient; } + + void setName(const String&); + const String& name() const { return m_name; } + + JSArrayBufferPrototype* arrayBufferPrototype() const { return m_arrayBufferPrototype.get(); } + +#define DEFINE_ACCESSORS_FOR_SIMPLE_TYPE(capitalName, lowerName, properName, instanceType, jsName) \ + Structure* properName ## Structure() { return m_ ## properName ## Structure.get(); } + + FOR_EACH_SIMPLE_BUILTIN_TYPE(DEFINE_ACCESSORS_FOR_SIMPLE_TYPE) + FOR_EACH_BUILTIN_DERIVED_ITERATOR_TYPE(DEFINE_ACCESSORS_FOR_SIMPLE_TYPE) + +#undef DEFINE_ACCESSORS_FOR_SIMPLE_TYPE + + Structure* typedArrayStructure(TypedArrayType type) const + { + return m_typedArrays[toIndex(type)].structure.get(); + } + bool isOriginalTypedArrayStructure(Structure* structure) + { + TypedArrayType type = structure->classInfo()->typedArrayStorageType; + if (type == NotTypedArray) + return false; + return typedArrayStructure(type) == structure; + } + + JSCell* actualPointerFor(Special::Pointer pointer) + { + ASSERT(pointer < Special::TableSize); + return m_specialPointers[pointer]; + } + JSCell* jsCellForLinkTimeConstant(LinkTimeConstant type) + { + unsigned index = static_cast<unsigned>(type); + ASSERT(index < LinkTimeConstantCount); + return m_linkTimeConstants[index]; + } + + WatchpointSet* masqueradesAsUndefinedWatchpoint() { return m_masqueradesAsUndefinedWatchpoint.get(); } + WatchpointSet* havingABadTimeWatchpoint() { return m_havingABadTimeWatchpoint.get(); } + WatchpointSet* varInjectionWatchpoint() { return m_varInjectionWatchpoint.get(); } + + bool isHavingABadTime() const + { + return m_havingABadTimeWatchpoint->hasBeenInvalidated(); + } + + void haveABadTime(VM&); + + bool objectPrototypeIsSane(); + bool arrayPrototypeChainIsSane(); + bool stringPrototypeChainIsSane(); + + void setProfileGroup(unsigned value) { createRareDataIfNeeded(); m_rareData->profileGroup = value; } + unsigned profileGroup() const + { + if (!m_rareData) + return 0; + return m_rareData->profileGroup; + } + + Debugger* debugger() const { return m_debugger; } + void setDebugger(Debugger* debugger) { m_debugger = debugger; } + + const GlobalObjectMethodTable* globalObjectMethodTable() const { return m_globalObjectMethodTable; } + + static bool allowsAccessFrom(const JSGlobalObject*, ExecState*) { return true; } + static bool supportsProfiling(const JSGlobalObject*) { return false; } + static bool supportsRichSourceInfo(const JSGlobalObject*) { return true; } + + JS_EXPORT_PRIVATE ExecState* globalExec(); + + static bool shouldInterruptScript(const JSGlobalObject*) { return true; } + static bool shouldInterruptScriptBeforeTimeout(const JSGlobalObject*) { return false; } + static RuntimeFlags javaScriptRuntimeFlags(const JSGlobalObject*) { return RuntimeFlags(); } + + void queueMicrotask(PassRefPtr<Microtask>); + + bool evalEnabled() const { return m_evalEnabled; } + const String& evalDisabledErrorMessage() const { return m_evalDisabledErrorMessage; } + void setEvalEnabled(bool enabled, const String& errorMessage = String()) + { + m_evalEnabled = enabled; + m_evalDisabledErrorMessage = errorMessage; + } + + void resetPrototype(VM&, JSValue prototype); + + VM& vm() const { return m_vm; } + JSObject* globalThis() const; + + static Structure* createStructure(VM& vm, JSValue prototype) + { + Structure* result = Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info()); + result->setTransitionWatchpointIsLikelyToBeFired(true); + return result; + } + + void registerWeakMap(OpaqueJSWeakObjectMap* map) + { + createRareDataIfNeeded(); + m_rareData->weakMaps.add(map); + } + + void unregisterWeakMap(OpaqueJSWeakObjectMap* map) + { + if (m_rareData) + m_rareData->weakMaps.remove(map); + } + + OpaqueJSClassDataMap& opaqueJSClassData() + { + createRareDataIfNeeded(); + return m_rareData->opaqueJSClassData; + } + + TemplateRegistry& templateRegistry() { return m_templateRegistry; } + + double weakRandomNumber() { return m_weakRandom.get(); } + unsigned weakRandomInteger() { return m_weakRandom.getUint32(); } + + UnlinkedProgramCodeBlock* createProgramCodeBlock(CallFrame*, ProgramExecutable*, JSObject** exception); + UnlinkedEvalCodeBlock* createEvalCodeBlock(CallFrame*, EvalExecutable*, ThisTDZMode, const VariableEnvironment*); + +protected: + struct GlobalPropertyInfo { + GlobalPropertyInfo(const Identifier& i, JSValue v, unsigned a) + : identifier(i) + , value(v) + , attributes(a) + { + } + + const Identifier identifier; + JSValue value; + unsigned attributes; + }; + JS_EXPORT_PRIVATE void addStaticGlobals(GlobalPropertyInfo*, int count); + + JS_EXPORT_PRIVATE static JSC::JSValue toThis(JSC::JSCell*, JSC::ExecState*, ECMAMode); + +private: + friend class LLIntOffsetsExtractor; + + JS_EXPORT_PRIVATE void setGlobalThis(VM&, JSObject* globalThis); + + JS_EXPORT_PRIVATE void init(VM&); + + void createThrowTypeError(VM&); + + JS_EXPORT_PRIVATE static void clearRareData(JSCell*); +}; + +JSGlobalObject* asGlobalObject(JSValue); + +inline JSGlobalObject* asGlobalObject(JSValue value) +{ + ASSERT(asObject(value)->isGlobalObject()); + return jsCast<JSGlobalObject*>(asObject(value)); +} + +inline bool JSGlobalObject::hasOwnPropertyForWrite(ExecState* exec, PropertyName propertyName) +{ + PropertySlot slot(this); + if (Base::getOwnPropertySlot(this, exec, propertyName, slot)) + return true; + bool slotIsWriteable; + return symbolTableGet(this, propertyName, slot, slotIsWriteable); +} + +inline bool JSGlobalObject::symbolTableHasProperty(PropertyName propertyName) +{ + SymbolTableEntry entry = symbolTable()->inlineGet(propertyName.uid()); + return !entry.isNull(); +} + +inline JSArray* constructEmptyArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, unsigned initialLength = 0) +{ + return ArrayAllocationProfile::updateLastAllocationFor(profile, JSArray::create(exec->vm(), initialLength >= MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH ? globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage) : globalObject->arrayStructureForProfileDuringAllocation(profile), initialLength)); +} + +inline JSArray* constructEmptyArray(ExecState* exec, ArrayAllocationProfile* profile, unsigned initialLength = 0) +{ + return constructEmptyArray(exec, profile, exec->lexicalGlobalObject(), initialLength); +} + +inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, const ArgList& values) +{ + return ArrayAllocationProfile::updateLastAllocationFor(profile, constructArray(exec, globalObject->arrayStructureForProfileDuringAllocation(profile), values)); +} + +inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, const ArgList& values) +{ + return constructArray(exec, profile, exec->lexicalGlobalObject(), values); +} + +inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, const JSValue* values, unsigned length) +{ + return ArrayAllocationProfile::updateLastAllocationFor(profile, constructArray(exec, globalObject->arrayStructureForProfileDuringAllocation(profile), values, length)); +} + +inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, const JSValue* values, unsigned length) +{ + return constructArray(exec, profile, exec->lexicalGlobalObject(), values, length); +} + +inline JSArray* constructArrayNegativeIndexed(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, const JSValue* values, unsigned length) +{ + return ArrayAllocationProfile::updateLastAllocationFor(profile, constructArrayNegativeIndexed(exec, globalObject->arrayStructureForProfileDuringAllocation(profile), values, length)); +} + +inline JSArray* constructArrayNegativeIndexed(ExecState* exec, ArrayAllocationProfile* profile, const JSValue* values, unsigned length) +{ + return constructArrayNegativeIndexed(exec, profile, exec->lexicalGlobalObject(), values, length); +} + +inline JSObject* ExecState::globalThisValue() const +{ + return lexicalGlobalObject()->globalThis(); +} + +inline JSObject* JSScope::globalThis() +{ + return globalObject()->globalThis(); +} + +inline JSObject* JSGlobalObject::globalThis() const +{ + return m_globalThis.get(); +} + +} // namespace JSC + +#endif // JSGlobalObject_h diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectDebuggable.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectDebuggable.cpp new file mode 100644 index 000000000..eecc5ba03 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectDebuggable.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSGlobalObjectDebuggable.h" + +#if ENABLE(REMOTE_INSPECTOR) + +#include "InspectorAgentBase.h" +#include "InspectorFrontendChannel.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "RemoteInspector.h" + +using namespace Inspector; + +namespace JSC { + +JSGlobalObjectDebuggable::JSGlobalObjectDebuggable(JSGlobalObject& globalObject) + : m_globalObject(globalObject) +{ +} + +String JSGlobalObjectDebuggable::name() const +{ + String name = m_globalObject.name(); + return name.isEmpty() ? ASCIILiteral("JSContext") : name; +} + +void JSGlobalObjectDebuggable::connect(FrontendChannel* frontendChannel, bool automaticInspection) +{ + JSLockHolder locker(&m_globalObject.vm()); + + m_globalObject.inspectorController().connectFrontend(frontendChannel, automaticInspection); +} + +void JSGlobalObjectDebuggable::disconnect() +{ + JSLockHolder locker(&m_globalObject.vm()); + + m_globalObject.inspectorController().disconnectFrontend(DisconnectReason::InspectorDestroyed); +} + +void JSGlobalObjectDebuggable::pause() +{ + JSLockHolder locker(&m_globalObject.vm()); + + m_globalObject.inspectorController().pause(); +} + +void JSGlobalObjectDebuggable::dispatchMessageFromRemoteFrontend(const String& message) +{ + JSLockHolder locker(&m_globalObject.vm()); + + m_globalObject.inspectorController().dispatchMessageFromFrontend(message); +} + +void JSGlobalObjectDebuggable::pauseWaitingForAutomaticInspection() +{ + JSC::JSLock::DropAllLocks dropAllLocks(&m_globalObject.vm()); + RemoteInspectorDebuggable::pauseWaitingForAutomaticInspection(); +} + +} // namespace JSC + +#endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectDebuggable.h b/Source/JavaScriptCore/runtime/JSGlobalObjectDebuggable.h new file mode 100644 index 000000000..60ce58a44 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectDebuggable.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSGlobalObjectDebuggable_h +#define JSGlobalObjectDebuggable_h + +#if ENABLE(REMOTE_INSPECTOR) + +#include "JSGlobalObjectInspectorController.h" +#include "RemoteInspectorDebuggable.h" +#include <wtf/Noncopyable.h> + +namespace Inspector { +class FrontendChannel; +enum class DisconnectReason; +} + +namespace JSC { + +class JSGlobalObject; + +class JSGlobalObjectDebuggable final : public Inspector::RemoteInspectorDebuggable { + WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(JSGlobalObjectDebuggable); +public: + JSGlobalObjectDebuggable(JSGlobalObject&); + ~JSGlobalObjectDebuggable() { } + + virtual Inspector::RemoteInspectorDebuggable::DebuggableType type() const override { return Inspector::RemoteInspectorDebuggable::JavaScript; } + + virtual String name() const override; + virtual bool hasLocalDebugger() const override { return false; } + + virtual void connect(Inspector::FrontendChannel*, bool automaticInspection) override; + virtual void disconnect() override; + virtual void dispatchMessageFromRemoteFrontend(const String& message) override; + virtual void pause() override; + + virtual bool automaticInspectionAllowed() const override { return true; } + virtual void pauseWaitingForAutomaticInspection() override; + +private: + JSGlobalObject& m_globalObject; +}; + +} // namespace JSC + +#endif // ENABLE(REMOTE_INSPECTOR) + +#endif // !defined(JSGlobalObjectDebuggable_h) diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp new file mode 100644 index 000000000..b7ee1251b --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -0,0 +1,894 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "JSGlobalObjectFunctions.h" + +#include "CallFrame.h" +#include "Interpreter.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "Lexer.h" +#include "LiteralParser.h" +#include "Nodes.h" +#include "JSCInlines.h" +#include "Parser.h" +#include "StackVisitor.h" +#include <wtf/dtoa.h> +#include <stdio.h> +#include <stdlib.h> +#include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> +#include <wtf/HexNumber.h> +#include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/unicode/UTF8.h> + +using namespace WTF; +using namespace Unicode; + +namespace JSC { + +template<unsigned charactersCount> +static Bitmap<256> makeCharacterBitmap(const char (&characters)[charactersCount]) +{ + Bitmap<256> bitmap; + for (unsigned i = 0; i < charactersCount; ++i) + bitmap.set(characters[i]); + return bitmap; +} + +static JSValue encode(ExecState* exec, const Bitmap<256>& doNotEscape) +{ + CString cstr = exec->argument(0).toString(exec)->view(exec).get().utf8(StrictConversion); + if (!cstr.data()) + return exec->vm().throwException(exec, createURIError(exec, ASCIILiteral("String contained an illegal UTF-16 sequence."))); + + JSStringBuilder builder; + const char* p = cstr.data(); + for (size_t k = 0; k < cstr.length(); k++, p++) { + char c = *p; + if (c && doNotEscape.get(static_cast<LChar>(c))) + builder.append(static_cast<LChar>(c)); + else { + builder.append(static_cast<LChar>('%')); + appendByteAsHex(c, builder); + } + } + return builder.build(exec); +} + +template <typename CharType> +ALWAYS_INLINE +static JSValue decode(ExecState* exec, const CharType* characters, int length, const Bitmap<256>& doNotUnescape, bool strict) +{ + JSStringBuilder builder; + int k = 0; + UChar u = 0; + while (k < length) { + const CharType* p = characters + k; + CharType c = *p; + if (c == '%') { + int charLen = 0; + if (k <= length - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) { + const char b0 = Lexer<CharType>::convertHex(p[1], p[2]); + const int sequenceLen = UTF8SequenceLength(b0); + if (sequenceLen && k <= length - sequenceLen * 3) { + charLen = sequenceLen * 3; + char sequence[5]; + sequence[0] = b0; + for (int i = 1; i < sequenceLen; ++i) { + const CharType* q = p + i * 3; + if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2])) + sequence[i] = Lexer<CharType>::convertHex(q[1], q[2]); + else { + charLen = 0; + break; + } + } + if (charLen != 0) { + sequence[sequenceLen] = 0; + const int character = decodeUTF8Sequence(sequence); + if (character < 0 || character >= 0x110000) + charLen = 0; + else if (character >= 0x10000) { + // Convert to surrogate pair. + builder.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10))); + u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF)); + } else + u = static_cast<UChar>(character); + } + } + } + if (charLen == 0) { + if (strict) + return exec->vm().throwException(exec, createURIError(exec, ASCIILiteral("URI error"))); + // The only case where we don't use "strict" mode is the "unescape" function. + // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE. + if (k <= length - 6 && p[1] == 'u' + && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3]) + && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) { + charLen = 6; + u = Lexer<UChar>::convertUnicode(p[2], p[3], p[4], p[5]); + } + } + if (charLen && (u == 0 || u >= 128 || !doNotUnescape.get(static_cast<LChar>(u)))) { + builder.append(u); + k += charLen; + continue; + } + } + k++; + builder.append(c); + } + return builder.build(exec); +} + +static JSValue decode(ExecState* exec, const Bitmap<256>& doNotUnescape, bool strict) +{ + StringView str = exec->argument(0).toString(exec)->view(exec); + + if (str.is8Bit()) + return decode(exec, str.characters8(), str.length(), doNotUnescape, strict); + return decode(exec, str.characters16(), str.length(), doNotUnescape, strict); +} + +bool isStrWhiteSpace(UChar c) +{ + switch (c) { + // ECMA-262-5th 7.2 & 7.3 + case 0x0009: + case 0x000A: + case 0x000B: + case 0x000C: + case 0x000D: + case 0x0020: + case 0x00A0: + case 0x180E: // This character used to be in Zs category before Unicode 6.3, and EcmaScript says that we should keep treating it as such. + case 0x2028: + case 0x2029: + case 0xFEFF: + return true; + default: + return c > 0xFF && u_charType(c) == U_SPACE_SEPARATOR; + } +} + +static int parseDigit(unsigned short c, int radix) +{ + int digit = -1; + + if (c >= '0' && c <= '9') + digit = c - '0'; + else if (c >= 'A' && c <= 'Z') + digit = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + digit = c - 'a' + 10; + + if (digit >= radix) + return -1; + return digit; +} + +double parseIntOverflow(const LChar* s, unsigned length, int radix) +{ + double number = 0.0; + double radixMultiplier = 1.0; + + for (const LChar* p = s + length - 1; p >= s; p--) { + if (radixMultiplier == std::numeric_limits<double>::infinity()) { + if (*p != '0') { + number = std::numeric_limits<double>::infinity(); + break; + } + } else { + int digit = parseDigit(*p, radix); + number += digit * radixMultiplier; + } + + radixMultiplier *= radix; + } + + return number; +} + +static double parseIntOverflow(const UChar* s, unsigned length, int radix) +{ + double number = 0.0; + double radixMultiplier = 1.0; + + for (const UChar* p = s + length - 1; p >= s; p--) { + if (radixMultiplier == std::numeric_limits<double>::infinity()) { + if (*p != '0') { + number = std::numeric_limits<double>::infinity(); + break; + } + } else { + int digit = parseDigit(*p, radix); + number += digit * radixMultiplier; + } + + radixMultiplier *= radix; + } + + return number; +} + +static double parseIntOverflow(StringView string, int radix) +{ + if (string.is8Bit()) + return parseIntOverflow(string.characters8(), string.length(), radix); + return parseIntOverflow(string.characters16(), string.length(), radix); +} + +// ES5.1 15.1.2.2 +template <typename CharType> +ALWAYS_INLINE +static double parseInt(StringView s, const CharType* data, int radix) +{ + // 1. Let inputString be ToString(string). + // 2. Let S be a newly created substring of inputString consisting of the first character that is not a + // StrWhiteSpaceChar and all characters following that character. (In other words, remove leading white + // space.) If inputString does not contain any such characters, let S be the empty string. + int length = s.length(); + int p = 0; + while (p < length && isStrWhiteSpace(data[p])) + ++p; + + // 3. Let sign be 1. + // 4. If S is not empty and the first character of S is a minus sign -, let sign be -1. + // 5. If S is not empty and the first character of S is a plus sign + or a minus sign -, then remove the first character from S. + double sign = 1; + if (p < length) { + if (data[p] == '+') + ++p; + else if (data[p] == '-') { + sign = -1; + ++p; + } + } + + // 6. Let R = ToInt32(radix). + // 7. Let stripPrefix be true. + // 8. If R != 0,then + // b. If R != 16, let stripPrefix be false. + // 9. Else, R == 0 + // a. LetR = 10. + // 10. If stripPrefix is true, then + // a. If the length of S is at least 2 and the first two characters of S are either ―0x or ―0X, + // then remove the first two characters from S and let R = 16. + // 11. If S contains any character that is not a radix-R digit, then let Z be the substring of S + // consisting of all characters before the first such character; otherwise, let Z be S. + if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) { + radix = 16; + p += 2; + } else if (radix == 0) + radix = 10; + + // 8.a If R < 2 or R > 36, then return NaN. + if (radix < 2 || radix > 36) + return PNaN; + + // 13. Let mathInt be the mathematical integer value that is represented by Z in radix-R notation, using the letters + // A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant + // digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation; + // and if R is not 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-dependent approximation to the + // mathematical integer value that is represented by Z in radix-R notation.) + // 14. Let number be the Number value for mathInt. + int firstDigitPosition = p; + bool sawDigit = false; + double number = 0; + while (p < length) { + int digit = parseDigit(data[p], radix); + if (digit == -1) + break; + sawDigit = true; + number *= radix; + number += digit; + ++p; + } + + // 12. If Z is empty, return NaN. + if (!sawDigit) + return PNaN; + + // Alternate code path for certain large numbers. + if (number >= mantissaOverflowLowerBound) { + if (radix == 10) { + size_t parsedLength; + number = parseDouble(s.substring(firstDigitPosition, p - firstDigitPosition), parsedLength); + } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) + number = parseIntOverflow(s.substring(firstDigitPosition, p - firstDigitPosition), radix); + } + + // 15. Return sign x number. + return sign * number; +} + +static double parseInt(StringView s, int radix) +{ + if (s.is8Bit()) + return parseInt(s, s.characters8(), radix); + return parseInt(s, s.characters16(), radix); +} + +static const int SizeOfInfinity = 8; + +template <typename CharType> +static bool isInfinity(const CharType* data, const CharType* end) +{ + return (end - data) >= SizeOfInfinity + && data[0] == 'I' + && data[1] == 'n' + && data[2] == 'f' + && data[3] == 'i' + && data[4] == 'n' + && data[5] == 'i' + && data[6] == 't' + && data[7] == 'y'; +} + +// See ecma-262 6th 11.8.3 +template <typename CharType> +static double jsBinaryIntegerLiteral(const CharType*& data, const CharType* end) +{ + // Binary number. + data += 2; + const CharType* firstDigitPosition = data; + double number = 0; + while (true) { + number = number * 2 + (*data - '0'); + ++data; + if (data == end) + break; + if (!isASCIIBinaryDigit(*data)) + break; + } + if (number >= mantissaOverflowLowerBound) + number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 2); + + return number; +} + +// See ecma-262 6th 11.8.3 +template <typename CharType> +static double jsOctalIntegerLiteral(const CharType*& data, const CharType* end) +{ + // Octal number. + data += 2; + const CharType* firstDigitPosition = data; + double number = 0; + while (true) { + number = number * 8 + (*data - '0'); + ++data; + if (data == end) + break; + if (!isASCIIOctalDigit(*data)) + break; + } + if (number >= mantissaOverflowLowerBound) + number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 8); + + return number; +} + +// See ecma-262 6th 11.8.3 +template <typename CharType> +static double jsHexIntegerLiteral(const CharType*& data, const CharType* end) +{ + // Hex number. + data += 2; + const CharType* firstDigitPosition = data; + double number = 0; + while (true) { + number = number * 16 + toASCIIHexValue(*data); + ++data; + if (data == end) + break; + if (!isASCIIHexDigit(*data)) + break; + } + if (number >= mantissaOverflowLowerBound) + number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16); + + return number; +} + +// See ecma-262 6th 11.8.3 +template <typename CharType> +static double jsStrDecimalLiteral(const CharType*& data, const CharType* end) +{ + RELEASE_ASSERT(data < end); + + size_t parsedLength; + double number = parseDouble(data, end - data, parsedLength); + if (parsedLength) { + data += parsedLength; + return number; + } + + // Check for [+-]?Infinity + switch (*data) { + case 'I': + if (isInfinity(data, end)) { + data += SizeOfInfinity; + return std::numeric_limits<double>::infinity(); + } + break; + + case '+': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return std::numeric_limits<double>::infinity(); + } + break; + + case '-': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return -std::numeric_limits<double>::infinity(); + } + break; + } + + // Not a number. + return PNaN; +} + +template <typename CharType> +static double toDouble(const CharType* characters, unsigned size) +{ + const CharType* endCharacters = characters + size; + + // Skip leading white space. + for (; characters < endCharacters; ++characters) { + if (!isStrWhiteSpace(*characters)) + break; + } + + // Empty string. + if (characters == endCharacters) + return 0.0; + + double number; + if (characters[0] == '0' && characters + 2 < endCharacters) { + if ((characters[1] | 0x20) == 'x' && isASCIIHexDigit(characters[2])) + number = jsHexIntegerLiteral(characters, endCharacters); + else if ((characters[1] | 0x20) == 'o' && isASCIIOctalDigit(characters[2])) + number = jsOctalIntegerLiteral(characters, endCharacters); + else if ((characters[1] | 0x20) == 'b' && isASCIIBinaryDigit(characters[2])) + number = jsBinaryIntegerLiteral(characters, endCharacters); + else + number = jsStrDecimalLiteral(characters, endCharacters); + } else + number = jsStrDecimalLiteral(characters, endCharacters); + + // Allow trailing white space. + for (; characters < endCharacters; ++characters) { + if (!isStrWhiteSpace(*characters)) + break; + } + if (characters != endCharacters) + return PNaN; + + return number; +} + +// See ecma-262 6th 11.8.3 +double jsToNumber(StringView s) +{ + unsigned size = s.length(); + + if (size == 1) { + UChar c = s[0]; + if (isASCIIDigit(c)) + return c - '0'; + if (isStrWhiteSpace(c)) + return 0; + return PNaN; + } + + if (s.is8Bit()) + return toDouble(s.characters8(), size); + return toDouble(s.characters16(), size); +} + +static double parseFloat(StringView s) +{ + unsigned size = s.length(); + + if (size == 1) { + UChar c = s[0]; + if (isASCIIDigit(c)) + return c - '0'; + return PNaN; + } + + if (s.is8Bit()) { + const LChar* data = s.characters8(); + const LChar* end = data + size; + + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return PNaN; + + return jsStrDecimalLiteral(data, end); + } + + const UChar* data = s.characters16(); + const UChar* end = data + size; + + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return PNaN; + + return jsStrDecimalLiteral(data, end); +} + +EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec) +{ + JSValue x = exec->argument(0); + if (!x.isString()) + return JSValue::encode(x); + + String s = x.toString(exec)->value(exec); + + if (s.is8Bit()) { + LiteralParser<LChar> preparser(exec, s.characters8(), s.length(), NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return JSValue::encode(parsedObject); + } else { + LiteralParser<UChar> preparser(exec, s.characters16(), s.length(), NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return JSValue::encode(parsedObject); + } + + JSGlobalObject* calleeGlobalObject = exec->callee()->globalObject(); + VariableEnvironment emptyTDZVariables; // Indirect eval does not have access to the lexical scope. + EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false, ThisTDZMode::CheckIfNeeded, &emptyTDZVariables); + if (!eval) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(exec->interpreter()->execute(eval, exec, calleeGlobalObject->globalThis(), calleeGlobalObject)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) +{ + JSValue value = exec->argument(0); + JSValue radixValue = exec->argument(1); + + // Optimized handling for numbers: + // If the argument is 0 or a number in range 10^-6 <= n < INT_MAX+1, then parseInt + // results in a truncation to integer. In the case of -0, this is converted to 0. + // + // This is also a truncation for values in the range INT_MAX+1 <= n < 10^21, + // however these values cannot be trivially truncated to int since 10^21 exceeds + // even the int64_t range. Negative numbers are a little trickier, the case for + // values in the range -10^21 < n <= -1 are similar to those for integer, but + // values in the range -1 < n <= -10^-6 need to truncate to -0, not 0. + static const double tenToTheMinus6 = 0.000001; + static const double intMaxPlusOne = 2147483648.0; + if (value.isNumber()) { + double n = value.asNumber(); + if (((n < intMaxPlusOne && n >= tenToTheMinus6) || !n) && radixValue.isUndefinedOrNull()) + return JSValue::encode(jsNumber(static_cast<int32_t>(n))); + } + + // If ToString throws, we shouldn't call ToInt32. + StringView s = value.toString(exec)->view(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(jsNumber(parseInt(s, radixValue.toInt32(exec)))); +} + +EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec) +{ + return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)->view(exec)))); +} + +EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec) +{ + return JSValue::encode(jsBoolean(std::isnan(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec) +{ + double n = exec->argument(0).toNumber(exec); + return JSValue::encode(jsBoolean(std::isfinite(n))); +} + +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec) +{ + static Bitmap<256> doNotUnescapeWhenDecodingURI = makeCharacterBitmap( + "#$&+,/:;=?@" + ); + + return JSValue::encode(decode(exec, doNotUnescapeWhenDecodingURI, true)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec) +{ + static Bitmap<256> emptyBitmap; + return JSValue::encode(decode(exec, emptyBitmap, true)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec) +{ + static Bitmap<256> doNotEscapeWhenEncodingURI = makeCharacterBitmap( + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "!#$&'()*+,-./:;=?@_~" + ); + + return JSValue::encode(encode(exec, doNotEscapeWhenEncodingURI)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec) +{ + static Bitmap<256> doNotEscapeWhenEncodingURIComponent = makeCharacterBitmap( + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "!'()*-._~" + ); + + return JSValue::encode(encode(exec, doNotEscapeWhenEncodingURIComponent)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) +{ + static Bitmap<256> doNotEscape = makeCharacterBitmap( + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "*+-./@_" + ); + + JSStringBuilder builder; + StringView str = exec->argument(0).toString(exec)->view(exec); + if (str.is8Bit()) { + const LChar* c = str.characters8(); + for (unsigned k = 0; k < str.length(); k++, c++) { + int u = c[0]; + if (u && doNotEscape.get(static_cast<LChar>(u))) + builder.append(*c); + else { + builder.append(static_cast<LChar>('%')); + appendByteAsHex(static_cast<LChar>(u), builder); + } + } + + return JSValue::encode(builder.build(exec)); + } + + const UChar* c = str.characters16(); + for (unsigned k = 0; k < str.length(); k++, c++) { + int u = c[0]; + if (u > 255) { + builder.append(static_cast<LChar>('%')); + builder.append(static_cast<LChar>('u')); + appendByteAsHex(u >> 8, builder); + appendByteAsHex(u & 0xFF, builder); + } else if (u != 0 && doNotEscape.get(static_cast<LChar>(u))) + builder.append(*c); + else { + builder.append(static_cast<LChar>('%')); + appendByteAsHex(u, builder); + } + } + + return JSValue::encode(builder.build(exec)); +} + +EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec) +{ + StringBuilder builder; + StringView str = exec->argument(0).toString(exec)->view(exec); + int k = 0; + int len = str.length(); + + if (str.is8Bit()) { + const LChar* characters = str.characters8(); + LChar convertedLChar; + while (k < len) { + const LChar* c = characters + k; + if (c[0] == '%' && k <= len - 6 && c[1] == 'u') { + if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { + builder.append(Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5])); + k += 6; + continue; + } + } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) { + convertedLChar = LChar(Lexer<LChar>::convertHex(c[1], c[2])); + c = &convertedLChar; + k += 2; + } + builder.append(*c); + k++; + } + } else { + const UChar* characters = str.characters16(); + + while (k < len) { + const UChar* c = characters + k; + UChar convertedUChar; + if (c[0] == '%' && k <= len - 6 && c[1] == 'u') { + if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { + convertedUChar = Lexer<UChar>::convertUnicode(c[2], c[3], c[4], c[5]); + c = &convertedUChar; + k += 5; + } + } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) { + convertedUChar = UChar(Lexer<UChar>::convertHex(c[1], c[2])); + c = &convertedUChar; + k += 2; + } + k++; + builder.append(*c); + } + } + + return JSValue::encode(jsString(exec, builder.toString())); +} + +EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState* exec) +{ + return throwVMTypeError(exec); +} + +class GlobalFuncProtoGetterFunctor { +public: + GlobalFuncProtoGetterFunctor(JSObject* thisObject) + : m_hasSkippedFirstFrame(false) + , m_thisObject(thisObject) + , m_result(JSValue::encode(jsUndefined())) + { + } + + EncodedJSValue result() { return m_result; } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + if (!m_hasSkippedFirstFrame) { + m_hasSkippedFirstFrame = true; + return StackVisitor::Continue; + } + + if (m_thisObject->allowsAccessFrom(visitor->callFrame())) + m_result = JSValue::encode(m_thisObject->prototype()); + + return StackVisitor::Done; + } + +private: + bool m_hasSkippedFirstFrame; + JSObject* m_thisObject; + EncodedJSValue m_result; +}; + +EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState* exec) +{ + if (exec->thisValue().isUndefinedOrNull()) + return throwVMError(exec, createTypeError(exec, "Can't convert undefined or null to object")); + + JSObject* thisObject = jsDynamicCast<JSObject*>(exec->thisValue().toThis(exec, NotStrictMode)); + + if (!thisObject) + return JSValue::encode(exec->thisValue().synthesizePrototype(exec)); + + GlobalFuncProtoGetterFunctor functor(thisObject); + exec->iterate(functor); + return functor.result(); +} + +class GlobalFuncProtoSetterFunctor { +public: + GlobalFuncProtoSetterFunctor(JSObject* thisObject) + : m_hasSkippedFirstFrame(false) + , m_allowsAccess(false) + , m_thisObject(thisObject) + { + } + + bool allowsAccess() const { return m_allowsAccess; } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + if (!m_hasSkippedFirstFrame) { + m_hasSkippedFirstFrame = true; + return StackVisitor::Continue; + } + + m_allowsAccess = m_thisObject->allowsAccessFrom(visitor->callFrame()); + return StackVisitor::Done; + } + +private: + bool m_hasSkippedFirstFrame; + bool m_allowsAccess; + JSObject* m_thisObject; +}; + +bool checkProtoSetterAccessAllowed(ExecState* exec, JSObject* object) +{ + GlobalFuncProtoSetterFunctor functor(object); + exec->iterate(functor); + return functor.allowsAccess(); +} + +EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState* exec) +{ + if (exec->thisValue().isUndefinedOrNull()) + return throwVMError(exec, createTypeError(exec, "Can't convert undefined or null to object")); + + JSValue value = exec->argument(0); + + JSObject* thisObject = jsDynamicCast<JSObject*>(exec->thisValue().toThis(exec, NotStrictMode)); + + // Setting __proto__ of a primitive should have no effect. + if (!thisObject) + return JSValue::encode(jsUndefined()); + + if (!checkProtoSetterAccessAllowed(exec, thisObject)) + return JSValue::encode(jsUndefined()); + + // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. + if (!value.isObject() && !value.isNull()) + return JSValue::encode(jsUndefined()); + + if (thisObject->prototype() == value) + return JSValue::encode(jsUndefined()); + + if (!thisObject->isExtensible()) + return throwVMError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); + + if (!thisObject->setPrototypeWithCycleCheck(exec, value)) + exec->vm().throwException(exec, createError(exec, ASCIILiteral("cyclic __proto__ value"))); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState* exec) +{ + dataLog(exec->argument(0).toWTFString(exec), "\n"); + return JSValue::encode(jsUndefined()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h new file mode 100644 index 000000000..b929f5143 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSGlobalObjectFunctions_h +#define JSGlobalObjectFunctions_h + +#include "JSCJSValue.h" +#include <unicode/uchar.h> +#include <wtf/text/LChar.h> + +namespace JSC { + +class ArgList; +class ExecState; +class JSObject; + +// FIXME: These functions should really be in JSGlobalObject.cpp, but putting them there +// is a 0.5% reduction. + +EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState*); +EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState*); + +bool checkProtoSetterAccessAllowed(ExecState*, JSObject*); + +static const double mantissaOverflowLowerBound = 9007199254740992.0; +double parseIntOverflow(const LChar*, unsigned length, int radix); +bool isStrWhiteSpace(UChar); +double jsToNumber(StringView); + +} // namespace JSC + +#endif // JSGlobalObjectFunctions_h diff --git a/Source/JavaScriptCore/runtime/JSInt16Array.h b/Source/JavaScriptCore/runtime/JSInt16Array.h new file mode 100644 index 000000000..803ee35fc --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSInt16Array.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSInt16Array_h +#define JSInt16Array_h + +#include "JSTypedArrays.h" + +#endif // JSInt16Array_h + diff --git a/Source/JavaScriptCore/runtime/JSInt32Array.h b/Source/JavaScriptCore/runtime/JSInt32Array.h new file mode 100644 index 000000000..c8c4131e5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSInt32Array.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSInt32Array_h +#define JSInt32Array_h + +#include "JSTypedArrays.h" + +#endif // JSInt32Array_h + diff --git a/Source/JavaScriptCore/runtime/JSInt8Array.h b/Source/JavaScriptCore/runtime/JSInt8Array.h new file mode 100644 index 000000000..9875fe6ad --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSInt8Array.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSInt8Array_h +#define JSInt8Array_h + +#include "JSTypedArrays.h" + +#endif // JSInt8Array_h + diff --git a/Source/JavaScriptCore/runtime/JSJob.cpp b/Source/JavaScriptCore/runtime/JSJob.cpp new file mode 100644 index 000000000..92d2122ba --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSJob.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "JSJob.h" + +#include "Error.h" +#include "Exception.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "Microtask.h" +#include "SlotVisitorInlines.h" +#include "StrongInlines.h" + +namespace JSC { + +class JSJobMicrotask final : public Microtask { +public: + JSJobMicrotask(VM& vm, JSValue job, JSArray* arguments) + { + m_job.set(vm, job); + m_arguments.set(vm, arguments); + } + + virtual ~JSJobMicrotask() + { + } + +private: + virtual void run(ExecState*) override; + + Strong<Unknown> m_job; + Strong<JSArray> m_arguments; +}; + +Ref<Microtask> createJSJob(VM& vm, JSValue job, JSArray* arguments) +{ + return adoptRef(*new JSJobMicrotask(vm, job, arguments)); +} + +void JSJobMicrotask::run(ExecState* exec) +{ + CallData handlerCallData; + CallType handlerCallType = getCallData(m_job.get(), handlerCallData); + ASSERT(handlerCallType != CallTypeNone); + + MarkedArgumentBuffer handlerArguments; + for (unsigned index = 0, length = m_arguments->length(); index < length; ++index) + handlerArguments.append(m_arguments->JSArray::get(exec, index)); + call(exec, m_job.get(), handlerCallType, handlerCallData, jsUndefined(), handlerArguments); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSJob.h b/Source/JavaScriptCore/runtime/JSJob.h new file mode 100644 index 000000000..0147c836e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSJob.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef JSJob_h +#define JSJob_h + +#include "JSCell.h" +#include "Structure.h" + +namespace JSC { + +class Microtask; +class JSArray; + +Ref<Microtask> createJSJob(VM&, JSValue job, JSArray* arguments); + +} // namespace JSC + +#endif // JSJob_h diff --git a/Source/JavaScriptCore/runtime/JSLexicalEnvironment.cpp b/Source/JavaScriptCore/runtime/JSLexicalEnvironment.cpp new file mode 100644 index 000000000..47a81b5f3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLexicalEnvironment.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2008, 2009, 2014, 2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "JSLexicalEnvironment.h" + +#include "Interpreter.h" +#include "JSFunction.h" +#include "JSCInlines.h" + +using namespace std; + +namespace JSC { + +const ClassInfo JSLexicalEnvironment::s_info = { "JSLexicalEnvironment", &Base::s_info, 0, CREATE_METHOD_TABLE(JSLexicalEnvironment) }; + +inline bool JSLexicalEnvironment::symbolTableGet(PropertyName propertyName, PropertySlot& slot) +{ + SymbolTableEntry entry = symbolTable()->inlineGet(propertyName.uid()); + if (entry.isNull()) + return false; + + ScopeOffset offset = entry.scopeOffset(); + + // Defend against the inspector asking for a var after it has been optimized out. + if (!isValid(offset)) + return false; + + JSValue result = variableAt(offset).get(); + slot.setValue(this, DontEnum, result); + return true; +} + +inline bool JSLexicalEnvironment::symbolTablePut(ExecState* exec, PropertyName propertyName, JSValue value, bool shouldThrow) +{ + VM& vm = exec->vm(); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + WriteBarrierBase<Unknown>* reg; + WatchpointSet* set; + { + GCSafeConcurrentJITLocker locker(symbolTable()->m_lock, exec->vm().heap); + SymbolTable::Map::iterator iter = symbolTable()->find(locker, propertyName.uid()); + if (iter == symbolTable()->end(locker)) + return false; + ASSERT(!iter->value.isNull()); + if (iter->value.isReadOnly()) { + if (shouldThrow || isLexicalScope()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return true; + } + ScopeOffset offset = iter->value.scopeOffset(); + // Defend against the inspector asking for a var after it has been optimized out. + if (!isValid(offset)) + return false; + set = iter->value.watchpointSet(); + reg = &variableAt(offset); + } + reg->set(vm, this, value); + if (set) + set->invalidate(VariableWriteFireDetail(this, propertyName)); // Don't mess around - if we had found this statically, we would have invalidated it. + return true; +} + +void JSLexicalEnvironment::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSLexicalEnvironment* thisObject = jsCast<JSLexicalEnvironment*>(object); + + { + ConcurrentJITLocker locker(thisObject->symbolTable()->m_lock); + SymbolTable::Map::iterator end = thisObject->symbolTable()->end(locker); + for (SymbolTable::Map::iterator it = thisObject->symbolTable()->begin(locker); it != end; ++it) { + if (it->value.getAttributes() & DontEnum && !mode.includeDontEnumProperties()) + continue; + if (!thisObject->isValid(it->value.scopeOffset())) + continue; + if (it->key->isSymbol() && !propertyNames.includeSymbolProperties()) + continue; + propertyNames.add(Identifier::fromUid(exec, it->key.get())); + } + } + // Skip the JSEnvironmentRecord implementation of getOwnNonIndexPropertyNames + JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); +} + +inline bool JSLexicalEnvironment::symbolTablePutWithAttributes(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + WriteBarrierBase<Unknown>* reg; + { + ConcurrentJITLocker locker(symbolTable()->m_lock); + SymbolTable::Map::iterator iter = symbolTable()->find(locker, propertyName.uid()); + if (iter == symbolTable()->end(locker)) + return false; + SymbolTableEntry& entry = iter->value; + ASSERT(!entry.isNull()); + + ScopeOffset offset = entry.scopeOffset(); + if (!isValid(offset)) + return false; + + entry.setAttributes(attributes); + reg = &variableAt(offset); + } + reg->set(vm, this, value); + return true; +} + +bool JSLexicalEnvironment::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSLexicalEnvironment* thisObject = jsCast<JSLexicalEnvironment*>(object); + + if (thisObject->symbolTableGet(propertyName, slot)) + return true; + + unsigned attributes; + if (JSValue value = thisObject->getDirect(exec->vm(), propertyName, attributes)) { + slot.setValue(thisObject, attributes, value); + return true; + } + + // We don't call through to JSObject because there's no way to give a + // lexical environment object getter properties or a prototype. + ASSERT(!thisObject->hasGetterSetterProperties()); + ASSERT(thisObject->prototype().isNull()); + return false; +} + +void JSLexicalEnvironment::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + JSLexicalEnvironment* thisObject = jsCast<JSLexicalEnvironment*>(cell); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); + + if (thisObject->symbolTablePut(exec, propertyName, value, slot.isStrictMode())) + return; + + // We don't call through to JSObject because __proto__ and getter/setter + // properties are non-standard extensions that other implementations do not + // expose in the lexicalEnvironment object. + ASSERT(!thisObject->hasGetterSetterProperties()); + thisObject->putOwnDataProperty(exec->vm(), propertyName, value, slot); +} + +bool JSLexicalEnvironment::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + if (propertyName == exec->propertyNames().arguments) + return false; + + return Base::deleteProperty(cell, exec, propertyName); +} + +JSValue JSLexicalEnvironment::toThis(JSCell*, ExecState* exec, ECMAMode ecmaMode) +{ + if (ecmaMode == StrictMode) + return jsUndefined(); + return exec->globalThisValue(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSLexicalEnvironment.h b/Source/JavaScriptCore/runtime/JSLexicalEnvironment.h new file mode 100644 index 000000000..9516318d7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLexicalEnvironment.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2008, 2009, 2013-2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef JSLexicalEnvironment_h +#define JSLexicalEnvironment_h + +#include "CodeBlock.h" +#include "CopiedSpaceInlines.h" +#include "JSEnvironmentRecord.h" +#include "SymbolTable.h" + +namespace JSC { + +class Register; + +class JSLexicalEnvironment : public JSEnvironmentRecord { +private: + JSLexicalEnvironment(VM&, Structure*, JSScope*, SymbolTable*); + +public: + typedef JSEnvironmentRecord Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames; + + static JSLexicalEnvironment* create( + VM& vm, Structure* structure, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue) + { + JSLexicalEnvironment* result = + new ( + NotNull, + allocateCell<JSLexicalEnvironment>(vm.heap, allocationSize(symbolTable))) + JSLexicalEnvironment(vm, structure, currentScope, symbolTable); + result->finishCreation(vm, initialValue); + return result; + } + + static JSLexicalEnvironment* create(VM& vm, JSGlobalObject* globalObject, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue) + { + Structure* structure = globalObject->activationStructure(); + return create(vm, structure, currentScope, symbolTable, initialValue); + } + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + + static JSValue toThis(JSCell*, ExecState*, ECMAMode); + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject) { return Structure::create(vm, globalObject, jsNull(), TypeInfo(ActivationObjectType, StructureFlags), info()); } + +private: + bool symbolTableGet(PropertyName, PropertySlot&); + bool symbolTablePut(ExecState*, PropertyName, JSValue, bool shouldThrow); + bool symbolTablePutWithAttributes(VM&, PropertyName, JSValue, unsigned attributes); +}; + +inline JSLexicalEnvironment::JSLexicalEnvironment(VM& vm, Structure* structure, JSScope* currentScope, SymbolTable* symbolTable) + : Base(vm, structure, currentScope, symbolTable) +{ +} + +inline JSLexicalEnvironment* asActivation(JSValue value) +{ + ASSERT(asObject(value)->inherits(JSLexicalEnvironment::info())); + return jsCast<JSLexicalEnvironment*>(asObject(value)); +} + +ALWAYS_INLINE JSLexicalEnvironment* Register::lexicalEnvironment() const +{ + return asActivation(jsValue()); +} + +} // namespace JSC + +#endif // JSLexicalEnvironment_h diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp new file mode 100644 index 000000000..828df168c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2005, 2008, 2012, 2014 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the NU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "JSLock.h" + +#include "Heap.h" +#include "CallFrame.h" +#include "JSGlobalObject.h" +#include "JSObject.h" +#include "JSCInlines.h" +#include <thread> + +namespace JSC { + +StaticLock GlobalJSLock::s_sharedInstanceMutex; + +GlobalJSLock::GlobalJSLock() +{ + s_sharedInstanceMutex.lock(); +} + +GlobalJSLock::~GlobalJSLock() +{ + s_sharedInstanceMutex.unlock(); +} + +JSLockHolder::JSLockHolder(ExecState* exec) + : m_vm(&exec->vm()) +{ + init(); +} + +JSLockHolder::JSLockHolder(VM* vm) + : m_vm(vm) +{ + init(); +} + +JSLockHolder::JSLockHolder(VM& vm) + : m_vm(&vm) +{ + init(); +} + +void JSLockHolder::init() +{ + m_vm->apiLock().lock(); +} + +JSLockHolder::~JSLockHolder() +{ + RefPtr<JSLock> apiLock(&m_vm->apiLock()); + m_vm = nullptr; + apiLock->unlock(); +} + +JSLock::JSLock(VM* vm) + : m_ownerThreadID(std::thread::id()) + , m_lockCount(0) + , m_lockDropDepth(0) + , m_hasExclusiveThread(false) + , m_vm(vm) + , m_entryAtomicStringTable(nullptr) +{ +} + +JSLock::~JSLock() +{ +} + +void JSLock::willDestroyVM(VM* vm) +{ + ASSERT_UNUSED(vm, m_vm == vm); + m_vm = nullptr; +} + +void JSLock::setExclusiveThread(std::thread::id threadId) +{ + RELEASE_ASSERT(!m_lockCount && m_ownerThreadID == std::thread::id()); + m_hasExclusiveThread = (threadId != std::thread::id()); + m_ownerThreadID = threadId; +} + +void JSLock::lock() +{ + lock(1); +} + +void JSLock::lock(intptr_t lockCount) +{ + ASSERT(lockCount > 0); + if (currentThreadIsHoldingLock()) { + m_lockCount += lockCount; + return; + } + + if (!m_hasExclusiveThread) { + m_lock.lock(); + m_ownerThreadID = std::this_thread::get_id(); + } + ASSERT(!m_lockCount); + m_lockCount = lockCount; + + didAcquireLock(); +} + +void JSLock::didAcquireLock() +{ + // FIXME: What should happen to the per-thread identifier table if we don't have a VM? + if (!m_vm) + return; + + RELEASE_ASSERT(!m_vm->stackPointerAtVMEntry()); + void* p = &p; // A proxy for the current stack pointer. + m_vm->setStackPointerAtVMEntry(p); + + WTFThreadData& threadData = wtfThreadData(); + m_vm->setLastStackTop(threadData.savedLastStackTop()); + + ASSERT(!m_entryAtomicStringTable); + m_entryAtomicStringTable = threadData.setCurrentAtomicStringTable(m_vm->atomicStringTable()); + ASSERT(m_entryAtomicStringTable); + + m_vm->heap.machineThreads().addCurrentThread(); +} + +void JSLock::unlock() +{ + unlock(1); +} + +void JSLock::unlock(intptr_t unlockCount) +{ + RELEASE_ASSERT(currentThreadIsHoldingLock()); + ASSERT(m_lockCount >= unlockCount); + + // Maintain m_lockCount while calling willReleaseLock() so that its callees know that + // they still have the lock. + if (unlockCount == m_lockCount) + willReleaseLock(); + + m_lockCount -= unlockCount; + + if (!m_lockCount) { + + if (!m_hasExclusiveThread) { + m_ownerThreadID = std::thread::id(); + m_lock.unlock(); + } + } +} + +void JSLock::willReleaseLock() +{ + if (m_vm) { + m_vm->drainMicrotasks(); + + m_vm->heap.releaseDelayedReleasedObjects(); + m_vm->setStackPointerAtVMEntry(nullptr); + } + + if (m_entryAtomicStringTable) { + wtfThreadData().setCurrentAtomicStringTable(m_entryAtomicStringTable); + m_entryAtomicStringTable = nullptr; + } +} + +void JSLock::lock(ExecState* exec) +{ + exec->vm().apiLock().lock(); +} + +void JSLock::unlock(ExecState* exec) +{ + exec->vm().apiLock().unlock(); +} + +bool JSLock::currentThreadIsHoldingLock() +{ + ASSERT(!m_hasExclusiveThread || (exclusiveThread() == std::this_thread::get_id())); + if (m_hasExclusiveThread) + return !!m_lockCount; + return m_ownerThreadID == std::this_thread::get_id(); +} + +// This function returns the number of locks that were dropped. +unsigned JSLock::dropAllLocks(DropAllLocks* dropper) +{ + if (m_hasExclusiveThread) { + ASSERT(exclusiveThread() == std::this_thread::get_id()); + return 0; + } + + if (!currentThreadIsHoldingLock()) + return 0; + + ++m_lockDropDepth; + + dropper->setDropDepth(m_lockDropDepth); + + WTFThreadData& threadData = wtfThreadData(); + threadData.setSavedStackPointerAtVMEntry(m_vm->stackPointerAtVMEntry()); + threadData.setSavedLastStackTop(m_vm->lastStackTop()); + + unsigned droppedLockCount = m_lockCount; + unlock(droppedLockCount); + + return droppedLockCount; +} + +void JSLock::grabAllLocks(DropAllLocks* dropper, unsigned droppedLockCount) +{ + ASSERT(!m_hasExclusiveThread || !droppedLockCount); + + // If no locks were dropped, nothing to do! + if (!droppedLockCount) + return; + + ASSERT(!currentThreadIsHoldingLock()); + lock(droppedLockCount); + + while (dropper->dropDepth() != m_lockDropDepth) { + unlock(droppedLockCount); + std::this_thread::yield(); + lock(droppedLockCount); + } + + --m_lockDropDepth; + + WTFThreadData& threadData = wtfThreadData(); + m_vm->setStackPointerAtVMEntry(threadData.savedStackPointerAtVMEntry()); + m_vm->setLastStackTop(threadData.savedLastStackTop()); +} + +JSLock::DropAllLocks::DropAllLocks(VM* vm) + : m_droppedLockCount(0) + // If the VM is in the middle of being destroyed then we don't want to resurrect it + // by allowing DropAllLocks to ref it. By this point the JSLock has already been + // released anyways, so it doesn't matter that DropAllLocks is a no-op. + , m_vm(vm->refCount() ? vm : nullptr) +{ + if (!m_vm) + return; + wtfThreadData().resetCurrentAtomicStringTable(); + RELEASE_ASSERT(!m_vm->apiLock().currentThreadIsHoldingLock() || !m_vm->isCollectorBusy()); + m_droppedLockCount = m_vm->apiLock().dropAllLocks(this); +} + +JSLock::DropAllLocks::DropAllLocks(ExecState* exec) + : DropAllLocks(exec ? &exec->vm() : nullptr) +{ +} + +JSLock::DropAllLocks::DropAllLocks(VM& vm) + : DropAllLocks(&vm) +{ +} + +JSLock::DropAllLocks::~DropAllLocks() +{ + if (!m_vm) + return; + m_vm->apiLock().grabAllLocks(this, m_droppedLockCount); + wtfThreadData().setCurrentAtomicStringTable(m_vm->atomicStringTable()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSLock.h b/Source/JavaScriptCore/runtime/JSLock.h new file mode 100644 index 000000000..4d0d17420 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLock.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005, 2008, 2009, 2014 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSLock_h +#define JSLock_h + +#include <mutex> +#include <thread> +#include <wtf/Assertions.h> +#include <wtf/Lock.h> +#include <wtf/Noncopyable.h> +#include <wtf/RefPtr.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/WTFThreadData.h> + +namespace JSC { + +// To make it safe to use JavaScript on multiple threads, it is +// important to lock before doing anything that allocates a +// JavaScript data structure or that interacts with shared state +// such as the protect count hash table. The simplest way to lock +// is to create a local JSLockHolder object in the scope where the lock +// must be held and pass it the context that requires protection. +// The lock is recursive so nesting is ok. The JSLock +// object also acts as a convenience short-hand for running important +// initialization routines. + +// To avoid deadlock, sometimes it is necessary to temporarily +// release the lock. Since it is recursive you actually have to +// release all locks held by your thread. This is safe to do if +// you are executing code that doesn't require the lock, and you +// reacquire the right number of locks at the end. You can do this +// by constructing a locally scoped JSLock::DropAllLocks object. The +// DropAllLocks object takes care to release the JSLock only if your +// thread acquired it to begin with. + +class ExecState; +class VM; + +// This class is used to protect the initialization of the legacy single +// shared VM. +class GlobalJSLock { + WTF_MAKE_NONCOPYABLE(GlobalJSLock); +public: + JS_EXPORT_PRIVATE GlobalJSLock(); + JS_EXPORT_PRIVATE ~GlobalJSLock(); +private: + static StaticLock s_sharedInstanceMutex; +}; + +class JSLockHolder { +public: + JS_EXPORT_PRIVATE JSLockHolder(VM*); + JS_EXPORT_PRIVATE JSLockHolder(VM&); + JS_EXPORT_PRIVATE JSLockHolder(ExecState*); + + JS_EXPORT_PRIVATE ~JSLockHolder(); +private: + void init(); + + RefPtr<VM> m_vm; +}; + +class JSLock : public ThreadSafeRefCounted<JSLock> { + WTF_MAKE_NONCOPYABLE(JSLock); +public: + JSLock(VM*); + JS_EXPORT_PRIVATE ~JSLock(); + + JS_EXPORT_PRIVATE void lock(); + JS_EXPORT_PRIVATE void unlock(); + + static void lock(ExecState*); + static void unlock(ExecState*); + static void lock(VM&); + static void unlock(VM&); + + VM* vm() { return m_vm; } + + bool hasExclusiveThread() const { return m_hasExclusiveThread; } + std::thread::id exclusiveThread() const + { + ASSERT(m_hasExclusiveThread); + return m_ownerThreadID; + } + JS_EXPORT_PRIVATE void setExclusiveThread(std::thread::id); + JS_EXPORT_PRIVATE bool currentThreadIsHoldingLock(); + + void willDestroyVM(VM*); + + class DropAllLocks { + WTF_MAKE_NONCOPYABLE(DropAllLocks); + public: + JS_EXPORT_PRIVATE DropAllLocks(ExecState*); + JS_EXPORT_PRIVATE DropAllLocks(VM*); + JS_EXPORT_PRIVATE DropAllLocks(VM&); + JS_EXPORT_PRIVATE ~DropAllLocks(); + + void setDropDepth(unsigned depth) { m_dropDepth = depth; } + unsigned dropDepth() const { return m_dropDepth; } + + private: + intptr_t m_droppedLockCount; + RefPtr<VM> m_vm; + unsigned m_dropDepth; + }; + +private: + void lock(intptr_t lockCount); + void unlock(intptr_t unlockCount); + + void didAcquireLock(); + void willReleaseLock(); + + unsigned dropAllLocks(DropAllLocks*); + void grabAllLocks(DropAllLocks*, unsigned lockCount); + + Lock m_lock; + std::thread::id m_ownerThreadID; + intptr_t m_lockCount; + unsigned m_lockDropDepth; + bool m_hasExclusiveThread; + VM* m_vm; + AtomicStringTable* m_entryAtomicStringTable; +}; + +} // namespace + +#endif // JSLock_h diff --git a/Source/JavaScriptCore/runtime/JSMap.cpp b/Source/JavaScriptCore/runtime/JSMap.cpp new file mode 100644 index 000000000..352648b5e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSMap.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSMap.h" + +#include "JSCJSValueInlines.h" +#include "JSMapIterator.h" +#include "MapDataInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSMap::s_info = { "Map", &Base::s_info, 0, CREATE_METHOD_TABLE(JSMap) }; + +void JSMap::destroy(JSCell* cell) +{ + JSMap* thisObject = jsCast<JSMap*>(cell); + thisObject->JSMap::~JSMap(); +} + +void JSMap::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Base::visitChildren(cell, visitor); + jsCast<JSMap*>(cell)->m_mapData.visitChildren(cell, visitor); +} + +void JSMap::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token) +{ + Base::copyBackingStore(cell, visitor, token); + jsCast<JSMap*>(cell)->m_mapData.copyBackingStore(visitor, token); +} + +bool JSMap::has(ExecState* exec, JSValue key) +{ + return m_mapData.contains(exec, key); +} + +size_t JSMap::size(ExecState* exec) +{ + return m_mapData.size(exec); +} + +JSValue JSMap::get(ExecState* exec, JSValue key) +{ + JSValue result = m_mapData.get(exec, key); + if (!result) + return jsUndefined(); + return result; +} + +void JSMap::set(ExecState* exec, JSValue key, JSValue value) +{ + m_mapData.set(exec, this, key, value); +} + +void JSMap::clear(ExecState*) +{ + m_mapData.clear(); +} + +bool JSMap::remove(ExecState* exec, JSValue key) +{ + return m_mapData.remove(exec, key); +} + +} diff --git a/Source/JavaScriptCore/runtime/JSMap.h b/Source/JavaScriptCore/runtime/JSMap.h new file mode 100644 index 000000000..837f7e369 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSMap.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSMap_h +#define JSMap_h + +#include "JSDestructibleObject.h" +#include "JSObject.h" +#include "MapData.h" + +namespace JSC { + +class JSMapIterator; + +class JSMap : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + friend class JSMapIterator; + + // Our marking functions expect Entry to maintain this layout, and have all + // fields be WriteBarrier<Unknown> + class Entry { + private: + WriteBarrier<Unknown> m_key; + WriteBarrier<Unknown> m_value; + + public: + const WriteBarrier<Unknown>& key() const + { + return m_key; + } + + const WriteBarrier<Unknown>& value() const + { + return m_value; + } + + void visitChildren(SlotVisitor& visitor) + { + visitor.append(&m_key); + visitor.append(&m_value); + } + + void setKey(VM& vm, const JSCell* owner, JSValue key) + { + m_key.set(vm, owner, key); + } + + void setKeyWithoutWriteBarrier(JSValue key) + { + m_key.setWithoutWriteBarrier(key); + } + + void setValue(VM& vm, const JSCell* owner, JSValue value) + { + m_value.set(vm, owner, value); + } + + void clear() + { + m_key.clear(); + m_value.clear(); + } + }; + + typedef MapDataImpl<Entry, JSMapIterator> MapData; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSMap* create(VM& vm, Structure* structure) + { + JSMap* instance = new (NotNull, allocateCell<JSMap>(vm.heap)) JSMap(vm, structure); + instance->finishCreation(vm); + return instance; + } + + static JSMap* create(ExecState* exec, Structure* structure) + { + return create(exec->vm(), structure); + } + + bool has(ExecState*, JSValue); + size_t size(ExecState*); + JSValue get(ExecState*, JSValue); + JS_EXPORT_PRIVATE void set(ExecState*, JSValue key, JSValue value); + void clear(ExecState*); + bool remove(ExecState*, JSValue); + +private: + JSMap(VM& vm, Structure* structure) + : Base(vm, structure) + , m_mapData(vm) + { + } + + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); + + MapData m_mapData; +}; + +} + +#endif // !defined(JSMap_h) diff --git a/Source/JavaScriptCore/runtime/JSMapIterator.cpp b/Source/JavaScriptCore/runtime/JSMapIterator.cpp new file mode 100644 index 000000000..885362bd4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSMapIterator.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSMapIterator.h" + +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSMap.h" +#include "MapDataInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSMapIterator::s_info = { "Map Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(JSMapIterator) }; + +void JSMapIterator::finishCreation(VM& vm, JSMap* iteratedObject) +{ + Base::finishCreation(vm); + m_map.set(vm, this, iteratedObject); +} + +void JSMapIterator::destroy(JSCell* cell) +{ + JSMapIterator* thisObject = jsCast<JSMapIterator*>(cell); + thisObject->JSMapIterator::~JSMapIterator(); +} + +void JSMapIterator::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSMapIterator* thisObject = jsCast<JSMapIterator*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_map); +} + +JSValue JSMapIterator::createPair(CallFrame* callFrame, JSValue key, JSValue value) +{ + MarkedArgumentBuffer args; + args.append(key); + args.append(value); + JSGlobalObject* globalObject = callFrame->callee()->globalObject(); + return constructArray(callFrame, 0, globalObject, args); +} + +JSMapIterator* JSMapIterator::clone(ExecState* exec) +{ + auto clone = JSMapIterator::create(exec->vm(), exec->callee()->globalObject()->mapIteratorStructure(), m_map.get(), m_kind); + clone->m_iterator = m_iterator; + return clone; +} + +} diff --git a/Source/JavaScriptCore/runtime/JSMapIterator.h b/Source/JavaScriptCore/runtime/JSMapIterator.h new file mode 100644 index 000000000..cc2ae867a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSMapIterator.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSMapIterator_h +#define JSMapIterator_h + +#include "JSDestructibleObject.h" +#include "JSMap.h" +#include "MapData.h" + +namespace JSC { +enum MapIterationKind : uint32_t { + MapIterateKey, + MapIterateValue, + MapIterateKeyValue, +}; + +class JSMapIterator : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSMapIterator* create(VM& vm, Structure* structure, JSMap* iteratedObject, MapIterationKind kind) + { + JSMapIterator* instance = new (NotNull, allocateCell<JSMapIterator>(vm.heap)) JSMapIterator(vm, structure, iteratedObject, kind); + instance->finishCreation(vm, iteratedObject); + return instance; + } + + bool next(CallFrame* callFrame, JSValue& value) + { + WTF::KeyValuePair<JSValue, JSValue> pair; + if (!m_iterator.next(pair)) + return false; + + if (m_kind == MapIterateValue) + value = pair.value; + else if (m_kind == MapIterateKey) + value = pair.key; + else + value = createPair(callFrame, pair.key, pair.value); + return true; + } + + bool nextKeyValue(JSValue& key, JSValue& value) + { + WTF::KeyValuePair<JSValue, JSValue> pair; + if (!m_iterator.next(pair)) + return false; + + key = pair.key; + value = pair.value; + return true; + } + + void finish() + { + m_iterator.finish(); + } + + MapIterationKind kind() const { return m_kind; } + JSValue iteratedValue() const { return m_map.get(); } + JSMapIterator* clone(ExecState*); + + JSMap::MapData::IteratorData* iteratorData() + { + return &m_iterator; + } + +private: + JSMapIterator(VM& vm, Structure* structure, JSMap* iteratedObject, MapIterationKind kind) + : Base(vm, structure) + , m_iterator(iteratedObject->m_mapData.createIteratorData(this)) + , m_kind(kind) + { + } + + static void destroy(JSCell*); + JS_EXPORT_PRIVATE void finishCreation(VM&, JSMap*); + JSValue createPair(CallFrame*, JSValue, JSValue); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<JSMap> m_map; + JSMap::MapData::IteratorData m_iterator; + MapIterationKind m_kind; +}; + +} + +#endif // !defined(JSMapIterator_h) diff --git a/Source/JavaScriptCore/runtime/JSNotAnObject.cpp b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp new file mode 100644 index 000000000..af4737e9b --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "JSNotAnObject.h" + +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSNotAnObject); + +const ClassInfo JSNotAnObject::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(JSNotAnObject) }; + +// JSValue methods +JSValue JSNotAnObject::defaultValue(const JSObject*, ExecState* exec, PreferredPrimitiveType) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return jsNumber(0); +} + +// JSObject methods +bool JSNotAnObject::getOwnPropertySlot(JSObject*, ExecState* exec, PropertyName, PropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::getOwnPropertySlotByIndex(JSObject*, ExecState* exec, unsigned, PropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +void JSNotAnObject::put(JSCell*, ExecState* exec, PropertyName , JSValue, PutPropertySlot&) +{ + ASSERT_UNUSED(exec, exec->hadException()); +} + +void JSNotAnObject::putByIndex(JSCell*, ExecState* exec, unsigned, JSValue, bool) +{ + ASSERT_UNUSED(exec, exec->hadException()); +} + +bool JSNotAnObject::deleteProperty(JSCell*, ExecState* exec, PropertyName) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +bool JSNotAnObject::deletePropertyByIndex(JSCell*, ExecState* exec, unsigned) +{ + ASSERT_UNUSED(exec, exec->hadException()); + return false; +} + +void JSNotAnObject::getOwnPropertyNames(JSObject*, ExecState* exec, PropertyNameArray&, EnumerationMode) +{ + ASSERT_UNUSED(exec, exec->hadException()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSNotAnObject.h b/Source/JavaScriptCore/runtime/JSNotAnObject.h new file mode 100644 index 000000000..0c26ec98b --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef JSNotAnObject_h +#define JSNotAnObject_h + +#include "JSObject.h" + +namespace JSC { + +// This unholy class is used to allow us to avoid multiple exception checks +// in certain SquirrelFish bytecodes -- effectively it just silently consumes +// any operations performed on the result of a failed toObject call. +class JSNotAnObject final : public JSNonFinalObject { +private: + explicit JSNotAnObject(VM& vm) + : JSNonFinalObject(vm, vm.notAnObjectStructure.get()) + { + } + +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames; + + static JSNotAnObject* create(VM& vm) + { + JSNotAnObject* object = new (NotNull, allocateCell<JSNotAnObject>(vm.heap)) JSNotAnObject(vm); + object->finishCreation(vm); + return object; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + DECLARE_INFO; + +private: + // JSValue methods + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + + // JSObject methods + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); + + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); +}; + +} // namespace JSC + +#endif // JSNotAnObject_h diff --git a/Source/JavaScriptCore/runtime/JSONObject.cpp b/Source/JavaScriptCore/runtime/JSONObject.cpp new file mode 100644 index 000000000..f67ba7ece --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSONObject.cpp @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSONObject.h" + +#include "BooleanObject.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSGlobalObject.h" +#include "LiteralParser.h" +#include "Local.h" +#include "LocalScope.h" +#include "Lookup.h" +#include "ObjectConstructor.h" +#include "JSCInlines.h" +#include "PropertyNameArray.h" +#include <wtf/MathExtras.h> +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSONObject); + +EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*); +EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*); + +} + +#include "JSONObject.lut.h" + +namespace JSC { + +JSONObject::JSONObject(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} + +void JSONObject::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +// PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked. +class PropertyNameForFunctionCall { +public: + PropertyNameForFunctionCall(const Identifier&); + PropertyNameForFunctionCall(unsigned); + + JSValue value(ExecState*) const; + +private: + const Identifier* m_identifier; + unsigned m_number; + mutable JSValue m_value; +}; + +class Stringifier { + WTF_MAKE_NONCOPYABLE(Stringifier); +public: + Stringifier(ExecState*, const Local<Unknown>& replacer, const Local<Unknown>& space); + Local<Unknown> stringify(Handle<Unknown>); + + void visitAggregate(SlotVisitor&); + +private: + class Holder { + public: + Holder(VM&, JSObject*); + + JSObject* object() const { return m_object.get(); } + + bool appendNextProperty(Stringifier&, StringBuilder&); + + private: + Local<JSObject> m_object; + const bool m_isArray; + bool m_isJSArray; + unsigned m_index; + unsigned m_size; + RefPtr<PropertyNameArrayData> m_propertyNames; + }; + + friend class Holder; + + JSValue toJSON(JSValue, const PropertyNameForFunctionCall&); + JSValue toJSONImpl(JSValue, const PropertyNameForFunctionCall&); + + enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedValue }; + StringifyResult appendStringifiedValue(StringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&); + + bool willIndent() const; + void indent(); + void unindent(); + void startNewLine(StringBuilder&) const; + + ExecState* const m_exec; + const Local<Unknown> m_replacer; + bool m_usingArrayReplacer; + PropertyNameArray m_arrayReplacerPropertyNames; + CallType m_replacerCallType; + CallData m_replacerCallData; + const String m_gap; + + Vector<Holder, 16, UnsafeVectorOverflow> m_holderStack; + String m_repeatedGap; + String m_indent; +}; + +// ------------------------------ helper functions -------------------------------- + +static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value) +{ + if (!value.isObject()) + return value; + JSObject* object = asObject(value); + if (object->inherits(NumberObject::info())) + return jsNumber(object->toNumber(exec)); + if (object->inherits(StringObject::info())) + return object->toString(exec); + if (object->inherits(BooleanObject::info())) + return object->toPrimitive(exec); + return value; +} + +static inline String gap(ExecState* exec, JSValue space) +{ + const unsigned maxGapLength = 10; + space = unwrapBoxedPrimitive(exec, space); + + // If the space value is a number, create a gap string with that number of spaces. + if (space.isNumber()) { + double spaceCount = space.asNumber(); + int count; + if (spaceCount > maxGapLength) + count = maxGapLength; + else if (!(spaceCount > 0)) + count = 0; + else + count = static_cast<int>(spaceCount); + UChar spaces[maxGapLength]; + for (int i = 0; i < count; ++i) + spaces[i] = ' '; + return String(spaces, count); + } + + // If the space value is a string, use it as the gap string, otherwise use no gap string. + String spaces = space.getString(exec); + if (spaces.length() > maxGapLength) { + spaces = spaces.substringSharingImpl(0, maxGapLength); + } + return spaces; +} + +// ------------------------------ PropertyNameForFunctionCall -------------------------------- + +inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier& identifier) + : m_identifier(&identifier) +{ +} + +inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number) + : m_identifier(0) + , m_number(number) +{ +} + +JSValue PropertyNameForFunctionCall::value(ExecState* exec) const +{ + if (!m_value) { + if (m_identifier) + m_value = jsString(exec, m_identifier->string()); + else + m_value = jsNumber(m_number); + } + return m_value; +} + +// ------------------------------ Stringifier -------------------------------- + +Stringifier::Stringifier(ExecState* exec, const Local<Unknown>& replacer, const Local<Unknown>& space) + : m_exec(exec) + , m_replacer(replacer) + , m_usingArrayReplacer(false) + , m_arrayReplacerPropertyNames(exec, PropertyNameMode::Strings) + , m_replacerCallType(CallTypeNone) + , m_gap(gap(exec, space.get())) +{ + if (!m_replacer.isObject()) + return; + + if (m_replacer.asObject()->inherits(JSArray::info())) { + m_usingArrayReplacer = true; + Handle<JSObject> array = m_replacer.asObject(); + unsigned length = array->get(exec, exec->vm().propertyNames->length).toUInt32(exec); + for (unsigned i = 0; i < length; ++i) { + JSValue name = array->get(exec, i); + if (exec->hadException()) + break; + + if (name.isObject()) { + if (!asObject(name)->inherits(NumberObject::info()) && !asObject(name)->inherits(StringObject::info())) + continue; + } else if (!name.isNumber() && !name.isString()) + continue; + + m_arrayReplacerPropertyNames.add(name.toString(exec)->toIdentifier(exec)); + } + return; + } + + m_replacerCallType = m_replacer.asObject()->methodTable()->getCallData(m_replacer.asObject().get(), m_replacerCallData); +} + +Local<Unknown> Stringifier::stringify(Handle<Unknown> value) +{ + JSObject* object = constructEmptyObject(m_exec); + if (m_exec->hadException()) + return Local<Unknown>(m_exec->vm(), jsNull()); + + PropertyNameForFunctionCall emptyPropertyName(m_exec->vm().propertyNames->emptyIdentifier); + object->putDirect(m_exec->vm(), m_exec->vm().propertyNames->emptyIdentifier, value.get()); + + StringBuilder result; + if (appendStringifiedValue(result, value.get(), object, emptyPropertyName) != StringifySucceeded) + return Local<Unknown>(m_exec->vm(), jsUndefined()); + if (m_exec->hadException()) + return Local<Unknown>(m_exec->vm(), jsNull()); + + return Local<Unknown>(m_exec->vm(), jsString(m_exec, result.toString())); +} + +ALWAYS_INLINE JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName) +{ + ASSERT(!m_exec->hadException()); + if (!value.isObject() || !asObject(value)->hasProperty(m_exec, m_exec->vm().propertyNames->toJSON)) + return value; + return toJSONImpl(value, propertyName); +} + +JSValue Stringifier::toJSONImpl(JSValue value, const PropertyNameForFunctionCall& propertyName) +{ + JSValue toJSONFunction = asObject(value)->get(m_exec, m_exec->vm().propertyNames->toJSON); + if (m_exec->hadException()) + return jsNull(); + + if (!toJSONFunction.isObject()) + return value; + + JSObject* object = asObject(toJSONFunction); + CallData callData; + CallType callType = object->methodTable()->getCallData(object, callData); + if (callType == CallTypeNone) + return value; + + MarkedArgumentBuffer args; + args.append(propertyName.value(m_exec)); + return call(m_exec, object, callType, callData, value, args); +} + +Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName) +{ + // Call the toJSON function. + value = toJSON(value, propertyName); + if (m_exec->hadException()) + return StringifyFailed; + + // Call the replacer function. + if (m_replacerCallType != CallTypeNone) { + MarkedArgumentBuffer args; + args.append(propertyName.value(m_exec)); + args.append(value); + value = call(m_exec, m_replacer.get(), m_replacerCallType, m_replacerCallData, holder, args); + if (m_exec->hadException()) + return StringifyFailed; + } + + if (value.isUndefined() && !holder->inherits(JSArray::info())) + return StringifyFailedDueToUndefinedValue; + + if (value.isNull()) { + builder.appendLiteral("null"); + return StringifySucceeded; + } + + value = unwrapBoxedPrimitive(m_exec, value); + + if (m_exec->hadException()) + return StringifyFailed; + + if (value.isBoolean()) { + if (value.isTrue()) + builder.appendLiteral("true"); + else + builder.appendLiteral("false"); + return StringifySucceeded; + } + + if (value.isString()) { + builder.appendQuotedJSONString(asString(value)->value(m_exec)); + return StringifySucceeded; + } + + if (value.isNumber()) { + if (value.isInt32()) + builder.appendNumber(value.asInt32()); + else { + double number = value.asNumber(); + if (!std::isfinite(number)) + builder.appendLiteral("null"); + else + builder.appendECMAScriptNumber(number); + } + return StringifySucceeded; + } + + if (!value.isObject()) + return StringifyFailed; + + JSObject* object = asObject(value); + + CallData callData; + if (object->methodTable()->getCallData(object, callData) != CallTypeNone) { + if (holder->inherits(JSArray::info())) { + builder.appendLiteral("null"); + return StringifySucceeded; + } + return StringifyFailedDueToUndefinedValue; + } + + // Handle cycle detection, and put the holder on the stack. + for (unsigned i = 0; i < m_holderStack.size(); i++) { + if (m_holderStack[i].object() == object) { + m_exec->vm().throwException(m_exec, createTypeError(m_exec, ASCIILiteral("JSON.stringify cannot serialize cyclic structures."))); + return StringifyFailed; + } + } + bool holderStackWasEmpty = m_holderStack.isEmpty(); + m_holderStack.append(Holder(m_exec->vm(), object)); + if (!holderStackWasEmpty) + return StringifySucceeded; + + do { + while (m_holderStack.last().appendNextProperty(*this, builder)) { + if (m_exec->hadException()) + return StringifyFailed; + } + m_holderStack.removeLast(); + } while (!m_holderStack.isEmpty()); + return StringifySucceeded; +} + +inline bool Stringifier::willIndent() const +{ + return !m_gap.isEmpty(); +} + +inline void Stringifier::indent() +{ + // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent. + unsigned newSize = m_indent.length() + m_gap.length(); + if (newSize > m_repeatedGap.length()) + m_repeatedGap = makeString(m_repeatedGap, m_gap); + ASSERT(newSize <= m_repeatedGap.length()); + m_indent = m_repeatedGap.substringSharingImpl(0, newSize); +} + +inline void Stringifier::unindent() +{ + ASSERT(m_indent.length() >= m_gap.length()); + m_indent = m_repeatedGap.substringSharingImpl(0, m_indent.length() - m_gap.length()); +} + +inline void Stringifier::startNewLine(StringBuilder& builder) const +{ + if (m_gap.isEmpty()) + return; + builder.append('\n'); + builder.append(m_indent); +} + +inline Stringifier::Holder::Holder(VM& vm, JSObject* object) + : m_object(vm, object) + , m_isArray(object->inherits(JSArray::info())) + , m_index(0) +#ifndef NDEBUG + , m_size(0) +#endif +{ +} + +bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBuilder& builder) +{ + ASSERT(m_index <= m_size); + + ExecState* exec = stringifier.m_exec; + + // First time through, initialize. + if (!m_index) { + if (m_isArray) { + m_isJSArray = isJSArray(m_object.get()); + if (m_isJSArray) + m_size = asArray(m_object.get())->length(); + else + m_size = m_object->get(exec, exec->vm().propertyNames->length).toUInt32(exec); + builder.append('['); + } else { + if (stringifier.m_usingArrayReplacer) + m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data(); + else { + PropertyNameArray objectPropertyNames(exec, PropertyNameMode::Strings); + m_object->methodTable()->getOwnPropertyNames(m_object.get(), exec, objectPropertyNames, EnumerationMode()); + m_propertyNames = objectPropertyNames.releaseData(); + } + m_size = m_propertyNames->propertyNameVector().size(); + builder.append('{'); + } + stringifier.indent(); + } + + // Last time through, finish up and return false. + if (m_index == m_size) { + stringifier.unindent(); + if (m_size && builder[builder.length() - 1] != '{') + stringifier.startNewLine(builder); + builder.append(m_isArray ? ']' : '}'); + return false; + } + + // Handle a single element of the array or object. + unsigned index = m_index++; + unsigned rollBackPoint = 0; + StringifyResult stringifyResult; + if (m_isArray) { + // Get the value. + JSValue value; + if (m_isJSArray && asArray(m_object.get())->canGetIndexQuickly(index)) + value = asArray(m_object.get())->getIndexQuickly(index); + else { + PropertySlot slot(m_object.get()); + if (m_object->methodTable()->getOwnPropertySlotByIndex(m_object.get(), exec, index, slot)) { + value = slot.getValue(exec, index); + if (exec->hadException()) + return false; + } else + value = jsUndefined(); + } + + // Append the separator string. + if (index) + builder.append(','); + stringifier.startNewLine(builder); + + // Append the stringified value. + stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object.get(), index); + } else { + // Get the value. + PropertySlot slot(m_object.get()); + Identifier& propertyName = m_propertyNames->propertyNameVector()[index]; + if (!m_object->methodTable()->getOwnPropertySlot(m_object.get(), exec, propertyName, slot)) + return true; + JSValue value = slot.getValue(exec, propertyName); + if (exec->hadException()) + return false; + + rollBackPoint = builder.length(); + + // Append the separator string. + if (builder[rollBackPoint - 1] != '{') + builder.append(','); + stringifier.startNewLine(builder); + + // Append the property name. + builder.appendQuotedJSONString(propertyName.string()); + builder.append(':'); + if (stringifier.willIndent()) + builder.append(' '); + + // Append the stringified value. + stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object.get(), propertyName); + } + + // From this point on, no access to the this pointer or to any members, because the + // Holder object may have moved if the call to stringify pushed a new Holder onto + // m_holderStack. + + switch (stringifyResult) { + case StringifyFailed: + builder.appendLiteral("null"); + break; + case StringifySucceeded: + break; + case StringifyFailedDueToUndefinedValue: + // This only occurs when get an undefined value for an object property. + // In this case we don't want the separator and property name that we + // already appended, so roll back. + builder.resize(rollBackPoint); + break; + } + + return true; +} + +// ------------------------------ JSONObject -------------------------------- + +const ClassInfo JSONObject::s_info = { "JSON", &JSNonFinalObject::s_info, &jsonTable, CREATE_METHOD_TABLE(JSONObject) }; + +/* Source for JSONObject.lut.h +@begin jsonTable + parse JSONProtoFuncParse DontEnum|Function 2 + stringify JSONProtoFuncStringify DontEnum|Function 3 +@end +*/ + +// ECMA 15.8 + +bool JSONObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, jsonTable, jsCast<JSONObject*>(object), propertyName, slot); +} + +class Walker { +public: + Walker(ExecState* exec, Handle<JSObject> function, CallType callType, CallData callData) + : m_exec(exec) + , m_function(exec->vm(), function) + , m_callType(callType) + , m_callData(callData) + { + } + JSValue walk(JSValue unfiltered); +private: + JSValue callReviver(JSObject* thisObj, JSValue property, JSValue unfiltered) + { + MarkedArgumentBuffer args; + args.append(property); + args.append(unfiltered); + return call(m_exec, m_function.get(), m_callType, m_callData, thisObj, args); + } + + friend class Holder; + + ExecState* m_exec; + Local<JSObject> m_function; + CallType m_callType; + CallData m_callData; +}; + +// We clamp recursion well beyond anything reasonable. +static const unsigned maximumFilterRecursion = 40000; +enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember, + ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember }; +NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) +{ + Vector<PropertyNameArray, 16, UnsafeVectorOverflow> propertyStack; + Vector<uint32_t, 16, UnsafeVectorOverflow> indexStack; + LocalStack<JSObject, 16> objectStack(m_exec->vm()); + LocalStack<JSArray, 16> arrayStack(m_exec->vm()); + + Vector<WalkerState, 16, UnsafeVectorOverflow> stateStack; + WalkerState state = StateUnknown; + JSValue inValue = unfiltered; + JSValue outValue = jsNull(); + + while (1) { + switch (state) { + arrayStartState: + case ArrayStartState: { + ASSERT(inValue.isObject()); + ASSERT(isJSArray(asObject(inValue)) || asObject(inValue)->inherits(JSArray::info())); + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) + return throwStackOverflowError(m_exec); + + JSArray* array = asArray(inValue); + arrayStack.push(array); + indexStack.append(0); + } + arrayStartVisitMember: + FALLTHROUGH; + case ArrayStartVisitMember: { + JSArray* array = arrayStack.peek(); + uint32_t index = indexStack.last(); + if (index == array->length()) { + outValue = array; + arrayStack.pop(); + indexStack.removeLast(); + break; + } + if (isJSArray(array) && array->canGetIndexQuickly(index)) + inValue = array->getIndexQuickly(index); + else { + PropertySlot slot(array); + if (array->methodTable()->getOwnPropertySlotByIndex(array, m_exec, index, slot)) + inValue = slot.getValue(m_exec, index); + else + inValue = jsUndefined(); + } + + if (inValue.isObject()) { + stateStack.append(ArrayEndVisitMember); + goto stateUnknown; + } else + outValue = inValue; + FALLTHROUGH; + } + case ArrayEndVisitMember: { + JSArray* array = arrayStack.peek(); + JSValue filteredValue = callReviver(array, jsString(m_exec, String::number(indexStack.last())), outValue); + if (filteredValue.isUndefined()) + array->methodTable()->deletePropertyByIndex(array, m_exec, indexStack.last()); + else + array->putDirectIndex(m_exec, indexStack.last(), filteredValue); + if (m_exec->hadException()) + return jsNull(); + indexStack.last()++; + goto arrayStartVisitMember; + } + objectStartState: + case ObjectStartState: { + ASSERT(inValue.isObject()); + ASSERT(!isJSArray(asObject(inValue)) && !asObject(inValue)->inherits(JSArray::info())); + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) + return throwStackOverflowError(m_exec); + + JSObject* object = asObject(inValue); + objectStack.push(object); + indexStack.append(0); + propertyStack.append(PropertyNameArray(m_exec, PropertyNameMode::Strings)); + object->methodTable()->getOwnPropertyNames(object, m_exec, propertyStack.last(), EnumerationMode()); + } + objectStartVisitMember: + FALLTHROUGH; + case ObjectStartVisitMember: { + JSObject* object = objectStack.peek(); + uint32_t index = indexStack.last(); + PropertyNameArray& properties = propertyStack.last(); + if (index == properties.size()) { + outValue = object; + objectStack.pop(); + indexStack.removeLast(); + propertyStack.removeLast(); + break; + } + PropertySlot slot(object); + if (object->methodTable()->getOwnPropertySlot(object, m_exec, properties[index], slot)) + inValue = slot.getValue(m_exec, properties[index]); + else + inValue = jsUndefined(); + + // The holder may be modified by the reviver function so any lookup may throw + if (m_exec->hadException()) + return jsNull(); + + if (inValue.isObject()) { + stateStack.append(ObjectEndVisitMember); + goto stateUnknown; + } else + outValue = inValue; + FALLTHROUGH; + } + case ObjectEndVisitMember: { + JSObject* object = objectStack.peek(); + Identifier prop = propertyStack.last()[indexStack.last()]; + PutPropertySlot slot(object); + JSValue filteredValue = callReviver(object, jsString(m_exec, prop.string()), outValue); + if (filteredValue.isUndefined()) + object->methodTable()->deleteProperty(object, m_exec, prop); + else + object->methodTable()->put(object, m_exec, prop, filteredValue, slot); + if (m_exec->hadException()) + return jsNull(); + indexStack.last()++; + goto objectStartVisitMember; + } + stateUnknown: + case StateUnknown: + if (!inValue.isObject()) { + outValue = inValue; + break; + } + JSObject* object = asObject(inValue); + if (isJSArray(object) || object->inherits(JSArray::info())) + goto arrayStartState; + goto objectStartState; + } + if (stateStack.isEmpty()) + break; + + state = stateStack.last(); + stateStack.removeLast(); + } + JSObject* finalHolder = constructEmptyObject(m_exec); + PutPropertySlot slot(finalHolder); + finalHolder->methodTable()->put(finalHolder, m_exec, m_exec->vm().propertyNames->emptyIdentifier, outValue, slot); + return callReviver(finalHolder, jsEmptyString(m_exec), outValue); +} + +// ECMA-262 v5 15.12.2 +EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec) +{ + if (!exec->argumentCount()) + return throwVMError(exec, createError(exec, ASCIILiteral("JSON.parse requires at least one parameter"))); + StringView source = exec->uncheckedArgument(0).toString(exec)->view(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + + JSValue unfiltered; + LocalScope scope(exec->vm()); + if (source.is8Bit()) { + LiteralParser<LChar> jsonParser(exec, source.characters8(), source.length(), StrictJSON); + unfiltered = jsonParser.tryLiteralParse(); + if (!unfiltered) + return throwVMError(exec, createSyntaxError(exec, jsonParser.getErrorMessage())); + } else { + LiteralParser<UChar> jsonParser(exec, source.characters16(), source.length(), StrictJSON); + unfiltered = jsonParser.tryLiteralParse(); + if (!unfiltered) + return throwVMError(exec, createSyntaxError(exec, jsonParser.getErrorMessage())); + } + + if (exec->argumentCount() < 2) + return JSValue::encode(unfiltered); + + JSValue function = exec->uncheckedArgument(1); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return JSValue::encode(unfiltered); + return JSValue::encode(Walker(exec, Local<JSObject>(exec->vm(), asObject(function)), callType, callData).walk(unfiltered)); +} + +// ECMA-262 v5 15.12.3 +EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec) +{ + if (!exec->argumentCount()) + return throwVMError(exec, createError(exec, ASCIILiteral("No input to stringify"))); + LocalScope scope(exec->vm()); + Local<Unknown> value(exec->vm(), exec->uncheckedArgument(0)); + Local<Unknown> replacer(exec->vm(), exec->argument(1)); + Local<Unknown> space(exec->vm(), exec->argument(2)); + JSValue result = Stringifier(exec, replacer, space).stringify(value).get(); + return JSValue::encode(result); +} + +JSValue JSONParse(ExecState* exec, const String& json) +{ + LocalScope scope(exec->vm()); + + if (json.is8Bit()) { + LiteralParser<LChar> jsonParser(exec, json.characters8(), json.length(), StrictJSON); + return jsonParser.tryLiteralParse(); + } + + LiteralParser<UChar> jsonParser(exec, json.characters16(), json.length(), StrictJSON); + return jsonParser.tryLiteralParse(); +} + +String JSONStringify(ExecState* exec, JSValue value, unsigned indent) +{ + LocalScope scope(exec->vm()); + Local<Unknown> result = Stringifier(exec, Local<Unknown>(exec->vm(), jsNull()), Local<Unknown>(exec->vm(), jsNumber(indent))).stringify(Local<Unknown>(exec->vm(), value)); + if (result.isUndefinedOrNull()) + return String(); + return result.getString(exec); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSONObject.h b/Source/JavaScriptCore/runtime/JSONObject.h new file mode 100644 index 000000000..d550525c9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSONObject.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSONObject_h +#define JSONObject_h + +#include "JSObject.h" + +namespace JSC { + +class Stringifier; + +class JSONObject : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static JSONObject* create(VM& vm, Structure* structure) + { + JSONObject* object = new (NotNull, allocateCell<JSONObject>(vm.heap)) JSONObject(vm, structure); + object->finishCreation(vm); + return object; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + DECLARE_INFO; + +protected: + void finishCreation(VM&); + +private: + JSONObject(VM&, Structure*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +JS_EXPORT_PRIVATE JSValue JSONParse(ExecState*, const String&); +JS_EXPORT_PRIVATE String JSONStringify(ExecState*, JSValue, unsigned indent); + + +} // namespace JSC + +#endif // JSONObject_h diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp new file mode 100644 index 000000000..52dea7801 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -0,0 +1,2823 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003-2006, 2008, 2009, 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel (eric@webkit.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "JSObject.h" + +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" +#include "CopyVisitor.h" +#include "CopyVisitorInlines.h" +#include "CustomGetterSetter.h" +#include "DatePrototype.h" +#include "ErrorConstructor.h" +#include "Exception.h" +#include "Executable.h" +#include "GetterSetter.h" +#include "IndexingHeaderInlines.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "Lookup.h" +#include "NativeErrorConstructor.h" +#include "Nodes.h" +#include "ObjectPrototype.h" +#include "JSCInlines.h" +#include "PropertyDescriptor.h" +#include "PropertyNameArray.h" +#include "Reject.h" +#include "SlotVisitorInlines.h" +#include <math.h> +#include <wtf/Assertions.h> + +namespace JSC { + +// We keep track of the size of the last array after it was grown. We use this +// as a simple heuristic for as the value to grow the next array from size 0. +// This value is capped by the constant FIRST_VECTOR_GROW defined in +// ArrayConventions.h. +static unsigned lastArraySize = 0; + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject); +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject); + +const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; + +const ClassInfo JSObject::s_info = { "Object", 0, 0, CREATE_METHOD_TABLE(JSObject) }; + +const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(JSFinalObject) }; + +static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode, bool didReify) +{ + VM& vm = exec->vm(); + + // Add properties from the static hashtables of properties + for (; classInfo; classInfo = classInfo->parentClass) { + const HashTable* table = classInfo->staticPropHashTable; + if (!table) + continue; + + for (auto iter = table->begin(); iter != table->end(); ++iter) { + if ((!(iter->attributes() & DontEnum) || mode.includeDontEnumProperties()) && !((iter->attributes() & BuiltinOrFunctionOrAccessor) && didReify)) + propertyNames.add(Identifier::fromString(&vm, iter.key())); + } + } +} + +ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butterfly, size_t storageSize) +{ + ASSERT(butterfly); + + Structure* structure = this->structure(); + + size_t propertyCapacity = structure->outOfLineCapacity(); + size_t preCapacity; + size_t indexingPayloadSizeInBytes; + bool hasIndexingHeader = this->hasIndexingHeader(); + if (UNLIKELY(hasIndexingHeader)) { + preCapacity = butterfly->indexingHeader()->preCapacity(structure); + indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); + } else { + preCapacity = 0; + indexingPayloadSizeInBytes = 0; + } + size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + if (visitor.checkIfShouldCopy(butterfly->base(preCapacity, propertyCapacity))) { + Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + + // Copy the properties. + PropertyStorage currentTarget = newButterfly->propertyStorage(); + PropertyStorage currentSource = butterfly->propertyStorage(); + for (size_t count = storageSize; count--;) + (--currentTarget)->setWithoutWriteBarrier((--currentSource)->get()); + + if (UNLIKELY(hasIndexingHeader)) { + *newButterfly->indexingHeader() = *butterfly->indexingHeader(); + + // Copy the array if appropriate. + + WriteBarrier<Unknown>* currentTarget; + WriteBarrier<Unknown>* currentSource; + size_t count; + + switch (this->indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: { + currentTarget = newButterfly->contiguous().data(); + currentSource = butterfly->contiguous().data(); + RELEASE_ASSERT(newButterfly->publicLength() <= newButterfly->vectorLength()); + count = newButterfly->vectorLength(); + break; + } + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage()); + currentTarget = newButterfly->arrayStorage()->m_vector; + currentSource = butterfly->arrayStorage()->m_vector; + count = newButterfly->arrayStorage()->vectorLength(); + break; + } + + default: + currentTarget = 0; + currentSource = 0; + count = 0; + break; + } + + memcpy(currentTarget, currentSource, count * sizeof(EncodedJSValue)); + } + + m_butterfly.setWithoutWriteBarrier(newButterfly); + visitor.didCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes); + } +} + +ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize) +{ + ASSERT(butterfly); + + Structure* structure = this->structure(visitor.vm()); + + size_t propertyCapacity = structure->outOfLineCapacity(); + size_t preCapacity; + size_t indexingPayloadSizeInBytes; + bool hasIndexingHeader = this->hasIndexingHeader(); + if (UNLIKELY(hasIndexingHeader)) { + preCapacity = butterfly->indexingHeader()->preCapacity(structure); + indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); + } else { + preCapacity = 0; + indexingPayloadSizeInBytes = 0; + } + size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + + // Mark the properties. + visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize); + visitor.copyLater( + this, ButterflyCopyToken, + butterfly->base(preCapacity, propertyCapacity), capacityInBytes); + + // Mark the array if appropriate. + switch (this->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + visitor.appendValues(butterfly->contiguous().data(), butterfly->publicLength()); + break; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength()); + if (butterfly->arrayStorage()->m_sparseMap) + visitor.append(&butterfly->arrayStorage()->m_sparseMap); + break; + default: + break; + } +} + +void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); +#if !ASSERT_DISABLED + bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation; + visitor.m_isCheckingForDefaultMarkViolation = false; +#endif + + JSCell::visitChildren(thisObject, visitor); + + Butterfly* butterfly = thisObject->butterfly(); + if (butterfly) + thisObject->visitButterfly(visitor, butterfly, thisObject->structure(visitor.vm())->outOfLineSize()); + +#if !ASSERT_DISABLED + visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; +#endif +} + +void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + if (token != ButterflyCopyToken) + return; + + Butterfly* butterfly = thisObject->butterfly(); + if (butterfly) + thisObject->copyButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); +} + +void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); +#if !ASSERT_DISABLED + bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation; + visitor.m_isCheckingForDefaultMarkViolation = false; +#endif + + JSCell::visitChildren(thisObject, visitor); + + Structure* structure = thisObject->structure(); + Butterfly* butterfly = thisObject->butterfly(); + if (butterfly) + thisObject->visitButterfly(visitor, butterfly, structure->outOfLineSize()); + + size_t storageSize = structure->inlineSize(); + visitor.appendValues(thisObject->inlineStorage(), storageSize); + +#if !ASSERT_DISABLED + visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; +#endif +} + +String JSObject::className(const JSObject* object) +{ + const ClassInfo* info = object->classInfo(); + ASSERT(info); + return info->className; +} + +String JSObject::calculatedClassName(JSObject* object) +{ + String prototypeFunctionName; + ExecState* exec = object->globalObject()->globalExec(); + PropertySlot slot(object->structure()->storedPrototype()); + PropertyName constructor(exec->propertyNames().constructor); + if (object->getPropertySlot(exec, constructor, slot)) { + if (slot.isValue()) { + JSValue constructorValue = slot.getValue(exec, constructor); + if (constructorValue.isCell()) { + if (JSCell* constructorCell = constructorValue.asCell()) { + if (JSObject* ctorObject = constructorCell->getObject()) { + if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(ctorObject)) + prototypeFunctionName = constructorFunction->calculatedDisplayName(exec); + else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(ctorObject)) + prototypeFunctionName = constructorFunction->calculatedDisplayName(exec); + } + } + } + } + } + + if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") { + String tableClassName = object->methodTable()->className(object); + if (!tableClassName.isNull() && tableClassName != "Object") + return tableClassName; + + String classInfoName = object->classInfo()->className; + if (!classInfoName.isNull()) + return classInfoName; + + if (prototypeFunctionName.isNull()) + return ASCIILiteral("Object"); + } + + return prototypeFunctionName; +} + +bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot) +{ + // NB. The fact that we're directly consulting our indexed storage implies that it is not + // legal for anyone to override getOwnPropertySlot() without also overriding + // getOwnPropertySlotByIndex(). + + if (i > MAX_ARRAY_INDEX) + return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); + + switch (thisObject->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + break; + + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->butterfly(); + if (i >= butterfly->vectorLength()) + return false; + + JSValue value = butterfly->contiguous()[i].get(); + if (value) { + slot.setValue(thisObject, None, value); + return true; + } + + return false; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->butterfly(); + if (i >= butterfly->vectorLength()) + return false; + + double value = butterfly->contiguousDouble()[i]; + if (value == value) { + slot.setValue(thisObject, None, JSValue(JSValue::EncodeAsDouble, value)); + return true; + } + + return false; + } + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); + if (i >= storage->length()) + return false; + + if (i < storage->vectorLength()) { + JSValue value = storage->m_vector[i].get(); + if (value) { + slot.setValue(thisObject, None, value); + return true; + } + } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->notFound()) { + it->value.get(thisObject, slot); + return true; + } + } + break; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + + return false; +} + +// ECMA 8.6.2.2 +void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + ASSERT(value); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); + VM& vm = exec->vm(); + + // Try indexed put first. This is required for correctness, since loads on property names that appear like + // valid indices will never look in the named property storage. + if (Optional<uint32_t> index = parseIndex(propertyName)) { + putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode()); + return; + } + + // Check if there are any setters or getters in the prototype chain + JSValue prototype; + if (propertyName != exec->propertyNames().underscoreProto) { + for (JSObject* obj = thisObject; !obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { + prototype = obj->prototype(); + if (prototype.isNull()) { + ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName)); + if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot) + && slot.isStrictMode()) + throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); + return; + } + } + } + + JSObject* obj; + for (obj = thisObject; ; obj = asObject(prototype)) { + unsigned attributes; + PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes); + if (isValidOffset(offset)) { + if (attributes & ReadOnly) { + ASSERT(thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); + if (slot.isStrictMode()) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError))); + return; + } + + JSValue gs = obj->getDirect(offset); + if (gs.isGetterSetter()) { + callSetter(exec, cell, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode); + if (!thisObject->structure()->isDictionary()) + slot.setCacheableSetter(obj, offset); + return; + } + if (gs.isCustomGetterSetter()) { + callCustomSetter(exec, gs, obj, slot.thisValue(), value); + slot.setCustomProperty(obj, jsCast<CustomGetterSetter*>(gs.asCell())->setter()); + return; + } + ASSERT(!(attributes & Accessor)); + + // If there's an existing property on the object or one of its + // prototypes it should be replaced, so break here. + break; + } + const ClassInfo* info = obj->classInfo(); + if (info->hasStaticSetterOrReadonlyProperties()) { + if (const HashTableValue* entry = obj->findPropertyHashEntry(propertyName)) { + if (!obj->staticFunctionsReified() || !(entry->attributes() & BuiltinOrFunctionOrAccessor)) { + putEntry(exec, entry, obj, propertyName, value, slot); + return; + } + } + } + prototype = obj->prototype(); + if (prototype.isNull()) + break; + } + + ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); + if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot) && slot.isStrictMode()) + throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); + return; +} + +void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + + if (propertyName > MAX_ARRAY_INDEX) { + PutPropertySlot slot(cell, shouldThrow); + thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); + return; + } + + switch (thisObject->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + break; + + case ALL_UNDECIDED_INDEXING_TYPES: { + thisObject->convertUndecidedForValue(exec->vm(), value); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + + case ALL_INT32_INDEXING_TYPES: { + if (!value.isInt32()) { + thisObject->convertInt32ForValue(exec->vm(), value); + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + FALLTHROUGH; + } + + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->butterfly(); + if (propertyName >= butterfly->vectorLength()) + break; + butterfly->contiguous()[propertyName].set(exec->vm(), thisObject, value); + if (propertyName >= butterfly->publicLength()) + butterfly->setPublicLength(propertyName + 1); + return; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + if (!value.isNumber()) { + thisObject->convertDoubleToContiguous(exec->vm()); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + thisObject->convertDoubleToContiguous(exec->vm()); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + Butterfly* butterfly = thisObject->butterfly(); + if (propertyName >= butterfly->vectorLength()) + break; + butterfly->contiguousDouble()[propertyName] = valueAsDouble; + if (propertyName >= butterfly->publicLength()) + butterfly->setPublicLength(propertyName + 1); + return; + } + + case NonArrayWithArrayStorage: + case ArrayWithArrayStorage: { + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); + + if (propertyName >= storage->vectorLength()) + break; + + WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName]; + unsigned length = storage->length(); + + // Update length & m_numValuesInVector as necessary. + if (propertyName >= length) { + length = propertyName + 1; + storage->setLength(length); + ++storage->m_numValuesInVector; + } else if (!valueSlot) + ++storage->m_numValuesInVector; + + valueSlot.set(exec->vm(), thisObject, value); + return; + } + + case NonArrayWithSlowPutArrayStorage: + case ArrayWithSlowPutArrayStorage: { + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); + + if (propertyName >= storage->vectorLength()) + break; + + WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName]; + unsigned length = storage->length(); + + // Update length & m_numValuesInVector as necessary. + if (propertyName >= length) { + if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow)) + return; + length = propertyName + 1; + storage->setLength(length); + ++storage->m_numValuesInVector; + } else if (!valueSlot) { + if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow)) + return; + ++storage->m_numValuesInVector; + } + + valueSlot.set(exec->vm(), thisObject, value); + return; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + } + + thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow); +} + +ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage) +{ + SparseArrayValueMap* map = storage->m_sparseMap.get(); + + if (!map) + map = allocateSparseIndexMap(vm); + + if (map->sparseMode()) + return storage; + + map->setSparseMode(); + + unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); + for (unsigned i = 0; i < usedVectorLength; ++i) { + JSValue value = storage->m_vector[i].get(); + // This will always be a new entry in the map, so no need to check we can write, + // and attributes are default so no need to set them. + if (value) + map->add(this, i).iterator->value.set(vm, map, value); + } + + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0)); + RELEASE_ASSERT(newButterfly); + newButterfly->arrayStorage()->m_indexBias = 0; + newButterfly->arrayStorage()->setVectorLength(0); + newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map); + setButterflyWithoutChangingStructure(vm, newButterfly); + + return newButterfly->arrayStorage(); +} + +void JSObject::enterDictionaryIndexingMode(VM& vm) +{ + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize + // this case if we ever cared. + enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, ensureArrayStorageSlow(vm)); + break; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage()); + break; + + default: + break; + } +} + +void JSObject::notifyPresenceOfIndexedAccessors(VM& vm) +{ + if (mayInterceptIndexedAccesses()) + return; + + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AddIndexedAccessors)); + + if (!vm.prototypeMap.isPrototype(this)) + return; + + globalObject()->haveABadTime(vm); +} + +Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length, size_t elementSize) +{ + ASSERT(length < MAX_ARRAY_INDEX); + IndexingType oldType = indexingType(); + ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType)); + ASSERT(!structure()->needsSlowPutIndexing()); + ASSERT(!indexingShouldBeSparse()); + unsigned vectorLength = std::max(length, BASE_VECTOR_LEN); + Butterfly* newButterfly = Butterfly::createOrGrowArrayRight( + m_butterfly.get(), vm, this, structure(), structure()->outOfLineCapacity(), false, 0, + elementSize * vectorLength); + newButterfly->setPublicLength(length); + newButterfly->setVectorLength(vectorLength); + return newButterfly; +} + +Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length) +{ + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateUndecided); + setStructureAndButterfly(vm, newStructure, newButterfly); + return newButterfly; +} + +ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length) +{ + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32); + setStructureAndButterfly(vm, newStructure, newButterfly); + return newButterfly->contiguousInt32(); +} + +ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length) +{ + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double)); + for (unsigned i = newButterfly->vectorLength(); i--;) + newButterfly->contiguousDouble()[i] = PNaN; + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble); + setStructureAndButterfly(vm, newStructure, newButterfly); + return newButterfly->contiguousDouble(); +} + +ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length) +{ + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous); + setStructureAndButterfly(vm, newStructure, newButterfly); + return newButterfly->contiguous(); +} + +ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength) +{ + DeferGC deferGC(vm.heap); + Structure* structure = this->structure(vm); + IndexingType oldType = indexingType(); + ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType)); + Butterfly* newButterfly = Butterfly::createOrGrowArrayRight( + m_butterfly.get(), vm, this, structure, structure->outOfLineCapacity(), false, 0, + ArrayStorage::sizeFor(vectorLength)); + RELEASE_ASSERT(newButterfly); + + ArrayStorage* result = newButterfly->arrayStorage(); + result->setLength(length); + result->setVectorLength(vectorLength); + result->m_sparseMap.clear(); + result->m_numValuesInVector = 0; + result->m_indexBias = 0; + Structure* newStructure = Structure::nonPropertyTransition(vm, structure, structure->suggestedArrayStorageTransition()); + setStructureAndButterfly(vm, newStructure, newButterfly); + return result; +} + +ArrayStorage* JSObject::createInitialArrayStorage(VM& vm) +{ + return createArrayStorage(vm, 0, BASE_VECTOR_LEN); +} + +ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm) +{ + ASSERT(hasUndecided(indexingType())); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32)); + return m_butterfly->contiguousInt32(); +} + +ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm) +{ + ASSERT(hasUndecided(indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) + m_butterfly->contiguousDouble()[i] = PNaN; + + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble)); + return m_butterfly->contiguousDouble(); +} + +ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm) +{ + ASSERT(hasUndecided(indexingType())); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength) +{ + Structure* structure = this->structure(vm); + unsigned publicLength = m_butterfly->publicLength(); + unsigned propertyCapacity = structure->outOfLineCapacity(); + unsigned propertySize = structure->outOfLineSize(); + + Butterfly* newButterfly = Butterfly::createUninitialized( + vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength)); + + memcpy( + newButterfly->propertyStorage() - propertySize, + m_butterfly->propertyStorage() - propertySize, + propertySize * sizeof(EncodedJSValue)); + + ArrayStorage* newStorage = newButterfly->arrayStorage(); + newStorage->setVectorLength(neededLength); + newStorage->setLength(publicLength); + newStorage->m_sparseMap.clear(); + newStorage->m_indexBias = 0; + newStorage->m_numValuesInVector = 0; + + return newStorage; +} + +ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition) +{ + DeferGC deferGC(vm.heap); + ASSERT(hasUndecided(indexingType())); + + unsigned vectorLength = m_butterfly->vectorLength(); + ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength); + // No need to copy elements. + + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + setStructureAndButterfly(vm, newStructure, storage->butterfly()); + return storage; +} + +ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm) +{ + return convertUndecidedToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); +} + +ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm) +{ + ASSERT(hasInt32(indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) { + WriteBarrier<Unknown>* current = &m_butterfly->contiguousInt32()[i]; + double* currentAsDouble = bitwise_cast<double*>(current); + JSValue v = current->get(); + if (!v) { + *currentAsDouble = PNaN; + continue; + } + ASSERT(v.isInt32()); + *currentAsDouble = v.asInt32(); + } + + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble)); + return m_butterfly->contiguousDouble(); +} + +ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm) +{ + ASSERT(hasInt32(indexingType())); + + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition) +{ + DeferGC deferGC(vm.heap); + ASSERT(hasInt32(indexingType())); + + unsigned vectorLength = m_butterfly->vectorLength(); + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength); + for (unsigned i = 0; i < m_butterfly->publicLength(); i++) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (v) { + newStorage->m_vector[i].setWithoutWriteBarrier(v); + newStorage->m_numValuesInVector++; + } else + ASSERT(newStorage->m_vector[i].get().isEmpty()); + } + + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); + return newStorage; +} + +ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm) +{ + return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); +} + +ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm) +{ + ASSERT(hasDouble(indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) { + double* current = &m_butterfly->contiguousDouble()[i]; + WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current); + double value = *current; + if (value != value) { + currentAsValue->clear(); + continue; + } + JSValue v = JSValue(JSValue::EncodeAsDouble, value); + currentAsValue->setWithoutWriteBarrier(v); + } + + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition) +{ + DeferGC deferGC(vm.heap); + ASSERT(hasDouble(indexingType())); + + unsigned vectorLength = m_butterfly->vectorLength(); + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength); + for (unsigned i = 0; i < m_butterfly->publicLength(); i++) { + double value = m_butterfly->contiguousDouble()[i]; + if (value == value) { + newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value)); + newStorage->m_numValuesInVector++; + } else + ASSERT(newStorage->m_vector[i].get().isEmpty()); + } + + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); + return newStorage; +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm) +{ + return convertDoubleToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); +} + +ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition) +{ + DeferGC deferGC(vm.heap); + ASSERT(hasContiguous(indexingType())); + + unsigned vectorLength = m_butterfly->vectorLength(); + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength); + for (unsigned i = 0; i < m_butterfly->publicLength(); i++) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (v) { + newStorage->m_vector[i].setWithoutWriteBarrier(v); + newStorage->m_numValuesInVector++; + } else + ASSERT(newStorage->m_vector[i].get().isEmpty()); + } + + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); + return newStorage; +} + +ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm) +{ + return convertContiguousToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); +} + +void JSObject::convertUndecidedForValue(VM& vm, JSValue value) +{ + if (value.isInt32()) { + convertUndecidedToInt32(vm); + return; + } + + if (value.isDouble() && value.asNumber() == value.asNumber()) { + convertUndecidedToDouble(vm); + return; + } + + convertUndecidedToContiguous(vm); +} + +void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value) +{ + if (value.isInt32()) { + createInitialInt32(vm, index + 1)[index].set(vm, this, value); + return; + } + + if (value.isDouble()) { + double doubleValue = value.asNumber(); + if (doubleValue == doubleValue) { + createInitialDouble(vm, index + 1)[index] = doubleValue; + return; + } + } + + createInitialContiguous(vm, index + 1)[index].set(vm, this, value); +} + +void JSObject::convertInt32ForValue(VM& vm, JSValue value) +{ + ASSERT(!value.isInt32()); + + if (value.isDouble() && !std::isnan(value.asDouble())) { + convertInt32ToDouble(vm); + return; + } + + convertInt32ToContiguous(vm); +} + +void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value) +{ + ASSERT(index < m_butterfly->publicLength()); + ASSERT(index < m_butterfly->vectorLength()); + convertUndecidedForValue(vm, value); + setIndexQuickly(vm, index, value); +} + +void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value) +{ + ASSERT(!value.isInt32()); + convertInt32ForValue(vm, value); + setIndexQuickly(vm, index, value); +} + +void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned index, JSValue value) +{ + ASSERT(!value.isNumber() || value.asNumber() != value.asNumber()); + convertDoubleToContiguous(vm); + setIndexQuickly(vm, index, value); +} + +ContiguousJSValues JSObject::ensureInt32Slow(VM& vm) +{ + ASSERT(inherits(info())); + + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) + return ContiguousJSValues(); + return createInitialInt32(vm, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToInt32(vm); + + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return ContiguousJSValues(); + + default: + CRASH(); + return ContiguousJSValues(); + } +} + +ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm) +{ + ASSERT(inherits(info())); + + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) + return ContiguousDoubles(); + return createInitialDouble(vm, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToDouble(vm); + + case ALL_INT32_INDEXING_TYPES: + return convertInt32ToDouble(vm); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return ContiguousDoubles(); + + default: + CRASH(); + return ContiguousDoubles(); + } +} + +ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm) +{ + ASSERT(inherits(info())); + + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) + return ContiguousJSValues(); + return createInitialContiguous(vm, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToContiguous(vm); + + case ALL_INT32_INDEXING_TYPES: + return convertInt32ToContiguous(vm); + + case ALL_DOUBLE_INDEXING_TYPES: + return convertDoubleToContiguous(vm); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return ContiguousJSValues(); + + default: + CRASH(); + return ContiguousJSValues(); + } +} + +ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm) +{ + ASSERT(inherits(info())); + + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse())) + return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm); + return createInitialArrayStorage(vm); + + case ALL_UNDECIDED_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure(vm)->needsSlowPutIndexing()); + return convertUndecidedToArrayStorage(vm); + + case ALL_INT32_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure(vm)->needsSlowPutIndexing()); + return convertInt32ToArrayStorage(vm); + + case ALL_DOUBLE_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure(vm)->needsSlowPutIndexing()); + return convertDoubleToArrayStorage(vm); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure(vm)->needsSlowPutIndexing()); + return convertContiguousToArrayStorage(vm); + + default: + RELEASE_ASSERT_NOT_REACHED(); + return 0; + } +} + +ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm) +{ + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: { + createArrayStorage(vm, 0, 0); + SparseArrayValueMap* map = allocateSparseIndexMap(vm); + map->setSparseMode(); + return arrayStorage(); + } + + case ALL_UNDECIDED_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertUndecidedToArrayStorage(vm)); + + case ALL_INT32_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertInt32ToArrayStorage(vm)); + + case ALL_DOUBLE_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertDoubleToArrayStorage(vm)); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm)); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage()); + + default: + CRASH(); + return 0; + } +} + +void JSObject::switchToSlowPutArrayStorage(VM& vm) +{ + switch (indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: + convertUndecidedToArrayStorage(vm, AllocateSlowPutArrayStorage); + break; + + case ALL_INT32_INDEXING_TYPES: + convertInt32ToArrayStorage(vm, AllocateSlowPutArrayStorage); + break; + + case ALL_DOUBLE_INDEXING_TYPES: + convertDoubleToArrayStorage(vm, AllocateSlowPutArrayStorage); + break; + + case ALL_CONTIGUOUS_INDEXING_TYPES: + convertContiguousToArrayStorage(vm, AllocateSlowPutArrayStorage); + break; + + case NonArrayWithArrayStorage: + case ArrayWithArrayStorage: { + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), SwitchToSlowPutArrayStorage); + setStructure(vm, newStructure); + break; + } + + default: + CRASH(); + break; + } +} + +void JSObject::setPrototype(VM& vm, JSValue prototype) +{ + ASSERT(prototype); + if (prototype.isObject()) + vm.prototypeMap.addPrototype(asObject(prototype)); + + Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype); + setStructure(vm, newStructure); + + if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses()) + return; + + if (vm.prototypeMap.isPrototype(this)) { + newStructure->globalObject()->haveABadTime(vm); + return; + } + + if (!hasIndexedProperties(indexingType())) + return; + + if (shouldUseSlowPut(indexingType())) + return; + + switchToSlowPutArrayStorage(vm); +} + +bool JSObject::setPrototypeWithCycleCheck(ExecState* exec, JSValue prototype) +{ + ASSERT(methodTable(exec->vm())->toThis(this, exec, NotStrictMode) == this); + JSValue nextPrototype = prototype; + while (nextPrototype && nextPrototype.isObject()) { + if (nextPrototype == this) + return false; + nextPrototype = asObject(nextPrototype)->prototype(); + } + setPrototype(exec->vm(), prototype); + return true; +} + +bool JSObject::allowsAccessFrom(ExecState* exec) +{ + JSGlobalObject* globalObject = this->globalObject(); + return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec); +} + +void JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter) +{ + PropertyDescriptor descriptor; + descriptor.setGetter(getter); + descriptor.setEnumerable(true); + descriptor.setConfigurable(true); + defineOwnProperty(this, exec, propertyName, descriptor, false); +} + +void JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter) +{ + PropertyDescriptor descriptor; + descriptor.setSetter(setter); + descriptor.setEnumerable(true); + descriptor.setConfigurable(true); + defineOwnProperty(this, exec, propertyName, descriptor, false); +} + +void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes) +{ + ASSERT(value.isGetterSetter() && (attributes & Accessor)); + + if (Optional<uint32_t> index = parseIndex(propertyName)) { + putDirectIndex(exec, index.value(), value, attributes, PutDirectIndexLikePutDirect); + return; + } + + putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes); +} + +void JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) +{ + ASSERT(!parseIndex(propertyName)); + + PutPropertySlot slot(this); + putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot); + + ASSERT(slot.type() == PutPropertySlot::NewProperty); + + Structure* structure = this->structure(vm); + if (attributes & ReadOnly) + structure->setContainsReadOnlyProperties(); + structure->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto); +} + +void JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) +{ + PutPropertySlot slot(this); + putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot); + + Structure* structure = this->structure(vm); + if (attributes & ReadOnly) + structure->setContainsReadOnlyProperties(); + + structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto); +} + +bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const +{ + PropertySlot slot(this); + return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); +} + +bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const +{ + PropertySlot slot(this); + return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); +} + +// ECMA 8.6.2.5 +bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + + if (Optional<uint32_t> index = parseIndex(propertyName)) + return thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, index.value()); + + if (!thisObject->staticFunctionsReified()) + thisObject->reifyStaticFunctionsForDelete(exec); + + unsigned attributes; + VM& vm = exec->vm(); + if (isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes))) { + if (attributes & DontDelete && !vm.isInDefineOwnProperty()) + return false; + thisObject->removeDirect(vm, propertyName); + return true; + } + + // Look in the static hashtable of properties + const HashTableValue* entry = thisObject->findPropertyHashEntry(propertyName); + if (entry) { + if (entry->attributes() & DontDelete && !vm.isInDefineOwnProperty()) + return false; // this builtin property can't be deleted + + PutPropertySlot slot(thisObject); + if (!(entry->attributes() & BuiltinOrFunctionOrAccessor)) { + ASSERT(thisObject->staticFunctionsReified()); + putEntry(exec, entry, thisObject, propertyName, jsUndefined(), slot); + } + } + + return true; +} + +bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const +{ + PropertySlot slot(this); + return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot); +} + +bool JSObject::hasOwnProperty(ExecState* exec, unsigned propertyName) const +{ + PropertySlot slot(this); + return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlotByIndex(const_cast<JSObject*>(this), exec, propertyName, slot); +} + +bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + + if (i > MAX_ARRAY_INDEX) + return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i)); + + switch (thisObject->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return true; + + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->butterfly(); + if (i >= butterfly->vectorLength()) + return true; + butterfly->contiguous()[i].clear(); + return true; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->butterfly(); + if (i >= butterfly->vectorLength()) + return true; + butterfly->contiguousDouble()[i] = PNaN; + return true; + } + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); + + if (i < storage->vectorLength()) { + WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; + if (valueSlot) { + valueSlot.clear(); + --storage->m_numValuesInVector; + } + } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { + SparseArrayValueMap::iterator it = map->find(i); + if (it != map->notFound()) { + if (it->value.attributes & DontDelete) + return false; + map->remove(it); + } + } + + return true; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } +} + +static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, PropertyName propertyName) +{ + JSValue function = object->get(exec, propertyName); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return exec->exception(); + + // Prevent "toString" and "valueOf" from observing execution if an exception + // is pending. + if (exec->hadException()) + return exec->exception(); + + JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList()); + ASSERT(!result.isGetterSetter()); + if (exec->hadException()) + return exec->exception(); + if (result.isObject()) + return JSValue(); + return result; +} + +bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const +{ + result = methodTable(exec->vm())->defaultValue(this, exec, PreferNumber); + number = result.toNumber(exec); + return !result.isString(); +} + +// ECMA 8.6.2.6 +JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint) +{ + // Make sure that whatever default value methods there are on object's prototype chain are + // being watched. + object->structure()->startWatchingInternalPropertiesIfNecessaryForEntireChain(exec->vm()); + + // Must call toString first for Date objects. + if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) { + JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString); + if (value) + return value; + value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf); + if (value) + return value; + } else { + JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf); + if (value) + return value; + value = callDefaultValueFunction(exec, object, exec->propertyNames().toString); + if (value) + return value; + } + + ASSERT(!exec->hadException()); + + return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("No default value"))); +} + +const HashTableValue* JSObject::findPropertyHashEntry(PropertyName propertyName) const +{ + for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { + if (const HashTable* propHashTable = info->staticPropHashTable) { + if (const HashTableValue* entry = propHashTable->entry(propertyName)) + return entry; + } + } + return 0; +} + +bool JSObject::hasInstance(ExecState* exec, JSValue value) +{ + VM& vm = exec->vm(); + TypeInfo info = structure(vm)->typeInfo(); + if (info.implementsDefaultHasInstance()) + return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype)); + if (info.implementsHasInstance()) + return methodTable(vm)->customHasInstance(this, exec, value); + vm.throwException(exec, createInvalidInstanceofParameterError(exec, this)); + return false; +} + +bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto) +{ + if (!value.isObject()) + return false; + + if (!proto.isObject()) { + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("instanceof called on an object with an invalid prototype property."))); + return false; + } + + JSObject* object = asObject(value); + while ((object = object->prototype().getObject())) { + if (proto == object) + return true; + } + return false; +} + +void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, propertyNames, mode); + + if (object->prototype().isNull()) + return; + + VM& vm = exec->vm(); + JSObject* prototype = asObject(object->prototype()); + while(1) { + if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) { + prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode); + break; + } + prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode); + JSValue nextProto = prototype->prototype(); + if (nextProto.isNull()) + break; + prototype = asObject(nextProto); + } +} + +void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (!mode.includeJSObjectProperties()) { + // We still have to get non-indexed properties from any subclasses of JSObject that have them. + object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); + return; + } + + if (propertyNames.includeStringProperties()) { + // Add numeric properties first. That appears to be the accepted convention. + // FIXME: Filling PropertyNameArray with an identifier for every integer + // is incredibly inefficient for large arrays. We need a different approach, + // which almost certainly means a different structure for PropertyNameArray. + switch (object->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + break; + + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = object->butterfly(); + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + if (!butterfly->contiguous()[i]) + continue; + propertyNames.add(i); + } + break; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = object->butterfly(); + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + double value = butterfly->contiguousDouble()[i]; + if (value != value) + continue; + propertyNames.add(i); + } + break; + } + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = object->m_butterfly->arrayStorage(); + + unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); + for (unsigned i = 0; i < usedVectorLength; ++i) { + if (storage->m_vector[i]) + propertyNames.add(i); + } + + if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { + Vector<unsigned, 0, UnsafeVectorOverflow> keys; + keys.reserveInitialCapacity(map->size()); + + SparseArrayValueMap::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { + if (mode.includeDontEnumProperties() || !(it->value.attributes & DontEnum)) + keys.uncheckedAppend(static_cast<unsigned>(it->key)); + } + + std::sort(keys.begin(), keys.end()); + for (unsigned i = 0; i < keys.size(); ++i) + propertyNames.add(keys[i]); + } + break; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } + + object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); +} + +void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified()); + + if (!mode.includeJSObjectProperties()) + return; + + VM& vm = exec->vm(); + object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode); +} + +double JSObject::toNumber(ExecState* exec) const +{ + JSValue primitive = toPrimitive(exec, PreferNumber); + if (exec->hadException()) // should be picked up soon in Nodes.cpp + return 0.0; + return primitive.toNumber(exec); +} + +JSString* JSObject::toString(ExecState* exec) const +{ + JSValue primitive = toPrimitive(exec, PreferString); + if (exec->hadException()) + return jsEmptyString(exec); + return primitive.toString(exec); +} + +JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode) +{ + return jsCast<JSObject*>(cell); +} + +void JSObject::seal(VM& vm) +{ + if (isSealed(vm)) + return; + preventExtensions(vm); + setStructure(vm, Structure::sealTransition(vm, structure(vm))); +} + +void JSObject::freeze(VM& vm) +{ + if (isFrozen(vm)) + return; + preventExtensions(vm); + setStructure(vm, Structure::freezeTransition(vm, structure(vm))); +} + +void JSObject::preventExtensions(VM& vm) +{ + enterDictionaryIndexingMode(vm); + if (isExtensible()) + setStructure(vm, Structure::preventExtensionsTransition(vm, structure(vm))); +} + +// This presently will flatten to an uncachable dictionary; this is suitable +// for use in delete, we may want to do something different elsewhere. +void JSObject::reifyStaticFunctionsForDelete(ExecState* exec) +{ + ASSERT(!staticFunctionsReified()); + VM& vm = exec->vm(); + + // If this object's ClassInfo has no static properties, then nothing to reify! + // We can safely set the flag to avoid the expensive check again in the future. + if (!classInfo()->hasStaticProperties()) { + structure(vm)->setStaticFunctionsReified(true); + return; + } + + if (!structure(vm)->isUncacheableDictionary()) + setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure(vm))); + + for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { + const HashTable* hashTable = info->staticPropHashTable; + if (!hashTable) + continue; + PropertySlot slot(this); + for (auto iter = hashTable->begin(); iter != hashTable->end(); ++iter) { + if (iter->attributes() & BuiltinOrFunctionOrAccessor) + setUpStaticFunctionSlot(globalObject()->globalExec(), iter.value(), this, Identifier::fromString(&vm, iter.key()), slot); + } + } + + structure(vm)->setStaticFunctionsReified(true); +} + +bool JSObject::removeDirect(VM& vm, PropertyName propertyName) +{ + Structure* structure = this->structure(vm); + if (!isValidOffset(structure->get(vm, propertyName))) + return false; + + PropertyOffset offset; + if (structure->isUncacheableDictionary()) { + offset = structure->removePropertyWithoutTransition(vm, propertyName); + if (offset == invalidOffset) + return false; + putDirectUndefined(offset); + return true; + } + + setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset)); + if (offset == invalidOffset) + return false; + putDirectUndefined(offset); + return true; +} + +NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue getterSetter, unsigned attributes, PropertyOffset offset) +{ + if (structure()->isDictionary()) { + slot.setGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter)); + return; + } + slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(getterSetter), offset); +} + +void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor) +{ + VM& vm = exec->vm(); + auto map = m_butterfly->arrayStorage()->m_sparseMap.get(); + + if (descriptor.isDataDescriptor()) { + if (descriptor.value()) + entryInMap->set(vm, map, descriptor.value()); + else if (oldDescriptor.isAccessorDescriptor()) + entryInMap->set(vm, map, jsUndefined()); + entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor; + return; + } + + if (descriptor.isAccessorDescriptor()) { + JSObject* getter = 0; + if (descriptor.getterPresent()) + getter = descriptor.getterObject(); + else if (oldDescriptor.isAccessorDescriptor()) + getter = oldDescriptor.getterObject(); + JSObject* setter = 0; + if (descriptor.setterPresent()) + setter = descriptor.setterObject(); + else if (oldDescriptor.isAccessorDescriptor()) + setter = oldDescriptor.setterObject(); + + GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject()); + if (getter) + accessor->setGetter(vm, exec->lexicalGlobalObject(), getter); + if (setter) + accessor->setSetter(vm, exec->lexicalGlobalObject(), setter); + + entryInMap->set(vm, map, accessor); + entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly; + return; + } + + ASSERT(descriptor.isGenericDescriptor()); + entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor); +} + +// Defined in ES5.1 8.12.9 +bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException) +{ + ASSERT(index <= MAX_ARRAY_INDEX); + + if (!inSparseIndexingMode()) { + // Fast case: we're putting a regular property to a regular array + // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false + // however if the property currently exists missing attributes will override from their current 'true' + // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode'). + if (!descriptor.attributes()) { + ASSERT(!descriptor.isAccessorDescriptor()); + return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); + } + + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->vm()); + } + + if (descriptor.attributes() & (ReadOnly | Accessor)) + notifyPresenceOfIndexedAccessors(exec->vm()); + + SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get(); + RELEASE_ASSERT(map); + + // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. + SparseArrayValueMap::AddResult result = map->add(this, index); + SparseArrayEntry* entryInMap = &result.iterator->value; + + // 2. Let extensible be the value of the [[Extensible]] internal property of O. + // 3. If current is undefined and extensible is false, then Reject. + // 4. If current is undefined and extensible is true, then + if (result.isNewEntry) { + if (!isExtensible()) { + map->remove(result.iterator); + return reject(exec, throwException, "Attempting to define property on object that is not extensible."); + } + + // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property + // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values + // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly + // created property is set to its default value. + // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of + // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by + // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property + // is set to its default value. + // 4.c. Return true. + + PropertyDescriptor defaults; + entryInMap->setWithoutWriteBarrier(jsUndefined()); + entryInMap->attributes = DontDelete | DontEnum | ReadOnly; + entryInMap->get(defaults); + + putIndexedDescriptor(exec, entryInMap, descriptor, defaults); + if (index >= m_butterfly->arrayStorage()->length()) + m_butterfly->arrayStorage()->setLength(index + 1); + return true; + } + + // 5. Return true, if every field in Desc is absent. + // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12). + PropertyDescriptor current; + entryInMap->get(current); + if (descriptor.isEmpty() || descriptor.equalTo(exec, current)) + return true; + + // 7. If the [[Configurable]] field of current is false then + if (!current.configurable()) { + // 7.a. Reject, if the [[Configurable]] field of Desc is true. + if (descriptor.configurablePresent() && descriptor.configurable()) + return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property."); + // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other. + if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable()) + return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property."); + } + + // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required. + if (!descriptor.isGenericDescriptor()) { + // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then + if (current.isDataDescriptor() != descriptor.isDataDescriptor()) { + // 9.a. Reject, if the [[Configurable]] field of current is false. + if (!current.configurable()) + return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property."); + // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a + // data property to an accessor property. Preserve the existing values of the converted property's + // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to + // their default values. + // 9.c. Else, convert the property named P of object O from an accessor property to a data property. + // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] + // attributes and set the rest of the property's attributes to their default values. + } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) { + // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then + // 10.a. If the [[Configurable]] field of current is false, then + if (!current.configurable() && !current.writable()) { + // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true. + if (descriptor.writable()) + return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property."); + // 10.a.ii. If the [[Writable]] field of current is false, then + // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false. + if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value())) + return reject(exec, throwException, "Attempting to change value of a readonly property."); + } + // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable. + } else { + ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent()); + // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then + if (!current.configurable()) { + // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false. + if (descriptor.setterPresent() && descriptor.setter() != current.setter()) + return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property."); + // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false. + if (descriptor.getterPresent() && descriptor.getter() != current.getter()) + return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property."); + } + } + } + + // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field. + putIndexedDescriptor(exec, entryInMap, descriptor, current); + // 13. Return true. + return true; +} + +SparseArrayValueMap* JSObject::allocateSparseIndexMap(VM& vm) +{ + SparseArrayValueMap* result = SparseArrayValueMap::create(vm); + arrayStorage()->m_sparseMap.set(vm, this, result); + return result; +} + +void JSObject::deallocateSparseIndexMap() +{ + if (ArrayStorage* arrayStorage = arrayStorageOrNull()) + arrayStorage->m_sparseMap.clear(); +} + +bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, JSValue thisValue, unsigned i, JSValue value, bool shouldThrow) +{ + for (JSObject* current = this; ;) { + // This has the same behavior with respect to prototypes as JSObject::put(). It only + // allows a prototype to intercept a put if (a) the prototype declares the property + // we're after rather than intercepting it via an override of JSObject::put(), and + // (b) that property is declared as ReadOnly or Accessor. + + ArrayStorage* storage = current->arrayStorageOrNull(); + if (storage && storage->m_sparseMap) { + SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i); + if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes & (Accessor | ReadOnly))) { + iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow); + return true; + } + } + + JSValue prototypeValue = current->prototype(); + if (prototypeValue.isNull()) + return false; + + current = asObject(prototypeValue); + } +} + +bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) +{ + JSValue prototypeValue = prototype(); + if (prototypeValue.isNull()) + return false; + + return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow); +} + +template<IndexingType indexingShape> +void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value) +{ + ASSERT((indexingType() & IndexingShapeMask) == indexingShape); + ASSERT(!indexingShouldBeSparse()); + + // For us to get here, the index is either greater than the public length, or greater than + // or equal to the vector length. + ASSERT(i >= m_butterfly->vectorLength()); + + VM& vm = exec->vm(); + + if (i >= MAX_ARRAY_INDEX - 1 + || (i >= MIN_SPARSE_ARRAY_INDEX + && !isDenseEnoughForVector(i, countElements<indexingShape>(butterfly()))) + || indexIsSufficientlyBeyondLengthForSparseMap(i, m_butterfly->vectorLength())) { + ASSERT(i <= MAX_ARRAY_INDEX); + ensureArrayStorageSlow(vm); + SparseArrayValueMap* map = allocateSparseIndexMap(vm); + map->putEntry(exec, this, i, value, false); + ASSERT(i >= arrayStorage()->length()); + arrayStorage()->setLength(i + 1); + return; + } + + ensureLength(vm, i + 1); + + RELEASE_ASSERT(i < m_butterfly->vectorLength()); + switch (indexingShape) { + case Int32Shape: + ASSERT(value.isInt32()); + m_butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value); + break; + + case DoubleShape: { + ASSERT(value.isNumber()); + double valueAsDouble = value.asNumber(); + ASSERT(valueAsDouble == valueAsDouble); + m_butterfly->contiguousDouble()[i] = valueAsDouble; + break; + } + + case ContiguousShape: + m_butterfly->contiguous()[i].set(vm, this, value); + break; + + default: + CRASH(); + } +} + +// Explicit instantiations needed by JSArray.cpp. +template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(ExecState*, unsigned, JSValue); +template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(ExecState*, unsigned, JSValue); +template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(ExecState*, unsigned, JSValue); + +void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) +{ + VM& vm = exec->vm(); + + // i should be a valid array index that is outside of the current vector. + ASSERT(i <= MAX_ARRAY_INDEX); + ASSERT(i >= storage->vectorLength()); + + SparseArrayValueMap* map = storage->m_sparseMap.get(); + + // First, handle cases where we don't currently have a sparse map. + if (LIKELY(!map)) { + // If the array is not extensible, we should have entered dictionary mode, and created the sparse map. + ASSERT(isExtensible()); + + // Update m_length if necessary. + if (i >= storage->length()) + storage->setLength(i + 1); + + // Check that it is sensible to still be using a vector, and then try to grow the vector. + if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()) + && isDenseEnoughForVector(i, storage->m_numValuesInVector) + && increaseVectorLength(vm, i + 1))) { + // success! - reread m_storage since it has likely been reallocated, and store to the vector. + storage = arrayStorage(); + storage->m_vector[i].set(vm, this, value); + ++storage->m_numValuesInVector; + return; + } + // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. + map = allocateSparseIndexMap(exec->vm()); + map->putEntry(exec, this, i, value, shouldThrow); + return; + } + + // Update m_length if necessary. + unsigned length = storage->length(); + if (i >= length) { + // Prohibit growing the array if length is not writable. + if (map->lengthIsReadOnly() || !isExtensible()) { + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + length = i + 1; + storage->setLength(length); + } + + // We are currently using a map - check whether we still want to be doing so. + // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. + unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); + if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length)) { + map->putEntry(exec, this, i, value, shouldThrow); + return; + } + + // Reread m_storage after increaseVectorLength, update m_numValuesInVector. + storage = arrayStorage(); + storage->m_numValuesInVector = numValuesInArray; + + // Copy all values from the map into the vector, and delete the map. + WriteBarrier<Unknown>* vector = storage->m_vector; + SparseArrayValueMap::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) + vector[it->key].set(vm, this, it->value.getNonSparseMode()); + deallocateSparseIndexMap(); + + // Store the new property into the vector. + WriteBarrier<Unknown>& valueSlot = vector[i]; + if (!valueSlot) + ++storage->m_numValuesInVector; + valueSlot.set(vm, this, value); +} + +void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) +{ + VM& vm = exec->vm(); + + // i should be a valid array index that is outside of the current vector. + ASSERT(i <= MAX_ARRAY_INDEX); + + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: { + if (indexingShouldBeSparse()) { + putByIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, shouldThrow, + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); + break; + } + if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) { + putByIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0)); + break; + } + if (structure(vm)->needsSlowPutIndexing()) { + // Convert the indexing type to the SlowPutArrayStorage and retry. + createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1)); + putByIndex(this, exec, i, value, shouldThrow); + break; + } + + createInitialForValueAndSet(vm, i, value); + break; + } + + case ALL_UNDECIDED_INDEXING_TYPES: { + CRASH(); + break; + } + + case ALL_INT32_INDEXING_TYPES: { + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value); + break; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value); + break; + } + + case ALL_CONTIGUOUS_INDEXING_TYPES: { + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value); + break; + } + + case NonArrayWithSlowPutArrayStorage: + case ArrayWithSlowPutArrayStorage: { + // No own property present in the vector, but there might be in the sparse map! + SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get(); + if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow)) + return; + FALLTHROUGH; + } + + case NonArrayWithArrayStorage: + case ArrayWithArrayStorage: + putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage()); + break; + + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage) +{ + VM& vm = exec->vm(); + + // i should be a valid array index that is outside of the current vector. + ASSERT(hasAnyArrayStorage(indexingType())); + ASSERT(arrayStorage() == storage); + ASSERT(i >= storage->vectorLength() || attributes); + ASSERT(i <= MAX_ARRAY_INDEX); + + SparseArrayValueMap* map = storage->m_sparseMap.get(); + + // First, handle cases where we don't currently have a sparse map. + if (LIKELY(!map)) { + // If the array is not extensible, we should have entered dictionary mode, and created the spare map. + ASSERT(isExtensible()); + + // Update m_length if necessary. + if (i >= storage->length()) + storage->setLength(i + 1); + + // Check that it is sensible to still be using a vector, and then try to grow the vector. + if (LIKELY( + !attributes + && (isDenseEnoughForVector(i, storage->m_numValuesInVector)) + && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength())) + && increaseVectorLength(vm, i + 1)) { + // success! - reread m_storage since it has likely been reallocated, and store to the vector. + storage = arrayStorage(); + storage->m_vector[i].set(vm, this, value); + ++storage->m_numValuesInVector; + return true; + } + // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. + map = allocateSparseIndexMap(exec->vm()); + return map->putDirect(exec, this, i, value, attributes, mode); + } + + // Update m_length if necessary. + unsigned length = storage->length(); + if (i >= length) { + if (mode != PutDirectIndexLikePutDirect) { + // Prohibit growing the array if length is not writable. + if (map->lengthIsReadOnly()) + return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError); + if (!isExtensible()) + return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible."); + } + length = i + 1; + storage->setLength(length); + } + + // We are currently using a map - check whether we still want to be doing so. + // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. + unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); + if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length)) + return map->putDirect(exec, this, i, value, attributes, mode); + + // Reread m_storage after increaseVectorLength, update m_numValuesInVector. + storage = arrayStorage(); + storage->m_numValuesInVector = numValuesInArray; + + // Copy all values from the map into the vector, and delete the map. + WriteBarrier<Unknown>* vector = storage->m_vector; + SparseArrayValueMap::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) + vector[it->key].set(vm, this, it->value.getNonSparseMode()); + deallocateSparseIndexMap(); + + // Store the new property into the vector. + WriteBarrier<Unknown>& valueSlot = vector[i]; + if (!valueSlot) + ++storage->m_numValuesInVector; + valueSlot.set(vm, this, value); + return true; +} + +bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode) +{ + VM& vm = exec->vm(); + + // i should be a valid array index that is outside of the current vector. + ASSERT(i <= MAX_ARRAY_INDEX); + + if (attributes & (ReadOnly | Accessor)) + notifyPresenceOfIndexedAccessors(vm); + + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: { + if (indexingShouldBeSparse() || attributes) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); + } + if (i >= MIN_SPARSE_ARRAY_INDEX) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0)); + } + if (structure(vm)->needsSlowPutIndexing()) { + ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1)); + storage->m_vector[i].set(vm, this, value); + storage->m_numValuesInVector++; + return true; + } + + createInitialForValueAndSet(vm, i, value); + return true; + } + + case ALL_UNDECIDED_INDEXING_TYPES: { + convertUndecidedForValue(exec->vm(), value); + // Reloop. + return putDirectIndex(exec, i, value, attributes, mode); + } + + case ALL_INT32_INDEXING_TYPES: { + if (attributes) { + if (i < m_butterfly->vectorLength()) + return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); + return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertInt32ToArrayStorage(vm)); + } + if (!value.isInt32()) { + convertInt32ForValue(vm, value); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value); + return true; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + if (attributes) { + if (i < m_butterfly->vectorLength()) + return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); + return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertDoubleToArrayStorage(vm)); + } + if (!value.isNumber()) { + convertDoubleToContiguous(vm); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + convertDoubleToContiguous(vm); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value); + return true; + } + + case ALL_CONTIGUOUS_INDEXING_TYPES: { + if (attributes) { + if (i < m_butterfly->vectorLength()) + return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); + return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertContiguousToArrayStorage(vm)); + } + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value); + return true; + } + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + if (attributes) { + if (i < m_butterfly->vectorLength()) + return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); + } + return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage()); + + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } +} + +void JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes) +{ + StringImpl* name = propertyName.publicName(); + if (!name) + name = vm.propertyNames->anonymous.impl(); + ASSERT(name); + + JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic); + putDirect(vm, propertyName, function, attributes); +} + +JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes) +{ + StringImpl* name = propertyName.publicName(); + if (!name) + name = vm.propertyNames->anonymous.impl(); + ASSERT(name); + JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject); + putDirect(vm, propertyName, function, attributes); + return function; +} + +JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes) +{ + StringImpl* name = propertyName.publicName(); + if (!name) + name = vm.propertyNames->anonymous.impl(); + ASSERT(name); + JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject); + putDirectWithoutTransition(vm, propertyName, function, attributes); + return function; +} + +void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes) +{ + StringImpl* name = propertyName.publicName(); + ASSERT(name); + + JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic); + putDirectWithoutTransition(vm, propertyName, function, attributes); +} + +ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength) +{ + ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH); + + unsigned increasedLength; + unsigned maxInitLength = std::min(currentLength, 100000U); + + if (desiredLength < maxInitLength) + increasedLength = maxInitLength; + else if (!currentVectorLength) + increasedLength = std::max(desiredLength, lastArraySize); + else { + increasedLength = timesThreePlusOneDividedByTwo(desiredLength); + } + + ASSERT(increasedLength >= desiredLength); + + lastArraySize = std::min(increasedLength, FIRST_VECTOR_GROW); + + return std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); +} + +ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) +{ + unsigned vectorLength; + unsigned length; + + if (hasIndexedProperties(indexingType())) { + vectorLength = m_butterfly->vectorLength(); + length = m_butterfly->publicLength(); + } else { + vectorLength = 0; + length = 0; + } + + return getNewVectorLength(vectorLength, length, desiredLength); +} + +template<IndexingType indexingShape> +unsigned JSObject::countElements(Butterfly* butterfly) +{ + unsigned numValues = 0; + for (unsigned i = butterfly->publicLength(); i--;) { + switch (indexingShape) { + case Int32Shape: + case ContiguousShape: + if (butterfly->contiguous()[i]) + numValues++; + break; + + case DoubleShape: { + double value = butterfly->contiguousDouble()[i]; + if (value == value) + numValues++; + break; + } + + default: + CRASH(); + } + } + return numValues; +} + +unsigned JSObject::countElements() +{ + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return 0; + + case ALL_INT32_INDEXING_TYPES: + return countElements<Int32Shape>(butterfly()); + + case ALL_DOUBLE_INDEXING_TYPES: + return countElements<DoubleShape>(butterfly()); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + return countElements<ContiguousShape>(butterfly()); + + default: + CRASH(); + return 0; + } +} + +bool JSObject::increaseVectorLength(VM& vm, unsigned newLength) +{ + // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map + // to the vector. Callers have to account for that, because they can do it more efficiently. + if (newLength > MAX_STORAGE_VECTOR_LENGTH) + return false; + + ArrayStorage* storage = arrayStorage(); + + if (newLength >= MIN_SPARSE_ARRAY_INDEX + && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector)) + return false; + + unsigned indexBias = storage->m_indexBias; + unsigned vectorLength = storage->vectorLength(); + ASSERT(newLength > vectorLength); + unsigned newVectorLength = getNewVectorLength(newLength); + + // Fast case - there is no precapacity. In these cases a realloc makes sense. + Structure* structure = this->structure(vm); + if (LIKELY(!indexBias)) { + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = storage->butterfly()->growArrayRight( + vm, this, structure, structure->outOfLineCapacity(), true, + ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength)); + if (!newButterfly) + return false; + newButterfly->arrayStorage()->setVectorLength(newVectorLength); + setButterflyWithoutChangingStructure(vm, newButterfly); + return true; + } + + // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length. + DeferGC deferGC(vm.heap); + unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); + Butterfly* newButterfly = storage->butterfly()->resizeArray( + vm, this, + structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), + newIndexBias, true, ArrayStorage::sizeFor(newVectorLength)); + if (!newButterfly) + return false; + newButterfly->arrayStorage()->setVectorLength(newVectorLength); + newButterfly->arrayStorage()->m_indexBias = newIndexBias; + setButterflyWithoutChangingStructure(vm, newButterfly); + return true; +} + +void JSObject::ensureLengthSlow(VM& vm, unsigned length) +{ + ASSERT(length < MAX_ARRAY_INDEX); + ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); + ASSERT(length > m_butterfly->vectorLength()); + + unsigned newVectorLength = std::min( + length << 1, + MAX_STORAGE_VECTOR_LENGTH); + unsigned oldVectorLength = m_butterfly->vectorLength(); + DeferGC deferGC(vm.heap); + m_butterfly.set(vm, this, m_butterfly->growArrayRight( + vm, this, structure(), structure()->outOfLineCapacity(), true, + oldVectorLength * sizeof(EncodedJSValue), + newVectorLength * sizeof(EncodedJSValue))); + + m_butterfly->setVectorLength(newVectorLength); + + if (hasDouble(indexingType())) { + for (unsigned i = oldVectorLength; i < newVectorLength; ++i) + m_butterfly->contiguousDouble().data()[i] = PNaN; + } +} + +void JSObject::reallocateAndShrinkButterfly(VM& vm, unsigned length) +{ + ASSERT(length < MAX_ARRAY_INDEX); + ASSERT(length < MAX_STORAGE_VECTOR_LENGTH); + ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); + ASSERT(m_butterfly->vectorLength() > length); + ASSERT(!m_butterfly->indexingHeader()->preCapacity(structure())); + + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = m_butterfly->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(length)); + m_butterfly.set(vm, this, newButterfly); + m_butterfly->setVectorLength(length); + m_butterfly->setPublicLength(length); +} + +Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize) +{ + ASSERT(newSize > oldSize); + + // It's important that this function not rely on structure(), for the property + // capacity, since we might have already mutated the structure in-place. + + return Butterfly::createOrGrowPropertyStorage(m_butterfly.get(), vm, this, structure(vm), oldSize, newSize); +} + +bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) +{ + JSC::PropertySlot slot(this); + if (!methodTable(exec->vm())->getOwnPropertySlot(this, exec, propertyName, slot)) + return false; + /* Workaround, JSDOMWindow::getOwnPropertySlot searches the prototype chain. :-( */ + if (slot.slotBase() != this && slot.slotBase() && slot.slotBase()->methodTable(exec->vm())->toThis(slot.slotBase(), exec, NotStrictMode) != this) + return false; + if (slot.isAccessor()) + descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes()); + else if (slot.attributes() & CustomAccessor) + descriptor.setCustomDescriptor(slot.attributes()); + else + descriptor.setDescriptor(slot.getValue(exec, propertyName), slot.attributes()); + return true; +} + +static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor) +{ + VM& vm = exec->vm(); + if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { + if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) { + GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject()); + if (oldDescriptor.getterPresent()) + accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject()); + if (oldDescriptor.setterPresent()) + accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject()); + target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); + return true; + } + JSValue newValue = jsUndefined(); + if (descriptor.value()) + newValue = descriptor.value(); + else if (oldDescriptor.value()) + newValue = oldDescriptor.value(); + target->putDirect(vm, propertyName, newValue, attributes & ~Accessor); + if (attributes & ReadOnly) + target->structure(vm)->setContainsReadOnlyProperties(); + return true; + } + attributes &= ~ReadOnly; + GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject()); + + if (descriptor.getterPresent()) + accessor->setGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject()); + else if (oldDescriptor.getterPresent()) + accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject()); + if (descriptor.setterPresent()) + accessor->setSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject()); + else if (oldDescriptor.setterPresent()) + accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject()); + + target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); + return true; +} + +void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value) +{ + if (Optional<uint32_t> index = parseIndex(propertyName)) + putDirectIndex(exec, index.value(), value); + else + putDirect(exec->vm(), propertyName, value); +} + +class DefineOwnPropertyScope { +public: + DefineOwnPropertyScope(ExecState* exec) + : m_vm(exec->vm()) + { + m_vm.setInDefineOwnProperty(true); + } + + ~DefineOwnPropertyScope() + { + m_vm.setInDefineOwnProperty(false); + } + +private: + VM& m_vm; +}; + +bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) +{ + // Track on the globaldata that we're in define property. + // Currently DefineOwnProperty uses delete to remove properties when they are being replaced + // (particularly when changing attributes), however delete won't allow non-configurable (i.e. + // DontDelete) properties to be deleted. For now, we can use this flag to make this work. + DefineOwnPropertyScope scope(exec); + + // If we have a new property we can just put it on normally + PropertyDescriptor current; + if (!getOwnPropertyDescriptor(exec, propertyName, current)) { + // unless extensions are prevented! + if (!isExtensible()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible."))); + return false; + } + PropertyDescriptor oldDescriptor; + oldDescriptor.setValue(jsUndefined()); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor); + } + + if (descriptor.isEmpty()) + return true; + + if (current.equalTo(exec, descriptor)) + return true; + + // Filter out invalid changes + if (!current.configurable()) { + if (descriptor.configurable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property."))); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property."))); + return false; + } + } + + // A generic descriptor is simply changing the attributes of an existing property + if (descriptor.isGenericDescriptor()) { + if (!current.attributesEqual(descriptor)) { + methodTable(exec->vm())->deleteProperty(this, exec, propertyName); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); + } + return true; + } + + // Changing between a normal property or an accessor property + if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { + if (!current.configurable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); + return false; + } + methodTable(exec->vm())->deleteProperty(this, exec, propertyName); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); + } + + // Changing the value and attributes of an existing property + if (descriptor.isDataDescriptor()) { + if (!current.configurable()) { + if (!current.writable() && descriptor.writable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property."))); + return false; + } + if (!current.writable()) { + if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value())) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property."))); + return false; + } + } + } + if (current.attributesEqual(descriptor) && !descriptor.value()) + return true; + methodTable(exec->vm())->deleteProperty(this, exec, propertyName); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); + } + + // Changing the accessor functions of an existing accessor property + ASSERT(descriptor.isAccessorDescriptor()); + if (!current.configurable()) { + if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change the setter of an unconfigurable property."))); + return false; + } + if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change the getter of an unconfigurable property."))); + return false; + } + if (current.attributes() & CustomAccessor) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); + return false; + } + } + JSValue accessor = getDirect(exec->vm(), propertyName); + if (!accessor) + return false; + GetterSetter* getterSetter; + bool getterSetterChanged = false; + if (accessor.isCustomGetterSetter()) + getterSetter = GetterSetter::create(exec->vm(), exec->lexicalGlobalObject()); + else { + ASSERT(accessor.isGetterSetter()); + getterSetter = asGetterSetter(accessor); + } + if (descriptor.setterPresent()) { + getterSetter = getterSetter->withSetter(exec->vm(), exec->lexicalGlobalObject(), descriptor.setterObject()); + getterSetterChanged = true; + } + if (descriptor.getterPresent()) { + getterSetter = getterSetter->withGetter(exec->vm(), exec->lexicalGlobalObject(), descriptor.getterObject()); + getterSetterChanged = true; + } + if (current.attributesEqual(descriptor) && !getterSetterChanged) + return true; + methodTable(exec->vm())->deleteProperty(this, exec, propertyName); + unsigned attrs = descriptor.attributesOverridingCurrent(current); + putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor); + return true; +} + +bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) +{ + // If it's an array index, then use the indexed property storage. + if (Optional<uint32_t> index = parseIndex(propertyName)) { + // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments. + // d. Reject if succeeded is false. + // e. If index >= oldLen + // e.i. Set oldLenDesc.[[Value]] to index + 1. + // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true. + // f. Return true. + return object->defineOwnIndexedProperty(exec, index.value(), descriptor, throwException); + } + + return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); +} + +JSObject* throwTypeError(ExecState* exec, const String& message) +{ + return exec->vm().throwException(exec, createTypeError(exec, message)); +} + +void JSObject::convertToDictionary(VM& vm) +{ + DeferredStructureTransitionWatchpointFire deferredWatchpointFire; + setStructure( + vm, Structure::toCacheableDictionaryTransition(vm, structure(vm), &deferredWatchpointFire)); +} + +void JSObject::shiftButterflyAfterFlattening(VM& vm, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter) +{ + Butterfly* butterfly = this->butterfly(); + size_t preCapacity = this->butterflyPreCapacity(); + void* currentBase = butterfly->base(preCapacity, outOfLineCapacityAfter); + void* newBase = butterfly->base(preCapacity, outOfLineCapacityBefore); + + memmove(newBase, currentBase, this->butterflyTotalSize()); + setButterflyWithoutChangingStructure(vm, Butterfly::fromBase(newBase, preCapacity, outOfLineCapacityAfter)); +} + +uint32_t JSObject::getEnumerableLength(ExecState* exec, JSObject* object) +{ + VM& vm = exec->vm(); + Structure* structure = object->structure(vm); + if (structure->holesMustForwardToPrototype(vm)) + return 0; + switch (object->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return 0; + + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = object->butterfly(); + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + if (!butterfly->contiguous()[i]) + return 0; + } + return usedLength; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = object->butterfly(); + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + double value = butterfly->contiguousDouble()[i]; + if (value != value) + return 0; + } + return usedLength; + } + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = object->m_butterfly->arrayStorage(); + if (storage->m_sparseMap.get()) + return 0; + + unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); + for (unsigned i = 0; i < usedVectorLength; ++i) { + if (!storage->m_vector[i]) + return 0; + } + return usedVectorLength; + } + + default: + RELEASE_ASSERT_NOT_REACHED(); + return 0; + } +} + +void JSObject::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + VM& vm = exec->vm(); + object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode); +} + +void JSObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + VM& vm = exec->vm(); + object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, EnumerationMode(mode, JSObjectPropertiesMode::Exclude)); + + if (object->prototype().isNull()) + return; + + JSObject* prototype = asObject(object->prototype()); + while (true) { + if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) { + prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode); + break; + } + prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode); + JSValue nextProto = prototype->prototype(); + if (nextProto.isNull()) + break; + prototype = asObject(nextProto); + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h new file mode 100644 index 000000000..2c5b8f733 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -0,0 +1,1487 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003-2009, 2012-2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSObject_h +#define JSObject_h + +#include "ArgList.h" +#include "ArrayConventions.h" +#include "ArrayStorage.h" +#include "Butterfly.h" +#include "CallFrame.h" +#include "ClassInfo.h" +#include "CommonIdentifiers.h" +#include "CopyWriteBarrier.h" +#include "CustomGetterSetter.h" +#include "DeferGC.h" +#include "Heap.h" +#include "HeapInlines.h" +#include "IndexingHeaderInlines.h" +#include "JSCell.h" +#include "PropertySlot.h" +#include "PropertyStorage.h" +#include "PutDirectIndexMode.h" +#include "PutPropertySlot.h" + +#include "Structure.h" +#include "VM.h" +#include "JSString.h" +#include "SparseArrayValueMap.h" +#include <wtf/StdLibExtras.h> + +namespace JSC { + +inline JSCell* getJSFunction(JSValue value) +{ + if (value.isCell() && (value.asCell()->type() == JSFunctionType)) + return value.asCell(); + return 0; +} + +class GetterSetter; +class InternalFunction; +class JSFunction; +class LLIntOffsetsExtractor; +class MarkedBlock; +class PropertyDescriptor; +class PropertyNameArray; +class Structure; +struct HashTable; +struct HashTableValue; + +JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&); +extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError; + +COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute); +COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute); +COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute); +COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute); +COMPILE_ASSERT(Function < FirstInternalAttribute, Function_is_below_FirstInternalAttribute); +COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute); + +class JSFinalObject; + +class JSObject : public JSCell { + friend class BatchedTransitionOptimizer; + friend class JIT; + friend class JSCell; + friend class JSFinalObject; + friend class MarkedBlock; + JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject*, PropertyName, PropertySlot&); + + enum PutMode { + PutModePut, + PutModeDefineOwnProperty, + }; + +public: + typedef JSCell Base; + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); + + JS_EXPORT_PRIVATE static String className(const JSObject*); + JS_EXPORT_PRIVATE static String calculatedClassName(JSObject*); + + JSValue prototype() const; + JS_EXPORT_PRIVATE void setPrototype(VM&, JSValue prototype); + JS_EXPORT_PRIVATE bool setPrototypeWithCycleCheck(ExecState*, JSValue prototype); + + bool mayInterceptIndexedAccesses() + { + return structure()->mayInterceptIndexedAccesses(); + } + + JSValue get(ExecState*, PropertyName) const; + JSValue get(ExecState*, unsigned propertyName) const; + + bool fastGetOwnPropertySlot(ExecState*, VM&, Structure&, PropertyName, PropertySlot&); + bool getPropertySlot(ExecState*, PropertyName, PropertySlot&); + bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); + + // The key difference between this and getOwnPropertySlot is that getOwnPropertySlot + // currently returns incorrect results for the DOM window (with non-own properties) + // being returned. Once this is fixed we should migrate code & remove this method. + JS_EXPORT_PRIVATE bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); + + JS_EXPORT_PRIVATE bool allowsAccessFrom(ExecState*); + + unsigned getArrayLength() const + { + if (!hasIndexedProperties(indexingType())) + return 0; + return m_butterfly->publicLength(); + } + + unsigned getVectorLength() + { + if (!hasIndexedProperties(indexingType())) + return 0; + return m_butterfly->vectorLength(); + } + + JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + + ALWAYS_INLINE void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) + { + if (canSetIndexQuickly(propertyName)) { + setIndexQuickly(exec->vm(), propertyName, value); + return; + } + methodTable(exec->vm())->putByIndex(this, exec, propertyName, value, shouldThrow); + } + + // This is similar to the putDirect* methods: + // - the prototype chain is not consulted + // - accessors are not called. + // - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default). + // This method creates a property with attributes writable, enumerable and configurable all set to true. + bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode) + { + if (!attributes && canSetIndexQuicklyForPutDirect(propertyName)) { + setIndexQuickly(exec->vm(), propertyName, value); + return true; + } + return putDirectIndexBeyondVectorLength(exec, propertyName, value, attributes, mode); + } + bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value) + { + return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect); + } + + // A non-throwing version of putDirect and putDirectIndex. + JS_EXPORT_PRIVATE void putDirectMayBeIndex(ExecState*, PropertyName, JSValue); + + bool hasIndexingHeader() const + { + return structure()->hasIndexingHeader(this); + } + + bool canGetIndexQuickly(unsigned i) + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i]; + case ALL_DOUBLE_INDEXING_TYPES: { + if (i >= m_butterfly->vectorLength()) + return false; + double value = m_butterfly->contiguousDouble()[i]; + if (value != value) + return false; + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + JSValue getIndexQuickly(unsigned i) + { + switch (indexingType()) { + case ALL_INT32_INDEXING_TYPES: + return jsNumber(m_butterfly->contiguous()[i].get().asInt32()); + case ALL_CONTIGUOUS_INDEXING_TYPES: + return m_butterfly->contiguous()[i].get(); + case ALL_DOUBLE_INDEXING_TYPES: + return JSValue(JSValue::EncodeAsDouble, m_butterfly->contiguousDouble()[i]); + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage()->m_vector[i].get(); + default: + RELEASE_ASSERT_NOT_REACHED(); + return JSValue(); + } + } + + JSValue tryGetIndexQuickly(unsigned i) + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + break; + case ALL_INT32_INDEXING_TYPES: + if (i < m_butterfly->publicLength()) { + JSValue result = m_butterfly->contiguous()[i].get(); + ASSERT(result.isInt32() || !result); + return result; + } + break; + case ALL_CONTIGUOUS_INDEXING_TYPES: + if (i < m_butterfly->publicLength()) + return m_butterfly->contiguous()[i].get(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + if (i >= m_butterfly->publicLength()) + break; + double result = m_butterfly->contiguousDouble()[i]; + if (result != result) + break; + return JSValue(JSValue::EncodeAsDouble, result); + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + if (i < m_butterfly->arrayStorage()->vectorLength()) + return m_butterfly->arrayStorage()->m_vector[i].get(); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + return JSValue(); + } + + JSValue getDirectIndex(ExecState* exec, unsigned i) + { + if (JSValue result = tryGetIndexQuickly(i)) + return result; + PropertySlot slot(this); + if (methodTable(exec->vm())->getOwnPropertySlotByIndex(this, exec, i, slot)) + return slot.getValue(exec, i); + return JSValue(); + } + + JSValue getIndex(ExecState* exec, unsigned i) + { + if (JSValue result = tryGetIndexQuickly(i)) + return result; + return get(exec, i); + } + + bool canSetIndexQuickly(unsigned i) + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case NonArrayWithArrayStorage: + case ArrayWithArrayStorage: + return i < m_butterfly->vectorLength(); + case NonArrayWithSlowPutArrayStorage: + case ArrayWithSlowPutArrayStorage: + return i < m_butterfly->arrayStorage()->vectorLength() + && !!m_butterfly->arrayStorage()->m_vector[i]; + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + bool canSetIndexQuicklyForPutDirect(unsigned i) + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return i < m_butterfly->vectorLength(); + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + void setIndexQuickly(VM& vm, unsigned i, JSValue v) + { + switch (indexingType()) { + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isInt32()) { + convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + FALLTHROUGH; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + m_butterfly->contiguous()[i].set(vm, this, v); + if (i >= m_butterfly->publicLength()) + m_butterfly->setPublicLength(i + 1); + break; + } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isNumber()) { + convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + double value = v.asNumber(); + if (value != value) { + convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + m_butterfly->contiguousDouble()[i] = value; + if (i >= m_butterfly->publicLength()) + m_butterfly->setPublicLength(i + 1); + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = m_butterfly->arrayStorage(); + WriteBarrier<Unknown>& x = storage->m_vector[i]; + JSValue old = x.get(); + x.set(vm, this, v); + if (!old) { + ++storage->m_numValuesInVector; + if (i >= storage->length()) + storage->setLength(i + 1); + } + break; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } + + void initializeIndex(VM& vm, unsigned i, JSValue v) + { + initializeIndex(vm, i, v, indexingType()); + } + + void initializeIndex(VM& vm, unsigned i, JSValue v, IndexingType indexingType) + { + switch (indexingType) { + case ALL_UNDECIDED_INDEXING_TYPES: { + setIndexQuicklyToUndecided(vm, i, v); + break; + } + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isInt32()) { + convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); + break; + } + FALLTHROUGH; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + m_butterfly->contiguous()[i].set(vm, this, v); + break; + } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isNumber()) { + convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + double value = v.asNumber(); + if (value != value) { + convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + m_butterfly->contiguousDouble()[i] = value; + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = m_butterfly->arrayStorage(); + ASSERT(i < storage->length()); + ASSERT(i < storage->m_numValuesInVector); + storage->m_vector[i].set(vm, this, v); + break; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } + + bool hasSparseMap() + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return false; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return !!m_butterfly->arrayStorage()->m_sparseMap; + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + bool inSparseIndexingMode() + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return false; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage()->inSparseMode(); + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + void enterDictionaryIndexingMode(VM&); + + // putDirect is effectively an unchecked vesion of 'defineOwnProperty': + // - the prototype chain is not consulted + // - accessors are not called. + // - attributes will be respected (after the call the property will exist with the given attributes) + // - the property name is assumed to not be an index. + void putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0); + void putDirect(VM&, PropertyName, JSValue, PutPropertySlot&); + void putDirectWithoutTransition(VM&, PropertyName, JSValue, unsigned attributes = 0); + void putDirectNonIndexAccessor(VM&, PropertyName, JSValue, unsigned attributes); + void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes); + JS_EXPORT_PRIVATE void putDirectCustomAccessor(VM&, PropertyName, JSValue, unsigned attributes); + + void putGetter(ExecState*, PropertyName, JSValue); + void putSetter(ExecState*, PropertyName, JSValue); + + JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const; + JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const; + bool hasOwnProperty(ExecState*, PropertyName) const; + bool hasOwnProperty(ExecState*, unsigned) const; + + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); + JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + + JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + + bool hasInstance(ExecState*, JSValue); + static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty); + + JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + JS_EXPORT_PRIVATE static uint32_t getEnumerableLength(ExecState*, JSObject*); + JS_EXPORT_PRIVATE static void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + JS_EXPORT_PRIVATE double toNumber(ExecState*) const; + JS_EXPORT_PRIVATE JSString* toString(ExecState*) const; + + JS_EXPORT_PRIVATE static JSValue toThis(JSCell*, ExecState*, ECMAMode); + + // This get function only looks at the property map. + JSValue getDirect(VM& vm, PropertyName propertyName) const + { + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName); + checkOffset(offset, structure->inlineCapacity()); + return offset != invalidOffset ? getDirect(offset) : JSValue(); + } + + JSValue getDirect(VM& vm, PropertyName propertyName, unsigned& attributes) const + { + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName, attributes); + checkOffset(offset, structure->inlineCapacity()); + return offset != invalidOffset ? getDirect(offset) : JSValue(); + } + + PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName) + { + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName); + checkOffset(offset, structure->inlineCapacity()); + return offset; + } + + PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName, unsigned& attributes) + { + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName, attributes); + checkOffset(offset, structure->inlineCapacity()); + return offset; + } + + bool hasInlineStorage() const { return structure()->hasInlineStorage(); } + ConstPropertyStorage inlineStorageUnsafe() const + { + return bitwise_cast<ConstPropertyStorage>(this + 1); + } + PropertyStorage inlineStorageUnsafe() + { + return bitwise_cast<PropertyStorage>(this + 1); + } + ConstPropertyStorage inlineStorage() const + { + ASSERT(hasInlineStorage()); + return inlineStorageUnsafe(); + } + PropertyStorage inlineStorage() + { + ASSERT(hasInlineStorage()); + return inlineStorageUnsafe(); + } + + const Butterfly* butterfly() const { return m_butterfly.get(); } + Butterfly* butterfly() { return m_butterfly.get(); } + + ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); } + PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); } + + const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const + { + if (isInlineOffset(offset)) + return &inlineStorage()[offsetInInlineStorage(offset)]; + return &outOfLineStorage()[offsetInOutOfLineStorage(offset)]; + } + + WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) + { + if (isInlineOffset(offset)) + return &inlineStorage()[offsetInInlineStorage(offset)]; + return &outOfLineStorage()[offsetInOutOfLineStorage(offset)]; + } + + void transitionTo(VM&, Structure*); + + JS_EXPORT_PRIVATE bool removeDirect(VM&, PropertyName); // Return true if anything is removed. + bool hasCustomProperties() { return structure()->didTransition(); } + bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); } + bool hasCustomGetterSetterProperties() { return structure()->hasCustomGetterSetterProperties(); } + + // putOwnDataProperty has 'put' like semantics, however this method: + // - assumes the object contains no own getter/setter properties. + // - provides no special handling for __proto__ + // - does not walk the prototype chain (to check for accessors or non-writable properties). + // This is used by JSLexicalEnvironment. + bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&); + + // Fast access to known property offsets. + JSValue getDirect(PropertyOffset offset) const { return locationForOffset(offset)->get(); } + void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); } + void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); } + + JS_EXPORT_PRIVATE void putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); + JS_EXPORT_PRIVATE JSFunction* putDirectBuiltinFunction(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes); + JSFunction* putDirectBuiltinFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes); + JS_EXPORT_PRIVATE void putDirectNativeFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); + + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + + bool isGlobalObject() const; + bool isVariableObject() const; + bool isStaticScopeObject() const; + bool isNameScopeObject() const; + bool isActivationObject() const; + bool isErrorInstance() const; + bool isWithScope() const; + + JS_EXPORT_PRIVATE void seal(VM&); + JS_EXPORT_PRIVATE void freeze(VM&); + JS_EXPORT_PRIVATE void preventExtensions(VM&); + bool isSealed(VM& vm) { return structure(vm)->isSealed(vm); } + bool isFrozen(VM& vm) { return structure(vm)->isFrozen(vm); } + bool isExtensible() { return structure()->isExtensible(); } + bool indexingShouldBeSparse() + { + return !isExtensible() + || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); + } + + bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } + void reifyStaticFunctionsForDelete(ExecState* exec); + + JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(VM&, size_t oldSize, size_t newSize); + void setButterflyWithoutChangingStructure(VM&, Butterfly*); + + void setStructure(VM&, Structure*); + void setStructureAndButterfly(VM&, Structure*, Butterfly*); + void setStructureAndReallocateStorageIfNecessary(VM&, unsigned oldCapacity, Structure*); + void setStructureAndReallocateStorageIfNecessary(VM&, Structure*); + + JS_EXPORT_PRIVATE void convertToDictionary(VM&); + + void flattenDictionaryObject(VM& vm) + { + structure(vm)->flattenDictionaryStructure(vm, this); + } + void shiftButterflyAfterFlattening(VM&, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter); + + JSGlobalObject* globalObject() const + { + ASSERT(structure()->globalObject()); + ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this); + return structure()->globalObject(); + } + + void switchToSlowPutArrayStorage(VM&); + + // The receiver is the prototype in this case. The following: + // + // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...) + // + // is equivalent to: + // + // foo->attemptToInterceptPutByIndexOnHole(...); + bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); + + // Returns 0 if int32 storage cannot be created - either because + // indexing should be sparse, we're having a bad time, or because + // we already have a more general form of storage (double, + // contiguous, array storage). + ContiguousJSValues ensureInt32(VM& vm) + { + if (LIKELY(hasInt32(indexingType()))) + return m_butterfly->contiguousInt32(); + + return ensureInt32Slow(vm); + } + + // Returns 0 if double storage cannot be created - either because + // indexing should be sparse, we're having a bad time, or because + // we already have a more general form of storage (contiguous, + // or array storage). + ContiguousDoubles ensureDouble(VM& vm) + { + if (LIKELY(hasDouble(indexingType()))) + return m_butterfly->contiguousDouble(); + + return ensureDoubleSlow(vm); + } + + // Returns 0 if contiguous storage cannot be created - either because + // indexing should be sparse or because we're having a bad time. + ContiguousJSValues ensureContiguous(VM& vm) + { + if (LIKELY(hasContiguous(indexingType()))) + return m_butterfly->contiguous(); + + return ensureContiguousSlow(vm); + } + + // Ensure that the object is in a mode where it has array storage. Use + // this if you're about to perform actions that would have required the + // object to be converted to have array storage, if it didn't have it + // already. + ArrayStorage* ensureArrayStorage(VM& vm) + { + if (LIKELY(hasAnyArrayStorage(indexingType()))) + return m_butterfly->arrayStorage(); + + return ensureArrayStorageSlow(vm); + } + + static size_t offsetOfInlineStorage(); + + static ptrdiff_t butterflyOffset() + { + return OBJECT_OFFSETOF(JSObject, m_butterfly); + } + + void* butterflyAddress() + { + return &m_butterfly; + } + + DECLARE_EXPORT_INFO; + +protected: + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(inherits(info())); + ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); + ASSERT(structure()->isObject()); + ASSERT(classInfo()); + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + // To instantiate objects you likely want JSFinalObject, below. + // To create derived types you likely want JSNonFinalObject, below. + JSObject(VM&, Structure*, Butterfly* = 0); + + void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize); + void copyButterfly(CopyVisitor&, Butterfly*, size_t storageSize); + + // Call this if you know that the object is in a mode where it has array + // storage. This will assert otherwise. + ArrayStorage* arrayStorage() + { + ASSERT(hasAnyArrayStorage(indexingType())); + return m_butterfly->arrayStorage(); + } + + // Call this if you want to predicate some actions on whether or not the + // object is in a mode where it has array storage. + ArrayStorage* arrayStorageOrNull() + { + switch (indexingType()) { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage(); + + default: + return 0; + } + } + + size_t butterflyTotalSize(); + size_t butterflyPreCapacity(); + + Butterfly* createInitialUndecided(VM&, unsigned length); + ContiguousJSValues createInitialInt32(VM&, unsigned length); + ContiguousDoubles createInitialDouble(VM&, unsigned length); + ContiguousJSValues createInitialContiguous(VM&, unsigned length); + + void convertUndecidedForValue(VM&, JSValue); + void createInitialForValueAndSet(VM&, unsigned index, JSValue); + void convertInt32ForValue(VM&, JSValue); + + ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength); + ArrayStorage* createInitialArrayStorage(VM&); + + ContiguousJSValues convertUndecidedToInt32(VM&); + ContiguousDoubles convertUndecidedToDouble(VM&); + ContiguousJSValues convertUndecidedToContiguous(VM&); + ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition); + ArrayStorage* convertUndecidedToArrayStorage(VM&); + + ContiguousDoubles convertInt32ToDouble(VM&); + ContiguousJSValues convertInt32ToContiguous(VM&); + ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition); + ArrayStorage* convertInt32ToArrayStorage(VM&); + + ContiguousJSValues convertDoubleToContiguous(VM&); + ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition); + ArrayStorage* convertDoubleToArrayStorage(VM&); + + ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition); + ArrayStorage* convertContiguousToArrayStorage(VM&); + + + ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM&); + + bool defineOwnNonIndexProperty(ExecState*, PropertyName, const PropertyDescriptor&, bool throwException); + + template<IndexingType indexingShape> + void putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue); + void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); + + bool increaseVectorLength(VM&, unsigned newLength); + void deallocateSparseIndexMap(); + bool defineOwnIndexedProperty(ExecState*, unsigned, const PropertyDescriptor&, bool throwException); + SparseArrayValueMap* allocateSparseIndexMap(VM&); + + void notifyPresenceOfIndexedAccessors(VM&); + + bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow); + + // Call this if you want setIndexQuickly to succeed and you're sure that + // the array is contiguous. + void ensureLength(VM& vm, unsigned length) + { + ASSERT(length < MAX_ARRAY_INDEX); + ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); + + if (m_butterfly->vectorLength() < length) + ensureLengthSlow(vm, length); + + if (m_butterfly->publicLength() < length) + m_butterfly->setPublicLength(length); + } + + // Call this if you want to shrink the butterfly backing store, and you're + // sure that the array is contiguous. + void reallocateAndShrinkButterfly(VM&, unsigned length); + + template<IndexingType indexingShape> + unsigned countElements(Butterfly*); + + // This is relevant to undecided, int32, double, and contiguous. + unsigned countElements(); + +private: + friend class LLIntOffsetsExtractor; + + // Nobody should ever ask any of these questions on something already known to be a JSObject. + using JSCell::isAPIValueWrapper; + using JSCell::isGetterSetter; + void getObject(); + void getString(ExecState* exec); + void isObject(); + void isString(); + + Butterfly* createInitialIndexedStorage(VM&, unsigned length, size_t elementSize); + + ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*); + + template<PutMode> + bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&); + + bool inlineGetOwnPropertySlot(VM&, Structure&, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, JSValue, unsigned, PropertyOffset); + void fillCustomGetterPropertySlot(PropertySlot&, JSValue, unsigned, Structure&); + + const HashTableValue* findPropertyHashEntry(PropertyName) const; + + void putIndexedDescriptor(ExecState*, SparseArrayEntry*, const PropertyDescriptor&, PropertyDescriptor& old); + + void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*); + JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode); + + unsigned getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength); + unsigned getNewVectorLength(unsigned desiredLength); + + ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(VM&, unsigned neededLength); + + JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(VM&, unsigned index, JSValue); + JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue); + JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue); + + void ensureLengthSlow(VM&, unsigned length); + + ContiguousJSValues ensureInt32Slow(VM&); + ContiguousDoubles ensureDoubleSlow(VM&); + ContiguousJSValues ensureContiguousSlow(VM&); + JS_EXPORT_PRIVATE ArrayStorage* ensureArrayStorageSlow(VM&); + +protected: + CopyWriteBarrier<Butterfly> m_butterfly; +#if USE(JSVALUE32_64) +private: + uint32_t m_padding; +#endif +}; + +// JSNonFinalObject is a type of JSObject that has some internal storage, +// but also preserves some space in the collector cell for additional +// data members in derived types. +class JSNonFinalObject : public JSObject { + friend class JSObject; + +public: + typedef JSObject Base; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + explicit JSNonFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = 0) + : JSObject(vm, structure, butterfly) + { + } + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(!this->structure()->hasInlineStorage()); + ASSERT(classInfo()); + } +}; + +class JSFinalObject; + +// JSFinalObject is a type of JSObject that contains sufficent internal +// storage to fully make use of the colloctor cell containing it. +class JSFinalObject : public JSObject { + friend class JSObject; + +public: + typedef JSObject Base; + static const unsigned StructureFlags = Base::StructureFlags; + + static size_t allocationSize(size_t inlineCapacity) + { + return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>); + } + + static const unsigned defaultSize = 64; + static inline unsigned defaultInlineCapacity() + { + return (defaultSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>); + } + + static const unsigned maxSize = 512; + static inline unsigned maxInlineCapacity() + { + return (maxSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>); + } + + static JSFinalObject* create(ExecState*, Structure*, Butterfly* = nullptr); + static JSFinalObject* create(VM&, Structure*); + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, unsigned inlineCapacity) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), info(), NonArray, inlineCapacity); + } + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + + DECLARE_EXPORT_INFO; + +protected: + void visitChildrenCommon(SlotVisitor&); + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(structure()->totalStorageCapacity() == structure()->inlineCapacity()); + ASSERT(classInfo()); + } + +private: + friend class LLIntOffsetsExtractor; + + explicit JSFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = nullptr) + : JSObject(vm, structure, butterfly) + { + } +}; + +inline JSFinalObject* JSFinalObject::create( + ExecState* exec, Structure* structure, Butterfly* butterfly) +{ + JSFinalObject* finalObject = new ( + NotNull, + allocateCell<JSFinalObject>( + *exec->heap(), + allocationSize(structure->inlineCapacity()) + ) + ) JSFinalObject(exec->vm(), structure, butterfly); + finalObject->finishCreation(exec->vm()); + return finalObject; +} + +inline JSFinalObject* JSFinalObject::create(VM& vm, Structure* structure) +{ + JSFinalObject* finalObject = new (NotNull, allocateCell<JSFinalObject>(vm.heap, allocationSize(structure->inlineCapacity()))) JSFinalObject(vm, structure); + finalObject->finishCreation(vm); + return finalObject; +} + +inline bool isJSFinalObject(JSCell* cell) +{ + return cell->classInfo() == JSFinalObject::info(); +} + +inline bool isJSFinalObject(JSValue value) +{ + return value.isCell() && isJSFinalObject(value.asCell()); +} + +inline size_t JSObject::offsetOfInlineStorage() +{ + return sizeof(JSObject); +} + +inline bool JSObject::isGlobalObject() const +{ + return type() == GlobalObjectType; +} + +inline bool JSObject::isVariableObject() const +{ + return type() == GlobalObjectType || type() == ActivationObjectType; +} + +inline bool JSObject::isStaticScopeObject() const +{ + JSType type = this->type(); + return type == NameScopeObjectType || type == ActivationObjectType; +} + +inline bool JSObject::isNameScopeObject() const +{ + return type() == NameScopeObjectType; +} + +inline bool JSObject::isActivationObject() const +{ + return type() == ActivationObjectType; +} + +inline bool JSObject::isErrorInstance() const +{ + return type() == ErrorInstanceType; +} + +inline bool JSObject::isWithScope() const +{ + return type() == WithScopeType; +} + +inline void JSObject::setStructureAndButterfly(VM& vm, Structure* structure, Butterfly* butterfly) +{ + ASSERT(structure); + ASSERT(!butterfly == (!structure->outOfLineCapacity() && !structure->hasIndexingHeader(this))); + m_butterfly.set(vm, this, butterfly); + setStructure(vm, structure); +} + +inline void JSObject::setStructure(VM& vm, Structure* structure) +{ + ASSERT(structure); + ASSERT(!m_butterfly == !(structure->outOfLineCapacity() || structure->hasIndexingHeader(this))); + JSCell::setStructure(vm, structure); +} + +inline void JSObject::setButterflyWithoutChangingStructure(VM& vm, Butterfly* butterfly) +{ + m_butterfly.set(vm, this, butterfly); +} + +inline CallType getCallData(JSValue value, CallData& callData) +{ + CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone; + ASSERT(result == CallTypeNone || value.isValidCallee()); + return result; +} + +inline ConstructType getConstructData(JSValue value, ConstructData& constructData) +{ + ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructTypeNone; + ASSERT(result == ConstructTypeNone || value.isValidCallee()); + return result; +} + +inline JSObject* asObject(JSCell* cell) +{ + ASSERT(cell->isObject()); + return jsCast<JSObject*>(cell); +} + +inline JSObject* asObject(JSValue value) +{ + return asObject(value.asCell()); +} + +inline JSObject::JSObject(VM& vm, Structure* structure, Butterfly* butterfly) + : JSCell(vm, structure) + , m_butterfly(vm, this, butterfly) +{ + vm.heap.ascribeOwner(this, butterfly); +} + +inline JSValue JSObject::prototype() const +{ + return structure()->storedPrototype(); +} + +ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(VM& vm, Structure& structure, PropertyName propertyName, PropertySlot& slot) +{ + unsigned attributes; + PropertyOffset offset = structure.get(vm, propertyName, attributes); + if (!isValidOffset(offset)) + return false; + + JSValue value = getDirect(offset); + if (structure.hasGetterSetterProperties() && value.isGetterSetter()) + fillGetterPropertySlot(slot, value, attributes, offset); + else if (structure.hasCustomGetterSetterProperties() && value.isCustomGetterSetter()) + fillCustomGetterPropertySlot(slot, value, attributes, structure); + else + slot.setValue(this, attributes, value, offset); + + return true; +} + +ALWAYS_INLINE void JSObject::fillCustomGetterPropertySlot(PropertySlot& slot, JSValue customGetterSetter, unsigned attributes, Structure& structure) +{ + if (structure.isDictionary()) { + slot.setCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter()); + return; + } + slot.setCacheableCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter()); +} + +// It may seem crazy to inline a function this large, especially a virtual function, +// but it makes a big difference to property lookup that derived classes can inline their +// base class call to this. +ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + VM& vm = exec->vm(); + Structure& structure = *object->structure(vm); + if (object->inlineGetOwnPropertySlot(vm, structure, propertyName, slot)) + return true; + if (Optional<uint32_t> index = parseIndex(propertyName)) + return getOwnPropertySlotByIndex(object, exec, index.value(), slot); + return false; +} + +ALWAYS_INLINE bool JSObject::fastGetOwnPropertySlot(ExecState* exec, VM& vm, Structure& structure, PropertyName propertyName, PropertySlot& slot) +{ + if (LIKELY(!TypeInfo::overridesGetOwnPropertySlot(inlineTypeFlags()))) + return inlineGetOwnPropertySlot(vm, structure, propertyName, slot); + return structure.classInfo()->methodTable.getOwnPropertySlot(this, exec, propertyName, slot); +} + +// It may seem crazy to inline a function this large but it makes a big difference +// since this is function very hot in variable lookup +ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + VM& vm = exec->vm(); + auto& structureIDTable = vm.heap.structureIDTable(); + JSObject* object = this; + while (true) { + Structure& structure = *structureIDTable.get(object->structureID()); + if (object->fastGetOwnPropertySlot(exec, vm, structure, propertyName, slot)) + return true; + JSValue prototype = structure.storedPrototype(); + if (!prototype.isObject()) + break; + object = asObject(prototype); + } + + if (Optional<uint32_t> index = parseIndex(propertyName)) + return getPropertySlot(exec, index.value(), slot); + return false; +} + +ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + VM& vm = exec->vm(); + auto& structureIDTable = vm.heap.structureIDTable(); + JSObject* object = this; + while (true) { + Structure& structure = *structureIDTable.get(object->structureID()); + if (structure.classInfo()->methodTable.getOwnPropertySlotByIndex(object, exec, propertyName, slot)) + return true; + JSValue prototype = structure.storedPrototype(); + if (!prototype.isObject()) + return false; + object = asObject(prototype); + } +} + +inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const +{ + PropertySlot slot(this); + if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, propertyName); + + return jsUndefined(); +} + +inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const +{ + PropertySlot slot(this); + if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) + return slot.getValue(exec, propertyName); + + return jsUndefined(); +} + +template<JSObject::PutMode mode> +inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot) +{ + ASSERT(value); + ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + ASSERT(!parseIndex(propertyName)); + + Structure* structure = this->structure(vm); + if (structure->isDictionary()) { + unsigned currentAttributes; + PropertyOffset offset = structure->get(vm, propertyName, currentAttributes); + if (offset != invalidOffset) { + if ((mode == PutModePut) && currentAttributes & ReadOnly) + return false; + + putDirect(vm, offset, value); + structure->didReplaceProperty(offset); + slot.setExistingProperty(this, offset); + + if ((attributes & Accessor) != (currentAttributes & Accessor)) { + ASSERT(!(attributes & ReadOnly)); + setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes)); + } + return true; + } + + if ((mode == PutModePut) && !isExtensible()) + return false; + + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = butterfly(); + if (this->structure()->putWillGrowOutOfLineStorage()) + newButterfly = growOutOfLineStorage(vm, this->structure()->outOfLineCapacity(), this->structure()->suggestedNewOutOfLineStorageCapacity()); + offset = this->structure()->addPropertyWithoutTransition(vm, propertyName, attributes); + setStructureAndButterfly(vm, this->structure(), newButterfly); + + validateOffset(offset); + ASSERT(this->structure()->isValidOffset(offset)); + putDirect(vm, offset, value); + slot.setNewProperty(this, offset); + if (attributes & ReadOnly) + this->structure()->setContainsReadOnlyProperties(); + return true; + } + + PropertyOffset offset; + size_t currentCapacity = this->structure()->outOfLineCapacity(); + if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, offset)) { + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = butterfly(); + if (currentCapacity != structure->outOfLineCapacity()) { + ASSERT(structure != this->structure()); + newButterfly = growOutOfLineStorage(vm, currentCapacity, structure->outOfLineCapacity()); + } + + validateOffset(offset); + ASSERT(structure->isValidOffset(offset)); + setStructureAndButterfly(vm, structure, newButterfly); + putDirect(vm, offset, value); + slot.setNewProperty(this, offset); + return true; + } + + unsigned currentAttributes; + offset = structure->get(vm, propertyName, currentAttributes); + if (offset != invalidOffset) { + if ((mode == PutModePut) && currentAttributes & ReadOnly) + return false; + + structure->didReplaceProperty(offset); + slot.setExistingProperty(this, offset); + putDirect(vm, offset, value); + + if ((attributes & Accessor) != (currentAttributes & Accessor)) { + ASSERT(!(attributes & ReadOnly)); + setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes)); + } + return true; + } + + if ((mode == PutModePut) && !isExtensible()) + return false; + + // We want the structure transition watchpoint to fire after this object has switched + // structure. This allows adaptive watchpoints to observe if the new structure is the one + // we want. + DeferredStructureTransitionWatchpointFire deferredWatchpointFire; + + structure = Structure::addPropertyTransition(vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire); + + validateOffset(offset); + ASSERT(structure->isValidOffset(offset)); + setStructureAndReallocateStorageIfNecessary(vm, structure); + + putDirect(vm, offset, value); + slot.setNewProperty(this, offset); + if (attributes & ReadOnly) + structure->setContainsReadOnlyProperties(); + return true; +} + +inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, unsigned oldCapacity, Structure* newStructure) +{ + ASSERT(oldCapacity <= newStructure->outOfLineCapacity()); + + if (oldCapacity == newStructure->outOfLineCapacity()) { + setStructure(vm, newStructure); + return; + } + + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = growOutOfLineStorage( + vm, oldCapacity, newStructure->outOfLineCapacity()); + setStructureAndButterfly(vm, newStructure, newButterfly); +} + +inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, Structure* newStructure) +{ + setStructureAndReallocateStorageIfNecessary( + vm, structure(vm)->outOfLineCapacity(), newStructure); +} + +inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(value); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + ASSERT(!structure()->hasGetterSetterProperties()); + ASSERT(!structure()->hasCustomGetterSetterProperties()); + + return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot); +} + +inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) +{ + ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); + ASSERT(!value.isCustomGetterSetter()); + PutPropertySlot slot(this); + putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot); +} + +inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(!value.isGetterSetter()); + ASSERT(!value.isCustomGetterSetter()); + putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot); +} + +inline void JSObject::putDirectWithoutTransition(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) +{ + DeferGC deferGC(vm.heap); + ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); + ASSERT(!value.isCustomGetterSetter()); + Butterfly* newButterfly = m_butterfly.get(); + if (structure()->putWillGrowOutOfLineStorage()) + newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); + PropertyOffset offset = structure()->addPropertyWithoutTransition(vm, propertyName, attributes); + setStructureAndButterfly(vm, structure(), newButterfly); + putDirect(vm, offset, value); +} + +inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const +{ + return methodTable()->defaultValue(this, exec, preferredType); +} + +ALWAYS_INLINE JSObject* Register::object() const +{ + return asObject(jsValue()); +} + +ALWAYS_INLINE Register& Register::operator=(JSObject* object) +{ + u.value = JSValue::encode(JSValue(object)); + return *this; +} + +inline size_t offsetInButterfly(PropertyOffset offset) +{ + return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); +} + +inline size_t JSObject::butterflyPreCapacity() +{ + if (UNLIKELY(hasIndexingHeader())) + return butterfly()->indexingHeader()->preCapacity(structure()); + return 0; +} + +inline size_t JSObject::butterflyTotalSize() +{ + Structure* structure = this->structure(); + Butterfly* butterfly = this->butterfly(); + size_t preCapacity; + size_t indexingPayloadSizeInBytes; + bool hasIndexingHeader = this->hasIndexingHeader(); + + if (UNLIKELY(hasIndexingHeader)) { + preCapacity = butterfly->indexingHeader()->preCapacity(structure); + indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); + } else { + preCapacity = 0; + indexingPayloadSizeInBytes = 0; + } + + return Butterfly::totalSize(preCapacity, structure->outOfLineCapacity(), hasIndexingHeader, indexingPayloadSizeInBytes); +} + +// Helpers for patching code where you want to emit a load or store and +// the base is: +// For inline offsets: a pointer to the out-of-line storage pointer. +// For out-of-line offsets: the base of the out-of-line storage. +inline size_t offsetRelativeToPatchedStorage(PropertyOffset offset) +{ + if (isOutOfLineOffset(offset)) + return sizeof(EncodedJSValue) * offsetInButterfly(offset); + return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset); +} + +// Returns the maximum offset (away from zero) a load instruction will encode. +inline size_t maxOffsetRelativeToPatchedStorage(PropertyOffset offset) +{ + ptrdiff_t addressOffset = static_cast<ptrdiff_t>(offsetRelativeToPatchedStorage(offset)); +#if USE(JSVALUE32_64) + if (addressOffset >= 0) + return static_cast<size_t>(addressOffset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag); +#endif + return static_cast<size_t>(addressOffset); +} + +inline int indexRelativeToBase(PropertyOffset offset) +{ + if (isOutOfLineOffset(offset)) + return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); + ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue))); + return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset); +} + +inline int offsetRelativeToBase(PropertyOffset offset) +{ + if (isOutOfLineOffset(offset)) + return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage(); + return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue); +} + +COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment); + +ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name) +{ + return Identifier::fromString(&vm, name); +} + +ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name) +{ + return name; +} + +// Helper for defining native functions, if you're not using a static hash table. +// Use this macro from within finishCreation() methods in prototypes. This assumes +// you've defined variables called exec, globalObject, and vm, and they +// have the expected meanings. +#define JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, attributes, length, intrinsic) \ + putDirectNativeFunction(\ + vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \ + (intrinsic), (attributes)) + +// As above, but this assumes that the function you're defining doesn't have an +// intrinsic. +#define JSC_NATIVE_FUNCTION(jsName, cppName, attributes, length) \ + JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, (attributes), (length), NoIntrinsic) + +// Identical helpers but for builtins. Note that currently, we don't support builtins that are +// also intrinsics, but we probably will do that eventually. +#define JSC_BUILTIN_FUNCTION(jsName, generatorName, attributes) \ + putDirectBuiltinFunction(\ + vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes)) + +} // namespace JSC + +#endif // JSObject_h diff --git a/Source/JavaScriptCore/runtime/JSPromise.cpp b/Source/JavaScriptCore/runtime/JSPromise.cpp new file mode 100644 index 000000000..2531976b5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPromise.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "JSPromise.h" + +#include "Error.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSPromiseConstructor.h" +#include "Microtask.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSPromise::s_info = { "Promise", &Base::s_info, 0, CREATE_METHOD_TABLE(JSPromise) }; + +JSPromise* JSPromise::create(VM& vm, JSGlobalObject* globalObject) +{ + JSPromise* promise = new (NotNull, allocateCell<JSPromise>(vm.heap)) JSPromise(vm, globalObject->promiseStructure()); + promise->finishCreation(vm); + return promise; +} + +Structure* JSPromise::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +JSPromise::JSPromise(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} + +void JSPromise::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + putDirect(vm, vm.propertyNames->promiseStatePrivateName, jsNumber(static_cast<unsigned>(Status::Pending))); + putDirect(vm, vm.propertyNames->promiseFulfillReactionsPrivateName, jsUndefined()); + putDirect(vm, vm.propertyNames->promiseRejectReactionsPrivateName, jsUndefined()); + putDirect(vm, vm.propertyNames->promiseResultPrivateName, jsUndefined()); +} + +auto JSPromise::status(VM& vm) const -> Status +{ + JSValue value = getDirect(vm, vm.propertyNames->promiseStatePrivateName); + ASSERT(value.isUInt32()); + return static_cast<Status>(value.asUInt32()); +} + +JSValue JSPromise::result(VM& vm) const +{ + return getDirect(vm, vm.propertyNames->promiseResultPrivateName); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSPromise.h b/Source/JavaScriptCore/runtime/JSPromise.h new file mode 100644 index 000000000..88e4c043e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPromise.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef JSPromise_h +#define JSPromise_h + +#include "JSObject.h" + +namespace JSC { + +class JSPromise : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static JSPromise* create(VM&, JSGlobalObject*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_EXPORT_INFO; + + enum class Status : unsigned { + Pending = 1, + Fulfilled, + Rejected + }; + + Status status(VM&) const; + JSValue result(VM&) const; + +private: + JSPromise(VM&, Structure*); + void finishCreation(VM&); +}; + +} // namespace JSC + +#endif // JSPromise_h diff --git a/Source/JavaScriptCore/runtime/JSPromiseConstructor.cpp b/Source/JavaScriptCore/runtime/JSPromiseConstructor.cpp new file mode 100644 index 000000000..44e1e73ca --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPromiseConstructor.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "JSPromiseConstructor.h" + +#include "Error.h" +#include "Exception.h" +#include "IteratorOperations.h" +#include "JSCBuiltins.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSPromise.h" +#include "JSPromisePrototype.h" +#include "Lookup.h" +#include "NumberObject.h" +#include "StructureInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSPromiseConstructor); + +} + +#include "JSPromiseConstructor.lut.h" + +namespace JSC { + +const ClassInfo JSPromiseConstructor::s_info = { "Function", &InternalFunction::s_info, &promiseConstructorTable, CREATE_METHOD_TABLE(JSPromiseConstructor) }; + +/* Source for JSPromiseConstructor.lut.h +@begin promiseConstructorTable + resolve JSPromiseConstructorFuncResolve DontEnum|Function 1 + reject JSPromiseConstructorFuncReject DontEnum|Function 1 + race JSPromiseConstructorFuncRace DontEnum|Function 1 + all JSPromiseConstructorFuncAll DontEnum|Function 1 +@end +*/ + +JSPromiseConstructor* JSPromiseConstructor::create(VM& vm, Structure* structure, JSPromisePrototype* promisePrototype) +{ + JSPromiseConstructor* constructor = new (NotNull, allocateCell<JSPromiseConstructor>(vm.heap)) JSPromiseConstructor(vm, structure); + constructor->finishCreation(vm, promisePrototype); + return constructor; +} + +Structure* JSPromiseConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +JSPromiseConstructor::JSPromiseConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void JSPromiseConstructor::finishCreation(VM& vm, JSPromisePrototype* promisePrototype) +{ + Base::finishCreation(vm, ASCIILiteral("Promise")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, promisePrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +static EncodedJSValue JSC_HOST_CALL constructPromise(ExecState* exec) +{ + VM& vm = exec->vm(); + JSGlobalObject* globalObject = exec->callee()->globalObject(); + + JSPromise* promise = JSPromise::create(vm, globalObject); + + JSFunction* initializePromise = globalObject->initializePromiseFunction(); + CallData callData; + CallType callType = getCallData(initializePromise, callData); + ASSERT(callType != CallTypeNone); + + MarkedArgumentBuffer arguments; + arguments.append(exec->argument(0)); + call(exec, initializePromise, callType, callData, promise, arguments); + + return JSValue::encode(promise); +} + +ConstructType JSPromiseConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructPromise; + return ConstructTypeHost; +} + +CallType JSPromiseConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = constructPromise; + return CallTypeHost; +} + +bool JSPromiseConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, promiseConstructorTable, jsCast<JSPromiseConstructor*>(object), propertyName, slot); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSPromiseConstructor.h b/Source/JavaScriptCore/runtime/JSPromiseConstructor.h new file mode 100644 index 000000000..5881e1630 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPromiseConstructor.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef JSPromiseConstructor_h +#define JSPromiseConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class JSPromise; +class JSPromisePrototype; + +class JSPromiseConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static JSPromiseConstructor* create(VM&, Structure*, JSPromisePrototype*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, JSPromisePrototype*); + +private: + JSPromiseConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // JSPromiseConstructor_h diff --git a/Source/JavaScriptCore/runtime/JSPromiseDeferred.cpp b/Source/JavaScriptCore/runtime/JSPromiseDeferred.cpp new file mode 100644 index 000000000..6f3543235 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPromiseDeferred.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "JSPromiseDeferred.h" + +#include "BuiltinNames.h" +#include "Error.h" +#include "Exception.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSPromise.h" +#include "JSPromiseConstructor.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSPromiseDeferred::s_info = { "JSPromiseDeferred", 0, 0, CREATE_METHOD_TABLE(JSPromiseDeferred) }; + +JSPromiseDeferred* JSPromiseDeferred::create(ExecState* exec, JSGlobalObject* globalObject) +{ + VM& vm = exec->vm(); + + JSFunction* newPromiseDeferredFunction = globalObject->newPromiseDeferredFunction(); + CallData callData; + CallType callType = JSC::getCallData(newPromiseDeferredFunction, callData); + ASSERT(callType != CallTypeNone); + + MarkedArgumentBuffer arguments; + JSValue deferred = call(exec, newPromiseDeferredFunction, callType, callData, jsUndefined(), arguments); + + JSValue promise = deferred.get(exec, vm.propertyNames->promisePrivateName); + ASSERT(promise.inherits(JSPromise::info())); + JSValue resolve = deferred.get(exec, vm.propertyNames->builtinNames().resolvePrivateName()); + JSValue reject = deferred.get(exec, vm.propertyNames->builtinNames().rejectPrivateName()); + + return JSPromiseDeferred::create(vm, jsCast<JSPromise*>(promise), resolve, reject); +} + +JSPromiseDeferred* JSPromiseDeferred::create(VM& vm, JSObject* promise, JSValue resolve, JSValue reject) +{ + JSPromiseDeferred* deferred = new (NotNull, allocateCell<JSPromiseDeferred>(vm.heap)) JSPromiseDeferred(vm); + deferred->finishCreation(vm, promise, resolve, reject); + return deferred; +} + +JSPromiseDeferred::JSPromiseDeferred(VM& vm) + : Base(vm, vm.promiseDeferredStructure.get()) +{ +} + +void JSPromiseDeferred::finishCreation(VM& vm, JSObject* promise, JSValue resolve, JSValue reject) +{ + Base::finishCreation(vm); + m_promise.set(vm, this, promise); + m_resolve.set(vm, this, resolve); + m_reject.set(vm, this, reject); +} + +void JSPromiseDeferred::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSPromiseDeferred* thisObject = jsCast<JSPromiseDeferred*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_promise); + visitor.append(&thisObject->m_resolve); + visitor.append(&thisObject->m_reject); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSPromiseDeferred.h b/Source/JavaScriptCore/runtime/JSPromiseDeferred.h new file mode 100644 index 000000000..dea0d17f9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPromiseDeferred.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef JSPromiseDeferred_h +#define JSPromiseDeferred_h + +#include "JSCell.h" +#include "Structure.h" + +namespace JSC { + +class JSPromiseDeferred final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + JS_EXPORT_PRIVATE static JSPromiseDeferred* create(ExecState*, JSGlobalObject*); + JS_EXPORT_PRIVATE static JSPromiseDeferred* create(VM&, JSObject* promise, JSValue resolve, JSValue reject); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); + } + + DECLARE_EXPORT_INFO; + + JSObject* promise() const { return m_promise.get(); } + JSValue resolve() const { return m_resolve.get(); } + JSValue reject() const { return m_reject.get(); } + +private: + JSPromiseDeferred(VM&); + void finishCreation(VM&, JSObject*, JSValue, JSValue); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<JSObject> m_promise; + WriteBarrier<Unknown> m_resolve; + WriteBarrier<Unknown> m_reject; +}; + +} // namespace JSC + +#endif // JSPromiseDeferred_h diff --git a/Source/JavaScriptCore/runtime/JSPromisePrototype.cpp b/Source/JavaScriptCore/runtime/JSPromisePrototype.cpp new file mode 100644 index 000000000..a0f102432 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPromisePrototype.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "JSPromisePrototype.h" + +#include "Error.h" +#include "JSCBuiltins.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "JSPromise.h" +#include "Microtask.h" +#include "StructureInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSPromisePrototype); + +} + +#include "JSPromisePrototype.lut.h" + +namespace JSC { + +const ClassInfo JSPromisePrototype::s_info = { "PromisePrototype", &JSNonFinalObject::s_info, &promisePrototypeTable, CREATE_METHOD_TABLE(JSPromisePrototype) }; + +/* Source for JSPromisePrototype.lut.h +@begin promisePrototypeTable + then JSPromisePrototypeFuncThen DontEnum|Function 2 + catch JSPromisePrototypeFuncCatch DontEnum|Function 1 +@end +*/ + +JSPromisePrototype* JSPromisePrototype::create(ExecState* exec, JSGlobalObject*, Structure* structure) +{ + VM& vm = exec->vm(); + JSPromisePrototype* object = new (NotNull, allocateCell<JSPromisePrototype>(vm.heap)) JSPromisePrototype(exec, structure); + object->finishCreation(vm, structure); + return object; +} + +Structure* JSPromisePrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +JSPromisePrototype::JSPromisePrototype(ExecState* exec, Structure* structure) + : JSNonFinalObject(exec->vm(), structure) +{ +} + +void JSPromisePrototype::finishCreation(VM& vm, Structure*) +{ + Base::finishCreation(vm); +} + +bool JSPromisePrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<JSObject>(exec, promisePrototypeTable, jsCast<JSPromisePrototype*>(object), propertyName, slot); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSPromisePrototype.h b/Source/JavaScriptCore/runtime/JSPromisePrototype.h new file mode 100644 index 000000000..458d14119 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPromisePrototype.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef JSPromisePrototype_h +#define JSPromisePrototype_h + +#include "JSObject.h" + +namespace JSC { + +class JSPromisePrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static JSPromisePrototype* create(ExecState*, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, Structure*); + +private: + JSPromisePrototype(ExecState*, Structure*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // JSPromisePrototype_h diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp b/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp new file mode 100644 index 000000000..690920f0c --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "JSPropertyNameEnumerator.h" + +#include "JSCInlines.h" +#include "StrongInlines.h" + +namespace JSC { + +const ClassInfo JSPropertyNameEnumerator::s_info = { "JSPropertyNameEnumerator", 0, 0, CREATE_METHOD_TABLE(JSPropertyNameEnumerator) }; + +JSPropertyNameEnumerator* JSPropertyNameEnumerator::create(VM& vm) +{ + if (!vm.emptyPropertyNameEnumerator.get()) { + PropertyNameArray propertyNames(&vm, PropertyNameMode::Strings); + vm.emptyPropertyNameEnumerator = Strong<JSCell>(vm, create(vm, 0, 0, 0, propertyNames)); + } + return jsCast<JSPropertyNameEnumerator*>(vm.emptyPropertyNameEnumerator.get()); +} + +JSPropertyNameEnumerator* JSPropertyNameEnumerator::create(VM& vm, Structure* structure, uint32_t indexedLength, uint32_t numberStructureProperties, PropertyNameArray& propertyNames) +{ + StructureID structureID = structure ? structure->id() : 0; + uint32_t inlineCapacity = structure ? structure->inlineCapacity() : 0; + JSPropertyNameEnumerator* enumerator = new (NotNull, + allocateCell<JSPropertyNameEnumerator>(vm.heap)) JSPropertyNameEnumerator(vm, structureID, inlineCapacity); + enumerator->finishCreation(vm, indexedLength, numberStructureProperties, propertyNames.data()); + return enumerator; +} + +JSPropertyNameEnumerator::JSPropertyNameEnumerator(VM& vm, StructureID structureID, uint32_t inlineCapacity) + : JSCell(vm, vm.propertyNameEnumeratorStructure.get()) + , m_cachedStructureID(structureID) + , m_cachedInlineCapacity(inlineCapacity) +{ +} + +void JSPropertyNameEnumerator::finishCreation(VM& vm, uint32_t indexedLength, uint32_t endStructurePropertyIndex, PassRefPtr<PropertyNameArrayData> idents) +{ + Base::finishCreation(vm); + + RefPtr<PropertyNameArrayData> identifiers = idents; + PropertyNameArrayData::PropertyNameVector& vector = identifiers->propertyNameVector(); + + m_indexedLength = indexedLength; + m_endStructurePropertyIndex = endStructurePropertyIndex; + m_endGenericPropertyIndex = vector.size(); + + m_propertyNames.resizeToFit(vector.size()); + for (unsigned i = 0; i < vector.size(); ++i) { + const Identifier& identifier = vector[i]; + m_propertyNames[i].set(vm, this, jsString(&vm, identifier.string())); + } +} + +void JSPropertyNameEnumerator::destroy(JSCell* cell) +{ + jsCast<JSPropertyNameEnumerator*>(cell)->JSPropertyNameEnumerator::~JSPropertyNameEnumerator(); +} + +void JSPropertyNameEnumerator::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Base::visitChildren(cell, visitor); + JSPropertyNameEnumerator* thisObject = jsCast<JSPropertyNameEnumerator*>(cell); + for (unsigned i = 0; i < thisObject->m_propertyNames.size(); ++i) + visitor.append(&thisObject->m_propertyNames[i]); + visitor.append(&thisObject->m_prototypeChain); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.h b/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.h new file mode 100644 index 000000000..c9ece11ab --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef JSPropertyNameEnumerator_h +#define JSPropertyNameEnumerator_h + +#include "JSCell.h" +#include "Operations.h" +#include "PropertyNameArray.h" +#include "Structure.h" + +namespace JSC { + +class Identifier; + +class JSPropertyNameEnumerator final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static JSPropertyNameEnumerator* create(VM&); + static JSPropertyNameEnumerator* create(VM&, Structure*, uint32_t, uint32_t, PropertyNameArray&); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); + } + + DECLARE_EXPORT_INFO; + + JSString* propertyNameAtIndex(uint32_t index) const + { + if (index >= m_propertyNames.size()) + return nullptr; + return m_propertyNames[index].get(); + } + + StructureChain* cachedPrototypeChain() const { return m_prototypeChain.get(); } + void setCachedPrototypeChain(VM& vm, StructureChain* prototypeChain) { return m_prototypeChain.set(vm, this, prototypeChain); } + + Structure* cachedStructure(VM& vm) const + { + if (!m_cachedStructureID) + return nullptr; + return vm.heap.structureIDTable().get(m_cachedStructureID); + } + StructureID cachedStructureID() const { return m_cachedStructureID; } + uint32_t indexedLength() const { return m_indexedLength; } + uint32_t endStructurePropertyIndex() const { return m_endStructurePropertyIndex; } + uint32_t endGenericPropertyIndex() const { return m_endGenericPropertyIndex; } + uint32_t cachedInlineCapacity() const { return m_cachedInlineCapacity; } + static ptrdiff_t cachedStructureIDOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_cachedStructureID); } + static ptrdiff_t indexedLengthOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_indexedLength); } + static ptrdiff_t endStructurePropertyIndexOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_endStructurePropertyIndex); } + static ptrdiff_t endGenericPropertyIndexOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_endGenericPropertyIndex); } + static ptrdiff_t cachedInlineCapacityOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_cachedInlineCapacity); } + static ptrdiff_t cachedPropertyNamesVectorOffset() + { + return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_propertyNames) + Vector<WriteBarrier<JSString>>::dataMemoryOffset(); + } + + static void visitChildren(JSCell*, SlotVisitor&); + +private: + JSPropertyNameEnumerator(VM&, StructureID, uint32_t); + void finishCreation(VM&, uint32_t, uint32_t, PassRefPtr<PropertyNameArrayData>); + + Vector<WriteBarrier<JSString>> m_propertyNames; + StructureID m_cachedStructureID; + WriteBarrier<StructureChain> m_prototypeChain; + uint32_t m_indexedLength; + uint32_t m_endStructurePropertyIndex; + uint32_t m_endGenericPropertyIndex; + uint32_t m_cachedInlineCapacity; +}; + +inline JSPropertyNameEnumerator* propertyNameEnumerator(ExecState* exec, JSObject* base) +{ + VM& vm = exec->vm(); + + uint32_t indexedLength = base->methodTable(vm)->getEnumerableLength(exec, base); + + JSPropertyNameEnumerator* enumerator = nullptr; + + Structure* structure = base->structure(vm); + if (!indexedLength + && (enumerator = structure->cachedPropertyNameEnumerator()) + && enumerator->cachedPrototypeChain() == structure->prototypeChain(exec)) + return enumerator; + + uint32_t numberStructureProperties = 0; + + PropertyNameArray propertyNames(exec, PropertyNameMode::Strings); + + if (structure->canAccessPropertiesQuickly() && indexedLength == base->getArrayLength()) { + base->methodTable(vm)->getStructurePropertyNames(base, exec, propertyNames, EnumerationMode()); + + numberStructureProperties = propertyNames.size(); + + base->methodTable(vm)->getGenericPropertyNames(base, exec, propertyNames, EnumerationMode()); + } else + base->methodTable(vm)->getPropertyNames(base, exec, propertyNames, EnumerationMode()); + + ASSERT(propertyNames.size() < UINT32_MAX); + + normalizePrototypeChain(exec, structure); + + enumerator = JSPropertyNameEnumerator::create(vm, structure, indexedLength, numberStructureProperties, propertyNames); + enumerator->setCachedPrototypeChain(vm, structure->prototypeChain(exec)); + if (!indexedLength && structure->canCachePropertyNameEnumerator()) + structure->setCachedPropertyNameEnumerator(vm, enumerator); + return enumerator; +} + +} // namespace JSC + +#endif // JSPropertyNameEnumerator_h diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp new file mode 100644 index 000000000..a9749a524 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSPropertyNameIterator.h" + +#include "IdentifierInlines.h" +#include "IteratorOperations.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSPropertyNameEnumerator.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL propertyNameIteratorFuncNext(ExecState*); + +const ClassInfo JSPropertyNameIterator::s_info = { "PropertyName Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(JSPropertyNameIterator) }; + +JSPropertyNameIterator::JSPropertyNameIterator(VM& vm, Structure* structure, JSObject* object, JSPropertyNameEnumerator* enumerator) + : Base(vm, structure) + , m_iteratedObject(vm, this, object) + , m_propertyNameEnumerator(vm, this, enumerator) + , m_enumerationPhase(EnumerationPhase::IndexedNames) + , m_cursor(0) +{ +} + +JSPropertyNameIterator* JSPropertyNameIterator::clone(ExecState* exec) +{ + auto iterator = JSPropertyNameIterator::create(exec, exec->callee()->globalObject()->propertyNameIteratorStructure(), m_iteratedObject.get(), m_propertyNameEnumerator.get()); + iterator->m_enumerationPhase = m_enumerationPhase; + iterator->m_cursor = m_cursor; + return iterator; +} + +JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, Structure* structure, JSObject* iteratedObject) +{ + return JSPropertyNameIterator::create(exec, structure, iteratedObject, propertyNameEnumerator(exec, iteratedObject)); +} + +JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, Structure* structure, JSObject* iteratedObject, JSPropertyNameEnumerator* enumerator) +{ + VM& vm = exec->vm(); + JSPropertyNameIterator* instance = new (NotNull, allocateCell<JSPropertyNameIterator>(vm.heap)) JSPropertyNameIterator(vm, structure, iteratedObject, enumerator); + instance->finishCreation(vm, structure->globalObject()); + return instance; +} + +void JSPropertyNameIterator::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + JSC_NATIVE_FUNCTION(vm.propertyNames->next, propertyNameIteratorFuncNext, DontEnum, 0); +} + +void JSPropertyNameIterator::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSPropertyNameIterator* thisObject = jsCast<JSPropertyNameIterator*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_iteratedObject); + visitor.append(&thisObject->m_propertyNameEnumerator); +} + +bool JSPropertyNameIterator::next(ExecState* exec, JSValue& output) +{ + if (m_enumerationPhase == EnumerationPhase::IndexedNames) { + for (; m_cursor < m_propertyNameEnumerator->indexedLength();) { + uint32_t index = m_cursor++; + if (m_iteratedObject->hasProperty(exec, index)) { + output = jsString(exec, Identifier::from(exec, index).string()); + return true; + } + } + m_cursor = 0; + m_enumerationPhase = EnumerationPhase::StructureNames; + } + + if (m_enumerationPhase == EnumerationPhase::StructureNames) { + for (; m_cursor < m_propertyNameEnumerator->endStructurePropertyIndex();) { + uint32_t index = m_cursor++; + JSString* propertyName = m_propertyNameEnumerator->propertyNameAtIndex(index); + ASSERT(propertyName); + if (m_iteratedObject->structure(exec->vm())->id() == m_propertyNameEnumerator->cachedStructureID()) { + output = propertyName; + return true; + } + + if (m_iteratedObject->hasProperty(exec, propertyName->toIdentifier(exec))) { + output = propertyName; + return true; + } + } + ASSERT(m_cursor >= m_propertyNameEnumerator->endStructurePropertyIndex()); + // Use the same m_cursor in the GenericNames phase. + m_enumerationPhase = EnumerationPhase::GenericNames; + } + + if (m_enumerationPhase == EnumerationPhase::GenericNames) { + for (; m_cursor < m_propertyNameEnumerator->endGenericPropertyIndex();) { + uint32_t index = m_cursor++; + JSString* propertyName = m_propertyNameEnumerator->propertyNameAtIndex(index); + ASSERT(propertyName); + if (m_iteratedObject->hasProperty(exec, propertyName->toIdentifier(exec))) { + output = propertyName; + return true; + } + } + m_enumerationPhase = EnumerationPhase::Done; + } + + return false; +} + +// ------------------------------ PropertyNameIterator Functions ---------------------------- + +EncodedJSValue JSC_HOST_CALL propertyNameIteratorFuncNext(ExecState* exec) +{ + JSPropertyNameIterator* iterator = jsDynamicCast<JSPropertyNameIterator*>(exec->thisValue()); + if (!iterator) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Cannot call PropertyNameIterator.next() on a non-PropertyNameIterator object"))); + + JSValue result; + if (iterator->next(exec, result)) + return JSValue::encode(createIteratorResultObject(exec, result, false)); + return JSValue::encode(createIteratorResultObject(exec, jsUndefined(), true)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h new file mode 100644 index 000000000..bf0dc10f3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSPropertyNameIterator_h +#define JSPropertyNameIterator_h + +#include "JSObject.h" +#include "JSPropertyNameEnumerator.h" + +namespace JSC { + +class JSPropertyNameIterator : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + enum class EnumerationPhase : uint32_t { + IndexedNames, + StructureNames, + GenericNames, + Done + }; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSPropertyNameIterator* create(ExecState*, Structure*, JSObject*); + + JSPropertyNameIterator* clone(ExecState*); + bool next(ExecState*, JSValue&); + + JSValue iteratedValue() const { return m_iteratedObject.get(); } + + static void visitChildren(JSCell*, SlotVisitor&); + +private: + JSPropertyNameIterator(VM&, Structure*, JSObject*, JSPropertyNameEnumerator*); + + void finishCreation(VM&, JSGlobalObject*); + + static JSPropertyNameIterator* create(ExecState*, Structure*, JSObject*, JSPropertyNameEnumerator*); + + WriteBarrier<JSObject> m_iteratedObject; + WriteBarrier<JSPropertyNameEnumerator> m_propertyNameEnumerator; + EnumerationPhase m_enumerationPhase; + uint32_t m_cursor; +}; + +} + +#endif // JSPropertyNameIterator_h diff --git a/Source/JavaScriptCore/runtime/JSProxy.cpp b/Source/JavaScriptCore/runtime/JSProxy.cpp new file mode 100644 index 000000000..f4c6829e0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSProxy.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "JSProxy.h" + +#include "JSGlobalObject.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSProxy); + +const ClassInfo JSProxy::s_info = { "JSProxy", &Base::s_info, 0, CREATE_METHOD_TABLE(JSProxy) }; + +void JSProxy::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_target); +} + +void JSProxy::setTarget(VM& vm, JSGlobalObject* globalObject) +{ + ASSERT_ARG(globalObject, globalObject); + m_target.set(vm, this, globalObject); + setPrototype(vm, globalObject->prototype()); + + PrototypeMap& prototypeMap = vm.prototypeMap; + if (!prototypeMap.isPrototype(this)) + return; + + // This is slow but constant time. We think it's very rare for a proxy + // to be a prototype, and reasonably rare to retarget a proxy, + // so slow constant time is OK. + for (size_t i = 0; i <= JSFinalObject::maxInlineCapacity(); ++i) + prototypeMap.clearEmptyObjectStructureForPrototype(this, i); +} + +String JSProxy::className(const JSObject* object) +{ + const JSProxy* thisObject = jsCast<const JSProxy*>(object); + return thisObject->target()->methodTable()->className(thisObject->target()); +} + +bool JSProxy::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + return thisObject->target()->methodTable(exec->vm())->getOwnPropertySlot(thisObject->target(), exec, propertyName, slot); +} + +bool JSProxy::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + return thisObject->target()->methodTable(exec->vm())->getOwnPropertySlotByIndex(thisObject->target(), exec, propertyName, slot); +} + +void JSProxy::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + thisObject->target()->methodTable(exec->vm())->put(thisObject->target(), exec, propertyName, value, slot); +} + +void JSProxy::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + thisObject->target()->methodTable(exec->vm())->putByIndex(thisObject->target(), exec, propertyName, value, shouldThrow); +} + +bool JSProxy::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + return thisObject->target()->methodTable(exec->vm())->defineOwnProperty(thisObject->target(), exec, propertyName, descriptor, shouldThrow); +} + +bool JSProxy::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + return thisObject->target()->methodTable(exec->vm())->deleteProperty(thisObject->target(), exec, propertyName); +} + +bool JSProxy::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + return thisObject->target()->methodTable(exec->vm())->deletePropertyByIndex(thisObject->target(), exec, propertyName); +} + +void JSProxy::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + thisObject->target()->methodTable(exec->vm())->getPropertyNames(thisObject->target(), exec, propertyNames, mode); +} + +uint32_t JSProxy::getEnumerableLength(ExecState* exec, JSObject* object) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + return thisObject->target()->methodTable(exec->vm())->getEnumerableLength(exec, thisObject->target()); +} + +void JSProxy::getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode) +{ + // Skip the structure loop, since it is invalid for proxies. +} + +void JSProxy::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + // Get *all* of the property names, not just the generic ones, since we skipped the structure + // ones above. + thisObject->target()->methodTable(exec->vm())->getPropertyNames(thisObject->target(), exec, propertyNames, mode); +} + +void JSProxy::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + thisObject->target()->methodTable(exec->vm())->getOwnPropertyNames(thisObject->target(), exec, propertyNames, mode); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSProxy.h b/Source/JavaScriptCore/runtime/JSProxy.h new file mode 100644 index 000000000..2bdcd1b72 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSProxy.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSProxy_h +#define JSProxy_h + +#include "JSDestructibleObject.h" + +namespace JSC { + +class JSProxy : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero; + + static JSProxy* create(VM& vm, Structure* structure, JSObject* target) + { + JSProxy* proxy = new (NotNull, allocateCell<JSProxy>(vm.heap)) JSProxy(vm, structure); + proxy->finishCreation(vm, target); + return proxy; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, JSType proxyType = ImpureProxyType) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(proxyType, StructureFlags), info()); + } + + DECLARE_EXPORT_INFO; + + JSObject* target() const { return m_target.get(); } + static ptrdiff_t targetOffset() { return OBJECT_OFFSETOF(JSProxy, m_target); } + +protected: + JSProxy(VM& vm, Structure* structure) + : JSDestructibleObject(vm, structure) + { + } + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + } + + void finishCreation(VM& vm, JSObject* target) + { + Base::finishCreation(vm); + m_target.set(vm, this, target); + } + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + + JS_EXPORT_PRIVATE void setTarget(VM&, JSGlobalObject*); + + JS_EXPORT_PRIVATE static String className(const JSObject*); + JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned, PropertySlot&); + JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned, JSValue, bool shouldThrow); + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); + JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned); + JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static uint32_t getEnumerableLength(ExecState*, JSObject*); + JS_EXPORT_PRIVATE static void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + +private: + WriteBarrier<JSObject> m_target; +}; + +} // namespace JSC + +#endif // JSProxy_h diff --git a/Source/JavaScriptCore/runtime/JSScope.cpp b/Source/JavaScriptCore/runtime/JSScope.cpp new file mode 100644 index 000000000..bb2efdd06 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSScope.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2012-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSScope.h" + +#include "JSGlobalObject.h" +#include "JSLexicalEnvironment.h" +#include "JSWithScope.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSScope); + +void JSScope::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSScope* thisObject = jsCast<JSScope*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_next); +} + +// Returns true if we found enough information to terminate optimization. +static inline bool abstractAccess(ExecState* exec, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, size_t depth, bool& needsVarInjectionChecks, ResolveOp& op) +{ + if (JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(scope)) { + if (ident == exec->propertyNames().arguments) { + // We know the property will be at this lexical environment scope, but we don't know how to cache it. + op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); + return true; + } + + SymbolTableEntry entry = lexicalEnvironment->symbolTable()->get(ident.impl()); + if (entry.isReadOnly() && getOrPut == Put) { + // We know the property will be at this lexical environment scope, but we don't know how to cache it. + op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); + return true; + } + + if (!entry.isNull()) { + op = ResolveOp(makeType(ClosureVar, needsVarInjectionChecks), depth, 0, lexicalEnvironment, entry.watchpointSet(), entry.scopeOffset().offset()); + return true; + } + + if (lexicalEnvironment->symbolTable()->usesNonStrictEval()) + needsVarInjectionChecks = true; + return false; + } + + if (JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(scope)) { + SymbolTableEntry entry = globalObject->symbolTable()->get(ident.impl()); + if (!entry.isNull()) { + if (getOrPut == Put && entry.isReadOnly()) { + // We know the property will be at global scope, but we don't know how to cache it. + op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); + return true; + } + + op = ResolveOp( + makeType(GlobalVar, needsVarInjectionChecks), depth, 0, 0, entry.watchpointSet(), + reinterpret_cast<uintptr_t>(globalObject->variableAt(entry.scopeOffset()).slot())); + return true; + } + + PropertySlot slot(globalObject); + if (!globalObject->getOwnPropertySlot(globalObject, exec, ident, slot) + || !slot.isCacheableValue() + || !globalObject->structure()->propertyAccessesAreCacheable() + || (globalObject->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto() && getOrPut == Put)) { + // We know the property will be at global scope, but we don't know how to cache it. + ASSERT(!scope->next()); + op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, 0, 0, 0, 0); + return true; + } + + WatchpointState state = globalObject->structure()->ensurePropertyReplacementWatchpointSet(exec->vm(), slot.cachedOffset())->state(); + if (state == IsWatched && getOrPut == Put) { + // The field exists, but because the replacement watchpoint is still intact. This is + // kind of dangerous. We have two options: + // 1) Invalidate the watchpoint set. That would work, but it's possible that this code + // path never executes - in which case this would be unwise. + // 2) Have the invalidation happen at run-time. All we have to do is leave the code + // uncached. The only downside is slightly more work when this does execute. + // We go with option (2) here because it seems less evil. + op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, 0, 0, 0, 0); + } else + op = ResolveOp(makeType(GlobalProperty, needsVarInjectionChecks), depth, globalObject->structure(), 0, 0, slot.cachedOffset()); + return true; + } + + op = ResolveOp(Dynamic, 0, 0, 0, 0, 0); + return true; +} + +JSObject* JSScope::objectAtScope(JSScope* scope) +{ + JSObject* object = scope; + if (object->type() == WithScopeType) + return jsCast<JSWithScope*>(object)->object(); + + return object; +} + +// When an exception occurs, the result of isUnscopable becomes false. +static inline bool isUnscopable(ExecState* exec, JSScope* scope, JSObject* object, const Identifier& ident) +{ + if (scope->type() != WithScopeType) + return false; + + JSValue unscopables = object->get(exec, exec->propertyNames().unscopablesSymbol); + if (exec->hadException()) + return false; + if (!unscopables.isObject()) + return false; + JSValue blocked = jsCast<JSObject*>(unscopables)->get(exec, ident); + if (exec->hadException()) + return false; + + return blocked.toBoolean(exec); +} + +JSValue JSScope::resolve(ExecState* exec, JSScope* scope, const Identifier& ident) +{ + ScopeChainIterator end = scope->end(); + ScopeChainIterator it = scope->begin(); + while (1) { + JSScope* scope = it.scope(); + JSObject* object = it.get(); + + if (++it == end) // Global scope. + return object; + + if (object->hasProperty(exec, ident)) { + if (!isUnscopable(exec, scope, object, ident)) + return object; + ASSERT_WITH_MESSAGE(!exec->hadException(), "When an exception occurs, the result of isUnscopable becomes false"); + } + } +} + +ResolveOp JSScope::abstractResolve(ExecState* exec, size_t depthOffset, JSScope* scope, const Identifier& ident, GetOrPut getOrPut, ResolveType unlinkedType) +{ + ResolveOp op(Dynamic, 0, 0, 0, 0, 0); + if (unlinkedType == Dynamic) + return op; + + bool needsVarInjectionChecks = JSC::needsVarInjectionChecks(unlinkedType); + size_t depth = depthOffset; + for (; scope; scope = scope->next()) { + if (abstractAccess(exec, scope, ident, getOrPut, depth, needsVarInjectionChecks, op)) + break; + ++depth; + } + + return op; +} + +void JSScope::collectVariablesUnderTDZ(JSScope* scope, VariableEnvironment& result) +{ + for (; scope; scope = scope->next()) { + if (!scope->isLexicalScope()) + continue; + SymbolTable* symbolTable = jsCast<JSLexicalEnvironment*>(scope)->symbolTable(); + ASSERT(symbolTable->scopeType() == SymbolTable::ScopeType::LexicalScope); + ConcurrentJITLocker locker(symbolTable->m_lock); + for (auto end = symbolTable->end(locker), iter = symbolTable->begin(locker); iter != end; ++iter) + result.add(iter->key); + } +} + +bool JSScope::isLexicalScope() +{ + JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(this); + if (!lexicalEnvironment) // Global object does not hold any lexical variables so we can ignore it. + return false; + + return lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::LexicalScope; +} + +bool JSScope::isCatchScope() +{ + JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(this); + if (!lexicalEnvironment) + return false; + return lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::CatchScope; +} + +bool JSScope::isFunctionNameScopeObject() +{ + JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(this); + if (!lexicalEnvironment) + return false; + return lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::FunctionNameScope; +} + +const char* resolveModeName(ResolveMode mode) +{ + static const char* const names[] = { + "ThrowIfNotFound", + "DoNotThrowIfNotFound" + }; + return names[mode]; +} + +const char* resolveTypeName(ResolveType type) +{ + static const char* const names[] = { + "GlobalProperty", + "GlobalVar", + "ClosureVar", + "LocalClosureVar", + "GlobalPropertyWithVarInjectionChecks", + "GlobalVarWithVarInjectionChecks", + "ClosureVarWithVarInjectionChecks", + "Dynamic" + }; + return names[type]; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSScope.h b/Source/JavaScriptCore/runtime/JSScope.h new file mode 100644 index 000000000..a65316749 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSScope.h @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2012, 2013, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSScope_h +#define JSScope_h + +#include "JSObject.h" +#include "VariableEnvironment.h" + +namespace JSC { + +class ScopeChainIterator; +class WatchpointSet; + +enum ResolveMode { + ThrowIfNotFound, + DoNotThrowIfNotFound +}; + +enum ResolveType { + // Lexical scope guaranteed a certain type of variable access. + GlobalProperty, + GlobalVar, + ClosureVar, + LocalClosureVar, + + // Ditto, but at least one intervening scope used non-strict eval, which + // can inject an intercepting var delcaration at runtime. + GlobalPropertyWithVarInjectionChecks, + GlobalVarWithVarInjectionChecks, + ClosureVarWithVarInjectionChecks, + + // Lexical scope didn't prove anything -- probably because of a 'with' scope. + Dynamic +}; + +const char* resolveModeName(ResolveMode mode); +const char* resolveTypeName(ResolveType type); + +inline ResolveType makeType(ResolveType type, bool needsVarInjectionChecks) +{ + if (!needsVarInjectionChecks) + return type; + + switch (type) { + case GlobalProperty: + return GlobalPropertyWithVarInjectionChecks; + case GlobalVar: + return GlobalVarWithVarInjectionChecks; + case ClosureVar: + case LocalClosureVar: + return ClosureVarWithVarInjectionChecks; + case GlobalPropertyWithVarInjectionChecks: + case GlobalVarWithVarInjectionChecks: + case ClosureVarWithVarInjectionChecks: + case Dynamic: + return type; + } + + RELEASE_ASSERT_NOT_REACHED(); + return type; +} + +inline bool needsVarInjectionChecks(ResolveType type) +{ + switch (type) { + case GlobalProperty: + case GlobalVar: + case ClosureVar: + case LocalClosureVar: + return false; + case GlobalPropertyWithVarInjectionChecks: + case GlobalVarWithVarInjectionChecks: + case ClosureVarWithVarInjectionChecks: + case Dynamic: + return true; + default: + RELEASE_ASSERT_NOT_REACHED(); + return true; + } +} + +struct ResolveOp { + ResolveOp(ResolveType type, size_t depth, Structure* structure, JSLexicalEnvironment* lexicalEnvironment, WatchpointSet* watchpointSet, uintptr_t operand) + : type(type) + , depth(depth) + , structure(structure) + , lexicalEnvironment(lexicalEnvironment) + , watchpointSet(watchpointSet) + , operand(operand) + { + } + + ResolveType type; + size_t depth; + Structure* structure; + JSLexicalEnvironment* lexicalEnvironment; + WatchpointSet* watchpointSet; + uintptr_t operand; +}; + +class ResolveModeAndType { + typedef unsigned Operand; +public: + static const size_t shift = sizeof(Operand) * 8 / 2; + static const unsigned mask = (1 << shift) - 1; + + ResolveModeAndType(ResolveMode resolveMode, ResolveType resolveType) + : m_operand((resolveMode << shift) | resolveType) + { + } + + explicit ResolveModeAndType(unsigned operand) + : m_operand(operand) + { + } + + ResolveMode mode() { return static_cast<ResolveMode>(m_operand >> shift); } + ResolveType type() { return static_cast<ResolveType>(m_operand & mask); } + unsigned operand() { return m_operand; } + +private: + Operand m_operand; +}; + +enum GetOrPut { Get, Put }; + +class JSScope : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags; + + friend class LLIntOffsetsExtractor; + static size_t offsetOfNext(); + + static JSObject* objectAtScope(JSScope*); + + static JSValue resolve(ExecState*, JSScope*, const Identifier&); + static ResolveOp abstractResolve(ExecState*, size_t depthOffset, JSScope*, const Identifier&, GetOrPut, ResolveType); + + static void collectVariablesUnderTDZ(JSScope*, VariableEnvironment& result); + + static void visitChildren(JSCell*, SlotVisitor&); + + bool isLexicalScope(); + bool isCatchScope(); + bool isFunctionNameScopeObject(); + + ScopeChainIterator begin(); + ScopeChainIterator end(); + JSScope* next(); + + JSGlobalObject* globalObject(); + VM* vm(); + JSObject* globalThis(); + +protected: + JSScope(VM&, Structure*, JSScope* next); + +private: + WriteBarrier<JSScope> m_next; +}; + +inline JSScope::JSScope(VM& vm, Structure* structure, JSScope* next) + : Base(vm, structure) + , m_next(vm, this, next, WriteBarrier<JSScope>::MayBeNull) +{ +} + +class ScopeChainIterator { +public: + ScopeChainIterator(JSScope* node) + : m_node(node) + { + } + + JSObject* get() const { return JSScope::objectAtScope(m_node); } + JSObject* operator->() const { return JSScope::objectAtScope(m_node); } + JSScope* scope() const { return m_node; } + + ScopeChainIterator& operator++() { m_node = m_node->next(); return *this; } + + // postfix ++ intentionally omitted + + bool operator==(const ScopeChainIterator& other) const { return m_node == other.m_node; } + bool operator!=(const ScopeChainIterator& other) const { return m_node != other.m_node; } + +private: + JSScope* m_node; +}; + +inline ScopeChainIterator JSScope::begin() +{ + return ScopeChainIterator(this); +} + +inline ScopeChainIterator JSScope::end() +{ + return ScopeChainIterator(0); +} + +inline JSScope* JSScope::next() +{ + return m_next.get(); +} + +inline JSGlobalObject* JSScope::globalObject() +{ + return structure()->globalObject(); +} + +inline VM* JSScope::vm() +{ + return MarkedBlock::blockFor(this)->vm(); +} + +inline Register& Register::operator=(JSScope* scope) +{ + *this = JSValue(scope); + return *this; +} + +inline JSScope* Register::scope() const +{ + return jsCast<JSScope*>(jsValue()); +} + +inline JSGlobalObject* ExecState::lexicalGlobalObject() const +{ + return callee()->globalObject(); +} + +inline size_t JSScope::offsetOfNext() +{ + return OBJECT_OFFSETOF(JSScope, m_next); +} + +} // namespace JSC + +#endif // JSScope_h diff --git a/Source/JavaScriptCore/runtime/JSSegmentedVariableObject.cpp b/Source/JavaScriptCore/runtime/JSSegmentedVariableObject.cpp new file mode 100644 index 000000000..2502fa839 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSSegmentedVariableObject.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012, 2013, 2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "JSSegmentedVariableObject.h" + +#include "JSCInlines.h" + +namespace JSC { + +ScopeOffset JSSegmentedVariableObject::findVariableIndex(void* variableAddress) +{ + ConcurrentJITLocker locker(m_lock); + + for (unsigned i = m_variables.size(); i--;) { + if (&m_variables[i] != variableAddress) + continue; + return ScopeOffset(i); + } + CRASH(); + return ScopeOffset(); +} + +ScopeOffset JSSegmentedVariableObject::addVariables(unsigned numberOfVariablesToAdd) +{ + ConcurrentJITLocker locker(m_lock); + + size_t oldSize = m_variables.size(); + m_variables.grow(oldSize + numberOfVariablesToAdd); + + for (size_t i = numberOfVariablesToAdd; i--;) + m_variables[oldSize + i].setWithoutWriteBarrier(jsUndefined()); + + return ScopeOffset(oldSize); +} + +void JSSegmentedVariableObject::visitChildren(JSCell* cell, SlotVisitor& slotVisitor) +{ + JSSegmentedVariableObject* thisObject = jsCast<JSSegmentedVariableObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + JSSymbolTableObject::visitChildren(thisObject, slotVisitor); + + for (unsigned i = thisObject->m_variables.size(); i--;) + slotVisitor.append(&thisObject->m_variables[i]); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSSegmentedVariableObject.h b/Source/JavaScriptCore/runtime/JSSegmentedVariableObject.h new file mode 100644 index 000000000..feaf0bb0e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSSegmentedVariableObject.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2012, 2013, 2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef JSSegmentedVariableObject_h +#define JSSegmentedVariableObject_h + +#include "ConcurrentJITLock.h" +#include "JSObject.h" +#include "JSSymbolTableObject.h" +#include "Register.h" +#include "SymbolTable.h" +#include <wtf/SegmentedVector.h> + +namespace JSC { + +class LLIntOffsetsExtractor; +class Register; + +// This is a mostly drop-in replacement for JSEnvironmentRecord, except that it preserves +// the invariant that after a variable is created, its address in memory will not change +// so long as the JSSegmentedVariableObject is alive. This allows optimizations based +// on getting the address of the variable and remembering it. As well, unlike a +// JSEnvironmentRecord, this will manage the memory for the registers itself and neither +// requires nor allows for the subclasses to manage that memory. Finally, +// JSSegmentedVariableObject has its own GC tracing functionality, since it knows the +// exact dimensions of the variables array at all times. + +class JSSegmentedVariableObject : public JSSymbolTableObject { + friend class JIT; + friend class LLIntOffsetsExtractor; + +public: + typedef JSSymbolTableObject Base; + + // This is not thread-safe, since m_variables is a segmented vector, and its spine can resize with + // malloc/free if new variables - unrelated to the one you are accessing - are added. You can get + // around this by grabbing m_lock, or finding some other way to get to the variable pointer (global + // variable access bytecode instructions will have a direct pointer already). + WriteBarrier<Unknown>& variableAt(ScopeOffset offset) { return m_variables[offset.offset()]; } + + // This is a slow method call, which searches the register bank to find the index + // given a pointer. It will CRASH() if it does not find the register. Only use this + // in debug code (like bytecode dumping). + JS_EXPORT_PRIVATE ScopeOffset findVariableIndex(void*); + + WriteBarrier<Unknown>* assertVariableIsInThisObject(WriteBarrier<Unknown>* variablePointer) + { + if (!ASSERT_DISABLED) + findVariableIndex(variablePointer); + return variablePointer; + } + + // Adds numberOfRegistersToAdd registers, initializes them to Undefined, and returns + // the index of the first one added. + JS_EXPORT_PRIVATE ScopeOffset addVariables(unsigned numberOfVariablesToAdd); + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + +protected: + JSSegmentedVariableObject(VM& vm, Structure* structure, JSScope* scope) + : JSSymbolTableObject(vm, structure, scope) + { + } + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + setSymbolTable(vm, SymbolTable::create(vm)); + } + + SegmentedVector<WriteBarrier<Unknown>, 16> m_variables; + ConcurrentJITLock m_lock; +}; + +} // namespace JSC + +#endif // JSSegmentedVariableObject_h + diff --git a/Source/JavaScriptCore/runtime/JSSet.cpp b/Source/JavaScriptCore/runtime/JSSet.cpp new file mode 100644 index 000000000..d875345da --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSSet.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSSet.h" + +#include "JSCJSValueInlines.h" +#include "JSSetIterator.h" +#include "MapDataInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSSet::s_info = { "Set", &Base::s_info, 0, CREATE_METHOD_TABLE(JSSet) }; + +void JSSet::destroy(JSCell* cell) +{ + JSSet* thisObject = jsCast<JSSet*>(cell); + thisObject->JSSet::~JSSet(); +} + +void JSSet::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Base::visitChildren(cell, visitor); + jsCast<JSSet*>(cell)->m_setData.visitChildren(cell, visitor); +} + +void JSSet::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token) +{ + Base::copyBackingStore(cell, visitor, token); + jsCast<JSSet*>(cell)->m_setData.copyBackingStore(visitor, token); +} + +bool JSSet::has(ExecState* exec, JSValue value) +{ + return m_setData.contains(exec, value); +} + +size_t JSSet::size(ExecState* exec) +{ + return m_setData.size(exec); +} + +void JSSet::add(ExecState* exec, JSValue value) +{ + m_setData.set(exec, this, value, value); +} + +void JSSet::clear(ExecState*) +{ + m_setData.clear(); +} + +bool JSSet::remove(ExecState* exec, JSValue value) +{ + return m_setData.remove(exec, value); +} + +} diff --git a/Source/JavaScriptCore/runtime/JSSet.h b/Source/JavaScriptCore/runtime/JSSet.h new file mode 100644 index 000000000..e55362ebb --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSSet.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSSet_h +#define JSSet_h + +#include "JSDestructibleObject.h" +#include "JSObject.h" +#include "MapData.h" + +namespace JSC { + +class JSSetIterator; + +class JSSet : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + friend class JSSetIterator; + + // Our marking functions expect Entry to maintain this layout, and have all + // fields be WriteBarrier<Unknown> + class Entry { + private: + WriteBarrier<Unknown> m_key; + + public: + const WriteBarrier<Unknown>& key() const + { + return m_key; + } + + const WriteBarrier<Unknown>& value() const + { + return m_key; + } + + void visitChildren(SlotVisitor& visitor) + { + visitor.append(&m_key); + } + + void setKey(VM& vm, const JSCell* owner, JSValue key) + { + m_key.set(vm, owner, key); + } + + void setKeyWithoutWriteBarrier(JSValue key) + { + m_key.setWithoutWriteBarrier(key); + } + + void setValue(VM&, const JSCell*, JSValue) + { + } + + void clear() + { + m_key.clear(); + } + }; + + typedef MapDataImpl<Entry, JSSetIterator> SetData; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSSet* create(VM& vm, Structure* structure) + { + JSSet* instance = new (NotNull, allocateCell<JSSet>(vm.heap)) JSSet(vm, structure); + instance->finishCreation(vm); + return instance; + } + + static JSSet* create(ExecState* exec, Structure* structure) + { + return create(exec->vm(), structure); + } + + bool has(ExecState*, JSValue); + size_t size(ExecState*); + JS_EXPORT_PRIVATE void add(ExecState*, JSValue); + void clear(ExecState*); + bool remove(ExecState*, JSValue); + +private: + JSSet(VM& vm, Structure* structure) + : Base(vm, structure) + , m_setData(vm) + { + } + + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); + + SetData m_setData; +}; + +} + +#endif // !defined(JSSet_h) diff --git a/Source/JavaScriptCore/runtime/JSSetIterator.cpp b/Source/JavaScriptCore/runtime/JSSetIterator.cpp new file mode 100644 index 000000000..3a0ddbeab --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSSetIterator.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSSetIterator.h" + +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSSet.h" +#include "MapDataInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSSetIterator::s_info = { "Set Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(JSSetIterator) }; + +void JSSetIterator::finishCreation(VM& vm, JSSet* iteratedObject) +{ + Base::finishCreation(vm); + m_set.set(vm, this, iteratedObject); +} + +void JSSetIterator::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSSetIterator* thisObject = jsCast<JSSetIterator*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_set); +} + +void JSSetIterator::destroy(JSCell* cell) +{ + JSSetIterator* thisObject = jsCast<JSSetIterator*>(cell); + thisObject->JSSetIterator::~JSSetIterator(); +} + +JSValue JSSetIterator::createPair(CallFrame* callFrame, JSValue key, JSValue value) +{ + MarkedArgumentBuffer args; + args.append(key); + args.append(value); + JSGlobalObject* globalObject = callFrame->callee()->globalObject(); + return constructArray(callFrame, 0, globalObject, args); +} + +JSSetIterator* JSSetIterator::clone(ExecState* exec) +{ + auto clone = JSSetIterator::create(exec->vm(), exec->callee()->globalObject()->setIteratorStructure(), m_set.get(), m_kind); + clone->m_iterator = m_iterator; + return clone; +} + +} diff --git a/Source/JavaScriptCore/runtime/JSSetIterator.h b/Source/JavaScriptCore/runtime/JSSetIterator.h new file mode 100644 index 000000000..6dad6b1b9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSSetIterator.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSSetIterator_h +#define JSSetIterator_h + +#include "JSDestructibleObject.h" +#include "JSSet.h" + +#include "MapData.h" + +namespace JSC { +enum SetIterationKind : uint32_t { + SetIterateKey, + SetIterateValue, + SetIterateKeyValue, +}; + +class JSSetIterator : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSSetIterator* create(VM& vm, Structure* structure, JSSet* iteratedObject, SetIterationKind kind) + { + JSSetIterator* instance = new (NotNull, allocateCell<JSSetIterator>(vm.heap)) JSSetIterator(vm, structure, iteratedObject, kind); + instance->finishCreation(vm, iteratedObject); + return instance; + } + + bool next(CallFrame* callFrame, JSValue& value) + { + WTF::KeyValuePair<JSValue, JSValue> pair; + if (!m_iterator.next(pair)) + return false; + if (m_kind == SetIterateValue || m_kind == SetIterateKey) + value = pair.key; + else + value = createPair(callFrame, pair.key, pair.key); + return true; + } + + void finish() + { + m_iterator.finish(); + } + + SetIterationKind kind() const { return m_kind; } + JSValue iteratedValue() const { return m_set.get(); } + JSSetIterator* clone(ExecState*); + + JSSet::SetData::IteratorData* iteratorData() + { + return &m_iterator; + } + +private: + JSSetIterator(VM& vm, Structure* structure, JSSet* iteratedObject, SetIterationKind kind) + : Base(vm, structure) + , m_iterator(iteratedObject->m_setData.createIteratorData(this)) + , m_kind(kind) + { + } + + static void destroy(JSCell*); + JS_EXPORT_PRIVATE void finishCreation(VM&, JSSet*); + JS_EXPORT_PRIVATE JSValue createPair(CallFrame*, JSValue, JSValue); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<JSSet> m_set; + JSSet::SetData::IteratorData m_iterator; + SetIterationKind m_kind; +}; + +} + +#endif // !defined(JSSetIterator_h) diff --git a/Source/JavaScriptCore/runtime/JSString.cpp b/Source/JavaScriptCore/runtime/JSString.cpp new file mode 100644 index 000000000..eb046ed2d --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSString.cpp @@ -0,0 +1,430 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "JSString.h" + +#include "JSGlobalObject.h" +#include "JSGlobalObjectFunctions.h" +#include "JSObject.h" +#include "JSCInlines.h" +#include "StringObject.h" +#include "StringPrototype.h" +#include "StrongInlines.h" + +namespace JSC { + +const ClassInfo JSString::s_info = { "string", 0, 0, CREATE_METHOD_TABLE(JSString) }; + +void JSRopeString::RopeBuilder::expand() +{ + ASSERT(m_index == JSRopeString::s_maxInternalRopeLength); + JSString* jsString = m_jsString; + RELEASE_ASSERT(jsString); + m_jsString = jsStringBuilder(&m_vm); + m_index = 0; + append(jsString); +} + +void JSString::destroy(JSCell* cell) +{ + JSString* thisObject = static_cast<JSString*>(cell); + thisObject->JSString::~JSString(); +} + +void JSString::dumpToStream(const JSCell* cell, PrintStream& out) +{ + const JSString* thisObject = jsCast<const JSString*>(cell); + out.printf("<%p, %s, [%u], ", thisObject, thisObject->className(), thisObject->length()); + if (thisObject->isRope()) + out.printf("[rope]"); + else { + WTF::StringImpl* ourImpl = thisObject->m_value.impl(); + if (ourImpl->is8Bit()) + out.printf("[8 %p]", ourImpl->characters8()); + else + out.printf("[16 %p]", ourImpl->characters16()); + } + out.printf(">"); +} + +void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSString* thisObject = jsCast<JSString*>(cell); + Base::visitChildren(thisObject, visitor); + + if (thisObject->isRope()) + static_cast<JSRopeString*>(thisObject)->visitFibers(visitor); + else { + StringImpl* impl = thisObject->m_value.impl(); + ASSERT(impl); + visitor.reportExtraMemoryVisited(thisObject, impl->costDuringGC()); + } +} + +void JSRopeString::visitFibers(SlotVisitor& visitor) +{ + if (isSubstring()) { + visitor.append(&substringBase()); + return; + } + for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) + visitor.append(&fiber(i)); +} + +static const unsigned maxLengthForOnStackResolve = 2048; + +void JSRopeString::resolveRopeInternal8(LChar* buffer) const +{ + if (isSubstring()) { + StringImpl::copyChars( + buffer, substringBase()->m_value.characters8() + substringOffset(), m_length); + return; + } + + resolveRopeInternal8NoSubstring(buffer); +} + +void JSRopeString::resolveRopeInternal8NoSubstring(LChar* buffer) const +{ + for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) { + if (fiber(i)->isRope()) { + resolveRopeSlowCase8(buffer); + return; + } + } + + LChar* position = buffer; + for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) { + const StringImpl& fiberString = *fiber(i)->m_value.impl(); + unsigned length = fiberString.length(); + StringImpl::copyChars(position, fiberString.characters8(), length); + position += length; + } + ASSERT((buffer + m_length) == position); +} + +void JSRopeString::resolveRopeInternal16(UChar* buffer) const +{ + if (isSubstring()) { + StringImpl::copyChars( + buffer, substringBase()->m_value.characters16() + substringOffset(), m_length); + return; + } + + resolveRopeInternal16NoSubstring(buffer); +} + +void JSRopeString::resolveRopeInternal16NoSubstring(UChar* buffer) const +{ + for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) { + if (fiber(i)->isRope()) { + resolveRopeSlowCase(buffer); + return; + } + } + + UChar* position = buffer; + for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) { + const StringImpl& fiberString = *fiber(i)->m_value.impl(); + unsigned length = fiberString.length(); + if (fiberString.is8Bit()) + StringImpl::copyChars(position, fiberString.characters8(), length); + else + StringImpl::copyChars(position, fiberString.characters16(), length); + position += length; + } + ASSERT((buffer + m_length) == position); +} + +void JSRopeString::resolveRopeToAtomicString(ExecState* exec) const +{ + if (m_length > maxLengthForOnStackResolve) { + resolveRope(exec); + m_value = AtomicString(m_value); + setIs8Bit(m_value.impl()->is8Bit()); + return; + } + + if (is8Bit()) { + LChar buffer[maxLengthForOnStackResolve]; + resolveRopeInternal8(buffer); + m_value = AtomicString(buffer, m_length); + setIs8Bit(m_value.impl()->is8Bit()); + } else { + UChar buffer[maxLengthForOnStackResolve]; + resolveRopeInternal16(buffer); + m_value = AtomicString(buffer, m_length); + setIs8Bit(m_value.impl()->is8Bit()); + } + + clearFibers(); + + // If we resolved a string that didn't previously exist, notify the heap that we've grown. + if (m_value.impl()->hasOneRef()) + Heap::heap(this)->reportExtraMemoryAllocated(m_value.impl()->cost()); +} + +void JSRopeString::clearFibers() const +{ + for (size_t i = 0; i < s_maxInternalRopeLength; ++i) + u[i].number = 0; +} + +RefPtr<AtomicStringImpl> JSRopeString::resolveRopeToExistingAtomicString(ExecState* exec) const +{ + if (m_length > maxLengthForOnStackResolve) { + resolveRope(exec); + if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(m_value.impl())) { + m_value = *existingAtomicString; + setIs8Bit(m_value.impl()->is8Bit()); + clearFibers(); + return existingAtomicString; + } + return nullptr; + } + + if (is8Bit()) { + LChar buffer[maxLengthForOnStackResolve]; + resolveRopeInternal8(buffer); + if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(buffer, m_length)) { + m_value = *existingAtomicString; + setIs8Bit(m_value.impl()->is8Bit()); + clearFibers(); + return existingAtomicString; + } + } else { + UChar buffer[maxLengthForOnStackResolve]; + resolveRopeInternal16(buffer); + if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(buffer, m_length)) { + m_value = *existingAtomicString; + setIs8Bit(m_value.impl()->is8Bit()); + clearFibers(); + return existingAtomicString; + } + } + + return nullptr; +} + +void JSRopeString::resolveRope(ExecState* exec) const +{ + ASSERT(isRope()); + + if (isSubstring()) { + ASSERT(!substringBase()->isRope()); + m_value = substringBase()->m_value.substring(substringOffset(), m_length); + substringBase().clear(); + return; + } + + if (is8Bit()) { + LChar* buffer; + if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) { + Heap::heap(this)->reportExtraMemoryAllocated(newImpl->cost()); + m_value = newImpl.release(); + } else { + outOfMemory(exec); + return; + } + resolveRopeInternal8NoSubstring(buffer); + clearFibers(); + ASSERT(!isRope()); + return; + } + + UChar* buffer; + if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) { + Heap::heap(this)->reportExtraMemoryAllocated(newImpl->cost()); + m_value = newImpl.release(); + } else { + outOfMemory(exec); + return; + } + + resolveRopeInternal16NoSubstring(buffer); + clearFibers(); + ASSERT(!isRope()); +} + +// Overview: These functions convert a JSString from holding a string in rope form +// down to a simple String representation. It does so by building up the string +// backwards, since we want to avoid recursion, we expect that the tree structure +// representing the rope is likely imbalanced with more nodes down the left side +// (since appending to the string is likely more common) - and as such resolving +// in this fashion should minimize work queue size. (If we built the queue forwards +// we would likely have to place all of the constituent StringImpls into the +// Vector before performing any concatenation, but by working backwards we likely +// only fill the queue with the number of substrings at any given level in a +// rope-of-ropes.) +void JSRopeString::resolveRopeSlowCase8(LChar* buffer) const +{ + LChar* position = buffer + m_length; // We will be working backwards over the rope. + Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method. + + for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) + workQueue.append(fiber(i).get()); + + while (!workQueue.isEmpty()) { + JSString* currentFiber = workQueue.last(); + workQueue.removeLast(); + + const LChar* characters; + + if (currentFiber->isRope()) { + JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber); + if (!currentFiberAsRope->isSubstring()) { + for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i) + workQueue.append(currentFiberAsRope->fiber(i).get()); + continue; + } + ASSERT(!currentFiberAsRope->substringBase()->isRope()); + characters = + currentFiberAsRope->substringBase()->m_value.characters8() + + currentFiberAsRope->substringOffset(); + } else + characters = currentFiber->m_value.characters8(); + + unsigned length = currentFiber->length(); + position -= length; + StringImpl::copyChars(position, characters, length); + } + + ASSERT(buffer == position); +} + +void JSRopeString::resolveRopeSlowCase(UChar* buffer) const +{ + UChar* position = buffer + m_length; // We will be working backwards over the rope. + Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK. + + for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) + workQueue.append(fiber(i).get()); + + while (!workQueue.isEmpty()) { + JSString* currentFiber = workQueue.last(); + workQueue.removeLast(); + + if (currentFiber->isRope()) { + JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber); + if (currentFiberAsRope->isSubstring()) { + ASSERT(!currentFiberAsRope->substringBase()->isRope()); + StringImpl* string = static_cast<StringImpl*>( + currentFiberAsRope->substringBase()->m_value.impl()); + unsigned offset = currentFiberAsRope->substringOffset(); + unsigned length = currentFiberAsRope->length(); + position -= length; + if (string->is8Bit()) + StringImpl::copyChars(position, string->characters8() + offset, length); + else + StringImpl::copyChars(position, string->characters16() + offset, length); + continue; + } + for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i) + workQueue.append(currentFiberAsRope->fiber(i).get()); + continue; + } + + StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl()); + unsigned length = string->length(); + position -= length; + if (string->is8Bit()) + StringImpl::copyChars(position, string->characters8(), length); + else + StringImpl::copyChars(position, string->characters16(), length); + } + + ASSERT(buffer == position); +} + +void JSRopeString::outOfMemory(ExecState* exec) const +{ + clearFibers(); + ASSERT(isRope()); + ASSERT(m_value.isNull()); + if (exec) + throwOutOfMemoryError(exec); +} + +JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const +{ + return const_cast<JSString*>(this); +} + +bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const +{ + result = this; + number = jsToNumber(view(exec)); + return false; +} + +double JSString::toNumber(ExecState* exec) const +{ + return jsToNumber(view(exec)); +} + +inline StringObject* StringObject::create(VM& vm, JSGlobalObject* globalObject, JSString* string) +{ + StringObject* object = new (NotNull, allocateCell<StringObject>(vm.heap)) StringObject(vm, globalObject->stringObjectStructure()); + object->finishCreation(vm, string); + return object; +} + +JSObject* JSString::toObject(ExecState* exec, JSGlobalObject* globalObject) const +{ + return StringObject::create(exec->vm(), globalObject, const_cast<JSString*>(this)); +} + +JSValue JSString::toThis(JSCell* cell, ExecState* exec, ECMAMode ecmaMode) +{ + if (ecmaMode == StrictMode) + return cell; + return StringObject::create(exec->vm(), exec->lexicalGlobalObject(), jsCast<JSString*>(cell)); +} + +bool JSString::getStringPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(m_length), DontEnum | DontDelete | ReadOnly); + return true; + } + + Optional<uint32_t> index = parseIndex(propertyName); + if (index && index.value() < m_length) { + descriptor.setDescriptor(getIndex(exec, index.value()), DontDelete | ReadOnly); + return true; + } + + return false; +} + +JSString* jsStringWithCacheSlowCase(VM& vm, StringImpl& stringImpl) +{ + if (JSString* string = vm.stringCache.get(&stringImpl)) + return string; + + JSString* string = jsString(&vm, String(stringImpl)); + vm.lastCachedString.set(vm, string); + return string; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h new file mode 100644 index 000000000..068f52fbb --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -0,0 +1,778 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2014 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSString_h +#define JSString_h + +#include "CallFrame.h" +#include "CommonIdentifiers.h" +#include "Identifier.h" +#include "PropertyDescriptor.h" +#include "PropertySlot.h" +#include "Structure.h" +#include <array> +#include <wtf/text/StringView.h> + +namespace JSC { + +class JSString; +class JSRopeString; +class LLIntOffsetsExtractor; + +JSString* jsEmptyString(VM*); +JSString* jsEmptyString(ExecState*); +JSString* jsString(VM*, const String&); // returns empty string if passed null string +JSString* jsString(ExecState*, const String&); // returns empty string if passed null string + +JSString* jsSingleCharacterString(VM*, UChar); +JSString* jsSingleCharacterString(ExecState*, UChar); +JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length); +JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned length); +JSString* jsSubstring8(VM*, const String&, unsigned offset, unsigned length); +JSString* jsSubstring8(ExecState*, const String&, unsigned offset, unsigned length); + +// Non-trivial strings are two or more characters long. +// These functions are faster than just calling jsString. +JSString* jsNontrivialString(VM*, const String&); +JSString* jsNontrivialString(ExecState*, const String&); +JSString* jsNontrivialString(ExecState*, String&&); + +// Should be used for strings that are owned by an object that will +// likely outlive the JSValue this makes, such as the parse tree or a +// DOM object that contains a String +JSString* jsOwnedString(VM*, const String&); +JSString* jsOwnedString(ExecState*, const String&); + +JSRopeString* jsStringBuilder(VM*); + +bool isJSString(JSValue); +JSString* asString(JSValue); + +struct StringViewWithUnderlyingString { + StringView view; + String underlyingString; +}; + +class JSString : public JSCell { +public: + friend class JIT; + friend class VM; + friend class SpecializedThunkJIT; + friend class JSRopeString; + friend class MarkStack; + friend class SlotVisitor; + friend struct ThunkHelpers; + + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal; + + static const bool needsDestruction = true; + static void destroy(JSCell*); + +private: + JSString(VM& vm, PassRefPtr<StringImpl> value) + : JSCell(vm, vm.stringStructure.get()) + , m_flags(0) + , m_value(value) + { + } + + JSString(VM& vm) + : JSCell(vm, vm.stringStructure.get()) + , m_flags(0) + { + } + + void finishCreation(VM& vm, size_t length) + { + ASSERT(!m_value.isNull()); + Base::finishCreation(vm); + m_length = length; + setIs8Bit(m_value.impl()->is8Bit()); + vm.m_newStringsSinceLastHashCons++; + } + + void finishCreation(VM& vm, size_t length, size_t cost) + { + ASSERT(!m_value.isNull()); + Base::finishCreation(vm); + m_length = length; + setIs8Bit(m_value.impl()->is8Bit()); + Heap::heap(this)->reportExtraMemoryAllocated(cost); + vm.m_newStringsSinceLastHashCons++; + } + +protected: + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + m_length = 0; + setIs8Bit(true); + vm.m_newStringsSinceLastHashCons++; + } + +public: + static JSString* create(VM& vm, PassRefPtr<StringImpl> value) + { + ASSERT(value); + int32_t length = value->length(); + RELEASE_ASSERT(length >= 0); + size_t cost = value->cost(); + JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value); + newString->finishCreation(vm, length, cost); + return newString; + } + static JSString* createHasOtherOwner(VM& vm, PassRefPtr<StringImpl> value) + { + ASSERT(value); + size_t length = value->length(); + JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value); + newString->finishCreation(vm, length); + return newString; + } + + Identifier toIdentifier(ExecState*) const; + AtomicString toAtomicString(ExecState*) const; + RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const; + + class SafeView; + SafeView view(ExecState*) const; + StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const; + + const String& value(ExecState*) const; + const String& tryGetValue() const; + const StringImpl* tryGetValueImpl() const; + unsigned length() const { return m_length; } + + JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; + bool toBoolean() const { return !!m_length; } + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + JSObject* toObject(ExecState*, JSGlobalObject*) const; + double toNumber(ExecState*) const; + + bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&); + bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); + + bool canGetIndex(unsigned i) { return i < m_length; } + JSString* getIndex(ExecState*, unsigned); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(StringType, StructureFlags), info()); + } + + static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); } + static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); } + static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); } + + DECLARE_EXPORT_INFO; + + static void dumpToStream(const JSCell*, PrintStream&); + static void visitChildren(JSCell*, SlotVisitor&); + + enum { + HashConsLock = 1u << 2, + IsHashConsSingleton = 1u << 1, + Is8Bit = 1u + }; + +protected: + friend class JSValue; + + bool isRope() const { return m_value.isNull(); } + bool isSubstring() const; + bool is8Bit() const { return m_flags & Is8Bit; } + void setIs8Bit(bool flag) const + { + if (flag) + m_flags |= Is8Bit; + else + m_flags &= ~Is8Bit; + } + bool shouldTryHashCons(); + bool isHashConsSingleton() const { return m_flags & IsHashConsSingleton; } + void clearHashConsSingleton() { m_flags &= ~IsHashConsSingleton; } + void setHashConsSingleton() { m_flags |= IsHashConsSingleton; } + bool tryHashConsLock(); + void releaseHashConsLock(); + + mutable unsigned m_flags; + + // A string is represented either by a String or a rope of fibers. + unsigned m_length; + mutable String m_value; + +private: + friend class LLIntOffsetsExtractor; + + static JSValue toThis(JSCell*, ExecState*, ECMAMode); + + String& string() { ASSERT(!isRope()); return m_value; } + StringView unsafeView(ExecState&) const; + + friend JSValue jsString(ExecState*, JSString*, JSString*); + friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length); +}; + +class JSRopeString final : public JSString { + friend class JSString; + + friend JSRopeString* jsStringBuilder(VM*); + +public: + class RopeBuilder { + public: + RopeBuilder(VM& vm) + : m_vm(vm) + , m_jsString(jsStringBuilder(&vm)) + , m_index(0) + { + } + + bool append(JSString* jsString) + { + if (m_index == JSRopeString::s_maxInternalRopeLength) + expand(); + if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) { + m_jsString = nullptr; + return false; + } + m_jsString->append(m_vm, m_index++, jsString); + return true; + } + + JSRopeString* release() + { + RELEASE_ASSERT(m_jsString); + JSRopeString* tmp = m_jsString; + m_jsString = 0; + return tmp; + } + + unsigned length() const { return m_jsString->m_length; } + + private: + void expand(); + + VM& m_vm; + JSRopeString* m_jsString; + size_t m_index; + }; + +private: + JSRopeString(VM& vm) + : JSString(vm) + { + } + + void finishCreation(VM& vm, JSString* s1, JSString* s2) + { + Base::finishCreation(vm); + m_length = s1->length() + s2->length(); + setIs8Bit(s1->is8Bit() && s2->is8Bit()); + setIsSubstring(false); + fiber(0).set(vm, this, s1); + fiber(1).set(vm, this, s2); + fiber(2).clear(); + } + + void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3) + { + Base::finishCreation(vm); + m_length = s1->length() + s2->length() + s3->length(); + setIs8Bit(s1->is8Bit() && s2->is8Bit() && s3->is8Bit()); + setIsSubstring(false); + fiber(0).set(vm, this, s1); + fiber(1).set(vm, this, s2); + fiber(2).set(vm, this, s3); + } + + void finishCreation(ExecState& exec, JSString& base, unsigned offset, unsigned length) + { + VM& vm = exec.vm(); + Base::finishCreation(vm); + ASSERT(!sumOverflows<int32_t>(offset, length)); + ASSERT(offset + length <= base.length()); + m_length = length; + setIs8Bit(base.is8Bit()); + setIsSubstring(true); + if (base.isSubstring()) { + JSRopeString& baseRope = static_cast<JSRopeString&>(base); + substringBase().set(vm, this, baseRope.substringBase().get()); + substringOffset() = baseRope.substringOffset() + offset; + } else { + substringBase().set(vm, this, &base); + substringOffset() = offset; + + // For now, let's not allow substrings with a rope base. + // Resolve non-substring rope bases so we don't have to deal with it. + // FIXME: Evaluate if this would be worth adding more branches. + if (base.isRope()) + static_cast<JSRopeString&>(base).resolveRope(&exec); + } + } + + void finishCreation(VM& vm) + { + JSString::finishCreation(vm); + setIsSubstring(false); + fiber(0).clear(); + fiber(1).clear(); + fiber(2).clear(); + } + + void append(VM& vm, size_t index, JSString* jsString) + { + fiber(index).set(vm, this, jsString); + m_length += jsString->m_length; + RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0); + setIs8Bit(is8Bit() && jsString->is8Bit()); + } + + static JSRopeString* createNull(VM& vm) + { + JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm); + newString->finishCreation(vm); + return newString; + } + +public: + static JSString* create(VM& vm, JSString* s1, JSString* s2) + { + JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm); + newString->finishCreation(vm, s1, s2); + return newString; + } + static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3) + { + JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm); + newString->finishCreation(vm, s1, s2, s3); + return newString; + } + + static JSString* create(ExecState& exec, JSString& base, unsigned offset, unsigned length) + { + JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(exec.vm().heap)) JSRopeString(exec.vm()); + newString->finishCreation(exec, base, offset, length); + return newString; + } + + void visitFibers(SlotVisitor&); + + static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, u); } + + static const unsigned s_maxInternalRopeLength = 3; + +private: + friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned); + friend JSValue jsStringFromArguments(ExecState*, JSValue); + + JS_EXPORT_PRIVATE void resolveRope(ExecState*) const; + JS_EXPORT_PRIVATE void resolveRopeToAtomicString(ExecState*) const; + JS_EXPORT_PRIVATE RefPtr<AtomicStringImpl> resolveRopeToExistingAtomicString(ExecState*) const; + void resolveRopeSlowCase8(LChar*) const; + void resolveRopeSlowCase(UChar*) const; + void outOfMemory(ExecState*) const; + void resolveRopeInternal8(LChar*) const; + void resolveRopeInternal8NoSubstring(LChar*) const; + void resolveRopeInternal16(UChar*) const; + void resolveRopeInternal16NoSubstring(UChar*) const; + void clearFibers() const; + StringView unsafeView(ExecState&) const; + StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const; + + WriteBarrierBase<JSString>& fiber(unsigned i) const + { + ASSERT(!isSubstring()); + ASSERT(i < s_maxInternalRopeLength); + return u[i].string; + } + + WriteBarrierBase<JSString>& substringBase() const + { + return u[1].string; + } + + uintptr_t& substringOffset() const + { + return u[2].number; + } + + static uintptr_t notSubstringSentinel() + { + return 0; + } + + static uintptr_t substringSentinel() + { + return 1; + } + + bool isSubstring() const + { + return u[0].number == substringSentinel(); + } + + void setIsSubstring(bool isSubstring) + { + u[0].number = isSubstring ? substringSentinel() : notSubstringSentinel(); + } + + mutable union { + uintptr_t number; + WriteBarrierBase<JSString> string; + } u[s_maxInternalRopeLength]; +}; + +class JSString::SafeView { +public: + SafeView(); + explicit SafeView(ExecState&, const JSString&); + operator StringView() const; + StringView get() const; + +private: + ExecState* m_state { nullptr }; + + // The following pointer is marked "volatile" to make the compiler leave it on the stack + // or in a register as long as this object is alive, even after the last use of the pointer. + // That's needed to prevent garbage collecting the string and possibly deleting the block + // with the characters in it, and then using the StringView after that. + const JSString* volatile m_string { nullptr }; +}; + +JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&); + +inline const StringImpl* JSString::tryGetValueImpl() const +{ + return m_value.impl(); +} + +inline JSString* asString(JSValue value) +{ + ASSERT(value.asCell()->isString()); + return jsCast<JSString*>(value.asCell()); +} + +inline JSString* jsEmptyString(VM* vm) +{ + return vm->smallStrings.emptyString(); +} + +ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c) +{ + if (c <= maxSingleCharacterString) + return vm->smallStrings.singleCharacterString(c); + return JSString::create(*vm, String(&c, 1).impl()); +} + +inline JSString* jsNontrivialString(VM* vm, const String& s) +{ + ASSERT(s.length() > 1); + return JSString::create(*vm, s.impl()); +} + +inline JSString* jsNontrivialString(VM* vm, String&& s) +{ + ASSERT(s.length() > 1); + return JSString::create(*vm, s.releaseImpl()); +} + +ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const +{ + return Identifier::fromString(exec, toAtomicString(exec)); +} + +ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const +{ + if (isRope()) + static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec); + return AtomicString(m_value); +} + +ALWAYS_INLINE RefPtr<AtomicStringImpl> JSString::toExistingAtomicString(ExecState* exec) const +{ + if (isRope()) + return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec); + if (m_value.impl()->isAtomic()) + return static_cast<AtomicStringImpl*>(m_value.impl()); + return AtomicStringImpl::lookUp(m_value.impl()); +} + +inline const String& JSString::value(ExecState* exec) const +{ + if (isRope()) + static_cast<const JSRopeString*>(this)->resolveRope(exec); + return m_value; +} + +inline const String& JSString::tryGetValue() const +{ + if (isRope()) + static_cast<const JSRopeString*>(this)->resolveRope(0); + return m_value; +} + +inline JSString* JSString::getIndex(ExecState* exec, unsigned i) +{ + ASSERT(canGetIndex(i)); + return jsSingleCharacterString(exec, unsafeView(*exec)[i]); +} + +inline JSString* jsString(VM* vm, const String& s) +{ + int size = s.length(); + if (!size) + return vm->smallStrings.emptyString(); + if (size == 1) { + UChar c = s.characterAt(0); + if (c <= maxSingleCharacterString) + return vm->smallStrings.singleCharacterString(c); + } + return JSString::create(*vm, s.impl()); +} + +inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length) +{ + ASSERT(offset <= static_cast<unsigned>(s->length())); + ASSERT(length <= static_cast<unsigned>(s->length())); + ASSERT(offset + length <= static_cast<unsigned>(s->length())); + VM& vm = exec->vm(); + if (!length) + return vm.smallStrings.emptyString(); + if (!offset && length == s->length()) + return s; + return JSRopeString::create(*exec, *s, offset, length); +} + +inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length) +{ + ASSERT(offset <= static_cast<unsigned>(s.length())); + ASSERT(length <= static_cast<unsigned>(s.length())); + ASSERT(offset + length <= static_cast<unsigned>(s.length())); + if (!length) + return vm->smallStrings.emptyString(); + if (length == 1) { + UChar c = s.characterAt(offset); + if (c <= maxSingleCharacterString) + return vm->smallStrings.singleCharacterString(c); + } + return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl8(s.impl(), offset, length)); +} + +inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length) +{ + ASSERT(offset <= static_cast<unsigned>(s.length())); + ASSERT(length <= static_cast<unsigned>(s.length())); + ASSERT(offset + length <= static_cast<unsigned>(s.length())); + if (!length) + return vm->smallStrings.emptyString(); + if (length == 1) { + UChar c = s.characterAt(offset); + if (c <= maxSingleCharacterString) + return vm->smallStrings.singleCharacterString(c); + } + return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, length)); +} + +inline JSString* jsOwnedString(VM* vm, const String& s) +{ + int size = s.length(); + if (!size) + return vm->smallStrings.emptyString(); + if (size == 1) { + UChar c = s.characterAt(0); + if (c <= maxSingleCharacterString) + return vm->smallStrings.singleCharacterString(c); + } + return JSString::createHasOtherOwner(*vm, s.impl()); +} + +inline JSRopeString* jsStringBuilder(VM* vm) +{ + return JSRopeString::createNull(*vm); +} + +inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); } +inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); } +inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); } +inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->vm(), s, offset, length); } +inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); } +inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); } +inline JSString* jsNontrivialString(ExecState* exec, String&& s) { return jsNontrivialString(&exec->vm(), WTF::move(s)); } +inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); } + +ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s) +{ + VM& vm = exec->vm(); + StringImpl* stringImpl = s.impl(); + if (!stringImpl || !stringImpl->length()) + return jsEmptyString(&vm); + + if (stringImpl->length() == 1) { + UChar singleCharacter = (*stringImpl)[0u]; + if (singleCharacter <= maxSingleCharacterString) + return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter)); + } + + if (JSString* lastCachedString = vm.lastCachedString.get()) { + if (lastCachedString->tryGetValueImpl() == stringImpl) + return lastCachedString; + } + + return jsStringWithCacheSlowCase(vm, *stringImpl); +} + +ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const AtomicString& s) +{ + return jsStringWithCache(exec, s.string()); +} + +ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + slot.setValue(this, DontEnum | DontDelete | ReadOnly, jsNumber(m_length)); + return true; + } + + Optional<uint32_t> index = parseIndex(propertyName); + if (index && index.value() < m_length) { + slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, index.value())); + return true; + } + + return false; +} + +ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + if (propertyName < m_length) { + slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, propertyName)); + return true; + } + + return false; +} + +inline bool isJSString(JSValue v) +{ + return v.isCell() && v.asCell()->type() == StringType; +} + +ALWAYS_INLINE StringView JSRopeString::unsafeView(ExecState& state) const +{ + if (isSubstring()) { + if (is8Bit()) + return StringView(substringBase()->m_value.characters8() + substringOffset(), m_length); + return StringView(substringBase()->m_value.characters16() + substringOffset(), m_length); + } + resolveRope(&state); + return m_value; +} + +ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState& state) const +{ + if (isSubstring()) { + auto& base = substringBase()->m_value; + if (is8Bit()) + return { { base.characters8() + substringOffset(), m_length }, base }; + return { { base.characters16() + substringOffset(), m_length }, base }; + } + resolveRope(&state); + return { m_value, m_value }; +} + +ALWAYS_INLINE StringView JSString::unsafeView(ExecState& state) const +{ + if (isRope()) + return static_cast<const JSRopeString*>(this)->unsafeView(state); + return m_value; +} + +ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState& state) const +{ + if (isRope()) + return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(state); + return { m_value, m_value }; +} + +inline bool JSString::isSubstring() const +{ + return isRope() && static_cast<const JSRopeString*>(this)->isSubstring(); +} + +inline JSString::SafeView::SafeView() +{ +} + +inline JSString::SafeView::SafeView(ExecState& state, const JSString& string) + : m_state(&state) + , m_string(&string) +{ +} + +inline JSString::SafeView::operator StringView() const +{ + return m_string->unsafeView(*m_state); +} + +inline StringView JSString::SafeView::get() const +{ + return *this; +} + +ALWAYS_INLINE JSString::SafeView JSString::view(ExecState* exec) const +{ + return SafeView(*exec, *this); +} + +// --- JSValue inlines ---------------------------- + +inline bool JSValue::toBoolean(ExecState* exec) const +{ + if (isInt32()) + return asInt32(); + if (isDouble()) + return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN + if (isCell()) + return asCell()->toBoolean(exec); + return isTrue(); // false, null, and undefined all convert to false. +} + +inline JSString* JSValue::toString(ExecState* exec) const +{ + if (isString()) + return jsCast<JSString*>(asCell()); + return toStringSlowCase(exec); +} + +inline String JSValue::toWTFString(ExecState* exec) const +{ + if (isString()) + return static_cast<JSString*>(asCell())->value(exec); + return toWTFStringSlowCase(exec); +} + +} // namespace JSC + +#endif // JSString_h diff --git a/Source/JavaScriptCore/runtime/JSStringBuilder.h b/Source/JavaScriptCore/runtime/JSStringBuilder.h new file mode 100644 index 000000000..34f43c4db --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringBuilder.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSStringBuilder_h +#define JSStringBuilder_h + +#include "ExceptionHelpers.h" +#include "JSString.h" +#include <wtf/Vector.h> + +namespace JSC { + +// FIXME: Should move the last few callers over from this to WTF::StringBuilder. +class JSStringBuilder { +public: + JSStringBuilder() + : m_okay(true) + , m_is8Bit(true) + { + } + + void append(LChar character) + { + if (m_is8Bit) { + m_okay &= buffer8.tryAppend(&character, 1); + return; + } + UChar upconvertedCharacter = character; + m_okay &= buffer16.tryAppend(&upconvertedCharacter, 1); + } + + void append(UChar character) + { + if (m_is8Bit) { + if (character < 0x100) { + LChar narrowedCharacter = character; + m_okay &= buffer8.tryAppend(&narrowedCharacter, 1); + return; + } + upConvert(); + } + m_okay &= buffer16.tryAppend(&character, 1); + } + + void append(const char* str) + { + append(reinterpret_cast<const LChar*>(str), strlen(str)); + } + + JSValue build(ExecState* exec) + { + if (!m_okay) + return throwOutOfMemoryError(exec); + if (m_is8Bit) { + buffer8.shrinkToFit(); + if (!buffer8.data()) + return throwOutOfMemoryError(exec); + return jsString(exec, String::adopt(buffer8)); + } + buffer16.shrinkToFit(); + if (!buffer16.data()) + return throwOutOfMemoryError(exec); + return jsString(exec, String::adopt(buffer16)); + } + +private: + void append(const LChar* characters, size_t length) + { + if (m_is8Bit) { + m_okay &= buffer8.tryAppend(characters, length); + return; + } + // FIXME: There must be a more efficient way of doing this. + m_okay &= buffer16.tryReserveCapacity(buffer16.size() + length); + for (size_t i = 0; i < length; i++) { + UChar upconvertedCharacter = characters[i]; + m_okay &= buffer16.tryAppend(&upconvertedCharacter, 1); + } + } + + void upConvert() + { + ASSERT(m_is8Bit); + size_t len = buffer8.size(); + + for (size_t i = 0; i < len; i++) + buffer16.append(buffer8[i]); + + buffer8.clear(); + m_is8Bit = false; + } + + Vector<LChar, 64, UnsafeVectorOverflow> buffer8; + Vector<UChar, 64, UnsafeVectorOverflow> buffer16; + bool m_okay; + bool m_is8Bit; +}; + +template<typename StringType> +inline JSValue jsMakeNontrivialString(ExecState* exec, StringType&& string) +{ + return jsNontrivialString(exec, std::forward<StringType>(string)); +} + +template<typename StringType, typename... StringTypes> +inline JSValue jsMakeNontrivialString(ExecState* exec, const StringType& string, const StringTypes&... strings) +{ + RefPtr<StringImpl> result = WTF::tryMakeString(string, strings...); + if (!result) + return throwOutOfMemoryError(exec); + return jsNontrivialString(exec, result.release()); +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/JSStringIterator.cpp b/Source/JavaScriptCore/runtime/JSStringIterator.cpp new file mode 100644 index 000000000..c6fede7ad --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringIterator.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSStringIterator.h" + +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo JSStringIterator::s_info = { "String Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(JSStringIterator) }; + +void JSStringIterator::finishCreation(VM& vm, JSGlobalObject*, JSString* iteratedString) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + putDirect(vm, vm.propertyNames->iteratedStringPrivateName, iteratedString); + putDirect(vm, vm.propertyNames->stringIteratorNextIndexPrivateName, jsNumber(0)); +} + +JSValue JSStringIterator::iteratedValue(ExecState* exec) const +{ + return getDirect(exec->vm(), exec->vm().propertyNames->iteratedStringPrivateName); +} + +JSStringIterator* JSStringIterator::clone(ExecState* exec) +{ + VM& vm = exec->vm(); + JSValue iteratedString = getDirect(vm, vm.propertyNames->iteratedStringPrivateName); + JSValue nextIndex = getDirect(vm, vm.propertyNames->stringIteratorNextIndexPrivateName); + + auto clone = JSStringIterator::create(exec, exec->callee()->globalObject()->stringIteratorStructure(), asString(iteratedString)); + clone->putDirect(vm, vm.propertyNames->stringIteratorNextIndexPrivateName, nextIndex); + return clone; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSStringIterator.h b/Source/JavaScriptCore/runtime/JSStringIterator.h new file mode 100644 index 000000000..6a789a319 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringIterator.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSStringIterator_h +#define JSStringIterator_h + +#include "JSObject.h" + +namespace JSC { + +class JSStringIterator : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSStringIterator* create(ExecState* exec, Structure* structure, JSString* iteratedString) + { + VM& vm = exec->vm(); + JSStringIterator* instance = new (NotNull, allocateCell<JSStringIterator>(vm.heap)) JSStringIterator(vm, structure); + instance->finishCreation(vm, structure->globalObject(), iteratedString); + return instance; + } + + JSValue iteratedValue(ExecState*) const; + JSStringIterator* clone(ExecState*); + +private: + JSStringIterator(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(VM&, JSGlobalObject*, JSString* iteratedString); +}; + +} + +#endif // !defined(JSStringIterator_h) diff --git a/Source/JavaScriptCore/runtime/JSStringJoiner.cpp b/Source/JavaScriptCore/runtime/JSStringJoiner.cpp new file mode 100644 index 000000000..6f1959388 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringJoiner.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSStringJoiner.h" + +#include "JSCInlines.h" + +namespace JSC { + +template<typename CharacterType> +static inline void appendStringToData(CharacterType*& data, StringView string) +{ + string.getCharactersWithUpconvert(data); + data += string.length(); +} + +template<typename CharacterType> +static inline String joinStrings(const Vector<StringViewWithUnderlyingString>& strings, StringView separator, unsigned joinedLength) +{ + ASSERT(joinedLength); + + CharacterType* data; + String result = StringImpl::tryCreateUninitialized(joinedLength, data); + if (result.isNull()) + return result; + + appendStringToData(data, strings[0].view); + + unsigned size = strings.size(); + + switch (separator.length()) { + case 0: + for (unsigned i = 1; i < size; ++i) + appendStringToData(data, strings[i].view); + break; + case 1: { + CharacterType separatorCharacter = separator[0]; + for (unsigned i = 1; i < size; ++i) { + *data++ = separatorCharacter; + appendStringToData(data, strings[i].view); + } + break; + } + default: + for (unsigned i = 1; i < size; ++i) { + appendStringToData(data, separator); + appendStringToData(data, strings[i].view); + } + } + ASSERT(data == result.characters<CharacterType>() + joinedLength); + + return result; +} + +inline unsigned JSStringJoiner::joinedLength(ExecState& state) const +{ + unsigned numberOfStrings = m_strings.size(); + if (!numberOfStrings) + return 0; + + Checked<unsigned, RecordOverflow> separatorLength = m_separator.length(); + Checked<unsigned, RecordOverflow> totalSeparatorsLength = separatorLength * (numberOfStrings - 1); + Checked<unsigned, RecordOverflow> totalLength = totalSeparatorsLength + m_accumulatedStringsLength; + + unsigned result; + if (totalLength.safeGet(result) == CheckedState::DidOverflow) { + throwOutOfMemoryError(&state); + return 0; + } + return result; +} + +JSValue JSStringJoiner::join(ExecState& state) +{ + ASSERT(m_strings.size() <= m_strings.capacity()); + + unsigned length = joinedLength(state); + if (state.hadException()) + return jsUndefined(); + + if (!length) + return jsEmptyString(&state); + + String result; + if (m_isAll8Bit) + result = joinStrings<LChar>(m_strings, m_separator, length); + else + result = joinStrings<UChar>(m_strings, m_separator, length); + + if (result.isNull()) + return throwOutOfMemoryError(&state); + + return jsString(&state, WTF::move(result)); +} + +} diff --git a/Source/JavaScriptCore/runtime/JSStringJoiner.h b/Source/JavaScriptCore/runtime/JSStringJoiner.h new file mode 100644 index 000000000..224784493 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSStringJoiner.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2012-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSStringJoiner_h +#define JSStringJoiner_h + +#include "ExceptionHelpers.h" +#include "JSCJSValue.h" + +namespace JSC { + +class JSStringJoiner { +public: + JSStringJoiner(ExecState&, LChar separator, unsigned stringCount); + JSStringJoiner(ExecState&, StringView separator, unsigned stringCount); + + void append(ExecState&, JSValue); + void appendEmptyString(); + + JSValue join(ExecState&); + +private: + void append(StringViewWithUnderlyingString&&); + void append8Bit(const String&); + void appendLiteral(const Identifier&); + unsigned joinedLength(ExecState&) const; + + LChar m_singleCharacterSeparator; + StringView m_separator; + Vector<StringViewWithUnderlyingString> m_strings; + Checked<unsigned, RecordOverflow> m_accumulatedStringsLength; + bool m_isAll8Bit { true }; +}; + +inline JSStringJoiner::JSStringJoiner(ExecState& state, StringView separator, unsigned stringCount) + : m_separator(separator) + , m_isAll8Bit(m_separator.is8Bit()) +{ + if (!m_strings.tryReserveCapacity(stringCount)) + throwOutOfMemoryError(&state); +} + +inline JSStringJoiner::JSStringJoiner(ExecState& state, LChar separator, unsigned stringCount) + : m_singleCharacterSeparator(separator) + , m_separator { &m_singleCharacterSeparator, 1 } +{ + if (!m_strings.tryReserveCapacity(stringCount)) + throwOutOfMemoryError(&state); +} + +ALWAYS_INLINE void JSStringJoiner::append(StringViewWithUnderlyingString&& string) +{ + m_accumulatedStringsLength += string.view.length(); + m_isAll8Bit = m_isAll8Bit && string.view.is8Bit(); + m_strings.uncheckedAppend(WTF::move(string)); +} + +ALWAYS_INLINE void JSStringJoiner::append8Bit(const String& string) +{ + ASSERT(string.is8Bit()); + m_accumulatedStringsLength += string.length(); + m_strings.uncheckedAppend({ string, string }); +} + +ALWAYS_INLINE void JSStringJoiner::appendLiteral(const Identifier& literal) +{ + m_accumulatedStringsLength += literal.length(); + ASSERT(literal.string().is8Bit()); + m_strings.uncheckedAppend({ literal.string(), { } }); +} + +ALWAYS_INLINE void JSStringJoiner::appendEmptyString() +{ + m_strings.uncheckedAppend({ { }, { } }); +} + +ALWAYS_INLINE void JSStringJoiner::append(ExecState& state, JSValue value) +{ + // The following code differs from using the result of JSValue::toString in the following ways: + // 1) It's inlined more than JSValue::toString is. + // 2) It includes conversion to WTF::String in a way that avoids allocating copies of substrings. + // 3) It doesn't create a JSString for numbers, true, or false. + // 4) It turns undefined and null into the empty string instead of "undefined" and "null". + // 5) It uses optimized code paths for all the cases known to be 8-bit and for the empty string. + + if (value.isCell()) { + if (value.asCell()->isString()) { + append(asString(value)->viewWithUnderlyingString(state)); + return; + } + append(value.toString(&state)->viewWithUnderlyingString(state)); + return; + } + + if (value.isInt32()) { + append8Bit(state.vm().numericStrings.add(value.asInt32())); + return; + } + if (value.isDouble()) { + append8Bit(state.vm().numericStrings.add(value.asDouble())); + return; + } + if (value.isTrue()) { + append8Bit(state.vm().propertyNames->trueKeyword.string()); + return; + } + if (value.isFalse()) { + append8Bit(state.vm().propertyNames->falseKeyword.string()); + return; + } + ASSERT(value.isUndefinedOrNull()); + appendEmptyString(); +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp b/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp new file mode 100644 index 000000000..83aa048ea --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012, 2013 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "JSSymbolTableObject.h" + +#include "JSGlobalObject.h" +#include "JSLexicalEnvironment.h" +#include "JSCInlines.h" +#include "PropertyNameArray.h" + +namespace JSC { + +void JSSymbolTableObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSSymbolTableObject* thisObject = jsCast<JSSymbolTableObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_symbolTable); +} + +bool JSSymbolTableObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSSymbolTableObject* thisObject = jsCast<JSSymbolTableObject*>(cell); + if (thisObject->symbolTable()->contains(propertyName.publicName())) + return false; + + return JSObject::deleteProperty(thisObject, exec, propertyName); +} + +void JSSymbolTableObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSSymbolTableObject* thisObject = jsCast<JSSymbolTableObject*>(object); + { + ConcurrentJITLocker locker(thisObject->symbolTable()->m_lock); + SymbolTable::Map::iterator end = thisObject->symbolTable()->end(locker); + for (SymbolTable::Map::iterator it = thisObject->symbolTable()->begin(locker); it != end; ++it) { + if (!(it->value.getAttributes() & DontEnum) || mode.includeDontEnumProperties()) { + if (it->key->isSymbol() && !propertyNames.includeSymbolProperties()) + continue; + propertyNames.add(Identifier::fromUid(exec, it->key.get())); + } + } + } + + JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h new file mode 100644 index 000000000..9fe8384c6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2012, 2014, 2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef JSSymbolTableObject_h +#define JSSymbolTableObject_h + +#include "JSScope.h" +#include "PropertyDescriptor.h" +#include "SymbolTable.h" +#include "VariableWriteFireDetail.h" + +namespace JSC { + +class JSSymbolTableObject; + +class JSSymbolTableObject : public JSScope { +public: + typedef JSScope Base; + static const unsigned StructureFlags = Base::StructureFlags | IsEnvironmentRecord | OverridesGetPropertyNames; + + SymbolTable* symbolTable() const { return m_symbolTable.get(); } + + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); + JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + + static ptrdiff_t offsetOfSymbolTable() { return OBJECT_OFFSETOF(JSSymbolTableObject, m_symbolTable); } + +protected: + JSSymbolTableObject(VM& vm, Structure* structure, JSScope* scope) + : Base(vm, structure, scope) + { + } + + JSSymbolTableObject(VM& vm, Structure* structure, JSScope* scope, SymbolTable* symbolTable) + : Base(vm, structure, scope) + { + ASSERT(symbolTable); + setSymbolTable(vm, symbolTable); + } + + void setSymbolTable(VM& vm, SymbolTable* symbolTable) + { + ASSERT(!m_symbolTable); + symbolTable->singletonScope()->notifyWrite(vm, this, "Allocated a scope"); + m_symbolTable.set(vm, this, symbolTable); + } + + static void visitChildren(JSCell*, SlotVisitor&); + +private: + WriteBarrier<SymbolTable> m_symbolTable; +}; + +template<typename SymbolTableObjectType> +inline bool symbolTableGet( + SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot) +{ + SymbolTable& symbolTable = *object->symbolTable(); + ConcurrentJITLocker locker(symbolTable.m_lock); + SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid()); + if (iter == symbolTable.end(locker)) + return false; + SymbolTableEntry::Fast entry = iter->value; + ASSERT(!entry.isNull()); + slot.setValue(object, entry.getAttributes() | DontDelete, object->variableAt(entry.scopeOffset()).get()); + return true; +} + +template<typename SymbolTableObjectType> +inline bool symbolTableGet( + SymbolTableObjectType* object, PropertyName propertyName, PropertyDescriptor& descriptor) +{ + SymbolTable& symbolTable = *object->symbolTable(); + ConcurrentJITLocker locker(symbolTable.m_lock); + SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid()); + if (iter == symbolTable.end(locker)) + return false; + SymbolTableEntry::Fast entry = iter->value; + ASSERT(!entry.isNull()); + descriptor.setDescriptor( + object->variableAt(entry.scopeOffset()).get(), entry.getAttributes() | DontDelete); + return true; +} + +template<typename SymbolTableObjectType> +inline bool symbolTableGet( + SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot, + bool& slotIsWriteable) +{ + SymbolTable& symbolTable = *object->symbolTable(); + ConcurrentJITLocker locker(symbolTable.m_lock); + SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid()); + if (iter == symbolTable.end(locker)) + return false; + SymbolTableEntry::Fast entry = iter->value; + ASSERT(!entry.isNull()); + slot.setValue(object, entry.getAttributes() | DontDelete, object->variableAt(entry.scopeOffset()).get()); + slotIsWriteable = !entry.isReadOnly(); + return true; +} + +template<typename SymbolTableObjectType> +inline bool symbolTablePut( + SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName, JSValue value, + bool shouldThrow) +{ + VM& vm = exec->vm(); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object)); + + WriteBarrierBase<Unknown>* reg; + WatchpointSet* set; + { + SymbolTable& symbolTable = *object->symbolTable(); + // FIXME: This is very suspicious. We shouldn't need a GC-safe lock here. + // https://bugs.webkit.org/show_bug.cgi?id=134601 + GCSafeConcurrentJITLocker locker(symbolTable.m_lock, exec->vm().heap); + SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid()); + if (iter == symbolTable.end(locker)) + return false; + bool wasFat; + SymbolTableEntry::Fast fastEntry = iter->value.getFast(wasFat); + ASSERT(!fastEntry.isNull()); + if (fastEntry.isReadOnly()) { + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return true; + } + set = iter->value.watchpointSet(); + reg = &object->variableAt(fastEntry.scopeOffset()); + } + // I'd prefer we not hold lock while executing barriers, since I prefer to reserve + // the right for barriers to be able to trigger GC. And I don't want to hold VM + // locks while GC'ing. + reg->set(vm, object, value); + if (set) + VariableWriteFireDetail::touch(set, object, propertyName); + return true; +} + +template<typename SymbolTableObjectType> +inline bool symbolTablePutWithAttributes( + SymbolTableObjectType* object, VM& vm, PropertyName propertyName, + JSValue value, unsigned attributes) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object)); + + WriteBarrierBase<Unknown>* reg; + WatchpointSet* set; + { + SymbolTable& symbolTable = *object->symbolTable(); + ConcurrentJITLocker locker(symbolTable.m_lock); + SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid()); + if (iter == symbolTable.end(locker)) + return false; + SymbolTableEntry& entry = iter->value; + ASSERT(!entry.isNull()); + set = entry.watchpointSet(); + entry.setAttributes(attributes); + reg = &object->variableAt(entry.scopeOffset()); + } + reg->set(vm, object, value); + if (set) + VariableWriteFireDetail::touch(set, object, propertyName); + return true; +} + +} // namespace JSC + +#endif // JSSymbolTableObject_h + diff --git a/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.cpp b/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.cpp new file mode 100644 index 000000000..e3f3ae990 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "JSTemplateRegistryKey.h" + +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "StructureInlines.h" +#include "VM.h" + +namespace JSC { + +const ClassInfo JSTemplateRegistryKey::s_info = { "TemplateRegistryKey", &Base::s_info, nullptr, CREATE_METHOD_TABLE(JSTemplateRegistryKey) }; + + +JSTemplateRegistryKey::JSTemplateRegistryKey(VM& vm, const TemplateRegistryKey& templateRegistryKey) + : Base(vm, vm.templateRegistryKeyStructure.get()) + , m_templateRegistryKey(templateRegistryKey) +{ +} + +JSTemplateRegistryKey* JSTemplateRegistryKey::create(VM& vm, const TemplateRegistryKey& templateRegistryKey) +{ + JSTemplateRegistryKey* result = new (NotNull, allocateCell<JSTemplateRegistryKey>(vm.heap)) JSTemplateRegistryKey(vm, templateRegistryKey); + result->finishCreation(vm); + return result; +} + +void JSTemplateRegistryKey::destroy(JSCell* cell) +{ + static_cast<JSTemplateRegistryKey*>(cell)->JSTemplateRegistryKey::~JSTemplateRegistryKey(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.h b/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.h new file mode 100644 index 000000000..fc6e39286 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTemplateRegistryKey.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef JSTemplateRegistryKey_h +#define JSTemplateRegistryKey_h + +#include "JSDestructibleObject.h" +#include "Structure.h" +#include "TemplateRegistryKey.h" + +namespace JSC { + +class JSTemplateRegistryKey final : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static JSTemplateRegistryKey* create(VM&, const TemplateRegistryKey&); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + DECLARE_INFO; + + const TemplateRegistryKey& templateRegistryKey() const { return m_templateRegistryKey; } + +protected: + static void destroy(JSCell*); + +private: + JSTemplateRegistryKey(VM&, const TemplateRegistryKey&); + + TemplateRegistryKey m_templateRegistryKey; +}; + +} // namespace JSC + +#endif // JSTemplateRegistryKey_h diff --git a/Source/JavaScriptCore/runtime/JSType.h b/Source/JavaScriptCore/runtime/JSType.h new file mode 100644 index 000000000..40326ce0f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSType.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSType_h +#define JSType_h + +namespace JSC { + +enum JSType : uint8_t { + UnspecifiedType, + UndefinedType, + BooleanType, + NumberType, + NullType, + + // The CellType value must come before any JSType that is a JSCell. + CellType, + StringType, + SymbolType, + + GetterSetterType, + CustomGetterSetterType, + APIValueWrapperType, + + EvalExecutableType, + ProgramExecutableType, + FunctionExecutableType, + + UnlinkedFunctionExecutableType, + UnlinkedProgramCodeBlockType, + UnlinkedEvalCodeBlockType, + UnlinkedFunctionCodeBlockType, + + // The ObjectType value must come before any JSType that is a subclass of JSObject. + ObjectType, + FinalObjectType, + JSCalleeType, + JSFunctionType, + NumberObjectType, + ErrorInstanceType, + PureForwardingProxyType, + ImpureProxyType, + WithScopeType, + DirectArgumentsType, + ScopedArgumentsType, + + Int8ArrayType, + Int16ArrayType, + Int32ArrayType, + Uint8ArrayType, + Uint8ClampedArrayType, + Uint16ArrayType, + Uint32ArrayType, + Float32ArrayType, + Float64ArrayType, + DataViewType, + + NameScopeObjectType, + + GlobalObjectType, + ActivationObjectType, + + LastJSCObjectType = ActivationObjectType, +}; + +COMPILE_ASSERT(sizeof(JSType) == sizeof(uint8_t), sizeof_jstype_is_one_byte); + +} // namespace JSC + +#endif diff --git a/Source/JavaScriptCore/runtime/JSTypeInfo.h b/Source/JavaScriptCore/runtime/JSTypeInfo.h new file mode 100644 index 000000000..27863abf2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypeInfo.h @@ -0,0 +1,124 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2008, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSTypeInfo_h +#define JSTypeInfo_h + +// This file would be called TypeInfo.h, but that conflicts with <typeinfo.h> +// in the STL on systems without case-sensitive file systems. + +#include "JSType.h" + +namespace JSC { + +class LLIntOffsetsExtractor; + +static const unsigned MasqueradesAsUndefined = 1; // WebCore uses MasqueradesAsUndefined to make document.all undetectable. +static const unsigned ImplementsHasInstance = 1 << 1; +static const unsigned OverridesHasInstance = 1 << 2; +static const unsigned ImplementsDefaultHasInstance = 1 << 3; +static const unsigned TypeOfShouldCallGetCallData = 1 << 4; // Need this flag if you override getCallData() and you want typeof to use this to determine if it should say "function". Currently we always set this flag when we override getCallData(). +static const unsigned OverridesGetOwnPropertySlot = 1 << 5; +static const unsigned InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero = 1 << 6; +static const unsigned StructureIsImmortal = 1 << 7; + +static const unsigned OverridesGetPropertyNames = 1 << 8; +static const unsigned ProhibitsPropertyCaching = 1 << 9; +static const unsigned HasImpureGetOwnPropertySlot = 1 << 10; +static const unsigned NewImpurePropertyFiresWatchpoints = 1 << 11; +static const unsigned IsEnvironmentRecord = 1 << 12; + +class TypeInfo { +public: + typedef uint8_t InlineTypeFlags; + typedef uint8_t OutOfLineTypeFlags; + + TypeInfo(JSType type, unsigned flags = 0) + : TypeInfo(type, flags & 0xff, flags >> 8) + { + } + + TypeInfo(JSType type, InlineTypeFlags inlineTypeFlags, OutOfLineTypeFlags outOfLineTypeFlags) + : m_type(type) + , m_flags(inlineTypeFlags) + , m_flags2(outOfLineTypeFlags) + { + // No object that doesn't ImplementsHasInstance should override it! + ASSERT((m_flags & (ImplementsHasInstance | OverridesHasInstance)) != OverridesHasInstance); + // ImplementsDefaultHasInstance means (ImplementsHasInstance & !OverridesHasInstance) + if ((m_flags & (ImplementsHasInstance | OverridesHasInstance)) == ImplementsHasInstance) + m_flags |= ImplementsDefaultHasInstance; + } + + JSType type() const { return static_cast<JSType>(m_type); } + bool isObject() const { return isObject(type()); } + static bool isObject(JSType type) { return type >= ObjectType; } + bool isFinalObject() const { return type() == FinalObjectType; } + bool isNumberObject() const { return type() == NumberObjectType; } + + unsigned flags() const { return (static_cast<unsigned>(m_flags2) << 8) | static_cast<unsigned>(m_flags); } + bool masqueradesAsUndefined() const { return isSetOnFlags1(MasqueradesAsUndefined); } + bool implementsHasInstance() const { return isSetOnFlags1(ImplementsHasInstance); } + bool overridesHasInstance() const { return isSetOnFlags1(OverridesHasInstance); } + bool implementsDefaultHasInstance() const { return isSetOnFlags1(ImplementsDefaultHasInstance); } + bool typeOfShouldCallGetCallData() const { return isSetOnFlags1(TypeOfShouldCallGetCallData); } + bool overridesGetOwnPropertySlot() const { return overridesGetOwnPropertySlot(inlineTypeFlags()); } + static bool overridesGetOwnPropertySlot(InlineTypeFlags flags) { return flags & OverridesGetOwnPropertySlot; } + bool interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero() const { return isSetOnFlags1(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero); } + bool structureIsImmortal() const { return isSetOnFlags1(StructureIsImmortal); } + bool overridesGetPropertyNames() const { return isSetOnFlags2(OverridesGetPropertyNames); } + bool prohibitsPropertyCaching() const { return isSetOnFlags2(ProhibitsPropertyCaching); } + bool hasImpureGetOwnPropertySlot() const { return isSetOnFlags2(HasImpureGetOwnPropertySlot); } + bool newImpurePropertyFiresWatchpoints() const { return isSetOnFlags2(NewImpurePropertyFiresWatchpoints); } + bool isEnvironmentRecord() const { return isSetOnFlags2(IsEnvironmentRecord); } + + static ptrdiff_t flagsOffset() + { + return OBJECT_OFFSETOF(TypeInfo, m_flags); + } + + static ptrdiff_t typeOffset() + { + return OBJECT_OFFSETOF(TypeInfo, m_type); + } + + InlineTypeFlags inlineTypeFlags() const { return m_flags; } + OutOfLineTypeFlags outOfLineTypeFlags() const { return m_flags2; } + +private: + friend class LLIntOffsetsExtractor; + + bool isSetOnFlags1(unsigned flag) const { ASSERT(flag <= (1 << 7)); return m_flags & flag; } + bool isSetOnFlags2(unsigned flag) const { ASSERT(flag >= (1 << 8)); return m_flags2 & (flag >> 8); } + + unsigned char m_type; + unsigned char m_flags; + unsigned char m_flags2; +}; + +} + +#endif // JSTypeInfo_h diff --git a/Source/JavaScriptCore/runtime/JSTypedArrayConstructors.cpp b/Source/JavaScriptCore/runtime/JSTypedArrayConstructors.cpp new file mode 100644 index 000000000..f42f31795 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypedArrayConstructors.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSTypedArrayConstructors.h" + +#include "JSGenericTypedArrayViewConstructorInlines.h" +#include "JSGenericTypedArrayViewInlines.h" +#include "JSCInlines.h" + +namespace JSC { + +#define MAKE_S_INFO(type) \ + template<> const ClassInfo JS##type##Constructor::s_info = {"Function", &JS##type##Constructor::Base::s_info, 0, CREATE_METHOD_TABLE(JS##type##Constructor)} + +MAKE_S_INFO(Int8Array); +MAKE_S_INFO(Int16Array); +MAKE_S_INFO(Int32Array); +MAKE_S_INFO(Uint8Array); +MAKE_S_INFO(Uint8ClampedArray); +MAKE_S_INFO(Uint16Array); +MAKE_S_INFO(Uint32Array); +MAKE_S_INFO(Float32Array); +MAKE_S_INFO(Float64Array); +MAKE_S_INFO(DataView); + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSTypedArrayConstructors.h b/Source/JavaScriptCore/runtime/JSTypedArrayConstructors.h new file mode 100644 index 000000000..882b9bb7b --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypedArrayConstructors.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSTypedArrayConstructors_h +#define JSTypedArrayConstructors_h + +#include "JSDataView.h" +#include "JSGenericTypedArrayViewConstructor.h" +#include "JSTypedArrays.h" + +namespace JSC { + +typedef JSGenericTypedArrayViewConstructor<JSInt8Array> JSInt8ArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSInt16Array> JSInt16ArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSInt32Array> JSInt32ArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSUint8Array> JSUint8ArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSUint8ClampedArray> JSUint8ClampedArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSUint16Array> JSUint16ArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSUint32Array> JSUint32ArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSFloat32Array> JSFloat32ArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSFloat64Array> JSFloat64ArrayConstructor; +typedef JSGenericTypedArrayViewConstructor<JSDataView> JSDataViewConstructor; + +} // namespace JSC + +#endif // JSTypedArrayConstructors_h diff --git a/Source/JavaScriptCore/runtime/JSTypedArrayPrototypes.cpp b/Source/JavaScriptCore/runtime/JSTypedArrayPrototypes.cpp new file mode 100644 index 000000000..c01d8f889 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypedArrayPrototypes.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSTypedArrayPrototypes.h" + +#include "JSGenericTypedArrayViewPrototypeInlines.h" +#include "JSCInlines.h" + +namespace JSC { + +#define MAKE_S_INFO(type) \ + template<> const ClassInfo JS##type##Prototype::s_info = {#type "Prototype", &JS##type##Prototype::Base::s_info, 0, CREATE_METHOD_TABLE(JS##type##Prototype)} + +MAKE_S_INFO(Int8Array); +MAKE_S_INFO(Int16Array); +MAKE_S_INFO(Int32Array); +MAKE_S_INFO(Uint8Array); +MAKE_S_INFO(Uint8ClampedArray); +MAKE_S_INFO(Uint16Array); +MAKE_S_INFO(Uint32Array); +MAKE_S_INFO(Float32Array); +MAKE_S_INFO(Float64Array); + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSTypedArrayPrototypes.h b/Source/JavaScriptCore/runtime/JSTypedArrayPrototypes.h new file mode 100644 index 000000000..b0f1c1147 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypedArrayPrototypes.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSTypedArrayPrototypes_h +#define JSTypedArrayPrototypes_h + +#include "JSGenericTypedArrayViewPrototype.h" +#include "JSTypedArrays.h" + +namespace JSC { + +typedef JSGenericTypedArrayViewPrototype<JSInt8Array> JSInt8ArrayPrototype; +typedef JSGenericTypedArrayViewPrototype<JSInt16Array> JSInt16ArrayPrototype; +typedef JSGenericTypedArrayViewPrototype<JSInt32Array> JSInt32ArrayPrototype; +typedef JSGenericTypedArrayViewPrototype<JSUint8Array> JSUint8ArrayPrototype; +typedef JSGenericTypedArrayViewPrototype<JSUint8ClampedArray> JSUint8ClampedArrayPrototype; +typedef JSGenericTypedArrayViewPrototype<JSUint16Array> JSUint16ArrayPrototype; +typedef JSGenericTypedArrayViewPrototype<JSUint32Array> JSUint32ArrayPrototype; +typedef JSGenericTypedArrayViewPrototype<JSFloat32Array> JSFloat32ArrayPrototype; +typedef JSGenericTypedArrayViewPrototype<JSFloat64Array> JSFloat64ArrayPrototype; + +} // namespace JSC + +#endif // JSTypedArrayPrototypes_h diff --git a/Source/JavaScriptCore/runtime/JSTypedArrays.cpp b/Source/JavaScriptCore/runtime/JSTypedArrays.cpp new file mode 100644 index 000000000..2c5c4747b --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypedArrays.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSTypedArrays.h" + +#include "CopyVisitorInlines.h" +#include "GenericTypedArrayViewInlines.h" +#include "JSGenericTypedArrayViewInlines.h" +#include "JSCInlines.h" + +namespace JSC { + +#define MAKE_S_INFO(type) \ + template<> const ClassInfo JS##type##Array::s_info = { \ + #type "Array", &JS##type##Array::Base::s_info, 0, \ + CREATE_METHOD_TABLE(JS##type##Array) \ + }; \ + const ClassInfo* get##type##ArrayClassInfo() { return &JS##type##Array::s_info; } + +MAKE_S_INFO(Int8); +MAKE_S_INFO(Int16); +MAKE_S_INFO(Int32); +MAKE_S_INFO(Uint8); +MAKE_S_INFO(Uint8Clamped); +MAKE_S_INFO(Uint16); +MAKE_S_INFO(Uint32); +MAKE_S_INFO(Float32); +MAKE_S_INFO(Float64); + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/JSTypedArrays.h b/Source/JavaScriptCore/runtime/JSTypedArrays.h new file mode 100644 index 000000000..87e3eac14 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSTypedArrays.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSTypedArrays_h +#define JSTypedArrays_h + +#include "JSGenericTypedArrayView.h" +#include "TypedArrayAdaptors.h" +#include "TypedArrays.h" + +namespace JSC { + +typedef JSGenericTypedArrayView<Int8Adaptor> JSInt8Array; +typedef JSGenericTypedArrayView<Int16Adaptor> JSInt16Array; +typedef JSGenericTypedArrayView<Int32Adaptor> JSInt32Array; +typedef JSGenericTypedArrayView<Uint8Adaptor> JSUint8Array; +typedef JSGenericTypedArrayView<Uint8ClampedAdaptor> JSUint8ClampedArray; +typedef JSGenericTypedArrayView<Uint16Adaptor> JSUint16Array; +typedef JSGenericTypedArrayView<Uint32Adaptor> JSUint32Array; +typedef JSGenericTypedArrayView<Float32Adaptor> JSFloat32Array; +typedef JSGenericTypedArrayView<Float64Adaptor> JSFloat64Array; + +} // namespace JSC + +#endif // JSTypedArrays_h + diff --git a/Source/JavaScriptCore/runtime/JSUint16Array.h b/Source/JavaScriptCore/runtime/JSUint16Array.h new file mode 100644 index 000000000..abcbed7ec --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSUint16Array.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSUint16Array_h +#define JSUint16Array_h + +#include "JSTypedArrays.h" + +#endif // JSUint16Array_h + diff --git a/Source/JavaScriptCore/runtime/JSUint32Array.h b/Source/JavaScriptCore/runtime/JSUint32Array.h new file mode 100644 index 000000000..c563a939e --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSUint32Array.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSUint32Array_h +#define JSUint32Array_h + +#include "JSTypedArrays.h" + +#endif // JSUint32Array_h + diff --git a/Source/JavaScriptCore/runtime/JSUint8Array.h b/Source/JavaScriptCore/runtime/JSUint8Array.h new file mode 100644 index 000000000..399b8be95 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSUint8Array.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSUint8Array_h +#define JSUint8Array_h + +#include "JSTypedArrays.h" + +#endif // JSUint8Array_h + diff --git a/Source/JavaScriptCore/runtime/JSUint8ClampedArray.h b/Source/JavaScriptCore/runtime/JSUint8ClampedArray.h new file mode 100644 index 000000000..2bef42a62 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSUint8ClampedArray.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSUint8ClampedArray_h +#define JSUint8ClampedArray_h + +#include "JSTypedArrays.h" + +#endif // JSUint8ClampedArray_h + diff --git a/Source/JavaScriptCore/runtime/JSWeakMap.cpp b/Source/JavaScriptCore/runtime/JSWeakMap.cpp new file mode 100644 index 000000000..80b46ce84 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWeakMap.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSWeakMap.h" + +#include "JSCJSValueInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" +#include "WeakMapData.h" +#include "WriteBarrierInlines.h" + +namespace JSC { + +const ClassInfo JSWeakMap::s_info = { "WeakMap", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWeakMap) }; + +void JSWeakMap::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + m_weakMapData.set(vm, this, WeakMapData::create(vm)); +} + +void JSWeakMap::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Base::visitChildren(cell, visitor); + JSWeakMap* thisObj = jsCast<JSWeakMap*>(cell); + visitor.append(&thisObj->m_weakMapData); +} + +} diff --git a/Source/JavaScriptCore/runtime/JSWeakMap.h b/Source/JavaScriptCore/runtime/JSWeakMap.h new file mode 100644 index 000000000..a3229c265 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWeakMap.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSWeakMap_h +#define JSWeakMap_h + +#include "JSObject.h" + +namespace JSC { + +class WeakMapData; + +class JSWeakMap : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSWeakMap* create(VM& vm, Structure* structure) + { + JSWeakMap* instance = new (NotNull, allocateCell<JSWeakMap>(vm.heap)) JSWeakMap(vm, structure); + instance->finishCreation(vm); + return instance; + } + + static JSWeakMap* create(ExecState* exec, Structure* structure) + { + return create(exec->vm(), structure); + } + + WeakMapData* weakMapData() { return m_weakMapData.get(); } + + JSValue get(CallFrame*, JSObject*); + bool has(CallFrame*, JSObject*); + bool remove(CallFrame*, JSObject*); + + void set(CallFrame*, JSObject*, JSValue); + void clear(CallFrame*); + +private: + JSWeakMap(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(VM&); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<WeakMapData> m_weakMapData; +}; + +} + +#endif // !defined(JSWeakMap_h) diff --git a/Source/JavaScriptCore/runtime/JSWeakSet.cpp b/Source/JavaScriptCore/runtime/JSWeakSet.cpp new file mode 100644 index 000000000..3b807535f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWeakSet.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSWeakSet.h" + +#include "JSCJSValueInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" +#include "WeakMapData.h" +#include "WriteBarrierInlines.h" + +namespace JSC { + +const ClassInfo JSWeakSet::s_info = { "WeakSet", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWeakSet) }; + +void JSWeakSet::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + m_weakMapData.set(vm, this, WeakMapData::create(vm)); +} + +void JSWeakSet::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Base::visitChildren(cell, visitor); + JSWeakSet* thisObj = jsCast<JSWeakSet*>(cell); + visitor.append(&thisObj->m_weakMapData); +} + +} diff --git a/Source/JavaScriptCore/runtime/JSWeakSet.h b/Source/JavaScriptCore/runtime/JSWeakSet.h new file mode 100644 index 000000000..da2ecaa4a --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWeakSet.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSWeakSet_h +#define JSWeakSet_h + +#include "JSObject.h" + +namespace JSC { + +class WeakMapData; + +class JSWeakSet : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSWeakSet* create(VM& vm, Structure* structure) + { + JSWeakSet* instance = new (NotNull, allocateCell<JSWeakSet>(vm.heap)) JSWeakSet(vm, structure); + instance->finishCreation(vm); + return instance; + } + + static JSWeakSet* create(ExecState* exec, Structure* structure) + { + return create(exec->vm(), structure); + } + + WeakMapData* weakMapData() { return m_weakMapData.get(); } + + JSValue get(CallFrame*, JSObject*); + bool has(CallFrame*, JSObject*); + bool remove(CallFrame*, JSObject*); + + void set(CallFrame*, JSObject*, JSValue); + void clear(CallFrame*); + +private: + JSWeakSet(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(VM&); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<WeakMapData> m_weakMapData; +}; + +} + +#endif // !defined(JSWeakSet_h) diff --git a/Source/JavaScriptCore/runtime/JSWithScope.cpp b/Source/JavaScriptCore/runtime/JSWithScope.cpp new file mode 100644 index 000000000..1ffe8d1f2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWithScope.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "JSWithScope.h" + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo JSWithScope::s_info = { "WithScope", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWithScope) }; + +void JSWithScope::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSWithScope* thisObject = jsCast<JSWithScope*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_object); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSWithScope.h b/Source/JavaScriptCore/runtime/JSWithScope.h new file mode 100644 index 000000000..8e5d09fa1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWithScope.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef JSWithScope_h +#define JSWithScope_h + +#include "JSGlobalObject.h" + +namespace JSC { + +class JSWithScope : public JSScope { +public: + typedef JSScope Base; + + static JSWithScope* create(ExecState* exec, JSObject* object, JSScope* next) + { + JSWithScope* withScope = new (NotNull, allocateCell<JSWithScope>(*exec->heap())) JSWithScope(exec, object, next); + withScope->finishCreation(exec->vm()); + return withScope; + } + + JSObject* object() { return m_object.get(); } + + static void visitChildren(JSCell*, SlotVisitor&); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(WithScopeType, StructureFlags), info()); + } + + DECLARE_EXPORT_INFO; + +private: + JSWithScope(ExecState* exec, JSObject* object, JSScope* next) + : Base( + exec->vm(), + exec->lexicalGlobalObject()->withScopeStructure(), + next + ) + , m_object(exec->vm(), this, object) + { + } + + WriteBarrier<JSObject> m_object; +}; + +} // namespace JSC + +#endif // JSWithScope_h diff --git a/Source/JavaScriptCore/runtime/JSWrapperObject.cpp b/Source/JavaScriptCore/runtime/JSWrapperObject.cpp new file mode 100644 index 000000000..b6fadadb0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWrapperObject.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006 Maks Orlovich + * Copyright (C) 2006, 2009, 2012 Apple, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "JSWrapperObject.h" + +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSWrapperObject); + +void JSWrapperObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSWrapperObject* thisObject = jsCast<JSWrapperObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + JSObject::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_internalValue); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSWrapperObject.h b/Source/JavaScriptCore/runtime/JSWrapperObject.h new file mode 100644 index 000000000..1036add59 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSWrapperObject.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2006 Maks Orlovich + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef JSWrapperObject_h +#define JSWrapperObject_h + +#include "JSDestructibleObject.h" + +namespace JSC { + +// This class is used as a base for classes such as String, +// Number, Boolean and Date which are wrappers for primitive types. +class JSWrapperObject : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static size_t allocationSize(size_t inlineCapacity) + { + ASSERT_UNUSED(inlineCapacity, !inlineCapacity); + return sizeof(JSWrapperObject); + } + + JSValue internalValue() const; + void setInternalValue(VM&, JSValue); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static ptrdiff_t internalValueOffset() { return OBJECT_OFFSETOF(JSWrapperObject, m_internalValue); } + static ptrdiff_t internalValueCellOffset() + { +#if USE(JSVALUE64) + return internalValueOffset(); +#else + return internalValueOffset() + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload); +#endif + } + +protected: + explicit JSWrapperObject(VM&, Structure*); + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + +private: + WriteBarrier<Unknown> m_internalValue; +}; + +inline JSWrapperObject::JSWrapperObject(VM& vm, Structure* structure) + : JSDestructibleObject(vm, structure) +{ +} + +inline JSValue JSWrapperObject::internalValue() const +{ + return m_internalValue.get(); +} + +inline void JSWrapperObject::setInternalValue(VM& vm, JSValue value) +{ + ASSERT(value); + ASSERT(!value.isObject()); + m_internalValue.set(vm, this, value); +} + +} // namespace JSC + +#endif // JSWrapperObject_h diff --git a/Source/JavaScriptCore/runtime/LiteralParser.cpp b/Source/JavaScriptCore/runtime/LiteralParser.cpp new file mode 100644 index 000000000..3aed10d9b --- /dev/null +++ b/Source/JavaScriptCore/runtime/LiteralParser.cpp @@ -0,0 +1,845 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2012 Mathias Bynens (mathias@qiwi.be) + * + * 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "LiteralParser.h" + +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" +#include "JSArray.h" +#include "JSString.h" +#include "Lexer.h" +#include "ObjectConstructor.h" +#include "JSCInlines.h" +#include "StrongInlines.h" +#include <wtf/ASCIICType.h> +#include <wtf/dtoa.h> +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +template <typename CharType> +static inline bool isJSONWhiteSpace(const CharType& c) +{ + // The JSON RFC 4627 defines a list of allowed characters to be considered + // insignificant white space: http://www.ietf.org/rfc/rfc4627.txt (2. JSON Grammar). + return c == ' ' || c == 0x9 || c == 0xA || c == 0xD; +} + +template <typename CharType> +bool LiteralParser<CharType>::tryJSONPParse(Vector<JSONPData>& results, bool needsFullSourceInfo) +{ + if (m_lexer.next() != TokIdentifier) + return false; + do { + Vector<JSONPPathEntry> path; + // Unguarded next to start off the lexer + Identifier name = Identifier::fromString(&m_exec->vm(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); + JSONPPathEntry entry; + if (name == m_exec->vm().propertyNames->varKeyword) { + if (m_lexer.next() != TokIdentifier) + return false; + entry.m_type = JSONPPathEntryTypeDeclare; + entry.m_pathEntryName = Identifier::fromString(&m_exec->vm(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); + path.append(entry); + } else { + entry.m_type = JSONPPathEntryTypeDot; + entry.m_pathEntryName = Identifier::fromString(&m_exec->vm(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); + path.append(entry); + } + if (isLexerKeyword(entry.m_pathEntryName)) + return false; + TokenType tokenType = m_lexer.next(); + if (entry.m_type == JSONPPathEntryTypeDeclare && tokenType != TokAssign) + return false; + while (tokenType != TokAssign) { + switch (tokenType) { + case TokLBracket: { + entry.m_type = JSONPPathEntryTypeLookup; + if (m_lexer.next() != TokNumber) + return false; + double doubleIndex = m_lexer.currentToken().numberToken; + int index = (int)doubleIndex; + if (index != doubleIndex || index < 0) + return false; + entry.m_pathIndex = index; + if (m_lexer.next() != TokRBracket) + return false; + break; + } + case TokDot: { + entry.m_type = JSONPPathEntryTypeDot; + if (m_lexer.next() != TokIdentifier) + return false; + entry.m_pathEntryName = Identifier::fromString(&m_exec->vm(), m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start); + break; + } + case TokLParen: { + if (path.last().m_type != JSONPPathEntryTypeDot || needsFullSourceInfo) + return false; + path.last().m_type = JSONPPathEntryTypeCall; + entry = path.last(); + goto startJSON; + } + default: + return false; + } + path.append(entry); + tokenType = m_lexer.next(); + } + startJSON: + m_lexer.next(); + results.append(JSONPData()); + results.last().m_value.set(m_exec->vm(), parse(StartParseExpression)); + if (!results.last().m_value) + return false; + results.last().m_path.swap(path); + if (entry.m_type == JSONPPathEntryTypeCall) { + if (m_lexer.currentToken().type != TokRParen) + return false; + m_lexer.next(); + } + if (m_lexer.currentToken().type != TokSemi) + break; + m_lexer.next(); + } while (m_lexer.currentToken().type == TokIdentifier); + return m_lexer.currentToken().type == TokEnd; +} + +template <typename CharType> +ALWAYS_INLINE const Identifier LiteralParser<CharType>::makeIdentifier(const LChar* characters, size_t length) +{ + if (!length) + return m_exec->vm().propertyNames->emptyIdentifier; + if (characters[0] >= MaximumCachableCharacter) + return Identifier::fromString(&m_exec->vm(), characters, length); + + if (length == 1) { + if (!m_shortIdentifiers[characters[0]].isNull()) + return m_shortIdentifiers[characters[0]]; + m_shortIdentifiers[characters[0]] = Identifier::fromString(&m_exec->vm(), characters, length); + return m_shortIdentifiers[characters[0]]; + } + if (!m_recentIdentifiers[characters[0]].isNull() && Identifier::equal(m_recentIdentifiers[characters[0]].impl(), characters, length)) + return m_recentIdentifiers[characters[0]]; + m_recentIdentifiers[characters[0]] = Identifier::fromString(&m_exec->vm(), characters, length); + return m_recentIdentifiers[characters[0]]; +} + +template <typename CharType> +ALWAYS_INLINE const Identifier LiteralParser<CharType>::makeIdentifier(const UChar* characters, size_t length) +{ + if (!length) + return m_exec->vm().propertyNames->emptyIdentifier; + if (characters[0] >= MaximumCachableCharacter) + return Identifier::fromString(&m_exec->vm(), characters, length); + + if (length == 1) { + if (!m_shortIdentifiers[characters[0]].isNull()) + return m_shortIdentifiers[characters[0]]; + m_shortIdentifiers[characters[0]] = Identifier::fromString(&m_exec->vm(), characters, length); + return m_shortIdentifiers[characters[0]]; + } + if (!m_recentIdentifiers[characters[0]].isNull() && Identifier::equal(m_recentIdentifiers[characters[0]].impl(), characters, length)) + return m_recentIdentifiers[characters[0]]; + m_recentIdentifiers[characters[0]] = Identifier::fromString(&m_exec->vm(), characters, length); + return m_recentIdentifiers[characters[0]]; +} + +template <typename CharType> +template <ParserMode mode> TokenType LiteralParser<CharType>::Lexer::lex(LiteralParserToken<CharType>& token) +{ + while (m_ptr < m_end && isJSONWhiteSpace(*m_ptr)) + ++m_ptr; + + ASSERT(m_ptr <= m_end); + if (m_ptr >= m_end) { + token.type = TokEnd; + token.start = token.end = m_ptr; + return TokEnd; + } + token.type = TokError; + token.start = m_ptr; + switch (*m_ptr) { + case '[': + token.type = TokLBracket; + token.end = ++m_ptr; + return TokLBracket; + case ']': + token.type = TokRBracket; + token.end = ++m_ptr; + return TokRBracket; + case '(': + token.type = TokLParen; + token.end = ++m_ptr; + return TokLParen; + case ')': + token.type = TokRParen; + token.end = ++m_ptr; + return TokRParen; + case '{': + token.type = TokLBrace; + token.end = ++m_ptr; + return TokLBrace; + case '}': + token.type = TokRBrace; + token.end = ++m_ptr; + return TokRBrace; + case ',': + token.type = TokComma; + token.end = ++m_ptr; + return TokComma; + case ':': + token.type = TokColon; + token.end = ++m_ptr; + return TokColon; + case '"': + return lexString<mode, '"'>(token); + case 't': + if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') { + m_ptr += 4; + token.type = TokTrue; + token.end = m_ptr; + return TokTrue; + } + break; + case 'f': + if (m_end - m_ptr >= 5 && m_ptr[1] == 'a' && m_ptr[2] == 'l' && m_ptr[3] == 's' && m_ptr[4] == 'e') { + m_ptr += 5; + token.type = TokFalse; + token.end = m_ptr; + return TokFalse; + } + break; + case 'n': + if (m_end - m_ptr >= 4 && m_ptr[1] == 'u' && m_ptr[2] == 'l' && m_ptr[3] == 'l') { + m_ptr += 4; + token.type = TokNull; + token.end = m_ptr; + return TokNull; + } + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return lexNumber(token); + } + if (m_ptr < m_end) { + if (*m_ptr == '.') { + token.type = TokDot; + token.end = ++m_ptr; + return TokDot; + } + if (*m_ptr == '=') { + token.type = TokAssign; + token.end = ++m_ptr; + return TokAssign; + } + if (*m_ptr == ';') { + token.type = TokSemi; + token.end = ++m_ptr; + return TokAssign; + } + if (isASCIIAlpha(*m_ptr) || *m_ptr == '_' || *m_ptr == '$') + return lexIdentifier(token); + if (*m_ptr == '\'') { + if (mode == StrictJSON) { + m_lexErrorMessage = ASCIILiteral("Single quotes (\') are not allowed in JSON"); + return TokError; + } + return lexString<mode, '\''>(token); + } + } + m_lexErrorMessage = String::format("Unrecognized token '%c'", *m_ptr); + return TokError; +} + +template <> +ALWAYS_INLINE TokenType LiteralParser<LChar>::Lexer::lexIdentifier(LiteralParserToken<LChar>& token) +{ + while (m_ptr < m_end && (isASCIIAlphanumeric(*m_ptr) || *m_ptr == '_' || *m_ptr == '$')) + m_ptr++; + token.stringIs8Bit = 1; + token.stringToken8 = token.start; + token.stringLength = m_ptr - token.start; + token.type = TokIdentifier; + token.end = m_ptr; + return TokIdentifier; +} + +template <> +ALWAYS_INLINE TokenType LiteralParser<UChar>::Lexer::lexIdentifier(LiteralParserToken<UChar>& token) +{ + while (m_ptr < m_end && (isASCIIAlphanumeric(*m_ptr) || *m_ptr == '_' || *m_ptr == '$' || *m_ptr == 0x200C || *m_ptr == 0x200D)) + m_ptr++; + token.stringIs8Bit = 0; + token.stringToken16 = token.start; + token.stringLength = m_ptr - token.start; + token.type = TokIdentifier; + token.end = m_ptr; + return TokIdentifier; +} + +template <typename CharType> +TokenType LiteralParser<CharType>::Lexer::next() +{ + if (m_mode == NonStrictJSON) + return lex<NonStrictJSON>(m_currentToken); + if (m_mode == JSONP) + return lex<JSONP>(m_currentToken); + return lex<StrictJSON>(m_currentToken); +} + +template <> +ALWAYS_INLINE void setParserTokenString<LChar>(LiteralParserToken<LChar>& token, const LChar* string) +{ + token.stringIs8Bit = 1; + token.stringToken8 = string; +} + +template <> +ALWAYS_INLINE void setParserTokenString<UChar>(LiteralParserToken<UChar>& token, const UChar* string) +{ + token.stringIs8Bit = 0; + token.stringToken16 = string; +} + +template <ParserMode mode, typename CharType, LChar terminator> static inline bool isSafeStringCharacter(LChar c) +{ + return (c >= ' ' && c != '\\' && c != terminator) || (c == '\t' && mode != StrictJSON); +} + +template <ParserMode mode, typename CharType, UChar terminator> static inline bool isSafeStringCharacter(UChar c) +{ + return (c >= ' ' && (mode == StrictJSON || c <= 0xff) && c != '\\' && c != terminator) || (c == '\t' && mode != StrictJSON); +} + +template <typename CharType> +template <ParserMode mode, char terminator> ALWAYS_INLINE TokenType LiteralParser<CharType>::Lexer::lexString(LiteralParserToken<CharType>& token) +{ + ++m_ptr; + const CharType* runStart = m_ptr; + StringBuilder builder; + do { + runStart = m_ptr; + while (m_ptr < m_end && isSafeStringCharacter<mode, CharType, terminator>(*m_ptr)) + ++m_ptr; + if (builder.length()) + builder.append(runStart, m_ptr - runStart); + if ((mode != NonStrictJSON) && m_ptr < m_end && *m_ptr == '\\') { + if (builder.isEmpty() && runStart < m_ptr) + builder.append(runStart, m_ptr - runStart); + ++m_ptr; + if (m_ptr >= m_end) { + m_lexErrorMessage = ASCIILiteral("Unterminated string"); + return TokError; + } + switch (*m_ptr) { + case '"': + builder.append('"'); + m_ptr++; + break; + case '\\': + builder.append('\\'); + m_ptr++; + break; + case '/': + builder.append('/'); + m_ptr++; + break; + case 'b': + builder.append('\b'); + m_ptr++; + break; + case 'f': + builder.append('\f'); + m_ptr++; + break; + case 'n': + builder.append('\n'); + m_ptr++; + break; + case 'r': + builder.append('\r'); + m_ptr++; + break; + case 't': + builder.append('\t'); + m_ptr++; + break; + + case 'u': + if ((m_end - m_ptr) < 5) { + m_lexErrorMessage = ASCIILiteral("\\u must be followed by 4 hex digits"); + return TokError; + } // uNNNN == 5 characters + for (int i = 1; i < 5; i++) { + if (!isASCIIHexDigit(m_ptr[i])) { + m_lexErrorMessage = String::format("\"\\%s\" is not a valid unicode escape", String(m_ptr, 5).ascii().data()); + return TokError; + } + } + builder.append(JSC::Lexer<CharType>::convertUnicode(m_ptr[1], m_ptr[2], m_ptr[3], m_ptr[4])); + m_ptr += 5; + break; + + default: + if (*m_ptr == '\'' && mode != StrictJSON) { + builder.append('\''); + m_ptr++; + break; + } + m_lexErrorMessage = String::format("Invalid escape character %c", *m_ptr); + return TokError; + } + } + } while ((mode != NonStrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != terminator); + + if (m_ptr >= m_end || *m_ptr != terminator) { + m_lexErrorMessage = ASCIILiteral("Unterminated string"); + return TokError; + } + + if (builder.isEmpty()) { + token.stringBuffer = String(); + setParserTokenString<CharType>(token, runStart); + token.stringLength = m_ptr - runStart; + } else { + token.stringBuffer = builder.toString(); + if (token.stringBuffer.is8Bit()) { + token.stringIs8Bit = 1; + token.stringToken8 = token.stringBuffer.characters8(); + } else { + token.stringIs8Bit = 0; + token.stringToken16 = token.stringBuffer.characters16(); + } + token.stringLength = token.stringBuffer.length(); + } + token.type = TokString; + token.end = ++m_ptr; + return TokString; +} + +template <typename CharType> +TokenType LiteralParser<CharType>::Lexer::lexNumber(LiteralParserToken<CharType>& token) +{ + // ES5 and json.org define numbers as + // number + // int + // int frac? exp? + // + // int + // -? 0 + // -? digit1-9 digits? + // + // digits + // digit digits? + // + // -?(0 | [1-9][0-9]*) ('.' [0-9]+)? ([eE][+-]? [0-9]+)? + + if (m_ptr < m_end && *m_ptr == '-') // -? + ++m_ptr; + + // (0 | [1-9][0-9]*) + if (m_ptr < m_end && *m_ptr == '0') // 0 + ++m_ptr; + else if (m_ptr < m_end && *m_ptr >= '1' && *m_ptr <= '9') { // [1-9] + ++m_ptr; + // [0-9]* + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + } else { + m_lexErrorMessage = ASCIILiteral("Invalid number"); + return TokError; + } + + // ('.' [0-9]+)? + if (m_ptr < m_end && *m_ptr == '.') { + ++m_ptr; + // [0-9]+ + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) { + m_lexErrorMessage = ASCIILiteral("Invalid digits after decimal point"); + return TokError; + } + + ++m_ptr; + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + } else if (m_ptr < m_end && (*m_ptr != 'e' && *m_ptr != 'E') && (m_ptr - token.start) < 10) { + double result = 0; + token.type = TokNumber; + token.end = m_ptr; + const CharType* digit = token.start; + int negative = 1; + if (*digit == '-') { + negative = -1; + digit++; + } + + while (digit < m_ptr) + result = result * 10 + (*digit++) - '0'; + result *= negative; + token.numberToken = result; + return TokNumber; + } + + // ([eE][+-]? [0-9]+)? + if (m_ptr < m_end && (*m_ptr == 'e' || *m_ptr == 'E')) { // [eE] + ++m_ptr; + + // [-+]? + if (m_ptr < m_end && (*m_ptr == '-' || *m_ptr == '+')) + ++m_ptr; + + // [0-9]+ + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) { + m_lexErrorMessage = ASCIILiteral("Exponent symbols should be followed by an optional '+' or '-' and then by at least one number"); + return TokError; + } + + ++m_ptr; + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + } + + token.type = TokNumber; + token.end = m_ptr; + size_t parsedLength; + token.numberToken = parseDouble(token.start, token.end - token.start, parsedLength); + return TokNumber; +} + +template <typename CharType> +JSValue LiteralParser<CharType>::parse(ParserState initialState) +{ + ParserState state = initialState; + MarkedArgumentBuffer objectStack; + JSValue lastValue; + Vector<ParserState, 16, UnsafeVectorOverflow> stateStack; + Vector<Identifier, 16, UnsafeVectorOverflow> identifierStack; + HashSet<JSObject*> visitedUnderscoreProto; + while (1) { + switch(state) { + startParseArray: + case StartParseArray: { + JSArray* array = constructEmptyArray(m_exec, 0); + objectStack.append(array); + } + doParseArrayStartExpression: + FALLTHROUGH; + case DoParseArrayStartExpression: { + TokenType lastToken = m_lexer.currentToken().type; + if (m_lexer.next() == TokRBracket) { + if (lastToken == TokComma) { + m_parseErrorMessage = ASCIILiteral("Unexpected comma at the end of array expression"); + return JSValue(); + } + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + + stateStack.append(DoParseArrayEndExpression); + goto startParseExpression; + } + case DoParseArrayEndExpression: { + JSArray* array = asArray(objectStack.last()); + array->putDirectIndex(m_exec, array->length(), lastValue); + + if (m_lexer.currentToken().type == TokComma) + goto doParseArrayStartExpression; + + if (m_lexer.currentToken().type != TokRBracket) { + m_parseErrorMessage = ASCIILiteral("Expected ']'"); + return JSValue(); + } + + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + startParseObject: + case StartParseObject: { + JSObject* object = constructEmptyObject(m_exec); + objectStack.append(object); + + TokenType type = m_lexer.next(); + if (type == TokString || (m_mode != StrictJSON && type == TokIdentifier)) { + LiteralParserToken<CharType> identifierToken = m_lexer.currentToken(); + + // Check for colon + if (m_lexer.next() != TokColon) { + m_parseErrorMessage = ASCIILiteral("Expected ':' before value in object property definition"); + return JSValue(); + } + + m_lexer.next(); + if (identifierToken.stringIs8Bit) + identifierStack.append(makeIdentifier(identifierToken.stringToken8, identifierToken.stringLength)); + else + identifierStack.append(makeIdentifier(identifierToken.stringToken16, identifierToken.stringLength)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } + if (type != TokRBrace) { + m_parseErrorMessage = ASCIILiteral("Expected '}'"); + return JSValue(); + } + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + doParseObjectStartExpression: + case DoParseObjectStartExpression: { + TokenType type = m_lexer.next(); + if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier)) { + m_parseErrorMessage = ASCIILiteral("Property name must be a string literal"); + return JSValue(); + } + LiteralParserToken<CharType> identifierToken = m_lexer.currentToken(); + + // Check for colon + if (m_lexer.next() != TokColon) { + m_parseErrorMessage = ASCIILiteral("Expected ':'"); + return JSValue(); + } + + m_lexer.next(); + if (identifierToken.stringIs8Bit) + identifierStack.append(makeIdentifier(identifierToken.stringToken8, identifierToken.stringLength)); + else + identifierStack.append(makeIdentifier(identifierToken.stringToken16, identifierToken.stringLength)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } + case DoParseObjectEndExpression: + { + JSObject* object = asObject(objectStack.last()); + PropertyName ident = identifierStack.last(); + if (m_mode != StrictJSON && ident == m_exec->vm().propertyNames->underscoreProto) { + if (!visitedUnderscoreProto.add(object).isNewEntry) { + m_parseErrorMessage = ASCIILiteral("Attempted to redefine __proto__ property"); + return JSValue(); + } + CodeBlock* codeBlock = m_exec->codeBlock(); + PutPropertySlot slot(object, codeBlock ? codeBlock->isStrictMode() : false); + objectStack.last().put(m_exec, ident, lastValue, slot); + } else { + if (Optional<uint32_t> index = parseIndex(ident)) + object->putDirectIndex(m_exec, index.value(), lastValue); + else + object->putDirect(m_exec->vm(), ident, lastValue); + } + identifierStack.removeLast(); + if (m_lexer.currentToken().type == TokComma) + goto doParseObjectStartExpression; + if (m_lexer.currentToken().type != TokRBrace) { + m_parseErrorMessage = ASCIILiteral("Expected '}'"); + return JSValue(); + } + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + startParseExpression: + case StartParseExpression: { + switch (m_lexer.currentToken().type) { + case TokLBracket: + goto startParseArray; + case TokLBrace: + goto startParseObject; + case TokString: { + LiteralParserToken<CharType> stringToken = m_lexer.currentToken(); + m_lexer.next(); + if (stringToken.stringIs8Bit) + lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken8, stringToken.stringLength).string()); + else + lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken16, stringToken.stringLength).string()); + break; + } + case TokNumber: { + LiteralParserToken<CharType> numberToken = m_lexer.currentToken(); + m_lexer.next(); + lastValue = jsNumber(numberToken.numberToken); + break; + } + case TokNull: + m_lexer.next(); + lastValue = jsNull(); + break; + + case TokTrue: + m_lexer.next(); + lastValue = jsBoolean(true); + break; + + case TokFalse: + m_lexer.next(); + lastValue = jsBoolean(false); + break; + case TokRBracket: + m_parseErrorMessage = ASCIILiteral("Unexpected token ']'"); + return JSValue(); + case TokRBrace: + m_parseErrorMessage = ASCIILiteral("Unexpected token '}'"); + return JSValue(); + case TokIdentifier: { + const LiteralParserToken<CharType>& token = m_lexer.currentToken(); + if (token.stringIs8Bit) + m_parseErrorMessage = String::format("Unexpected identifier \"%s\"", String(m_lexer.currentToken().stringToken8, m_lexer.currentToken().stringLength).ascii().data()); + else + m_parseErrorMessage = String::format("Unexpected identifier \"%s\"", String(m_lexer.currentToken().stringToken16, m_lexer.currentToken().stringLength).ascii().data()); + return JSValue(); + } + case TokColon: + m_parseErrorMessage = ASCIILiteral("Unexpected token ':'"); + return JSValue(); + case TokLParen: + m_parseErrorMessage = ASCIILiteral("Unexpected token '('"); + return JSValue(); + case TokRParen: + m_parseErrorMessage = ASCIILiteral("Unexpected token ')'"); + return JSValue(); + case TokComma: + m_parseErrorMessage = ASCIILiteral("Unexpected token ','"); + return JSValue(); + case TokDot: + m_parseErrorMessage = ASCIILiteral("Unexpected token '.'"); + return JSValue(); + case TokAssign: + m_parseErrorMessage = ASCIILiteral("Unexpected token '='"); + return JSValue(); + case TokSemi: + m_parseErrorMessage = ASCIILiteral("Unexpected token ';'"); + return JSValue(); + case TokEnd: + m_parseErrorMessage = ASCIILiteral("Unexpected EOF"); + return JSValue(); + case TokError: + default: + // Error + m_parseErrorMessage = ASCIILiteral("Could not parse value expression"); + return JSValue(); + } + break; + } + case StartParseStatement: { + switch (m_lexer.currentToken().type) { + case TokLBracket: + case TokNumber: + case TokString: + goto startParseExpression; + + case TokLParen: { + m_lexer.next(); + stateStack.append(StartParseStatementEndStatement); + goto startParseExpression; + } + case TokRBracket: + m_parseErrorMessage = ASCIILiteral("Unexpected token ']'"); + return JSValue(); + case TokLBrace: + m_parseErrorMessage = ASCIILiteral("Unexpected token '{'"); + return JSValue(); + case TokRBrace: + m_parseErrorMessage = ASCIILiteral("Unexpected token '}'"); + return JSValue(); + case TokIdentifier: + m_parseErrorMessage = ASCIILiteral("Unexpected identifier"); + return JSValue(); + case TokColon: + m_parseErrorMessage = ASCIILiteral("Unexpected token ':'"); + return JSValue(); + case TokRParen: + m_parseErrorMessage = ASCIILiteral("Unexpected token ')'"); + return JSValue(); + case TokComma: + m_parseErrorMessage = ASCIILiteral("Unexpected token ','"); + return JSValue(); + case TokTrue: + m_parseErrorMessage = ASCIILiteral("Unexpected token 'true'"); + return JSValue(); + case TokFalse: + m_parseErrorMessage = ASCIILiteral("Unexpected token 'false'"); + return JSValue(); + case TokNull: + m_parseErrorMessage = ASCIILiteral("Unexpected token 'null'"); + return JSValue(); + case TokEnd: + m_parseErrorMessage = ASCIILiteral("Unexpected EOF"); + return JSValue(); + case TokDot: + m_parseErrorMessage = ASCIILiteral("Unexpected token '.'"); + return JSValue(); + case TokAssign: + m_parseErrorMessage = ASCIILiteral("Unexpected token '='"); + return JSValue(); + case TokSemi: + m_parseErrorMessage = ASCIILiteral("Unexpected token ';'"); + return JSValue(); + case TokError: + default: + m_parseErrorMessage = ASCIILiteral("Could not parse statement"); + return JSValue(); + } + } + case StartParseStatementEndStatement: { + ASSERT(stateStack.isEmpty()); + if (m_lexer.currentToken().type != TokRParen) + return JSValue(); + if (m_lexer.next() == TokEnd) + return lastValue; + m_parseErrorMessage = ASCIILiteral("Unexpected content at end of JSON literal"); + return JSValue(); + } + default: + RELEASE_ASSERT_NOT_REACHED(); + } + if (stateStack.isEmpty()) + return lastValue; + state = stateStack.last(); + stateStack.removeLast(); + continue; + } +} + +// Instantiate the two flavors of LiteralParser we need instead of putting most of this file in LiteralParser.h +template class LiteralParser<LChar>; +template class LiteralParser<UChar>; + +} diff --git a/Source/JavaScriptCore/runtime/LiteralParser.h b/Source/JavaScriptCore/runtime/LiteralParser.h new file mode 100644 index 000000000..fcb79fa40 --- /dev/null +++ b/Source/JavaScriptCore/runtime/LiteralParser.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef LiteralParser_h +#define LiteralParser_h + +#include "Identifier.h" +#include "JSCJSValue.h" +#include "JSGlobalObjectFunctions.h" +#include <array> +#include <wtf/text/WTFString.h> + +namespace JSC { + +typedef enum { StrictJSON, NonStrictJSON, JSONP } ParserMode; + +enum JSONPPathEntryType { + JSONPPathEntryTypeDeclare, // var pathEntryName = JSON + JSONPPathEntryTypeDot, // <prior entries>.pathEntryName = JSON + JSONPPathEntryTypeLookup, // <prior entries>[pathIndex] = JSON + JSONPPathEntryTypeCall // <prior entries>(JSON) +}; + +enum ParserState { StartParseObject, StartParseArray, StartParseExpression, + StartParseStatement, StartParseStatementEndStatement, + DoParseObjectStartExpression, DoParseObjectEndExpression, + DoParseArrayStartExpression, DoParseArrayEndExpression }; +enum TokenType { TokLBracket, TokRBracket, TokLBrace, TokRBrace, + TokString, TokIdentifier, TokNumber, TokColon, + TokLParen, TokRParen, TokComma, TokTrue, TokFalse, + TokNull, TokEnd, TokDot, TokAssign, TokSemi, TokError }; + +struct JSONPPathEntry { + JSONPPathEntryType m_type; + Identifier m_pathEntryName; + int m_pathIndex; +}; + +struct JSONPData { + Vector<JSONPPathEntry> m_path; + Strong<Unknown> m_value; +}; + +template <typename CharType> +struct LiteralParserToken { + TokenType type; + const CharType* start; + const CharType* end; + String stringBuffer; + union { + double numberToken; + struct { + union { + const LChar* stringToken8; + const UChar* stringToken16; + }; + unsigned stringIs8Bit : 1; + unsigned stringLength : 31; + }; + }; +}; + +template <typename CharType> +ALWAYS_INLINE void setParserTokenString(LiteralParserToken<CharType>&, const CharType* string); + +template <typename CharType> +class LiteralParser { +public: + LiteralParser(ExecState* exec, const CharType* characters, unsigned length, ParserMode mode) + : m_exec(exec) + , m_lexer(characters, length, mode) + , m_mode(mode) + { + } + + String getErrorMessage() + { + if (!m_lexer.getErrorMessage().isEmpty()) + return String::format("JSON Parse error: %s", m_lexer.getErrorMessage().ascii().data()); + if (!m_parseErrorMessage.isEmpty()) + return String::format("JSON Parse error: %s", m_parseErrorMessage.ascii().data()); + return ASCIILiteral("JSON Parse error: Unable to parse JSON string"); + } + + JSValue tryLiteralParse() + { + m_lexer.next(); + JSValue result = parse(m_mode == StrictJSON ? StartParseExpression : StartParseStatement); + if (m_lexer.currentToken().type == TokSemi) + m_lexer.next(); + if (m_lexer.currentToken().type != TokEnd) + return JSValue(); + return result; + } + + bool tryJSONPParse(Vector<JSONPData>&, bool needsFullSourceInfo); + +private: + class Lexer { + public: + Lexer(const CharType* characters, unsigned length, ParserMode mode) + : m_mode(mode) + , m_ptr(characters) + , m_end(characters + length) + { + } + + TokenType next(); + + const LiteralParserToken<CharType>& currentToken() + { + return m_currentToken; + } + + String getErrorMessage() { return m_lexErrorMessage; } + + private: + String m_lexErrorMessage; + template <ParserMode mode> TokenType lex(LiteralParserToken<CharType>&); + ALWAYS_INLINE TokenType lexIdentifier(LiteralParserToken<CharType>&); + template <ParserMode mode, char terminator> ALWAYS_INLINE TokenType lexString(LiteralParserToken<CharType>&); + ALWAYS_INLINE TokenType lexNumber(LiteralParserToken<CharType>&); + LiteralParserToken<CharType> m_currentToken; + ParserMode m_mode; + const CharType* m_ptr; + const CharType* m_end; + }; + + class StackGuard; + JSValue parse(ParserState); + + ExecState* m_exec; + typename LiteralParser<CharType>::Lexer m_lexer; + ParserMode m_mode; + String m_parseErrorMessage; + static unsigned const MaximumCachableCharacter = 128; + std::array<Identifier, MaximumCachableCharacter> m_shortIdentifiers; + std::array<Identifier, MaximumCachableCharacter> m_recentIdentifiers; + ALWAYS_INLINE const Identifier makeIdentifier(const LChar* characters, size_t length); + ALWAYS_INLINE const Identifier makeIdentifier(const UChar* characters, size_t length); + }; + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/Lookup.cpp b/Source/JavaScriptCore/runtime/Lookup.cpp new file mode 100644 index 000000000..7df15b486 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Lookup.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "Lookup.h" + +#include "Executable.h" +#include "GetterSetter.h" +#include "JSFunction.h" +#include "JSCInlines.h" + +namespace JSC { + +void reifyStaticAccessor(VM& vm, const HashTableValue& value, JSObject& thisObj, PropertyName propertyName) +{ + JSGlobalObject* globalObject = thisObj.globalObject(); + GetterSetter* accessor = GetterSetter::create(vm, globalObject); + if (value.accessorGetter()) { + RefPtr<StringImpl> getterName = WTF::tryMakeString(ASCIILiteral("get "), String(*propertyName.publicName())); + if (!getterName) + return; + accessor->setGetter(vm, globalObject, JSFunction::create(vm, globalObject, 0, *getterName, value.accessorGetter())); + } + thisObj.putDirectNonIndexAccessor(vm, propertyName, accessor, value.attributes()); +} + +bool setUpStaticFunctionSlot(ExecState* exec, const HashTableValue* entry, JSObject* thisObj, PropertyName propertyName, PropertySlot& slot) +{ + ASSERT(thisObj->globalObject()); + ASSERT(entry->attributes() & BuiltinOrFunctionOrAccessor); + VM& vm = exec->vm(); + unsigned attributes; + bool isAccessor = entry->attributes() & Accessor; + PropertyOffset offset = thisObj->getDirectOffset(vm, propertyName, attributes); + + if (!isValidOffset(offset)) { + // If a property is ever deleted from an object with a static table, then we reify + // all static functions at that time - after this we shouldn't be re-adding anything. + if (thisObj->staticFunctionsReified()) + return false; + + if (entry->attributes() & Builtin) + thisObj->putDirectBuiltinFunction(vm, thisObj->globalObject(), propertyName, entry->builtinGenerator()(vm), entry->attributes()); + else if (entry->attributes() & Function) { + thisObj->putDirectNativeFunction( + vm, thisObj->globalObject(), propertyName, entry->functionLength(), + entry->function(), entry->intrinsic(), entry->attributes()); + } else { + ASSERT(isAccessor); + reifyStaticAccessor(vm, *entry, *thisObj, propertyName); + } + + offset = thisObj->getDirectOffset(vm, propertyName, attributes); + ASSERT(isValidOffset(offset)); + } + + if (isAccessor) + slot.setCacheableGetterSlot(thisObj, attributes, jsCast<GetterSetter*>(thisObj->getDirect(offset)), offset); + else + slot.setValue(thisObj, attributes, thisObj->getDirect(offset), offset); + return true; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Lookup.h b/Source/JavaScriptCore/runtime/Lookup.h new file mode 100644 index 000000000..6f6023651 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Lookup.h @@ -0,0 +1,322 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef Lookup_h +#define Lookup_h + +#include "BatchedTransitionOptimizer.h" +#include "CallFrame.h" +#include "CustomGetterSetter.h" +#include "Identifier.h" +#include "IdentifierInlines.h" +#include "Intrinsic.h" +#include "JSGlobalObject.h" +#include "PropertySlot.h" +#include "PutPropertySlot.h" +#include <wtf/Assertions.h> + +namespace JSC { + +struct CompactHashIndex { + const int16_t value; + const int16_t next; +}; + +// FIXME: There is no reason this get function can't be simpler. +// ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject) +typedef PropertySlot::GetValueFunc GetFunction; +typedef PutPropertySlot::PutValueFunc PutFunction; +typedef FunctionExecutable* (*BuiltinGenerator)(VM&); + +// Hash table generated by the create_hash_table script. +struct HashTableValue { + const char* m_key; // property name + unsigned m_attributes; // JSObject attributes + Intrinsic m_intrinsic; + union ValueStorage { + constexpr ValueStorage(intptr_t value1, intptr_t value2) + : value1(value1) + , value2(value2) + { } + constexpr ValueStorage(long long constant) + : constant(constant) + { } + + struct { + intptr_t value1; + intptr_t value2; + }; + long long constant; + } m_values; + + unsigned attributes() const { return m_attributes; } + + Intrinsic intrinsic() const { ASSERT(m_attributes & Function); return m_intrinsic; } + BuiltinGenerator builtinGenerator() const { ASSERT(m_attributes & Builtin); return reinterpret_cast<BuiltinGenerator>(m_values.value1); } + NativeFunction function() const { ASSERT(m_attributes & Function); return reinterpret_cast<NativeFunction>(m_values.value1); } + unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_values.value2); } + + GetFunction propertyGetter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrAccessorOrConstant)); return reinterpret_cast<GetFunction>(m_values.value1); } + PutFunction propertyPutter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrAccessorOrConstant)); return reinterpret_cast<PutFunction>(m_values.value2); } + + NativeFunction accessorGetter() const { ASSERT(m_attributes & Accessor); return reinterpret_cast<NativeFunction>(m_values.value1); } + NativeFunction accessorSetter() const { ASSERT(m_attributes & Accessor); return reinterpret_cast<NativeFunction>(m_values.value2); } + + long long constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_values.constant; } + + intptr_t lexerValue() const { ASSERT(!m_attributes); return m_values.value1; } +}; + +struct HashTable { + int numberOfValues; + int indexMask; + bool hasSetterOrReadonlyProperties; + + const HashTableValue* values; // Fixed values generated by script. + const CompactHashIndex* index; + + // Find an entry in the table, and return the entry. + ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const + { + if (propertyName.isSymbol()) + return nullptr; + + auto uid = propertyName.uid(); + if (!uid) + return nullptr; + + int indexEntry = IdentifierRepHash::hash(uid) & indexMask; + int valueIndex = index[indexEntry].value; + if (valueIndex == -1) + return nullptr; + + while (true) { + if (WTF::equal(uid, values[valueIndex].m_key)) + return &values[valueIndex]; + + indexEntry = index[indexEntry].next; + if (indexEntry == -1) + return nullptr; + valueIndex = index[indexEntry].value; + ASSERT(valueIndex != -1); + }; + } + + class ConstIterator { + public: + ConstIterator(const HashTable* table, int position) + : m_table(table) + , m_position(position) + { + skipInvalidKeys(); + } + + const HashTableValue* value() + { + return &m_table->values[m_position]; + } + + const char* key() + { + return m_table->values[m_position].m_key; + } + + const HashTableValue* operator->() + { + return value(); + } + + bool operator!=(const ConstIterator& other) + { + ASSERT(m_table == other.m_table); + return m_position != other.m_position; + } + + ConstIterator& operator++() + { + ASSERT(m_position < m_table->numberOfValues); + ++m_position; + skipInvalidKeys(); + return *this; + } + + private: + void skipInvalidKeys() + { + ASSERT(m_position <= m_table->numberOfValues); + while (m_position < m_table->numberOfValues && !m_table->values[m_position].m_key) + ++m_position; + ASSERT(m_position <= m_table->numberOfValues); + } + + const HashTable* m_table; + int m_position; + }; + + ConstIterator begin() const + { + return ConstIterator(this, 0); + } + ConstIterator end() const + { + return ConstIterator(this, numberOfValues); + } +}; + +JS_EXPORT_PRIVATE bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject* thisObject, PropertyName, PropertySlot&); +JS_EXPORT_PRIVATE void reifyStaticAccessor(VM&, const HashTableValue&, JSObject& thisObject, PropertyName); + +/** + * This method does it all (looking in the hashtable, checking for function + * overrides, creating the function or retrieving from cache, calling + * getValueProperty in case of a non-function property, forwarding to parent if + * unknown property). + */ +template <class ThisImp, class ParentImp> +inline bool getStaticPropertySlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) +{ + const HashTableValue* entry = table.entry(propertyName); + + if (!entry) // not found, forward to parent + return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); + + if (entry->attributes() & BuiltinOrFunctionOrAccessor) + return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + + if (entry->attributes() & ConstantInteger) { + slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger())); + return true; + } + + slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter()); + return true; +} + +/** + * Simplified version of getStaticPropertySlot in case there are only functions. + * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing + * a dummy getValueProperty. + */ +template <class ParentImp> +inline bool getStaticFunctionSlot(ExecState* exec, const HashTable& table, JSObject* thisObj, PropertyName propertyName, PropertySlot& slot) +{ + if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot)) + return true; + + const HashTableValue* entry = table.entry(propertyName); + if (!entry) + return false; + + return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); +} + +/** + * Simplified version of getStaticPropertySlot in case there are no functions, only "values". + * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. + */ +template <class ThisImp, class ParentImp> +inline bool getStaticValueSlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) +{ + const HashTableValue* entry = table.entry(propertyName); + + if (!entry) // not found, forward to parent + return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); + + ASSERT(!(entry->attributes() & BuiltinOrFunctionOrAccessor)); + + if (entry->attributes() & ConstantInteger) { + slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger())); + return true; + } + + slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter()); + return true; +} + +inline void putEntry(ExecState* exec, const HashTableValue* entry, JSObject* base, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + // If this is a function put it as an override property. + if (entry->attributes() & BuiltinOrFunction) { + if (JSObject* thisObject = jsDynamicCast<JSObject*>(slot.thisValue())) + thisObject->putDirect(exec->vm(), propertyName, value); + } else if (entry->attributes() & Accessor) { + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + } else if (!(entry->attributes() & ReadOnly)) { + entry->propertyPutter()(exec, base, JSValue::encode(slot.thisValue()), JSValue::encode(value)); + slot.setCustomProperty(base, entry->propertyPutter()); + } else if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); +} + +/** + * This one is for "put". + * It looks up a hash entry for the property to be set. If an entry + * is found it sets the value and returns true, else it returns false. + */ +inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSObject* base, JSValue value, const HashTable& table, PutPropertySlot& slot) +{ + const HashTableValue* entry = table.entry(propertyName); + + if (!entry) + return false; + + putEntry(exec, entry, base, propertyName, value, slot); + return true; +} + +template<unsigned numberOfValues> +inline void reifyStaticProperties(VM& vm, const HashTableValue (&values)[numberOfValues], JSObject& thisObj) +{ + BatchedTransitionOptimizer transitionOptimizer(vm, &thisObj); + for (auto& value : values) { + if (!value.m_key) + continue; + + Identifier propertyName = Identifier::fromString(&vm, reinterpret_cast<const LChar*>(value.m_key), strlen(value.m_key)); + if (value.attributes() & Builtin) { + thisObj.putDirectBuiltinFunction(vm, thisObj.globalObject(), propertyName, value.builtinGenerator()(vm), value.attributes()); + continue; + } + + if (value.attributes() & Function) { + thisObj.putDirectNativeFunction(vm, thisObj.globalObject(), propertyName, value.functionLength(), + value.function(), value.intrinsic(), value.attributes()); + continue; + } + + if (value.attributes() & ConstantInteger) { + thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), value.attributes()); + continue; + } + + if (value.attributes() & Accessor) { + reifyStaticAccessor(vm, value, thisObj, propertyName); + continue; + } + + CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, value.propertyGetter(), value.propertyPutter()); + thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, value.attributes()); + } +} + +} // namespace JSC + +#endif // Lookup_h diff --git a/Source/JavaScriptCore/runtime/MapConstructor.cpp b/Source/JavaScriptCore/runtime/MapConstructor.cpp new file mode 100644 index 000000000..fad52c056 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MapConstructor.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "MapConstructor.h" + +#include "Error.h" +#include "IteratorOperations.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "JSMap.h" +#include "MapPrototype.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo MapConstructor::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(MapConstructor) }; + +void MapConstructor::finishCreation(VM& vm, MapPrototype* mapPrototype) +{ + Base::finishCreation(vm, mapPrototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, mapPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), ReadOnly | DontEnum | DontDelete); +} + +static EncodedJSValue JSC_HOST_CALL callMap(ExecState* exec) +{ + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Map cannot be called as a function"))); +} + +static EncodedJSValue JSC_HOST_CALL constructMap(ExecState* exec) +{ + JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject(); + Structure* mapStructure = globalObject->mapStructure(); + JSMap* map = JSMap::create(exec, mapStructure); + JSValue iterable = exec->argument(0); + if (iterable.isUndefinedOrNull()) + return JSValue::encode(map); + + JSValue adderFunction = map->JSObject::get(exec, exec->propertyNames().set); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData adderFunctionCallData; + CallType adderFunctionCallType = getCallData(adderFunction, adderFunctionCallData); + if (adderFunctionCallType == CallTypeNone) + return JSValue::encode(throwTypeError(exec)); + + JSValue iteratorFunction = iterable.get(exec, exec->propertyNames().iteratorSymbol); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData iteratorFunctionCallData; + CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData); + if (iteratorFunctionCallType == CallTypeNone) + return JSValue::encode(throwTypeError(exec)); + + ArgList iteratorFunctionArguments; + JSValue iterator = call(exec, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (!iterator.isObject()) + return JSValue::encode(throwTypeError(exec)); + + while (true) { + JSValue next = iteratorStep(exec, iterator); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (next.isFalse()) + return JSValue::encode(map); + + JSValue nextItem = iteratorValue(exec, next); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (!nextItem.isObject()) { + throwTypeError(exec); + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + + JSValue key = nextItem.get(exec, 0); + if (exec->hadException()) { + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + + JSValue value = nextItem.get(exec, 1); + if (exec->hadException()) { + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + + MarkedArgumentBuffer arguments; + arguments.append(key); + arguments.append(value); + call(exec, adderFunction, adderFunctionCallType, adderFunctionCallData, map, arguments); + if (exec->hadException()) { + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + } + RELEASE_ASSERT_NOT_REACHED(); + return JSValue::encode(map); +} + +ConstructType MapConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructMap; + return ConstructTypeHost; +} + +CallType MapConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callMap; + return CallTypeHost; +} + +} diff --git a/Source/JavaScriptCore/runtime/MapConstructor.h b/Source/JavaScriptCore/runtime/MapConstructor.h new file mode 100644 index 000000000..1a85de5b1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MapConstructor.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef MapConstructor_h +#define MapConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class MapPrototype; + +class MapConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + + static MapConstructor* create(VM& vm, Structure* structure, MapPrototype* mapPrototype) + { + MapConstructor* constructor = new (NotNull, allocateCell<MapConstructor>(vm.heap)) MapConstructor(vm, structure); + constructor->finishCreation(vm, mapPrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + MapConstructor(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, MapPrototype*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} + +#endif // !defined(MapConstructor_h) diff --git a/Source/JavaScriptCore/runtime/MapData.h b/Source/JavaScriptCore/runtime/MapData.h new file mode 100644 index 000000000..3b5863efc --- /dev/null +++ b/Source/JavaScriptCore/runtime/MapData.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef MapData_h +#define MapData_h + +#include "JSCell.h" +#include "WeakGCMapInlines.h" +#include <wtf/HashFunctions.h> +#include <wtf/HashMap.h> +#include <wtf/MathExtras.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace JSC { + +class ExecState; +class VM; + +template<typename Entry, typename JSIterator> +class MapDataImpl { +public: + enum : int32_t { + minimumMapSize = 8 + }; + + class IteratorData { + public: + friend class MapDataImpl; + + IteratorData(const MapDataImpl*); + bool next(WTF::KeyValuePair<JSValue, JSValue>&); + + // This function is called while packing a map's backing store. The + // passed-in index is the new index the entry would have after packing. + void didRemoveEntry(int32_t packedIndex) + { + if (isFinished()) + return; + + if (m_index <= packedIndex) + return; + + --m_index; + } + + void didRemoveAllEntries() + { + if (isFinished()) + return; + m_index = 0; + } + + void finish() + { + m_index = -1; + } + + private: + bool ensureSlot() const; + bool isFinished() const { return m_index == -1; } + int32_t refreshCursor() const; + + const MapDataImpl* m_mapData; + mutable int32_t m_index; + }; + + struct KeyType { + ALWAYS_INLINE KeyType() { } + KeyType(JSValue); + JSValue value; + }; + + MapDataImpl(VM&); + + void set(ExecState*, JSCell* owner, KeyType, JSValue); + JSValue get(ExecState*, KeyType); + bool remove(ExecState*, KeyType); + bool contains(ExecState*, KeyType); + size_t size(ExecState*) const { return m_size - m_deletedCount; } + + IteratorData createIteratorData(JSIterator*); + + void clear(); + + void visitChildren(JSCell* owner, SlotVisitor&); + void copyBackingStore(CopyVisitor&, CopyToken); + +private: + typedef WTF::UnsignedWithZeroKeyHashTraits<int32_t> IndexTraits; + + typedef HashMap<JSCell*, int32_t, typename WTF::DefaultHash<JSCell*>::Hash, WTF::HashTraits<JSCell*>, IndexTraits> CellKeyedMap; + typedef HashMap<EncodedJSValue, int32_t, EncodedJSValueHash, EncodedJSValueHashTraits, IndexTraits> ValueKeyedMap; + typedef HashMap<StringImpl*, int32_t, typename WTF::DefaultHash<StringImpl*>::Hash, WTF::HashTraits<StringImpl*>, IndexTraits> StringKeyedMap; + typedef HashMap<SymbolImpl*, int32_t, typename WTF::PtrHash<SymbolImpl*>, WTF::HashTraits<SymbolImpl*>, IndexTraits> SymbolKeyedMap; + + size_t capacityInBytes() { return m_capacity * sizeof(Entry); } + + ALWAYS_INLINE Entry* find(ExecState*, KeyType); + ALWAYS_INLINE Entry* add(ExecState*, JSCell* owner, KeyType); + template <typename Map, typename Key> ALWAYS_INLINE Entry* add(ExecState*, JSCell* owner, Map&, Key, KeyType); + + ALWAYS_INLINE bool shouldPack() const { return m_deletedCount; } + CheckedBoolean ensureSpaceForAppend(ExecState*, JSCell* owner); + + ALWAYS_INLINE void replaceAndPackBackingStore(Entry* destination, int32_t newSize); + ALWAYS_INLINE void replaceBackingStore(Entry* destination, int32_t newSize); + + CellKeyedMap m_cellKeyedTable; + ValueKeyedMap m_valueKeyedTable; + StringKeyedMap m_stringKeyedTable; + SymbolKeyedMap m_symbolKeyedTable; + int32_t m_capacity; + int32_t m_size; + int32_t m_deletedCount; + Entry* m_entries; + WeakGCMap<JSIterator*, JSIterator> m_iterators; +}; + +template<typename Entry, typename JSIterator> +ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::MapDataImpl(VM& vm) + : m_capacity(0) + , m_size(0) + , m_deletedCount(0) + , m_entries(nullptr) + , m_iterators(vm) +{ +} + +template<typename Entry, typename JSIterator> +ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::KeyType::KeyType(JSValue v) +{ + if (!v.isDouble()) { + value = v; + return; + } + double d = v.asDouble(); + if (std::isnan(d)) { + value = v; + return; + } + + int i = static_cast<int>(v.asDouble()); + if (i != d) + value = v; + else + value = jsNumber(i); +} + +template<typename Entry, typename JSIterator> +ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::IteratorData::IteratorData(const MapDataImpl<Entry, JSIterator>* mapData) + : m_mapData(mapData) + , m_index(0) +{ +} + +template<typename Entry, typename JSIterator> +ALWAYS_INLINE bool MapDataImpl<Entry, JSIterator>::IteratorData::next(WTF::KeyValuePair<JSValue, JSValue>& pair) +{ + if (!ensureSlot()) + return false; + Entry* entry = &m_mapData->m_entries[m_index]; + pair = WTF::KeyValuePair<JSValue, JSValue>(entry->key().get(), entry->value().get()); + m_index += 1; + return true; +} + +// This is a bit gnarly. We use an index of -1 to indicate the +// finished state. By casting to unsigned we can immediately +// test if both iterators are at the end of their iteration. +template<typename Entry, typename JSIterator> +ALWAYS_INLINE bool MapDataImpl<Entry, JSIterator>::IteratorData::ensureSlot() const +{ + int32_t index = refreshCursor(); + return static_cast<size_t>(index) < static_cast<size_t>(m_mapData->m_size); +} + +template<typename Entry, typename JSIterator> +ALWAYS_INLINE int32_t MapDataImpl<Entry, JSIterator>::IteratorData::refreshCursor() const +{ + if (isFinished()) + return m_index; + + Entry* entries = m_mapData->m_entries; + size_t end = m_mapData->m_size; + while (static_cast<size_t>(m_index) < end && !entries[m_index].key()) + m_index++; + return m_index; +} + +} + +#endif /* !defined(MapData_h) */ diff --git a/Source/JavaScriptCore/runtime/MapDataInlines.h b/Source/JavaScriptCore/runtime/MapDataInlines.h new file mode 100644 index 000000000..176acc1b3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MapDataInlines.h @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "CopiedAllocator.h" +#include "CopyVisitorInlines.h" +#include "ExceptionHelpers.h" +#include "JSCJSValueInlines.h" +#include "MapData.h" +#include "SlotVisitorInlines.h" + +#include <wtf/CryptographicallyRandomNumber.h> +#include <wtf/MathExtras.h> + + +namespace JSC { + +template<typename Entry, typename JSIterator> +inline void MapDataImpl<Entry, JSIterator>::clear() +{ + m_cellKeyedTable.clear(); + m_valueKeyedTable.clear(); + m_stringKeyedTable.clear(); + m_symbolKeyedTable.clear(); + m_capacity = 0; + m_size = 0; + m_deletedCount = 0; + m_entries = nullptr; + m_iterators.forEach([](JSIterator* iterator, JSIterator*) { + iterator->iteratorData()->didRemoveAllEntries(); + }); +} + +template<typename Entry, typename JSIterator> +inline Entry* MapDataImpl<Entry, JSIterator>::find(ExecState* exec, KeyType key) +{ + if (key.value.isString()) { + auto iter = m_stringKeyedTable.find(asString(key.value)->value(exec).impl()); + if (iter == m_stringKeyedTable.end()) + return 0; + return &m_entries[iter->value]; + } + if (key.value.isSymbol()) { + auto iter = m_symbolKeyedTable.find(asSymbol(key.value)->privateName().uid()); + if (iter == m_symbolKeyedTable.end()) + return 0; + return &m_entries[iter->value]; + } + if (key.value.isCell()) { + auto iter = m_cellKeyedTable.find(key.value.asCell()); + if (iter == m_cellKeyedTable.end()) + return 0; + return &m_entries[iter->value]; + } + + auto iter = m_valueKeyedTable.find(JSValue::encode(key.value)); + if (iter == m_valueKeyedTable.end()) + return 0; + return &m_entries[iter->value]; +} + +template<typename Entry, typename JSIterator> +inline bool MapDataImpl<Entry, JSIterator>::contains(ExecState* exec, KeyType key) +{ + return find(exec, key); +} + +template<typename Entry, typename JSIterator> +template <typename Map, typename Key> +inline Entry* MapDataImpl<Entry, JSIterator>::add(ExecState* exec, JSCell* owner, Map& map, Key key, KeyType keyValue) +{ + typename Map::iterator location = map.find(key); + if (location != map.end()) + return &m_entries[location->value]; + + if (!ensureSpaceForAppend(exec, owner)) + return 0; + + auto result = map.add(key, m_size); + RELEASE_ASSERT(result.isNewEntry); + Entry* entry = &m_entries[m_size++]; + new (entry) Entry(); + entry->setKey(exec->vm(), owner, keyValue.value); + return entry; +} + +template<typename Entry, typename JSIterator> +inline void MapDataImpl<Entry, JSIterator>::set(ExecState* exec, JSCell* owner, KeyType key, JSValue value) +{ + Entry* location = add(exec, owner, key); + if (!location) + return; + location->setValue(exec->vm(), owner, value); +} + +template<typename Entry, typename JSIterator> +inline Entry* MapDataImpl<Entry, JSIterator>::add(ExecState* exec, JSCell* owner, KeyType key) +{ + if (key.value.isString()) + return add(exec, owner, m_stringKeyedTable, asString(key.value)->value(exec).impl(), key); + if (key.value.isSymbol()) + return add(exec, owner, m_symbolKeyedTable, asSymbol(key.value)->privateName().uid(), key); + if (key.value.isCell()) + return add(exec, owner, m_cellKeyedTable, key.value.asCell(), key); + return add(exec, owner, m_valueKeyedTable, JSValue::encode(key.value), key); +} + +template<typename Entry, typename JSIterator> +inline JSValue MapDataImpl<Entry, JSIterator>::get(ExecState* exec, KeyType key) +{ + if (Entry* entry = find(exec, key)) + return entry->value().get(); + return JSValue(); +} + +template<typename Entry, typename JSIterator> +inline bool MapDataImpl<Entry, JSIterator>::remove(ExecState* exec, KeyType key) +{ + int32_t location; + if (key.value.isString()) { + auto iter = m_stringKeyedTable.find(asString(key.value)->value(exec).impl()); + if (iter == m_stringKeyedTable.end()) + return false; + location = iter->value; + m_stringKeyedTable.remove(iter); + } else if (key.value.isSymbol()) { + auto iter = m_symbolKeyedTable.find(asSymbol(key.value)->privateName().uid()); + if (iter == m_symbolKeyedTable.end()) + return false; + location = iter->value; + m_symbolKeyedTable.remove(iter); + } else if (key.value.isCell()) { + auto iter = m_cellKeyedTable.find(key.value.asCell()); + if (iter == m_cellKeyedTable.end()) + return false; + location = iter->value; + m_cellKeyedTable.remove(iter); + } else { + auto iter = m_valueKeyedTable.find(JSValue::encode(key.value)); + if (iter == m_valueKeyedTable.end()) + return false; + location = iter->value; + m_valueKeyedTable.remove(iter); + } + m_entries[location].clear(); + m_deletedCount++; + return true; +} + +template<typename Entry, typename JSIterator> +inline void MapDataImpl<Entry, JSIterator>::replaceAndPackBackingStore(Entry* destination, int32_t newCapacity) +{ + ASSERT(shouldPack()); + int32_t newEnd = 0; + RELEASE_ASSERT(newCapacity > 0); + for (int32_t i = 0; i < m_size; i++) { + Entry& entry = m_entries[i]; + if (!entry.key()) { + m_iterators.forEach([newEnd](JSIterator* iterator, JSIterator*) { + iterator->iteratorData()->didRemoveEntry(newEnd); + }); + continue; + } + ASSERT(newEnd < newCapacity); + destination[newEnd] = entry; + + // We overwrite the old entry with a forwarding index for the new entry, + // so that we can fix up our hash tables below without doing additional + // hash lookups + entry.setKeyWithoutWriteBarrier(jsNumber(newEnd)); + newEnd++; + } + + // Fixup for the hashmaps + for (auto ptr = m_valueKeyedTable.begin(); ptr != m_valueKeyedTable.end(); ++ptr) + ptr->value = m_entries[ptr->value].key().get().asInt32(); + for (auto ptr = m_cellKeyedTable.begin(); ptr != m_cellKeyedTable.end(); ++ptr) + ptr->value = m_entries[ptr->value].key().get().asInt32(); + for (auto ptr = m_stringKeyedTable.begin(); ptr != m_stringKeyedTable.end(); ++ptr) + ptr->value = m_entries[ptr->value].key().get().asInt32(); + for (auto ptr = m_symbolKeyedTable.begin(); ptr != m_symbolKeyedTable.end(); ++ptr) + ptr->value = m_entries[ptr->value].key().get().asInt32(); + + ASSERT((m_size - newEnd) == m_deletedCount); + m_deletedCount = 0; + + m_capacity = newCapacity; + m_size = newEnd; + m_entries = destination; +} + +template<typename Entry, typename JSIterator> +inline void MapDataImpl<Entry, JSIterator>::replaceBackingStore(Entry* destination, int32_t newCapacity) +{ + ASSERT(!shouldPack()); + RELEASE_ASSERT(newCapacity > 0); + ASSERT(newCapacity >= m_capacity); + memcpy(destination, m_entries, sizeof(Entry) * m_size); + m_capacity = newCapacity; + m_entries = destination; +} + +template<typename Entry, typename JSIterator> +inline CheckedBoolean MapDataImpl<Entry, JSIterator>::ensureSpaceForAppend(ExecState* exec, JSCell* owner) +{ + if (m_capacity > m_size) + return true; + + size_t requiredSize = std::max(m_capacity + (m_capacity / 2) + 1, static_cast<int32_t>(minimumMapSize)); + void* newStorage = nullptr; + DeferGC defer(*exec->heap()); + if (!exec->heap()->tryAllocateStorage(owner, requiredSize * sizeof(Entry), &newStorage)) { + throwOutOfMemoryError(exec); + return false; + } + Entry* newEntries = static_cast<Entry*>(newStorage); + if (shouldPack()) + replaceAndPackBackingStore(newEntries, requiredSize); + else + replaceBackingStore(newEntries, requiredSize); + exec->heap()->writeBarrier(owner); + return true; +} + +template<typename Entry, typename JSIterator> +inline void MapDataImpl<Entry, JSIterator>::visitChildren(JSCell* owner, SlotVisitor& visitor) +{ + Entry* entries = m_entries; + if (!entries) + return; + if (m_deletedCount) { + for (int32_t i = 0; i < m_size; i++) { + if (!entries[i].key()) + continue; + entries[i].visitChildren(visitor); + } + } else { + // Guaranteed that all fields of Entry type is WriteBarrier<Unknown>. + visitor.appendValues(reinterpret_cast<WriteBarrier<Unknown>*>(&entries[0]), m_size * (sizeof(Entry) / sizeof(WriteBarrier<Unknown>))); + } + + visitor.copyLater(owner, MapBackingStoreCopyToken, entries, capacityInBytes()); +} + +template<typename Entry, typename JSIterator> +inline void MapDataImpl<Entry, JSIterator>::copyBackingStore(CopyVisitor& visitor, CopyToken token) +{ + if (token == MapBackingStoreCopyToken && visitor.checkIfShouldCopy(m_entries)) { + Entry* oldEntries = m_entries; + Entry* newEntries = static_cast<Entry*>(visitor.allocateNewSpace(capacityInBytes())); + if (shouldPack()) + replaceAndPackBackingStore(newEntries, m_capacity); + else + replaceBackingStore(newEntries, m_capacity); + visitor.didCopy(oldEntries, capacityInBytes()); + } +} + +template<typename Entry, typename JSIterator> +inline auto MapDataImpl<Entry, JSIterator>::createIteratorData(JSIterator* iterator) -> IteratorData +{ + m_iterators.set(iterator, iterator); + return IteratorData(this); +} + +} diff --git a/Source/JavaScriptCore/runtime/MapIteratorPrototype.cpp b/Source/JavaScriptCore/runtime/MapIteratorPrototype.cpp new file mode 100644 index 000000000..5e7ec33f9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MapIteratorPrototype.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "MapIteratorPrototype.h" + +#include "IteratorOperations.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSMapIterator.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo MapIteratorPrototype::s_info = { "Map Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(MapIteratorPrototype) }; + +static EncodedJSValue JSC_HOST_CALL MapIteratorPrototypeFuncNext(ExecState*); + +void MapIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + JSC_NATIVE_FUNCTION(vm.propertyNames->next, MapIteratorPrototypeFuncNext, DontEnum, 0); +} + +EncodedJSValue JSC_HOST_CALL MapIteratorPrototypeFuncNext(CallFrame* callFrame) +{ + JSMapIterator* iterator = jsDynamicCast<JSMapIterator*>(callFrame->thisValue()); + if (!iterator) + return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot call MapIterator.next() on a non-MapIterator object"))); + + JSValue result; + if (iterator->next(callFrame, result)) + return JSValue::encode(createIteratorResultObject(callFrame, result, false)); + iterator->finish(); + return JSValue::encode(createIteratorResultObject(callFrame, jsUndefined(), true)); +} + + +} diff --git a/Source/JavaScriptCore/runtime/MapIteratorPrototype.h b/Source/JavaScriptCore/runtime/MapIteratorPrototype.h new file mode 100644 index 000000000..c56034f8a --- /dev/null +++ b/Source/JavaScriptCore/runtime/MapIteratorPrototype.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef MapIteratorPrototype_h +#define MapIteratorPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class MapIteratorPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static MapIteratorPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + MapIteratorPrototype* prototype = new (NotNull, allocateCell<MapIteratorPrototype>(vm.heap)) MapIteratorPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + MapIteratorPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); +}; + +} + +#endif // !defined(MapIteratorPrototype_h) diff --git a/Source/JavaScriptCore/runtime/MapPrototype.cpp b/Source/JavaScriptCore/runtime/MapPrototype.cpp new file mode 100644 index 000000000..96deae714 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MapPrototype.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "MapPrototype.h" + +#include "CachedCall.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "GetterSetter.h" +#include "JSCJSValueInlines.h" +#include "JSFunctionInlines.h" +#include "JSMap.h" +#include "JSMapIterator.h" +#include "MapDataInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo MapPrototype::s_info = { "Map", &Base::s_info, 0, CREATE_METHOD_TABLE(MapPrototype) }; + +static EncodedJSValue JSC_HOST_CALL mapProtoFuncClear(ExecState*); +static EncodedJSValue JSC_HOST_CALL mapProtoFuncDelete(ExecState*); +static EncodedJSValue JSC_HOST_CALL mapProtoFuncForEach(ExecState*); +static EncodedJSValue JSC_HOST_CALL mapProtoFuncGet(ExecState*); +static EncodedJSValue JSC_HOST_CALL mapProtoFuncHas(ExecState*); +static EncodedJSValue JSC_HOST_CALL mapProtoFuncSet(ExecState*); +static EncodedJSValue JSC_HOST_CALL mapProtoFuncKeys(ExecState*); +static EncodedJSValue JSC_HOST_CALL mapProtoFuncValues(ExecState*); +static EncodedJSValue JSC_HOST_CALL mapProtoFuncEntries(ExecState*); + +static EncodedJSValue JSC_HOST_CALL mapProtoFuncSize(ExecState*); + +void MapPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + JSC_NATIVE_FUNCTION(vm.propertyNames->clear, mapProtoFuncClear, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->deleteKeyword, mapProtoFuncDelete, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->forEach, mapProtoFuncForEach, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->get, mapProtoFuncGet, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->has, mapProtoFuncHas, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->set, mapProtoFuncSet, DontEnum, 2); + JSC_NATIVE_FUNCTION(vm.propertyNames->keys, mapProtoFuncKeys, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->values, mapProtoFuncValues, DontEnum, 0); + + JSFunction* entries = JSFunction::create(vm, globalObject, 0, vm.propertyNames->entries.string(), mapProtoFuncEntries); + putDirectWithoutTransition(vm, vm.propertyNames->entries, entries, DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, entries, DontEnum); + + GetterSetter* accessor = GetterSetter::create(vm, globalObject); + JSFunction* function = JSFunction::create(vm, globalObject, 0, vm.propertyNames->size.string(), mapProtoFuncSize); + accessor->setGetter(vm, globalObject, function); + putDirectNonIndexAccessor(vm, vm.propertyNames->size, accessor, DontEnum | Accessor); +} + +ALWAYS_INLINE static JSMap* getMap(CallFrame* callFrame, JSValue thisValue) +{ + if (!thisValue.isObject()) { + throwVMError(callFrame, createNotAnObjectError(callFrame, thisValue)); + return nullptr; + } + JSMap* map = jsDynamicCast<JSMap*>(thisValue); + if (!map) { + throwTypeError(callFrame, ASCIILiteral("Map operation called on non-Map object")); + return nullptr; + } + return map; +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncClear(CallFrame* callFrame) +{ + JSMap* map = getMap(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + map->clear(callFrame); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncDelete(CallFrame* callFrame) +{ + JSMap* map = getMap(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + return JSValue::encode(jsBoolean(map->remove(callFrame, callFrame->argument(0)))); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncForEach(CallFrame* callFrame) +{ + JSMap* map = getMap(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + JSValue callBack = callFrame->argument(0); + CallData callData; + CallType callType = getCallData(callBack, callData); + if (callType == CallTypeNone) + return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral("Map.prototype.forEach called without callback"))); + JSValue thisValue = callFrame->argument(1); + VM* vm = &callFrame->vm(); + JSMapIterator* iterator = JSMapIterator::create(*vm, callFrame->callee()->globalObject()->mapIteratorStructure(), map, MapIterateKeyValue); + JSValue key, value; + if (callType == CallTypeJS) { + JSFunction* function = jsCast<JSFunction*>(callBack); + CachedCall cachedCall(callFrame, function, 3); + while (iterator->nextKeyValue(key, value) && !vm->exception()) { + cachedCall.setThis(thisValue); + cachedCall.setArgument(0, value); + cachedCall.setArgument(1, key); + cachedCall.setArgument(2, map); + cachedCall.call(); + } + iterator->finish(); + } else { + while (iterator->nextKeyValue(key, value) && !vm->exception()) { + MarkedArgumentBuffer args; + args.append(value); + args.append(key); + args.append(map); + JSC::call(callFrame, callBack, callType, callData, thisValue, args); + } + iterator->finish(); + } + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncGet(CallFrame* callFrame) +{ + JSMap* map = getMap(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + return JSValue::encode(map->get(callFrame, callFrame->argument(0))); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncHas(CallFrame* callFrame) +{ + JSMap* map = getMap(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + return JSValue::encode(jsBoolean(map->has(callFrame, callFrame->argument(0)))); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncSet(CallFrame* callFrame) +{ + JSValue thisValue = callFrame->thisValue(); + JSMap* map = getMap(callFrame, thisValue); + if (!map) + return JSValue::encode(jsUndefined()); + map->set(callFrame, callFrame->argument(0), callFrame->argument(1)); + return JSValue::encode(thisValue); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncSize(CallFrame* callFrame) +{ + JSMap* map = getMap(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + return JSValue::encode(jsNumber(map->size(callFrame))); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncValues(CallFrame* callFrame) +{ + JSMap* thisObj = jsDynamicCast<JSMap*>(callFrame->thisValue()); + if (!thisObj) + return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot create a Map value iterator for a non-Map object."))); + return JSValue::encode(JSMapIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->mapIteratorStructure(), thisObj, MapIterateValue)); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncEntries(CallFrame* callFrame) +{ + JSMap* thisObj = jsDynamicCast<JSMap*>(callFrame->thisValue()); + if (!thisObj) + return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot create a Map entry iterator for a non-Map object."))); + return JSValue::encode(JSMapIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->mapIteratorStructure(), thisObj, MapIterateKeyValue)); +} + +EncodedJSValue JSC_HOST_CALL mapProtoFuncKeys(CallFrame* callFrame) +{ + JSMap* thisObj = jsDynamicCast<JSMap*>(callFrame->thisValue()); + if (!thisObj) + return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot create a Map key iterator for a non-Map object."))); + return JSValue::encode(JSMapIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->mapIteratorStructure(), thisObj, MapIterateKey)); +} + +} diff --git a/Source/JavaScriptCore/runtime/MapPrototype.h b/Source/JavaScriptCore/runtime/MapPrototype.h new file mode 100644 index 000000000..eaa582537 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MapPrototype.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef MapPrototype_h +#define MapPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class MapPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static MapPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + MapPrototype* prototype = new (NotNull, allocateCell<MapPrototype>(vm.heap)) MapPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + MapPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); +}; + +} + +#endif // !defined(MapPrototype_h) diff --git a/Source/JavaScriptCore/runtime/MatchResult.h b/Source/JavaScriptCore/runtime/MatchResult.h new file mode 100644 index 000000000..d87c8516b --- /dev/null +++ b/Source/JavaScriptCore/runtime/MatchResult.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef MatchResult_h +#define MatchResult_h + +typedef uint64_t EncodedMatchResult; + +struct MatchResult { + ALWAYS_INLINE MatchResult(size_t start, size_t end) + : start(start) + , end(end) + { + } + + explicit ALWAYS_INLINE MatchResult(EncodedMatchResult encoded) + { + union u { + uint64_t encoded; + struct s { + size_t start; + size_t end; + } split; + } value; + value.encoded = encoded; + start = value.split.start; + end = value.split.end; + } + + ALWAYS_INLINE static MatchResult failed() + { + return MatchResult(WTF::notFound, 0); + } + + ALWAYS_INLINE operator bool() + { + return start != WTF::notFound; + } + + ALWAYS_INLINE bool empty() + { + return start == end; + } + + size_t start; + size_t end; +}; + +#endif diff --git a/Source/JavaScriptCore/runtime/MathCommon.cpp b/Source/JavaScriptCore/runtime/MathCommon.cpp new file mode 100644 index 000000000..d6882c03e --- /dev/null +++ b/Source/JavaScriptCore/runtime/MathCommon.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "MathCommon.h" + +#include <cmath> +#include "PureNaN.h" + +namespace JSC { + +#if PLATFORM(IOS) && CPU(ARM_THUMB2) + +// The following code is taken from netlib.org: +// http://www.netlib.org/fdlibm/fdlibm.h +// http://www.netlib.org/fdlibm/e_pow.c +// http://www.netlib.org/fdlibm/s_scalbn.c +// +// And was originally distributed under the following license: + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_pow(x,y) return x**y + * + * n + * Method: Let x = 2 * (1+f) + * 1. Compute and return log2(x) in two pieces: + * log2(x) = w1 + w2, + * where w1 has 53-24 = 29 bit trailing zeros. + * 2. Perform y*log2(x) = n+y' by simulating muti-precision + * arithmetic, where |y'|<=0.5. + * 3. Return x**y = 2**n*exp(y'*log2) + * + * Special cases: + * 1. (anything) ** 0 is 1 + * 2. (anything) ** 1 is itself + * 3. (anything) ** NAN is NAN + * 4. NAN ** (anything except 0) is NAN + * 5. +-(|x| > 1) ** +INF is +INF + * 6. +-(|x| > 1) ** -INF is +0 + * 7. +-(|x| < 1) ** +INF is +0 + * 8. +-(|x| < 1) ** -INF is +INF + * 9. +-1 ** +-INF is NAN + * 10. +0 ** (+anything except 0, NAN) is +0 + * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 + * 12. +0 ** (-anything except 0, NAN) is +INF + * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF + * 14. -0 ** (odd integer) = -( +0 ** (odd integer) ) + * 15. +INF ** (+anything except 0,NAN) is +INF + * 16. +INF ** (-anything except 0,NAN) is +0 + * 17. -INF ** (anything) = -0 ** (-anything) + * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) + * 19. (-anything except 0 and inf) ** (non-integer) is NAN + * + * Accuracy: + * pow(x,y) returns x**y nearly rounded. In particular + * pow(integer,integer) + * always returns the correct integer provided it is + * representable. + * + * Constants : + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#define __HI(x) *(1+(int*)&x) +#define __LO(x) *(int*)&x + +static const double +bp[] = {1.0, 1.5,}, +dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */ +dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */ +zero = 0.0, +one = 1.0, +two = 2.0, +two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ +huge = 1.0e300, +tiny = 1.0e-300, +/* for scalbn */ +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ +/* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ +L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ +L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ +L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ +L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ +L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ +L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ +P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ +P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ +P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ +P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ +P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */ +lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ +lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ +ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */ +cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ +cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ +cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ +ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ +ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ +ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ + +inline double fdlibmScalbn (double x, int n) +{ + int k,hx,lx; + hx = __HI(x); + lx = __LO(x); + k = (hx&0x7ff00000)>>20; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ + x *= two54; + hx = __HI(x); + k = ((hx&0x7ff00000)>>20) - 54; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0x7ff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0x7fe) return huge*copysign(huge,x); /* overflow */ + if (k > 0) /* normal result */ + {__HI(x) = (hx&0x800fffff)|(k<<20); return x;} + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge*copysign(huge,x); /*overflow*/ + else return tiny*copysign(tiny,x); /*underflow*/ + } + k += 54; /* subnormal result */ + __HI(x) = (hx&0x800fffff)|(k<<20); + return x*twom54; +} + +static double fdlibmPow(double x, double y) +{ + double z,ax,z_h,z_l,p_h,p_l; + double y1,t1,t2,r,s,t,u,v,w; + int i0,i1,i,j,k,yisint,n; + int hx,hy,ix,iy; + unsigned lx,ly; + + i0 = ((*(int*)&one)>>29)^1; i1=1-i0; + hx = __HI(x); lx = __LO(x); + hy = __HI(y); ly = __LO(y); + ix = hx&0x7fffffff; iy = hy&0x7fffffff; + + /* y==zero: x**0 = 1 */ + if((iy|ly)==0) return one; + + /* +-NaN return x+y */ + if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) || + iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0))) + return x+y; + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if(hx<0) { + if(iy>=0x43400000) yisint = 2; /* even integer y */ + else if(iy>=0x3ff00000) { + k = (iy>>20)-0x3ff; /* exponent */ + if(k>20) { + j = ly>>(52-k); + if(static_cast<unsigned>(j<<(52-k))==ly) yisint = 2-(j&1); + } else if(ly==0) { + j = iy>>(20-k); + if((j<<(20-k))==iy) yisint = 2-(j&1); + } + } + } + + /* special value of y */ + if(ly==0) { + if (iy==0x7ff00000) { /* y is +-inf */ + if(((ix-0x3ff00000)|lx)==0) + return y - y; /* inf**+-1 is NaN */ + else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */ + return (hy>=0)? y: zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy<0)?-y: zero; + } + if(iy==0x3ff00000) { /* y is +-1 */ + if(hy<0) return one/x; else return x; + } + if(hy==0x40000000) return x*x; /* y is 2 */ + if(hy==0x3fe00000) { /* y is 0.5 */ + if(hx>=0) /* x >= +0 */ + return sqrt(x); + } + } + + ax = fabs(x); + /* special value of x */ + if(lx==0) { + if(ix==0x7ff00000||ix==0||ix==0x3ff00000){ + z = ax; /*x is +-0,+-inf,+-1*/ + if(hy<0) z = one/z; /* z = (1/|x|) */ + if(hx<0) { + if(((ix-0x3ff00000)|yisint)==0) { + z = (z-z)/(z-z); /* (-1)**non-int is NaN */ + } else if(yisint==1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + } + + n = (hx>>31)+1; + + /* (x<0)**(non-int) is NaN */ + if((n|yisint)==0) return (x-x)/(x-x); + + s = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if((n|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */ + + /* |y| is huge */ + if(iy>0x41e00000) { /* if |y| > 2**31 */ + if(iy>0x43f00000){ /* if |y| > 2**64, must o/uflow */ + if(ix<=0x3fefffff) return (hy<0)? huge*huge:tiny*tiny; + if(ix>=0x3ff00000) return (hy>0)? huge*huge:tiny*tiny; + } + /* over/underflow if x is not close to one */ + if(ix<0x3fefffff) return (hy<0)? s*huge*huge:s*tiny*tiny; + if(ix>0x3ff00000) return (hy>0)? s*huge*huge:s*tiny*tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = ax-one; /* t has 20 trailing zeros */ + w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25)); + u = ivln2_h*t; /* ivln2_h has 21 sig. bits */ + v = t*ivln2_l-w*ivln2; + t1 = u+v; + __LO(t1) = 0; + t2 = v-(t1-u); + } else { + double ss,s2,s_h,s_l,t_h,t_l; + n = 0; + /* take care subnormal number */ + if(ix<0x00100000) + {ax *= two53; n -= 53; ix = __HI(ax); } + n += ((ix)>>20)-0x3ff; + j = ix&0x000fffff; + /* determine interval */ + ix = j|0x3ff00000; /* normalize ix */ + if(j<=0x3988E) k=0; /* |x|<sqrt(3/2) */ + else if(j<0xBB67A) k=1; /* |x|<sqrt(3) */ + else {k=0;n+=1;ix -= 0x00100000;} + __HI(ax) = ix; + + /* compute ss = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */ + u = ax-bp[k]; /* bp[0]=1.0, bp[1]=1.5 */ + v = one/(ax+bp[k]); + ss = u*v; + s_h = ss; + __LO(s_h) = 0; + /* t_h=ax+bp[k] High */ + t_h = zero; + __HI(t_h)=((ix>>1)|0x20000000)+0x00080000+(k<<18); + t_l = ax - (t_h-bp[k]); + s_l = v*((u-s_h*t_h)-s_h*t_l); + /* compute log(ax) */ + s2 = ss*ss; + r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6))))); + r += s_l*(s_h+ss); + s2 = s_h*s_h; + t_h = 3.0+s2+r; + __LO(t_h) = 0; + t_l = r-((t_h-3.0)-s2); + /* u+v = ss*(1+...) */ + u = s_h*t_h; + v = s_l*t_h+t_l*ss; + /* 2/(3log2)*(ss+...) */ + p_h = u+v; + __LO(p_h) = 0; + p_l = v-(p_h-u); + z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l*p_h+p_l*cp+dp_l[k]; + /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (double)n; + t1 = (((z_h+z_l)+dp_h[k])+t); + __LO(t1) = 0; + t2 = z_l-(((t1-t)-dp_h[k])-z_h); + } + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + y1 = y; + __LO(y1) = 0; + p_l = (y-y1)*t1+y*t2; + p_h = y1*t1; + z = p_l+p_h; + j = __HI(z); + i = __LO(z); + if (j>=0x40900000) { /* z >= 1024 */ + if(((j-0x40900000)|i)!=0) /* if z > 1024 */ + return s*huge*huge; /* overflow */ + else { + if(p_l+ovt>z-p_h) return s*huge*huge; /* overflow */ + } + } else if((j&0x7fffffff)>=0x4090cc00 ) { /* z <= -1075 */ + if(((j-0xc090cc00)|i)!=0) /* z < -1075 */ + return s*tiny*tiny; /* underflow */ + else { + if(p_l<=z-p_h) return s*tiny*tiny; /* underflow */ + } + } + /* + * compute 2**(p_h+p_l) + */ + i = j&0x7fffffff; + k = (i>>20)-0x3ff; + n = 0; + if(i>0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j+(0x00100000>>(k+1)); + k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */ + t = zero; + __HI(t) = (n&~(0x000fffff>>k)); + n = ((n&0x000fffff)|0x00100000)>>(20-k); + if(j<0) n = -n; + p_h -= t; + } + t = p_l+p_h; + __LO(t) = 0; + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + z = u+v; + w = v-(z-u); + t = z*z; + t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + r = (z*t1)/(t1-two)-(w+z*w); + z = one-(r-z); + j = __HI(z); + j += (n<<20); + if((j>>20)<=0) z = fdlibmScalbn(z,n); /* subnormal output */ + else __HI(z) += (n<<20); + return s*z; +} + +static ALWAYS_INLINE bool isDenormal(double x) +{ + static const uint64_t signbit = 0x8000000000000000ULL; + static const uint64_t minNormal = 0x0001000000000000ULL; + return (bitwise_cast<uint64_t>(x) & ~signbit) - 1 < minNormal - 1; +} + +static ALWAYS_INLINE bool isEdgeCase(double x) +{ + static const uint64_t signbit = 0x8000000000000000ULL; + static const uint64_t infinity = 0x7fffffffffffffffULL; + return (bitwise_cast<uint64_t>(x) & ~signbit) - 1 >= infinity - 1; +} + +static ALWAYS_INLINE double mathPowInternal(double x, double y) +{ + if (!isDenormal(x) && !isDenormal(y)) { + double libmResult = std::pow(x, y); + if (libmResult || isEdgeCase(x) || isEdgeCase(y)) + return libmResult; + } + return fdlibmPow(x, y); +} + +#else + +ALWAYS_INLINE double mathPowInternal(double x, double y) +{ + return pow(x, y); +} + +#endif + +double JIT_OPERATION operationMathPow(double x, double y) +{ + if (std::isnan(y)) + return PNaN; + if (std::isinf(y) && fabs(x) == 1) + return PNaN; + int32_t yAsInt = y; + if (static_cast<double>(yAsInt) != y || yAsInt < 0) + return mathPowInternal(x, y); + + // If the exponent is a positive int32 integer, we do a fast exponentiation + double result = 1; + while (yAsInt) { + if (yAsInt & 1) + result *= x; + x *= x; + yAsInt >>= 1; + } + return result; +} + +extern "C" { +double jsRound(double value) +{ + double integer = ceil(value); + return integer - (integer - value > 0.5); +} +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/MathCommon.h b/Source/JavaScriptCore/runtime/MathCommon.h new file mode 100644 index 000000000..2f84ed727 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MathCommon.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef MathCommon_h +#define MathCommon_h + +#include "JITOperations.h" + +#ifndef JIT_OPERATION +#define JIT_OPERATION +#endif + +namespace JSC { +double JIT_OPERATION operationMathPow(double x, double y) WTF_INTERNAL; + +inline int clz32(uint32_t number) +{ +#if COMPILER(GCC_OR_CLANG) + int zeroCount = 32; + if (number) + zeroCount = __builtin_clz(number); + return zeroCount; +#else + int zeroCount = 0; + for (int i = 31; i >= 0; i--) { + if (!(number >> i)) + zeroCount++; + else + break; + } + return zeroCount; +#endif +} + +extern "C" { +double JIT_OPERATION jsRound(double value) REFERENCED_FROM_ASM WTF_INTERNAL; +} + +} + +#endif // MathCommon_h diff --git a/Source/JavaScriptCore/runtime/MathObject.cpp b/Source/JavaScriptCore/runtime/MathObject.cpp new file mode 100644 index 000000000..d23e46b6b --- /dev/null +++ b/Source/JavaScriptCore/runtime/MathObject.cpp @@ -0,0 +1,376 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2013 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "MathObject.h" + +#include "Lookup.h" +#include "MathCommon.h" +#include "ObjectPrototype.h" +#include "JSCInlines.h" +#include <time.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/RandomNumber.h> +#include <wtf/RandomNumberSeed.h> +#include <wtf/Vector.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(MathObject); + +EncodedJSValue JSC_HOST_CALL mathProtoFuncACos(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncACosh(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncASin(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncASinh(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncATan(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncATanh(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncCbrt(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncClz32(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncCos(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncCosh(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncExp(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncExpm1(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncFround(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncHypot(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog1p(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog10(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog2(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncMax(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncMin(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncPow(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncRound(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncSign(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncSin(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncSinh(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncTan(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncTanh(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncTrunc(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncIMul(ExecState*); + +} + +namespace JSC { + +const ClassInfo MathObject::s_info = { "Math", &Base::s_info, 0, CREATE_METHOD_TABLE(MathObject) }; + +MathObject::MathObject(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} + +void MathObject::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "E"), jsNumber(exp(1.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "LN2"), jsNumber(log(2.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "LN10"), jsNumber(log(10.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "LOG2E"), jsNumber(1.0 / log(2.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "LOG10E"), jsNumber(0.4342944819032518), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "PI"), jsNumber(piDouble), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "SQRT1_2"), jsNumber(sqrt(0.5)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "SQRT2"), jsNumber(sqrt(2.0)), DontDelete | DontEnum | ReadOnly); + + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "abs"), 1, mathProtoFuncAbs, AbsIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "acos"), 1, mathProtoFuncACos, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "asin"), 1, mathProtoFuncASin, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "atan"), 1, mathProtoFuncATan, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "acosh"), 1, mathProtoFuncACosh, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "asinh"), 1, mathProtoFuncASinh, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "atanh"), 1, mathProtoFuncATanh, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "atan2"), 2, mathProtoFuncATan2, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "cbrt"), 1, mathProtoFuncCbrt, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "ceil"), 1, mathProtoFuncCeil, CeilIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "clz32"), 1, mathProtoFuncClz32, Clz32Intrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "cos"), 1, mathProtoFuncCos, CosIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "cosh"), 1, mathProtoFuncCosh, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "exp"), 1, mathProtoFuncExp, ExpIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "expm1"), 1, mathProtoFuncExpm1, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "floor"), 1, mathProtoFuncFloor, FloorIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "fround"), 1, mathProtoFuncFround, FRoundIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "hypot"), 2, mathProtoFuncHypot, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "log"), 1, mathProtoFuncLog, LogIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "log10"), 1, mathProtoFuncLog10, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "log1p"), 1, mathProtoFuncLog1p, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "log2"), 1, mathProtoFuncLog2, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "max"), 2, mathProtoFuncMax, MaxIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "min"), 2, mathProtoFuncMin, MinIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "pow"), 2, mathProtoFuncPow, PowIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "random"), 0, mathProtoFuncRandom, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "round"), 1, mathProtoFuncRound, RoundIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sign"), 1, mathProtoFuncSign, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sin"), 1, mathProtoFuncSin, SinIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sinh"), 1, mathProtoFuncSinh, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sqrt"), 1, mathProtoFuncSqrt, SqrtIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "tan"), 1, mathProtoFuncTan, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "tanh"), 1, mathProtoFuncTanh, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "trunc"), 1, mathProtoFuncTrunc, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "imul"), 2, mathProtoFuncIMul, IMulIntrinsic, DontEnum | Function); +} + +// ------------------------------ Functions -------------------------------- + +EncodedJSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState* exec) +{ + return JSValue::encode(jsNumber(fabs(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncACos(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(acos(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncASin(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(asin(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncATan(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(atan(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState* exec) +{ + double arg0 = exec->argument(0).toNumber(exec); + double arg1 = exec->argument(1).toNumber(exec); + return JSValue::encode(jsDoubleNumber(atan2(arg0, arg1))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState* exec) +{ + return JSValue::encode(jsNumber(ceil(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncClz32(ExecState* exec) +{ + uint32_t value = exec->argument(0).toUInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + return JSValue::encode(JSValue(clz32(value))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncCos(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(cos(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncExp(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(exp(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState* exec) +{ + return JSValue::encode(jsNumber(floor(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncHypot(ExecState* exec) +{ + unsigned argsCount = exec->argumentCount(); + double max = 0; + Vector<double, 8> args; + args.reserveInitialCapacity(argsCount); + for (unsigned i = 0; i < argsCount; ++i) { + args.uncheckedAppend(exec->uncheckedArgument(i).toNumber(exec)); + if (exec->hadException()) + return JSValue::encode(jsNull()); + if (std::isinf(args[i])) + return JSValue::encode(jsDoubleNumber(+std::numeric_limits<double>::infinity())); + max = std::max(fabs(args[i]), max); + } + if (!max) + max = 1; + // Kahan summation algorithm significantly reduces the numerical error in the total obtained. + double sum = 0; + double compensation = 0; + for (double argument : args) { + double scaledArgument = argument / max; + double summand = scaledArgument * scaledArgument - compensation; + double preliminary = sum + summand; + compensation = (preliminary - sum) - summand; + sum = preliminary; + } + return JSValue::encode(jsDoubleNumber(sqrt(sum) * max)); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(log(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncMax(ExecState* exec) +{ + unsigned argsCount = exec->argumentCount(); + double result = -std::numeric_limits<double>::infinity(); + for (unsigned k = 0; k < argsCount; ++k) { + double val = exec->uncheckedArgument(k).toNumber(exec); + if (std::isnan(val)) { + result = PNaN; + } else if (val > result || (!val && !result && !std::signbit(val))) + result = val; + } + return JSValue::encode(jsNumber(result)); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncMin(ExecState* exec) +{ + unsigned argsCount = exec->argumentCount(); + double result = +std::numeric_limits<double>::infinity(); + for (unsigned k = 0; k < argsCount; ++k) { + double val = exec->uncheckedArgument(k).toNumber(exec); + if (std::isnan(val)) { + result = PNaN; + } else if (val < result || (!val && !result && std::signbit(val))) + result = val; + } + return JSValue::encode(jsNumber(result)); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncPow(ExecState* exec) +{ + // ECMA 15.8.2.1.13 + + double arg = exec->argument(0).toNumber(exec); + double arg2 = exec->argument(1).toNumber(exec); + + return JSValue::encode(JSValue(operationMathPow(arg, arg2))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(exec->lexicalGlobalObject()->weakRandomNumber())); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncRound(ExecState* exec) +{ + return JSValue::encode(jsNumber(jsRound(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncSign(ExecState* exec) +{ + double arg = exec->argument(0).toNumber(exec); + if (std::isnan(arg)) + return JSValue::encode(jsNaN()); + if (!arg) + return JSValue::encode(std::signbit(arg) ? jsNumber(-0.0) : jsNumber(0)); + return JSValue::encode(jsNumber(std::signbit(arg) ? -1 : 1)); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncSin(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(sin(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(sqrt(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncTan(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(tan(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncIMul(ExecState* exec) +{ + int32_t left = exec->argument(0).toInt32(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + int32_t right = exec->argument(1).toInt32(exec); + return JSValue::encode(jsNumber(left * right)); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncACosh(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(acosh(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncASinh(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(asinh(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncATanh(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(atanh(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncCbrt(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(cbrt(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncCosh(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(cosh(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncExpm1(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(expm1(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncFround(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(static_cast<float>(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog1p(ExecState* exec) +{ + double value = exec->argument(0).toNumber(exec); + if (value == 0) + return JSValue::encode(jsDoubleNumber(value)); + return JSValue::encode(jsDoubleNumber(log1p(value))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog10(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(log10(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog2(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(log2(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncSinh(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(sinh(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncTanh(ExecState* exec) +{ + return JSValue::encode(jsDoubleNumber(tanh(exec->argument(0).toNumber(exec)))); +} + +EncodedJSValue JSC_HOST_CALL mathProtoFuncTrunc(ExecState*exec) +{ + return JSValue::encode(jsNumber(exec->argument(0).toIntegerPreserveNaN(exec))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/MathObject.h b/Source/JavaScriptCore/runtime/MathObject.h new file mode 100644 index 000000000..9d1c181c0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MathObject.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef MathObject_h +#define MathObject_h + +#include "JSObject.h" + +namespace JSC { + +class MathObject : public JSNonFinalObject { +private: + MathObject(VM&, Structure*); + +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static MathObject* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + MathObject* object = new (NotNull, allocateCell<MathObject>(vm.heap)) MathObject(vm, structure); + object->finishCreation(vm, globalObject); + return object; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, JSGlobalObject*); +}; + +EncodedJSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState*); +EncodedJSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState*); + +} // namespace JSC + +#endif // MathObject_h diff --git a/Source/JavaScriptCore/runtime/MemoryStatistics.cpp b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp new file mode 100644 index 000000000..a2eaf349c --- /dev/null +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "MemoryStatistics.h" + +#include "ExecutableAllocator.h" +#include "VM.h" +#include "JSStack.h" + +namespace JSC { + +GlobalMemoryStatistics globalMemoryStatistics() +{ + GlobalMemoryStatistics stats; + + stats.stackBytes = JSStack::committedByteCount(); +#if ENABLE(EXECUTABLE_ALLOCATOR_FIXED) || (PLATFORM(EFL) && ENABLE(JIT)) + stats.JITBytes = ExecutableAllocator::committedByteCount(); +#else + stats.JITBytes = 0; +#endif + return stats; +} + +} + + diff --git a/Source/JavaScriptCore/runtime/MemoryStatistics.h b/Source/JavaScriptCore/runtime/MemoryStatistics.h new file mode 100644 index 000000000..79cec9c21 --- /dev/null +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef MemoryStatistics_h +#define MemoryStatistics_h + +#include "Heap.h" + +class VM; + +namespace JSC { + +struct GlobalMemoryStatistics { + size_t stackBytes; + size_t JITBytes; +}; + +JS_EXPORT_PRIVATE GlobalMemoryStatistics globalMemoryStatistics(); + +} + +#endif // MemoryStatistics_h + diff --git a/Source/JavaScriptCore/runtime/Microtask.h b/Source/JavaScriptCore/runtime/Microtask.h new file mode 100644 index 000000000..f996a7c75 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Microtask.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef Microtask_h +#define Microtask_h + +#include <wtf/RefCounted.h> + +namespace JSC { + +class ExecState; + +class Microtask : public RefCounted<Microtask> { +public: + virtual ~Microtask() + { + } + + virtual void run(ExecState*) = 0; +}; + +} // namespace JSC + +#endif // Microtask_h diff --git a/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp b/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp new file mode 100644 index 000000000..3fb58a328 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "NativeErrorConstructor.h" + +#include "ErrorInstance.h" +#include "JSFunction.h" +#include "JSString.h" +#include "NativeErrorPrototype.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(NativeErrorConstructor); + +const ClassInfo NativeErrorConstructor::s_info = { "Function", &InternalFunction::s_info, 0, CREATE_METHOD_TABLE(NativeErrorConstructor) }; + +NativeErrorConstructor::NativeErrorConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void NativeErrorConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, Structure* prototypeStructure, const String& name) +{ + Base::finishCreation(vm, name); + ASSERT(inherits(info())); + + NativeErrorPrototype* prototype = NativeErrorPrototype::create(vm, globalObject, prototypeStructure, name, this); + + putDirect(vm, vm.propertyNames->length, jsNumber(1), DontDelete | ReadOnly | DontEnum); // ECMA 15.11.7.5 + putDirect(vm, vm.propertyNames->prototype, prototype, DontDelete | ReadOnly | DontEnum); + m_errorStructure.set(vm, this, ErrorInstance::createStructure(vm, globalObject, prototype)); + ASSERT(m_errorStructure); + ASSERT(m_errorStructure->isObject()); +} + +void NativeErrorConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + NativeErrorConstructor* thisObject = jsCast<NativeErrorConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_errorStructure); +} + +EncodedJSValue JSC_HOST_CALL Interpreter::constructWithNativeErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argument(0); + Structure* errorStructure = static_cast<NativeErrorConstructor*>(exec->callee())->errorStructure(); + ASSERT(errorStructure); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message, nullptr, TypeNothing, false)); +} + +ConstructType NativeErrorConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = Interpreter::constructWithNativeErrorConstructor; + return ConstructTypeHost; +} + +EncodedJSValue JSC_HOST_CALL Interpreter::callNativeErrorConstructor(ExecState* exec) +{ + JSValue message = exec->argument(0); + Structure* errorStructure = static_cast<NativeErrorConstructor*>(exec->callee())->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message, nullptr, TypeNothing, false)); +} + +CallType NativeErrorConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = Interpreter::callNativeErrorConstructor; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NativeErrorConstructor.h b/Source/JavaScriptCore/runtime/NativeErrorConstructor.h new file mode 100644 index 000000000..b582ef392 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorConstructor.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NativeErrorConstructor_h +#define NativeErrorConstructor_h + +#include "InternalFunction.h" +#include "NativeErrorPrototype.h" + +namespace JSC { + +class ErrorInstance; +class FunctionPrototype; +class NativeErrorPrototype; + +class NativeErrorConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + + static NativeErrorConstructor* create(VM& vm, JSGlobalObject* globalObject, Structure* structure, Structure* prototypeStructure, const String& name) + { + NativeErrorConstructor* constructor = new (NotNull, allocateCell<NativeErrorConstructor>(vm.heap)) NativeErrorConstructor(vm, structure); + constructor->finishCreation(vm, globalObject, prototypeStructure, name); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + Structure* errorStructure() { return m_errorStructure.get(); } + +protected: + void finishCreation(VM&, JSGlobalObject*, Structure* prototypeStructure, const String& name); + +private: + NativeErrorConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<Structure> m_errorStructure; +}; + +} // namespace JSC + +#endif // NativeErrorConstructor_h diff --git a/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp b/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp new file mode 100644 index 000000000..967ac5f8f --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "NativeErrorPrototype.h" + +#include "JSGlobalObject.h" +#include "JSString.h" +#include "NativeErrorConstructor.h" +#include "JSCInlines.h" + +namespace JSC { + +NativeErrorPrototype::NativeErrorPrototype(VM& vm, Structure* structure) + : ErrorPrototype(vm, structure) +{ +} + +void NativeErrorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, const WTF::String& nameAndMessage, NativeErrorConstructor* constructor) +{ + Base::finishCreation(vm, globalObject); + putDirect(vm, vm.propertyNames->name, jsString(&vm, nameAndMessage), DontEnum); + putDirect(vm, vm.propertyNames->message, jsEmptyString(&vm), DontEnum); + putDirect(vm, vm.propertyNames->constructor, constructor, DontEnum); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NativeErrorPrototype.h b/Source/JavaScriptCore/runtime/NativeErrorPrototype.h new file mode 100644 index 000000000..5812e6363 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NativeErrorPrototype.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NativeErrorPrototype_h +#define NativeErrorPrototype_h + +#include "ErrorPrototype.h" + +namespace JSC { + +class NativeErrorConstructor; + +class NativeErrorPrototype : public ErrorPrototype { +private: + NativeErrorPrototype(VM&, Structure*); + +public: + typedef ErrorPrototype Base; + + static NativeErrorPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure, const String& name, NativeErrorConstructor* constructor) + { + NativeErrorPrototype* prototype = new (NotNull, allocateCell<NativeErrorPrototype>(vm.heap)) NativeErrorPrototype(vm, structure); + prototype->finishCreation(vm, globalObject, name, constructor); + return prototype; + } + +protected: + void finishCreation(VM&, JSGlobalObject*, const String& nameAndMessage, NativeErrorConstructor*); +}; + +} // namespace JSC + +#endif // NativeErrorPrototype_h diff --git a/Source/JavaScriptCore/runtime/NullGetterFunction.cpp b/Source/JavaScriptCore/runtime/NullGetterFunction.cpp new file mode 100644 index 000000000..4243e5c41 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NullGetterFunction.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "NullGetterFunction.h" + +#include "JSCJSValueInlines.h" +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo NullGetterFunction::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(NullGetterFunction) }; + +static EncodedJSValue JSC_HOST_CALL callReturnUndefined(ExecState*) +{ + return JSValue::encode(jsUndefined()); +} + +CallType NullGetterFunction::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callReturnUndefined; + return CallTypeHost; +} + +ConstructType NullGetterFunction::getConstructData(JSCell*, ConstructData&) +{ + return ConstructTypeNone; +} + +} diff --git a/Source/JavaScriptCore/runtime/NullGetterFunction.h b/Source/JavaScriptCore/runtime/NullGetterFunction.h new file mode 100644 index 000000000..d95feb731 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NullGetterFunction.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef NullGetterFunction_h +#define NullGetterFunction_h + +#include "InternalFunction.h" + +namespace JSC { + +class NullGetterFunction : public InternalFunction { +public: + typedef InternalFunction Base; + + static NullGetterFunction* create(VM& vm, Structure* structure) + { + NullGetterFunction* function = new (NotNull, allocateCell< NullGetterFunction>(vm.heap)) NullGetterFunction(vm, structure); + function->finishCreation(vm, String()); + return function; + } + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + NullGetterFunction(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} + +#endif // NullGetterFunction_h diff --git a/Source/JavaScriptCore/runtime/NullSetterFunction.cpp b/Source/JavaScriptCore/runtime/NullSetterFunction.cpp new file mode 100644 index 000000000..118fb1520 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NullSetterFunction.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "NullSetterFunction.h" + +#include "Error.h" +#include "JSCInlines.h" +#include "JSCJSValueInlines.h" +#include "StackVisitor.h" + +namespace JSC { + +const ClassInfo NullSetterFunction::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(NullSetterFunction) }; + + +class GetCallerStrictnessFunctor { +public: + GetCallerStrictnessFunctor() + : m_iterations(0) + , m_callerIsStrict(false) + { + } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + ++m_iterations; + if (m_iterations < 2) + return StackVisitor::Continue; + + CodeBlock* codeBlock = visitor->codeBlock(); + m_callerIsStrict = codeBlock && codeBlock->isStrictMode(); + return StackVisitor::Done; + } + + bool callerIsStrict() const { return m_callerIsStrict; } + +private: + int m_iterations; + bool m_callerIsStrict; +}; + +static bool callerIsStrict(ExecState* exec) +{ + GetCallerStrictnessFunctor iter; + exec->iterate(iter); + return iter.callerIsStrict(); +} + +static EncodedJSValue JSC_HOST_CALL callReturnUndefined(ExecState* exec) +{ + if (callerIsStrict(exec)) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Setting a property that has only a getter"))); + return JSValue::encode(jsUndefined()); +} + +CallType NullSetterFunction::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callReturnUndefined; + return CallTypeHost; +} + +ConstructType NullSetterFunction::getConstructData(JSCell*, ConstructData&) +{ + return ConstructTypeNone; +} + +} diff --git a/Source/JavaScriptCore/runtime/NullSetterFunction.h b/Source/JavaScriptCore/runtime/NullSetterFunction.h new file mode 100644 index 000000000..ccdc6f1dd --- /dev/null +++ b/Source/JavaScriptCore/runtime/NullSetterFunction.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef NullSetterFunction_h +#define NullSetterFunction_h + +#include "InternalFunction.h" + +namespace JSC { + +class NullSetterFunction : public InternalFunction { +public: + typedef InternalFunction Base; + + static NullSetterFunction* create(VM& vm, Structure* structure) + { + NullSetterFunction* function = new (NotNull, allocateCell< NullSetterFunction>(vm.heap)) NullSetterFunction(vm, structure); + function->finishCreation(vm, String()); + return function; + } + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + NullSetterFunction(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} + +#endif // NullSetterFunction_h diff --git a/Source/JavaScriptCore/runtime/NumberConstructor.cpp b/Source/JavaScriptCore/runtime/NumberConstructor.cpp new file mode 100644 index 000000000..0a21512d3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberConstructor.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "NumberConstructor.h" + +#include "Lookup.h" +#include "NumberObject.h" +#include "NumberPrototype.h" +#include "JSCInlines.h" +#include "JSGlobalObjectFunctions.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsFinite(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsInteger(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsNaN(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsSafeInteger(ExecState*); + +} // namespace JSC + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(NumberConstructor); + +const ClassInfo NumberConstructor::s_info = { "Function", &InternalFunction::s_info, 0, CREATE_METHOD_TABLE(NumberConstructor) }; + +NumberConstructor::NumberConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void NumberConstructor::finishCreation(VM& vm, NumberPrototype* numberPrototype) +{ + Base::finishCreation(vm, NumberPrototype::info()->className); + ASSERT(inherits(info())); + + // Number.Prototype + putDirectWithoutTransition(vm, vm.propertyNames->prototype, numberPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); + + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "EPSILON"), jsDoubleNumber(std::numeric_limits<double>::epsilon()), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MAX_VALUE"), jsDoubleNumber(1.7976931348623157E+308), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MIN_VALUE"), jsDoubleNumber(5E-324), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MAX_SAFE_INTEGER"), jsDoubleNumber(9007199254740991.0), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MIN_SAFE_INTEGER"), jsDoubleNumber(-9007199254740991.0), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "NEGATIVE_INFINITY"), jsDoubleNumber(-std::numeric_limits<double>::infinity()), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "POSITIVE_INFINITY"), jsDoubleNumber(std::numeric_limits<double>::infinity()), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "NaN"), jsNaN(), DontDelete | DontEnum | ReadOnly); + + putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "isFinite"), 1, numberConstructorFuncIsFinite, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "isInteger"), 1, numberConstructorFuncIsInteger, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "isNaN"), 1, numberConstructorFuncIsNaN, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "isSafeInteger"), 1, numberConstructorFuncIsSafeInteger, NoIntrinsic, DontEnum | Function); + putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "parseFloat"), 1, globalFuncParseFloat, NoIntrinsic, DontEnum | Function); + putDirectWithoutTransition(vm, Identifier::fromString(&vm, "parseInt"), numberPrototype->globalObject()->parseIntFunction(), DontEnum | Function); +} + +// ECMA 15.7.1 +static EncodedJSValue JSC_HOST_CALL constructWithNumberConstructor(ExecState* exec) +{ + NumberObject* object = NumberObject::create(exec->vm(), asInternalFunction(exec->callee())->globalObject()->numberObjectStructure()); + double n = exec->argumentCount() ? exec->uncheckedArgument(0).toNumber(exec) : 0; + object->setInternalValue(exec->vm(), jsNumber(n)); + return JSValue::encode(object); +} + +ConstructType NumberConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithNumberConstructor; + return ConstructTypeHost; +} + +// ECMA 15.7.2 +static EncodedJSValue JSC_HOST_CALL callNumberConstructor(ExecState* exec) +{ + return JSValue::encode(jsNumber(!exec->argumentCount() ? 0 : exec->uncheckedArgument(0).toNumber(exec))); +} + +CallType NumberConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callNumberConstructor; + return CallTypeHost; +} + +// ECMA-262 20.1.2.2 +static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsFinite(ExecState* exec) +{ + JSValue argument = exec->argument(0); + return JSValue::encode(jsBoolean(argument.isNumber() && (argument.isInt32() || std::isfinite(argument.asDouble())))); +} + +// ECMA-262 20.1.2.3 +static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsInteger(ExecState* exec) +{ + JSValue argument = exec->argument(0); + bool isInteger; + if (argument.isInt32()) + isInteger = true; + else if (!argument.isDouble()) + isInteger = false; + else { + double number = argument.asDouble(); + isInteger = std::isfinite(number) && trunc(number) == number; + } + return JSValue::encode(jsBoolean(isInteger)); +} + +// ECMA-262 20.1.2.4 +static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsNaN(ExecState* exec) +{ + JSValue argument = exec->argument(0); + return JSValue::encode(jsBoolean(argument.isDouble() && std::isnan(argument.asDouble()))); +} + +// ECMA-262 20.1.2.5 +static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsSafeInteger(ExecState* exec) +{ + JSValue argument = exec->argument(0); + bool isInteger; + if (argument.isInt32()) + isInteger = true; + else if (!argument.isDouble()) + isInteger = false; + else { + double number = argument.asDouble(); + isInteger = trunc(number) == number && std::abs(number) <= 9007199254740991.0; + } + return JSValue::encode(jsBoolean(isInteger)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NumberConstructor.h b/Source/JavaScriptCore/runtime/NumberConstructor.h new file mode 100644 index 000000000..f8b1f7f44 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberConstructor.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NumberConstructor_h +#define NumberConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class NumberPrototype; + +class NumberConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | ImplementsHasInstance; + + static NumberConstructor* create(VM& vm, Structure* structure, NumberPrototype* numberPrototype) + { + NumberConstructor* constructor = new (NotNull, allocateCell<NumberConstructor>(vm.heap)) NumberConstructor(vm, structure); + constructor->finishCreation(vm, numberPrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) + { + return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, NumberPrototype*); + +private: + NumberConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} // namespace JSC + +#endif // NumberConstructor_h diff --git a/Source/JavaScriptCore/runtime/NumberObject.cpp b/Source/JavaScriptCore/runtime/NumberObject.cpp new file mode 100644 index 000000000..31350ccbd --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberObject.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "NumberObject.h" + +#include "JSGlobalObject.h" +#include "NumberPrototype.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(NumberObject); + +const ClassInfo NumberObject::s_info = { "Number", &JSWrapperObject::s_info, 0, CREATE_METHOD_TABLE(NumberObject) }; + +NumberObject::NumberObject(VM& vm, Structure* structure) + : JSWrapperObject(vm, structure) +{ +} + +void NumberObject::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +NumberObject* constructNumber(ExecState* exec, JSGlobalObject* globalObject, JSValue number) +{ + NumberObject* object = NumberObject::create(exec->vm(), globalObject->numberObjectStructure()); + object->setInternalValue(exec->vm(), number); + return object; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NumberObject.h b/Source/JavaScriptCore/runtime/NumberObject.h new file mode 100644 index 000000000..d979dd48a --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberObject.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NumberObject_h +#define NumberObject_h + +#include "JSWrapperObject.h" + +namespace JSC { + +class NumberObject : public JSWrapperObject { +protected: + NumberObject(VM&, Structure*); + void finishCreation(VM&); + +public: + typedef JSWrapperObject Base; + + static NumberObject* create(VM& vm, Structure* structure) + { + NumberObject* number = new (NotNull, allocateCell<NumberObject>(vm.heap)) NumberObject(vm, structure); + number->finishCreation(vm); + return number; + } + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(NumberObjectType, StructureFlags), info()); + } +}; + +JS_EXPORT_PRIVATE NumberObject* constructNumber(ExecState*, JSGlobalObject*, JSValue); + +} // namespace JSC + +#endif // NumberObject_h diff --git a/Source/JavaScriptCore/runtime/NumberPrototype.cpp b/Source/JavaScriptCore/runtime/NumberPrototype.cpp new file mode 100644 index 000000000..ea711c0f7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberPrototype.cpp @@ -0,0 +1,529 @@ +/* + * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#include "config.h" +#include "NumberPrototype.h" + +#include "BigInteger.h" +#include "Error.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "JSCInlines.h" +#include "Uint16WithFraction.h" +#include <wtf/dtoa.h> +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/Vector.h> +#include <wtf/dtoa/double-conversion.h> + +using namespace WTF::double_conversion; + +// To avoid conflict with WTF::StringBuilder. +typedef WTF::double_conversion::StringBuilder DoubleConversionStringBuilder; + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState*); + +} + +#include "NumberPrototype.lut.h" + +namespace JSC { + +const ClassInfo NumberPrototype::s_info = { "Number", &NumberObject::s_info, &numberPrototypeTable, CREATE_METHOD_TABLE(NumberPrototype) }; + +/* Source for NumberPrototype.lut.h +@begin numberPrototypeTable + toString numberProtoFuncToString DontEnum|Function 1 + toLocaleString numberProtoFuncToLocaleString DontEnum|Function 0 + valueOf numberProtoFuncValueOf DontEnum|Function 0 + toFixed numberProtoFuncToFixed DontEnum|Function 1 + toExponential numberProtoFuncToExponential DontEnum|Function 1 + toPrecision numberProtoFuncToPrecision DontEnum|Function 1 +@end +*/ + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(NumberPrototype); + +NumberPrototype::NumberPrototype(VM& vm, Structure* structure) + : NumberObject(vm, structure) +{ +} + +void NumberPrototype::finishCreation(VM& vm, JSGlobalObject*) +{ + Base::finishCreation(vm); + setInternalValue(vm, jsNumber(0)); + + ASSERT(inherits(info())); +} + +bool NumberPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<NumberObject>(exec, numberPrototypeTable, jsCast<NumberPrototype*>(object), propertyName, slot); +} + +// ------------------------------ Functions --------------------------- + +static ALWAYS_INLINE bool toThisNumber(JSValue thisValue, double& x) +{ + if (thisValue.isInt32()) { + x = thisValue.asInt32(); + return true; + } + + if (thisValue.isDouble()) { + x = thisValue.asDouble(); + return true; + } + + if (thisValue.isCell() && thisValue.asCell()->type() == NumberObjectType) { + x = static_cast<const NumberObject*>(thisValue.asCell())->internalValue().asNumber(); + return true; + } + + return false; +} + +static ALWAYS_INLINE bool getIntegerArgumentInRange(ExecState* exec, int low, int high, int& result, bool& isUndefined) +{ + result = 0; + isUndefined = false; + + JSValue argument0 = exec->argument(0); + if (argument0.isUndefined()) { + isUndefined = true; + return true; + } + + double asDouble = argument0.toInteger(exec); + if (asDouble < low || asDouble > high) + return false; + + result = static_cast<int>(asDouble); + return true; +} + +// The largest finite floating point number is 1.mantissa * 2^(0x7fe-0x3ff). +// Since 2^N in binary is a one bit followed by N zero bits. 1 * 2^3ff requires +// at most 1024 characters to the left of a decimal point, in base 2 (1025 if +// we include a minus sign). For the fraction, a value with an exponent of 0 +// has up to 52 bits to the right of the decimal point. Each decrement of the +// exponent down to a minimum of -0x3fe adds an additional digit to the length +// of the fraction. As such the maximum fraction size is 1075 (1076 including +// a point). We pick a buffer size such that can simply place the point in the +// center of the buffer, and are guaranteed to have enough space in each direction +// fo any number of digits an IEEE number may require to represent. +typedef char RadixBuffer[2180]; + +// Mapping from integers 0..35 to digit identifying this value, for radix 2..36. +static const char radixDigits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radix) +{ + ASSERT(std::isfinite(number)); + ASSERT(radix >= 2 && radix <= 36); + + // Position the decimal point at the center of the string, set + // the startOfResultString pointer to point at the decimal point. + char* decimalPoint = buffer + sizeof(buffer) / 2; + char* startOfResultString = decimalPoint; + + // Extract the sign. + bool isNegative = number < 0; + if (std::signbit(number)) + number = -number; + double integerPart = floor(number); + + // We use this to test for odd values in odd radix bases. + // Where the base is even, (e.g. 10), to determine whether a value is even we need only + // consider the least significant digit. For example, 124 in base 10 is even, because '4' + // is even. if the radix is odd, then the radix raised to an integer power is also odd. + // E.g. in base 5, 124 represents (1 * 125 + 2 * 25 + 4 * 5). Since each digit in the value + // is multiplied by an odd number, the result is even if the sum of all digits is even. + // + // For the integer portion of the result, we only need test whether the integer value is + // even or odd. For each digit of the fraction added, we should invert our idea of whether + // the number is odd if the new digit is odd. + // + // Also initialize digit to this value; for even radix values we only need track whether + // the last individual digit was odd. + bool integerPartIsOdd = integerPart <= static_cast<double>(0x1FFFFFFFFFFFFFull) && static_cast<int64_t>(integerPart) & 1; + ASSERT(integerPartIsOdd == static_cast<bool>(fmod(integerPart, 2))); + bool isOddInOddRadix = integerPartIsOdd; + uint32_t digit = integerPartIsOdd; + + // Check if the value has a fractional part to convert. + double fractionPart = number - integerPart; + if (fractionPart) { + // Write the decimal point now. + *decimalPoint = '.'; + + // Higher precision representation of the fractional part. + Uint16WithFraction fraction(fractionPart); + + bool needsRoundingUp = false; + char* endOfResultString = decimalPoint + 1; + + // Calculate the delta from the current number to the next & previous possible IEEE numbers. + double nextNumber = nextafter(number, std::numeric_limits<double>::infinity()); + double lastNumber = nextafter(number, -std::numeric_limits<double>::infinity()); + ASSERT(std::isfinite(nextNumber) && !std::signbit(nextNumber)); + ASSERT(std::isfinite(lastNumber) && !std::signbit(lastNumber)); + double deltaNextDouble = nextNumber - number; + double deltaLastDouble = number - lastNumber; + ASSERT(std::isfinite(deltaNextDouble) && !std::signbit(deltaNextDouble)); + ASSERT(std::isfinite(deltaLastDouble) && !std::signbit(deltaLastDouble)); + + // We track the delta from the current value to the next, to track how many digits of the + // fraction we need to write. For example, if the value we are converting is precisely + // 1.2345, so far we have written the digits "1.23" to a string leaving a remainder of + // 0.45, and we want to determine whether we can round off, or whether we need to keep + // appending digits ('4'). We can stop adding digits provided that then next possible + // lower IEEE value is further from 1.23 than the remainder we'd be rounding off (0.45), + // which is to say, less than 1.2255. Put another way, the delta between the prior + // possible value and this number must be more than 2x the remainder we'd be rounding off + // (or more simply half the delta between numbers must be greater than the remainder). + // + // Similarly we need track the delta to the next possible value, to dertermine whether + // to round up. In almost all cases (other than at exponent boundaries) the deltas to + // prior and subsequent values are identical, so we don't need track then separately. + if (deltaNextDouble != deltaLastDouble) { + // Since the deltas are different track them separately. Pre-multiply by 0.5. + Uint16WithFraction halfDeltaNext(deltaNextDouble, 1); + Uint16WithFraction halfDeltaLast(deltaLastDouble, 1); + + while (true) { + // examine the remainder to determine whether we should be considering rounding + // up or down. If remainder is precisely 0.5 rounding is to even. + int dComparePoint5 = fraction.comparePoint5(); + if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) { + // Check for rounding up; are we closer to the value we'd round off to than + // the next IEEE value would be? + if (fraction.sumGreaterThanOne(halfDeltaNext)) { + needsRoundingUp = true; + break; + } + } else { + // Check for rounding down; are we closer to the value we'd round off to than + // the prior IEEE value would be? + if (fraction < halfDeltaLast) + break; + } + + ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1)); + // Write a digit to the string. + fraction *= radix; + digit = fraction.floorAndSubtract(); + *endOfResultString++ = radixDigits[digit]; + // Keep track whether the portion written is currently even, if the radix is odd. + if (digit & 1) + isOddInOddRadix = !isOddInOddRadix; + + // Shift the fractions by radix. + halfDeltaNext *= radix; + halfDeltaLast *= radix; + } + } else { + // This code is identical to that above, except since deltaNextDouble != deltaLastDouble + // we don't need to track these two values separately. + Uint16WithFraction halfDelta(deltaNextDouble, 1); + + while (true) { + int dComparePoint5 = fraction.comparePoint5(); + if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) { + if (fraction.sumGreaterThanOne(halfDelta)) { + needsRoundingUp = true; + break; + } + } else if (fraction < halfDelta) + break; + + ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1)); + fraction *= radix; + digit = fraction.floorAndSubtract(); + if (digit & 1) + isOddInOddRadix = !isOddInOddRadix; + *endOfResultString++ = radixDigits[digit]; + + halfDelta *= radix; + } + } + + // Check if the fraction needs rounding off (flag set in the loop writing digits, above). + if (needsRoundingUp) { + // Whilst the last digit is the maximum in the current radix, remove it. + // e.g. rounding up the last digit in "12.3999" is the same as rounding up the + // last digit in "12.3" - both round up to "12.4". + while (endOfResultString[-1] == radixDigits[radix - 1]) + --endOfResultString; + + // Radix digits are sequential in ascii/unicode, except for '9' and 'a'. + // E.g. the first 'if' case handles rounding 67.89 to 67.8a in base 16. + // The 'else if' case handles rounding of all other digits. + if (endOfResultString[-1] == '9') + endOfResultString[-1] = 'a'; + else if (endOfResultString[-1] != '.') + ++endOfResultString[-1]; + else { + // One other possibility - there may be no digits to round up in the fraction + // (or all may be been rounded off already), in which case we may need to + // round into the integer portion of the number. Remove the decimal point. + --endOfResultString; + // In order to get here there must have been a non-zero fraction, in which case + // there must be at least one bit of the value's mantissa not in use in the + // integer part of the number. As such, adding to the integer part should not + // be able to lose precision. + ASSERT((integerPart + 1) - integerPart == 1); + ++integerPart; + } + } else { + // We only need to check for trailing zeros if the value does not get rounded up. + while (endOfResultString[-1] == '0') + --endOfResultString; + } + + *endOfResultString = '\0'; + ASSERT(endOfResultString < buffer + sizeof(buffer)); + } else + *decimalPoint = '\0'; + + BigInteger units(integerPart); + + // Always loop at least once, to emit at least '0'. + do { + ASSERT(buffer < startOfResultString); + + // Read a single digit and write it to the front of the string. + // Divide by radix to remove one digit from the value. + digit = units.divide(radix); + *--startOfResultString = radixDigits[digit]; + } while (!!units); + + // If the number is negative, prepend '-'. + if (isNegative) + *--startOfResultString = '-'; + ASSERT(buffer <= startOfResultString); + + return startOfResultString; +} + +static String toStringWithRadix(int32_t number, unsigned radix) +{ + LChar buf[1 + 32]; // Worst case is radix == 2, which gives us 32 digits + sign. + LChar* end = buf + WTF_ARRAY_LENGTH(buf); + LChar* p = end; + + bool negative = false; + uint32_t positiveNumber = number; + if (number < 0) { + negative = true; + positiveNumber = -number; + } + + while (positiveNumber) { + uint32_t index = positiveNumber % radix; + ASSERT(index < sizeof(radixDigits)); + *--p = static_cast<LChar>(radixDigits[index]); + positiveNumber /= radix; + } + if (negative) + *--p = '-'; + + return String(p, static_cast<unsigned>(end - p)); +} + +// toExponential converts a number to a string, always formatting as an expoential. +// This method takes an optional argument specifying a number of *decimal places* +// to round the significand to (or, put another way, this method optionally rounds +// to argument-plus-one significant figures). +EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState* exec) +{ + double x; + if (!toThisNumber(exec->thisValue(), x)) + return throwVMTypeError(exec); + + // Get the argument. + int decimalPlacesInExponent; + bool isUndefined; + if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlacesInExponent, isUndefined)) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("toExponential() argument must be between 0 and 20"))); + + // Handle NaN and Infinity. + if (!std::isfinite(x)) + return JSValue::encode(jsNontrivialString(exec, String::numberToStringECMAScript(x))); + + // Round if the argument is not undefined, always format as exponential. + char buffer[WTF::NumberToStringBufferLength]; + DoubleConversionStringBuilder builder(buffer, WTF::NumberToStringBufferLength); + const DoubleToStringConverter& converter = DoubleToStringConverter::EcmaScriptConverter(); + builder.Reset(); + isUndefined + ? converter.ToExponential(x, -1, &builder) + : converter.ToExponential(x, decimalPlacesInExponent, &builder); + return JSValue::encode(jsString(exec, String(builder.Finalize()))); +} + +// toFixed converts a number to a string, always formatting as an a decimal fraction. +// This method takes an argument specifying a number of decimal places to round the +// significand to. However when converting large values (1e+21 and above) this +// method will instead fallback to calling ToString. +EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState* exec) +{ + double x; + if (!toThisNumber(exec->thisValue(), x)) + return throwVMTypeError(exec); + + // Get the argument. + int decimalPlaces; + bool isUndefined; // This is ignored; undefined treated as 0. + if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlaces, isUndefined)) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("toFixed() argument must be between 0 and 20"))); + + // 15.7.4.5.7 states "If x >= 10^21, then let m = ToString(x)" + // This also covers Ininity, and structure the check so that NaN + // values are also handled by numberToString + if (!(fabs(x) < 1e+21)) + return JSValue::encode(jsString(exec, String::numberToStringECMAScript(x))); + + // The check above will return false for NaN or Infinity, these will be + // handled by numberToString. + ASSERT(std::isfinite(x)); + + NumberToStringBuffer buffer; + return JSValue::encode(jsString(exec, String(numberToFixedWidthString(x, decimalPlaces, buffer)))); +} + +// toPrecision converts a number to a string, takeing an argument specifying a +// number of significant figures to round the significand to. For positive +// exponent, all values that can be represented using a decimal fraction will +// be, e.g. when rounding to 3 s.f. any value up to 999 will be formated as a +// decimal, whilst 1000 is converted to the exponential representation 1.00e+3. +// For negative exponents values >= 1e-6 are formated as decimal fractions, +// with smaller values converted to exponential representation. +EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState* exec) +{ + double x; + if (!toThisNumber(exec->thisValue(), x)) + return throwVMTypeError(exec); + + // Get the argument. + int significantFigures; + bool isUndefined; + if (!getIntegerArgumentInRange(exec, 1, 21, significantFigures, isUndefined)) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("toPrecision() argument must be between 1 and 21"))); + + // To precision called with no argument is treated as ToString. + if (isUndefined) + return JSValue::encode(jsString(exec, String::numberToStringECMAScript(x))); + + // Handle NaN and Infinity. + if (!std::isfinite(x)) + return JSValue::encode(jsNontrivialString(exec, String::numberToStringECMAScript(x))); + + NumberToStringBuffer buffer; + return JSValue::encode(jsString(exec, String(numberToFixedPrecisionString(x, significantFigures, buffer)))); +} + +static inline int32_t extractRadixFromArgs(ExecState* exec) +{ + JSValue radixValue = exec->argument(0); + int32_t radix; + if (radixValue.isInt32()) + radix = radixValue.asInt32(); + else if (radixValue.isUndefined()) + radix = 10; + else + radix = static_cast<int32_t>(radixValue.toInteger(exec)); // nan -> 0 + + return radix; +} + +static inline EncodedJSValue integerValueToString(ExecState* exec, int32_t radix, int32_t value) +{ + // A negative value casted to unsigned would be bigger than 36 (the max radix). + if (static_cast<unsigned>(value) < static_cast<unsigned>(radix)) { + ASSERT(value <= 36); + ASSERT(value >= 0); + VM* vm = &exec->vm(); + return JSValue::encode(vm->smallStrings.singleCharacterString(radixDigits[value])); + } + + if (radix == 10) { + VM* vm = &exec->vm(); + return JSValue::encode(jsString(vm, vm->numericStrings.add(value))); + } + + return JSValue::encode(jsString(exec, toStringWithRadix(value, radix))); + +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec) +{ + double doubleValue; + if (!toThisNumber(exec->thisValue(), doubleValue)) + return throwVMTypeError(exec); + + int32_t radix = extractRadixFromArgs(exec); + if (radix < 2 || radix > 36) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("toString() radix argument must be between 2 and 36"))); + + int32_t integerValue = static_cast<int32_t>(doubleValue); + if (integerValue == doubleValue) + return integerValueToString(exec, radix, integerValue); + + if (radix == 10) { + VM* vm = &exec->vm(); + return JSValue::encode(jsString(vm, vm->numericStrings.add(doubleValue))); + } + + if (!std::isfinite(doubleValue)) + return JSValue::encode(jsNontrivialString(exec, String::numberToStringECMAScript(doubleValue))); + + RadixBuffer s; + return JSValue::encode(jsString(exec, toStringWithRadix(s, doubleValue, radix))); +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec) +{ + double x; + if (!toThisNumber(exec->thisValue(), x)) + return throwVMTypeError(exec); + + return JSValue::encode(jsNumber(x).toString(exec)); +} + +EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec) +{ + double x; + if (!toThisNumber(exec->thisValue(), x)) + return throwVMTypeError(exec); + return JSValue::encode(jsNumber(x)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/NumberPrototype.h b/Source/JavaScriptCore/runtime/NumberPrototype.h new file mode 100644 index 000000000..45acd8fb1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumberPrototype.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef NumberPrototype_h +#define NumberPrototype_h + +#include "NumberObject.h" + +namespace JSC { + +class NumberPrototype : public NumberObject { +public: + typedef NumberObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static NumberPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + NumberPrototype* prototype = new (NotNull, allocateCell<NumberPrototype>(vm.heap)) NumberPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(NumberObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, JSGlobalObject*); + +private: + NumberPrototype(VM&, Structure*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // NumberPrototype_h diff --git a/Source/JavaScriptCore/runtime/NumericStrings.h b/Source/JavaScriptCore/runtime/NumericStrings.h new file mode 100644 index 000000000..ac8d41603 --- /dev/null +++ b/Source/JavaScriptCore/runtime/NumericStrings.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef NumericStrings_h +#define NumericStrings_h + +#include <array> +#include <wtf/HashFunctions.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +class NumericStrings { +public: + ALWAYS_INLINE String add(double d) + { + CacheEntry<double>& entry = lookup(d); + if (d == entry.key && !entry.value.isNull()) + return entry.value; + entry.key = d; + entry.value = String::numberToStringECMAScript(d); + return entry.value; + } + + ALWAYS_INLINE String add(int i) + { + if (static_cast<unsigned>(i) < cacheSize) + return lookupSmallString(static_cast<unsigned>(i)); + CacheEntry<int>& entry = lookup(i); + if (i == entry.key && !entry.value.isNull()) + return entry.value; + entry.key = i; + entry.value = String::number(i); + return entry.value; + } + + ALWAYS_INLINE String add(unsigned i) + { + if (i < cacheSize) + return lookupSmallString(static_cast<unsigned>(i)); + CacheEntry<unsigned>& entry = lookup(i); + if (i == entry.key && !entry.value.isNull()) + return entry.value; + entry.key = i; + entry.value = String::number(i); + return entry.value; + } +private: + static const size_t cacheSize = 64; + + template<typename T> + struct CacheEntry { + T key; + String value; + }; + + CacheEntry<double>& lookup(double d) { return doubleCache[WTF::FloatHash<double>::hash(d) & (cacheSize - 1)]; } + CacheEntry<int>& lookup(int i) { return intCache[WTF::IntHash<int>::hash(i) & (cacheSize - 1)]; } + CacheEntry<unsigned>& lookup(unsigned i) { return unsignedCache[WTF::IntHash<unsigned>::hash(i) & (cacheSize - 1)]; } + ALWAYS_INLINE const String& lookupSmallString(unsigned i) + { + ASSERT(i < cacheSize); + if (smallIntCache[i].isNull()) + smallIntCache[i] = String::number(i); + return smallIntCache[i]; + } + + std::array<CacheEntry<double>, cacheSize> doubleCache; + std::array<CacheEntry<int>, cacheSize> intCache; + std::array<CacheEntry<unsigned>, cacheSize> unsignedCache; + std::array<String, cacheSize> smallIntCache; +}; + +} // namespace JSC + +#endif // NumericStrings_h diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp new file mode 100644 index 000000000..65acb4817 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -0,0 +1,678 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ObjectConstructor.h" + +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSCInlines.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSGlobalObjectFunctions.h" +#include "Lookup.h" +#include "ObjectPrototype.h" +#include "PropertyDescriptor.h" +#include "PropertyNameArray.h" +#include "StackVisitor.h" +#include "Symbol.h" + +namespace JSC { + +EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState*); + +} + +#include "ObjectConstructor.lut.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectConstructor); + +const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_info, &objectConstructorTable, CREATE_METHOD_TABLE(ObjectConstructor) }; + +/* Source for ObjectConstructor.lut.h +@begin objectConstructorTable + getPrototypeOf objectConstructorGetPrototypeOf DontEnum|Function 1 + setPrototypeOf objectConstructorSetPrototypeOf DontEnum|Function 2 + getOwnPropertyDescriptor objectConstructorGetOwnPropertyDescriptor DontEnum|Function 2 + getOwnPropertyNames objectConstructorGetOwnPropertyNames DontEnum|Function 1 + keys objectConstructorKeys DontEnum|Function 1 + defineProperty objectConstructorDefineProperty DontEnum|Function 3 + defineProperties objectConstructorDefineProperties DontEnum|Function 2 + create objectConstructorCreate DontEnum|Function 2 + seal objectConstructorSeal DontEnum|Function 1 + freeze objectConstructorFreeze DontEnum|Function 1 + preventExtensions objectConstructorPreventExtensions DontEnum|Function 1 + isSealed objectConstructorIsSealed DontEnum|Function 1 + isFrozen objectConstructorIsFrozen DontEnum|Function 1 + isExtensible objectConstructorIsExtensible DontEnum|Function 1 + is objectConstructorIs DontEnum|Function 2 + assign objectConstructorAssign DontEnum|Function 2 +@end +*/ + +ObjectConstructor::ObjectConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void ObjectConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, ObjectPrototype* objectPrototype) +{ + Base::finishCreation(vm, objectPrototype->classInfo()->className); + // ECMA 15.2.3.1 + putDirectWithoutTransition(vm, vm.propertyNames->prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); + // no. of arguments for constructor + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); + + JSC_NATIVE_FUNCTION("getOwnPropertySymbols", objectConstructorGetOwnPropertySymbols, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->getPrototypeOfPrivateName, objectConstructorGetPrototypeOf, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->getOwnPropertyNamesPrivateName, objectConstructorGetOwnPropertyNames, DontEnum, 1); +} + +JSFunction* ObjectConstructor::addDefineProperty(ExecState* exec, JSGlobalObject* globalObject) +{ + VM& vm = exec->vm(); + JSFunction* definePropertyFunction = JSFunction::create(vm, globalObject, 3, vm.propertyNames->defineProperty.string(), objectConstructorDefineProperty); + putDirectWithoutTransition(vm, vm.propertyNames->defineProperty, definePropertyFunction, DontEnum); + return definePropertyFunction; +} + +bool ObjectConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<JSObject>(exec, objectConstructorTable, jsCast<ObjectConstructor*>(object), propertyName, slot); +} + +static ALWAYS_INLINE JSObject* constructObject(ExecState* exec) +{ + JSGlobalObject* globalObject = exec->callee()->globalObject(); + ArgList args(exec); + JSValue arg = args.at(0); + if (arg.isUndefinedOrNull()) + return constructEmptyObject(exec, globalObject->objectPrototype()); + return arg.toObject(exec, globalObject); +} + +static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec) +{ + return JSValue::encode(constructObject(exec)); +} + +ConstructType ObjectConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithObjectConstructor; + return ConstructTypeHost; +} + +static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec) +{ + return JSValue::encode(constructObject(exec)); +} + +CallType ObjectConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callObjectConstructor; + return CallTypeHost; +} + +class ObjectConstructorGetPrototypeOfFunctor { +public: + ObjectConstructorGetPrototypeOfFunctor(JSObject* object) + : m_hasSkippedFirstFrame(false) + , m_object(object) + , m_result(jsUndefined()) + { + } + + JSValue result() const { return m_result; } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + if (!m_hasSkippedFirstFrame) { + m_hasSkippedFirstFrame = true; + return StackVisitor::Continue; + } + + if (m_object->allowsAccessFrom(visitor->callFrame())) + m_result = m_object->prototype(); + return StackVisitor::Done; + } + +private: + bool m_hasSkippedFirstFrame; + JSObject* m_object; + JSValue m_result; +}; + +JSValue objectConstructorGetPrototypeOf(ExecState* exec, JSObject* object) +{ + ObjectConstructorGetPrototypeOfFunctor functor(object); + exec->iterate(functor); + return functor.result(); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec) +{ + JSObject* object = exec->argument(0).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + return JSValue::encode(objectConstructorGetPrototypeOf(exec, object)); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(ExecState* exec) +{ + JSValue objectValue = exec->argument(0); + if (objectValue.isUndefinedOrNull()) + return throwVMTypeError(exec); + + JSValue protoValue = exec->argument(1); + if (!protoValue.isObject() && !protoValue.isNull()) + return throwVMTypeError(exec); + + JSObject* object = objectValue.toObject(exec); + if (exec->hadException()) + return JSValue::encode(objectValue); + + if (!checkProtoSetterAccessAllowed(exec, object)) + return JSValue::encode(objectValue); + + if (object->prototype() == protoValue) + return JSValue::encode(objectValue); + + if (!object->isExtensible()) + return throwVMError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); + + if (!object->setPrototypeWithCycleCheck(exec, protoValue)) { + exec->vm().throwException(exec, createError(exec, ASCIILiteral("cyclic __proto__ value"))); + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(objectValue); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec) +{ + JSObject* object = exec->argument(0).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + auto propertyName = exec->argument(1).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + PropertyDescriptor descriptor; + if (!object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) + return JSValue::encode(jsUndefined()); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSObject* description = constructEmptyObject(exec); + if (!descriptor.isAccessorDescriptor()) { + description->putDirect(exec->vm(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); + description->putDirect(exec->vm(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); + } else { + ASSERT(descriptor.getter()); + ASSERT(descriptor.setter()); + description->putDirect(exec->vm(), exec->propertyNames().get, descriptor.getter(), 0); + description->putDirect(exec->vm(), exec->propertyNames().set, descriptor.setter(), 0); + } + + description->putDirect(exec->vm(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); + description->putDirect(exec->vm(), exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); + + return JSValue::encode(description); +} + +// FIXME: Use the enumeration cache. +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec) +{ + JSObject* object = exec->argument(0).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Include)); +} + +// FIXME: Use the enumeration cache. +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertySymbols(ExecState* exec) +{ + JSObject* object = exec->argument(0).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Symbols, DontEnumPropertiesMode::Include)); +} + +// FIXME: Use the enumeration cache. +EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec) +{ + JSObject* object = exec->argument(0).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude)); +} + +EncodedJSValue JSC_HOST_CALL ownEnumerablePropertyKeys(ExecState* exec) +{ + JSObject* object = exec->argument(0).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Exclude)); +} + +// ES5 8.10.5 ToPropertyDescriptor +bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) +{ + if (!in.isObject()) { + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Property description must be an object."))); + return false; + } + JSObject* description = asObject(in); + + PropertySlot enumerableSlot(description); + if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { + desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); + if (exec->hadException()) + return false; + } + + PropertySlot configurableSlot(description); + if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { + desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); + if (exec->hadException()) + return false; + } + + JSValue value; + PropertySlot valueSlot(description); + if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { + desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); + if (exec->hadException()) + return false; + } + + PropertySlot writableSlot(description); + if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { + desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); + if (exec->hadException()) + return false; + } + + PropertySlot getSlot(description); + if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { + JSValue get = getSlot.getValue(exec, exec->propertyNames().get); + if (exec->hadException()) + return false; + if (!get.isUndefined()) { + CallData callData; + if (getCallData(get, callData) == CallTypeNone) { + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Getter must be a function."))); + return false; + } + } + desc.setGetter(get); + } + + PropertySlot setSlot(description); + if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { + JSValue set = setSlot.getValue(exec, exec->propertyNames().set); + if (exec->hadException()) + return false; + if (!set.isUndefined()) { + CallData callData; + if (getCallData(set, callData) == CallTypeNone) { + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Setter must be a function."))); + return false; + } + } + desc.setSetter(set); + } + + if (!desc.isAccessorDescriptor()) + return true; + + if (desc.value()) { + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Invalid property. 'value' present on property with getter or setter."))); + return false; + } + + if (desc.writablePresent()) { + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Invalid property. 'writable' present on property with getter or setter."))); + return false; + } + return true; +} + +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec) +{ + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Properties can only be defined on Objects."))); + JSObject* O = asObject(exec->argument(0)); + auto propertyName = exec->argument(1).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsNull()); + PropertyDescriptor descriptor; + if (!toPropertyDescriptor(exec, exec->argument(2), descriptor)) + return JSValue::encode(jsNull()); + ASSERT((descriptor.attributes() & Accessor) || (!descriptor.isAccessorDescriptor())); + ASSERT(!exec->hadException()); + O->methodTable(exec->vm())->defineOwnProperty(O, exec, propertyName, descriptor, true); + return JSValue::encode(O); +} + +static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) +{ + PropertyNameArray propertyNames(exec, PropertyNameMode::StringsAndSymbols); + asObject(properties)->methodTable(exec->vm())->getOwnPropertyNames(asObject(properties), exec, propertyNames, EnumerationMode(DontEnumPropertiesMode::Exclude)); + size_t numProperties = propertyNames.size(); + Vector<PropertyDescriptor> descriptors; + MarkedArgumentBuffer markBuffer; + for (size_t i = 0; i < numProperties; i++) { + JSValue prop = properties->get(exec, propertyNames[i]); + if (exec->hadException()) + return jsNull(); + PropertyDescriptor descriptor; + if (!toPropertyDescriptor(exec, prop, descriptor)) + return jsNull(); + descriptors.append(descriptor); + // Ensure we mark all the values that we're accumulating + if (descriptor.isDataDescriptor() && descriptor.value()) + markBuffer.append(descriptor.value()); + if (descriptor.isAccessorDescriptor()) { + if (descriptor.getter()) + markBuffer.append(descriptor.getter()); + if (descriptor.setter()) + markBuffer.append(descriptor.setter()); + } + } + for (size_t i = 0; i < numProperties; i++) { + Identifier propertyName = propertyNames[i]; + if (exec->propertyNames().isPrivateName(propertyName)) + continue; + object->methodTable(exec->vm())->defineOwnProperty(object, exec, propertyName, descriptors[i], true); + if (exec->hadException()) + return jsNull(); + } + return object; +} + +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec) +{ + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Properties can only be defined on Objects."))); + return JSValue::encode(defineProperties(exec, asObject(exec->argument(0)), exec->argument(1).toObject(exec))); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec) +{ + JSValue proto = exec->argument(0); + if (!proto.isObject() && !proto.isNull()) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object prototype may only be an Object or null."))); + JSObject* newObject = proto.isObject() + ? constructEmptyObject(exec, asObject(proto)) + : constructEmptyObject(exec, exec->lexicalGlobalObject()->nullPrototypeObjectStructure()); + if (exec->argument(1).isUndefined()) + return JSValue::encode(newObject); + if (!exec->argument(1).isObject()) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Property descriptor list must be an Object."))); + return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1)))); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec) +{ + // 1. If Type(O) is not Object, return O. + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return JSValue::encode(obj); + JSObject* object = asObject(obj); + + if (isJSFinalObject(object)) { + object->seal(exec->vm()); + return JSValue::encode(obj); + } + + // 2. For each named own property name P of O, + PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols); + object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include)); + PropertyNameArray::const_iterator end = properties.end(); + for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { + Identifier propertyName = *iter; + if (exec->propertyNames().isPrivateName(propertyName)) + continue; + // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. + PropertyDescriptor desc; + if (!object->getOwnPropertyDescriptor(exec, propertyName, desc)) + continue; + // b. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false. + desc.setConfigurable(false); + // c. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments. + object->methodTable(exec->vm())->defineOwnProperty(object, exec, propertyName, desc, true); + if (exec->hadException()) + return JSValue::encode(obj); + } + + // 3. Set the [[Extensible]] internal property of O to false. + object->preventExtensions(exec->vm()); + + // 4. Return O. + return JSValue::encode(obj); +} + +JSObject* objectConstructorFreeze(ExecState* exec, JSObject* object) +{ + if (isJSFinalObject(object) && !hasIndexedProperties(object->indexingType())) { + object->freeze(exec->vm()); + return object; + } + + // 2. For each named own property name P of O, + PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols); + object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include)); + PropertyNameArray::const_iterator end = properties.end(); + for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { + Identifier propertyName = *iter; + if (exec->propertyNames().isPrivateName(propertyName)) + continue; + // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. + PropertyDescriptor desc; + if (!object->getOwnPropertyDescriptor(exec, propertyName, desc)) + continue; + // b. If IsDataDescriptor(desc) is true, then + // i. If desc.[[Writable]] is true, set desc.[[Writable]] to false. + if (desc.isDataDescriptor()) + desc.setWritable(false); + // c. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false. + desc.setConfigurable(false); + // d. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments. + object->methodTable(exec->vm())->defineOwnProperty(object, exec, propertyName, desc, true); + if (exec->hadException()) + return object; + } + + // 3. Set the [[Extensible]] internal property of O to false. + object->preventExtensions(exec->vm()); + + // 4. Return O. + return object; +} + +EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec) +{ + // 1. If Type(O) is not Object, return O. + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return JSValue::encode(obj); + return JSValue::encode(objectConstructorFreeze(exec, asObject(obj))); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec) +{ + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return JSValue::encode(obj); + asObject(obj)->preventExtensions(exec->vm()); + return JSValue::encode(obj); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec) +{ + // 1. If Type(O) is not Object, return true. + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return JSValue::encode(jsBoolean(true)); + JSObject* object = asObject(obj); + + if (isJSFinalObject(object)) + return JSValue::encode(jsBoolean(object->isSealed(exec->vm()))); + + // 2. For each named own property name P of O, + PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols); + object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include)); + PropertyNameArray::const_iterator end = properties.end(); + for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { + Identifier propertyName = *iter; + if (exec->propertyNames().isPrivateName(propertyName)) + continue; + // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. + PropertyDescriptor desc; + if (!object->getOwnPropertyDescriptor(exec, propertyName, desc)) + continue; + // b. If desc.[[Configurable]] is true, then return false. + if (desc.configurable()) + return JSValue::encode(jsBoolean(false)); + } + + // 3. If the [[Extensible]] internal property of O is false, then return true. + // 4. Otherwise, return false. + return JSValue::encode(jsBoolean(!object->isExtensible())); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec) +{ + // 1. If Type(O) is not Object, return true. + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return JSValue::encode(jsBoolean(true)); + JSObject* object = asObject(obj); + + if (isJSFinalObject(object)) + return JSValue::encode(jsBoolean(object->isFrozen(exec->vm()))); + + // 2. For each named own property name P of O, + PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols); + object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include)); + PropertyNameArray::const_iterator end = properties.end(); + for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { + Identifier propertyName = *iter; + if (exec->propertyNames().isPrivateName(propertyName)) + continue; + // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. + PropertyDescriptor desc; + if (!object->getOwnPropertyDescriptor(exec, propertyName, desc)) + continue; + // b. If IsDataDescriptor(desc) is true then + // i. If desc.[[Writable]] is true, return false. c. If desc.[[Configurable]] is true, then return false. + if ((desc.isDataDescriptor() && desc.writable()) || desc.configurable()) + return JSValue::encode(jsBoolean(false)); + } + + // 3. If the [[Extensible]] internal property of O is false, then return true. + // 4. Otherwise, return false. + return JSValue::encode(jsBoolean(!object->isExtensible())); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec) +{ + JSValue obj = exec->argument(0); + if (!obj.isObject()) + return JSValue::encode(jsBoolean(false)); + return JSValue::encode(jsBoolean(asObject(obj)->isExtensible())); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState* exec) +{ + return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1)))); +} + +// FIXME: Use the enumeration cache. +JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode) +{ + PropertyNameArray properties(exec, propertyNameMode); + object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode)); + + JSArray* keys = constructEmptyArray(exec, 0); + + switch (propertyNameMode) { + case PropertyNameMode::Strings: { + size_t numProperties = properties.size(); + for (size_t i = 0; i < numProperties; i++) { + const auto& identifier = properties[i]; + ASSERT(!identifier.isSymbol()); + keys->push(exec, jsOwnedString(exec, identifier.string())); + } + break; + } + + case PropertyNameMode::Symbols: { + size_t numProperties = properties.size(); + for (size_t i = 0; i < numProperties; i++) { + const auto& identifier = properties[i]; + ASSERT(identifier.isSymbol()); + if (!exec->propertyNames().isPrivateName(identifier)) + keys->push(exec, Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*identifier.impl()))); + } + break; + } + + case PropertyNameMode::StringsAndSymbols: { + Vector<Identifier, 16> propertySymbols; + size_t numProperties = properties.size(); + for (size_t i = 0; i < numProperties; i++) { + const auto& identifier = properties[i]; + if (identifier.isSymbol()) { + if (!exec->propertyNames().isPrivateName(identifier)) + propertySymbols.append(identifier); + } else + keys->push(exec, jsOwnedString(exec, identifier.string())); + } + + // To ensure the order defined in the spec (9.1.12), we append symbols at the last elements of keys. + for (const auto& identifier : propertySymbols) + keys->push(exec, Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*identifier.impl()))); + + break; + } + } + + return keys; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.h b/Source/JavaScriptCore/runtime/ObjectConstructor.h new file mode 100644 index 000000000..15b0d4dae --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ObjectConstructor_h +#define ObjectConstructor_h + +#include "InternalFunction.h" +#include "JSGlobalObject.h" +#include "ObjectPrototype.h" + +namespace JSC { + +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertySymbols(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState*); +EncodedJSValue JSC_HOST_CALL ownEnumerablePropertyKeys(ExecState*); + +class ObjectPrototype; + +class ObjectConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static ObjectConstructor* create(VM& vm, JSGlobalObject* globalObject, Structure* structure, ObjectPrototype* objectPrototype) + { + ObjectConstructor* constructor = new (NotNull, allocateCell<ObjectConstructor>(vm.heap)) ObjectConstructor(vm, structure); + constructor->finishCreation(vm, globalObject, objectPrototype); + return constructor; + } + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + JSFunction* addDefineProperty(ExecState*, JSGlobalObject*); + +protected: + void finishCreation(VM&, JSGlobalObject*, ObjectPrototype*); + +private: + ObjectConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure) +{ + return JSFinalObject::create(exec, structure); +} + +inline JSObject* constructEmptyObject(ExecState* exec, JSObject* prototype, unsigned inlineCapacity) +{ + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + PrototypeMap& prototypeMap = globalObject->vm().prototypeMap; + Structure* structure = prototypeMap.emptyObjectStructureForPrototype( + prototype, inlineCapacity); + return constructEmptyObject(exec, structure); +} + +inline JSObject* constructEmptyObject(ExecState* exec, JSObject* prototype) +{ + return constructEmptyObject(exec, prototype, JSFinalObject::defaultInlineCapacity()); +} + +inline JSObject* constructEmptyObject(ExecState* exec) +{ + return constructEmptyObject(exec, exec->lexicalGlobalObject()->objectPrototype()); +} + +JSObject* objectConstructorFreeze(ExecState*, JSObject*); +JSValue objectConstructorGetPrototypeOf(ExecState*, JSObject*); +JSArray* ownPropertyKeys(ExecState*, JSObject*, PropertyNameMode, DontEnumPropertiesMode); +bool toPropertyDescriptor(ExecState*, JSValue, PropertyDescriptor&); + +} // namespace JSC + +#endif // ObjectConstructor_h diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp new file mode 100644 index 000000000..905d0d256 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "ObjectPrototype.h" + +#include "Error.h" +#include "GetterSetter.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSCInlines.h" +#include "StructureRareDataInlines.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState*); + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectPrototype); + +const ClassInfo ObjectPrototype::s_info = { "Object", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(ObjectPrototype) }; + +ObjectPrototype::ObjectPrototype(VM& vm, Structure* stucture) + : JSNonFinalObject(vm, stucture) +{ +} + +void ObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + JSC_NATIVE_FUNCTION(vm.propertyNames->toString, objectProtoFuncToString, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->toLocaleString, objectProtoFuncToLocaleString, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->valueOf, objectProtoFuncValueOf, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->hasOwnProperty, objectProtoFuncHasOwnProperty, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->isPrototypeOf, objectProtoFuncIsPrototypeOf, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->__defineGetter__, objectProtoFuncDefineGetter, DontEnum, 2); + JSC_NATIVE_FUNCTION(vm.propertyNames->__defineSetter__, objectProtoFuncDefineSetter, DontEnum, 2); + JSC_NATIVE_FUNCTION(vm.propertyNames->__lookupGetter__, objectProtoFuncLookupGetter, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->__lookupSetter__, objectProtoFuncLookupSetter, DontEnum, 1); +} + +ObjectPrototype* ObjectPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + ObjectPrototype* prototype = new (NotNull, allocateCell<ObjectPrototype>(vm.heap)) ObjectPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; +} + +// ------------------------------ Functions -------------------------------- + +EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState* exec) +{ + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + return JSValue::encode(thisValue.toObject(exec)); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState* exec) +{ + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + auto propertyName = exec->argument(0).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + return JSValue::encode(jsBoolean(thisValue.toObject(exec)->hasOwnProperty(exec, propertyName))); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec) +{ + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + JSObject* thisObj = thisValue.toObject(exec); + + if (!exec->argument(0).isObject()) + return JSValue::encode(jsBoolean(false)); + + JSValue v = asObject(exec->argument(0))->prototype(); + + while (true) { + if (!v.isObject()) + return JSValue::encode(jsBoolean(false)); + if (v == thisObj) + return JSValue::encode(jsBoolean(true)); + v = asObject(v)->prototype(); + } +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState* exec) +{ + JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue get = exec->argument(1); + CallData callData; + if (getCallData(get, callData) == CallTypeNone) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("invalid getter usage"))); + + auto propertyName = exec->argument(0).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + PropertyDescriptor descriptor; + descriptor.setGetter(get); + descriptor.setEnumerable(true); + descriptor.setConfigurable(true); + + bool shouldThrow = false; + thisObject->methodTable(exec->vm())->defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); + + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState* exec) +{ + JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue set = exec->argument(1); + CallData callData; + if (getCallData(set, callData) == CallTypeNone) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("invalid setter usage"))); + + auto propertyName = exec->argument(0).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + PropertyDescriptor descriptor; + descriptor.setSetter(set); + descriptor.setEnumerable(true); + descriptor.setConfigurable(true); + + bool shouldThrow = false; + thisObject->methodTable(exec->vm())->defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); + + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec) +{ + JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + auto propertyName = exec->argument(0).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + PropertySlot slot(thisObject); + if (thisObject->getPropertySlot(exec, propertyName, slot) && slot.isAccessor()) { + GetterSetter* getterSetter = slot.getterSetter(); + return getterSetter->isGetterNull() ? JSValue::encode(jsUndefined()) : JSValue::encode(getterSetter->getter()); + } + + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState* exec) +{ + JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + auto propertyName = exec->argument(0).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + PropertySlot slot(thisObject); + if (thisObject->getPropertySlot(exec, propertyName, slot) && slot.isAccessor()) { + GetterSetter* getterSetter = slot.getterSetter(); + return getterSetter->isSetterNull() ? JSValue::encode(jsUndefined()) : JSValue::encode(getterSetter->setter()); + } + + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState* exec) +{ + auto propertyName = exec->argument(0).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + PropertyDescriptor descriptor; + bool enumerable = thisObject->getOwnPropertyDescriptor(exec, propertyName, descriptor) && descriptor.enumerable(); + return JSValue::encode(jsBoolean(enumerable)); +} + +// 15.2.4.3 Object.prototype.toLocaleString() +EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec) +{ + // 1. Let O be the result of calling ToObject passing the this value as the argument. + JSObject* object = exec->thisValue().toThis(exec, StrictMode).toObject(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + // 2. Let toString be the result of calling the [[Get]] internal method of O passing "toString" as the argument. + JSValue toString = object->get(exec, exec->propertyNames().toString); + + // 3. If IsCallable(toString) is false, throw a TypeError exception. + CallData callData; + CallType callType = getCallData(toString, callData); + if (callType == CallTypeNone) + return JSValue::encode(jsUndefined()); + + // 4. Return the result of calling the [[Call]] internal method of toString passing O as the this value and no arguments. + return JSValue::encode(call(exec, toString, callType, callData, object, exec->emptyList())); +} + +EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec) +{ + VM& vm = exec->vm(); + JSValue thisValue = exec->thisValue().toThis(exec, StrictMode); + if (thisValue.isUndefinedOrNull()) + return JSValue::encode(thisValue.isUndefined() ? vm.smallStrings.undefinedObjectString() : vm.smallStrings.nullObjectString()); + JSObject* thisObject = thisValue.toObject(exec); + + JSString* result = thisObject->structure(vm)->objectToStringValue(); + if (!result) { + RefPtr<StringImpl> newString = WTF::tryMakeString("[object ", thisObject->methodTable(exec->vm())->className(thisObject), "]"); + if (!newString) + return JSValue::encode(throwOutOfMemoryError(exec)); + + result = jsNontrivialString(&vm, newString.release()); + thisObject->structure(vm)->setObjectToStringValue(vm, result); + } + return JSValue::encode(result); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.h b/Source/JavaScriptCore/runtime/ObjectPrototype.h new file mode 100644 index 000000000..34238a9f4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ObjectPrototype_h +#define ObjectPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class ObjectPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static ObjectPrototype* create(VM&, JSGlobalObject*, Structure*); + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, JSGlobalObject*); + +private: + ObjectPrototype(VM&, Structure*); +}; + +JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState*); + +} // namespace JSC + +#endif // ObjectPrototype_h diff --git a/Source/JavaScriptCore/runtime/Operations.cpp b/Source/JavaScriptCore/runtime/Operations.cpp new file mode 100644 index 000000000..5732cfd0c --- /dev/null +++ b/Source/JavaScriptCore/runtime/Operations.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Operations.h" + +#include "Error.h" +#include "JSCInlines.h" +#include "JSObject.h" +#include "JSString.h" +#include <wtf/MathExtras.h> + +namespace JSC { + +bool JSValue::equalSlowCase(ExecState* exec, JSValue v1, JSValue v2) +{ + return equalSlowCaseInline(exec, v1, v2); +} + +bool JSValue::strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2) +{ + return strictEqualSlowCaseInline(exec, v1, v2); +} + +NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2) +{ + // exception for the Date exception in defaultValue() + JSValue p1 = v1.toPrimitive(callFrame); + JSValue p2 = v2.toPrimitive(callFrame); + + if (p1.isString()) + return jsString(callFrame, asString(p1), p2.toString(callFrame)); + + if (p2.isString()) + return jsString(callFrame, p1.toString(callFrame), asString(p2)); + + return jsNumber(p1.toNumber(callFrame) + p2.toNumber(callFrame)); +} + +JSValue jsTypeStringForValue(VM& vm, JSGlobalObject* globalObject, JSValue v) +{ + if (v.isUndefined()) + return vm.smallStrings.undefinedString(); + if (v.isBoolean()) + return vm.smallStrings.booleanString(); + if (v.isNumber()) + return vm.smallStrings.numberString(); + if (v.isString()) + return vm.smallStrings.stringString(); + if (v.isSymbol()) + return vm.smallStrings.symbolString(); + if (v.isObject()) { + JSObject* object = asObject(v); + // Return "undefined" for objects that should be treated + // as null when doing comparisons. + if (object->structure(vm)->masqueradesAsUndefined(globalObject)) + return vm.smallStrings.undefinedString(); + if (object->type() == JSFunctionType) + return vm.smallStrings.functionString(); + if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) { + CallData callData; + JSObject* object = asObject(v); + if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone) + return vm.smallStrings.functionString(); + } + } + return vm.smallStrings.objectString(); +} + +JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v) +{ + return jsTypeStringForValue(callFrame->vm(), callFrame->lexicalGlobalObject(), v); +} + +bool jsIsObjectTypeOrNull(CallFrame* callFrame, JSValue v) +{ + if (!v.isCell()) + return v.isNull(); + + JSType type = v.asCell()->type(); + if (type == StringType || type == SymbolType) + return false; + if (type >= ObjectType) { + if (asObject(v)->structure(callFrame->vm())->masqueradesAsUndefined(callFrame->lexicalGlobalObject())) + return false; + CallData callData; + JSObject* object = asObject(v); + if (object->methodTable(callFrame->vm())->getCallData(object, callData) != CallTypeNone) + return false; + } + return true; +} + +bool jsIsFunctionType(JSValue v) +{ + if (v.isObject()) { + CallData callData; + JSObject* object = asObject(v); + if (object->methodTable()->getCallData(object, callData) != CallTypeNone) + return true; + } + return false; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Operations.h b/Source/JavaScriptCore/runtime/Operations.h new file mode 100644 index 000000000..057f59471 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Operations.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009, 2013, 2014 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef Operations_h +#define Operations_h + +#include "CallFrame.h" +#include "ExceptionHelpers.h" +#include "JSCJSValue.h" + +namespace JSC { + +NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue); +JSValue jsTypeStringForValue(CallFrame*, JSValue); +JSValue jsTypeStringForValue(VM&, JSGlobalObject*, JSValue); +bool jsIsObjectTypeOrNull(CallFrame*, JSValue); +bool jsIsFunctionType(JSValue); + +ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) +{ + VM& vm = exec->vm(); + + int32_t length1 = s1->length(); + if (!length1) + return s2; + int32_t length2 = s2->length(); + if (!length2) + return s1; + if (sumOverflows<int32_t>(length1, length2)) + return throwOutOfMemoryError(exec); + + return JSRopeString::create(vm, s1, s2); +} + +ALWAYS_INLINE JSValue jsString(ExecState* exec, const String& u1, const String& u2, const String& u3) +{ + VM* vm = &exec->vm(); + + int32_t length1 = u1.length(); + int32_t length2 = u2.length(); + int32_t length3 = u3.length(); + + if (length1 < 0 || length2 < 0 || length3 < 0) + return throwOutOfMemoryError(exec); + + if (!length1) + return jsString(exec, jsString(vm, u2), jsString(vm, u3)); + if (!length2) + return jsString(exec, jsString(vm, u1), jsString(vm, u3)); + if (!length3) + return jsString(exec, jsString(vm, u1), jsString(vm, u2)); + + if (sumOverflows<int32_t>(length1, length2, length3)) + return throwOutOfMemoryError(exec); + + return JSRopeString::create(exec->vm(), jsString(vm, u1), jsString(vm, u2), jsString(vm, u3)); +} + +ALWAYS_INLINE JSValue jsStringFromRegisterArray(ExecState* exec, Register* strings, unsigned count) +{ + VM* vm = &exec->vm(); + JSRopeString::RopeBuilder ropeBuilder(*vm); + + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[-static_cast<int>(i)].jsValue(); + if (!ropeBuilder.append(v.toString(exec))) + return throwOutOfMemoryError(exec); + } + + return ropeBuilder.release(); +} + +ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue) +{ + VM* vm = &exec->vm(); + JSRopeString::RopeBuilder ropeBuilder(*vm); + ropeBuilder.append(thisValue.toString(exec)); + + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + JSValue v = exec->argument(i); + if (!ropeBuilder.append(v.toString(exec))) + return throwOutOfMemoryError(exec); + } + + return ropeBuilder.release(); +} + +// See ES5 11.8.1/11.8.2/11.8.5 for definition of leftFirst, this value ensures correct +// evaluation ordering for argument conversions for '<' and '>'. For '<' pass the value +// true, for leftFirst, for '>' pass the value false (and reverse operand order). +template<bool leftFirst> +ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) +{ + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() < v2.asInt32(); + + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() < v2.asNumber(); + + if (isJSString(v1) && isJSString(v2)) + return codePointCompareLessThan(asString(v1)->value(callFrame), asString(v2)->value(callFrame)); + + double n1; + double n2; + JSValue p1; + JSValue p2; + bool wasNotString1; + bool wasNotString2; + if (leftFirst) { + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + } else { + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + } + + if (wasNotString1 | wasNotString2) + return n1 < n2; + return codePointCompareLessThan(asString(p1)->value(callFrame), asString(p2)->value(callFrame)); +} + +// See ES5 11.8.3/11.8.4/11.8.5 for definition of leftFirst, this value ensures correct +// evaluation ordering for argument conversions for '<=' and '=>'. For '<=' pass the +// value true, for leftFirst, for '=>' pass the value false (and reverse operand order). +template<bool leftFirst> +ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) +{ + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() <= v2.asInt32(); + + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() <= v2.asNumber(); + + if (isJSString(v1) && isJSString(v2)) + return !codePointCompareLessThan(asString(v2)->value(callFrame), asString(v1)->value(callFrame)); + + double n1; + double n2; + JSValue p1; + JSValue p2; + bool wasNotString1; + bool wasNotString2; + if (leftFirst) { + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + } else { + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + } + + if (wasNotString1 | wasNotString2) + return n1 <= n2; + return !codePointCompareLessThan(asString(p2)->value(callFrame), asString(p1)->value(callFrame)); +} + +// Fast-path choices here are based on frequency data from SunSpider: +// <times> Add case: <t1> <t2> +// --------------------------- +// 5626160 Add case: 3 3 (of these, 3637690 are for immediate values) +// 247412 Add case: 5 5 +// 20900 Add case: 5 6 +// 13962 Add case: 5 3 +// 4000 Add case: 3 5 + +ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) +{ + if (v1.isNumber() && v2.isNumber()) + return jsNumber(v1.asNumber() + v2.asNumber()); + + if (v1.isString() && !v2.isObject()) + return jsString(callFrame, asString(v1), v2.toString(callFrame)); + + // All other cases are pretty uncommon + return jsAddSlowCase(callFrame, v1, v2); +} + +#define InvalidPrototypeChain (std::numeric_limits<size_t>::max()) + +inline size_t normalizePrototypeChain(CallFrame* callFrame, Structure* structure) +{ + VM& vm = callFrame->vm(); + size_t count = 0; + while (1) { + if (structure->isProxy()) + return InvalidPrototypeChain; + JSValue v = structure->prototypeForLookup(callFrame); + if (v.isNull()) + return count; + + JSCell* base = v.asCell(); + structure = base->structure(vm); + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (structure->isDictionary()) + structure->flattenDictionaryStructure(vm, asObject(base)); + + ++count; + } +} + +} // namespace JSC + +#endif // Operations_h diff --git a/Source/JavaScriptCore/runtime/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp new file mode 100644 index 000000000..fe830b47b --- /dev/null +++ b/Source/JavaScriptCore/runtime/Options.cpp @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2011-2012, 2014-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "Options.h" + +#include "HeapStatistics.h" +#include <algorithm> +#include <limits> +#include <math.h> +#include <mutex> +#include <stdlib.h> +#include <string.h> +#include <wtf/DataLog.h> +#include <wtf/NumberOfCores.h> +#include <wtf/PageBlock.h> +#include <wtf/StdLibExtras.h> +#include <wtf/StringExtras.h> +#include <wtf/text/StringBuilder.h> + +#if OS(DARWIN) && ENABLE(PARALLEL_GC) +#include <sys/sysctl.h> +#endif + +#if OS(WINDOWS) +#include "MacroAssemblerX86.h" +#endif + +namespace JSC { + +static bool parse(const char* string, bool& value) +{ + if (!strcasecmp(string, "true") || !strcasecmp(string, "yes") || !strcmp(string, "1")) { + value = true; + return true; + } + if (!strcasecmp(string, "false") || !strcasecmp(string, "no") || !strcmp(string, "0")) { + value = false; + return true; + } + return false; +} + +static bool parse(const char* string, int32_t& value) +{ + return sscanf(string, "%d", &value) == 1; +} + +static bool parse(const char* string, unsigned& value) +{ + return sscanf(string, "%u", &value) == 1; +} + +static bool parse(const char* string, double& value) +{ + return sscanf(string, "%lf", &value) == 1; +} + +static bool parse(const char* string, OptionRange& value) +{ + return value.init(string); +} + +static bool parse(const char* string, const char*& value) +{ + if (!strlen(string)) + string = nullptr; + value = string; + return true; +} + +static bool parse(const char* string, GCLogging::Level& value) +{ + if (!strcasecmp(string, "none") || !strcasecmp(string, "no") || !strcasecmp(string, "false") || !strcmp(string, "0")) { + value = GCLogging::None; + return true; + } + + if (!strcasecmp(string, "basic") || !strcasecmp(string, "yes") || !strcasecmp(string, "true") || !strcmp(string, "1")) { + value = GCLogging::Basic; + return true; + } + + if (!strcasecmp(string, "verbose") || !strcmp(string, "2")) { + value = GCLogging::Verbose; + return true; + } + + return false; +} + +template<typename T> +bool overrideOptionWithHeuristic(T& variable, const char* name) +{ + const char* stringValue = getenv(name); + if (!stringValue) + return false; + + if (parse(stringValue, variable)) + return true; + + fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue); + return false; +} + +static unsigned computeNumberOfWorkerThreads(int maxNumberOfWorkerThreads, int minimum = 1) +{ + int cpusToUse = std::min(WTF::numberOfProcessorCores(), maxNumberOfWorkerThreads); + + // Be paranoid, it is the OS we're dealing with, after all. + ASSERT(cpusToUse >= 1); + return std::max(cpusToUse, minimum); +} + +static int32_t computePriorityDeltaOfWorkerThreads(int32_t twoCorePriorityDelta, int32_t multiCorePriorityDelta) +{ + if (WTF::numberOfProcessorCores() <= 2) + return twoCorePriorityDelta; + + return multiCorePriorityDelta; +} + +static unsigned computeNumberOfGCMarkers(unsigned maxNumberOfGCMarkers) +{ +#if ENABLE(PARALLEL_GC) + return computeNumberOfWorkerThreads(maxNumberOfGCMarkers); +#else + UNUSED_PARAM(maxNumberOfGCMarkers); + return 1; +#endif +} + +const char* const OptionRange::s_nullRangeStr = "<null>"; + +bool OptionRange::init(const char* rangeString) +{ + // rangeString should be in the form of [!]<low>[:<high>] + // where low and high are unsigned + + bool invert = false; + + if (!rangeString) { + m_state = InitError; + return false; + } + + if (!strcmp(rangeString, s_nullRangeStr)) { + m_state = Uninitialized; + return true; + } + + m_rangeString = rangeString; + + if (*rangeString == '!') { + invert = true; + rangeString++; + } + + int scanResult = sscanf(rangeString, " %u:%u", &m_lowLimit, &m_highLimit); + + if (!scanResult || scanResult == EOF) { + m_state = InitError; + return false; + } + + if (scanResult == 1) + m_highLimit = m_lowLimit; + + if (m_lowLimit > m_highLimit) { + m_state = InitError; + return false; + } + + m_state = invert ? Inverted : Normal; + + return true; +} + +bool OptionRange::isInRange(unsigned count) +{ + if (m_state < Normal) + return true; + + if ((m_lowLimit <= count) && (count <= m_highLimit)) + return m_state == Normal ? true : false; + + return m_state == Normal ? false : true; +} + +void OptionRange::dump(PrintStream& out) const +{ + out.print(m_rangeString); +} + +Options::Entry Options::s_options[Options::numberOfOptions]; +Options::Entry Options::s_defaultOptions[Options::numberOfOptions]; + +// Realize the names for each of the options: +const Options::EntryInfo Options::s_optionsInfo[Options::numberOfOptions] = { +#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \ + { #name_, description_, Options::Type::type_##Type }, + JSC_OPTIONS(FOR_EACH_OPTION) +#undef FOR_EACH_OPTION +}; + +static void scaleJITPolicy() +{ + auto& scaleFactor = Options::jitPolicyScale(); + if (scaleFactor > 1.0) + scaleFactor = 1.0; + else if (scaleFactor < 0.0) + scaleFactor = 0.0; + + struct OptionToScale { + Options::OptionID id; + int32_t minVal; + }; + + static const OptionToScale optionsToScale[] = { + { Options::thresholdForJITAfterWarmUpID, 0 }, + { Options::thresholdForJITSoonID, 0 }, + { Options::thresholdForOptimizeAfterWarmUpID, 1 }, + { Options::thresholdForOptimizeAfterLongWarmUpID, 1 }, + { Options::thresholdForOptimizeSoonID, 1 }, + { Options::thresholdForFTLOptimizeSoonID, 2 }, + { Options::thresholdForFTLOptimizeAfterWarmUpID, 2 } + }; + + const int numberOfOptionsToScale = sizeof(optionsToScale) / sizeof(OptionToScale); + for (int i = 0; i < numberOfOptionsToScale; i++) { + Option option(optionsToScale[i].id); + ASSERT(option.type() == Options::Type::int32Type); + option.int32Val() *= scaleFactor; + option.int32Val() = std::max(option.int32Val(), optionsToScale[i].minVal); + } +} + +static void recomputeDependentOptions() +{ +#if !ENABLE(JIT) + Options::useLLInt() = true; + Options::useJIT() = false; + Options::useDFGJIT() = false; + Options::useFTLJIT() = false; +#endif +#if !ENABLE(YARR_JIT) + Options::useRegExpJIT() = false; +#endif +#if !ENABLE(CONCURRENT_JIT) + Options::enableConcurrentJIT() = false; +#endif +#if !ENABLE(DFG_JIT) + Options::useDFGJIT() = false; + Options::useFTLJIT() = false; +#endif +#if !ENABLE(FTL_JIT) + Options::useFTLJIT() = false; +#endif +#if OS(WINDOWS) && CPU(X86) + // Disable JIT on Windows if SSE2 is not present + if (!MacroAssemblerX86::supportsFloatingPoint()) + Options::useJIT() = false; +#endif + if (Options::showDisassembly() + || Options::showDFGDisassembly() + || Options::showFTLDisassembly() + || Options::dumpBytecodeAtDFGTime() + || Options::dumpGraphAtEachPhase() + || Options::verboseCompilation() + || Options::verboseFTLCompilation() + || Options::logCompilationChanges() + || Options::validateGraph() + || Options::validateGraphAtEachPhase() + || Options::verboseOSR() + || Options::verboseCompilationQueue() + || Options::reportCompileTimes() + || Options::reportFTLCompileTimes() + || Options::verboseCFA() + || Options::verboseFTLFailure()) + Options::alwaysComputeHash() = true; + + if (Option(Options::jitPolicyScaleID).isOverridden()) + scaleJITPolicy(); + + if (Options::forceEagerCompilation()) { + Options::thresholdForJITAfterWarmUp() = 10; + Options::thresholdForJITSoon() = 10; + Options::thresholdForOptimizeAfterWarmUp() = 20; + Options::thresholdForOptimizeAfterLongWarmUp() = 20; + Options::thresholdForOptimizeSoon() = 20; + Options::thresholdForFTLOptimizeAfterWarmUp() = 20; + Options::thresholdForFTLOptimizeSoon() = 20; + Options::maximumEvalCacheableSourceLength() = 150000; + Options::enableConcurrentJIT() = false; + } + + // Compute the maximum value of the reoptimization retry counter. This is simply + // the largest value at which we don't overflow the execute counter, when using it + // to left-shift the execution counter by this amount. Currently the value ends + // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles + // total on a 32-bit processor. + Options::reoptimizationRetryCounterMax() = 0; + while ((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << (Options::reoptimizationRetryCounterMax() + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max())) + Options::reoptimizationRetryCounterMax()++; + + ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) > 0); + ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max())); +} + +void Options::initialize() +{ + static std::once_flag initializeOptionsOnceFlag; + + std::call_once( + initializeOptionsOnceFlag, + [] { + // Initialize each of the options with their default values: +#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \ + name_() = defaultValue_; \ + name_##Default() = defaultValue_; + JSC_OPTIONS(FOR_EACH_OPTION) +#undef FOR_EACH_OPTION + + // It *probably* makes sense for other platforms to enable this. +#if PLATFORM(IOS) && CPU(ARM64) + enableLLVMFastISel() = true; +#endif + + // Allow environment vars to override options if applicable. + // The evn var should be the name of the option prefixed with + // "JSC_". +#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \ + overrideOptionWithHeuristic(name_(), "JSC_" #name_); + JSC_OPTIONS(FOR_EACH_OPTION) +#undef FOR_EACH_OPTION + +#if 0 + ; // Deconfuse editors that do auto indentation +#endif + + recomputeDependentOptions(); + + // Do range checks where needed and make corrections to the options: + ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp()); + ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon()); + ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0); + + dumpOptionsIfNeeded(); + ensureOptionsAreCoherent(); + }); +} + +void Options::dumpOptionsIfNeeded() +{ + if (Options::showOptions()) { + DumpLevel level = static_cast<DumpLevel>(Options::showOptions()); + if (level > DumpLevel::Verbose) + level = DumpLevel::Verbose; + + const char* title = nullptr; + switch (level) { + case DumpLevel::None: + break; + case DumpLevel::Overridden: + title = "Overridden JSC options:"; + break; + case DumpLevel::All: + title = "All JSC options:"; + break; + case DumpLevel::Verbose: + title = "All JSC options with descriptions:"; + break; + } + + StringBuilder builder; + dumpAllOptions(builder, level, title, nullptr, " ", "\n", ShowDefaults); + dataLog(builder.toString()); + } +} + +bool Options::setOptions(const char* optionsStr) +{ + Vector<char*> options; + + size_t length = strlen(optionsStr); + char* optionsStrCopy = WTF::fastStrDup(optionsStr); + char* end = optionsStrCopy + length; + char* p = optionsStrCopy; + + while (p < end) { + char* optionStart = p; + p = strstr(p, "="); + if (!p) { + dataLogF("'=' not found in option string: %p\n", optionStart); + return false; + } + p++; + + char* valueBegin = p; + bool hasStringValue = false; + const int minStringLength = 2; // The min is an empty string i.e. 2 double quotes. + if ((p + minStringLength < end) && (*p == '"')) { + p = strstr(p + 1, "\""); + if (!p) { + dataLogF("Missing trailing '\"' in option string: %p\n", optionStart); + return false; // End of string not found. + } + hasStringValue = true; + } + + p = strstr(p, " "); + if (!p) + p = end; // No more " " separator. Hence, this is the last arg. + + // If we have a well-formed string value, strip the quotes. + if (hasStringValue) { + char* valueEnd = p; + ASSERT((*valueBegin == '"') && ((valueEnd - valueBegin) >= minStringLength) && (valueEnd[-1] == '"')); + memmove(valueBegin, valueBegin + 1, valueEnd - valueBegin - minStringLength); + valueEnd[-minStringLength] = '\0'; + } + + // Strip leading -- if present. + if ((p - optionStart > 2) && optionStart[0] == '-' && optionStart[1] == '-') + optionStart += 2; + + *p++ = '\0'; + options.append(optionStart); + } + + bool success = true; + for (auto& option : options) { + bool optionSuccess = setOption(option); + if (!optionSuccess) { + dataLogF("Failed to set option : %s\n", option); + success = false; + } + } + + dumpOptionsIfNeeded(); + return success; +} + +// Parses a single command line option in the format "<optionName>=<value>" +// (no spaces allowed) and set the specified option if appropriate. +bool Options::setOption(const char* arg) +{ + // arg should look like this: + // <jscOptionName>=<appropriate value> + const char* equalStr = strchr(arg, '='); + if (!equalStr) + return false; + + const char* valueStr = equalStr + 1; + + // For each option, check if the specify arg is a match. If so, set the arg + // if the value makes sense. Otherwise, move on to checking the next option. +#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \ + if (strlen(#name_) == static_cast<size_t>(equalStr - arg) \ + && !strncmp(arg, #name_, equalStr - arg)) { \ + type_ value; \ + value = (defaultValue_); \ + bool success = parse(valueStr, value); \ + if (success) { \ + name_() = value; \ + recomputeDependentOptions(); \ + return true; \ + } \ + return false; \ + } + + JSC_OPTIONS(FOR_EACH_OPTION) +#undef FOR_EACH_OPTION + + return false; // No option matched. +} + +void Options::dumpAllOptions(StringBuilder& builder, DumpLevel level, const char* title, + const char* separator, const char* optionHeader, const char* optionFooter, ShowDefaultsOption showDefaultsOption) +{ + if (title) { + builder.append(title); + builder.append('\n'); + } + + for (int id = 0; id < numberOfOptions; id++) { + if (separator && id) + builder.append(separator); + dumpOption(builder, level, static_cast<OptionID>(id), optionHeader, optionFooter, showDefaultsOption); + } +} + +void Options::dumpAllOptionsInALine(StringBuilder& builder) +{ + dumpAllOptions(builder, DumpLevel::All, nullptr, " ", nullptr, nullptr, DontShowDefaults); +} + +void Options::dumpAllOptions(FILE* stream, DumpLevel level, const char* title) +{ + StringBuilder builder; + dumpAllOptions(builder, level, title, nullptr, " ", "\n", ShowDefaults); + fprintf(stream, "%s", builder.toString().ascii().data()); +} + +void Options::dumpOption(StringBuilder& builder, DumpLevel level, OptionID id, + const char* header, const char* footer, ShowDefaultsOption showDefaultsOption) +{ + if (id >= numberOfOptions) + return; // Illegal option. + + Option option(id); + bool wasOverridden = option.isOverridden(); + bool needsDescription = (level == DumpLevel::Verbose && option.description()); + + if (level == DumpLevel::Overridden && !wasOverridden) + return; + + if (header) + builder.append(header); + builder.append(option.name()); + builder.append('='); + option.dump(builder); + + if (wasOverridden && (showDefaultsOption == ShowDefaults)) { + builder.append(" (default: "); + option.defaultOption().dump(builder); + builder.append(")"); + } + + if (needsDescription) { + builder.append(" ... "); + builder.append(option.description()); + } + + builder.append(footer); +} + +void Options::ensureOptionsAreCoherent() +{ + bool coherent = true; + if (!(useLLInt() || useJIT())) { + coherent = false; + dataLog("INCOHERENT OPTIONS: at least one of useLLInt or useJIT must be true\n"); + } + if (!coherent) + CRASH(); +} + +void Option::dump(StringBuilder& builder) const +{ + switch (type()) { + case Options::Type::boolType: + builder.append(m_entry.boolVal ? "true" : "false"); + break; + case Options::Type::unsignedType: + builder.appendNumber(m_entry.unsignedVal); + break; + case Options::Type::doubleType: + builder.appendNumber(m_entry.doubleVal); + break; + case Options::Type::int32Type: + builder.appendNumber(m_entry.int32Val); + break; + case Options::Type::optionRangeType: + builder.append(m_entry.optionRangeVal.rangeString()); + break; + case Options::Type::optionStringType: { + const char* option = m_entry.optionStringVal; + if (!option) + option = ""; + builder.append('"'); + builder.append(option); + builder.append('"'); + break; + } + case Options::Type::gcLogLevelType: { + builder.append(GCLogging::levelAsString(m_entry.gcLogLevelVal)); + break; + } + } +} + +bool Option::operator==(const Option& other) const +{ + switch (type()) { + case Options::Type::boolType: + return m_entry.boolVal == other.m_entry.boolVal; + case Options::Type::unsignedType: + return m_entry.unsignedVal == other.m_entry.unsignedVal; + case Options::Type::doubleType: + return (m_entry.doubleVal == other.m_entry.doubleVal) || (isnan(m_entry.doubleVal) && isnan(other.m_entry.doubleVal)); + case Options::Type::int32Type: + return m_entry.int32Val == other.m_entry.int32Val; + case Options::Type::optionRangeType: + return m_entry.optionRangeVal.rangeString() == other.m_entry.optionRangeVal.rangeString(); + case Options::Type::optionStringType: + return (m_entry.optionStringVal == other.m_entry.optionStringVal) + || (m_entry.optionStringVal && other.m_entry.optionStringVal && !strcmp(m_entry.optionStringVal, other.m_entry.optionStringVal)); + case Options::Type::gcLogLevelType: + return m_entry.gcLogLevelVal == other.m_entry.gcLogLevelVal; + } + return false; +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h new file mode 100644 index 000000000..1a6555ed6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Options.h @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2011-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Options_h +#define Options_h + +#include "GCLogging.h" +#include "JSExportMacros.h" +#include <stdint.h> +#include <stdio.h> +#include <wtf/PrintStream.h> +#include <wtf/StdLibExtras.h> + +namespace WTF { +class StringBuilder; +} +using WTF::StringBuilder; + +namespace JSC { + +// How do JSC VM options work? +// =========================== +// The JSC_OPTIONS() macro below defines a list of all JSC options in use, +// along with their types and default values. The options values are actually +// realized as an array of Options::Entry elements. +// +// Options::initialize() will initialize the array of options values with +// the defaults specified in JSC_OPTIONS() below. After that, the values can +// be programmatically read and written to using an accessor method with the +// same name as the option. For example, the option "useJIT" can be read and +// set like so: +// +// bool jitIsOn = Options::useJIT(); // Get the option value. +// Options::useJIT() = false; // Sets the option value. +// +// If you want to tweak any of these values programmatically for testing +// purposes, you can do so in Options::initialize() after the default values +// are set. +// +// Alternatively, you can override the default values by specifying +// environment variables of the form: JSC_<name of JSC option>. +// +// Note: Options::initialize() tries to ensure some sanity on the option values +// which are set by doing some range checks, and value corrections. These +// checks are done after the option values are set. If you alter the option +// values after the sanity checks (for your own testing), then you're liable to +// ensure that the new values set are sane and reasonable for your own run. + +class OptionRange { +private: + enum RangeState { Uninitialized, InitError, Normal, Inverted }; +public: + OptionRange& operator= (const int& rhs) + { // Only needed for initialization + if (!rhs) { + m_state = Uninitialized; + m_rangeString = 0; + m_lowLimit = 0; + m_highLimit = 0; + } + return *this; + } + + bool init(const char*); + bool isInRange(unsigned); + const char* rangeString() const { return (m_state > InitError) ? m_rangeString : s_nullRangeStr; } + + void dump(PrintStream& out) const; + +private: + static const char* const s_nullRangeStr; + + RangeState m_state; + const char* m_rangeString; + unsigned m_lowLimit; + unsigned m_highLimit; +}; + +typedef OptionRange optionRange; +typedef const char* optionString; + +#define JSC_OPTIONS(v) \ + v(unsigned, showOptions, 0, "shows JSC options (0 = None, 1 = Overridden only, 2 = All, 3 = Verbose)") \ + \ + v(bool, useLLInt, true, "allows the LLINT to be used if true") \ + v(bool, useJIT, true, "allows the baseline JIT to be used if true") \ + v(bool, useDFGJIT, true, "allows the DFG JIT to be used if true") \ + v(bool, useRegExpJIT, true, "allows the RegExp JIT to be used if true") \ + \ + v(bool, reportMustSucceedExecutableAllocations, false, nullptr) \ + \ + v(unsigned, maxPerThreadStackUsage, 4 * MB, nullptr) \ + v(unsigned, reservedZoneSize, 128 * KB, nullptr) \ + v(unsigned, errorModeReservedZoneSize, 64 * KB, nullptr) \ + \ + v(bool, crashIfCantAllocateJITMemory, false, nullptr) \ + v(unsigned, jitMemoryReservationSize, 0, nullptr) \ + \ + v(bool, forceDFGCodeBlockLiveness, false, nullptr) \ + v(bool, forceICFailure, false, nullptr) \ + \ + v(bool, dumpGeneratedBytecodes, false, nullptr) \ + v(bool, dumpBytecodeLivenessResults, false, nullptr) \ + v(bool, validateBytecode, false, nullptr) \ + v(bool, forceDebuggerBytecodeGeneration, false, nullptr) \ + v(bool, forceProfilerBytecodeGeneration, false, nullptr) \ + \ + v(bool, enableFunctionDotArguments, true, nullptr) \ + \ + /* showDisassembly implies showDFGDisassembly. */ \ + v(bool, showDisassembly, false, "dumps disassembly of all JIT compiled code upon compilation") \ + v(bool, asyncDisassembly, false, nullptr) \ + v(bool, showDFGDisassembly, false, "dumps disassembly of DFG function upon compilation") \ + v(bool, showFTLDisassembly, false, "dumps disassembly of FTL function upon compilation") \ + v(bool, showAllDFGNodes, false, nullptr) \ + v(optionRange, bytecodeRangeToDFGCompile, 0, "bytecode size range to allow DFG compilation on, e.g. 1:100") \ + v(optionString, dfgWhitelist, nullptr, "file with list of function signatures to allow DFG compilation on") \ + v(bool, dumpSourceAtDFGTime, false, "dumps source code of JS function being DFG compiled") \ + v(bool, dumpBytecodeAtDFGTime, false, "dumps bytecode of JS function being DFG compiled") \ + v(bool, dumpGraphAfterParsing, false, nullptr) \ + v(bool, dumpGraphAtEachPhase, false, nullptr) \ + v(bool, verboseDFGByteCodeParsing, false, nullptr) \ + v(bool, verboseCompilation, false, nullptr) \ + v(bool, verboseFTLCompilation, false, nullptr) \ + v(bool, logCompilationChanges, false, nullptr) \ + v(bool, printEachOSRExit, false, nullptr) \ + v(bool, validateGraph, false, nullptr) \ + v(bool, validateGraphAtEachPhase, false, nullptr) \ + v(bool, verboseValidationFailure, false, nullptr) \ + v(bool, verboseOSR, false, nullptr) \ + v(bool, verboseFTLOSRExit, false, nullptr) \ + v(bool, verboseCallLink, false, nullptr) \ + v(bool, verboseCompilationQueue, false, nullptr) \ + v(bool, reportCompileTimes, false, "dumps JS function signature and the time it took to compile") \ + v(bool, reportFTLCompileTimes, false, "dumps JS function signature and the time it took to FTL compile") \ + v(bool, reportTotalCompileTimes, false, nullptr) \ + v(bool, verboseCFA, false, nullptr) \ + v(bool, verboseFTLToJSThunk, false, nullptr) \ + v(bool, verboseFTLFailure, false, nullptr) \ + v(bool, alwaysComputeHash, false, nullptr) \ + v(bool, testTheFTL, false, nullptr) \ + v(bool, verboseSanitizeStack, false, nullptr) \ + v(bool, alwaysDoFullCollection, false, nullptr) \ + v(bool, eagerlyUpdateTopCallFrame, false, nullptr) \ + \ + v(bool, enableOSREntryToDFG, true, nullptr) \ + v(bool, enableOSREntryToFTL, true, nullptr) \ + \ + v(bool, useFTLJIT, true, "allows the FTL JIT to be used if true") \ + v(bool, useFTLTBAA, true, nullptr) \ + v(bool, enableLLVMFastISel, false, nullptr) \ + v(bool, useLLVMSmallCodeModel, false, nullptr) \ + v(bool, dumpLLVMIR, false, nullptr) \ + v(bool, validateFTLOSRExitLiveness, false, nullptr) \ + v(bool, llvmAlwaysFailsBeforeCompile, false, nullptr) \ + v(bool, llvmAlwaysFailsBeforeLink, false, nullptr) \ + v(bool, llvmSimpleOpt, true, nullptr) \ + v(unsigned, llvmBackendOptimizationLevel, 2, nullptr) \ + v(unsigned, llvmOptimizationLevel, 2, nullptr) \ + v(unsigned, llvmSizeLevel, 0, nullptr) \ + v(unsigned, llvmMaxStackSize, 128 * KB, nullptr) \ + v(bool, llvmDisallowAVX, true, nullptr) \ + v(bool, ftlCrashes, false, nullptr) /* fool-proof way of checking that you ended up in the FTL. ;-) */\ + v(bool, ftlCrashesIfCantInitializeLLVM, false, nullptr) \ + v(bool, clobberAllRegsInFTLICSlowPath, !ASSERT_DISABLED, nullptr) \ + v(bool, assumeAllRegsInFTLICAreLive, false, nullptr) \ + v(bool, enableAccessInlining, true, nullptr) \ + v(bool, enablePolyvariantDevirtualization, true, nullptr) \ + v(bool, enablePolymorphicAccessInlining, true, nullptr) \ + v(bool, enablePolymorphicCallInlining, true, nullptr) \ + v(unsigned, maxPolymorphicCallVariantListSize, 15, nullptr) \ + v(unsigned, maxPolymorphicCallVariantListSizeForTopTier, 5, nullptr) \ + v(unsigned, maxPolymorphicCallVariantsForInlining, 5, nullptr) \ + v(unsigned, frequentCallThreshold, 2, nullptr) \ + v(double, minimumCallToKnownRate, 0.51, nullptr) \ + v(bool, enableMovHintRemoval, true, nullptr) \ + v(bool, enableObjectAllocationSinking, true, nullptr) \ + \ + v(bool, enableConcurrentJIT, true, "allows the DFG / FTL compilation in threads other than the executing JS thread") \ + v(unsigned, numberOfDFGCompilerThreads, computeNumberOfWorkerThreads(2, 2) - 1, nullptr) \ + v(unsigned, numberOfFTLCompilerThreads, computeNumberOfWorkerThreads(8, 2) - 1, nullptr) \ + v(int32, priorityDeltaOfDFGCompilerThreads, computePriorityDeltaOfWorkerThreads(-1, 0), nullptr) \ + v(int32, priorityDeltaOfFTLCompilerThreads, computePriorityDeltaOfWorkerThreads(-2, 0), nullptr) \ + \ + v(bool, enableProfiler, false, nullptr) \ + \ + v(bool, forceUDis86Disassembler, false, nullptr) \ + v(bool, forceLLVMDisassembler, false, nullptr) \ + \ + v(bool, enableArchitectureSpecificOptimizations, true, nullptr) \ + \ + v(bool, breakOnThrow, false, nullptr) \ + \ + v(unsigned, maximumOptimizationCandidateInstructionCount, 100000, nullptr) \ + \ + v(unsigned, maximumFunctionForCallInlineCandidateInstructionCount, 180, nullptr) \ + v(unsigned, maximumFunctionForClosureCallInlineCandidateInstructionCount, 100, nullptr) \ + v(unsigned, maximumFunctionForConstructInlineCandidateInstructionCount, 100, nullptr) \ + \ + v(unsigned, maximumFTLCandidateInstructionCount, 20000, nullptr) \ + \ + /* Depth of inline stack, so 1 = no inlining, 2 = one level, etc. */ \ + v(unsigned, maximumInliningDepth, 5, "maximum allowed inlining depth. Depth of 1 means no inlining") \ + v(unsigned, maximumInliningRecursion, 2, nullptr) \ + \ + v(unsigned, maximumLLVMInstructionCountForNativeInlining, 80, nullptr) \ + \ + /* Maximum size of a caller for enabling inlining. This is purely to protect us */\ + /* from super long compiles that take a lot of memory. */\ + v(unsigned, maximumInliningCallerSize, 10000, nullptr) \ + \ + v(unsigned, maximumVarargsForInlining, 100, nullptr) \ + \ + v(bool, enablePolyvariantCallInlining, true, nullptr) \ + v(bool, enablePolyvariantByIdInlining, true, nullptr) \ + \ + v(unsigned, maximumBinaryStringSwitchCaseLength, 50, nullptr) \ + v(unsigned, maximumBinaryStringSwitchTotalLength, 2000, nullptr) \ + \ + v(double, jitPolicyScale, 1.0, "scale JIT thresholds to this specified ratio between 0.0 (compile ASAP) and 1.0 (compile like normal).") \ + v(bool, forceEagerCompilation, false, nullptr) \ + v(int32, thresholdForJITAfterWarmUp, 500, nullptr) \ + v(int32, thresholdForJITSoon, 100, nullptr) \ + \ + v(int32, thresholdForOptimizeAfterWarmUp, 1000, nullptr) \ + v(int32, thresholdForOptimizeAfterLongWarmUp, 1000, nullptr) \ + v(int32, thresholdForOptimizeSoon, 1000, nullptr) \ + v(int32, executionCounterIncrementForLoop, 1, nullptr) \ + v(int32, executionCounterIncrementForEntry, 15, nullptr) \ + \ + v(int32, thresholdForFTLOptimizeAfterWarmUp, 100000, nullptr) \ + v(int32, thresholdForFTLOptimizeSoon, 1000, nullptr) \ + v(int32, ftlTierUpCounterIncrementForLoop, 1, nullptr) \ + v(int32, ftlTierUpCounterIncrementForReturn, 15, nullptr) \ + v(unsigned, ftlOSREntryFailureCountForReoptimization, 15, nullptr) \ + v(unsigned, ftlOSREntryRetryThreshold, 100, nullptr) \ + \ + v(int32, evalThresholdMultiplier, 10, nullptr) \ + v(unsigned, maximumEvalCacheableSourceLength, 256, nullptr) \ + \ + v(bool, randomizeExecutionCountsBetweenCheckpoints, false, nullptr) \ + v(int32, maximumExecutionCountsBetweenCheckpointsForBaseline, 1000, nullptr) \ + v(int32, maximumExecutionCountsBetweenCheckpointsForUpperTiers, 50000, nullptr) \ + \ + v(unsigned, likelyToTakeSlowCaseMinimumCount, 20, nullptr) \ + v(unsigned, couldTakeSlowCaseMinimumCount, 10, nullptr) \ + \ + v(unsigned, osrExitCountForReoptimization, 100, nullptr) \ + v(unsigned, osrExitCountForReoptimizationFromLoop, 5, nullptr) \ + \ + v(unsigned, reoptimizationRetryCounterMax, 0, nullptr) \ + \ + v(unsigned, minimumOptimizationDelay, 1, nullptr) \ + v(unsigned, maximumOptimizationDelay, 5, nullptr) \ + v(double, desiredProfileLivenessRate, 0.75, nullptr) \ + v(double, desiredProfileFullnessRate, 0.35, nullptr) \ + \ + v(double, doubleVoteRatioForDoubleFormat, 2, nullptr) \ + v(double, structureCheckVoteRatioForHoisting, 1, nullptr) \ + v(double, checkArrayVoteRatioForHoisting, 1, nullptr) \ + \ + v(unsigned, minimumNumberOfScansBetweenRebalance, 100, nullptr) \ + v(unsigned, numberOfGCMarkers, computeNumberOfGCMarkers(7), nullptr) \ + v(unsigned, opaqueRootMergeThreshold, 1000, nullptr) \ + v(double, minHeapUtilization, 0.8, nullptr) \ + v(double, minCopiedBlockUtilization, 0.9, nullptr) \ + v(double, minMarkedBlockUtilization, 0.9, nullptr) \ + v(unsigned, slowPathAllocsBetweenGCs, 0, "force a GC on every Nth slow path alloc, where N is specified by this option") \ + \ + v(double, percentCPUPerMBForFullTimer, 0.0003125, nullptr) \ + v(double, percentCPUPerMBForEdenTimer, 0.0025, nullptr) \ + v(double, collectionTimerMaxPercentCPU, 0.05, nullptr) \ + \ + v(bool, forceWeakRandomSeed, false, nullptr) \ + v(unsigned, forcedWeakRandomSeed, 0, nullptr) \ + \ + v(bool, useZombieMode, false, "debugging option to scribble over dead objects with 0xdeadbeef") \ + v(bool, objectsAreImmortal, false, "debugging option to keep all objects alive forever") \ + v(bool, showObjectStatistics, false, nullptr) \ + \ + v(gcLogLevel, logGC, GCLogging::None, "debugging option to log GC activity (0 = None, 1 = Basic, 2 = Verbose)") \ + v(bool, disableGC, false, nullptr) \ + v(unsigned, gcMaxHeapSize, 0, nullptr) \ + v(unsigned, forceRAMSize, 0, nullptr) \ + v(bool, recordGCPauseTimes, false, nullptr) \ + v(bool, logHeapStatisticsAtExit, false, nullptr) \ + v(bool, enableTypeProfiler, false, nullptr) \ + v(bool, enableControlFlowProfiler, false, nullptr) \ + \ + v(bool, verifyHeap, false, nullptr) \ + v(unsigned, numberOfGCCyclesToRecordForVerification, 3, nullptr) \ + \ + v(bool, enableExceptionFuzz, false, nullptr) \ + v(unsigned, fireExceptionFuzzAt, 0, nullptr) \ + \ + v(bool, enableExecutableAllocationFuzz, false, nullptr) \ + v(unsigned, fireExecutableAllocationFuzzAt, 0, nullptr) \ + v(unsigned, fireExecutableAllocationFuzzAtOrAfter, 0, nullptr) \ + v(bool, verboseExecutableAllocationFuzz, false, nullptr) \ + \ + v(bool, enableOSRExitFuzz, false, nullptr) \ + v(unsigned, fireOSRExitFuzzAtStatic, 0, nullptr) \ + v(unsigned, fireOSRExitFuzzAt, 0, nullptr) \ + v(unsigned, fireOSRExitFuzzAtOrAfter, 0, nullptr) \ + \ + v(bool, enableDollarVM, false, "installs the $vm debugging tool in global objects") \ + v(optionString, functionOverrides, nullptr, "file with debugging overrides for function bodies") \ + \ + v(unsigned, watchdog, 0, "watchdog timeout (0 = Disabled, N = a timeout period of N milliseconds)") \ + \ + v(bool, dumpModuleRecord, false, nullptr) \ + +class Options { +public: + enum class DumpLevel { + None = 0, + Overridden, + All, + Verbose + }; + + // This typedef is to allow us to eliminate the '_' in the field name in + // union inside Entry. This is needed to keep the style checker happy. + typedef int32_t int32; + + // Declare the option IDs: + enum OptionID { +#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \ + name_##ID, + JSC_OPTIONS(FOR_EACH_OPTION) +#undef FOR_EACH_OPTION + numberOfOptions + }; + + enum class Type { + boolType, + unsignedType, + doubleType, + int32Type, + optionRangeType, + optionStringType, + gcLogLevelType, + }; + + JS_EXPORT_PRIVATE static void initialize(); + + // Parses a string of options where each option is of the format "--<optionName>=<value>" + // and are separated by a space. The leading "--" is optional and will be ignored. + JS_EXPORT_PRIVATE static bool setOptions(const char* optionsList); + + // Parses a single command line option in the format "<optionName>=<value>" + // (no spaces allowed) and set the specified option if appropriate. + JS_EXPORT_PRIVATE static bool setOption(const char* arg); + + JS_EXPORT_PRIVATE static void dumpAllOptions(FILE*, DumpLevel, const char* title = nullptr); + JS_EXPORT_PRIVATE static void dumpAllOptionsInALine(StringBuilder&); + + JS_EXPORT_PRIVATE static void ensureOptionsAreCoherent(); + + // Declare accessors for each option: +#define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \ + ALWAYS_INLINE static type_& name_() { return s_options[name_##ID].type_##Val; } \ + ALWAYS_INLINE static type_& name_##Default() { return s_defaultOptions[name_##ID].type_##Val; } + + JSC_OPTIONS(FOR_EACH_OPTION) +#undef FOR_EACH_OPTION + +private: + // For storing for an option value: + union Entry { + bool boolVal; + unsigned unsignedVal; + double doubleVal; + int32 int32Val; + OptionRange optionRangeVal; + const char* optionStringVal; + GCLogging::Level gcLogLevelVal; + }; + + // For storing constant meta data about each option: + struct EntryInfo { + const char* name; + const char* description; + Type type; + }; + + Options(); + + enum ShowDefaultsOption { + DontShowDefaults, + ShowDefaults + }; + static void dumpOptionsIfNeeded(); + static void dumpAllOptions(StringBuilder&, DumpLevel, const char* title, + const char* separator, const char* optionHeader, const char* optionFooter, ShowDefaultsOption); + static void dumpOption(StringBuilder&, DumpLevel, OptionID, + const char* optionHeader, const char* optionFooter, ShowDefaultsOption); + + // Declare the singleton instance of the options store: + JS_EXPORTDATA static Entry s_options[numberOfOptions]; + static Entry s_defaultOptions[numberOfOptions]; + static const EntryInfo s_optionsInfo[numberOfOptions]; + + friend class Option; +}; + +class Option { +public: + Option(Options::OptionID id) + : m_id(id) + , m_entry(Options::s_options[m_id]) + { + } + + void dump(StringBuilder&) const; + + bool operator==(const Option& other) const; + bool operator!=(const Option& other) const { return !(*this == other); } + + const char* name() const; + const char* description() const; + Options::Type type() const; + bool isOverridden() const; + const Option defaultOption() const; + + bool& boolVal(); + unsigned& unsignedVal(); + double& doubleVal(); + int32_t& int32Val(); + OptionRange optionRangeVal(); + const char* optionStringVal(); + GCLogging::Level& gcLogLevelVal(); + +private: + // Only used for constructing default Options. + Option(Options::OptionID id, Options::Entry& entry) + : m_id(id) + , m_entry(entry) + { + } + + Options::OptionID m_id; + Options::Entry& m_entry; +}; + +inline const char* Option::name() const +{ + return Options::s_optionsInfo[m_id].name; +} + +inline const char* Option::description() const +{ + return Options::s_optionsInfo[m_id].description; +} + +inline Options::Type Option::type() const +{ + return Options::s_optionsInfo[m_id].type; +} + +inline bool Option::isOverridden() const +{ + return *this != defaultOption(); +} + +inline const Option Option::defaultOption() const +{ + return Option(m_id, Options::s_defaultOptions[m_id]); +} + +inline bool& Option::boolVal() +{ + return m_entry.boolVal; +} + +inline unsigned& Option::unsignedVal() +{ + return m_entry.unsignedVal; +} + +inline double& Option::doubleVal() +{ + return m_entry.doubleVal; +} + +inline int32_t& Option::int32Val() +{ + return m_entry.int32Val; +} + +inline OptionRange Option::optionRangeVal() +{ + return m_entry.optionRangeVal; +} + +inline const char* Option::optionStringVal() +{ + return m_entry.optionStringVal; +} + +inline GCLogging::Level& Option::gcLogLevelVal() +{ + return m_entry.gcLogLevelVal; +} + +} // namespace JSC + +#endif // Options_h diff --git a/Source/JavaScriptCore/runtime/PrivateName.h b/Source/JavaScriptCore/runtime/PrivateName.h new file mode 100644 index 000000000..2b6ba017d --- /dev/null +++ b/Source/JavaScriptCore/runtime/PrivateName.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef PrivateName_h +#define PrivateName_h + +#include <wtf/text/SymbolImpl.h> + +namespace JSC { + +class PrivateName { +public: + PrivateName() + : m_uid(StringImpl::createSymbolEmpty()) + { + } + + explicit PrivateName(SymbolImpl& uid) + : m_uid(&uid) + { + } + + enum DescriptionTag { Description }; + explicit PrivateName(DescriptionTag, const String& description) + : m_uid(StringImpl::createSymbol(description.impl())) + { + } + + SymbolImpl* uid() const { return m_uid.get(); } + + bool operator==(const PrivateName& other) const { return uid() == other.uid(); } + bool operator!=(const PrivateName& other) const { return uid() != other.uid(); } + +private: + RefPtr<SymbolImpl> m_uid; +}; + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp new file mode 100644 index 000000000..5722e88da --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" + +#include "PropertyDescriptor.h" + +#include "GetterSetter.h" +#include "JSObject.h" +#include "JSCInlines.h" + +namespace JSC { +unsigned PropertyDescriptor::defaultAttributes = DontDelete | DontEnum | ReadOnly; + +bool PropertyDescriptor::writable() const +{ + ASSERT(!isAccessorDescriptor()); + return !(m_attributes & ReadOnly); +} + +bool PropertyDescriptor::enumerable() const +{ + return !(m_attributes & DontEnum); +} + +bool PropertyDescriptor::configurable() const +{ + return !(m_attributes & DontDelete); +} + +bool PropertyDescriptor::isDataDescriptor() const +{ + return m_value || (m_seenAttributes & WritablePresent); +} + +bool PropertyDescriptor::isGenericDescriptor() const +{ + return !isAccessorDescriptor() && !isDataDescriptor(); +} + +bool PropertyDescriptor::isAccessorDescriptor() const +{ + return m_getter || m_setter; +} + +void PropertyDescriptor::setUndefined() +{ + m_value = jsUndefined(); + m_attributes = ReadOnly | DontDelete | DontEnum; +} + +JSValue PropertyDescriptor::getter() const +{ + ASSERT(isAccessorDescriptor()); + return m_getter; +} + +JSValue PropertyDescriptor::setter() const +{ + ASSERT(isAccessorDescriptor()); + return m_setter; +} + +JSObject* PropertyDescriptor::getterObject() const +{ + ASSERT(isAccessorDescriptor() && getterPresent()); + return m_getter.isObject() ? asObject(m_getter) : 0; +} + +JSObject* PropertyDescriptor::setterObject() const +{ + ASSERT(isAccessorDescriptor() && setterPresent()); + return m_setter.isObject() ? asObject(m_setter) : 0; +} + +void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes) +{ + ASSERT(value); + ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); + + m_attributes = attributes; + if (value.isGetterSetter()) { + m_attributes &= ~ReadOnly; // FIXME: we should be able to ASSERT this! + + GetterSetter* accessor = asGetterSetter(value); + m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined(); + m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined(); + m_seenAttributes = EnumerablePresent | ConfigurablePresent; + } else { + m_value = value; + m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent; + } +} + +void PropertyDescriptor::setCustomDescriptor(unsigned attributes) +{ + m_attributes = attributes | Accessor | CustomAccessor; + m_attributes &= ~ReadOnly; + m_seenAttributes = EnumerablePresent | ConfigurablePresent; + setGetter(jsUndefined()); + setSetter(jsUndefined()); + m_value = JSValue(); +} + +void PropertyDescriptor::setAccessorDescriptor(GetterSetter* accessor, unsigned attributes) +{ + ASSERT(attributes & Accessor); + attributes &= ~ReadOnly; // FIXME: we should be able to ASSERT this! + + m_attributes = attributes; + m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined(); + m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined(); + m_seenAttributes = EnumerablePresent | ConfigurablePresent; +} + +void PropertyDescriptor::setWritable(bool writable) +{ + if (writable) + m_attributes &= ~ReadOnly; + else + m_attributes |= ReadOnly; + m_seenAttributes |= WritablePresent; +} + +void PropertyDescriptor::setEnumerable(bool enumerable) +{ + if (enumerable) + m_attributes &= ~DontEnum; + else + m_attributes |= DontEnum; + m_seenAttributes |= EnumerablePresent; +} + +void PropertyDescriptor::setConfigurable(bool configurable) +{ + if (configurable) + m_attributes &= ~DontDelete; + else + m_attributes |= DontDelete; + m_seenAttributes |= ConfigurablePresent; +} + +void PropertyDescriptor::setSetter(JSValue setter) +{ + m_setter = setter; + m_attributes |= Accessor; + m_attributes &= ~ReadOnly; +} + +void PropertyDescriptor::setGetter(JSValue getter) +{ + m_getter = getter; + m_attributes |= Accessor; + m_attributes &= ~ReadOnly; +} + +// See ES5.1 9.12 +bool sameValue(ExecState* exec, JSValue a, JSValue b) +{ + if (!a.isNumber()) + return JSValue::strictEqual(exec, a, b); + if (!b.isNumber()) + return false; + double x = a.asNumber(); + double y = b.asNumber(); + bool xIsNaN = std::isnan(x); + bool yIsNaN = std::isnan(y); + if (xIsNaN || yIsNaN) + return xIsNaN && yIsNaN; + return bitwise_cast<uint64_t>(x) == bitwise_cast<uint64_t>(y); +} + +bool PropertyDescriptor::equalTo(ExecState* exec, const PropertyDescriptor& other) const +{ + if (other.m_value.isEmpty() != m_value.isEmpty() + || other.m_getter.isEmpty() != m_getter.isEmpty() + || other.m_setter.isEmpty() != m_setter.isEmpty()) + return false; + return (!m_value || sameValue(exec, other.m_value, m_value)) + && (!m_getter || JSValue::strictEqual(exec, other.m_getter, m_getter)) + && (!m_setter || JSValue::strictEqual(exec, other.m_setter, m_setter)) + && attributesEqual(other); +} + +bool PropertyDescriptor::attributesEqual(const PropertyDescriptor& other) const +{ + unsigned mismatch = other.m_attributes ^ m_attributes; + unsigned sharedSeen = other.m_seenAttributes & m_seenAttributes; + if (sharedSeen & WritablePresent && mismatch & ReadOnly) + return false; + if (sharedSeen & ConfigurablePresent && mismatch & DontDelete) + return false; + if (sharedSeen & EnumerablePresent && mismatch & DontEnum) + return false; + return true; +} + +unsigned PropertyDescriptor::attributesOverridingCurrent(const PropertyDescriptor& current) const +{ + unsigned currentAttributes = current.m_attributes; + if (isDataDescriptor() && current.isAccessorDescriptor()) + currentAttributes |= ReadOnly; + unsigned overrideMask = 0; + if (writablePresent()) + overrideMask |= ReadOnly; + if (enumerablePresent()) + overrideMask |= DontEnum; + if (configurablePresent()) + overrideMask |= DontDelete; + if (isAccessorDescriptor()) + overrideMask |= Accessor; + return (m_attributes & overrideMask) | (currentAttributes & ~overrideMask); +} + +} diff --git a/Source/JavaScriptCore/runtime/PropertyDescriptor.h b/Source/JavaScriptCore/runtime/PropertyDescriptor.h new file mode 100644 index 000000000..0345b4e4a --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyDescriptor.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PropertyDescriptor_h +#define PropertyDescriptor_h + +#include "JSCJSValue.h" + +namespace JSC { + +class GetterSetter; + +// See ES5.1 9.12 +bool sameValue(ExecState*, JSValue, JSValue); + +class PropertyDescriptor { +public: + PropertyDescriptor() + : m_attributes(defaultAttributes) + , m_seenAttributes(0) + { + } + PropertyDescriptor(JSValue value, unsigned attributes) + : m_value(value) + , m_attributes(attributes) + , m_seenAttributes(EnumerablePresent | ConfigurablePresent | WritablePresent) + { + ASSERT(m_value); + ASSERT(!m_value.isGetterSetter()); + ASSERT(!m_value.isCustomGetterSetter()); + } + JS_EXPORT_PRIVATE bool writable() const; + JS_EXPORT_PRIVATE bool enumerable() const; + JS_EXPORT_PRIVATE bool configurable() const; + JS_EXPORT_PRIVATE bool isDataDescriptor() const; + bool isGenericDescriptor() const; + JS_EXPORT_PRIVATE bool isAccessorDescriptor() const; + unsigned attributes() const { return m_attributes; } + JSValue value() const { return m_value; } + JS_EXPORT_PRIVATE JSValue getter() const; + JS_EXPORT_PRIVATE JSValue setter() const; + JSObject* getterObject() const; + JSObject* setterObject() const; + JS_EXPORT_PRIVATE void setUndefined(); + JS_EXPORT_PRIVATE void setDescriptor(JSValue, unsigned attributes); + JS_EXPORT_PRIVATE void setCustomDescriptor(unsigned attributes); + JS_EXPORT_PRIVATE void setAccessorDescriptor(GetterSetter* accessor, unsigned attributes); + JS_EXPORT_PRIVATE void setWritable(bool); + JS_EXPORT_PRIVATE void setEnumerable(bool); + JS_EXPORT_PRIVATE void setConfigurable(bool); + void setValue(JSValue value) { m_value = value; } + JS_EXPORT_PRIVATE void setSetter(JSValue); + JS_EXPORT_PRIVATE void setGetter(JSValue); + bool isEmpty() const { return !(m_value || m_getter || m_setter || m_seenAttributes); } + bool writablePresent() const { return m_seenAttributes & WritablePresent; } + bool enumerablePresent() const { return m_seenAttributes & EnumerablePresent; } + bool configurablePresent() const { return m_seenAttributes & ConfigurablePresent; } + bool setterPresent() const { return !!m_setter; } + bool getterPresent() const { return !!m_getter; } + bool equalTo(ExecState*, const PropertyDescriptor& other) const; + bool attributesEqual(const PropertyDescriptor& other) const; + unsigned attributesOverridingCurrent(const PropertyDescriptor& current) const; + +private: + JS_EXPORTDATA static unsigned defaultAttributes; + bool operator==(const PropertyDescriptor&) { return false; } + enum { WritablePresent = 1, EnumerablePresent = 2, ConfigurablePresent = 4}; + // May be a getter/setter + JSValue m_value; + JSValue m_getter; + JSValue m_setter; + unsigned m_attributes; + unsigned m_seenAttributes; +}; + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/PropertyMapHashTable.h b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h new file mode 100644 index 000000000..b068b991d --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2014 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef PropertyMapHashTable_h +#define PropertyMapHashTable_h + +#include "JSExportMacros.h" +#include "PropertyOffset.h" +#include "Structure.h" +#include "WriteBarrier.h" +#include <wtf/CryptographicallyRandomNumber.h> +#include <wtf/HashTable.h> +#include <wtf/MathExtras.h> +#include <wtf/Vector.h> +#include <wtf/text/AtomicStringImpl.h> + + +#define DUMP_PROPERTYMAP_STATS 0 +#define DUMP_PROPERTYMAP_COLLISIONS 0 + +#define PROPERTY_MAP_DELETED_ENTRY_KEY ((UniquedStringImpl*)1) + +namespace JSC { + +#if DUMP_PROPERTYMAP_STATS + +struct PropertyMapHashTableStats { + std::atomic<unsigned> numFinds; + std::atomic<unsigned> numCollisions; + std::atomic<unsigned> numLookups; + std::atomic<unsigned> numLookupProbing; + std::atomic<unsigned> numAdds; + std::atomic<unsigned> numRemoves; + std::atomic<unsigned> numRehashes; + std::atomic<unsigned> numReinserts; +}; + +JS_EXPORTDATA extern PropertyMapHashTableStats* propertyMapHashTableStats; + +#endif + +inline bool isPowerOf2(unsigned v) +{ + return hasOneBitSet(v); +} + +inline unsigned nextPowerOf2(unsigned v) +{ + // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html + // Devised by Sean Anderson, Sepember 14, 2001 + + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + return v; +} + +class PropertyTable final : public JSCell { + + // This is the implementation for 'iterator' and 'const_iterator', + // used for iterating over the table in insertion order. + template<typename T> + class ordered_iterator { + public: + ordered_iterator<T>& operator++() + { + m_valuePtr = skipDeletedEntries(m_valuePtr + 1); + return *this; + } + + bool operator==(const ordered_iterator<T>& other) + { + return m_valuePtr == other.m_valuePtr; + } + + bool operator!=(const ordered_iterator<T>& other) + { + return m_valuePtr != other.m_valuePtr; + } + + T& operator*() + { + return *m_valuePtr; + } + + T* operator->() + { + return m_valuePtr; + } + + ordered_iterator(T* valuePtr) + : m_valuePtr(valuePtr) + { + } + + private: + T* m_valuePtr; + }; + +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); + } + + typedef UniquedStringImpl* KeyType; + typedef PropertyMapEntry ValueType; + + // The in order iterator provides overloaded * and -> to access the Value at the current position. + typedef ordered_iterator<ValueType> iterator; + typedef ordered_iterator<const ValueType> const_iterator; + + // The find_iterator is a pair of a pointer to a Value* an the entry in the index. + // If 'find' does not find an entry then iter.first will be 0, and iter.second will + // give the point in m_index where an entry should be inserted. + typedef std::pair<ValueType*, unsigned> find_iterator; + + // Constructor is passed an initial capacity, a PropertyTable to copy, or both. + static PropertyTable* create(VM&, unsigned initialCapacity); + static PropertyTable* clone(VM&, const PropertyTable&); + static PropertyTable* clone(VM&, unsigned initialCapacity, const PropertyTable&); + ~PropertyTable(); + + // Ordered iteration methods. + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + // Find a value in the table. + find_iterator find(const KeyType&); + ValueType* get(const KeyType&); + // Add a value to the table + enum EffectOnPropertyOffset { PropertyOffsetMayChange, PropertyOffsetMustNotChange }; + std::pair<find_iterator, bool> add(const ValueType& entry, PropertyOffset&, EffectOnPropertyOffset); + // Remove a value from the table. + void remove(const find_iterator& iter); + void remove(const KeyType& key); + + // Returns the number of values in the hashtable. + unsigned size() const; + + // Checks if there are any values in the hashtable. + bool isEmpty() const; + + // Number of slots in the property storage array in use, included deletedOffsets. + unsigned propertyStorageSize() const; + + // Used to maintain a list of unused entries in the property storage. + void clearDeletedOffsets(); + bool hasDeletedOffset(); + PropertyOffset getDeletedOffset(); + void addDeletedOffset(PropertyOffset); + + PropertyOffset nextOffset(PropertyOffset inlineCapacity); + + // Copy this PropertyTable, ensuring the copy has at least the capacity provided. + PropertyTable* copy(VM&, unsigned newCapacity); + +#ifndef NDEBUG + size_t sizeInMemory(); + void checkConsistency(); +#endif + +private: + PropertyTable(VM&, unsigned initialCapacity); + PropertyTable(VM&, const PropertyTable&); + PropertyTable(VM&, unsigned initialCapacity, const PropertyTable&); + + PropertyTable(const PropertyTable&); + // Used to insert a value known not to be in the table, and where we know capacity to be available. + void reinsert(const ValueType& entry); + + // Rehash the table. Used to grow, or to recover deleted slots. + void rehash(unsigned newCapacity); + + // The capacity of the table of values is half of the size of the index. + unsigned tableCapacity() const; + + // We keep an extra deleted slot after the array to make iteration work, + // and to use for deleted values. Index values into the array are 1-based, + // so this is tableCapacity() + 1. + // For example, if m_tableSize is 16, then tableCapacity() is 8 - but the + // values array is actually 9 long (the 9th used for the deleted value/ + // iteration guard). The 8 valid entries are numbered 1..8, so the + // deleted index is 9 (0 being reserved for empty). + unsigned deletedEntryIndex() const; + + // Used in iterator creation/progression. + template<typename T> + static T* skipDeletedEntries(T* valuePtr); + + // The table of values lies after the hash index. + ValueType* table(); + const ValueType* table() const; + + // total number of used entries in the values array - by either valid entries, or deleted ones. + unsigned usedCount() const; + + // The size in bytes of data needed for by the table. + size_t dataSize(); + + // Calculates the appropriate table size (rounds up to a power of two). + static unsigned sizeForCapacity(unsigned capacity); + + // Check if capacity is available. + bool canInsert(); + + unsigned m_indexSize; + unsigned m_indexMask; + unsigned* m_index; + unsigned m_keyCount; + unsigned m_deletedCount; + std::unique_ptr<Vector<PropertyOffset>> m_deletedOffsets; + + static const unsigned MinimumTableSize = 16; + static const unsigned EmptyEntryIndex = 0; +}; + +inline PropertyTable::iterator PropertyTable::begin() +{ + return iterator(skipDeletedEntries(table())); +} + +inline PropertyTable::iterator PropertyTable::end() +{ + return iterator(table() + usedCount()); +} + +inline PropertyTable::const_iterator PropertyTable::begin() const +{ + return const_iterator(skipDeletedEntries(table())); +} + +inline PropertyTable::const_iterator PropertyTable::end() const +{ + return const_iterator(table() + usedCount()); +} + +inline PropertyTable::find_iterator PropertyTable::find(const KeyType& key) +{ + ASSERT(key); + ASSERT(key->isAtomic() || key->isSymbol()); + unsigned hash = IdentifierRepHash::hash(key); + unsigned step = 0; + +#if DUMP_PROPERTYMAP_STATS + ++propertyMapHashTableStats->numFinds; +#endif + + while (true) { + unsigned entryIndex = m_index[hash & m_indexMask]; + if (entryIndex == EmptyEntryIndex) + return std::make_pair((ValueType*)0, hash & m_indexMask); + if (key == table()[entryIndex - 1].key) + return std::make_pair(&table()[entryIndex - 1], hash & m_indexMask); + + if (!step) + step = WTF::doubleHash(IdentifierRepHash::hash(key)) | 1; + +#if DUMP_PROPERTYMAP_STATS + ++propertyMapHashTableStats->numCollisions; +#endif + +#if DUMP_PROPERTYMAP_COLLISIONS + dataLog("PropertyTable collision for ", key, " (", hash, ") with step ", step, "\n"); + dataLog("Collided with ", table()[entryIndex - 1].key, "(", IdentifierRepHash::hash(table()[entryIndex - 1].key), ")\n"); +#endif + + hash += step; + } +} + +inline PropertyTable::ValueType* PropertyTable::get(const KeyType& key) +{ + ASSERT(key); + ASSERT(key->isAtomic() || key->isSymbol()); + + if (!m_keyCount) + return nullptr; + + unsigned hash = IdentifierRepHash::hash(key); + unsigned step = 0; + +#if DUMP_PROPERTYMAP_STATS + ++propertyMapHashTableStats->numLookups; +#endif + + while (true) { + unsigned entryIndex = m_index[hash & m_indexMask]; + if (entryIndex == EmptyEntryIndex) + return nullptr; + if (key == table()[entryIndex - 1].key) + return &table()[entryIndex - 1]; + +#if DUMP_PROPERTYMAP_STATS + ++propertyMapHashTableStats->numLookupProbing; +#endif + + if (!step) + step = WTF::doubleHash(IdentifierRepHash::hash(key)) | 1; + hash += step; + } +} + +inline std::pair<PropertyTable::find_iterator, bool> PropertyTable::add(const ValueType& entry, PropertyOffset& offset, EffectOnPropertyOffset offsetEffect) +{ + // Look for a value with a matching key already in the array. + find_iterator iter = find(entry.key); + if (iter.first) { + RELEASE_ASSERT(iter.first->offset <= offset); + return std::make_pair(iter, false); + } + +#if DUMP_PROPERTYMAP_STATS + ++propertyMapHashTableStats->numAdds; +#endif + + // Ref the key + entry.key->ref(); + + // ensure capacity is available. + if (!canInsert()) { + rehash(m_keyCount + 1); + iter = find(entry.key); + ASSERT(!iter.first); + } + + // Allocate a slot in the hashtable, and set the index to reference this. + unsigned entryIndex = usedCount() + 1; + m_index[iter.second] = entryIndex; + iter.first = &table()[entryIndex - 1]; + *iter.first = entry; + + ++m_keyCount; + + if (offsetEffect == PropertyOffsetMayChange) + offset = std::max(offset, entry.offset); + else + RELEASE_ASSERT(offset >= entry.offset); + + return std::make_pair(iter, true); +} + +inline void PropertyTable::remove(const find_iterator& iter) +{ + // Removing a key that doesn't exist does nothing! + if (!iter.first) + return; + +#if DUMP_PROPERTYMAP_STATS + ++propertyMapHashTableStats->numRemoves; +#endif + + // Replace this one element with the deleted sentinel. Also clear out + // the entry so we can iterate all the entries as needed. + m_index[iter.second] = deletedEntryIndex(); + iter.first->key->deref(); + iter.first->key = PROPERTY_MAP_DELETED_ENTRY_KEY; + + ASSERT(m_keyCount >= 1); + --m_keyCount; + ++m_deletedCount; + + if (m_deletedCount * 4 >= m_indexSize) + rehash(m_keyCount); +} + +inline void PropertyTable::remove(const KeyType& key) +{ + remove(find(key)); +} + +// returns the number of values in the hashtable. +inline unsigned PropertyTable::size() const +{ + return m_keyCount; +} + +inline bool PropertyTable::isEmpty() const +{ + return !m_keyCount; +} + +inline unsigned PropertyTable::propertyStorageSize() const +{ + return size() + (m_deletedOffsets ? m_deletedOffsets->size() : 0); +} + +inline void PropertyTable::clearDeletedOffsets() +{ + m_deletedOffsets = nullptr; +} + +inline bool PropertyTable::hasDeletedOffset() +{ + return m_deletedOffsets && !m_deletedOffsets->isEmpty(); +} + +inline PropertyOffset PropertyTable::getDeletedOffset() +{ + PropertyOffset offset = m_deletedOffsets->last(); + m_deletedOffsets->removeLast(); + return offset; +} + +inline void PropertyTable::addDeletedOffset(PropertyOffset offset) +{ + if (!m_deletedOffsets) + m_deletedOffsets = std::make_unique<Vector<PropertyOffset>>(); + m_deletedOffsets->append(offset); +} + +inline PropertyOffset PropertyTable::nextOffset(PropertyOffset inlineCapacity) +{ + if (hasDeletedOffset()) + return getDeletedOffset(); + + return offsetForPropertyNumber(size(), inlineCapacity); +} + +inline PropertyTable* PropertyTable::copy(VM& vm, unsigned newCapacity) +{ + ASSERT(newCapacity >= m_keyCount); + + // Fast case; if the new table will be the same m_indexSize as this one, we can memcpy it, + // save rehashing all keys. + if (sizeForCapacity(newCapacity) == m_indexSize) + return PropertyTable::clone(vm, *this); + return PropertyTable::clone(vm, newCapacity, *this); +} + +#ifndef NDEBUG +inline size_t PropertyTable::sizeInMemory() +{ + size_t result = sizeof(PropertyTable) + dataSize(); + if (m_deletedOffsets) + result += (m_deletedOffsets->capacity() * sizeof(PropertyOffset)); + return result; +} +#endif + +inline void PropertyTable::reinsert(const ValueType& entry) +{ +#if DUMP_PROPERTYMAP_STATS + ++propertyMapHashTableStats->numReinserts; +#endif + + // Used to insert a value known not to be in the table, and where + // we know capacity to be available. + ASSERT(canInsert()); + find_iterator iter = find(entry.key); + ASSERT(!iter.first); + + unsigned entryIndex = usedCount() + 1; + m_index[iter.second] = entryIndex; + table()[entryIndex - 1] = entry; + + ++m_keyCount; +} + +inline void PropertyTable::rehash(unsigned newCapacity) +{ +#if DUMP_PROPERTYMAP_STATS + ++propertyMapHashTableStats->numRehashes; +#endif + + unsigned* oldEntryIndices = m_index; + iterator iter = this->begin(); + iterator end = this->end(); + + m_indexSize = sizeForCapacity(newCapacity); + m_indexMask = m_indexSize - 1; + m_keyCount = 0; + m_deletedCount = 0; + m_index = static_cast<unsigned*>(fastZeroedMalloc(dataSize())); + + for (; iter != end; ++iter) { + ASSERT(canInsert()); + reinsert(*iter); + } + + fastFree(oldEntryIndices); +} + +inline unsigned PropertyTable::tableCapacity() const { return m_indexSize >> 1; } + +inline unsigned PropertyTable::deletedEntryIndex() const { return tableCapacity() + 1; } + +template<typename T> +inline T* PropertyTable::skipDeletedEntries(T* valuePtr) +{ + while (valuePtr->key == PROPERTY_MAP_DELETED_ENTRY_KEY) + ++valuePtr; + return valuePtr; +} + +inline PropertyTable::ValueType* PropertyTable::table() +{ + // The table of values lies after the hash index. + return reinterpret_cast<ValueType*>(m_index + m_indexSize); +} + +inline const PropertyTable::ValueType* PropertyTable::table() const +{ + // The table of values lies after the hash index. + return reinterpret_cast<const ValueType*>(m_index + m_indexSize); +} + +inline unsigned PropertyTable::usedCount() const +{ + // Total number of used entries in the values array - by either valid entries, or deleted ones. + return m_keyCount + m_deletedCount; +} + +inline size_t PropertyTable::dataSize() +{ + // The size in bytes of data needed for by the table. + return m_indexSize * sizeof(unsigned) + ((tableCapacity()) + 1) * sizeof(ValueType); +} + +inline unsigned PropertyTable::sizeForCapacity(unsigned capacity) +{ + if (capacity < MinimumTableSize / 2) + return MinimumTableSize; + return nextPowerOf2(capacity + 1) * 2; +} + +inline bool PropertyTable::canInsert() +{ + return usedCount() < tableCapacity(); +} + +} // namespace JSC + +#endif // PropertyMapHashTable_h diff --git a/Source/JavaScriptCore/runtime/PropertyName.h b/Source/JavaScriptCore/runtime/PropertyName.h new file mode 100644 index 000000000..eaba62804 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyName.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef PropertyName_h +#define PropertyName_h + +#include "Identifier.h" +#include "PrivateName.h" +#include <wtf/Optional.h> + +namespace JSC { + +class PropertyName { +public: + PropertyName(UniquedStringImpl* propertyName) + : m_impl(propertyName) + { + } + + PropertyName(const Identifier& propertyName) + : PropertyName(propertyName.impl()) + { + } + + PropertyName(const PrivateName& propertyName) + : m_impl(propertyName.uid()) + { + ASSERT(m_impl); + ASSERT(m_impl->isSymbol()); + } + + bool isSymbol() + { + return m_impl && m_impl->isSymbol(); + } + + UniquedStringImpl* uid() const + { + return m_impl; + } + + AtomicStringImpl* publicName() const + { + return (!m_impl || m_impl->isSymbol()) ? nullptr : static_cast<AtomicStringImpl*>(m_impl); + } + + void dump(PrintStream& out) const + { + if (m_impl) + out.print(m_impl); + else + out.print("<null property name>"); + } + +private: + UniquedStringImpl* m_impl; +}; + +inline bool operator==(PropertyName a, const Identifier& b) +{ + return a.uid() == b.impl(); +} + +inline bool operator==(const Identifier& a, PropertyName b) +{ + return a.impl() == b.uid(); +} + +inline bool operator==(PropertyName a, PropertyName b) +{ + return a.uid() == b.uid(); +} + +inline bool operator==(PropertyName a, const char* b) +{ + return equal(a.uid(), b); +} + +inline bool operator!=(PropertyName a, const Identifier& b) +{ + return a.uid() != b.impl(); +} + +inline bool operator!=(const Identifier& a, PropertyName b) +{ + return a.impl() != b.uid(); +} + +inline bool operator!=(PropertyName a, PropertyName b) +{ + return a.uid() != b.uid(); +} + +ALWAYS_INLINE Optional<uint32_t> parseIndex(PropertyName propertyName) +{ + auto uid = propertyName.uid(); + if (!uid) + return Nullopt; + if (uid->isSymbol()) + return Nullopt; + return parseIndex(*uid); +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/PropertyNameArray.h b/Source/JavaScriptCore/runtime/PropertyNameArray.h new file mode 100644 index 000000000..14f6f785c --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyNameArray.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2006, 2008, 2012 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef PropertyNameArray_h +#define PropertyNameArray_h + +#include "CallFrame.h" +#include "Identifier.h" +#include <wtf/HashSet.h> +#include <wtf/Vector.h> + +namespace JSC { + +// FIXME: Rename to PropertyNameArray. +class PropertyNameArrayData : public RefCounted<PropertyNameArrayData> { +public: + typedef Vector<Identifier, 20> PropertyNameVector; + + static Ref<PropertyNameArrayData> create() { return adoptRef(*new PropertyNameArrayData); } + + PropertyNameVector& propertyNameVector() { return m_propertyNameVector; } + +private: + PropertyNameArrayData() + { + } + + PropertyNameVector m_propertyNameVector; +}; + +// FIXME: Rename to PropertyNameArrayBuilder. +class PropertyNameArray { +public: + PropertyNameArray(VM* vm, PropertyNameMode mode) + : m_data(PropertyNameArrayData::create()) + , m_vm(vm) + , m_mode(mode) + { + } + + PropertyNameArray(ExecState* exec, PropertyNameMode mode) + : PropertyNameArray(&exec->vm(), mode) + { + } + + VM* vm() { return m_vm; } + + void add(uint32_t index) + { + add(Identifier::from(m_vm, index)); + } + + void add(const Identifier&); + void add(UniquedStringImpl*); + void addKnownUnique(UniquedStringImpl*); + + Identifier& operator[](unsigned i) { return m_data->propertyNameVector()[i]; } + const Identifier& operator[](unsigned i) const { return m_data->propertyNameVector()[i]; } + + void setData(PassRefPtr<PropertyNameArrayData> data) { m_data = data; } + PropertyNameArrayData* data() { return m_data.get(); } + PassRefPtr<PropertyNameArrayData> releaseData() { return m_data.release(); } + + // FIXME: Remove these functions. + bool canAddKnownUniqueForStructure() const { return m_data->propertyNameVector().isEmpty(); } + typedef PropertyNameArrayData::PropertyNameVector::const_iterator const_iterator; + size_t size() const { return m_data->propertyNameVector().size(); } + const_iterator begin() const { return m_data->propertyNameVector().begin(); } + const_iterator end() const { return m_data->propertyNameVector().end(); } + + PropertyNameMode mode() const { return m_mode; } + bool includeSymbolProperties() const; + bool includeStringProperties() const; + +private: + bool isUidMatchedToTypeMode(UniquedStringImpl* identifier); + + RefPtr<PropertyNameArrayData> m_data; + HashSet<UniquedStringImpl*> m_set; + VM* m_vm; + PropertyNameMode m_mode; +}; + +ALWAYS_INLINE void PropertyNameArray::add(const Identifier& identifier) +{ + add(identifier.impl()); +} + +ALWAYS_INLINE void PropertyNameArray::addKnownUnique(UniquedStringImpl* identifier) +{ + if (!isUidMatchedToTypeMode(identifier)) + return; + m_data->propertyNameVector().append(Identifier::fromUid(m_vm, identifier)); +} + +ALWAYS_INLINE void PropertyNameArray::add(UniquedStringImpl* identifier) +{ + static const unsigned setThreshold = 20; + + ASSERT(identifier); + + if (!isUidMatchedToTypeMode(identifier)) + return; + + if (size() < setThreshold) { + if (m_data->propertyNameVector().contains(identifier)) + return; + } else { + if (m_set.isEmpty()) { + for (Identifier& name : m_data->propertyNameVector()) + m_set.add(name.impl()); + } + if (!m_set.add(identifier).isNewEntry) + return; + } + + addKnownUnique(identifier); +} + +ALWAYS_INLINE bool PropertyNameArray::isUidMatchedToTypeMode(UniquedStringImpl* identifier) +{ + if (identifier->isSymbol()) + return includeSymbolProperties(); + return includeStringProperties(); +} + +ALWAYS_INLINE bool PropertyNameArray::includeSymbolProperties() const +{ + return static_cast<std::underlying_type<PropertyNameMode>::type>(m_mode) & static_cast<std::underlying_type<PropertyNameMode>::type>(PropertyNameMode::Symbols); +} + +ALWAYS_INLINE bool PropertyNameArray::includeStringProperties() const +{ + return static_cast<std::underlying_type<PropertyNameMode>::type>(m_mode) & static_cast<std::underlying_type<PropertyNameMode>::type>(PropertyNameMode::Strings); +} + +} // namespace JSC + +#endif // PropertyNameArray_h diff --git a/Source/JavaScriptCore/runtime/PropertyOffset.h b/Source/JavaScriptCore/runtime/PropertyOffset.h new file mode 100644 index 000000000..aeedb70bd --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyOffset.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PropertyOffset_h +#define PropertyOffset_h + +#include <wtf/StdLibExtras.h> + +namespace JSC { + +typedef int PropertyOffset; + +static const PropertyOffset invalidOffset = -1; +static const PropertyOffset firstOutOfLineOffset = 100; + +// Declare all of the functions because they tend to do forward calls. +inline void checkOffset(PropertyOffset); +inline void checkOffset(PropertyOffset, int inlineCapacity); +inline void validateOffset(PropertyOffset); +inline void validateOffset(PropertyOffset, int inlineCapacity); +inline bool isValidOffset(PropertyOffset); +inline bool isInlineOffset(PropertyOffset); +inline bool isOutOfLineOffset(PropertyOffset); +inline size_t offsetInInlineStorage(PropertyOffset); +inline size_t offsetInOutOfLineStorage(PropertyOffset); +inline size_t offsetInRespectiveStorage(PropertyOffset); +inline size_t numberOfOutOfLineSlotsForLastOffset(PropertyOffset); +inline size_t numberOfSlotsForLastOffset(PropertyOffset, int inlineCapacity); + +inline void checkOffset(PropertyOffset offset) +{ + UNUSED_PARAM(offset); + ASSERT(offset >= invalidOffset); +} + +inline void checkOffset(PropertyOffset offset, int inlineCapacity) +{ + UNUSED_PARAM(offset); + UNUSED_PARAM(inlineCapacity); + ASSERT(offset >= invalidOffset); + ASSERT(offset == invalidOffset + || offset < inlineCapacity + || isOutOfLineOffset(offset)); +} + +inline void validateOffset(PropertyOffset offset) +{ + checkOffset(offset); + ASSERT(isValidOffset(offset)); +} + +inline void validateOffset(PropertyOffset offset, int inlineCapacity) +{ + checkOffset(offset, inlineCapacity); + ASSERT(isValidOffset(offset)); +} + +inline bool isValidOffset(PropertyOffset offset) +{ + checkOffset(offset); + return offset != invalidOffset; +} + +inline bool isInlineOffset(PropertyOffset offset) +{ + checkOffset(offset); + return offset < firstOutOfLineOffset; +} + +inline bool isOutOfLineOffset(PropertyOffset offset) +{ + checkOffset(offset); + return !isInlineOffset(offset); +} + +inline size_t offsetInInlineStorage(PropertyOffset offset) +{ + validateOffset(offset); + ASSERT(isInlineOffset(offset)); + return offset; +} + +inline size_t offsetInOutOfLineStorage(PropertyOffset offset) +{ + validateOffset(offset); + ASSERT(isOutOfLineOffset(offset)); + return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 1; +} + +inline size_t offsetInRespectiveStorage(PropertyOffset offset) +{ + if (isInlineOffset(offset)) + return offsetInInlineStorage(offset); + return offsetInOutOfLineStorage(offset); +} + +inline size_t numberOfOutOfLineSlotsForLastOffset(PropertyOffset offset) +{ + checkOffset(offset); + if (offset < firstOutOfLineOffset) + return 0; + return offset - firstOutOfLineOffset + 1; +} + +inline size_t numberOfSlotsForLastOffset(PropertyOffset offset, int inlineCapacity) +{ + checkOffset(offset, inlineCapacity); + if (offset < inlineCapacity) + return offset + 1; + return inlineCapacity + numberOfOutOfLineSlotsForLastOffset(offset); +} + +inline PropertyOffset offsetForPropertyNumber(int propertyNumber, int inlineCapacity) +{ + PropertyOffset offset = propertyNumber; + if (offset >= inlineCapacity) { + offset += firstOutOfLineOffset; + offset -= inlineCapacity; + } + return offset; +} + +} // namespace JSC + +#endif // PropertyOffset_h diff --git a/Source/JavaScriptCore/runtime/PropertySlot.cpp b/Source/JavaScriptCore/runtime/PropertySlot.cpp new file mode 100644 index 000000000..c949f4bd2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertySlot.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "PropertySlot.h" + +#include "GetterSetter.h" +#include "JSCJSValueInlines.h" + +namespace JSC { + +JSValue PropertySlot::functionGetter(ExecState* exec) const +{ + ASSERT(m_thisValue); + return callGetter(exec, m_thisValue, m_data.getter.getterSetter); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/PropertySlot.h b/Source/JavaScriptCore/runtime/PropertySlot.h new file mode 100644 index 000000000..7aefcd37f --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertySlot.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2005, 2007, 2008, 2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef PropertySlot_h +#define PropertySlot_h + +#include "JSCJSValue.h" +#include "PropertyName.h" +#include "PropertyOffset.h" +#include "Register.h" +#include <wtf/Assertions.h> + +namespace JSC { + +class ExecState; +class GetterSetter; +class JSObject; + +// ECMA 262-3 8.6.1 +// Property attributes +enum Attribute { + None = 0, + ReadOnly = 1 << 1, // property can be only read, not written + DontEnum = 1 << 2, // property doesn't appear in (for .. in ..) + DontDelete = 1 << 3, // property can't be deleted + Function = 1 << 4, // property is a function - only used by static hashtables + Accessor = 1 << 5, // property is a getter/setter + CustomAccessor = 1 << 6, + Builtin = 1 << 7, // property is a builtin function - only used by static hashtables + ConstantInteger = 1 << 8, // property is a constant integer - only used by static hashtables + BuiltinOrFunction = Builtin | Function, // helper only used by static hashtables + BuiltinOrFunctionOrAccessor = Builtin | Function | Accessor, // helper only used by static hashtables + BuiltinOrFunctionOrAccessorOrConstant = Builtin | Function | Accessor | ConstantInteger, // helper only used by static hashtables +}; + +class PropertySlot { + enum PropertyType { + TypeUnset, + TypeValue, + TypeGetter, + TypeCustom + }; + + enum CacheabilityType { + CachingDisallowed, + CachingAllowed + }; + +public: + explicit PropertySlot(const JSValue thisValue) + : m_propertyType(TypeUnset) + , m_offset(invalidOffset) + , m_thisValue(thisValue) + , m_slotBase(nullptr) + , m_watchpointSet(nullptr) + , m_cacheability(CachingAllowed) + { + } + + typedef EncodedJSValue (*GetValueFunc)(ExecState*, JSObject* slotBase, EncodedJSValue thisValue, PropertyName); + + JSValue getValue(ExecState*, PropertyName) const; + JSValue getValue(ExecState*, unsigned propertyName) const; + + bool isCacheable() const { return m_cacheability == CachingAllowed && m_offset != invalidOffset; } + bool isUnset() const { return m_propertyType == TypeUnset; } + bool isValue() const { return m_propertyType == TypeValue; } + bool isAccessor() const { return m_propertyType == TypeGetter; } + bool isCustom() const { return m_propertyType == TypeCustom; } + bool isCacheableValue() const { return isCacheable() && isValue(); } + bool isCacheableGetter() const { return isCacheable() && isAccessor(); } + bool isCacheableCustom() const { return isCacheable() && isCustom(); } + + void disableCaching() + { + m_cacheability = CachingDisallowed; + } + + unsigned attributes() const { return m_attributes; } + + PropertyOffset cachedOffset() const + { + ASSERT(isCacheable()); + return m_offset; + } + + GetterSetter* getterSetter() const + { + ASSERT(isAccessor()); + return m_data.getter.getterSetter; + } + + GetValueFunc customGetter() const + { + ASSERT(isCacheableCustom()); + return m_data.custom.getValue; + } + + JSObject* slotBase() const + { + return m_slotBase; + } + + WatchpointSet* watchpointSet() const + { + return m_watchpointSet; + } + + void setValue(JSObject* slotBase, unsigned attributes, JSValue value) + { + m_data.value = JSValue::encode(value); + m_attributes = attributes; + + ASSERT(slotBase); + m_slotBase = slotBase; + m_propertyType = TypeValue; + m_offset = invalidOffset; + } + + void setValue(JSObject* slotBase, unsigned attributes, JSValue value, PropertyOffset offset) + { + ASSERT(value); + m_data.value = JSValue::encode(value); + m_attributes = attributes; + + ASSERT(slotBase); + m_slotBase = slotBase; + m_propertyType = TypeValue; + m_offset = offset; + } + + void setValue(JSString*, unsigned attributes, JSValue value) + { + ASSERT(value); + m_data.value = JSValue::encode(value); + m_attributes = attributes; + + m_slotBase = 0; + m_propertyType = TypeValue; + m_offset = invalidOffset; + } + + void setCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue) + { + ASSERT(getValue); + m_data.custom.getValue = getValue; + m_attributes = attributes; + + ASSERT(slotBase); + m_slotBase = slotBase; + m_propertyType = TypeCustom; + m_offset = invalidOffset; + } + + void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue) + { + ASSERT(getValue); + m_data.custom.getValue = getValue; + m_attributes = attributes; + + ASSERT(slotBase); + m_slotBase = slotBase; + m_propertyType = TypeCustom; + m_offset = !invalidOffset; + } + + void setGetterSlot(JSObject* slotBase, unsigned attributes, GetterSetter* getterSetter) + { + ASSERT(getterSetter); + m_data.getter.getterSetter = getterSetter; + m_attributes = attributes; + + ASSERT(slotBase); + m_slotBase = slotBase; + m_propertyType = TypeGetter; + m_offset = invalidOffset; + } + + void setCacheableGetterSlot(JSObject* slotBase, unsigned attributes, GetterSetter* getterSetter, PropertyOffset offset) + { + ASSERT(getterSetter); + m_data.getter.getterSetter = getterSetter; + m_attributes = attributes; + + ASSERT(slotBase); + m_slotBase = slotBase; + m_propertyType = TypeGetter; + m_offset = offset; + } + + void setThisValue(JSValue thisValue) + { + m_thisValue = thisValue; + } + + void setUndefined() + { + m_data.value = JSValue::encode(jsUndefined()); + m_attributes = ReadOnly | DontDelete | DontEnum; + + m_slotBase = 0; + m_propertyType = TypeValue; + m_offset = invalidOffset; + } + + void setWatchpointSet(WatchpointSet& set) + { + m_watchpointSet = &set; + } + +private: + JS_EXPORT_PRIVATE JSValue functionGetter(ExecState*) const; + + unsigned m_attributes; + union { + EncodedJSValue value; + struct { + GetterSetter* getterSetter; + } getter; + struct { + GetValueFunc getValue; + } custom; + } m_data; + + PropertyType m_propertyType; + PropertyOffset m_offset; + JSValue m_thisValue; + JSObject* m_slotBase; + WatchpointSet* m_watchpointSet; + CacheabilityType m_cacheability; +}; + +ALWAYS_INLINE JSValue PropertySlot::getValue(ExecState* exec, PropertyName propertyName) const +{ + if (m_propertyType == TypeValue) + return JSValue::decode(m_data.value); + if (m_propertyType == TypeGetter) + return functionGetter(exec); + return JSValue::decode(m_data.custom.getValue(exec, slotBase(), JSValue::encode(m_thisValue), propertyName)); +} + +ALWAYS_INLINE JSValue PropertySlot::getValue(ExecState* exec, unsigned propertyName) const +{ + if (m_propertyType == TypeValue) + return JSValue::decode(m_data.value); + if (m_propertyType == TypeGetter) + return functionGetter(exec); + return JSValue::decode(m_data.custom.getValue(exec, slotBase(), JSValue::encode(m_thisValue), Identifier::from(exec, propertyName))); +} + +} // namespace JSC + +#endif // PropertySlot_h diff --git a/Source/JavaScriptCore/runtime/PropertyStorage.h b/Source/JavaScriptCore/runtime/PropertyStorage.h new file mode 100644 index 000000000..3d7bb020c --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyStorage.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef PropertyStorage_h +#define PropertyStorage_h + +#include "WriteBarrier.h" + +namespace JSC { + +typedef WriteBarrierBase<Unknown>* PropertyStorage; +typedef const WriteBarrierBase<Unknown>* ConstPropertyStorage; + +} // namespace JSC + +#endif // PropertyStorage_h + diff --git a/Source/JavaScriptCore/runtime/PropertyTable.cpp b/Source/JavaScriptCore/runtime/PropertyTable.cpp new file mode 100644 index 000000000..74474c6d9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PropertyTable.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "PropertyMapHashTable.h" + +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "SlotVisitorInlines.h" +#include "StructureInlines.h" + +#include <wtf/CryptographicallyRandomNumber.h> + +namespace JSC { + +const ClassInfo PropertyTable::s_info = { "PropertyTable", 0, 0, CREATE_METHOD_TABLE(PropertyTable) }; + +PropertyTable* PropertyTable::create(VM& vm, unsigned initialCapacity) +{ + PropertyTable* table = new (NotNull, allocateCell<PropertyTable>(vm.heap)) PropertyTable(vm, initialCapacity); + table->finishCreation(vm); + return table; +} + +PropertyTable* PropertyTable::clone(VM& vm, const PropertyTable& other) +{ + PropertyTable* table = new (NotNull, allocateCell<PropertyTable>(vm.heap)) PropertyTable(vm, other); + table->finishCreation(vm); + return table; +} + +PropertyTable* PropertyTable::clone(VM& vm, unsigned initialCapacity, const PropertyTable& other) +{ + PropertyTable* table = new (NotNull, allocateCell<PropertyTable>(vm.heap)) PropertyTable(vm, initialCapacity, other); + table->finishCreation(vm); + return table; +} + +PropertyTable::PropertyTable(VM& vm, unsigned initialCapacity) + : JSCell(vm, vm.propertyTableStructure.get()) + , m_indexSize(sizeForCapacity(initialCapacity)) + , m_indexMask(m_indexSize - 1) + , m_index(static_cast<unsigned*>(fastZeroedMalloc(dataSize()))) + , m_keyCount(0) + , m_deletedCount(0) +{ + ASSERT(isPowerOf2(m_indexSize)); +} + +PropertyTable::PropertyTable(VM& vm, const PropertyTable& other) + : JSCell(vm, vm.propertyTableStructure.get()) + , m_indexSize(other.m_indexSize) + , m_indexMask(other.m_indexMask) + , m_index(static_cast<unsigned*>(fastMalloc(dataSize()))) + , m_keyCount(other.m_keyCount) + , m_deletedCount(other.m_deletedCount) +{ + ASSERT(isPowerOf2(m_indexSize)); + + memcpy(m_index, other.m_index, dataSize()); + + iterator end = this->end(); + for (iterator iter = begin(); iter != end; ++iter) + iter->key->ref(); + + // Copy the m_deletedOffsets vector. + Vector<PropertyOffset>* otherDeletedOffsets = other.m_deletedOffsets.get(); + if (otherDeletedOffsets) + m_deletedOffsets = std::make_unique<Vector<PropertyOffset>>(*otherDeletedOffsets); +} + +PropertyTable::PropertyTable(VM& vm, unsigned initialCapacity, const PropertyTable& other) + : JSCell(vm, vm.propertyTableStructure.get()) + , m_indexSize(sizeForCapacity(initialCapacity)) + , m_indexMask(m_indexSize - 1) + , m_index(static_cast<unsigned*>(fastZeroedMalloc(dataSize()))) + , m_keyCount(0) + , m_deletedCount(0) +{ + ASSERT(isPowerOf2(m_indexSize)); + ASSERT(initialCapacity >= other.m_keyCount); + + const_iterator end = other.end(); + for (const_iterator iter = other.begin(); iter != end; ++iter) { + ASSERT(canInsert()); + reinsert(*iter); + iter->key->ref(); + } + + // Copy the m_deletedOffsets vector. + Vector<PropertyOffset>* otherDeletedOffsets = other.m_deletedOffsets.get(); + if (otherDeletedOffsets) + m_deletedOffsets = std::make_unique<Vector<PropertyOffset>>(*otherDeletedOffsets); +} + +void PropertyTable::destroy(JSCell* cell) +{ + static_cast<PropertyTable*>(cell)->PropertyTable::~PropertyTable(); +} + +PropertyTable::~PropertyTable() +{ + iterator end = this->end(); + for (iterator iter = begin(); iter != end; ++iter) + iter->key->deref(); + + fastFree(m_index); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/Protect.h b/Source/JavaScriptCore/runtime/Protect.h new file mode 100644 index 000000000..e72cafb82 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Protect.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + +#ifndef Protect_h +#define Protect_h + +#include "Heap.h" +#include "JSCJSValue.h" + +namespace JSC { + +inline void gcProtect(JSCell* val) +{ + Heap::heap(val)->protect(val); +} + +inline void gcUnprotect(JSCell* val) +{ + Heap::heap(val)->unprotect(val); +} + +inline void gcProtectNullTolerant(JSCell* val) +{ + if (val) + gcProtect(val); +} + +inline void gcUnprotectNullTolerant(JSCell* val) +{ + if (val) + gcUnprotect(val); +} + +inline void gcProtect(JSValue value) +{ + if (value && value.isCell()) + gcProtect(value.asCell()); +} + +inline void gcUnprotect(JSValue value) +{ + if (value && value.isCell()) + gcUnprotect(value.asCell()); +} + +} // namespace JSC + +#endif // Protect_h diff --git a/Source/JavaScriptCore/runtime/PrototypeMap.cpp b/Source/JavaScriptCore/runtime/PrototypeMap.cpp new file mode 100644 index 000000000..43f03441b --- /dev/null +++ b/Source/JavaScriptCore/runtime/PrototypeMap.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "PrototypeMap.h" + +#include "JSGlobalObject.h" +#include "JSCInlines.h" + +namespace JSC { + +void PrototypeMap::addPrototype(JSObject* object) +{ + m_prototypes.set(object, object); + + // Note that this method makes the somewhat odd decision to not check if this + // object currently has indexed accessors. We could do that check here, and if + // indexed accessors were found, we could tell the global object to have a bad + // time. But we avoid this, to allow the following to be always fast: + // + // 1) Create an object. + // 2) Give it a setter or read-only property that happens to have a numeric name. + // 3) Allocate objects that use this object as a prototype. + // + // This avoids anyone having a bad time. Even if the instance objects end up + // having indexed storage, the creation of indexed storage leads to a prototype + // chain walk that detects the presence of indexed setters and then does the + // right thing. As a result, having a bad time only happens if you add an + // indexed setter (or getter, or read-only field) to an object that is already + // used as a prototype. +} + +Structure* PrototypeMap::emptyObjectStructureForPrototype(JSObject* prototype, unsigned inlineCapacity) +{ + auto key = std::make_pair(prototype, inlineCapacity); + if (Structure* structure = m_structures.get(key)) { + ASSERT(isPrototype(prototype)); + return structure; + } + + addPrototype(prototype); + Structure* structure = JSFinalObject::createStructure( + prototype->globalObject()->vm(), prototype->globalObject(), prototype, inlineCapacity); + m_structures.set(key, Weak<Structure>(structure)); + return structure; +} + +void PrototypeMap::clearEmptyObjectStructureForPrototype(JSObject* object, unsigned inlineCapacity) +{ + m_structures.remove(std::make_pair(object, inlineCapacity)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/PrototypeMap.h b/Source/JavaScriptCore/runtime/PrototypeMap.h new file mode 100644 index 000000000..505e0b85c --- /dev/null +++ b/Source/JavaScriptCore/runtime/PrototypeMap.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PrototypeMap_h +#define PrototypeMap_h + +#include "WeakGCMap.h" +#include <wtf/TriState.h> + +namespace JSC { + +class JSObject; +class Structure; +class VM; + +// Tracks the canonical structure an object should be allocated with when inheriting from a given prototype. +class PrototypeMap { +public: + explicit PrototypeMap(VM& vm) + : m_prototypes(vm) + , m_structures(vm) + { + } + + JS_EXPORT_PRIVATE Structure* emptyObjectStructureForPrototype(JSObject*, unsigned inlineCapacity); + void clearEmptyObjectStructureForPrototype(JSObject*, unsigned inlineCapacity); + void addPrototype(JSObject*); + TriState isPrototype(JSObject*) const; // Returns a conservative estimate. + +private: + WeakGCMap<JSObject*, JSObject> m_prototypes; + typedef WeakGCMap<std::pair<JSObject*, unsigned>, Structure> StructureMap; + StructureMap m_structures; +}; + +inline TriState PrototypeMap::isPrototype(JSObject* object) const +{ + if (!m_prototypes.contains(object)) + return FalseTriState; + + // We know that 'object' was used as a prototype at one time, so be + // conservative and say that it might still be so. (It would be expensive + // to find out for sure, and we don't know of any cases where being precise + // would improve performance.) + return MixedTriState; +} + +} // namespace JSC + +#endif // PrototypeMap_h diff --git a/Source/JavaScriptCore/runtime/PureNaN.h b/Source/JavaScriptCore/runtime/PureNaN.h new file mode 100644 index 000000000..a236d09e1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PureNaN.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PureNaN_h +#define PureNaN_h + +#include <wtf/Assertions.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { + +// NaN (not-a-number) double values are central to how JavaScriptCore encodes JavaScript +// values (JSValues). All values, including integers and non-numeric values, are always +// encoded using the IEEE 854 binary double format. Non-double values are encoded using +// a NaN with the sign bit set. The 51-bit payload is then used for encoding the actual +// value - be it an integer or a pointer to an object, or something else. But we only +// make use of the low 49 bits and the top 15 bits being all set to 1 is the indicator +// that a value is not a double. Top 15 bits being set to 1 also indicate a signed +// signaling NaN with some additional NaN payload bits. +// +// Our use of NaN encoding means that we have to be careful with how we use NaNs for +// ordinary doubles. For example, it would be wrong to ever use a NaN that has the top +// 15 bits set, as that would look like a non-double value to JSC. +// +// We can trust that on all of the hardware/OS combinations that we care about, +// NaN-producing math operations never produce a NaN that looks like a tagged value. But +// if we're ever in a situation where we worry about it, we can use purifyNaN() to get a +// NaN that doesn't look like a tagged non-double value. The JavaScript language doesn't +// distinguish between different flavors of NaN and there is no way to detect what kind +// of NaN you have - hence so long as all double NaNs are purified then our tagging +// scheme remains sound. +// +// It's worth noting that there are cases, like sin(), that will almost produce a NaN +// that breaks us. sin(-inf) returns 0xfff8000000000000. This doesn't break us because +// not all of the top 15 bits are set. But it's very close. Hence our assumptions about +// NaN are just about the most aggressive assumptions we could possibly make without +// having to call purifyNaN() in surprising places. +// +// For naming purposes, we say that a NaN is "pure" if it is safe to tag, in the sense +// that doing so would result in a tagged value that would pass the "are you a double" +// test. We say that a NaN is "impure" if attempting to tag it would result in a value +// that would look like something other than a double. + +// Returns some kind of pure NaN. +inline double pureNaN() +{ + // Be sure that we return exactly the kind of NaN that is safe. We engineer the bits + // ourselves to ensure that it's !isImpureNaN(). FWIW, this is what + // numeric_limits<double>::quiet_NaN() returns on Mac/X86_64. But AFAICT there is + // no guarantee that quiet_NaN would return a pureNaN on all platforms. For example, + // the docs appear to imply that quiet_NaN could even return a double with the + // signaling bit set on hardware that doesn't do signaling. That would probably + // never happen, but it's healthy to be paranoid. + return bitwise_cast<double>(0x7ff8000000000000ll); +} + +#define PNaN (pureNaN()) + +inline bool isImpureNaN(double value) +{ + // Tests if the double value would break JSVALUE64 encoding, which is the most + // aggressive kind of encoding that we currently use. + return bitwise_cast<uint64_t>(value) >= 0xfffe000000000000llu; +} + +// If the given value is NaN then return a NaN that is known to be pure. +inline double purifyNaN(double value) +{ + if (value != value) + return PNaN; + return value; +} + +} // namespace JSC + +#endif // PureNaN_h diff --git a/Source/JavaScriptCore/runtime/PutDirectIndexMode.h b/Source/JavaScriptCore/runtime/PutDirectIndexMode.h new file mode 100644 index 000000000..3eb3f5818 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PutDirectIndexMode.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PutDirectIndexMode_h +#define PutDirectIndexMode_h + +namespace JSC { + +enum PutDirectIndexMode { PutDirectIndexLikePutDirect, PutDirectIndexShouldNotThrow, PutDirectIndexShouldThrow }; + +} // namespace JSC + +#endif // PutDirectIndexMode_h + diff --git a/Source/JavaScriptCore/runtime/PutPropertySlot.h b/Source/JavaScriptCore/runtime/PutPropertySlot.h new file mode 100644 index 000000000..d105ac600 --- /dev/null +++ b/Source/JavaScriptCore/runtime/PutPropertySlot.h @@ -0,0 +1,120 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2008 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef PutPropertySlot_h +#define PutPropertySlot_h + +#include "JSCJSValue.h" +#include "PropertyOffset.h" + +#include <wtf/Assertions.h> + +namespace JSC { + +class JSObject; +class JSFunction; + +class PutPropertySlot { +public: + enum Type { Uncachable, ExistingProperty, NewProperty, SetterProperty, CustomProperty }; + enum Context { UnknownContext, PutById, PutByIdEval }; + typedef void (*PutValueFunc)(ExecState*, JSObject* base, EncodedJSValue thisObject, EncodedJSValue value); + + PutPropertySlot(JSValue thisValue, bool isStrictMode = false, Context context = UnknownContext) + : m_type(Uncachable) + , m_base(0) + , m_thisValue(thisValue) + , m_offset(invalidOffset) + , m_isStrictMode(isStrictMode) + , m_context(context) + , m_putFunction(nullptr) + { + } + + void setExistingProperty(JSObject* base, PropertyOffset offset) + { + m_type = ExistingProperty; + m_base = base; + m_offset = offset; + } + + void setNewProperty(JSObject* base, PropertyOffset offset) + { + m_type = NewProperty; + m_base = base; + m_offset = offset; + } + + void setCustomProperty(JSObject* base, PutValueFunc function) + { + m_type = CustomProperty; + m_base = base; + m_putFunction = function; + } + + void setCacheableSetter(JSObject* base, PropertyOffset offset) + { + m_type = SetterProperty; + m_base = base; + m_offset = offset; + } + + void setThisValue(JSValue thisValue) + { + m_thisValue = thisValue; + } + + PutValueFunc customSetter() const { return m_putFunction; } + + Context context() const { return static_cast<Context>(m_context); } + + Type type() const { return m_type; } + JSObject* base() const { return m_base; } + JSValue thisValue() const { return m_thisValue; } + + bool isStrictMode() const { return m_isStrictMode; } + bool isCacheablePut() const { return m_type == NewProperty || m_type == ExistingProperty; } + bool isCacheableSetter() const { return m_type == SetterProperty; } + bool isCacheableCustom() const { return m_type == CustomProperty; } + + PropertyOffset cachedOffset() const + { + return m_offset; + } + +private: + Type m_type; + JSObject* m_base; + JSValue m_thisValue; + PropertyOffset m_offset; + bool m_isStrictMode; + uint8_t m_context; + PutValueFunc m_putFunction; +}; + +} // namespace JSC + +#endif // PutPropertySlot_h diff --git a/Source/JavaScriptCore/runtime/ReflectObject.cpp b/Source/JavaScriptCore/runtime/ReflectObject.cpp new file mode 100644 index 000000000..b9a127ecf --- /dev/null +++ b/Source/JavaScriptCore/runtime/ReflectObject.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ReflectObject.h" + +#include "JSCInlines.h" +#include "JSGlobalObjectFunctions.h" +#include "JSPropertyNameIterator.h" +#include "Lookup.h" +#include "ObjectConstructor.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL reflectObjectDefineProperty(ExecState*); +static EncodedJSValue JSC_HOST_CALL reflectObjectEnumerate(ExecState*); +static EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState*); +static EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState*); +static EncodedJSValue JSC_HOST_CALL reflectObjectPreventExtensions(ExecState*); +static EncodedJSValue JSC_HOST_CALL reflectObjectSetPrototypeOf(ExecState*); + +} + +#include "ReflectObject.lut.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ReflectObject); + +const ClassInfo ReflectObject::s_info = { "Reflect", &Base::s_info, &reflectObjectTable, CREATE_METHOD_TABLE(ReflectObject) }; + +/* Source for ReflectObject.lut.h +@begin reflectObjectTable + apply reflectObjectApply DontEnum|Function 3 + defineProperty reflectObjectDefineProperty DontEnum|Function 3 + deleteProperty reflectObjectDeleteProperty DontEnum|Function 2 + enumerate reflectObjectEnumerate DontEnum|Function 1 + getPrototypeOf reflectObjectGetPrototypeOf DontEnum|Function 1 + has reflectObjectHas DontEnum|Function 2 + isExtensible reflectObjectIsExtensible DontEnum|Function 1 + ownKeys reflectObjectOwnKeys DontEnum|Function 1 + preventExtensions reflectObjectPreventExtensions DontEnum|Function 1 + setPrototypeOf reflectObjectSetPrototypeOf DontEnum|Function 2 +@end +*/ + +ReflectObject::ReflectObject(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} + +void ReflectObject::finishCreation(VM& vm, JSGlobalObject*) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +bool ReflectObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<Base>(exec, reflectObjectTable, jsCast<ReflectObject*>(object), propertyName, slot); +} + +// ------------------------------ Functions -------------------------------- + +// http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.defineproperty +EncodedJSValue JSC_HOST_CALL reflectObjectDefineProperty(ExecState* exec) +{ + JSValue target = exec->argument(0); + if (!target.isObject()) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.defineProperty requires the first argument be an object"))); + auto propertyName = exec->argument(1).toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + PropertyDescriptor descriptor; + if (!toPropertyDescriptor(exec, exec->argument(2), descriptor)) + return JSValue::encode(jsUndefined()); + ASSERT((descriptor.attributes() & Accessor) || (!descriptor.isAccessorDescriptor())); + ASSERT(!exec->hadException()); + + // Reflect.defineProperty should not throw an error when the defineOwnProperty operation fails. + bool shouldThrow = false; + JSObject* targetObject = asObject(target); + return JSValue::encode(jsBoolean(targetObject->methodTable(exec->vm())->defineOwnProperty(targetObject, exec, propertyName, descriptor, shouldThrow))); +} + +// http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.enumerate +EncodedJSValue JSC_HOST_CALL reflectObjectEnumerate(ExecState* exec) +{ + JSValue target = exec->argument(0); + if (!target.isObject()) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.enumerate requires the first argument be an object"))); + return JSValue::encode(JSPropertyNameIterator::create(exec, exec->lexicalGlobalObject()->propertyNameIteratorStructure(), asObject(target))); +} + +// http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.getprototypeof +EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState* exec) +{ + JSValue target = exec->argument(0); + if (!target.isObject()) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.getPrototypeOf requires the first argument be an object"))); + return JSValue::encode(objectConstructorGetPrototypeOf(exec, asObject(target))); +} + +// http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.isextensible +EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState* exec) +{ + JSValue target = exec->argument(0); + if (!target.isObject()) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.isExtensible requires the first argument be an object"))); + return JSValue::encode(jsBoolean(asObject(target)->isExtensible())); +} + +// http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.ownkeys +EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState* exec) +{ + JSValue target = exec->argument(0); + if (!target.isObject()) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.ownKeys requires the first argument be an object"))); + return JSValue::encode(ownPropertyKeys(exec, jsCast<JSObject*>(target), PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include)); +} + +// http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.preventextensions +EncodedJSValue JSC_HOST_CALL reflectObjectPreventExtensions(ExecState* exec) +{ + JSValue target = exec->argument(0); + if (!target.isObject()) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.preventExtensions requires the first argument be an object"))); + asObject(target)->preventExtensions(exec->vm()); + return JSValue::encode(jsBoolean(true)); +} + +// http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.setprototypeof +EncodedJSValue JSC_HOST_CALL reflectObjectSetPrototypeOf(ExecState* exec) +{ + JSValue target = exec->argument(0); + if (!target.isObject()) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.setPrototypeOf requires the first argument be an object"))); + JSValue proto = exec->argument(1); + if (!proto.isObject() && !proto.isNull()) + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.setPrototypeOf requires the second argument be either an object or null"))); + + JSObject* object = asObject(target); + + if (!checkProtoSetterAccessAllowed(exec, object)) + return JSValue::encode(jsBoolean(false)); + + if (object->prototype() == proto) + return JSValue::encode(jsBoolean(true)); + + if (!object->isExtensible()) + return JSValue::encode(jsBoolean(false)); + + return JSValue::encode(jsBoolean(object->setPrototypeWithCycleCheck(exec, proto))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ReflectObject.h b/Source/JavaScriptCore/runtime/ReflectObject.h new file mode 100644 index 000000000..6a7350229 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ReflectObject.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ReflectObject_h +#define ReflectObject_h + +#include "JSObject.h" + +namespace JSC { + +class ReflectObject : public JSNonFinalObject { +private: + ReflectObject(VM&, Structure*); + +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static ReflectObject* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + ReflectObject* object = new (NotNull, allocateCell<ReflectObject>(vm.heap)) ReflectObject(vm, structure); + object->finishCreation(vm, globalObject); + return object; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + +protected: + void finishCreation(VM&, JSGlobalObject*); +}; + +} // namespace JSC + +#endif // ReflectObject_h diff --git a/Source/JavaScriptCore/runtime/RegExp.cpp b/Source/JavaScriptCore/runtime/RegExp.cpp new file mode 100644 index 000000000..7a6f80c9e --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExp.cpp @@ -0,0 +1,593 @@ +/* + * Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org) + * Copyright (c) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExp.h" + +#include "Lexer.h" +#include "JSCInlines.h" +#include "RegExpCache.h" +#include "Yarr.h" +#include "YarrJIT.h" +#include <wtf/Assertions.h> + +#define REGEXP_FUNC_TEST_DATA_GEN 0 + +#if REGEXP_FUNC_TEST_DATA_GEN +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#endif + +namespace JSC { + +const ClassInfo RegExp::s_info = { "RegExp", 0, 0, CREATE_METHOD_TABLE(RegExp) }; + +RegExpFlags regExpFlags(const String& string) +{ + RegExpFlags flags = NoFlags; + + for (unsigned i = 0; i < string.length(); ++i) { + switch (string[i]) { + case 'g': + if (flags & FlagGlobal) + return InvalidFlags; + flags = static_cast<RegExpFlags>(flags | FlagGlobal); + break; + + case 'i': + if (flags & FlagIgnoreCase) + return InvalidFlags; + flags = static_cast<RegExpFlags>(flags | FlagIgnoreCase); + break; + + case 'm': + if (flags & FlagMultiline) + return InvalidFlags; + flags = static_cast<RegExpFlags>(flags | FlagMultiline); + break; + + default: + return InvalidFlags; + } + } + + return flags; +} + +#if REGEXP_FUNC_TEST_DATA_GEN +class RegExpFunctionalTestCollector { + // This class is not thread safe. +protected: + static const char* const s_fileName; + +public: + static RegExpFunctionalTestCollector* get(); + + ~RegExpFunctionalTestCollector(); + + void outputOneTest(RegExp*, String, int, int*, int); + void clearRegExp(RegExp* regExp) + { + if (regExp == m_lastRegExp) + m_lastRegExp = 0; + } + +private: + RegExpFunctionalTestCollector(); + + void outputEscapedString(const String&, bool escapeSlash = false); + + static RegExpFunctionalTestCollector* s_instance; + FILE* m_file; + RegExp* m_lastRegExp; +}; + +const char* const RegExpFunctionalTestCollector::s_fileName = "/tmp/RegExpTestsData"; +RegExpFunctionalTestCollector* RegExpFunctionalTestCollector::s_instance = 0; + +RegExpFunctionalTestCollector* RegExpFunctionalTestCollector::get() +{ + if (!s_instance) + s_instance = new RegExpFunctionalTestCollector(); + + return s_instance; +} + +void RegExpFunctionalTestCollector::outputOneTest(RegExp* regExp, const String& s, int startOffset, int* ovector, int result) +{ + if ((!m_lastRegExp) || (m_lastRegExp != regExp)) { + m_lastRegExp = regExp; + fputc('/', m_file); + outputEscapedString(regExp->pattern(), true); + fputc('/', m_file); + if (regExp->global()) + fputc('g', m_file); + if (regExp->ignoreCase()) + fputc('i', m_file); + if (regExp->multiline()) + fputc('m', m_file); + fprintf(m_file, "\n"); + } + + fprintf(m_file, " \""); + outputEscapedString(s); + fprintf(m_file, "\", %d, %d, (", startOffset, result); + for (unsigned i = 0; i <= regExp->numSubpatterns(); i++) { + int subpatternBegin = ovector[i * 2]; + int subpatternEnd = ovector[i * 2 + 1]; + if (subpatternBegin == -1) + subpatternEnd = -1; + fprintf(m_file, "%d, %d", subpatternBegin, subpatternEnd); + if (i < regExp->numSubpatterns()) + fputs(", ", m_file); + } + + fprintf(m_file, ")\n"); + fflush(m_file); +} + +RegExpFunctionalTestCollector::RegExpFunctionalTestCollector() +{ + m_file = fopen(s_fileName, "r+"); + if (!m_file) + m_file = fopen(s_fileName, "w+"); + + fseek(m_file, 0L, SEEK_END); +} + +RegExpFunctionalTestCollector::~RegExpFunctionalTestCollector() +{ + fclose(m_file); + s_instance = 0; +} + +void RegExpFunctionalTestCollector::outputEscapedString(const String& s, bool escapeSlash) +{ + int len = s.length(); + + for (int i = 0; i < len; ++i) { + UChar c = s[i]; + + switch (c) { + case '\0': + fputs("\\0", m_file); + break; + case '\a': + fputs("\\a", m_file); + break; + case '\b': + fputs("\\b", m_file); + break; + case '\f': + fputs("\\f", m_file); + break; + case '\n': + fputs("\\n", m_file); + break; + case '\r': + fputs("\\r", m_file); + break; + case '\t': + fputs("\\t", m_file); + break; + case '\v': + fputs("\\v", m_file); + break; + case '/': + if (escapeSlash) + fputs("\\/", m_file); + else + fputs("/", m_file); + break; + case '\"': + fputs("\\\"", m_file); + break; + case '\\': + fputs("\\\\", m_file); + break; + case '\?': + fputs("\?", m_file); + break; + default: + if (c > 0x7f) + fprintf(m_file, "\\u%04x", c); + else + fputc(c, m_file); + break; + } + } +} +#endif + +RegExp::RegExp(VM& vm, const String& patternString, RegExpFlags flags) + : JSCell(vm, vm.regExpStructure.get()) + , m_state(NotCompiled) + , m_patternString(patternString) + , m_flags(flags) + , m_constructionError(0) + , m_numSubpatterns(0) +#if ENABLE(REGEXP_TRACING) + , m_rtMatchOnlyTotalSubjectStringLen(0.0) + , m_rtMatchTotalSubjectStringLen(0.0) + , m_rtMatchOnlyCallCount(0) + , m_rtMatchOnlyFoundCount(0) + , m_rtMatchCallCount(0) + , m_rtMatchFoundCount(0) +#endif +{ +} + +void RegExp::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + Yarr::YarrPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); + if (m_constructionError) + m_state = ParseError; + else + m_numSubpatterns = pattern.m_numSubpatterns; +} + +void RegExp::destroy(JSCell* cell) +{ + RegExp* thisObject = static_cast<RegExp*>(cell); +#if REGEXP_FUNC_TEST_DATA_GEN + RegExpFunctionalTestCollector::get()->clearRegExp(this); +#endif + thisObject->RegExp::~RegExp(); +} + +RegExp* RegExp::createWithoutCaching(VM& vm, const String& patternString, RegExpFlags flags) +{ + RegExp* regExp = new (NotNull, allocateCell<RegExp>(vm.heap)) RegExp(vm, patternString, flags); + regExp->finishCreation(vm); + return regExp; +} + +RegExp* RegExp::create(VM& vm, const String& patternString, RegExpFlags flags) +{ + return vm.regExpCache()->lookupOrCreate(patternString, flags); +} + +void RegExp::compile(VM* vm, Yarr::YarrCharSize charSize) +{ + Yarr::YarrPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); + if (m_constructionError) { + RELEASE_ASSERT_NOT_REACHED(); +#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) + m_state = ParseError; + return; +#endif + } + ASSERT(m_numSubpatterns == pattern.m_numSubpatterns); + + if (!hasCode()) { + ASSERT(m_state == NotCompiled); + vm->regExpCache()->addToStrongCache(this); + m_state = ByteCode; + } + +#if ENABLE(YARR_JIT) + if (!pattern.m_containsBackreferences && !pattern.containsUnsignedLengthPattern() && vm->canUseRegExpJIT()) { + Yarr::jitCompile(pattern, charSize, vm, m_regExpJITCode); + if (!m_regExpJITCode.isFallBack()) { + m_state = JITCode; + return; + } + } +#else + UNUSED_PARAM(charSize); +#endif + + m_state = ByteCode; + m_regExpBytecode = Yarr::byteCompile(pattern, &vm->m_regExpAllocator); +} + +void RegExp::compileIfNecessary(VM& vm, Yarr::YarrCharSize charSize) +{ + if (hasCode()) { +#if ENABLE(YARR_JIT) + if (m_state != JITCode) + return; + if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCode())) + return; + if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCode())) + return; +#else + return; +#endif + } + + compile(&vm, charSize); +} + +int RegExp::match(VM& vm, const String& s, unsigned startOffset, Vector<int, 32>& ovector) +{ +#if ENABLE(REGEXP_TRACING) + m_rtMatchCallCount++; + m_rtMatchTotalSubjectStringLen += (double)(s.length() - startOffset); +#endif + + ASSERT(m_state != ParseError); + compileIfNecessary(vm, s.is8Bit() ? Yarr::Char8 : Yarr::Char16); + + int offsetVectorSize = (m_numSubpatterns + 1) * 2; + ovector.resize(offsetVectorSize); + int* offsetVector = ovector.data(); + + int result; +#if ENABLE(YARR_JIT) + if (m_state == JITCode) { + if (s.is8Bit()) + result = m_regExpJITCode.execute(s.characters8(), startOffset, s.length(), offsetVector).start; + else + result = m_regExpJITCode.execute(s.characters16(), startOffset, s.length(), offsetVector).start; +#if ENABLE(YARR_JIT_DEBUG) + matchCompareWithInterpreter(s, startOffset, offsetVector, result); +#endif + } else +#endif + result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector)); + + // FIXME: The YARR engine should handle unsigned or size_t length matches. + // The YARR Interpreter is "unsigned" clean, while the YARR JIT hasn't been addressed. + // The offset vector handling needs to change as well. + // Right now we convert a match where the offsets overflowed into match failure. + // There are two places in WebCore that call the interpreter directly that need to + // have their offsets changed to int as well. They are yarr/RegularExpression.cpp + // and inspector/ContentSearchUtilities.cpp + if (s.length() > INT_MAX) { + bool overflowed = false; + + if (result < -1) + overflowed = true; + + for (unsigned i = 0; i <= m_numSubpatterns; i++) { + if ((offsetVector[i*2] < -1) || ((offsetVector[i*2] >= 0) && (offsetVector[i*2+1] < -1))) { + overflowed = true; + offsetVector[i*2] = -1; + offsetVector[i*2+1] = -1; + } + } + + if (overflowed) + result = -1; + } + + ASSERT(result >= -1); + +#if REGEXP_FUNC_TEST_DATA_GEN + RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result); +#endif + +#if ENABLE(REGEXP_TRACING) + if (result != -1) + m_rtMatchFoundCount++; +#endif + + return result; +} + +void RegExp::compileMatchOnly(VM* vm, Yarr::YarrCharSize charSize) +{ + Yarr::YarrPattern pattern(m_patternString, ignoreCase(), multiline(), &m_constructionError); + if (m_constructionError) { + RELEASE_ASSERT_NOT_REACHED(); +#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) + m_state = ParseError; + return; +#endif + } + ASSERT(m_numSubpatterns == pattern.m_numSubpatterns); + + if (!hasCode()) { + ASSERT(m_state == NotCompiled); + vm->regExpCache()->addToStrongCache(this); + m_state = ByteCode; + } + +#if ENABLE(YARR_JIT) + if (!pattern.m_containsBackreferences && !pattern.containsUnsignedLengthPattern() && vm->canUseRegExpJIT()) { + Yarr::jitCompile(pattern, charSize, vm, m_regExpJITCode, Yarr::MatchOnly); + if (!m_regExpJITCode.isFallBack()) { + m_state = JITCode; + return; + } + } +#else + UNUSED_PARAM(charSize); +#endif + + m_state = ByteCode; + m_regExpBytecode = Yarr::byteCompile(pattern, &vm->m_regExpAllocator); +} + +void RegExp::compileIfNecessaryMatchOnly(VM& vm, Yarr::YarrCharSize charSize) +{ + if (hasCode()) { +#if ENABLE(YARR_JIT) + if (m_state != JITCode) + return; + if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCodeMatchOnly())) + return; + if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCodeMatchOnly())) + return; +#else + return; +#endif + } + + compileMatchOnly(&vm, charSize); +} + +MatchResult RegExp::match(VM& vm, const String& s, unsigned startOffset) +{ +#if ENABLE(REGEXP_TRACING) + m_rtMatchOnlyCallCount++; + m_rtMatchOnlyTotalSubjectStringLen += (double)(s.length() - startOffset); +#endif + + ASSERT(m_state != ParseError); + compileIfNecessaryMatchOnly(vm, s.is8Bit() ? Yarr::Char8 : Yarr::Char16); + +#if ENABLE(YARR_JIT) + if (m_state == JITCode) { + MatchResult result = s.is8Bit() ? + m_regExpJITCode.execute(s.characters8(), startOffset, s.length()) : + m_regExpJITCode.execute(s.characters16(), startOffset, s.length()); +#if ENABLE(REGEXP_TRACING) + if (!result) + m_rtMatchOnlyFoundCount++; +#endif + return result; + } +#endif + + int offsetVectorSize = (m_numSubpatterns + 1) * 2; + int* offsetVector; + Vector<int, 32> nonReturnedOvector; + nonReturnedOvector.resize(offsetVectorSize); + offsetVector = nonReturnedOvector.data(); + int r = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector)); +#if REGEXP_FUNC_TEST_DATA_GEN + RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result); +#endif + + if (r >= 0) { +#if ENABLE(REGEXP_TRACING) + m_rtMatchOnlyFoundCount++; +#endif + return MatchResult(r, reinterpret_cast<unsigned*>(offsetVector)[1]); + } + + return MatchResult::failed(); +} + +void RegExp::deleteCode() +{ + if (!hasCode()) + return; + m_state = NotCompiled; +#if ENABLE(YARR_JIT) + m_regExpJITCode.clear(); +#endif + m_regExpBytecode = nullptr; +} + +#if ENABLE(YARR_JIT_DEBUG) +void RegExp::matchCompareWithInterpreter(const String& s, int startOffset, int* offsetVector, int jitResult) +{ + int offsetVectorSize = (m_numSubpatterns + 1) * 2; + Vector<int, 32> interpreterOvector; + interpreterOvector.resize(offsetVectorSize); + int* interpreterOffsetVector = interpreterOvector.data(); + int interpreterResult = 0; + int differences = 0; + + // Initialize interpreterOffsetVector with the return value (index 0) and the + // first subpattern start indicies (even index values) set to -1. + // No need to init the subpattern end indicies. + for (unsigned j = 0, i = 0; i < m_numSubpatterns + 1; j += 2, i++) + interpreterOffsetVector[j] = -1; + + interpreterResult = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, interpreterOffsetVector); + + if (jitResult != interpreterResult) + differences++; + + for (unsigned j = 2, i = 0; i < m_numSubpatterns; j +=2, i++) + if ((offsetVector[j] != interpreterOffsetVector[j]) + || ((offsetVector[j] >= 0) && (offsetVector[j+1] != interpreterOffsetVector[j+1]))) + differences++; + + if (differences) { + dataLogF("RegExp Discrepency for /%s/\n string input ", pattern().utf8().data()); + unsigned segmentLen = s.length() - static_cast<unsigned>(startOffset); + + dataLogF((segmentLen < 150) ? "\"%s\"\n" : "\"%148s...\"\n", s.utf8().data() + startOffset); + + if (jitResult != interpreterResult) { + dataLogF(" JIT result = %d, blah interpreted result = %d\n", jitResult, interpreterResult); + differences--; + } else { + dataLogF(" Correct result = %d\n", jitResult); + } + + if (differences) { + for (unsigned j = 2, i = 0; i < m_numSubpatterns; j +=2, i++) { + if (offsetVector[j] != interpreterOffsetVector[j]) + dataLogF(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j, offsetVector[j], j, interpreterOffsetVector[j]); + if ((offsetVector[j] >= 0) && (offsetVector[j+1] != interpreterOffsetVector[j+1])) + dataLogF(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j+1, offsetVector[j+1], j+1, interpreterOffsetVector[j+1]); + } + } + } +} +#endif + +#if ENABLE(REGEXP_TRACING) + void RegExp::printTraceData() + { + char formattedPattern[41]; + char rawPattern[41]; + + strncpy(rawPattern, pattern().utf8().data(), 40); + rawPattern[40]= '\0'; + + int pattLen = strlen(rawPattern); + + snprintf(formattedPattern, 41, (pattLen <= 38) ? "/%.38s/" : "/%.36s...", rawPattern); + +#if ENABLE(YARR_JIT) + Yarr::YarrCodeBlock& codeBlock = m_regExpJITCode; + + const size_t jitAddrSize = 20; + char jit8BitMatchOnlyAddr[jitAddrSize]; + char jit16BitMatchOnlyAddr[jitAddrSize]; + char jit8BitMatchAddr[jitAddrSize]; + char jit16BitMatchAddr[jitAddrSize]; + if (m_state == ByteCode) { + snprintf(jit8BitMatchOnlyAddr, jitAddrSize, "fallback "); + snprintf(jit16BitMatchOnlyAddr, jitAddrSize, "---- "); + snprintf(jit8BitMatchAddr, jitAddrSize, "fallback "); + snprintf(jit16BitMatchAddr, jitAddrSize, "---- "); + } else { + snprintf(jit8BitMatchOnlyAddr, jitAddrSize, "0x%014lx", reinterpret_cast<unsigned long int>(codeBlock.get8BitMatchOnlyAddr())); + snprintf(jit16BitMatchOnlyAddr, jitAddrSize, "0x%014lx", reinterpret_cast<unsigned long int>(codeBlock.get16BitMatchOnlyAddr())); + snprintf(jit8BitMatchAddr, jitAddrSize, "0x%014lx", reinterpret_cast<unsigned long int>(codeBlock.get8BitMatchAddr())); + snprintf(jit16BitMatchAddr, jitAddrSize, "0x%014lx", reinterpret_cast<unsigned long int>(codeBlock.get16BitMatchAddr())); + } +#else + const char* jit8BitMatchOnlyAddr = "JIT Off"; + const char* jit16BitMatchOnlyAddr = ""; + const char* jit8BitMatchAddr = "JIT Off"; + const char* jit16BitMatchAddr = ""; +#endif + unsigned averageMatchOnlyStringLen = (unsigned)(m_rtMatchOnlyTotalSubjectStringLen / m_rtMatchOnlyCallCount); + unsigned averageMatchStringLen = (unsigned)(m_rtMatchTotalSubjectStringLen / m_rtMatchCallCount); + + printf("%-40.40s %16.16s %16.16s %10d %10d %10u\n", formattedPattern, jit8BitMatchOnlyAddr, jit16BitMatchOnlyAddr, m_rtMatchOnlyCallCount, m_rtMatchOnlyFoundCount, averageMatchOnlyStringLen); + printf(" %16.16s %16.16s %10d %10d %10u\n", jit8BitMatchAddr, jit16BitMatchAddr, m_rtMatchCallCount, m_rtMatchFoundCount, averageMatchStringLen); + } +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExp.h b/Source/JavaScriptCore/runtime/RegExp.h new file mode 100644 index 000000000..5c05c5b1a --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExp.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExp_h +#define RegExp_h + +#include "ExecutableAllocator.h" +#include "MatchResult.h" +#include "RegExpKey.h" +#include "Structure.h" +#include "yarr/Yarr.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +#if ENABLE(YARR_JIT) +#include "yarr/YarrJIT.h" +#endif + +namespace JSC { + +struct RegExpRepresentation; +class VM; + +JS_EXPORT_PRIVATE RegExpFlags regExpFlags(const String&); + +class RegExp final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + JS_EXPORT_PRIVATE static RegExp* create(VM&, const String& pattern, RegExpFlags); + static const bool needsDestruction = true; + static void destroy(JSCell*); + + bool global() const { return m_flags & FlagGlobal; } + bool ignoreCase() const { return m_flags & FlagIgnoreCase; } + bool multiline() const { return m_flags & FlagMultiline; } + + const String& pattern() const { return m_patternString; } + + bool isValid() const { return !m_constructionError && m_flags != InvalidFlags; } + const char* errorMessage() const { return m_constructionError; } + + JS_EXPORT_PRIVATE int match(VM&, const String&, unsigned startOffset, Vector<int, 32>& ovector); + JS_EXPORT_PRIVATE MatchResult match(VM&, const String&, unsigned startOffset); + unsigned numSubpatterns() const { return m_numSubpatterns; } + + bool hasCode() + { + return m_state != NotCompiled; + } + + void deleteCode(); + +#if ENABLE(REGEXP_TRACING) + void printTraceData(); +#endif + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); + } + + DECLARE_INFO; + + RegExpKey key() { return RegExpKey(m_flags, m_patternString); } + +protected: + void finishCreation(VM&); + +private: + friend class RegExpCache; + RegExp(VM&, const String&, RegExpFlags); + + static RegExp* createWithoutCaching(VM&, const String&, RegExpFlags); + + enum RegExpState { + ParseError, + JITCode, + ByteCode, + NotCompiled + }; + + RegExpState m_state; + + void compile(VM*, Yarr::YarrCharSize); + void compileIfNecessary(VM&, Yarr::YarrCharSize); + + void compileMatchOnly(VM*, Yarr::YarrCharSize); + void compileIfNecessaryMatchOnly(VM&, Yarr::YarrCharSize); + +#if ENABLE(YARR_JIT_DEBUG) + void matchCompareWithInterpreter(const String&, int startOffset, int* offsetVector, int jitResult); +#endif + + String m_patternString; + RegExpFlags m_flags; + const char* m_constructionError; + unsigned m_numSubpatterns; +#if ENABLE(REGEXP_TRACING) + double m_rtMatchOnlyTotalSubjectStringLen; + double m_rtMatchTotalSubjectStringLen; + unsigned m_rtMatchOnlyCallCount; + unsigned m_rtMatchOnlyFoundCount; + unsigned m_rtMatchCallCount; + unsigned m_rtMatchFoundCount; +#endif + +#if ENABLE(YARR_JIT) + Yarr::YarrCodeBlock m_regExpJITCode; +#endif + std::unique_ptr<Yarr::BytecodePattern> m_regExpBytecode; +}; + +} // namespace JSC + +#endif // RegExp_h diff --git a/Source/JavaScriptCore/runtime/RegExpCache.cpp b/Source/JavaScriptCore/runtime/RegExpCache.cpp new file mode 100644 index 000000000..8f4660a78 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCache.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * Copyright (C) 2012 Apple Inc. All rights reserved. + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "config.h" +#include "RegExpCache.h" + +#include "JSCInlines.h" +#include "RegExpObject.h" +#include "StrongInlines.h" + +namespace JSC { + +RegExp* RegExpCache::lookupOrCreate(const String& patternString, RegExpFlags flags) +{ + RegExpKey key(flags, patternString); + if (RegExp* regExp = m_weakCache.get(key)) + return regExp; + + RegExp* regExp = RegExp::createWithoutCaching(*m_vm, patternString, flags); +#if ENABLE(REGEXP_TRACING) + m_vm->addRegExpToTrace(regExp); +#endif + + weakAdd(m_weakCache, key, Weak<RegExp>(regExp, this)); + return regExp; +} + +RegExpCache::RegExpCache(VM* vm) + : m_nextEntryInStrongCache(0) + , m_vm(vm) +{ +} + +void RegExpCache::finalize(Handle<Unknown> handle, void*) +{ + RegExp* regExp = static_cast<RegExp*>(handle.get().asCell()); + weakRemove(m_weakCache, regExp->key(), regExp); +} + +void RegExpCache::addToStrongCache(RegExp* regExp) +{ + String pattern = regExp->pattern(); + if (pattern.length() > maxStrongCacheablePatternLength) + return; + m_strongCache[m_nextEntryInStrongCache].set(*m_vm, regExp); + m_nextEntryInStrongCache++; + if (m_nextEntryInStrongCache == maxStrongCacheableEntries) + m_nextEntryInStrongCache = 0; +} + +void RegExpCache::deleteAllCode() +{ + for (int i = 0; i < maxStrongCacheableEntries; i++) + m_strongCache[i].clear(); + m_nextEntryInStrongCache = 0; + + RegExpCacheMap::iterator end = m_weakCache.end(); + for (RegExpCacheMap::iterator it = m_weakCache.begin(); it != end; ++it) { + RegExp* regExp = it->value.get(); + if (!regExp) // Skip zombies. + continue; + regExp->deleteCode(); + } +} + +} diff --git a/Source/JavaScriptCore/runtime/RegExpCache.h b/Source/JavaScriptCore/runtime/RegExpCache.h new file mode 100644 index 000000000..942f3dcb9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCache.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "RegExp.h" +#include "RegExpKey.h" +#include "Strong.h" +#include "Weak.h" +#include "WeakInlines.h" +#include <array> +#include <wtf/HashMap.h> + +#ifndef RegExpCache_h +#define RegExpCache_h + +namespace JSC { + +class RegExpCache : private WeakHandleOwner { + WTF_MAKE_FAST_ALLOCATED; + + friend class RegExp; + typedef HashMap<RegExpKey, Weak<RegExp>> RegExpCacheMap; + +public: + RegExpCache(VM* vm); + void deleteAllCode(); + +private: + + static const unsigned maxStrongCacheablePatternLength = 256; + + static const int maxStrongCacheableEntries = 32; + + virtual void finalize(Handle<Unknown>, void* context) override; + + RegExp* lookupOrCreate(const WTF::String& patternString, RegExpFlags); + void addToStrongCache(RegExp*); + RegExpCacheMap m_weakCache; // Holds all regular expressions currently live. + int m_nextEntryInStrongCache; + std::array<Strong<RegExp>, maxStrongCacheableEntries> m_strongCache; // Holds a select few regular expressions that have compiled and executed + VM* m_vm; +}; + +} // namespace JSC + +#endif // RegExpCache_h diff --git a/Source/JavaScriptCore/runtime/RegExpCachedResult.cpp b/Source/JavaScriptCore/runtime/RegExpCachedResult.cpp new file mode 100644 index 000000000..e93061886 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCachedResult.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "RegExpCachedResult.h" + +#include "JSCInlines.h" +#include "RegExpMatchesArray.h" + +namespace JSC { + +void RegExpCachedResult::visitChildren(SlotVisitor& visitor) +{ + visitor.append(&m_lastInput); + visitor.append(&m_lastRegExp); + visitor.append(&m_reifiedInput); + visitor.append(&m_reifiedResult); + visitor.append(&m_reifiedLeftContext); + visitor.append(&m_reifiedRightContext); +} + +JSArray* RegExpCachedResult::lastResult(ExecState* exec, JSObject* owner) +{ + if (!m_reified) { + m_reifiedInput.set(exec->vm(), owner, m_lastInput.get()); + m_reifiedResult.set(exec->vm(), owner, createRegExpMatchesArray(exec, m_lastInput.get(), m_lastRegExp.get(), m_result)); + m_reified = true; + } + return m_reifiedResult.get(); +} + +JSString* RegExpCachedResult::leftContext(ExecState* exec, JSObject* owner) +{ + // Make sure we're reified. + lastResult(exec, owner); + if (!m_reifiedLeftContext) + m_reifiedLeftContext.set(exec->vm(), owner, m_result.start ? jsSubstring(exec, m_reifiedInput.get(), 0, m_result.start) : jsEmptyString(exec)); + return m_reifiedLeftContext.get(); +} + +JSString* RegExpCachedResult::rightContext(ExecState* exec, JSObject* owner) +{ + // Make sure we're reified. + lastResult(exec, owner); + if (!m_reifiedRightContext) { + unsigned length = m_reifiedInput->length(); + m_reifiedRightContext.set(exec->vm(), owner, m_result.end != length ? jsSubstring(exec, m_reifiedInput.get(), m_result.end, length - m_result.end) : jsEmptyString(exec)); + } + return m_reifiedRightContext.get(); +} + +void RegExpCachedResult::setInput(ExecState* exec, JSObject* owner, JSString* input) +{ + // Make sure we're reified, otherwise m_reifiedInput will be ignored. + lastResult(exec, owner); + ASSERT(m_reified); + m_reifiedInput.set(exec->vm(), owner, input); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpCachedResult.h b/Source/JavaScriptCore/runtime/RegExpCachedResult.h new file mode 100644 index 000000000..bf6c0b864 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpCachedResult.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef RegExpCachedResult_h +#define RegExpCachedResult_h + +#include "RegExpObject.h" + +namespace JSC { + +class JSArray; +class JSString; + +// RegExpCachedResult is used to track the cached results of the last +// match, stores on the RegExp constructor (e.g. $&, $_, $1, $2 ...). +// These values will be lazily generated on demand, so the cached result +// may be in a lazy or reified state. A lazy state is indicated by a +// value of m_result indicating a successful match, and a reified state +// is indicated by setting m_result to MatchResult::failed(). +// Following a successful match, m_result, m_lastInput and m_lastRegExp +// can be used to reify the results from the match, following reification +// m_reifiedResult and m_reifiedInput hold the cached results. +class RegExpCachedResult { +public: + RegExpCachedResult(VM& vm, JSObject* owner, RegExp* emptyRegExp) + : m_result(0, 0) + , m_reified(false) + { + m_lastInput.set(vm, owner, jsEmptyString(&vm)); + m_lastRegExp.set(vm, owner, emptyRegExp); + } + + ALWAYS_INLINE void record(VM& vm, JSObject* owner, RegExp* regExp, JSString* input, MatchResult result) + { + m_lastRegExp.set(vm, owner, regExp); + m_lastInput.set(vm, owner, input); + m_reifiedLeftContext.clear(); + m_reifiedRightContext.clear(); + m_result = result; + m_reified = false; + } + + JSArray* lastResult(ExecState*, JSObject* owner); + void setInput(ExecState*, JSObject* owner, JSString*); + + JSString* leftContext(ExecState*, JSObject* owner); + JSString* rightContext(ExecState*, JSObject* owner); + + JSString* input() + { + return m_reified ? m_reifiedInput.get() : m_lastInput.get(); + } + + void visitChildren(SlotVisitor&); + +private: + MatchResult m_result; + bool m_reified; + WriteBarrier<JSString> m_lastInput; + WriteBarrier<RegExp> m_lastRegExp; + WriteBarrier<JSArray> m_reifiedResult; + WriteBarrier<JSString> m_reifiedInput; + WriteBarrier<JSString> m_reifiedLeftContext; + WriteBarrier<JSString> m_reifiedRightContext; +}; + +} // namespace JSC + +#endif // RegExpCachedResult_h diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp new file mode 100644 index 000000000..ee1d8b3fc --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExpConstructor.h" + +#include "Error.h" +#include "JSCInlines.h" +#include "RegExpMatchesArray.h" +#include "RegExpPrototype.h" + +namespace JSC { + +static EncodedJSValue regExpConstructorInput(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorMultiline(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorLastMatch(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorLastParen(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorLeftContext(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorRightContext(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar1(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar2(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar3(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar4(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar5(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar6(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar7(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar8(ExecState*, JSObject*, EncodedJSValue, PropertyName); +static EncodedJSValue regExpConstructorDollar9(ExecState*, JSObject*, EncodedJSValue, PropertyName); + +static void setRegExpConstructorInput(ExecState*, JSObject*, EncodedJSValue, EncodedJSValue); +static void setRegExpConstructorMultiline(ExecState*, JSObject*, EncodedJSValue, EncodedJSValue); + +} // namespace JSC + +#include "RegExpConstructor.lut.h" + +namespace JSC { + +const ClassInfo RegExpConstructor::s_info = { "Function", &InternalFunction::s_info, ®ExpConstructorTable, CREATE_METHOD_TABLE(RegExpConstructor) }; + +/* Source for RegExpConstructor.lut.h +@begin regExpConstructorTable + input regExpConstructorInput None + $_ regExpConstructorInput DontEnum + multiline regExpConstructorMultiline None + $* regExpConstructorMultiline DontEnum + lastMatch regExpConstructorLastMatch DontDelete|ReadOnly + $& regExpConstructorLastMatch DontDelete|ReadOnly|DontEnum + lastParen regExpConstructorLastParen DontDelete|ReadOnly + $+ regExpConstructorLastParen DontDelete|ReadOnly|DontEnum + leftContext regExpConstructorLeftContext DontDelete|ReadOnly + $` regExpConstructorLeftContext DontDelete|ReadOnly|DontEnum + rightContext regExpConstructorRightContext DontDelete|ReadOnly + $' regExpConstructorRightContext DontDelete|ReadOnly|DontEnum + $1 regExpConstructorDollar1 DontDelete|ReadOnly + $2 regExpConstructorDollar2 DontDelete|ReadOnly + $3 regExpConstructorDollar3 DontDelete|ReadOnly + $4 regExpConstructorDollar4 DontDelete|ReadOnly + $5 regExpConstructorDollar5 DontDelete|ReadOnly + $6 regExpConstructorDollar6 DontDelete|ReadOnly + $7 regExpConstructorDollar7 DontDelete|ReadOnly + $8 regExpConstructorDollar8 DontDelete|ReadOnly + $9 regExpConstructorDollar9 DontDelete|ReadOnly +@end +*/ + +RegExpConstructor::RegExpConstructor(VM& vm, Structure* structure, RegExpPrototype* regExpPrototype) + : InternalFunction(vm, structure) + , m_cachedResult(vm, this, regExpPrototype->regExp()) + , m_multiline(false) +{ +} + +void RegExpConstructor::finishCreation(VM& vm, RegExpPrototype* regExpPrototype) +{ + Base::finishCreation(vm, regExpPrototype->classInfo()->className); + ASSERT(inherits(info())); + + // ECMA 15.10.5.1 RegExp.prototype + putDirectWithoutTransition(vm, vm.propertyNames->prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly); + + // no. of arguments for constructor + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(2), ReadOnly | DontDelete | DontEnum); +} + +void RegExpConstructor::destroy(JSCell* cell) +{ + static_cast<RegExpConstructor*>(cell)->RegExpConstructor::~RegExpConstructor(); +} + +void RegExpConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + RegExpConstructor* thisObject = jsCast<RegExpConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + thisObject->m_cachedResult.visitChildren(visitor); +} + +JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) +{ + JSArray* array = m_cachedResult.lastResult(exec, this); + + if (i < array->length()) { + JSValue result = JSValue(array).get(exec, i); + ASSERT(result.isString() || result.isUndefined()); + if (!result.isUndefined()) + return result; + } + return jsEmptyString(exec); +} + +JSValue RegExpConstructor::getLastParen(ExecState* exec) +{ + JSArray* array = m_cachedResult.lastResult(exec, this); + unsigned length = array->length(); + if (length > 1) { + JSValue result = JSValue(array).get(exec, length - 1); + ASSERT(result.isString() || result.isUndefined()); + if (!result.isUndefined()) + return result; + } + return jsEmptyString(exec); +} + +JSValue RegExpConstructor::getLeftContext(ExecState* exec) +{ + return m_cachedResult.leftContext(exec, this); +} + +JSValue RegExpConstructor::getRightContext(ExecState* exec) +{ + return m_cachedResult.rightContext(exec, this); +} + +bool RegExpConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, regExpConstructorTable, jsCast<RegExpConstructor*>(object), propertyName, slot); +} + +EncodedJSValue regExpConstructorDollar1(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 1)); +} + +EncodedJSValue regExpConstructorDollar2(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 2)); +} + +EncodedJSValue regExpConstructorDollar3(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 3)); +} + +EncodedJSValue regExpConstructorDollar4(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 4)); +} + +EncodedJSValue regExpConstructorDollar5(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 5)); +} + +EncodedJSValue regExpConstructorDollar6(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 6)); +} + +EncodedJSValue regExpConstructorDollar7(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 7)); +} + +EncodedJSValue regExpConstructorDollar8(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 8)); +} + +EncodedJSValue regExpConstructorDollar9(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 9)); +} + +EncodedJSValue regExpConstructorInput(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->input()); +} + +EncodedJSValue regExpConstructorMultiline(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(jsBoolean(asRegExpConstructor(slotBase)->multiline())); +} + +EncodedJSValue regExpConstructorLastMatch(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 0)); +} + +EncodedJSValue regExpConstructorLastParen(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getLastParen(exec)); +} + +EncodedJSValue regExpConstructorLeftContext(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getLeftContext(exec)); +} + +EncodedJSValue regExpConstructorRightContext(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(asRegExpConstructor(slotBase)->getRightContext(exec)); +} + +void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, EncodedJSValue, EncodedJSValue value) +{ + if (auto constructor = jsDynamicCast<RegExpConstructor*>(baseObject)) + constructor->setInput(exec, JSValue::decode(value).toString(exec)); +} + +void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, EncodedJSValue, EncodedJSValue value) +{ + if (auto constructor = jsDynamicCast<RegExpConstructor*>(baseObject)) + constructor->setMultiline(JSValue::decode(value).toBoolean(exec)); +} + +// ECMA 15.10.4 +JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, bool callAsConstructor) +{ + JSValue arg0 = args.at(0); + JSValue arg1 = args.at(1); + + if (arg0.inherits(RegExpObject::info())) { + if (!arg1.isUndefined()) + return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Cannot supply flags when constructing one RegExp from another."))); + // If called as a function, this just returns the first argument (see 15.10.3.1). + if (callAsConstructor) { + RegExp* regExp = static_cast<RegExpObject*>(asObject(arg0))->regExp(); + return RegExpObject::create(exec->vm(), globalObject->regExpStructure(), regExp); + } + return asObject(arg0); + } + + String pattern = arg0.isUndefined() ? emptyString() : arg0.toString(exec)->value(exec); + if (exec->hadException()) + return 0; + + RegExpFlags flags = NoFlags; + if (!arg1.isUndefined()) { + flags = regExpFlags(arg1.toString(exec)->value(exec)); + if (exec->hadException()) + return 0; + if (flags == InvalidFlags) + return exec->vm().throwException(exec, createSyntaxError(exec, ASCIILiteral("Invalid flags supplied to RegExp constructor."))); + } + + VM& vm = exec->vm(); + RegExp* regExp = RegExp::create(vm, pattern, flags); + if (!regExp->isValid()) + return vm.throwException(exec, createSyntaxError(exec, regExp->errorMessage())); + return RegExpObject::create(vm, globalObject->regExpStructure(), regExp); +} + +static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args, true)); +} + +ConstructType RegExpConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithRegExpConstructor; + return ConstructTypeHost; +} + +// ECMA 15.10.3 +static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec) +{ + ArgList args(exec); + return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args)); +} + +CallType RegExpConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callRegExpConstructor; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.h b/Source/JavaScriptCore/runtime/RegExpConstructor.h new file mode 100644 index 000000000..cabee19c9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExpConstructor_h +#define RegExpConstructor_h + +#include "InternalFunction.h" +#include "RegExp.h" +#include "RegExpCachedResult.h" +#include "RegExpObject.h" + +namespace JSC { + +class RegExpPrototype; + +class RegExpConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static RegExpConstructor* create(VM& vm, Structure* structure, RegExpPrototype* regExpPrototype) + { + RegExpConstructor* constructor = new (NotNull, allocateCell<RegExpConstructor>(vm.heap)) RegExpConstructor(vm, structure, regExpPrototype); + constructor->finishCreation(vm, regExpPrototype); + return constructor; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + + DECLARE_INFO; + + MatchResult performMatch(VM&, RegExp*, JSString*, const String&, int startOffset, int** ovector); + MatchResult performMatch(VM&, RegExp*, JSString*, const String&, int startOffset); + + void setMultiline(bool multiline) { m_multiline = multiline; } + bool multiline() const { return m_multiline; } + + JSValue getBackref(ExecState*, unsigned); + JSValue getLastParen(ExecState*); + JSValue getLeftContext(ExecState*); + JSValue getRightContext(ExecState*); + + void setInput(ExecState* exec, JSString* string) { m_cachedResult.setInput(exec, this, string); } + JSString* input() { return m_cachedResult.input(); } + + static void visitChildren(JSCell*, SlotVisitor&); + +protected: + void finishCreation(VM&, RegExpPrototype*); + +private: + RegExpConstructor(VM&, Structure*, RegExpPrototype*); + static void destroy(JSCell*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + RegExpCachedResult m_cachedResult; + bool m_multiline; + Vector<int, 32> m_ovector; +}; + +RegExpConstructor* asRegExpConstructor(JSValue); + +JSObject* constructRegExp(ExecState*, JSGlobalObject*, const ArgList&, bool callAsConstructor = false); + +inline RegExpConstructor* asRegExpConstructor(JSValue value) +{ + ASSERT(asObject(value)->inherits(RegExpConstructor::info())); + return static_cast<RegExpConstructor*>(asObject(value)); +} + +/* + To facilitate result caching, exec(), test(), match(), search(), and replace() dipatch regular + expression matching through the performMatch function. We use cached results to calculate, + e.g., RegExp.lastMatch and RegExp.leftParen. +*/ +ALWAYS_INLINE MatchResult RegExpConstructor::performMatch(VM& vm, RegExp* regExp, JSString* string, const String& input, int startOffset, int** ovector) +{ + int position = regExp->match(vm, input, startOffset, m_ovector); + + if (ovector) + *ovector = m_ovector.data(); + + if (position == -1) + return MatchResult::failed(); + + ASSERT(!m_ovector.isEmpty()); + ASSERT(m_ovector[0] == position); + ASSERT(m_ovector[1] >= position); + size_t end = m_ovector[1]; + + m_cachedResult.record(vm, this, regExp, string, MatchResult(position, end)); + + return MatchResult(position, end); +} +ALWAYS_INLINE MatchResult RegExpConstructor::performMatch(VM& vm, RegExp* regExp, JSString* string, const String& input, int startOffset) +{ + MatchResult result = regExp->match(vm, input, startOffset); + if (result) + m_cachedResult.record(vm, this, regExp, string, result); + return result; +} + +} // namespace JSC + +#endif // RegExpConstructor_h diff --git a/Source/JavaScriptCore/runtime/RegExpKey.h b/Source/JavaScriptCore/runtime/RegExpKey.h new file mode 100644 index 000000000..58fa38725 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpKey.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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. + */ + +#ifndef RegExpKey_h +#define RegExpKey_h + +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +enum RegExpFlags { + NoFlags = 0, + FlagGlobal = 1, + FlagIgnoreCase = 2, + FlagMultiline = 4, + InvalidFlags = 8, + DeletedValueFlags = -1 +}; + +struct RegExpKey { + RegExpFlags flagsValue; + RefPtr<StringImpl> pattern; + + RegExpKey() + : flagsValue(NoFlags) + { + } + + RegExpKey(RegExpFlags flags) + : flagsValue(flags) + { + } + + RegExpKey(RegExpFlags flags, const String& pattern) + : flagsValue(flags) + , pattern(pattern.impl()) + { + } + + RegExpKey(RegExpFlags flags, const PassRefPtr<StringImpl> pattern) + : flagsValue(flags) + , pattern(pattern) + { + } + + RegExpKey(RegExpFlags flags, const RefPtr<StringImpl>& pattern) + : flagsValue(flags) + , pattern(pattern) + { + } + + friend inline bool operator==(const RegExpKey& a, const RegExpKey& b); + + struct Hash { + static unsigned hash(const RegExpKey& key) { return key.pattern->hash(); } + static bool equal(const RegExpKey& a, const RegExpKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; + }; +}; + +inline bool operator==(const RegExpKey& a, const RegExpKey& b) +{ + if (a.flagsValue != b.flagsValue) + return false; + if (!a.pattern) + return !b.pattern; + if (!b.pattern) + return false; + return equal(a.pattern.get(), b.pattern.get()); +} + +} // namespace JSC + +namespace WTF { +template<typename T> struct DefaultHash; + +template<> struct DefaultHash<JSC::RegExpKey> { + typedef JSC::RegExpKey::Hash Hash; +}; + +template<> struct HashTraits<JSC::RegExpKey> : GenericHashTraits<JSC::RegExpKey> { + static const bool emptyValueIsZero = true; + static void constructDeletedValue(JSC::RegExpKey& slot) { slot.flagsValue = JSC::DeletedValueFlags; } + static bool isDeletedValue(const JSC::RegExpKey& value) { return value.flagsValue == JSC::DeletedValueFlags; } +}; +} // namespace WTF + +#endif // RegExpKey_h diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp new file mode 100644 index 000000000..440cc9512 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "RegExpMatchesArray.h" + +#include "ButterflyInlines.h" +#include "JSCInlines.h" + +namespace JSC { + +static const PropertyOffset indexPropertyOffset = 100; +static const PropertyOffset inputPropertyOffset = 101; + +static JSArray* tryCreateUninitializedRegExpMatchesArray(VM& vm, Structure* structure, unsigned initialLength) +{ + unsigned vectorLength = std::max(BASE_VECTOR_LEN, initialLength); + if (vectorLength > MAX_STORAGE_VECTOR_LENGTH) + return 0; + + void* temp; + if (!vm.heap.tryAllocateStorage(0, Butterfly::totalSize(0, structure->outOfLineCapacity(), true, vectorLength * sizeof(EncodedJSValue)), &temp)) + return 0; + Butterfly* butterfly = Butterfly::fromBase(temp, 0, structure->outOfLineCapacity()); + butterfly->setVectorLength(vectorLength); + butterfly->setPublicLength(initialLength); + + return JSArray::createWithButterfly(vm, structure, butterfly); +} + +JSArray* createRegExpMatchesArray(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result) +{ + ASSERT(result); + VM& vm = exec->vm(); + JSArray* array = tryCreateUninitializedRegExpMatchesArray(vm, exec->lexicalGlobalObject()->regExpMatchesArrayStructure(), regExp->numSubpatterns() + 1); + RELEASE_ASSERT(array); + + SamplingRegion samplingRegion("Reifying substring properties"); + + array->initializeIndex(vm, 0, jsSubstring(exec, input, result.start, result.end - result.start), ArrayWithContiguous); + + if (unsigned numSubpatterns = regExp->numSubpatterns()) { + Vector<int, 32> subpatternResults; + int position = regExp->match(vm, input->value(exec), result.start, subpatternResults); + ASSERT_UNUSED(position, position >= 0 && static_cast<size_t>(position) == result.start); + ASSERT(result.start == static_cast<size_t>(subpatternResults[0])); + ASSERT(result.end == static_cast<size_t>(subpatternResults[1])); + + for (unsigned i = 1; i <= numSubpatterns; ++i) { + int start = subpatternResults[2 * i]; + if (start >= 0) + array->initializeIndex(vm, i, jsSubstring(exec, input, start, subpatternResults[2 * i + 1] - start), ArrayWithContiguous); + else + array->initializeIndex(vm, i, jsUndefined(), ArrayWithContiguous); + } + } + + array->putDirect(vm, indexPropertyOffset, jsNumber(result.start)); + array->putDirect(vm, inputPropertyOffset, input); + + return array; +} + +Structure* createRegExpMatchesArrayStructure(VM& vm, JSGlobalObject& globalObject) +{ + Structure* structure = globalObject.arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous); + PropertyOffset offset; + structure = structure->addPropertyTransition(vm, structure, vm.propertyNames->index, 0, offset); + ASSERT(offset == indexPropertyOffset); + structure = structure->addPropertyTransition(vm, structure, vm.propertyNames->input, 0, offset); + ASSERT(offset == inputPropertyOffset); + return structure; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h new file mode 100644 index 000000000..669dd39ed --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExpMatchesArray_h +#define RegExpMatchesArray_h + +#include "JSArray.h" +#include "JSGlobalObject.h" +#include "RegExpObject.h" + +namespace JSC { + +JSArray* createRegExpMatchesArray(ExecState*, JSString*, RegExp*, MatchResult); +Structure* createRegExpMatchesArrayStructure(VM&, JSGlobalObject&); + +} + +#endif // RegExpMatchesArray_h diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp new file mode 100644 index 000000000..60a26ef67 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExpObject.h" + +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "Lookup.h" +#include "JSCInlines.h" +#include "RegExpConstructor.h" +#include "RegExpMatchesArray.h" +#include "RegExpPrototype.h" +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(RegExpObject); + +const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, nullptr, CREATE_METHOD_TABLE(RegExpObject) }; + +RegExpObject::RegExpObject(VM& vm, Structure* structure, RegExp* regExp) + : JSNonFinalObject(vm, structure) + , m_regExp(vm, this, regExp) + , m_lastIndexIsWritable(true) +{ + m_lastIndex.setWithoutWriteBarrier(jsNumber(0)); +} + +void RegExpObject::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + RegExpObject* thisObject = jsCast<RegExpObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_regExp); + visitor.append(&thisObject->m_lastIndex); +} + +bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + if (propertyName == exec->propertyNames().lastIndex) { + RegExpObject* regExp = asRegExpObject(object); + unsigned attributes = regExp->m_lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly; + slot.setValue(regExp, attributes, regExp->getLastIndex()); + return true; + } + return Base::getOwnPropertySlot(object, exec, propertyName, slot); +} + +bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + if (propertyName == exec->propertyNames().lastIndex) + return false; + return Base::deleteProperty(cell, exec, propertyName); +} + +void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode.includeDontEnumProperties()) + propertyNames.add(exec->propertyNames().lastIndex); + Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); +} + +void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode.includeDontEnumProperties()) + propertyNames.add(exec->propertyNames().lastIndex); + Base::getPropertyNames(object, exec, propertyNames, mode); +} + +void RegExpObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode.includeDontEnumProperties()) + propertyNames.add(exec->propertyNames().lastIndex); + Base::getGenericPropertyNames(object, exec, propertyNames, mode); +} + +static bool reject(ExecState* exec, bool throwException, const char* message) +{ + if (throwException) + throwTypeError(exec, ASCIILiteral(message)); + return false; +} + +bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow) +{ + if (propertyName == exec->propertyNames().lastIndex) { + RegExpObject* regExp = asRegExpObject(object); + if (descriptor.configurablePresent() && descriptor.configurable()) + return reject(exec, shouldThrow, "Attempting to change configurable attribute of unconfigurable property."); + if (descriptor.enumerablePresent() && descriptor.enumerable()) + return reject(exec, shouldThrow, "Attempting to change enumerable attribute of unconfigurable property."); + if (descriptor.isAccessorDescriptor()) + return reject(exec, shouldThrow, "Attempting to change access mechanism for an unconfigurable property."); + if (!regExp->m_lastIndexIsWritable) { + if (descriptor.writablePresent() && descriptor.writable()) + return reject(exec, shouldThrow, "Attempting to change writable attribute of unconfigurable property."); + if (!sameValue(exec, regExp->getLastIndex(), descriptor.value())) + return reject(exec, shouldThrow, "Attempting to change value of a readonly property."); + return true; + } + if (descriptor.writablePresent() && !descriptor.writable()) + regExp->m_lastIndexIsWritable = false; + if (descriptor.value()) + regExp->setLastIndex(exec, descriptor.value(), false); + return true; + } + + return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); +} + +static void regExpObjectSetLastIndexStrict(ExecState* exec, JSObject* slotBase, EncodedJSValue, EncodedJSValue value) +{ + asRegExpObject(slotBase)->setLastIndex(exec, JSValue::decode(value), true); +} + +static void regExpObjectSetLastIndexNonStrict(ExecState* exec, JSObject* slotBase, EncodedJSValue, EncodedJSValue value) +{ + asRegExpObject(slotBase)->setLastIndex(exec, JSValue::decode(value), false); +} + +void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + if (propertyName == exec->propertyNames().lastIndex) { + asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode()); + slot.setCustomProperty(asRegExpObject(cell), slot.isStrictMode() + ? regExpObjectSetLastIndexStrict + : regExpObjectSetLastIndexNonStrict); + return; + } + Base::put(cell, exec, propertyName, value, slot); +} + +JSValue RegExpObject::exec(ExecState* exec, JSString* string) +{ + if (MatchResult result = match(exec, string)) + return createRegExpMatchesArray(exec, string, regExp(), result); + return jsNull(); +} + +// Shared implementation used by test and exec. +MatchResult RegExpObject::match(ExecState* exec, JSString* string) +{ + RegExp* regExp = this->regExp(); + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + String input = string->value(exec); + VM& vm = exec->vm(); + if (!regExp->global()) + return regExpConstructor->performMatch(vm, regExp, string, input, 0); + + JSValue jsLastIndex = getLastIndex(); + unsigned lastIndex; + if (LIKELY(jsLastIndex.isUInt32())) { + lastIndex = jsLastIndex.asUInt32(); + if (lastIndex > input.length()) { + setLastIndex(exec, 0); + return MatchResult::failed(); + } + } else { + double doubleLastIndex = jsLastIndex.toInteger(exec); + if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { + setLastIndex(exec, 0); + return MatchResult::failed(); + } + lastIndex = static_cast<unsigned>(doubleLastIndex); + } + + MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex); + setLastIndex(exec, result.end); + return result; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpObject.h b/Source/JavaScriptCore/runtime/RegExpObject.h new file mode 100644 index 000000000..a1f571d9f --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpObject.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExpObject_h +#define RegExpObject_h + +#include "JSObject.h" +#include "RegExp.h" + +namespace JSC { + +class RegExpObject : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames; + + static RegExpObject* create(VM& vm, Structure* structure, RegExp* regExp) + { + RegExpObject* object = new (NotNull, allocateCell<RegExpObject>(vm.heap)) RegExpObject(vm, structure, regExp); + object->finishCreation(vm); + return object; + } + + void setRegExp(VM& vm, RegExp* r) { m_regExp.set(vm, this, r); } + RegExp* regExp() const { return m_regExp.get(); } + + void setLastIndex(ExecState* exec, size_t lastIndex) + { + m_lastIndex.setWithoutWriteBarrier(jsNumber(lastIndex)); + if (LIKELY(m_lastIndexIsWritable)) + m_lastIndex.setWithoutWriteBarrier(jsNumber(lastIndex)); + else + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + } + void setLastIndex(ExecState* exec, JSValue lastIndex, bool shouldThrow) + { + if (LIKELY(m_lastIndexIsWritable)) + m_lastIndex.set(exec->vm(), this, lastIndex); + else if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + } + JSValue getLastIndex() const + { + return m_lastIndex.get(); + } + + bool test(ExecState* exec, JSString* string) { return match(exec, string); } + JSValue exec(ExecState*, JSString*); + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + JS_EXPORT_PRIVATE RegExpObject(VM&, Structure*, RegExp*); + JS_EXPORT_PRIVATE void finishCreation(VM&); + + static void visitChildren(JSCell*, SlotVisitor&); + + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); + JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + +private: + MatchResult match(ExecState*, JSString*); + + WriteBarrier<RegExp> m_regExp; + WriteBarrier<Unknown> m_lastIndex; + bool m_lastIndexIsWritable; +}; + +RegExpObject* asRegExpObject(JSValue); + +inline RegExpObject* asRegExpObject(JSValue value) +{ + ASSERT(asObject(value)->inherits(RegExpObject::info())); + return static_cast<RegExpObject*>(asObject(value)); +} + +} // namespace JSC + +#endif // RegExpObject_h diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp new file mode 100644 index 000000000..fb6fc5f4a --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExpPrototype.h" + +#include "ArrayPrototype.h" +#include "Error.h" +#include "JSArray.h" +#include "JSCJSValue.h" +#include "JSFunction.h" +#include "JSObject.h" +#include "JSString.h" +#include "JSStringBuilder.h" +#include "Lexer.h" +#include "ObjectPrototype.h" +#include "JSCInlines.h" +#include "RegExpObject.h" +#include "RegExp.h" +#include "RegExpCache.h" +#include "StringRecursionChecker.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoGetterGlobal(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoGetterIgnoreCase(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoGetterMultiline(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoGetterSource(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoGetterFlags(ExecState*); + +} + +#include "RegExpPrototype.lut.h" + +namespace JSC { + +const ClassInfo RegExpPrototype::s_info = { "RegExp", &RegExpObject::s_info, ®ExpPrototypeTable, CREATE_METHOD_TABLE(RegExpPrototype) }; + +/* Source for RegExpPrototype.lut.h +@begin regExpPrototypeTable + compile regExpProtoFuncCompile DontEnum|Function 2 + exec regExpProtoFuncExec DontEnum|Function 1 + test regExpProtoFuncTest DontEnum|Function 1 + toString regExpProtoFuncToString DontEnum|Function 0 + global regExpProtoGetterGlobal DontEnum|Accessor + ignoreCase regExpProtoGetterIgnoreCase DontEnum|Accessor + multiline regExpProtoGetterMultiline DontEnum|Accessor + source regExpProtoGetterSource DontEnum|Accessor + flags regExpProtoGetterFlags DontEnum|Accessor +@end +*/ + +RegExpPrototype::RegExpPrototype(VM& vm, Structure* structure, RegExp* regExp) + : RegExpObject(vm, structure, regExp) +{ +} + +bool RegExpPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<Base>(exec, regExpPrototypeTable, jsCast<RegExpPrototype*>(object), propertyName, slot); +} + +// ------------------------------ Functions --------------------------- + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(RegExpObject::info())) + return throwVMTypeError(exec); + return JSValue::encode(jsBoolean(asRegExpObject(thisValue)->test(exec, exec->argument(0).toString(exec)))); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(RegExpObject::info())) + return throwVMTypeError(exec); + return JSValue::encode(asRegExpObject(thisValue)->exec(exec, exec->argument(0).toString(exec))); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(RegExpObject::info())) + return throwVMTypeError(exec); + + RegExp* regExp; + JSValue arg0 = exec->argument(0); + JSValue arg1 = exec->argument(1); + + if (arg0.inherits(RegExpObject::info())) { + if (!arg1.isUndefined()) + return throwVMError(exec, createTypeError(exec, ASCIILiteral("Cannot supply flags when constructing one RegExp from another."))); + regExp = asRegExpObject(arg0)->regExp(); + } else { + String pattern = !exec->argumentCount() ? emptyString() : arg0.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + RegExpFlags flags = NoFlags; + if (!arg1.isUndefined()) { + flags = regExpFlags(arg1.toString(exec)->value(exec)); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (flags == InvalidFlags) + return throwVMError(exec, createSyntaxError(exec, ASCIILiteral("Invalid flags supplied to RegExp constructor."))); + } + regExp = RegExp::create(exec->vm(), pattern, flags); + } + + if (!regExp->isValid()) + return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage())); + + asRegExpObject(thisValue)->setRegExp(exec->vm(), regExp); + asRegExpObject(thisValue)->setLastIndex(exec, 0); + return JSValue::encode(jsUndefined()); +} + +typedef std::array<char, 3 + 1> FlagsString; // 3 different flags and a null character terminator. + +static inline FlagsString flagsString(ExecState* exec, JSObject* regexp) +{ + FlagsString string; + + JSValue globalValue = regexp->get(exec, exec->propertyNames().global); + if (exec->hadException()) + return string; + JSValue ignoreCaseValue = regexp->get(exec, exec->propertyNames().ignoreCase); + if (exec->hadException()) + return string; + JSValue multilineValue = regexp->get(exec, exec->propertyNames().multiline); + + unsigned index = 0; + if (globalValue.toBoolean(exec)) + string[index++] = 'g'; + if (ignoreCaseValue.toBoolean(exec)) + string[index++] = 'i'; + if (multilineValue.toBoolean(exec)) + string[index++] = 'm'; + ASSERT(index < string.size()); + string[index] = 0; + return string; +} + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.isObject()) + return throwVMTypeError(exec); + + JSObject* thisObject = asObject(thisValue); + + StringRecursionChecker checker(exec, thisObject); + if (JSValue earlyReturnValue = checker.earlyReturnValue()) + return JSValue::encode(earlyReturnValue); + + JSValue sourceValue = thisObject->get(exec, exec->propertyNames().source); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + String source = sourceValue.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + auto flags = flagsString(exec, thisObject); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(jsMakeNontrivialString(exec, '/', source, '/', flags.data())); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoGetterGlobal(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(RegExpObject::info())) + return throwVMTypeError(exec); + + return JSValue::encode(jsBoolean(asRegExpObject(thisValue)->regExp()->global())); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoGetterIgnoreCase(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(RegExpObject::info())) + return throwVMTypeError(exec); + + return JSValue::encode(jsBoolean(asRegExpObject(thisValue)->regExp()->ignoreCase())); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoGetterMultiline(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(RegExpObject::info())) + return throwVMTypeError(exec); + + return JSValue::encode(jsBoolean(asRegExpObject(thisValue)->regExp()->multiline())); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoGetterFlags(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.isObject()) + return throwVMTypeError(exec); + + auto flags = flagsString(exec, asObject(thisValue)); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(jsString(exec, flags.data())); +} + +template <typename CharacterType> +static inline void appendLineTerminatorEscape(StringBuilder&, CharacterType); + +template <> +inline void appendLineTerminatorEscape<LChar>(StringBuilder& builder, LChar lineTerminator) +{ + if (lineTerminator == '\n') + builder.append('n'); + else + builder.append('r'); +} + +template <> +inline void appendLineTerminatorEscape<UChar>(StringBuilder& builder, UChar lineTerminator) +{ + if (lineTerminator == '\n') + builder.append('n'); + else if (lineTerminator == '\r') + builder.append('r'); + else if (lineTerminator == 0x2028) + builder.appendLiteral("u2028"); + else + builder.appendLiteral("u2029"); +} + +template <typename CharacterType> +static inline JSValue regExpProtoGetterSourceInternal(ExecState* exec, const String& pattern, const CharacterType* characters, unsigned length) +{ + bool previousCharacterWasBackslash = false; + bool inBrackets = false; + bool shouldEscape = false; + + // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/', + // and also states that the result must be a valid RegularExpressionLiteral. '//' is + // not a valid RegularExpressionLiteral (since it is a single line comment), and hence + // source cannot ever validly be "". If the source is empty, return a different Pattern + // that would match the same thing. + if (!length) + return jsNontrivialString(exec, ASCIILiteral("(?:)")); + + // early return for strings that don't contain a forwards slash and LineTerminator + for (unsigned i = 0; i < length; ++i) { + CharacterType ch = characters[i]; + if (!previousCharacterWasBackslash) { + if (inBrackets) { + if (ch == ']') + inBrackets = false; + } else { + if (ch == '/') { + shouldEscape = true; + break; + } + if (ch == '[') + inBrackets = true; + } + } + + if (Lexer<CharacterType>::isLineTerminator(ch)) { + shouldEscape = true; + break; + } + + if (previousCharacterWasBackslash) + previousCharacterWasBackslash = false; + else + previousCharacterWasBackslash = ch == '\\'; + } + + if (!shouldEscape) + return jsString(exec, pattern); + + previousCharacterWasBackslash = false; + inBrackets = false; + StringBuilder result; + for (unsigned i = 0; i < length; ++i) { + CharacterType ch = characters[i]; + if (!previousCharacterWasBackslash) { + if (inBrackets) { + if (ch == ']') + inBrackets = false; + } else { + if (ch == '/') + result.append('\\'); + else if (ch == '[') + inBrackets = true; + } + } + + // escape LineTerminator + if (Lexer<CharacterType>::isLineTerminator(ch)) { + if (!previousCharacterWasBackslash) + result.append('\\'); + + appendLineTerminatorEscape<CharacterType>(result, ch); + } else + result.append(ch); + + if (previousCharacterWasBackslash) + previousCharacterWasBackslash = false; + else + previousCharacterWasBackslash = ch == '\\'; + } + + return jsString(exec, result.toString()); +} + +EncodedJSValue JSC_HOST_CALL regExpProtoGetterSource(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!thisValue.inherits(RegExpObject::info())) + return throwVMTypeError(exec); + + String pattern = asRegExpObject(thisValue)->regExp()->pattern(); + if (pattern.is8Bit()) + return JSValue::encode(regExpProtoGetterSourceInternal(exec, pattern, pattern.characters8(), pattern.length())); + return JSValue::encode(regExpProtoGetterSourceInternal(exec, pattern, pattern.characters16(), pattern.length())); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.h b/Source/JavaScriptCore/runtime/RegExpPrototype.h new file mode 100644 index 000000000..cb6ee6974 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef RegExpPrototype_h +#define RegExpPrototype_h + +#include "RegExpObject.h" +#include "JSObject.h" + +namespace JSC { + +class RegExpPrototype : public RegExpObject { +public: + typedef RegExpObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static RegExpPrototype* create(VM& vm, Structure* structure, RegExp* regExp) + { + RegExpPrototype* prototype = new (NotNull, allocateCell<RegExpPrototype>(vm.heap)) RegExpPrototype(vm, structure, regExp); + prototype->finishCreation(vm); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + RegExpPrototype(VM&, Structure*, RegExp*); + +private: + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // RegExpPrototype_h diff --git a/Source/JavaScriptCore/runtime/RegisterPreservationMode.h b/Source/JavaScriptCore/runtime/RegisterPreservationMode.h new file mode 100644 index 000000000..3351db4a3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegisterPreservationMode.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef RegisterPreservationMode_h +#define RegisterPreservationMode_h + +namespace JSC { + +enum RegisterPreservationMode { + RegisterPreservationNotRequired, + MustPreserveRegisters +}; + +} // namespace JSC + +#endif // RegisterPreservationMode_h + diff --git a/Source/JavaScriptCore/runtime/Reject.h b/Source/JavaScriptCore/runtime/Reject.h new file mode 100644 index 000000000..7f1a5db4d --- /dev/null +++ b/Source/JavaScriptCore/runtime/Reject.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Reject_h +#define Reject_h + +#include "CallFrame.h" +#include "Error.h" + +namespace JSC { + +inline bool reject(ExecState* exec, bool throwException, const char* message) +{ + if (throwException) + throwTypeError(exec, message); + return false; +} + +} // namespace JSC + +#endif // Reject_h + diff --git a/Source/JavaScriptCore/runtime/RuntimeFlags.h b/Source/JavaScriptCore/runtime/RuntimeFlags.h new file mode 100644 index 000000000..8cd30c6da --- /dev/null +++ b/Source/JavaScriptCore/runtime/RuntimeFlags.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef RuntimeFlags_h +#define RuntimeFlags_h + +#include <initializer_list> + +namespace JSC { + +// macro(name, isEnabledFlag) +#define JSC_RUNTIME_FLAG(macro) + +class RuntimeFlags { +private: + enum RuntimeFlagShiftValue : unsigned { +#define JSC_DECLARE_RUNTIME_FLAG_SHIFT_VALUE(name, isEnabledFlag) shiftValueFor##name, + JSC_RUNTIME_FLAG(JSC_DECLARE_RUNTIME_FLAG_SHIFT_VALUE) +#undef JSC_DECLARE_RUNTIME_FLAG_SHIFT_VALUE + }; + +public: + enum RuntimeFlag : unsigned { +#define JSC_DECLARE_RUNTIME_FLAG(name, isEnabledFlag) name = 1u << (shiftValueFor##name), + JSC_RUNTIME_FLAG(JSC_DECLARE_RUNTIME_FLAG) +#undef JSC_DECLARE_RUNTIME_FLAG + }; + +#define JSC_DECLARE_RUNTIME_FLAG_ACCESSOR(name, isEnabledFlag) \ + void set##name(bool value)\ + {\ + if (value)\ + m_flags |= name;\ + else\ + m_flags &= (~name);\ + }\ + bool is##name() const\ + {\ + return m_flags & name;\ + } + JSC_RUNTIME_FLAG(JSC_DECLARE_RUNTIME_FLAG_ACCESSOR) +#undef JSC_DECLARE_RUNTIME_FLAG_ACCESSOR + + RuntimeFlags() + : RuntimeFlags(0u) + { + } + + RuntimeFlags(std::initializer_list<RuntimeFlag> initializerList) + : RuntimeFlags() + { + for (RuntimeFlag flag : initializerList) + m_flags |= flag; + } + + explicit RuntimeFlags(unsigned flags) + : m_flags(flags) + { + } + + static RuntimeFlags createAllEnabled() + { + return RuntimeFlags( +#define JSC_USE_RUNTIME_FLAG(name, isEnabledFlag) ((isEnabledFlag) ? name : 0u) | + JSC_RUNTIME_FLAG(JSC_USE_RUNTIME_FLAG) +#undef JSC_USE_RUNTIME_FLAG + 0u + ); + } + +private: + unsigned m_flags; +}; + +} // namespace JSC + +#endif // RuntimeFlags_h diff --git a/Source/JavaScriptCore/runtime/RuntimeType.cpp b/Source/JavaScriptCore/runtime/RuntimeType.cpp new file mode 100644 index 000000000..4e7b3a7e1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RuntimeType.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * Copyright (C) Saam Barati <saambarati1@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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "RuntimeType.h" + +#include "JSCJSValue.h" +#include "JSCJSValueInlines.h" + +namespace JSC { + +RuntimeType runtimeTypeForValue(JSValue value) +{ + if (value.isUndefined()) + return TypeUndefined; + if (value.isNull()) + return TypeNull; + if (value.isMachineInt()) + return TypeMachineInt; + if (value.isNumber()) + return TypeNumber; + if (value.isString()) + return TypeString; + if (value.isBoolean()) + return TypeBoolean; + if (value.isObject()) + return TypeObject; + if (value.isFunction()) + return TypeFunction; + if (value.isSymbol()) + return TypeSymbol; + + return TypeNothing; +} + +String runtimeTypeAsString(RuntimeType type) +{ + if (type == TypeUndefined) + return ASCIILiteral("Undefined"); + if (type == TypeNull) + return ASCIILiteral("Null"); + if (type == TypeMachineInt) + return ASCIILiteral("Integer"); + if (type == TypeNumber) + return ASCIILiteral("Number"); + if (type == TypeString) + return ASCIILiteral("String"); + if (type == TypeObject) + return ASCIILiteral("Object"); + if (type == TypeBoolean) + return ASCIILiteral("Boolean"); + if (type == TypeFunction) + return ASCIILiteral("Function"); + if (type == TypeNothing) + return ASCIILiteral("(Nothing)"); + + RELEASE_ASSERT_NOT_REACHED(); + return emptyString(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/RuntimeType.h b/Source/JavaScriptCore/runtime/RuntimeType.h new file mode 100644 index 000000000..d57b8fbf0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RuntimeType.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * Copyright (C) Saam Barati <saambarati1@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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef RuntimeType_h +#define RuntimeType_h + +#include <wtf/text/WTFString.h> + +namespace JSC { + +enum RuntimeType : uint16_t { + TypeNothing = 0x0, + TypeFunction = 0x1, + TypeUndefined = 0x2, + TypeNull = 0x4, + TypeBoolean = 0x8, + TypeMachineInt = 0x10, + TypeNumber = 0x20, + TypeString = 0x40, + TypeObject = 0x80, + TypeSymbol = 0x100 +}; + +typedef uint16_t RuntimeTypeMask; + +class JSValue; +RuntimeType runtimeTypeForValue(JSValue); +String runtimeTypeAsString(RuntimeType); + +ALWAYS_INLINE bool runtimeTypeIsPrimitive(RuntimeTypeMask type) +{ + return type & ~(TypeFunction | TypeObject); +} + +} // namespace JSC + +#endif // RuntimeType_h diff --git a/Source/JavaScriptCore/runtime/SamplingCounter.cpp b/Source/JavaScriptCore/runtime/SamplingCounter.cpp new file mode 100644 index 000000000..b5564b4f9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SamplingCounter.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "SamplingCounter.h" + +namespace JSC { + +void AbstractSamplingCounter::dump() +{ +#if ENABLE(SAMPLING_COUNTERS) + if (s_abstractSamplingCounterChain != &s_abstractSamplingCounterChainEnd) { + dataLogF("\nSampling Counter Values:\n"); + for (AbstractSamplingCounter* currCounter = s_abstractSamplingCounterChain; (currCounter != &s_abstractSamplingCounterChainEnd); currCounter = currCounter->m_next) + dataLogF("\t%s\t: %lld\n", currCounter->m_name, currCounter->m_counter); + dataLogF("\n\n"); + } + s_completed = true; +#endif +} + +AbstractSamplingCounter AbstractSamplingCounter::s_abstractSamplingCounterChainEnd; +AbstractSamplingCounter* AbstractSamplingCounter::s_abstractSamplingCounterChain = &s_abstractSamplingCounterChainEnd; +bool AbstractSamplingCounter::s_completed = false; + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/SamplingCounter.h b/Source/JavaScriptCore/runtime/SamplingCounter.h new file mode 100644 index 000000000..d1acdcee5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SamplingCounter.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2011 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef SamplingCounter_h +#define SamplingCounter_h + +#include <stdint.h> +#include <wtf/Assertions.h> + +namespace JSC { + +// AbstractSamplingCounter: +// +// Implements a named set of counters, printed on exit if ENABLE(SAMPLING_COUNTERS). +// See subclasses below, SamplingCounter, GlobalSamplingCounter and DeletableSamplingCounter. +class AbstractSamplingCounter { + friend class DeletableSamplingCounter; +public: + void count(uint32_t count = 1) + { + m_counter += count; + } + + JS_EXPORT_PRIVATE static void dump(); + + int64_t* addressOfCounter() { return &m_counter; } + +protected: + // Effectively the contructor, however called lazily in the case of GlobalSamplingCounter. + void init(const char* name) + { + m_counter = 0; + m_name = name; + + // Set m_next to point to the head of the chain, and inform whatever is + // currently at the head that this node will now hold the pointer to it. + m_next = s_abstractSamplingCounterChain; + s_abstractSamplingCounterChain->m_referer = &m_next; + // Add this node to the head of the list. + s_abstractSamplingCounterChain = this; + m_referer = &s_abstractSamplingCounterChain; + } + + int64_t m_counter; + const char* m_name; + AbstractSamplingCounter* m_next; + // This is a pointer to the pointer to this node in the chain; used to + // allow fast linked list deletion. + AbstractSamplingCounter** m_referer; + // Null object used to detect end of static chain. + static AbstractSamplingCounter s_abstractSamplingCounterChainEnd; + JS_EXPORTDATA static AbstractSamplingCounter* s_abstractSamplingCounterChain; + static bool s_completed; +}; + +#if ENABLE(SAMPLING_COUNTERS) +// SamplingCounter: +// +// This class is suitable and (hopefully!) convenient for cases where a counter is +// required within the scope of a single function. It can be instantiated as a +// static variable since it contains a constructor but not a destructor (static +// variables in WebKit cannot have destructors). +// +// For example: +// +// void someFunction() +// { +// static SamplingCounter countMe("This is my counter. There are many like it, but this one is mine."); +// countMe.count(); +// // ... +// } +// +class SamplingCounter : public AbstractSamplingCounter { +public: + SamplingCounter(const char* name) { init(name); } +}; + +// GlobalSamplingCounter: +// +// This class is suitable for use where a counter is to be declared globally, +// since it contains neither a constructor nor destructor. Instead, ensure +// that 'name()' is called to provide the counter with a name (and also to +// allow it to be printed out on exit). +// +// GlobalSamplingCounter globalCounter; +// +// void firstFunction() +// { +// // Put this within a function that is definitely called! +// // (Or alternatively alongside all calls to 'count()'). +// globalCounter.name("I Name You Destroyer."); +// globalCounter.count(); +// // ... +// } +// +// void secondFunction() +// { +// globalCounter.count(); +// // ... +// } +// +class GlobalSamplingCounter : public AbstractSamplingCounter { +public: + void name(const char* name) + { + // Global objects should be mapped in zero filled memory, so this should + // be a safe (albeit not necessarily threadsafe) check for 'first call'. + if (!m_next) + init(name); + } +}; + +// DeletableSamplingCounter: +// +// The above classes (SamplingCounter, GlobalSamplingCounter), are intended for +// use within a global or static scope, and as such cannot have a destructor. +// This means there is no convenient way for them to remove themselves from the +// static list of counters, and should an instance of either class be freed +// before 'dump()' has walked over the list it will potentially walk over an +// invalid pointer. +// +// This class is intended for use where the counter may possibly be deleted before +// the program exits. Should this occur, the counter will print it's value to +// stderr, and remove itself from the static list. Example: +// +// DeletableSamplingCounter* counter = new DeletableSamplingCounter("The Counter With No Name"); +// counter->count(); +// delete counter; +// +class DeletableSamplingCounter : public AbstractSamplingCounter { +public: + DeletableSamplingCounter(const char* name) { init(name); } + + ~DeletableSamplingCounter() + { + if (!s_completed) + dataFile("DeletableSamplingCounter \"%s\" deleted early (with count %lld)\n", m_name, m_counter); + // Our m_referer pointer should know where the pointer to this node is, + // and m_next should know that this node is the previous node in the list. + ASSERT(*m_referer == this); + ASSERT(m_next->m_referer == &m_next); + // Remove this node from the list, and inform m_next that we have done so. + m_next->m_referer = m_referer; + *m_referer = m_next; + } +}; +#endif + +} // namespace JSC + +#endif // SamplingCounter_h + + diff --git a/Source/JavaScriptCore/runtime/ScopeOffset.cpp b/Source/JavaScriptCore/runtime/ScopeOffset.cpp new file mode 100644 index 000000000..1753165e5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopeOffset.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ScopeOffset.h" + +namespace JSC { + +void ScopeOffset::dump(PrintStream& out) const +{ + if (!*this) { + out.print("scopeInvalid"); + return; + } + + out.print("scope", offset()); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/ScopeOffset.h b/Source/JavaScriptCore/runtime/ScopeOffset.h new file mode 100644 index 000000000..4dbd18605 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopeOffset.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ScopeOffset_h +#define ScopeOffset_h + +#include "GenericOffset.h" +#include <wtf/PrintStream.h> + +namespace JSC { + +// This is an offset into a scope of some kind. It could be an activation scope or it could be a +// global object. +class ScopeOffset : public GenericOffset<ScopeOffset> { +public: + ScopeOffset() { } + + explicit ScopeOffset(unsigned offset) + : GenericOffset(offset) + { + } + + void dump(PrintStream&) const; +}; + +} // namespace JSC + +#endif // ScopeOffset_h + diff --git a/Source/JavaScriptCore/runtime/ScopedArguments.cpp b/Source/JavaScriptCore/runtime/ScopedArguments.cpp new file mode 100644 index 000000000..a5a2fc75b --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopedArguments.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ScopedArguments.h" + +#include "GenericArgumentsInlines.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ScopedArguments); + +const ClassInfo ScopedArguments::s_info = { "Arguments", &Base::s_info, 0, CREATE_METHOD_TABLE(ScopedArguments) }; + +ScopedArguments::ScopedArguments(VM& vm, Structure* structure, unsigned totalLength) + : GenericArguments(vm, structure) + , m_overrodeThings(false) + , m_totalLength(totalLength) +{ +} + +void ScopedArguments::finishCreation(VM& vm, JSFunction* callee, ScopedArgumentsTable* table, JSLexicalEnvironment* scope) +{ + Base::finishCreation(vm); + m_callee.set(vm, this, callee); + m_table.set(vm, this, table); + m_scope.set(vm, this, scope); +} + +ScopedArguments* ScopedArguments::createUninitialized(VM& vm, Structure* structure, JSFunction* callee, ScopedArgumentsTable* table, JSLexicalEnvironment* scope, unsigned totalLength) +{ + unsigned overflowLength; + if (totalLength > table->length()) + overflowLength = totalLength - table->length(); + else + overflowLength = 0; + ScopedArguments* result = new ( + NotNull, + allocateCell<ScopedArguments>(vm.heap, allocationSize(overflowLength))) + ScopedArguments(vm, structure, totalLength); + result->finishCreation(vm, callee, table, scope); + return result; +} + +ScopedArguments* ScopedArguments::create(VM& vm, Structure* structure, JSFunction* callee, ScopedArgumentsTable* table, JSLexicalEnvironment* scope, unsigned totalLength) +{ + ScopedArguments* result = + createUninitialized(vm, structure, callee, table, scope, totalLength); + + unsigned namedLength = table->length(); + for (unsigned i = namedLength; i < totalLength; ++i) + result->overflowStorage()[i - namedLength].clear(); + + return result; +} + +ScopedArguments* ScopedArguments::createByCopying(ExecState* exec, ScopedArgumentsTable* table, JSLexicalEnvironment* scope) +{ + return createByCopyingFrom( + exec->vm(), exec->lexicalGlobalObject()->scopedArgumentsStructure(), + exec->registers() + CallFrame::argumentOffset(0), exec->argumentCount(), + jsCast<JSFunction*>(exec->callee()), table, scope); +} + +ScopedArguments* ScopedArguments::createByCopyingFrom(VM& vm, Structure* structure, Register* argumentsStart, unsigned totalLength, JSFunction* callee, ScopedArgumentsTable* table, JSLexicalEnvironment* scope) +{ + ScopedArguments* result = + createUninitialized(vm, structure, callee, table, scope, totalLength); + + unsigned namedLength = table->length(); + for (unsigned i = namedLength; i < totalLength; ++i) + result->overflowStorage()[i - namedLength].set(vm, result, argumentsStart[i].jsValue()); + + return result; +} + +void ScopedArguments::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + ScopedArguments* thisObject = static_cast<ScopedArguments*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + visitor.append(&thisObject->m_callee); + visitor.append(&thisObject->m_table); + visitor.append(&thisObject->m_scope); + + if (thisObject->m_totalLength > thisObject->m_table->length()) { + visitor.appendValues( + thisObject->overflowStorage(), thisObject->m_totalLength - thisObject->m_table->length()); + } +} + +Structure* ScopedArguments::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ScopedArgumentsType, StructureFlags), info()); +} + +void ScopedArguments::overrideThings(VM& vm) +{ + RELEASE_ASSERT(!m_overrodeThings); + + putDirect(vm, vm.propertyNames->length, jsNumber(m_table->length()), DontEnum); + putDirect(vm, vm.propertyNames->callee, m_callee.get(), DontEnum); + putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject()->arrayProtoValuesFunction(), DontEnum); + + m_overrodeThings = true; +} + +void ScopedArguments::overrideThingsIfNecessary(VM& vm) +{ + if (!m_overrodeThings) + overrideThings(vm); +} + +void ScopedArguments::overrideArgument(VM& vm, uint32_t i) +{ + ASSERT_WITH_SECURITY_IMPLICATION(i < m_totalLength); + unsigned namedLength = m_table->length(); + if (i < namedLength) + m_table.set(vm, this, m_table->set(vm, i, ScopeOffset())); + else + overflowStorage()[i - namedLength].clear(); +} + +void ScopedArguments::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length) +{ + GenericArguments::copyToArguments(exec, firstElementDest, offset, length); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/ScopedArguments.h b/Source/JavaScriptCore/runtime/ScopedArguments.h new file mode 100644 index 000000000..8d36a1bab --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopedArguments.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ScopedArguments_h +#define ScopedArguments_h + +#include "GenericArguments.h" +#include "JSLexicalEnvironment.h" + +namespace JSC { + +// This is an Arguments-class object that we create when you say "arguments" inside a function, +// and one or more of the arguments may be captured in the function's activation. The function +// will copy its formally declared arguments into the activation and then create this object. This +// object will store the overflow arguments, if there are any. This object will use the symbol +// table's ScopedArgumentsTable and the activation, or its overflow storage, to handle all indexed +// lookups. +class ScopedArguments : public GenericArguments<ScopedArguments> { +private: + ScopedArguments(VM&, Structure*, unsigned totalLength); + void finishCreation(VM&, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*); + +public: + // Creates an arguments object but leaves it uninitialized. This is dangerous if we GC right + // after allocation. + static ScopedArguments* createUninitialized(VM&, Structure*, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*, unsigned totalLength); + + // Creates an arguments object and initializes everything to the empty value. Use this if you + // cannot guarantee that you'll immediately initialize all of the elements. + static ScopedArguments* create(VM&, Structure*, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*, unsigned totalLength); + + // Creates an arguments object by copying the arguments from the stack. + static ScopedArguments* createByCopying(ExecState*, ScopedArgumentsTable*, JSLexicalEnvironment*); + + // Creates an arguments object by copying the arguments from a well-defined stack location. + static ScopedArguments* createByCopyingFrom(VM&, Structure*, Register* argumentsStart, unsigned totalLength, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*); + + static void visitChildren(JSCell*, SlotVisitor&); + + uint32_t internalLength() const + { + return m_totalLength; + } + + uint32_t length(ExecState* exec) const + { + if (UNLIKELY(m_overrodeThings)) + return get(exec, exec->propertyNames().length).toUInt32(exec); + return internalLength(); + } + + bool canAccessIndexQuickly(uint32_t i) const + { + if (i >= m_totalLength) + return false; + unsigned namedLength = m_table->length(); + if (i < namedLength) + return !!m_table->get(i); + return !!overflowStorage()[i - namedLength].get(); + } + + bool canAccessArgumentIndexQuicklyInDFG(uint32_t i) const + { + return canAccessIndexQuickly(i); + } + + JSValue getIndexQuickly(uint32_t i) const + { + ASSERT_WITH_SECURITY_IMPLICATION(canAccessIndexQuickly(i)); + unsigned namedLength = m_table->length(); + if (i < namedLength) + return m_scope->variableAt(m_table->get(i)).get(); + return overflowStorage()[i - namedLength].get(); + } + + void setIndexQuickly(VM& vm, uint32_t i, JSValue value) + { + ASSERT_WITH_SECURITY_IMPLICATION(canAccessIndexQuickly(i)); + unsigned namedLength = m_table->length(); + if (i < namedLength) + m_scope->variableAt(m_table->get(i)).set(vm, this, value); + else + overflowStorage()[i - namedLength].set(vm, this, value); + } + + WriteBarrier<JSFunction>& callee() + { + return m_callee; + } + + bool overrodeThings() const { return m_overrodeThings; } + void overrideThings(VM&); + void overrideThingsIfNecessary(VM&); + void overrideArgument(VM&, uint32_t index); + + void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length); + + DECLARE_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + static ptrdiff_t offsetOfOverrodeThings() { return OBJECT_OFFSETOF(ScopedArguments, m_overrodeThings); } + static ptrdiff_t offsetOfTotalLength() { return OBJECT_OFFSETOF(ScopedArguments, m_totalLength); } + static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(ScopedArguments, m_table); } + static ptrdiff_t offsetOfScope() { return OBJECT_OFFSETOF(ScopedArguments, m_scope); } + + static size_t overflowStorageOffset() + { + return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<Unknown>)>(sizeof(ScopedArguments)); + } + + static size_t allocationSize(unsigned overflowArgumentsLength) + { + return overflowStorageOffset() + sizeof(WriteBarrier<Unknown>) * overflowArgumentsLength; + } + +private: + WriteBarrier<Unknown>* overflowStorage() const + { + return bitwise_cast<WriteBarrier<Unknown>*>( + bitwise_cast<char*>(this) + overflowStorageOffset()); + } + + + bool m_overrodeThings; // True if length, callee, and caller are fully materialized in the object. + unsigned m_totalLength; // The length of declared plus overflow arguments. + WriteBarrier<JSFunction> m_callee; + WriteBarrier<ScopedArgumentsTable> m_table; + WriteBarrier<JSLexicalEnvironment> m_scope; +}; + +} // namespace JSC + +#endif // ScopedArguments_h + diff --git a/Source/JavaScriptCore/runtime/ScopedArgumentsTable.cpp b/Source/JavaScriptCore/runtime/ScopedArgumentsTable.cpp new file mode 100644 index 000000000..9be4797b0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopedArgumentsTable.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "ScopedArgumentsTable.h" + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo ScopedArgumentsTable::s_info = { "ScopedArgumentsTable", 0, 0, CREATE_METHOD_TABLE(ScopedArgumentsTable) }; + +ScopedArgumentsTable::ScopedArgumentsTable(VM& vm) + : Base(vm, vm.scopedArgumentsTableStructure.get()) + , m_length(0) + , m_locked(false) +{ +} + +ScopedArgumentsTable::~ScopedArgumentsTable() +{ +} + +void ScopedArgumentsTable::destroy(JSCell* cell) +{ + static_cast<ScopedArgumentsTable*>(cell)->ScopedArgumentsTable::~ScopedArgumentsTable(); +} + +ScopedArgumentsTable* ScopedArgumentsTable::create(VM& vm) +{ + ScopedArgumentsTable* result = + new (NotNull, allocateCell<ScopedArgumentsTable>(vm.heap)) ScopedArgumentsTable(vm); + result->finishCreation(vm); + return result; +} + +ScopedArgumentsTable* ScopedArgumentsTable::create(VM& vm, uint32_t length) +{ + ScopedArgumentsTable* result = create(vm); + result->m_length = length; + result->m_arguments = std::make_unique<ScopeOffset[]>(length); + return result; +} + +ScopedArgumentsTable* ScopedArgumentsTable::clone(VM& vm) +{ + ScopedArgumentsTable* result = create(vm, m_length); + for (unsigned i = m_length; i--;) + result->m_arguments[i] = m_arguments[i]; + return result; +} + +ScopedArgumentsTable* ScopedArgumentsTable::setLength(VM& vm, uint32_t newLength) +{ + if (LIKELY(!m_locked)) { + std::unique_ptr<ScopeOffset[]> newArguments = std::make_unique<ScopeOffset[]>(newLength); + for (unsigned i = std::min(m_length, newLength); i--;) + newArguments[i] = m_arguments[i]; + m_length = newLength; + m_arguments = WTF::move(newArguments); + return this; + } + + ScopedArgumentsTable* result = create(vm, newLength); + for (unsigned i = std::min(m_length, newLength); i--;) + result->m_arguments[i] = m_arguments[i]; + return result; +} + +ScopedArgumentsTable* ScopedArgumentsTable::set(VM& vm, uint32_t i, ScopeOffset value) +{ + ScopedArgumentsTable* result; + if (UNLIKELY(m_locked)) + result = clone(vm); + else + result = this; + result->at(i) = value; + return result; +} + +Structure* ScopedArgumentsTable::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/ScopedArgumentsTable.h b/Source/JavaScriptCore/runtime/ScopedArgumentsTable.h new file mode 100644 index 000000000..b6d7ddc17 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScopedArgumentsTable.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ScopedArgumentsTable_h +#define ScopedArgumentsTable_h + +#include "JSCell.h" +#include "ScopeOffset.h" +#include <wtf/Assertions.h> + +namespace JSC { + +// This class's only job is to hold onto the list of ScopeOffsets for each argument that a +// function has. Most of the time, the BytecodeGenerator will create one of these and it will +// never be modified subsequently. There is a rare case where a ScopedArguments object is created +// and aliases one of these and then decides to modify it; in that case we do copy-on-write. This +// makes sense because such modifications are so uncommon. You'd have to do something crazy like +// "delete arguments[i]" or some variant of defineOwnProperty. +class ScopedArgumentsTable final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + +private: + ScopedArgumentsTable(VM&); + ~ScopedArgumentsTable(); + +public: + static ScopedArgumentsTable* create(VM&); + static ScopedArgumentsTable* create(VM&, uint32_t length); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + ScopedArgumentsTable* clone(VM&); + + uint32_t length() const { return m_length; } + ScopedArgumentsTable* setLength(VM&, uint32_t newLength); + + ScopeOffset get(uint32_t i) const + { + return const_cast<ScopedArgumentsTable*>(this)->at(i); + } + + void lock() + { + m_locked = true; + } + + ScopedArgumentsTable* set(VM&, uint32_t index, ScopeOffset); + + DECLARE_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(ScopedArgumentsTable, m_length); } + static ptrdiff_t offsetOfArguments() { return OBJECT_OFFSETOF(ScopedArgumentsTable, m_arguments); } + +private: + ScopeOffset& at(uint32_t i) + { + ASSERT_WITH_SECURITY_IMPLICATION(i < m_length); + return m_arguments[i]; + } + + uint32_t m_length; + bool m_locked; // Being locked means that there are multiple references to this object and none of them expect to see the others' modifications. This means that modifications need to make a copy first. + std::unique_ptr<ScopeOffset[]> m_arguments; +}; + +} // namespace JSC + +#endif // ScopedArgumentsTable_h + diff --git a/Source/JavaScriptCore/runtime/SetConstructor.cpp b/Source/JavaScriptCore/runtime/SetConstructor.cpp new file mode 100644 index 000000000..fb97b6d9f --- /dev/null +++ b/Source/JavaScriptCore/runtime/SetConstructor.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "SetConstructor.h" + +#include "Error.h" +#include "IteratorOperations.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "JSSet.h" +#include "MapData.h" +#include "SetPrototype.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo SetConstructor::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(SetConstructor) }; + +void SetConstructor::finishCreation(VM& vm, SetPrototype* setPrototype) +{ + Base::finishCreation(vm, setPrototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, setPrototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), ReadOnly | DontEnum | DontDelete); +} + +static EncodedJSValue JSC_HOST_CALL callSet(ExecState* exec) +{ + return JSValue::encode(throwTypeError(exec, ASCIILiteral("Set cannot be called as a function"))); +} + +static EncodedJSValue JSC_HOST_CALL constructSet(ExecState* exec) +{ + JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject(); + Structure* setStructure = globalObject->setStructure(); + JSSet* set = JSSet::create(exec, setStructure); + JSValue iterable = exec->argument(0); + if (iterable.isUndefinedOrNull()) + return JSValue::encode(set); + + JSValue adderFunction = set->get(exec, exec->propertyNames().add); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData adderFunctionCallData; + CallType adderFunctionCallType = getCallData(adderFunction, adderFunctionCallData); + if (adderFunctionCallType == CallTypeNone) + return JSValue::encode(throwTypeError(exec)); + + JSValue iteratorFunction = iterable.get(exec, exec->propertyNames().iteratorSymbol); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData iteratorFunctionCallData; + CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData); + if (iteratorFunctionCallType == CallTypeNone) + return JSValue::encode(throwTypeError(exec)); + + ArgList iteratorFunctionArguments; + JSValue iterator = call(exec, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (!iterator.isObject()) + return JSValue::encode(throwTypeError(exec)); + + while (true) { + JSValue next = iteratorStep(exec, iterator); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (next.isFalse()) + return JSValue::encode(set); + + JSValue nextValue = iteratorValue(exec, next); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + MarkedArgumentBuffer arguments; + arguments.append(nextValue); + call(exec, adderFunction, adderFunctionCallType, adderFunctionCallData, set, arguments); + if (exec->hadException()) { + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + } + RELEASE_ASSERT_NOT_REACHED(); + return JSValue::encode(set); +} + +ConstructType SetConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructSet; + return ConstructTypeHost; +} + +CallType SetConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callSet; + return CallTypeHost; +} + +} diff --git a/Source/JavaScriptCore/runtime/SetConstructor.h b/Source/JavaScriptCore/runtime/SetConstructor.h new file mode 100644 index 000000000..efbd88bb3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SetConstructor.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef SetConstructor_h +#define SetConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class SetPrototype; + +class SetConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + + static SetConstructor* create(VM& vm, Structure* structure, SetPrototype* setPrototype) + { + SetConstructor* constructor = new (NotNull, allocateCell<SetConstructor>(vm.heap)) SetConstructor(vm, structure); + constructor->finishCreation(vm, setPrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + SetConstructor(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, SetPrototype*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} + +#endif // !defined(MapConstructor_h) diff --git a/Source/JavaScriptCore/runtime/SetIteratorPrototype.cpp b/Source/JavaScriptCore/runtime/SetIteratorPrototype.cpp new file mode 100644 index 000000000..ab8df4447 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SetIteratorPrototype.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "SetIteratorPrototype.h" + +#include "IteratorOperations.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSSetIterator.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo SetIteratorPrototype::s_info = { "Set Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(SetIteratorPrototype) }; + +static EncodedJSValue JSC_HOST_CALL SetIteratorPrototypeFuncNext(ExecState*); + +void SetIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + JSC_NATIVE_FUNCTION(vm.propertyNames->next, SetIteratorPrototypeFuncNext, DontEnum, 0); +} + +EncodedJSValue JSC_HOST_CALL SetIteratorPrototypeFuncNext(CallFrame* callFrame) +{ + JSValue result; + JSSetIterator* iterator = jsDynamicCast<JSSetIterator*>(callFrame->thisValue()); + if (!iterator) + return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot call SetIterator.next() on a non-SetIterator object"))); + + if (iterator->next(callFrame, result)) + return JSValue::encode(createIteratorResultObject(callFrame, result, false)); + iterator->finish(); + return JSValue::encode(createIteratorResultObject(callFrame, jsUndefined(), true)); +} + +} diff --git a/Source/JavaScriptCore/runtime/SetIteratorPrototype.h b/Source/JavaScriptCore/runtime/SetIteratorPrototype.h new file mode 100644 index 000000000..72b3330ba --- /dev/null +++ b/Source/JavaScriptCore/runtime/SetIteratorPrototype.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef SetIteratorPrototype_h +#define SetIteratorPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class SetIteratorPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static SetIteratorPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + SetIteratorPrototype* prototype = new (NotNull, allocateCell<SetIteratorPrototype>(vm.heap)) SetIteratorPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + SetIteratorPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); +}; + +} + +#endif // !defined(SetIteratorPrototype_h) diff --git a/Source/JavaScriptCore/runtime/SetPrototype.cpp b/Source/JavaScriptCore/runtime/SetPrototype.cpp new file mode 100644 index 000000000..e83e38325 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SetPrototype.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "SetPrototype.h" + +#include "CachedCall.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "GetterSetter.h" +#include "JSCJSValueInlines.h" +#include "JSFunctionInlines.h" +#include "JSSet.h" +#include "JSSetIterator.h" +#include "MapDataInlines.h" +#include "StructureInlines.h" + +namespace JSC { + +const ClassInfo SetPrototype::s_info = { "Set", &Base::s_info, 0, CREATE_METHOD_TABLE(SetPrototype) }; + +static EncodedJSValue JSC_HOST_CALL setProtoFuncAdd(ExecState*); +static EncodedJSValue JSC_HOST_CALL setProtoFuncClear(ExecState*); +static EncodedJSValue JSC_HOST_CALL setProtoFuncDelete(ExecState*); +static EncodedJSValue JSC_HOST_CALL setProtoFuncForEach(ExecState*); +static EncodedJSValue JSC_HOST_CALL setProtoFuncHas(ExecState*); +static EncodedJSValue JSC_HOST_CALL setProtoFuncValues(ExecState*); +static EncodedJSValue JSC_HOST_CALL setProtoFuncEntries(ExecState*); + + +static EncodedJSValue JSC_HOST_CALL setProtoFuncSize(ExecState*); + +void SetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + JSC_NATIVE_FUNCTION(vm.propertyNames->add, setProtoFuncAdd, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->clear, setProtoFuncClear, DontEnum, 0); + JSC_NATIVE_FUNCTION(vm.propertyNames->deleteKeyword, setProtoFuncDelete, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->forEach, setProtoFuncForEach, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->has, setProtoFuncHas, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->entries, setProtoFuncEntries, DontEnum, 0); + + JSFunction* values = JSFunction::create(vm, globalObject, 0, vm.propertyNames->values.string(), setProtoFuncValues); + putDirectWithoutTransition(vm, vm.propertyNames->values, values, DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->keys, values, DontEnum); + putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, values, DontEnum); + + GetterSetter* accessor = GetterSetter::create(vm, globalObject); + JSFunction* function = JSFunction::create(vm, globalObject, 0, vm.propertyNames->size.string(), setProtoFuncSize); + accessor->setGetter(vm, globalObject, function); + putDirectNonIndexAccessor(vm, vm.propertyNames->size, accessor, DontEnum | Accessor); +} + +ALWAYS_INLINE static JSSet* getSet(CallFrame* callFrame, JSValue thisValue) +{ + if (!thisValue.isObject()) { + throwVMError(callFrame, createNotAnObjectError(callFrame, thisValue)); + return nullptr; + } + JSSet* set = jsDynamicCast<JSSet*>(thisValue); + if (!set) { + throwTypeError(callFrame, ASCIILiteral("Set operation called on non-Set object")); + return nullptr; + } + return set; +} + +EncodedJSValue JSC_HOST_CALL setProtoFuncAdd(CallFrame* callFrame) +{ + JSValue thisValue = callFrame->thisValue(); + JSSet* set = getSet(callFrame, thisValue); + if (!set) + return JSValue::encode(jsUndefined()); + set->add(callFrame, callFrame->argument(0)); + return JSValue::encode(thisValue); +} + +EncodedJSValue JSC_HOST_CALL setProtoFuncClear(CallFrame* callFrame) +{ + JSSet* set = getSet(callFrame, callFrame->thisValue()); + if (!set) + return JSValue::encode(jsUndefined()); + set->clear(callFrame); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL setProtoFuncDelete(CallFrame* callFrame) +{ + JSSet* set = getSet(callFrame, callFrame->thisValue()); + if (!set) + return JSValue::encode(jsUndefined()); + return JSValue::encode(jsBoolean(set->remove(callFrame, callFrame->argument(0)))); +} + +EncodedJSValue JSC_HOST_CALL setProtoFuncForEach(CallFrame* callFrame) +{ + JSSet* set = getSet(callFrame, callFrame->thisValue()); + if (!set) + return JSValue::encode(jsUndefined()); + JSValue callBack = callFrame->argument(0); + CallData callData; + CallType callType = getCallData(callBack, callData); + if (callType == CallTypeNone) + return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral("Set.prototype.forEach called without callback"))); + JSValue thisValue = callFrame->argument(1); + VM* vm = &callFrame->vm(); + JSSetIterator* iterator = JSSetIterator::create(*vm, callFrame->callee()->globalObject()->setIteratorStructure(), set, SetIterateKey); + JSValue key; + if (callType == CallTypeJS) { + JSFunction* function = jsCast<JSFunction*>(callBack); + CachedCall cachedCall(callFrame, function, 3); + while (iterator->next(callFrame, key) && !vm->exception()) { + cachedCall.setThis(thisValue); + cachedCall.setArgument(0, key); + cachedCall.setArgument(1, key); + cachedCall.setArgument(2, set); + cachedCall.call(); + } + iterator->finish(); + } else { + while (iterator->next(callFrame, key) && !vm->exception()) { + MarkedArgumentBuffer args; + args.append(key); + args.append(key); + args.append(set); + JSC::call(callFrame, callBack, callType, callData, thisValue, args); + } + iterator->finish(); + } + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL setProtoFuncHas(CallFrame* callFrame) +{ + JSSet* set = getSet(callFrame, callFrame->thisValue()); + if (!set) + return JSValue::encode(jsUndefined()); + return JSValue::encode(jsBoolean(set->has(callFrame, callFrame->argument(0)))); +} + +EncodedJSValue JSC_HOST_CALL setProtoFuncSize(CallFrame* callFrame) +{ + JSSet* set = getSet(callFrame, callFrame->thisValue()); + if (!set) + return JSValue::encode(jsUndefined()); + return JSValue::encode(jsNumber(set->size(callFrame))); +} + +EncodedJSValue JSC_HOST_CALL setProtoFuncValues(CallFrame* callFrame) +{ + JSSet* thisObj = jsDynamicCast<JSSet*>(callFrame->thisValue()); + if (!thisObj) + return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot create a Set value iterator for a non-Set object."))); + return JSValue::encode(JSSetIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->setIteratorStructure(), thisObj, SetIterateValue)); +} + +EncodedJSValue JSC_HOST_CALL setProtoFuncEntries(CallFrame* callFrame) +{ + JSSet* thisObj = jsDynamicCast<JSSet*>(callFrame->thisValue()); + if (!thisObj) + return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot create a Set entry iterator for a non-Set object."))); + return JSValue::encode(JSSetIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->setIteratorStructure(), thisObj, SetIterateKeyValue)); +} + +} diff --git a/Source/JavaScriptCore/runtime/SetPrototype.h b/Source/JavaScriptCore/runtime/SetPrototype.h new file mode 100644 index 000000000..bdf90f433 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SetPrototype.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef SetPrototype_h +#define SetPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class SetPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static SetPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + SetPrototype* prototype = new (NotNull, allocateCell<SetPrototype>(vm.heap)) SetPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + SetPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); +}; + +} + +#endif // !defined(SetPrototype_h) diff --git a/Source/JavaScriptCore/runtime/SimpleTypedArrayController.cpp b/Source/JavaScriptCore/runtime/SimpleTypedArrayController.cpp new file mode 100644 index 000000000..9245c2c5d --- /dev/null +++ b/Source/JavaScriptCore/runtime/SimpleTypedArrayController.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "SimpleTypedArrayController.h" + +#include "ArrayBuffer.h" +#include "JSArrayBuffer.h" +#include "JSCInlines.h" + +namespace JSC { + +SimpleTypedArrayController::SimpleTypedArrayController() { } +SimpleTypedArrayController::~SimpleTypedArrayController() { } + +JSArrayBuffer* SimpleTypedArrayController::toJS( + ExecState* exec, JSGlobalObject* globalObject, ArrayBuffer* native) +{ + if (JSArrayBuffer* buffer = native->m_wrapper.get()) + return buffer; + + JSArrayBuffer* result = JSArrayBuffer::create( + exec->vm(), globalObject->arrayBufferStructure(), native); + native->m_wrapper = Weak<JSArrayBuffer>(result); + return result; +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/SimpleTypedArrayController.h b/Source/JavaScriptCore/runtime/SimpleTypedArrayController.h new file mode 100644 index 000000000..3d55b8930 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SimpleTypedArrayController.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef SimpleTypedArrayController_h +#define SimpleTypedArrayController_h + +#include "Handle.h" +#include "TypedArrayController.h" + +namespace JSC { + +// The default controller used for managing the relationship between +// array buffers and their wrappers in JavaScriptCore. This isn't what +// WebCore uses, but it is what JSC uses when running standalone. This +// is pretty simple: +// +// - If the JSArrayBuffer is live, then the ArrayBuffer stays alive. +// +// - If you ask an ArrayBuffer for a JSArrayBuffer after one had +// already been created and it didn't die, then you get the same +// one. + +class SimpleTypedArrayController : public TypedArrayController { +public: + SimpleTypedArrayController(); + virtual ~SimpleTypedArrayController(); + + virtual JSArrayBuffer* toJS(ExecState*, JSGlobalObject*, ArrayBuffer*) override; +}; + +} // namespace JSC + +#endif // SimpleTypedArrayController_h + diff --git a/Source/JavaScriptCore/runtime/SmallStrings.cpp b/Source/JavaScriptCore/runtime/SmallStrings.cpp new file mode 100644 index 000000000..9c6a43ae2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SmallStrings.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "SmallStrings.h" + +#include "HeapRootVisitor.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "JSCInlines.h" +#include <wtf/Noncopyable.h> +#include <wtf/text/StringImpl.h> + +namespace JSC { + +class SmallStringsStorage { + WTF_MAKE_NONCOPYABLE(SmallStringsStorage); WTF_MAKE_FAST_ALLOCATED; +public: + SmallStringsStorage(); + + StringImpl* rep(unsigned char character) + { + return m_reps[character].get(); + } + +private: + static const unsigned singleCharacterStringCount = maxSingleCharacterString + 1; + + RefPtr<StringImpl> m_reps[singleCharacterStringCount]; +}; + +SmallStringsStorage::SmallStringsStorage() +{ + LChar* characterBuffer = 0; + RefPtr<StringImpl> baseString = StringImpl::createUninitialized(singleCharacterStringCount, characterBuffer); + for (unsigned i = 0; i < singleCharacterStringCount; ++i) { + characterBuffer[i] = i; + m_reps[i] = AtomicStringImpl::add(PassRefPtr<StringImpl>(StringImpl::createSubstringSharingImpl(baseString, i, 1)).get()); + } +} + +SmallStrings::SmallStrings() + : m_emptyString(0) +#define JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE(name) , m_##name(0) + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE) +#undef JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE + , m_nullObjectString(nullptr) + , m_undefinedObjectString(nullptr) + , m_needsToBeVisited(true) +{ + COMPILE_ASSERT(singleCharacterStringCount == sizeof(m_singleCharacterStrings) / sizeof(m_singleCharacterStrings[0]), IsNumCharactersConstInSyncWithClassUsage); + + for (unsigned i = 0; i < singleCharacterStringCount; ++i) + m_singleCharacterStrings[i] = 0; +} + +void SmallStrings::initializeCommonStrings(VM& vm) +{ + createEmptyString(&vm); + for (unsigned i = 0; i <= maxSingleCharacterString; ++i) + createSingleCharacterString(&vm, i); +#define JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE(name) initialize(&vm, m_##name, #name); + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE) +#undef JSC_COMMON_STRINGS_ATTRIBUTE_INITIALIZE + initialize(&vm, m_nullObjectString, "[object Null]"); + initialize(&vm, m_undefinedObjectString, "[object Undefined]"); +} + +void SmallStrings::visitStrongReferences(SlotVisitor& visitor) +{ + m_needsToBeVisited = false; + visitor.appendUnbarrieredPointer(&m_emptyString); + for (unsigned i = 0; i <= maxSingleCharacterString; ++i) + visitor.appendUnbarrieredPointer(m_singleCharacterStrings + i); +#define JSC_COMMON_STRINGS_ATTRIBUTE_VISIT(name) visitor.appendUnbarrieredPointer(&m_##name); + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_VISIT) +#undef JSC_COMMON_STRINGS_ATTRIBUTE_VISIT + visitor.appendUnbarrieredPointer(&m_nullObjectString); + visitor.appendUnbarrieredPointer(&m_undefinedObjectString); +} + +SmallStrings::~SmallStrings() +{ +} + +void SmallStrings::createEmptyString(VM* vm) +{ + ASSERT(!m_emptyString); + m_emptyString = JSString::createHasOtherOwner(*vm, StringImpl::empty()); + m_needsToBeVisited = true; +} + +void SmallStrings::createSingleCharacterString(VM* vm, unsigned char character) +{ + if (!m_storage) + m_storage = std::make_unique<SmallStringsStorage>(); + ASSERT(!m_singleCharacterStrings[character]); + m_singleCharacterStrings[character] = JSString::createHasOtherOwner(*vm, PassRefPtr<StringImpl>(m_storage->rep(character))); + m_needsToBeVisited = true; +} + +StringImpl* SmallStrings::singleCharacterStringRep(unsigned char character) +{ + if (!m_storage) + m_storage = std::make_unique<SmallStringsStorage>(); + return m_storage->rep(character); +} + +void SmallStrings::initialize(VM* vm, JSString*& string, const char* value) +{ + string = JSString::create(*vm, Identifier::fromString(vm, value).impl()); + m_needsToBeVisited = true; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/SmallStrings.h b/Source/JavaScriptCore/runtime/SmallStrings.h new file mode 100644 index 000000000..909bae1de --- /dev/null +++ b/Source/JavaScriptCore/runtime/SmallStrings.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008, 2009, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef SmallStrings_h +#define SmallStrings_h + +#include "TypeofType.h" +#include "WriteBarrier.h" +#include <wtf/Noncopyable.h> + +#define JSC_COMMON_STRINGS_EACH_NAME(macro) \ + macro(boolean) \ + macro(false) \ + macro(function) \ + macro(number) \ + macro(null) \ + macro(object) \ + macro(undefined) \ + macro(string) \ + macro(symbol) \ + macro(true) + +namespace WTF { +class StringImpl; +} + +namespace JSC { + +class HeapRootVisitor; +class VM; +class JSString; +class SmallStringsStorage; +class SlotVisitor; + +static const unsigned maxSingleCharacterString = 0xFF; + +class SmallStrings { + WTF_MAKE_NONCOPYABLE(SmallStrings); +public: + SmallStrings(); + ~SmallStrings(); + + JSString* emptyString() + { + return m_emptyString; + } + + JSString* singleCharacterString(unsigned char character) + { + return m_singleCharacterStrings[character]; + } + + JS_EXPORT_PRIVATE WTF::StringImpl* singleCharacterStringRep(unsigned char character); + + JSString** singleCharacterStrings() { return &m_singleCharacterStrings[0]; } + + void initializeCommonStrings(VM&); + void visitStrongReferences(SlotVisitor&); + +#define JSC_COMMON_STRINGS_ACCESSOR_DEFINITION(name) \ + JSString* name##String() const \ + { \ + return m_##name; \ + } + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ACCESSOR_DEFINITION) +#undef JSC_COMMON_STRINGS_ACCESSOR_DEFINITION + + JSString* typeString(TypeofType type) const + { + switch (type) { + case TypeofType::Undefined: + return undefinedString(); + case TypeofType::Boolean: + return booleanString(); + case TypeofType::Number: + return numberString(); + case TypeofType::String: + return stringString(); + case TypeofType::Symbol: + return symbolString(); + case TypeofType::Object: + return objectString(); + case TypeofType::Function: + return functionString(); + } + + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; + } + + JSString* nullObjectString() const { return m_nullObjectString; } + JSString* undefinedObjectString() const { return m_undefinedObjectString; } + + bool needsToBeVisited(HeapOperation collectionType) const + { + if (collectionType == FullCollection) + return true; + return m_needsToBeVisited; + } + +private: + static const unsigned singleCharacterStringCount = maxSingleCharacterString + 1; + + JS_EXPORT_PRIVATE void createEmptyString(VM*); + JS_EXPORT_PRIVATE void createSingleCharacterString(VM*, unsigned char); + + void initialize(VM*, JSString*&, const char* value); + + JSString* m_emptyString; +#define JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION(name) JSString* m_##name; + JSC_COMMON_STRINGS_EACH_NAME(JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION) +#undef JSC_COMMON_STRINGS_ATTRIBUTE_DECLARATION + JSString* m_nullObjectString; + JSString* m_undefinedObjectString; + JSString* m_singleCharacterStrings[singleCharacterStringCount]; + std::unique_ptr<SmallStringsStorage> m_storage; + bool m_needsToBeVisited; +}; + +} // namespace JSC + +#endif // SmallStrings_h diff --git a/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp b/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp new file mode 100644 index 000000000..62d34baaf --- /dev/null +++ b/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "SparseArrayValueMap.h" + +#include "ClassInfo.h" +#include "GetterSetter.h" +#include "JSObject.h" +#include "JSCInlines.h" +#include "PropertySlot.h" +#include "Reject.h" +#include "SlotVisitor.h" +#include "Structure.h" + +namespace JSC { + +const ClassInfo SparseArrayValueMap::s_info = { "SparseArrayValueMap", 0, 0, CREATE_METHOD_TABLE(SparseArrayValueMap) }; + +SparseArrayValueMap::SparseArrayValueMap(VM& vm) + : Base(vm, vm.sparseArrayValueMapStructure.get()) + , m_flags(Normal) + , m_reportedCapacity(0) +{ +} + +SparseArrayValueMap::~SparseArrayValueMap() +{ +} + +void SparseArrayValueMap::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +SparseArrayValueMap* SparseArrayValueMap::create(VM& vm) +{ + SparseArrayValueMap* result = new (NotNull, allocateCell<SparseArrayValueMap>(vm.heap)) SparseArrayValueMap(vm); + result->finishCreation(vm); + return result; +} + +void SparseArrayValueMap::destroy(JSCell* cell) +{ + static_cast<SparseArrayValueMap*>(cell)->SparseArrayValueMap::~SparseArrayValueMap(); +} + +Structure* SparseArrayValueMap::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); +} + +SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSObject* array, unsigned i) +{ + SparseArrayEntry entry; + entry.setWithoutWriteBarrier(jsUndefined()); + + AddResult result = m_map.add(i, entry); + size_t capacity = m_map.capacity(); + if (capacity != m_reportedCapacity) { + // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated. + // https://bugs.webkit.org/show_bug.cgi?id=142595 + Heap::heap(array)->deprecatedReportExtraMemory((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>))); + m_reportedCapacity = capacity; + } + return result; +} + +void SparseArrayValueMap::putEntry(ExecState* exec, JSObject* array, unsigned i, JSValue value, bool shouldThrow) +{ + AddResult result = add(array, i); + SparseArrayEntry& entry = result.iterator->value; + + // To save a separate find & add, we first always add to the sparse map. + // In the uncommon case that this is a new property, and the array is not + // extensible, this is not the right thing to have done - so remove again. + if (result.isNewEntry && !array->isExtensible()) { + remove(result.iterator); + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + + entry.put(exec, array, this, value, shouldThrow); +} + +bool SparseArrayValueMap::putDirect(ExecState* exec, JSObject* array, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode) +{ + AddResult result = add(array, i); + SparseArrayEntry& entry = result.iterator->value; + + // To save a separate find & add, we first always add to the sparse map. + // In the uncommon case that this is a new property, and the array is not + // extensible, this is not the right thing to have done - so remove again. + if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isExtensible()) { + remove(result.iterator); + return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible."); + } + + entry.attributes = attributes; + entry.set(exec->vm(), this, value); + return true; +} + +void SparseArrayEntry::get(JSObject* thisObject, PropertySlot& slot) const +{ + JSValue value = Base::get(); + ASSERT(value); + + if (LIKELY(!value.isGetterSetter())) { + slot.setValue(thisObject, attributes, value); + return; + } + + slot.setGetterSlot(thisObject, attributes, jsCast<GetterSetter*>(value)); +} + +void SparseArrayEntry::get(PropertyDescriptor& descriptor) const +{ + descriptor.setDescriptor(Base::get(), attributes); +} + +JSValue SparseArrayEntry::get(ExecState* exec, JSObject* array) const +{ + JSValue value = Base::get(); + ASSERT(value); + + if (LIKELY(!value.isGetterSetter())) + return value; + + return callGetter(exec, array, jsCast<GetterSetter*>(value)); +} + +void SparseArrayEntry::put(ExecState* exec, JSValue thisValue, SparseArrayValueMap* map, JSValue value, bool shouldThrow) +{ + if (!(attributes & Accessor)) { + if (attributes & ReadOnly) { + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + + set(exec->vm(), map, value); + return; + } + + callSetter(exec, thisValue, Base::get(), value, shouldThrow ? StrictMode : NotStrictMode); +} + +JSValue SparseArrayEntry::getNonSparseMode() const +{ + ASSERT(!attributes); + return Base::get(); +} + +void SparseArrayValueMap::visitChildren(JSCell* thisObject, SlotVisitor& visitor) +{ + Base::visitChildren(thisObject, visitor); + + SparseArrayValueMap* thisMap = jsCast<SparseArrayValueMap*>(thisObject); + iterator end = thisMap->m_map.end(); + for (iterator it = thisMap->m_map.begin(); it != end; ++it) + visitor.append(&it->value); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/SparseArrayValueMap.h b/Source/JavaScriptCore/runtime/SparseArrayValueMap.h new file mode 100644 index 000000000..ff36caa71 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SparseArrayValueMap.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef SparseArrayValueMap_h +#define SparseArrayValueMap_h + +#include "JSCell.h" +#include "JSTypeInfo.h" +#include "PropertyDescriptor.h" +#include "PutDirectIndexMode.h" +#include "WriteBarrier.h" +#include <wtf/HashMap.h> + +namespace JSC { + +class SparseArrayValueMap; + +struct SparseArrayEntry : public WriteBarrier<Unknown> { + typedef WriteBarrier<Unknown> Base; + + SparseArrayEntry() : attributes(0) { } + + JSValue get(ExecState*, JSObject*) const; + void get(JSObject*, PropertySlot&) const; + void get(PropertyDescriptor&) const; + void put(ExecState*, JSValue thisValue, SparseArrayValueMap*, JSValue, bool shouldThrow); + JSValue getNonSparseMode() const; + + unsigned attributes; +}; + +class SparseArrayValueMap final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + +private: + typedef HashMap<uint64_t, SparseArrayEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> Map; + + enum Flags { + Normal = 0, + SparseMode = 1, + LengthIsReadOnly = 2, + }; + + SparseArrayValueMap(VM&); + ~SparseArrayValueMap(); + + void finishCreation(VM&); + +public: + DECLARE_EXPORT_INFO; + + typedef Map::iterator iterator; + typedef Map::const_iterator const_iterator; + typedef Map::AddResult AddResult; + + static SparseArrayValueMap* create(VM&); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + static void visitChildren(JSCell*, SlotVisitor&); + + bool sparseMode() + { + return m_flags & SparseMode; + } + + void setSparseMode() + { + m_flags = static_cast<Flags>(m_flags | SparseMode); + } + + bool lengthIsReadOnly() + { + return m_flags & LengthIsReadOnly; + } + + void setLengthIsReadOnly() + { + m_flags = static_cast<Flags>(m_flags | LengthIsReadOnly); + } + + // These methods may mutate the contents of the map + void putEntry(ExecState*, JSObject*, unsigned, JSValue, bool shouldThrow); + bool putDirect(ExecState*, JSObject*, unsigned, JSValue, unsigned attributes, PutDirectIndexMode); + AddResult add(JSObject*, unsigned); + iterator find(unsigned i) { return m_map.find(i); } + // This should ASSERT the remove is valid (check the result of the find). + void remove(iterator it) { m_map.remove(it); } + void remove(unsigned i) { m_map.remove(i); } + + // These methods do not mutate the contents of the map. + iterator notFound() { return m_map.end(); } + bool isEmpty() const { return m_map.isEmpty(); } + bool contains(unsigned i) const { return m_map.contains(i); } + size_t size() const { return m_map.size(); } + // Only allow const begin/end iteration. + const_iterator begin() const { return m_map.begin(); } + const_iterator end() const { return m_map.end(); } + +private: + Map m_map; + Flags m_flags; + size_t m_reportedCapacity; +}; + +} // namespace JSC + +#endif // SparseArrayValueMap_h + diff --git a/Source/JavaScriptCore/runtime/StackAlignment.h b/Source/JavaScriptCore/runtime/StackAlignment.h new file mode 100644 index 000000000..d29802b3f --- /dev/null +++ b/Source/JavaScriptCore/runtime/StackAlignment.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StackAlignment_h +#define StackAlignment_h + +#include "JSCJSValue.h" +#include "JSStack.h" +#include <wtf/MathExtras.h> + +namespace JSC { + +// NB. Different platforms may have different requirements here. But 16 bytes is very common. +inline unsigned stackAlignmentBytes() { return 16; } + +inline unsigned stackAlignmentRegisters() +{ + return stackAlignmentBytes() / sizeof(EncodedJSValue); +} + +// Align argument count taking into account the CallFrameHeaderSize may be +// an "unaligned" count of registers. +inline unsigned roundArgumentCountToAlignFrame(unsigned argumentCount) +{ + return WTF::roundUpToMultipleOf(stackAlignmentRegisters(), argumentCount + JSStack::CallFrameHeaderSize) - JSStack::CallFrameHeaderSize; +} + +// Align local register count to make the last local end on a stack aligned address given the +// CallFrame is at an address that is stack aligned minus JSStack::CallerFrameAndPCSize +inline unsigned roundLocalRegisterCountForFramePointerOffset(unsigned localRegisterCount) +{ + return WTF::roundUpToMultipleOf(stackAlignmentRegisters(), localRegisterCount + JSStack::CallerFrameAndPCSize) - JSStack::CallerFrameAndPCSize; +} + +inline unsigned logStackAlignmentRegisters() +{ + return WTF::fastLog2(stackAlignmentRegisters()); +} + +} // namespace JSC + +#endif // StackAlignment_h + diff --git a/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp b/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp new file mode 100644 index 000000000..4260e10b9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StrictEvalActivation.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "StrictEvalActivation.h" + +#include "JSGlobalObject.h" +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(StrictEvalActivation); + +const ClassInfo StrictEvalActivation::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(StrictEvalActivation) }; + +StrictEvalActivation::StrictEvalActivation(ExecState* exec, JSScope* currentScope) + : Base( + exec->vm(), + exec->lexicalGlobalObject()->strictEvalActivationStructure(), + currentScope + ) +{ +} + +bool StrictEvalActivation::deleteProperty(JSCell*, ExecState*, PropertyName) +{ + return false; +} + +JSValue StrictEvalActivation::toThis(JSCell*, ExecState* exec, ECMAMode ecmaMode) +{ + if (ecmaMode == StrictMode) + return jsUndefined(); + return exec->globalThisValue(); +} + +} diff --git a/Source/JavaScriptCore/runtime/StrictEvalActivation.h b/Source/JavaScriptCore/runtime/StrictEvalActivation.h new file mode 100644 index 000000000..13157f68a --- /dev/null +++ b/Source/JavaScriptCore/runtime/StrictEvalActivation.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef StrictEvalActivation_h +#define StrictEvalActivation_h + +#include "JSScope.h" + +namespace JSC { + +class StrictEvalActivation : public JSScope { +public: + typedef JSScope Base; + static const unsigned StructureFlags = Base::StructureFlags | IsEnvironmentRecord; + + static StrictEvalActivation* create(ExecState* exec, JSScope* currentScope) + { + StrictEvalActivation* lexicalEnvironment = new (NotNull, allocateCell<StrictEvalActivation>(*exec->heap())) StrictEvalActivation(exec, currentScope); + lexicalEnvironment->finishCreation(exec->vm()); + return lexicalEnvironment; + } + + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static JSValue toThis(JSCell*, ExecState*, ECMAMode); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + DECLARE_INFO; + +private: + StrictEvalActivation(ExecState*, JSScope*); +}; + +} // namespace JSC + +#endif // StrictEvalActivation_h diff --git a/Source/JavaScriptCore/runtime/StringConstructor.cpp b/Source/JavaScriptCore/runtime/StringConstructor.cpp new file mode 100644 index 000000000..e8d396970 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringConstructor.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StringConstructor.h" + +#include "Error.h" +#include "Executable.h" +#include "JITCode.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSCInlines.h" +#include "StringPrototype.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL stringFromCharCode(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringFromCodePoint(ExecState*); + +} + +#include "StringConstructor.lut.h" + +namespace JSC { + +const ClassInfo StringConstructor::s_info = { "Function", &InternalFunction::s_info, &stringConstructorTable, CREATE_METHOD_TABLE(StringConstructor) }; + +/* Source for StringConstructor.lut.h +@begin stringConstructorTable + fromCharCode stringFromCharCode DontEnum|Function 1 + fromCodePoint stringFromCodePoint DontEnum|Function 1 + raw stringRaw DontEnum|Function 1 +@end +*/ + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(StringConstructor); + +StringConstructor::StringConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +void StringConstructor::finishCreation(VM& vm, StringPrototype* stringPrototype) +{ + Base::finishCreation(vm, stringPrototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, stringPrototype, ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +bool StringConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<InternalFunction>(exec, stringConstructorTable, jsCast<StringConstructor*>(object), propertyName, slot); +} + +// ------------------------------ Functions -------------------------------- + +static NEVER_INLINE JSValue stringFromCharCodeSlowCase(ExecState* exec) +{ + unsigned length = exec->argumentCount(); + UChar* buf; + PassRefPtr<StringImpl> impl = StringImpl::createUninitialized(length, buf); + for (unsigned i = 0; i < length; ++i) + buf[i] = static_cast<UChar>(exec->uncheckedArgument(i).toUInt32(exec)); + return jsString(exec, impl); +} + +static EncodedJSValue JSC_HOST_CALL stringFromCharCode(ExecState* exec) +{ + if (LIKELY(exec->argumentCount() == 1)) + return JSValue::encode(jsSingleCharacterString(exec, exec->uncheckedArgument(0).toUInt32(exec))); + return JSValue::encode(stringFromCharCodeSlowCase(exec)); +} + +JSCell* JSC_HOST_CALL stringFromCharCode(ExecState* exec, int32_t arg) +{ + return jsSingleCharacterString(exec, arg); +} + +static EncodedJSValue JSC_HOST_CALL stringFromCodePoint(ExecState* exec) +{ + unsigned length = exec->argumentCount(); + StringBuilder builder; + builder.reserveCapacity(length); + + for (unsigned i = 0; i < length; ++i) { + double codePointAsDouble = exec->uncheckedArgument(i).toNumber(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + uint32_t codePoint = static_cast<uint32_t>(codePointAsDouble); + + if (codePoint != codePointAsDouble || codePoint > UCHAR_MAX_VALUE) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("Arguments contain a value that is out of range of code points"))); + + if (U_IS_BMP(codePoint)) + builder.append(static_cast<UChar>(codePoint)); + else { + builder.append(U16_LEAD(codePoint)); + builder.append(U16_TRAIL(codePoint)); + } + } + + return JSValue::encode(jsString(exec, builder.toString())); +} + +static EncodedJSValue JSC_HOST_CALL constructWithStringConstructor(ExecState* exec) +{ + JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject(); + VM& vm = exec->vm(); + + if (!exec->argumentCount()) + return JSValue::encode(StringObject::create(vm, globalObject->stringObjectStructure())); + + return JSValue::encode(StringObject::create(vm, globalObject->stringObjectStructure(), exec->uncheckedArgument(0).toString(exec))); +} + +ConstructType StringConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWithStringConstructor; + return ConstructTypeHost; +} + +JSCell* stringConstructor(ExecState* exec, JSValue argument) +{ + if (argument.isSymbol()) + return jsNontrivialString(exec, asSymbol(argument)->descriptiveString()); + return argument.toString(exec); +} + +static EncodedJSValue JSC_HOST_CALL callStringConstructor(ExecState* exec) +{ + if (!exec->argumentCount()) + return JSValue::encode(jsEmptyString(exec)); + return JSValue::encode(stringConstructor(exec, exec->uncheckedArgument(0))); +} + +CallType StringConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callStringConstructor; + return CallTypeHost; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringConstructor.h b/Source/JavaScriptCore/runtime/StringConstructor.h new file mode 100644 index 000000000..357511a57 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringConstructor.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef StringConstructor_h +#define StringConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class StringPrototype; + +class StringConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static StringConstructor* create(VM& vm, Structure* structure, StringPrototype* stringPrototype) + { + StringConstructor* constructor = new (NotNull, allocateCell<StringConstructor>(vm.heap)) StringConstructor(vm, structure); + constructor->finishCreation(vm, stringPrototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + StringConstructor(VM&, Structure*); + void finishCreation(VM&, StringPrototype*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +JSCell* JSC_HOST_CALL stringFromCharCode(ExecState*, int32_t); +JSCell* stringConstructor(ExecState*, JSValue); + +} // namespace JSC + +#endif // StringConstructor_h diff --git a/Source/JavaScriptCore/runtime/StringIteratorPrototype.cpp b/Source/JavaScriptCore/runtime/StringIteratorPrototype.cpp new file mode 100644 index 000000000..b58d0ddfb --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringIteratorPrototype.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Apple, Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "StringIteratorPrototype.h" + +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "JSStringIterator.h" +#include "ObjectConstructor.h" +#include "StructureInlines.h" + +namespace JSC { + +} + +#include "StringIteratorPrototype.lut.h" + +namespace JSC { + +const ClassInfo StringIteratorPrototype::s_info = { "String Iterator", &Base::s_info, &stringIteratorPrototypeTable, CREATE_METHOD_TABLE(StringIteratorPrototype) }; + +/* Source for StringIteratorPrototype.lut.h +@begin stringIteratorPrototypeTable + next stringIteratorPrototypeFuncNext DontEnum|Function 0 +@end +*/ + +void StringIteratorPrototype::finishCreation(VM& vm, JSGlobalObject*) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); +} + +bool StringIteratorPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + return getStaticFunctionSlot<Base>(exec, stringIteratorPrototypeTable, jsCast<StringIteratorPrototype*>(object), propertyName, slot); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringIteratorPrototype.h b/Source/JavaScriptCore/runtime/StringIteratorPrototype.h new file mode 100644 index 000000000..393af560f --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringIteratorPrototype.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 Apple, Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StringIteratorPrototype_h +#define StringIteratorPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class StringIteratorPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static StringIteratorPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + StringIteratorPrototype* prototype = new (NotNull, allocateCell<StringIteratorPrototype>(vm.heap)) StringIteratorPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + StringIteratorPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} + +#endif // !defined(StringIteratorPrototype_h) diff --git a/Source/JavaScriptCore/runtime/StringObject.cpp b/Source/JavaScriptCore/runtime/StringObject.cpp new file mode 100644 index 000000000..b5aa3f72d --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringObject.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StringObject.h" + +#include "Error.h" +#include "JSGlobalObject.h" +#include "JSCInlines.h" +#include "PropertyNameArray.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(StringObject); + +const ClassInfo StringObject::s_info = { "String", &JSWrapperObject::s_info, 0, CREATE_METHOD_TABLE(StringObject) }; + +StringObject::StringObject(VM& vm, Structure* structure) + : JSWrapperObject(vm, structure) +{ +} + +void StringObject::finishCreation(VM& vm, JSString* string) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + setInternalValue(vm, string); +} + +bool StringObject::getOwnPropertySlot(JSObject* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + StringObject* thisObject = jsCast<StringObject*>(cell); + if (thisObject->internalValue()->getStringPropertySlot(exec, propertyName, slot)) + return true; + return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); +} + +bool StringObject::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + StringObject* thisObject = jsCast<StringObject*>(object); + if (thisObject->internalValue()->getStringPropertySlot(exec, propertyName, slot)) + return true; + return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); +} + +void StringObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + if (propertyName == exec->propertyNames().length) { + if (slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + JSObject::put(cell, exec, propertyName, value, slot); +} + +void StringObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) +{ + StringObject* thisObject = jsCast<StringObject*>(cell); + if (thisObject->internalValue()->canGetIndex(propertyName)) { + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } + JSObject::putByIndex(cell, exec, propertyName, value, shouldThrow); +} + +bool StringObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) +{ + StringObject* thisObject = jsCast<StringObject*>(object); + + if (propertyName == exec->propertyNames().length) { + if (!object->isExtensible()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible."))); + return false; + } + if (descriptor.configurablePresent() && descriptor.configurable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property."))); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property."))); + return false; + } + if (descriptor.isAccessorDescriptor()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); + return false; + } + if (descriptor.writablePresent() && descriptor.writable()) { + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property."))); + return false; + } + if (!descriptor.value()) + return true; + if (propertyName == exec->propertyNames().length && sameValue(exec, descriptor.value(), jsNumber(thisObject->internalValue()->length()))) + return true; + if (throwException) + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property."))); + return false; + } + + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); +} + +bool StringObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + StringObject* thisObject = jsCast<StringObject*>(cell); + if (propertyName == exec->propertyNames().length) + return false; + Optional<uint32_t> index = parseIndex(propertyName); + if (index && thisObject->internalValue()->canGetIndex(index.value())) { + return false; + } + return JSObject::deleteProperty(thisObject, exec, propertyName); +} + +bool StringObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) +{ + StringObject* thisObject = jsCast<StringObject*>(cell); + if (thisObject->internalValue()->canGetIndex(i)) + return false; + return JSObject::deletePropertyByIndex(thisObject, exec, i); +} + +void StringObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + StringObject* thisObject = jsCast<StringObject*>(object); + if (propertyNames.includeStringProperties()) { + int size = thisObject->internalValue()->length(); + for (int i = 0; i < size; ++i) + propertyNames.add(Identifier::from(exec, i)); + } + if (mode.includeDontEnumProperties()) + propertyNames.add(exec->propertyNames().length); + return JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); +} + +StringObject* constructString(VM& vm, JSGlobalObject* globalObject, JSValue string) +{ + StringObject* object = StringObject::create(vm, globalObject->stringObjectStructure()); + object->setInternalValue(vm, string); + return object; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringObject.h b/Source/JavaScriptCore/runtime/StringObject.h new file mode 100644 index 000000000..90e6e5cca --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringObject.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef StringObject_h +#define StringObject_h + +#include "JSWrapperObject.h" +#include "JSString.h" + +namespace JSC { + +class StringObject : public JSWrapperObject { +public: + typedef JSWrapperObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames; + + static StringObject* create(VM& vm, Structure* structure) + { + JSString* string = jsEmptyString(&vm); + StringObject* object = new (NotNull, allocateCell<StringObject>(vm.heap)) StringObject(vm, structure); + object->finishCreation(vm, string); + return object; + } + static StringObject* create(VM& vm, Structure* structure, JSString* string) + { + StringObject* object = new (NotNull, allocateCell<StringObject>(vm.heap)) StringObject(vm, structure); + object->finishCreation(vm, string); + return object; + } + static StringObject* create(VM&, JSGlobalObject*, JSString*); + + JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); + + JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); + JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + + DECLARE_EXPORT_INFO; + + JSString* internalValue() const { return asString(JSWrapperObject::internalValue());} + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + JS_EXPORT_PRIVATE void finishCreation(VM&, JSString*); + JS_EXPORT_PRIVATE StringObject(VM&, Structure*); +}; + +StringObject* asStringObject(JSValue); + +inline StringObject* asStringObject(JSValue value) +{ + ASSERT(asObject(value)->inherits(StringObject::info())); + return static_cast<StringObject*>(asObject(value)); +} + +JS_EXPORT_PRIVATE StringObject* constructString(VM&, JSGlobalObject*, JSValue); + +} // namespace JSC + +#endif // StringObject_h diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp new file mode 100644 index 000000000..2edbf42c7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -0,0 +1,1792 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * Copyright (C) 2015 Jordan Harband (ljharb@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StringPrototype.h" + +#include "ButterflyInlines.h" +#include "CachedCall.h" +#include "CopiedSpaceInlines.h" +#include "Error.h" +#include "Executable.h" +#include "JSGlobalObjectFunctions.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "JSStringBuilder.h" +#include "Lookup.h" +#include "ObjectPrototype.h" +#include "JSCInlines.h" +#include "JSStringIterator.h" +#include "PropertyNameArray.h" +#include "RegExpCache.h" +#include "RegExpConstructor.h" +#include "RegExpMatchesArray.h" +#include "RegExpObject.h" +#include <algorithm> +#include <wtf/ASCIICType.h> +#include <wtf/MathExtras.h> +#include <wtf/text/StringView.h> +#include <wtf/unicode/Collator.h> + +using namespace WTF; + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(StringPrototype); + +EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeat(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState*); +EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState*); + +const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, 0, CREATE_METHOD_TABLE(StringPrototype) }; + +// ECMA 15.5.4 +StringPrototype::StringPrototype(VM& vm, Structure* structure) + : StringObject(vm, structure) +{ +} + +void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSString* nameAndMessage) +{ + Base::finishCreation(vm, nameAndMessage); + ASSERT(inherits(info())); + + JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->toString, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic); + JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->valueOf, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic); + JSC_NATIVE_INTRINSIC_FUNCTION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic); + JSC_NATIVE_INTRINSIC_FUNCTION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic); + JSC_NATIVE_FUNCTION("codePointAt", stringProtoFuncCodePointAt, DontEnum, 1); + JSC_NATIVE_FUNCTION("concat", stringProtoFuncConcat, DontEnum, 1); + JSC_NATIVE_FUNCTION("indexOf", stringProtoFuncIndexOf, DontEnum, 1); + JSC_NATIVE_FUNCTION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1); + JSC_NATIVE_FUNCTION("match", stringProtoFuncMatch, DontEnum, 1); + JSC_NATIVE_FUNCTION("repeat", stringProtoFuncRepeat, DontEnum, 1); + JSC_NATIVE_FUNCTION("replace", stringProtoFuncReplace, DontEnum, 2); + JSC_NATIVE_FUNCTION("search", stringProtoFuncSearch, DontEnum, 1); + JSC_NATIVE_FUNCTION("slice", stringProtoFuncSlice, DontEnum, 2); + JSC_NATIVE_FUNCTION("split", stringProtoFuncSplit, DontEnum, 2); + JSC_NATIVE_FUNCTION("substr", stringProtoFuncSubstr, DontEnum, 2); + JSC_NATIVE_FUNCTION("substring", stringProtoFuncSubstring, DontEnum, 2); + JSC_NATIVE_FUNCTION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0); + JSC_NATIVE_FUNCTION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0); + JSC_NATIVE_FUNCTION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1); + JSC_NATIVE_FUNCTION("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0); + JSC_NATIVE_FUNCTION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0); + JSC_NATIVE_FUNCTION("big", stringProtoFuncBig, DontEnum, 0); + JSC_NATIVE_FUNCTION("small", stringProtoFuncSmall, DontEnum, 0); + JSC_NATIVE_FUNCTION("blink", stringProtoFuncBlink, DontEnum, 0); + JSC_NATIVE_FUNCTION("bold", stringProtoFuncBold, DontEnum, 0); + JSC_NATIVE_FUNCTION("fixed", stringProtoFuncFixed, DontEnum, 0); + JSC_NATIVE_FUNCTION("italics", stringProtoFuncItalics, DontEnum, 0); + JSC_NATIVE_FUNCTION("strike", stringProtoFuncStrike, DontEnum, 0); + JSC_NATIVE_FUNCTION("sub", stringProtoFuncSub, DontEnum, 0); + JSC_NATIVE_FUNCTION("sup", stringProtoFuncSup, DontEnum, 0); + JSC_NATIVE_FUNCTION("fontcolor", stringProtoFuncFontcolor, DontEnum, 1); + JSC_NATIVE_FUNCTION("fontsize", stringProtoFuncFontsize, DontEnum, 1); + JSC_NATIVE_FUNCTION("anchor", stringProtoFuncAnchor, DontEnum, 1); + JSC_NATIVE_FUNCTION("link", stringProtoFuncLink, DontEnum, 1); + JSC_NATIVE_FUNCTION("trim", stringProtoFuncTrim, DontEnum, 0); + JSC_NATIVE_FUNCTION("trimLeft", stringProtoFuncTrimLeft, DontEnum, 0); + JSC_NATIVE_FUNCTION("trimRight", stringProtoFuncTrimRight, DontEnum, 0); + JSC_NATIVE_FUNCTION("startsWith", stringProtoFuncStartsWith, DontEnum, 1); + JSC_NATIVE_FUNCTION("endsWith", stringProtoFuncEndsWith, DontEnum, 1); + JSC_NATIVE_FUNCTION("includes", stringProtoFuncIncludes, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorSymbol, stringProtoFuncIterator, DontEnum, 0); + + JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->charCodeAtPrivateName, stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic); + + // The constructor will be added later, after StringConstructor has been built + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), DontDelete | ReadOnly | DontEnum); +} + +StringPrototype* StringPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + JSString* empty = jsEmptyString(&vm); + StringPrototype* prototype = new (NotNull, allocateCell<StringPrototype>(vm.heap)) StringPrototype(vm, structure); + prototype->finishCreation(vm, globalObject, empty); + return prototype; +} + +// ------------------------------ Functions -------------------------- + +// Helper for producing a JSString for 'string', where 'string' was been produced by +// calling ToString on 'originalValue'. In cases where 'originalValue' already was a +// string primitive we can just use this, otherwise we need to allocate a new JSString. +static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const String& string) +{ + if (originalValue.isString()) { + ASSERT(asString(originalValue)->value(exec) == string); + return asString(originalValue); + } + return jsString(exec, string); +} + +// Helper that tries to use the JSString substring sharing mechanism if 'originalValue' is a JSString. +static inline JSString* jsSubstring(ExecState* exec, JSValue originalValue, const String& string, unsigned offset, unsigned length) +{ + if (originalValue.isString()) { + ASSERT(asString(originalValue)->value(exec) == string); + return jsSubstring(exec, asString(originalValue), offset, length); + } + return jsSubstring(exec, string, offset, length); +} + +static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i) +{ + StringBuilder substitutedReplacement; + int offset = 0; + do { + if (i + 1 == replacement.length()) + break; + + UChar ref = replacement[i + 1]; + if (ref == '$') { + // "$$" -> "$" + ++i; + substitutedReplacement.append(replacement.substring(offset, i - offset)); + offset = i + 1; + continue; + } + + int backrefStart; + int backrefLength; + int advance = 0; + if (ref == '&') { + backrefStart = ovector[0]; + backrefLength = ovector[1] - backrefStart; + } else if (ref == '`') { + backrefStart = 0; + backrefLength = ovector[0]; + } else if (ref == '\'') { + backrefStart = ovector[1]; + backrefLength = source.length() - backrefStart; + } else if (reg && ref >= '0' && ref <= '9') { + // 1- and 2-digit back references are allowed + unsigned backrefIndex = ref - '0'; + if (backrefIndex > reg->numSubpatterns()) + continue; + if (replacement.length() > i + 2) { + ref = replacement[i + 2]; + if (ref >= '0' && ref <= '9') { + backrefIndex = 10 * backrefIndex + ref - '0'; + if (backrefIndex > reg->numSubpatterns()) + backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference + else + advance = 1; + } + } + if (!backrefIndex) + continue; + backrefStart = ovector[2 * backrefIndex]; + backrefLength = ovector[2 * backrefIndex + 1] - backrefStart; + } else + continue; + + if (i - offset) + substitutedReplacement.append(replacement.substring(offset, i - offset)); + i += 1 + advance; + offset = i + 1; + if (backrefStart >= 0) + substitutedReplacement.append(source.substring(backrefStart, backrefLength)); + } while ((i = replacement.find('$', i + 1)) != notFound); + + if (replacement.length() - offset) + substitutedReplacement.append(replacement.substring(offset)); + + return substitutedReplacement.toString(); +} + +static inline String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg) +{ + size_t i = replacement.find('$'); + if (UNLIKELY(i != notFound)) + return substituteBackreferencesSlow(replacement, source, ovector, reg, i); + + return replacement; +} + +struct StringRange { + StringRange(int pos, int len) + : position(pos) + , length(len) + { + } + + StringRange() + { + } + + int position; + int length; +}; + +static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount) +{ + if (rangeCount == 1) { + int sourceSize = source.length(); + int position = substringRanges[0].position; + int length = substringRanges[0].length; + if (position <= 0 && length >= sourceSize) + return sourceVal; + // We could call String::substringSharingImpl(), but this would result in redundant checks. + return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length))); + } + + int totalLength = 0; + for (int i = 0; i < rangeCount; i++) + totalLength += substringRanges[i].length; + + if (!totalLength) + return jsEmptyString(exec); + + if (source.is8Bit()) { + LChar* buffer; + const LChar* sourceData = source.characters8(); + RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); + if (!impl) + return throwOutOfMemoryError(exec); + + int bufferPos = 0; + for (int i = 0; i < rangeCount; i++) { + if (int srcLen = substringRanges[i].length) { + StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } + } + + return jsString(exec, impl.release()); + } + + UChar* buffer; + const UChar* sourceData = source.characters16(); + + RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); + if (!impl) + return throwOutOfMemoryError(exec); + + int bufferPos = 0; + for (int i = 0; i < rangeCount; i++) { + if (int srcLen = substringRanges[i].length) { + StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } + } + + return jsString(exec, impl.release()); +} + +static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount) +{ + if (rangeCount == 1 && separatorCount == 0) { + int sourceSize = source.length(); + int position = substringRanges[0].position; + int length = substringRanges[0].length; + if (position <= 0 && length >= sourceSize) + return sourceVal; + // We could call String::substringSharingImpl(), but this would result in redundant checks. + return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length))); + } + + Checked<int, RecordOverflow> totalLength = 0; + bool allSeparators8Bit = true; + for (int i = 0; i < rangeCount; i++) + totalLength += substringRanges[i].length; + for (int i = 0; i < separatorCount; i++) { + totalLength += separators[i].length(); + if (separators[i].length() && !separators[i].is8Bit()) + allSeparators8Bit = false; + } + if (totalLength.hasOverflowed()) + return throwOutOfMemoryError(exec); + + if (!totalLength) + return jsEmptyString(exec); + + if (source.is8Bit() && allSeparators8Bit) { + LChar* buffer; + const LChar* sourceData = source.characters8(); + + RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer); + if (!impl) + return throwOutOfMemoryError(exec); + + int maxCount = std::max(rangeCount, separatorCount); + int bufferPos = 0; + for (int i = 0; i < maxCount; i++) { + if (i < rangeCount) { + if (int srcLen = substringRanges[i].length) { + StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } + } + if (i < separatorCount) { + if (int sepLen = separators[i].length()) { + StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen); + bufferPos += sepLen; + } + } + } + + return jsString(exec, impl.release()); + } + + UChar* buffer; + RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer); + if (!impl) + return throwOutOfMemoryError(exec); + + int maxCount = std::max(rangeCount, separatorCount); + int bufferPos = 0; + for (int i = 0; i < maxCount; i++) { + if (i < rangeCount) { + if (int srcLen = substringRanges[i].length) { + if (source.is8Bit()) + StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen); + else + StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } + } + if (i < separatorCount) { + if (int sepLen = separators[i].length()) { + if (separators[i].is8Bit()) + StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen); + else + StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen); + bufferPos += sepLen; + } + } + } + + return jsString(exec, impl.release()); +} + +static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp) +{ + size_t lastIndex = 0; + unsigned startPosition = 0; + + Vector<StringRange, 16> sourceRanges; + VM* vm = &exec->vm(); + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + unsigned sourceLen = source.length(); + + while (true) { + MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition); + if (!result) + break; + + if (lastIndex < result.start) + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); + + lastIndex = result.end; + startPosition = lastIndex; + + // special case of empty match + if (result.empty()) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } + + if (!lastIndex) + return JSValue::encode(string); + + if (static_cast<unsigned>(lastIndex) < sourceLen) + sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); + + return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size())); +} + +static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue) +{ + JSValue replaceValue = exec->argument(1); + String replacementString; + CallData callData; + CallType callType = getCallData(replaceValue, callData); + if (callType == CallTypeNone) + replacementString = replaceValue.toString(exec)->value(exec); + + const String& source = string->value(exec); + unsigned sourceLen = source.length(); + if (exec->hadException()) + return JSValue::encode(JSValue()); + RegExpObject* regExpObject = asRegExpObject(searchValue); + RegExp* regExp = regExpObject->regExp(); + bool global = regExp->global(); + + if (global) { + // ES5.1 15.5.4.10 step 8.a. + regExpObject->setLastIndex(exec, 0); + if (exec->hadException()) + return JSValue::encode(JSValue()); + + if (callType == CallTypeNone && !replacementString.length()) + return removeUsingRegExpSearch(exec, string, source, regExp); + } + + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + + size_t lastIndex = 0; + unsigned startPosition = 0; + + Vector<StringRange, 16> sourceRanges; + Vector<String, 16> replacements; + + // This is either a loop (if global is set) or a one-way (if not). + if (global && callType == CallTypeJS) { + // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string + int argCount = regExp->numSubpatterns() + 1 + 2; + JSFunction* func = jsCast<JSFunction*>(replaceValue); + CachedCall cachedCall(exec, func, argCount); + if (exec->hadException()) + return JSValue::encode(jsNull()); + VM* vm = &exec->vm(); + if (source.is8Bit()) { + while (true) { + int* ovector; + MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); + if (!result) + break; + + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); + + unsigned i = 0; + for (; i < regExp->numSubpatterns() + 1; ++i) { + int matchStart = ovector[i * 2]; + int matchLen = ovector[i * 2 + 1] - matchStart; + + if (matchStart < 0) + cachedCall.setArgument(i, jsUndefined()); + else + cachedCall.setArgument(i, jsSubstring8(vm, source, matchStart, matchLen)); + } + + cachedCall.setArgument(i++, jsNumber(result.start)); + cachedCall.setArgument(i++, string); + + cachedCall.setThis(jsUndefined()); + JSValue jsResult = cachedCall.call(); + replacements.append(jsResult.toString(exec)->value(exec)); + if (exec->hadException()) + break; + + lastIndex = result.end; + startPosition = lastIndex; + + // special case of empty match + if (result.empty()) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } + } else { + while (true) { + int* ovector; + MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); + if (!result) + break; + + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); + + unsigned i = 0; + for (; i < regExp->numSubpatterns() + 1; ++i) { + int matchStart = ovector[i * 2]; + int matchLen = ovector[i * 2 + 1] - matchStart; + + if (matchStart < 0) + cachedCall.setArgument(i, jsUndefined()); + else + cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen)); + } + + cachedCall.setArgument(i++, jsNumber(result.start)); + cachedCall.setArgument(i++, string); + + cachedCall.setThis(jsUndefined()); + JSValue jsResult = cachedCall.call(); + replacements.append(jsResult.toString(exec)->value(exec)); + if (exec->hadException()) + break; + + lastIndex = result.end; + startPosition = lastIndex; + + // special case of empty match + if (result.empty()) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } + } + } else { + VM* vm = &exec->vm(); + do { + int* ovector; + MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); + if (!result) + break; + + if (callType != CallTypeNone) { + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); + + MarkedArgumentBuffer args; + + for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) { + int matchStart = ovector[i * 2]; + int matchLen = ovector[i * 2 + 1] - matchStart; + + if (matchStart < 0) + args.append(jsUndefined()); + else + args.append(jsSubstring(exec, source, matchStart, matchLen)); + } + + args.append(jsNumber(result.start)); + args.append(string); + + replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec)); + if (exec->hadException()) + break; + } else { + int replLen = replacementString.length(); + if (lastIndex < result.start || replLen) { + sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); + + if (replLen) + replacements.append(substituteBackreferences(replacementString, source, ovector, regExp)); + else + replacements.append(String()); + } + } + + lastIndex = result.end; + startPosition = lastIndex; + + // special case of empty match + if (result.empty()) { + startPosition++; + if (startPosition > sourceLen) + break; + } + } while (global); + } + + if (!lastIndex && replacements.isEmpty()) + return JSValue::encode(string); + + if (static_cast<unsigned>(lastIndex) < sourceLen) + sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); + + return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); +} + +static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue) +{ + const String& string = jsString->value(exec); + String searchString = searchValue.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + size_t matchStart = string.find(searchString); + + if (matchStart == notFound) + return JSValue::encode(jsString); + + JSValue replaceValue = exec->argument(1); + CallData callData; + CallType callType = getCallData(replaceValue, callData); + if (callType != CallTypeNone) { + MarkedArgumentBuffer args; + args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length())); + args.append(jsNumber(matchStart)); + args.append(jsString); + replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + String replaceString = replaceValue.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + StringImpl* stringImpl = string.impl(); + String leftPart(StringImpl::createSubstringSharingImpl(stringImpl, 0, matchStart)); + + size_t matchEnd = matchStart + searchString.impl()->length(); + int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)}; + String middlePart = substituteBackreferences(replaceString, string, ovector, 0); + + size_t leftLength = stringImpl->length() - matchEnd; + String rightPart(StringImpl::createSubstringSharingImpl(stringImpl, matchEnd, leftLength)); + return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart)); +} + +static inline bool checkObjectCoercible(JSValue thisValue) +{ + if (thisValue.isString()) + return true; + + if (thisValue.isUndefinedOrNull()) + return false; + + if (thisValue.isCell() && thisValue.asCell()->structure()->typeInfo().isEnvironmentRecord()) + return false; + + return true; +} + +template <typename CharacterType> +static inline JSValue repeatCharacter(ExecState* exec, CharacterType character, unsigned repeatCount) +{ + CharacterType* buffer = nullptr; + RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(repeatCount, buffer); + if (!impl) + return throwOutOfMemoryError(exec); + + std::fill_n(buffer, repeatCount, character); + + return jsString(exec, impl.release()); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeat(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + JSString* string = thisValue.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + double repeatCountDouble = exec->argument(0).toInteger(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + if (repeatCountDouble < 0 || std::isinf(repeatCountDouble)) + return throwVMError(exec, createRangeError(exec, ASCIILiteral("repeat() argument must be greater than or equal to 0 and not be infinity"))); + + VM& vm = exec->vm(); + + if (!string->length() || !repeatCountDouble) + return JSValue::encode(jsEmptyString(&vm)); + + if (repeatCountDouble == 1) + return JSValue::encode(string); + + // JSString requires the limitation that its length is in the range of int32_t. + if (repeatCountDouble > std::numeric_limits<int32_t>::max() / string->length()) + return JSValue::encode(throwOutOfMemoryError(exec)); + unsigned repeatCount = static_cast<unsigned>(repeatCountDouble); + + // For a string which length is small, instead of creating ropes, + // allocating a sequential buffer and fill with the repeated string for efficiency. + if (string->length() == 1) { + String repeatedString = string->value(exec); + UChar character = repeatedString.at(0); + if (!(character & ~0xff)) + return JSValue::encode(repeatCharacter(exec, static_cast<LChar>(character), repeatCount)); + return JSValue::encode(repeatCharacter(exec, character, repeatCount)); + } + + JSRopeString::RopeBuilder ropeBuilder(vm); + for (unsigned i = 0; i < repeatCount; ++i) { + if (!ropeBuilder.append(string)) + return JSValue::encode(throwOutOfMemoryError(exec)); + } + return JSValue::encode(ropeBuilder.release()); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + JSString* string = thisValue.toString(exec); + JSValue searchValue = exec->argument(0); + + if (searchValue.inherits(RegExpObject::info())) + return replaceUsingRegExpSearch(exec, string, searchValue); + return replaceUsingStringSearch(exec, string, searchValue); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + // Also used for valueOf. + + if (thisValue.isString()) + return JSValue::encode(thisValue); + + if (thisValue.inherits(StringObject::info())) + return JSValue::encode(asStringObject(thisValue)->internalValue()); + + return throwVMTypeError(exec); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + StringView string = thisValue.toString(exec)->view(exec); + JSValue a0 = exec->argument(0); + if (a0.isUInt32()) { + uint32_t i = a0.asUInt32(); + if (i < string.length()) + return JSValue::encode(jsSingleCharacterString(exec, string[i])); + return JSValue::encode(jsEmptyString(exec)); + } + double dpos = a0.toInteger(exec); + if (dpos >= 0 && dpos < string.length()) + return JSValue::encode(jsSingleCharacterString(exec, string[static_cast<unsigned>(dpos)])); + return JSValue::encode(jsEmptyString(exec)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + StringView string = thisValue.toString(exec)->view(exec); + JSValue a0 = exec->argument(0); + if (a0.isUInt32()) { + uint32_t i = a0.asUInt32(); + if (i < string.length()) + return JSValue::encode(jsNumber(string[i])); + return JSValue::encode(jsNaN()); + } + double dpos = a0.toInteger(exec); + if (dpos >= 0 && dpos < string.length()) + return JSValue::encode(jsNumber(string[static_cast<int>(dpos)])); + return JSValue::encode(jsNaN()); +} + +static inline UChar32 codePointAt(const String& string, unsigned position, unsigned length) +{ + RELEASE_ASSERT(position < length); + if (string.is8Bit()) + return string.characters8()[position]; + UChar32 character; + U16_NEXT(string.characters16(), position, length, character); + return character; +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + String string = thisValue.toWTFString(exec); + unsigned length = string.length(); + + JSValue argument0 = exec->argument(0); + if (argument0.isUInt32()) { + unsigned position = argument0.asUInt32(); + if (position < length) + return JSValue::encode(jsNumber(codePointAt(string, position, length))); + return JSValue::encode(jsUndefined()); + } + + if (UNLIKELY(exec->hadException())) + return JSValue::encode(jsUndefined()); + + double doublePosition = argument0.toInteger(exec); + if (doublePosition >= 0 && doublePosition < length) + return JSValue::encode(jsNumber(codePointAt(string, static_cast<unsigned>(doublePosition), length))); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (thisValue.isString() && exec->argumentCount() == 1) + return JSValue::encode(jsString(exec, asString(thisValue), exec->uncheckedArgument(0).toString(exec))); + + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + return JSValue::encode(jsStringFromArguments(exec, thisValue)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + JSString* thisJSString = thisValue.toString(exec); + JSString* otherJSString = a0.toString(exec); + + unsigned pos = 0; + if (!a1.isUndefined()) { + int len = thisJSString->length(); + RELEASE_ASSERT(len >= 0); + if (a1.isUInt32()) + pos = std::min<uint32_t>(a1.asUInt32(), len); + else { + double dpos = a1.toInteger(exec); + if (dpos < 0) + dpos = 0; + else if (dpos > len) + dpos = len; + pos = static_cast<unsigned>(dpos); + } + } + + if (thisJSString->length() < otherJSString->length() + pos) + return JSValue::encode(jsNumber(-1)); + + size_t result = thisJSString->view(exec).get().find(otherJSString->view(exec), pos); + if (result == notFound) + return JSValue::encode(jsNumber(-1)); + return JSValue::encode(jsNumber(result)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + JSString* thisJSString = thisValue.toString(exec); + unsigned len = thisJSString->length(); + JSString* otherJSString = a0.toString(exec); + + double dpos = a1.toIntegerPreserveNaN(exec); + unsigned startPosition; + if (dpos < 0) + startPosition = 0; + else if (!(dpos <= len)) // true for NaN + startPosition = len; + else + startPosition = static_cast<unsigned>(dpos); + + if (len < otherJSString->length()) + return JSValue::encode(jsNumber(-1)); + + String thisString = thisJSString->value(exec); + String otherString = otherJSString->value(exec); + size_t result; + if (!startPosition) + result = thisString.startsWith(otherString) ? 0 : notFound; + else + result = thisString.reverseFind(otherString, startPosition); + if (result == notFound) + return JSValue::encode(jsNumber(-1)); + return JSValue::encode(jsNumber(result)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + JSString* string = thisValue.toString(exec); + String s = string->value(exec); + VM* vm = &exec->vm(); + + JSValue a0 = exec->argument(0); + + RegExp* regExp; + bool global = false; + if (a0.inherits(RegExpObject::info())) { + RegExpObject* regExpObject = asRegExpObject(a0); + regExp = regExpObject->regExp(); + if ((global = regExp->global())) { + // ES5.1 15.5.4.10 step 8.a. + regExpObject->setLastIndex(exec, 0); + if (exec->hadException()) + return JSValue::encode(JSValue()); + } + } else { + /* + * ECMA 15.5.4.12 String.prototype.search (regexp) + * If regexp is not an object whose [[Class]] property is "RegExp", it is + * replaced with the result of the expression new RegExp(regexp). + * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string. + */ + regExp = RegExp::create(exec->vm(), a0.isUndefined() ? emptyString() : a0.toString(exec)->value(exec), NoFlags); + if (!regExp->isValid()) + return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage())); + } + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, s, 0); + // case without 'g' flag is handled like RegExp.prototype.exec + if (!global) + return JSValue::encode(result ? createRegExpMatchesArray(exec, string, regExp, result) : jsNull()); + + // return array of matches + MarkedArgumentBuffer list; + while (result) { + size_t end = result.end; + size_t length = end - result.start; + list.append(jsSubstring(exec, s, result.start, length)); + if (!length) + ++end; + result = regExpConstructor->performMatch(*vm, regExp, string, s, end); + } + if (list.isEmpty()) { + // if there are no matches at all, it's important to return + // Null instead of an empty array, because this matches + // other browsers and because Null is a false value. + return JSValue::encode(jsNull()); + } + + return JSValue::encode(constructArray(exec, static_cast<ArrayAllocationProfile*>(0), list)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + JSString* string = thisValue.toString(exec); + String s = string->value(exec); + VM* vm = &exec->vm(); + + JSValue a0 = exec->argument(0); + + RegExp* reg; + if (a0.inherits(RegExpObject::info())) + reg = asRegExpObject(a0)->regExp(); + else { + /* + * ECMA 15.5.4.12 String.prototype.search (regexp) + * If regexp is not an object whose [[Class]] property is "RegExp", it is + * replaced with the result of the expression new RegExp(regexp). + * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string. + */ + reg = RegExp::create(exec->vm(), a0.isUndefined() ? emptyString() : a0.toString(exec)->value(exec), NoFlags); + if (!reg->isValid()) + return throwVMError(exec, createSyntaxError(exec, reg->errorMessage())); + } + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + MatchResult result = regExpConstructor->performMatch(*vm, reg, string, s, 0); + return JSValue::encode(result ? jsNumber(result.start) : jsNumber(-1)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + int len = s.length(); + RELEASE_ASSERT(len >= 0); + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + // The arg processing is very much like ArrayProtoFunc::Slice + double start = a0.toInteger(exec); + double end = a1.isUndefined() ? len : a1.toInteger(exec); + double from = start < 0 ? len + start : start; + double to = end < 0 ? len + end : end; + if (to > from && to > 0 && from < len) { + if (from < 0) + from = 0; + if (to > len) + to = len; + return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from))); + } + + return JSValue::encode(jsEmptyString(exec)); +} + +// Return true in case of early return (resultLength got to limitLength). +template<typename CharacterType> +static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray* result, JSValue originalValue, const String& input, StringImpl* string, UChar separatorCharacter, size_t& position, unsigned& resultLength, unsigned limitLength) +{ + // 12. Let q = p. + size_t matchPosition; + const CharacterType* characters = string->characters<CharacterType>(); + // 13. Repeat, while q != s + // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. + // b. If z is failure, then let q = q+1. + // c. Else, z is not failure + while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) { + // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through q (exclusive). + // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), + // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position)); + // 3. Increment lengthA by 1. + // 4. If lengthA == lim, return A. + if (++resultLength == limitLength) + return true; + + // 5. Let p = e. + // 8. Let q = p. + position = matchPosition + 1; + } + return false; +} + +// ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) +{ + // 1. Call CheckObjectCoercible passing the this value as its argument. + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + // 2. Let S be the result of calling ToString, giving it the this value as its argument. + // 6. Let s be the number of characters in S. + String input = thisValue.toString(exec)->value(exec); + + // 3. Let A be a new array created as if by the expression new Array() + // where Array is the standard built-in constructor with that name. + JSArray* result = constructEmptyArray(exec, 0); + + // 4. Let lengthA be 0. + unsigned resultLength = 0; + + // 5. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit). + JSValue limitValue = exec->argument(1); + unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec); + + // 7. Let p = 0. + size_t position = 0; + + // 8. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator; + // otherwise let R = ToString(separator). + JSValue separatorValue = exec->argument(0); + if (separatorValue.inherits(RegExpObject::info())) { + VM* vm = &exec->vm(); + RegExp* reg = asRegExpObject(separatorValue)->regExp(); + + // 9. If lim == 0, return A. + if (!limit) + return JSValue::encode(result); + + // 10. If separator is undefined, then + if (separatorValue.isUndefined()) { + // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0", + // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input)); + // b. Return A. + return JSValue::encode(result); + } + + // 11. If s == 0, then + if (input.isEmpty()) { + // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result. + // b. If z is not failure, return A. + // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0", + // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + // d. Return A. + if (!reg->match(*vm, input, 0)) + result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input)); + return JSValue::encode(result); + } + + // 12. Let q = p. + size_t matchPosition = 0; + // 13. Repeat, while q != s + while (matchPosition < input.length()) { + // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. + Vector<int, 32> ovector; + int mpos = reg->match(*vm, input, matchPosition, ovector); + + // b. If z is a failure then we can break because there are no matches + if (mpos < 0) + break; + matchPosition = mpos; + + // if the match is the empty match at the end, break. + if (matchPosition >= input.length()) + break; + + // c. Else, z is not failure + // i. z must be a State. Let e be z's endIndex and let cap be z's captures array. + size_t matchEnd = ovector[1]; + + // ii. If e == p, then let q = q + 1. + if (matchEnd == position) { + ++matchPosition; + continue; + } + // iii. if matchEnd == 0 then position should also be zero and thus matchEnd should equal position. + ASSERT(matchEnd); + + // iii. Else, e != p + + // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through q (exclusive). + // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), + // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position)); + + // 3. Increment lengthA by 1. + // 4. If lengthA == lim, return A. + if (++resultLength == limit) + return JSValue::encode(result); + + // 5. Let p = e. + // 8. Let q = p. + position = matchEnd; + matchPosition = matchEnd; + + // 6. Let i = 0. + // 7. Repeat, while i is not equal to the number of elements in cap. + // a Let i = i + 1. + for (unsigned i = 1; i <= reg->numSubpatterns(); ++i) { + // b Call the [[DefineOwnProperty]] internal method of A with arguments + // ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]: + // true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + int sub = ovector[i * 2]; + result->putDirectIndex(exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, thisValue, input, sub, ovector[i * 2 + 1] - sub)); + // c Increment lengthA by 1. + // d If lengthA == lim, return A. + if (++resultLength == limit) + return JSValue::encode(result); + } + } + } else { + String separator = separatorValue.toString(exec)->value(exec); + + // 9. If lim == 0, return A. + if (!limit) + return JSValue::encode(result); + + // 10. If separator is undefined, then + JSValue separatorValue = exec->argument(0); + if (separatorValue.isUndefined()) { + // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0", + // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input)); + // b. Return A. + return JSValue::encode(result); + } + + // 11. If s == 0, then + if (input.isEmpty()) { + // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result. + // b. If z is not failure, return A. + // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0", + // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + // d. Return A. + if (!separator.isEmpty()) + result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input)); + return JSValue::encode(result); + } + + // Optimized case for splitting on the empty string. + if (separator.isEmpty()) { + limit = std::min(limit, input.length()); + // Zero limt/input length handled in steps 9/11 respectively, above. + ASSERT(limit); + + do { + result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position])); + } while (++position < limit); + + return JSValue::encode(result); + } + + // 3 cases: + // -separator length == 1, 8 bits + // -separator length == 1, 16 bits + // -separator length > 1 + StringImpl* stringImpl = input.impl(); + StringImpl* separatorImpl = separator.impl(); + size_t separatorLength = separatorImpl->length(); + + if (separatorLength == 1) { + UChar separatorCharacter; + if (separatorImpl->is8Bit()) + separatorCharacter = separatorImpl->characters8()[0]; + else + separatorCharacter = separatorImpl->characters16()[0]; + + if (stringImpl->is8Bit()) { + if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) + return JSValue::encode(result); + } else { + if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) + return JSValue::encode(result); + } + } else { + // 12. Let q = p. + size_t matchPosition; + // 13. Repeat, while q != s + // a. Call SplitMatch(S, q, R) and let z be its MatchResult result. + // b. If z is failure, then let q = q+1. + // c. Else, z is not failure + while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) { + // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through q (exclusive). + // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), + // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position)); + // 3. Increment lengthA by 1. + // 4. If lengthA == lim, return A. + if (++resultLength == limit) + return JSValue::encode(result); + + // 5. Let p = e. + // 8. Let q = p. + position = matchPosition + separator.length(); + } + } + } + + // 14. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive) + // through s (exclusive). + // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor + // {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false. + result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position)); + + // 16. Return A. + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + unsigned len; + JSString* jsString = 0; + String uString; + if (thisValue.isString()) { + jsString = jsCast<JSString*>(thisValue.asCell()); + len = jsString->length(); + } else { + uString = thisValue.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + len = uString.length(); + } + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + + double start = a0.toInteger(exec); + double length = a1.isUndefined() ? len : a1.toInteger(exec); + if (start >= len || length <= 0) + return JSValue::encode(jsEmptyString(exec)); + if (start < 0) { + start += len; + if (start < 0) + start = 0; + } + if (start + length > len) + length = len - start; + unsigned substringStart = static_cast<unsigned>(start); + unsigned substringLength = static_cast<unsigned>(length); + if (jsString) + return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength)); + return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec) +{ + SamplingRegion samplingRegion("Doing substringing"); + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + JSString* jsString = thisValue.toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); + int len = jsString->length(); + RELEASE_ASSERT(len >= 0); + + double start = a0.toNumber(exec); + double end; + if (!(start >= 0)) // check for negative values or NaN + start = 0; + else if (start > len) + start = len; + if (a1.isUndefined()) + end = len; + else { + end = a1.toNumber(exec); + if (!(end >= 0)) // check for negative values or NaN + end = 0; + else if (end > len) + end = len; + } + if (start > end) { + double temp = end; + end = start; + start = temp; + } + unsigned substringStart = static_cast<unsigned>(start); + unsigned substringLength = static_cast<unsigned>(end) - substringStart; + return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + JSString* sVal = thisValue.toString(exec); + const String& s = sVal->value(exec); + + int sSize = s.length(); + if (!sSize) + return JSValue::encode(sVal); + RELEASE_ASSERT(sSize >= 0); + + StringImpl* ourImpl = s.impl(); + RefPtr<StringImpl> lower = ourImpl->lower(); + if (ourImpl == lower) + return JSValue::encode(sVal); + return JSValue::encode(jsString(exec, String(lower.release()))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + JSString* sVal = thisValue.toString(exec); + const String& s = sVal->value(exec); + + int sSize = s.length(); + if (!sSize) + return JSValue::encode(sVal); + + StringImpl* sImpl = s.impl(); + RefPtr<StringImpl> upper = sImpl->upper(); + if (sImpl == upper) + return JSValue::encode(sVal); + return JSValue::encode(jsString(exec, String(upper.release()))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + + JSValue a0 = exec->argument(0); + return JSValue::encode(jsNumber(Collator().collate(s, a0.toString(exec)->value(exec)))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + JSValue a0 = exec->argument(0); + String color = a0.toWTFString(exec); + color.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + JSValue a0 = exec->argument(0); + + uint32_t smallInteger; + if (a0.getUInt32(smallInteger) && smallInteger <= 9) { + unsigned stringSize = s.length(); + unsigned bufferSize = 22 + stringSize; + // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version? + UChar* buffer; + PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer); + if (!impl) + return JSValue::encode(jsUndefined()); + buffer[0] = '<'; + buffer[1] = 'f'; + buffer[2] = 'o'; + buffer[3] = 'n'; + buffer[4] = 't'; + buffer[5] = ' '; + buffer[6] = 's'; + buffer[7] = 'i'; + buffer[8] = 'z'; + buffer[9] = 'e'; + buffer[10] = '='; + buffer[11] = '"'; + buffer[12] = '0' + smallInteger; + buffer[13] = '"'; + buffer[14] = '>'; + StringView(s).getCharactersWithUpconvert(&buffer[15]); + buffer[15 + stringSize] = '<'; + buffer[16 + stringSize] = '/'; + buffer[17 + stringSize] = 'f'; + buffer[18 + stringSize] = 'o'; + buffer[19 + stringSize] = 'n'; + buffer[20 + stringSize] = 't'; + buffer[21 + stringSize] = '>'; + return JSValue::encode(jsNontrivialString(exec, impl)); + } + + String fontSize = a0.toWTFString(exec); + fontSize.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + JSValue a0 = exec->argument(0); + String anchor = a0.toWTFString(exec); + anchor.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>")); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + String s = thisValue.toString(exec)->value(exec); + JSValue a0 = exec->argument(0); + String linkText = a0.toWTFString(exec); + linkText.replaceWithLiteral('"', """); + + unsigned linkTextSize = linkText.length(); + unsigned stringSize = s.length(); + unsigned bufferSize = 15 + linkTextSize + stringSize; + // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version? + UChar* buffer; + PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer); + if (!impl) + return JSValue::encode(jsUndefined()); + buffer[0] = '<'; + buffer[1] = 'a'; + buffer[2] = ' '; + buffer[3] = 'h'; + buffer[4] = 'r'; + buffer[5] = 'e'; + buffer[6] = 'f'; + buffer[7] = '='; + buffer[8] = '"'; + StringView(linkText).getCharactersWithUpconvert(&buffer[9]); + buffer[9 + linkTextSize] = '"'; + buffer[10 + linkTextSize] = '>'; + StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]); + buffer[11 + linkTextSize + stringSize] = '<'; + buffer[12 + linkTextSize + stringSize] = '/'; + buffer[13 + linkTextSize + stringSize] = 'a'; + buffer[14 + linkTextSize + stringSize] = '>'; + return JSValue::encode(jsNontrivialString(exec, impl)); +} + +enum { + TrimLeft = 1, + TrimRight = 2 +}; + +static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind) +{ + if (!checkObjectCoercible(thisValue)) + return throwTypeError(exec); + String str = thisValue.toString(exec)->value(exec); + unsigned left = 0; + if (trimKind & TrimLeft) { + while (left < str.length() && isStrWhiteSpace(str[left])) + left++; + } + unsigned right = str.length(); + if (trimKind & TrimRight) { + while (right > left && isStrWhiteSpace(str[right - 1])) + right--; + } + + // Don't gc allocate a new string if we don't have to. + if (left == 0 && right == str.length() && thisValue.isString()) + return thisValue; + + return jsString(exec, str.substringSharingImpl(left, right - left)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimLeft)); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimRight)); +} + +static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max) +{ + if (value < min) + return min; + if (value > max) + return max; + return static_cast<unsigned>(value); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + String stringToSearchIn = thisValue.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue a0 = exec->argument(0); + if (jsDynamicCast<RegExpObject*>(a0)) + return throwVMTypeError(exec); + + String searchString = a0.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue positionArg = exec->argument(1); + unsigned start = 0; + if (positionArg.isInt32()) + start = std::max(0, positionArg.asInt32()); + else { + unsigned length = stringToSearchIn.length(); + start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + String stringToSearchIn = thisValue.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue a0 = exec->argument(0); + if (jsDynamicCast<RegExpObject*>(a0)) + return throwVMTypeError(exec); + + String searchString = a0.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + unsigned length = stringToSearchIn.length(); + + JSValue endPositionArg = exec->argument(1); + unsigned end = length; + if (endPositionArg.isInt32()) + end = std::max(0, endPositionArg.asInt32()); + else if (!endPositionArg.isUndefined()) { + end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length)))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + + String stringToSearchIn = thisValue.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue a0 = exec->argument(0); + if (jsDynamicCast<RegExpObject*>(a0)) + return throwVMTypeError(exec); + + String searchString = a0.toString(exec)->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + JSValue positionArg = exec->argument(1); + unsigned start = 0; + if (positionArg.isInt32()) + start = std::max(0, positionArg.asInt32()); + else { + unsigned length = stringToSearchIn.length(); + start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start))); +} + +EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (!checkObjectCoercible(thisValue)) + return throwVMTypeError(exec); + JSString* string = thisValue.toString(exec); + return JSValue::encode(JSStringIterator::create(exec, exec->callee()->globalObject()->stringIteratorStructure(), string)); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StringPrototype.h b/Source/JavaScriptCore/runtime/StringPrototype.h new file mode 100644 index 000000000..bd4f02a61 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringPrototype.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef StringPrototype_h +#define StringPrototype_h + +#include "StringObject.h" + +namespace JSC { + +class ObjectPrototype; + +class StringPrototype : public StringObject { +private: + StringPrototype(VM&, Structure*); + +public: + typedef StringObject Base; + static const unsigned StructureFlags = Base::StructureFlags; + + static StringPrototype* create(VM&, JSGlobalObject*, Structure*); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + DECLARE_INFO; + +protected: + void finishCreation(VM&, JSGlobalObject*, JSString*); +}; + +} // namespace JSC + +#endif // StringPrototype_h diff --git a/Source/JavaScriptCore/runtime/StringRecursionChecker.cpp b/Source/JavaScriptCore/runtime/StringRecursionChecker.cpp new file mode 100644 index 000000000..638310787 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringRecursionChecker.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StringRecursionChecker.h" + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSCInlines.h" + +namespace JSC { + +JSValue StringRecursionChecker::throwStackOverflowError() +{ + return JSC::throwStackOverflowError(m_exec); +} + +JSValue StringRecursionChecker::emptyString() +{ + return jsEmptyString(m_exec); +} + +} diff --git a/Source/JavaScriptCore/runtime/StringRecursionChecker.h b/Source/JavaScriptCore/runtime/StringRecursionChecker.h new file mode 100644 index 000000000..0f1990e76 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StringRecursionChecker.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef StringRecursionChecker_h +#define StringRecursionChecker_h + +#include "Interpreter.h" +#include <wtf/StackStats.h> +#include <wtf/WTFThreadData.h> + +namespace JSC { + +class StringRecursionChecker { + WTF_MAKE_NONCOPYABLE(StringRecursionChecker); + +public: + StringRecursionChecker(ExecState*, JSObject* thisObject); + ~StringRecursionChecker(); + + JSValue earlyReturnValue() const; // 0 if everything is OK, value to return for failure cases + +private: + JSValue throwStackOverflowError(); + JSValue emptyString(); + JSValue performCheck(); + + ExecState* m_exec; + JSObject* m_thisObject; + JSValue m_earlyReturnValue; + + StackStats::CheckPoint stackCheckpoint; +}; + +inline JSValue StringRecursionChecker::performCheck() +{ + VM& vm = m_exec->vm(); + if (!vm.isSafeToRecurse()) + return throwStackOverflowError(); + + bool alreadyVisited = false; + if (!vm.stringRecursionCheckFirstObject) + vm.stringRecursionCheckFirstObject = m_thisObject; + else if (vm.stringRecursionCheckFirstObject == m_thisObject) + alreadyVisited = true; + else + alreadyVisited = !vm.stringRecursionCheckVisitedObjects.add(m_thisObject).isNewEntry; + + if (alreadyVisited) + return emptyString(); // Return empty string to avoid infinite recursion. + return JSValue(); // Indicate success. +} + +inline StringRecursionChecker::StringRecursionChecker(ExecState* exec, JSObject* thisObject) + : m_exec(exec) + , m_thisObject(thisObject) + , m_earlyReturnValue(performCheck()) +{ +} + +inline JSValue StringRecursionChecker::earlyReturnValue() const +{ + return m_earlyReturnValue; +} + +inline StringRecursionChecker::~StringRecursionChecker() +{ + if (m_earlyReturnValue) + return; + + VM& vm = m_exec->vm(); + if (vm.stringRecursionCheckFirstObject == m_thisObject) + vm.stringRecursionCheckFirstObject = nullptr; + else { + ASSERT(vm.stringRecursionCheckVisitedObjects.contains(m_thisObject)); + vm.stringRecursionCheckVisitedObjects.remove(m_thisObject); + } +} + +} + +#endif diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp new file mode 100644 index 000000000..28f8d4803 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -0,0 +1,1336 @@ +/* + * Copyright (C) 2008, 2009, 2013-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "Structure.h" + +#include "CodeBlock.h" +#include "DumpContext.h" +#include "JSCInlines.h" +#include "JSObject.h" +#include "JSPropertyNameEnumerator.h" +#include "Lookup.h" +#include "PropertyMapHashTable.h" +#include "PropertyNameArray.h" +#include "StructureChain.h" +#include "StructureRareDataInlines.h" +#include "WeakGCMapInlines.h" +#include <wtf/CommaPrinter.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/ProcessID.h> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/RefPtr.h> +#include <wtf/Threading.h> + +#define DUMP_STRUCTURE_ID_STATISTICS 0 + +#ifndef NDEBUG +#define DO_PROPERTYMAP_CONSTENCY_CHECK 0 +#else +#define DO_PROPERTYMAP_CONSTENCY_CHECK 0 +#endif + +using namespace std; +using namespace WTF; + +namespace JSC { + +#if DUMP_STRUCTURE_ID_STATISTICS +static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); +#endif + +class SingleSlotTransitionWeakOwner final : public WeakHandleOwner { + void finalize(Handle<Unknown>, void* context) override + { + StructureTransitionTable* table = reinterpret_cast<StructureTransitionTable*>(context); + ASSERT(table->isUsingSingleSlot()); + WeakSet::deallocate(table->weakImpl()); + table->m_data = StructureTransitionTable::UsingSingleSlotFlag; + } +}; + +static SingleSlotTransitionWeakOwner& singleSlotTransitionWeakOwner() +{ + static NeverDestroyed<SingleSlotTransitionWeakOwner> owner; + return owner; +} + +inline Structure* StructureTransitionTable::singleTransition() const +{ + ASSERT(isUsingSingleSlot()); + if (WeakImpl* impl = this->weakImpl()) { + if (impl->state() == WeakImpl::Live) + return jsCast<Structure*>(impl->jsValue().asCell()); + } + return nullptr; +} + +inline void StructureTransitionTable::setSingleTransition(Structure* structure) +{ + ASSERT(isUsingSingleSlot()); + if (WeakImpl* impl = this->weakImpl()) + WeakSet::deallocate(impl); + WeakImpl* impl = WeakSet::allocate(structure, &singleSlotTransitionWeakOwner(), this); + m_data = reinterpret_cast<intptr_t>(impl) | UsingSingleSlotFlag; +} + +bool StructureTransitionTable::contains(UniquedStringImpl* rep, unsigned attributes) const +{ + if (isUsingSingleSlot()) { + Structure* transition = singleTransition(); + return transition && transition->m_nameInPrevious == rep && transition->attributesInPrevious() == attributes; + } + return map()->get(std::make_pair(rep, attributes)); +} + +Structure* StructureTransitionTable::get(UniquedStringImpl* rep, unsigned attributes) const +{ + if (isUsingSingleSlot()) { + Structure* transition = singleTransition(); + return (transition && transition->m_nameInPrevious == rep && transition->attributesInPrevious() == attributes) ? transition : 0; + } + return map()->get(std::make_pair(rep, attributes)); +} + +void StructureTransitionTable::add(VM& vm, Structure* structure) +{ + if (isUsingSingleSlot()) { + Structure* existingTransition = singleTransition(); + + // This handles the first transition being added. + if (!existingTransition) { + setSingleTransition(structure); + return; + } + + // This handles the second transition being added + // (or the first transition being despecified!) + setMap(new TransitionMap(vm)); + add(vm, existingTransition); + } + + // Add the structure to the map. + + // Newer versions of the STL have an std::make_pair function that takes rvalue references. + // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue. + // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details + map()->set(std::make_pair(structure->m_nameInPrevious.get(), +structure->attributesInPrevious()), structure); +} + +void Structure::dumpStatistics() +{ +#if DUMP_STRUCTURE_ID_STATISTICS + unsigned numberLeaf = 0; + unsigned numberUsingSingleSlot = 0; + unsigned numberSingletons = 0; + unsigned numberWithPropertyMaps = 0; + unsigned totalPropertyMapsSize = 0; + + HashSet<Structure*>::const_iterator end = liveStructureSet.end(); + for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) { + Structure* structure = *it; + + switch (structure->m_transitionTable.size()) { + case 0: + ++numberLeaf; + if (!structure->previousID()) + ++numberSingletons; + break; + + case 1: + ++numberUsingSingleSlot; + break; + } + + if (structure->propertyTable()) { + ++numberWithPropertyMaps; + totalPropertyMapsSize += structure->propertyTable()->sizeInMemory(); + } + } + + dataLogF("Number of live Structures: %d\n", liveStructureSet.size()); + dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); + dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf); + dataLogF("Number of Structures that singletons: %d\n", numberSingletons); + dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); + + dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); + dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize); + dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); +#else + dataLogF("Dumping Structure statistics is not enabled.\n"); +#endif +} + +Structure::Structure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity) + : JSCell(vm, vm.structureStructure.get()) + , m_blob(vm.heap.structureIDTable().allocateID(this), indexingType, typeInfo) + , m_outOfLineTypeFlags(typeInfo.outOfLineTypeFlags()) + , m_globalObject(vm, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull) + , m_prototype(vm, this, prototype) + , m_classInfo(classInfo) + , m_transitionWatchpointSet(IsWatched) + , m_offset(invalidOffset) + , m_inlineCapacity(inlineCapacity) + , m_bitField(0) +{ + setDictionaryKind(NoneDictionaryKind); + setIsPinnedPropertyTable(false); + setHasGetterSetterProperties(classInfo->hasStaticSetterOrReadonlyProperties()); + setHasCustomGetterSetterProperties(false); + setHasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo->hasStaticSetterOrReadonlyProperties()); + setHasNonEnumerableProperties(false); + setAttributesInPrevious(0); + setPreventExtensions(false); + setDidTransition(false); + setStaticFunctionsReified(false); + setHasRareData(false); + setTransitionWatchpointIsLikelyToBeFired(false); + + ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity()); + ASSERT(static_cast<PropertyOffset>(inlineCapacity) < firstOutOfLineOffset); + ASSERT(!hasRareData()); + ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); + ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); +} + +const ClassInfo Structure::s_info = { "Structure", 0, 0, CREATE_METHOD_TABLE(Structure) }; + +Structure::Structure(VM& vm) + : JSCell(CreatingEarlyCell) + , m_prototype(vm, this, jsNull()) + , m_classInfo(info()) + , m_transitionWatchpointSet(IsWatched) + , m_offset(invalidOffset) + , m_inlineCapacity(0) + , m_bitField(0) +{ + setDictionaryKind(NoneDictionaryKind); + setIsPinnedPropertyTable(false); + setHasGetterSetterProperties(m_classInfo->hasStaticSetterOrReadonlyProperties()); + setHasCustomGetterSetterProperties(false); + setHasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo->hasStaticSetterOrReadonlyProperties()); + setHasNonEnumerableProperties(false); + setAttributesInPrevious(0); + setPreventExtensions(false); + setDidTransition(false); + setStaticFunctionsReified(false); + setHasRareData(false); + setTransitionWatchpointIsLikelyToBeFired(false); + + TypeInfo typeInfo = TypeInfo(CellType, StructureFlags); + m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), 0, typeInfo); + m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags(); + + ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); + ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); +} + +Structure::Structure(VM& vm, Structure* previous, DeferredStructureTransitionWatchpointFire* deferred) + : JSCell(vm, vm.structureStructure.get()) + , m_prototype(vm, this, previous->storedPrototype()) + , m_classInfo(previous->m_classInfo) + , m_transitionWatchpointSet(IsWatched) + , m_offset(invalidOffset) + , m_inlineCapacity(previous->m_inlineCapacity) + , m_bitField(0) +{ + setDictionaryKind(previous->dictionaryKind()); + setIsPinnedPropertyTable(previous->hasBeenFlattenedBefore()); + setHasGetterSetterProperties(previous->hasGetterSetterProperties()); + setHasCustomGetterSetterProperties(previous->hasCustomGetterSetterProperties()); + setHasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->hasReadOnlyOrGetterSetterPropertiesExcludingProto()); + setHasNonEnumerableProperties(previous->hasNonEnumerableProperties()); + setAttributesInPrevious(0); + setPreventExtensions(previous->preventExtensions()); + setDidTransition(true); + setStaticFunctionsReified(previous->staticFunctionsReified()); + setHasRareData(false); + + TypeInfo typeInfo = previous->typeInfo(); + m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), previous->indexingTypeIncludingHistory(), typeInfo); + m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags(); + + ASSERT(!previous->typeInfo().structureIsImmortal()); + setPreviousID(vm, previous); + + previous->didTransitionFromThisStructure(deferred); + + // Copy this bit now, in case previous was being watched. + setTransitionWatchpointIsLikelyToBeFired(previous->transitionWatchpointIsLikelyToBeFired()); + + if (previous->m_globalObject) + m_globalObject.set(vm, this, previous->m_globalObject.get()); + ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); + ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); +} + +Structure::~Structure() +{ + if (typeInfo().structureIsImmortal()) + return; + Heap::heap(this)->structureIDTable().deallocateID(this, m_blob.structureID()); +} + +void Structure::destroy(JSCell* cell) +{ + static_cast<Structure*>(cell)->Structure::~Structure(); +} + +void Structure::findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*& structure, PropertyTable*& table) +{ + ASSERT(structures.isEmpty()); + table = 0; + + for (structure = this; structure; structure = structure->previousID()) { + structure->m_lock.lock(); + + table = structure->propertyTable().get(); + if (table) { + // Leave the structure locked, so that the caller can do things to it atomically + // before it loses its property table. + return; + } + + structures.append(structure); + structure->m_lock.unlock(); + } + + ASSERT(!structure); + ASSERT(!table); +} + +void Structure::materializePropertyMap(VM& vm) +{ + ASSERT(structure()->classInfo() == info()); + ASSERT(!propertyTable()); + + Vector<Structure*, 8> structures; + Structure* structure; + PropertyTable* table; + + findStructuresAndMapForMaterialization(structures, structure, table); + + if (table) { + table = table->copy(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); + structure->m_lock.unlock(); + } + + // Must hold the lock on this structure, since we will be modifying this structure's + // property map. We don't want getConcurrently() to see the property map in a half-baked + // state. + GCSafeConcurrentJITLocker locker(m_lock, vm.heap); + if (!table) + createPropertyMap(locker, vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); + else + propertyTable().set(vm, this, table); + + for (size_t i = structures.size(); i--;) { + structure = structures[i]; + if (!structure->m_nameInPrevious) + continue; + PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->attributesInPrevious()); + propertyTable()->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange); + } + + checkOffsetConsistency(); +} + +Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, UniquedStringImpl* uid, unsigned attributes, PropertyOffset& offset) +{ + ASSERT(!structure->isDictionary()); + ASSERT(structure->isObject()); + + if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes)) { + validateOffset(existingTransition->m_offset, existingTransition->inlineCapacity()); + offset = existingTransition->m_offset; + return existingTransition; + } + + return 0; +} + +Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset) +{ + ASSERT(!isCompilationThread()); + return addPropertyTransitionToExistingStructureImpl(structure, propertyName.uid(), attributes, offset); +} + +Structure* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure* structure, UniquedStringImpl* uid, unsigned attributes, PropertyOffset& offset) +{ + ConcurrentJITLocker locker(structure->m_lock); + return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, offset); +} + +bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const +{ + for (const Structure* current = this; ;) { + if (current->mayInterceptIndexedAccesses()) + return true; + + JSValue prototype = current->storedPrototype(); + if (prototype.isNull()) + return false; + + current = asObject(prototype)->structure(); + } +} + +bool Structure::holesMustForwardToPrototype(VM& vm) const +{ + if (this->mayInterceptIndexedAccesses()) + return true; + + JSValue prototype = this->storedPrototype(); + if (!prototype.isObject()) + return false; + JSObject* object = asObject(prototype); + + while (true) { + Structure& structure = *object->structure(vm); + if (hasIndexedProperties(object->indexingType()) || structure.mayInterceptIndexedAccesses()) + return true; + prototype = structure.storedPrototype(); + if (!prototype.isObject()) + return false; + object = asObject(prototype); + } + + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +bool Structure::needsSlowPutIndexing() const +{ + return anyObjectInChainMayInterceptIndexedAccesses() + || globalObject()->isHavingABadTime(); +} + +NonPropertyTransition Structure::suggestedArrayStorageTransition() const +{ + if (needsSlowPutIndexing()) + return AllocateSlowPutArrayStorage; + + return AllocateArrayStorage; +} + +Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset, PutPropertySlot::Context context, DeferredStructureTransitionWatchpointFire* deferred) +{ + ASSERT(!structure->isDictionary()); + ASSERT(structure->isObject()); + ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, offset)); + + int maxTransitionLength; + if (context == PutPropertySlot::PutById) + maxTransitionLength = s_maxTransitionLengthForNonEvalPutById; + else + maxTransitionLength = s_maxTransitionLength; + if (structure->transitionCount() > maxTransitionLength) { + Structure* transition = toCacheableDictionaryTransition(vm, structure, deferred); + ASSERT(structure != transition); + offset = transition->add(vm, propertyName, attributes); + return transition; + } + + Structure* transition = create(vm, structure, deferred); + + transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get()); + transition->m_nameInPrevious = propertyName.uid(); + transition->setAttributesInPrevious(attributes); + transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); + transition->m_offset = structure->m_offset; + + offset = transition->add(vm, propertyName, attributes); + + checkOffset(transition->m_offset, transition->inlineCapacity()); + { + ConcurrentJITLocker locker(structure->m_lock); + structure->m_transitionTable.add(vm, transition); + } + transition->checkOffsetConsistency(); + structure->checkOffsetConsistency(); + return transition; +} + +Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset) +{ + ASSERT(!structure->isUncacheableDictionary()); + + Structure* transition = toUncacheableDictionaryTransition(vm, structure); + + offset = transition->remove(propertyName); + + transition->checkOffsetConsistency(); + return transition; +} + +Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JSValue prototype) +{ + Structure* transition = create(vm, structure); + + transition->m_prototype.set(vm, transition, prototype); + + DeferGC deferGC(vm.heap); + structure->materializePropertyMapIfNecessary(vm, deferGC); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); + transition->m_offset = structure->m_offset; + transition->pin(); + + transition->checkOffsetConsistency(); + return transition; +} + +Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes) +{ + DeferGC deferGC(vm.heap); + if (!structure->isUncacheableDictionary()) { + Structure* transition = create(vm, structure); + + structure->materializePropertyMapIfNecessary(vm, deferGC); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); + transition->m_offset = structure->m_offset; + transition->pin(); + + structure = transition; + } + + ASSERT(structure->propertyTable()); + PropertyMapEntry* entry = structure->propertyTable()->get(propertyName.uid()); + ASSERT(entry); + entry->attributes = attributes; + + structure->checkOffsetConsistency(); + return structure; +} + +Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind, DeferredStructureTransitionWatchpointFire* deferred) +{ + ASSERT(!structure->isUncacheableDictionary()); + + Structure* transition = create(vm, structure, deferred); + + DeferGC deferGC(vm.heap); + structure->materializePropertyMapIfNecessary(vm, deferGC); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); + transition->m_offset = structure->m_offset; + transition->setDictionaryKind(kind); + transition->pin(); + transition->setTransitionWatchpointIsLikelyToBeFired(true); + + transition->checkOffsetConsistency(); + return transition; +} + +Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure, DeferredStructureTransitionWatchpointFire* deferred) +{ + return toDictionaryTransition(vm, structure, CachedDictionaryKind, deferred); +} + +Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure) +{ + return toDictionaryTransition(vm, structure, UncachedDictionaryKind); +} + +// In future we may want to cache this transition. +Structure* Structure::sealTransition(VM& vm, Structure* structure) +{ + Structure* transition = preventExtensionsTransition(vm, structure); + + if (transition->propertyTable()) { + PropertyTable::iterator end = transition->propertyTable()->end(); + for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter) + iter->attributes |= DontDelete; + } + + transition->checkOffsetConsistency(); + return transition; +} + +// In future we may want to cache this transition. +Structure* Structure::freezeTransition(VM& vm, Structure* structure) +{ + Structure* transition = preventExtensionsTransition(vm, structure); + + if (transition->propertyTable()) { + PropertyTable::iterator iter = transition->propertyTable()->begin(); + PropertyTable::iterator end = transition->propertyTable()->end(); + if (iter != end) + transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); + for (; iter != end; ++iter) + iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); + } + + ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties()); + ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties()); + transition->checkOffsetConsistency(); + return transition; +} + +// In future we may want to cache this transition. +Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure) +{ + Structure* transition = create(vm, structure); + + // Don't set m_offset, as one can not transition to this. + + DeferGC deferGC(vm.heap); + structure->materializePropertyMapIfNecessary(vm, deferGC); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); + transition->m_offset = structure->m_offset; + transition->setPreventExtensions(true); + transition->pin(); + + transition->checkOffsetConsistency(); + return transition; +} + +PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm) +{ + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessaryForPinning(vm, deferGC); + + if (isPinnedPropertyTable()) + return propertyTable()->copy(vm, propertyTable()->size() + 1); + + // Hold the lock while stealing the table - so that getConcurrently() on another thread + // will either have to bypass this structure, or will get to use the property table + // before it is stolen. + ConcurrentJITLocker locker(m_lock); + PropertyTable* takenPropertyTable = propertyTable().get(); + propertyTable().clear(); + return takenPropertyTable; +} + +Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind) +{ + unsigned attributes = toAttributes(transitionKind); + IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); + + if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { + if (globalObject->isOriginalArrayStructure(structure)) { + Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); + if (result->indexingTypeIncludingHistory() == indexingType) { + structure->didTransitionFromThisStructure(); + return result; + } + } + } + + Structure* existingTransition; + if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) { + ASSERT(existingTransition->attributesInPrevious() == attributes); + ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); + return existingTransition; + } + + Structure* transition = create(vm, structure); + transition->setAttributesInPrevious(attributes); + transition->m_blob.setIndexingType(indexingType); + transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); + transition->m_offset = structure->m_offset; + checkOffset(transition->m_offset, transition->inlineCapacity()); + + if (structure->isDictionary()) + transition->pin(); + else { + ConcurrentJITLocker locker(structure->m_lock); + structure->m_transitionTable.add(vm, transition); + } + transition->checkOffsetConsistency(); + return transition; +} + +// In future we may want to cache this property. +bool Structure::isSealed(VM& vm) +{ + if (isExtensible()) + return false; + + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessary(vm, deferGC); + if (!propertyTable()) + return true; + + PropertyTable::iterator end = propertyTable()->end(); + for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { + if ((iter->attributes & DontDelete) != DontDelete) + return false; + } + return true; +} + +// In future we may want to cache this property. +bool Structure::isFrozen(VM& vm) +{ + if (isExtensible()) + return false; + + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessary(vm, deferGC); + if (!propertyTable()) + return true; + + PropertyTable::iterator end = propertyTable()->end(); + for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { + if (!(iter->attributes & DontDelete)) + return false; + if (!(iter->attributes & (ReadOnly | Accessor))) + return false; + } + return true; +} + +Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object) +{ + checkOffsetConsistency(); + ASSERT(isDictionary()); + + size_t beforeOutOfLineCapacity = this->outOfLineCapacity(); + if (isUncacheableDictionary()) { + ASSERT(propertyTable()); + + size_t propertyCount = propertyTable()->size(); + + // Holds our values compacted by insertion order. + Vector<JSValue> values(propertyCount); + + // Copies out our values from their hashed locations, compacting property table offsets as we go. + unsigned i = 0; + PropertyTable::iterator end = propertyTable()->end(); + m_offset = invalidOffset; + for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter, ++i) { + values[i] = object->getDirect(iter->offset); + m_offset = iter->offset = offsetForPropertyNumber(i, m_inlineCapacity); + } + + // Copies in our values to their compacted locations. + for (unsigned i = 0; i < propertyCount; i++) + object->putDirect(vm, offsetForPropertyNumber(i, m_inlineCapacity), values[i]); + + propertyTable()->clearDeletedOffsets(); + checkOffsetConsistency(); + } + + setDictionaryKind(NoneDictionaryKind); + setHasBeenFlattenedBefore(true); + + size_t afterOutOfLineCapacity = this->outOfLineCapacity(); + + if (beforeOutOfLineCapacity != afterOutOfLineCapacity) { + ASSERT(beforeOutOfLineCapacity > afterOutOfLineCapacity); + // If the object had a Butterfly but after flattening/compacting we no longer have need of it, + // we need to zero it out because the collector depends on the Structure to know the size for copying. + if (object->butterfly() && !afterOutOfLineCapacity && !this->hasIndexingHeader(object)) + object->setStructureAndButterfly(vm, this, 0); + // If the object was down-sized to the point where the base of the Butterfly is no longer within the + // first CopiedBlock::blockSize bytes, we'll get the wrong answer if we try to mask the base back to + // the CopiedBlock header. To prevent this case we need to memmove the Butterfly down. + else if (object->butterfly()) + object->shiftButterflyAfterFlattening(vm, beforeOutOfLineCapacity, afterOutOfLineCapacity); + } + + return this; +} + +PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes) +{ + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessaryForPinning(vm, deferGC); + + pin(); + + return add(vm, propertyName, attributes); +} + +PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName) +{ + ASSERT(isUncacheableDictionary()); + + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessaryForPinning(vm, deferGC); + + pin(); + return remove(propertyName); +} + +void Structure::pin() +{ + ASSERT(propertyTable()); + setIsPinnedPropertyTable(true); + clearPreviousID(); + m_nameInPrevious = nullptr; +} + +void Structure::allocateRareData(VM& vm) +{ + ASSERT(!hasRareData()); + StructureRareData* rareData = StructureRareData::create(vm, previous()); + WTF::storeStoreFence(); + m_previousOrRareData.set(vm, this, rareData); + WTF::storeStoreFence(); + setHasRareData(true); + ASSERT(hasRareData()); +} + +WatchpointSet* Structure::ensurePropertyReplacementWatchpointSet(VM& vm, PropertyOffset offset) +{ + ASSERT(!isUncacheableDictionary()); + + // In some places it's convenient to call this with an invalid offset. So, we do the check here. + if (!isValidOffset(offset)) + return nullptr; + + if (!hasRareData()) + allocateRareData(vm); + ConcurrentJITLocker locker(m_lock); + StructureRareData* rareData = this->rareData(); + if (!rareData->m_replacementWatchpointSets) { + rareData->m_replacementWatchpointSets = + std::make_unique<StructureRareData::PropertyWatchpointMap>(); + WTF::storeStoreFence(); + } + auto result = rareData->m_replacementWatchpointSets->add(offset, nullptr); + if (result.isNewEntry) + result.iterator->value = adoptRef(new WatchpointSet(IsWatched)); + return result.iterator->value.get(); +} + +void Structure::startWatchingPropertyForReplacements(VM& vm, PropertyName propertyName) +{ + ASSERT(!isUncacheableDictionary()); + + startWatchingPropertyForReplacements(vm, get(vm, propertyName)); +} + +void Structure::didCachePropertyReplacement(VM& vm, PropertyOffset offset) +{ + ensurePropertyReplacementWatchpointSet(vm, offset)->fireAll("Did cache property replacement"); +} + +void Structure::startWatchingInternalProperties(VM& vm) +{ + if (!isUncacheableDictionary()) { + startWatchingPropertyForReplacements(vm, vm.propertyNames->toString); + startWatchingPropertyForReplacements(vm, vm.propertyNames->valueOf); + } + setDidWatchInternalProperties(true); +} + +#if DUMP_PROPERTYMAP_STATS + +PropertyMapHashTableStats* propertyMapHashTableStats = 0; + +struct PropertyMapStatisticsExitLogger { + PropertyMapStatisticsExitLogger(); + ~PropertyMapStatisticsExitLogger(); +}; + +DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger, logger, ); + +PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger() +{ + propertyMapHashTableStats = adoptPtr(new PropertyMapHashTableStats()).leakPtr(); +} + +PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() +{ + unsigned finds = propertyMapHashTableStats->numFinds; + unsigned collisions = propertyMapHashTableStats->numCollisions; + dataLogF("\nJSC::PropertyMap statistics for process %d\n\n", getCurrentProcessID()); + dataLogF("%d finds\n", finds); + dataLogF("%d collisions (%.1f%%)\n", collisions, 100.0 * collisions / finds); + dataLogF("%d lookups\n", propertyMapHashTableStats->numLookups.load()); + dataLogF("%d lookup probings\n", propertyMapHashTableStats->numLookupProbing.load()); + dataLogF("%d adds\n", propertyMapHashTableStats->numAdds.load()); + dataLogF("%d removes\n", propertyMapHashTableStats->numRemoves.load()); + dataLogF("%d rehashes\n", propertyMapHashTableStats->numRehashes.load()); + dataLogF("%d reinserts\n", propertyMapHashTableStats->numReinserts.load()); +} + +#endif + +PropertyTable* Structure::copyPropertyTable(VM& vm) +{ + if (!propertyTable()) + return 0; + return PropertyTable::clone(vm, *propertyTable().get()); +} + +PropertyTable* Structure::copyPropertyTableForPinning(VM& vm) +{ + if (propertyTable()) + return PropertyTable::clone(vm, *propertyTable().get()); + return PropertyTable::create(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); +} + +PropertyOffset Structure::getConcurrently(UniquedStringImpl* uid, unsigned& attributes) +{ + PropertyOffset result = invalidOffset; + + forEachPropertyConcurrently( + [&] (const PropertyMapEntry& candidate) -> bool { + if (candidate.key != uid) + return true; + + result = candidate.offset; + attributes = candidate.attributes; + return false; + }); + + return result; +} + +Vector<PropertyMapEntry> Structure::getPropertiesConcurrently() +{ + Vector<PropertyMapEntry> result; + + forEachPropertyConcurrently( + [&] (const PropertyMapEntry& entry) -> bool { + result.append(entry); + return true; + }); + + return result; +} + +PropertyOffset Structure::add(VM& vm, PropertyName propertyName, unsigned attributes) +{ + GCSafeConcurrentJITLocker locker(m_lock, vm.heap); + + ASSERT(!JSC::isValidOffset(get(vm, propertyName))); + + checkConsistency(); + if (attributes & DontEnum) + setHasNonEnumerableProperties(true); + + auto rep = propertyName.uid(); + + if (!propertyTable()) + createPropertyMap(locker, vm); + + PropertyOffset newOffset = propertyTable()->nextOffset(m_inlineCapacity); + + propertyTable()->add(PropertyMapEntry(rep, newOffset, attributes), m_offset, PropertyTable::PropertyOffsetMayChange); + + checkConsistency(); + return newOffset; +} + +PropertyOffset Structure::remove(PropertyName propertyName) +{ + ConcurrentJITLocker locker(m_lock); + + checkConsistency(); + + auto rep = propertyName.uid(); + + if (!propertyTable()) + return invalidOffset; + + PropertyTable::find_iterator position = propertyTable()->find(rep); + if (!position.first) + return invalidOffset; + + PropertyOffset offset = position.first->offset; + + propertyTable()->remove(position); + propertyTable()->addDeletedOffset(offset); + + checkConsistency(); + return offset; +} + +void Structure::createPropertyMap(const GCSafeConcurrentJITLocker&, VM& vm, unsigned capacity) +{ + ASSERT(!propertyTable()); + + checkConsistency(); + propertyTable().set(vm, this, PropertyTable::create(vm, capacity)); +} + +void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessary(vm, deferGC); + if (!propertyTable()) + return; + + bool knownUnique = propertyNames.canAddKnownUniqueForStructure(); + + PropertyTable::iterator end = propertyTable()->end(); + for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { + ASSERT(hasNonEnumerableProperties() || !(iter->attributes & DontEnum)); + if (!(iter->attributes & DontEnum) || mode.includeDontEnumProperties()) { + if (iter->key->isSymbol() && !propertyNames.includeSymbolProperties()) + continue; + if (knownUnique) + propertyNames.addKnownUnique(iter->key); + else + propertyNames.add(iter->key); + } + } +} + +void StructureFireDetail::dump(PrintStream& out) const +{ + out.print("Structure transition from ", *m_structure); +} + +DeferredStructureTransitionWatchpointFire::DeferredStructureTransitionWatchpointFire() + : m_structure(nullptr) +{ +} + +DeferredStructureTransitionWatchpointFire::~DeferredStructureTransitionWatchpointFire() +{ + if (m_structure) + m_structure->transitionWatchpointSet().fireAll(StructureFireDetail(m_structure)); +} + +void DeferredStructureTransitionWatchpointFire::add(const Structure* structure) +{ + RELEASE_ASSERT(!m_structure); + RELEASE_ASSERT(structure); + m_structure = structure; +} + +void Structure::didTransitionFromThisStructure(DeferredStructureTransitionWatchpointFire* deferred) const +{ + // If the structure is being watched, and this is the kind of structure that the DFG would + // like to watch, then make sure to note for all future versions of this structure that it's + // unwise to watch it. + if (m_transitionWatchpointSet.isBeingWatched()) + const_cast<Structure*>(this)->setTransitionWatchpointIsLikelyToBeFired(true); + + if (deferred) + deferred->add(this); + else + m_transitionWatchpointSet.fireAll(StructureFireDetail(this)); +} + +JSValue Structure::prototypeForLookup(CodeBlock* codeBlock) const +{ + return prototypeForLookup(codeBlock->globalObject()); +} + +void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Structure* thisObject = jsCast<Structure*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + JSCell::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_globalObject); + if (!thisObject->isObject()) + thisObject->m_cachedPrototypeChain.clear(); + else { + visitor.append(&thisObject->m_prototype); + visitor.append(&thisObject->m_cachedPrototypeChain); + } + visitor.append(&thisObject->m_previousOrRareData); + + if (thisObject->isPinnedPropertyTable()) { + ASSERT(thisObject->m_propertyTableUnsafe); + visitor.append(&thisObject->m_propertyTableUnsafe); + } else if (thisObject->m_propertyTableUnsafe) + thisObject->m_propertyTableUnsafe.clear(); +} + +bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName) +{ + if (parseIndex(propertyName)) + return anyObjectInChainMayInterceptIndexedAccesses(); + + for (Structure* current = this; ;) { + JSValue prototype = current->storedPrototype(); + if (prototype.isNull()) + return false; + + current = prototype.asCell()->structure(vm); + + unsigned attributes; + PropertyOffset offset = current->get(vm, propertyName, attributes); + if (!JSC::isValidOffset(offset)) + continue; + + if (attributes & (ReadOnly | Accessor)) + return true; + + return false; + } +} + +PassRefPtr<StructureShape> Structure::toStructureShape(JSValue value) +{ + RefPtr<StructureShape> baseShape = StructureShape::create(); + RefPtr<StructureShape> curShape = baseShape; + Structure* curStructure = this; + JSValue curValue = value; + while (curStructure) { + Vector<Structure*, 8> structures; + Structure* structure; + PropertyTable* table; + + curStructure->findStructuresAndMapForMaterialization(structures, structure, table); + if (table) { + PropertyTable::iterator iter = table->begin(); + PropertyTable::iterator end = table->end(); + for (; iter != end; ++iter) + curShape->addProperty(*iter->key); + + structure->m_lock.unlock(); + } + for (unsigned i = structures.size(); i--;) { + Structure* structure = structures[i]; + if (structure->m_nameInPrevious) + curShape->addProperty(*structure->m_nameInPrevious); + } + + if (JSObject* curObject = curValue.getObject()) + curShape->setConstructorName(JSObject::calculatedClassName(curObject)); + else + curShape->setConstructorName(curStructure->classInfo()->className); + + if (curStructure->isDictionary()) + curShape->enterDictionaryMode(); + + curShape->markAsFinal(); + + if (curStructure->storedPrototypeStructure()) { + RefPtr<StructureShape> newShape = StructureShape::create(); + curShape->setProto(newShape); + curShape = newShape.release(); + curValue = curStructure->storedPrototype(); + } + + curStructure = curStructure->storedPrototypeStructure(); + } + + return baseShape.release(); +} + +bool Structure::canUseForAllocationsOf(Structure* other) +{ + return inlineCapacity() == other->inlineCapacity() + && storedPrototype() == other->storedPrototype() + && objectInitializationBlob() == other->objectInitializationBlob(); +} + +void Structure::dump(PrintStream& out) const +{ + out.print(RawPointer(this), ":[", classInfo()->className, ", {"); + + CommaPrinter comma; + + const_cast<Structure*>(this)->forEachPropertyConcurrently( + [&] (const PropertyMapEntry& entry) -> bool { + out.print(comma, entry.key, ":", static_cast<int>(entry.offset)); + return true; + }); + + out.print("}, ", IndexingTypeDump(indexingType())); + + if (m_prototype.get().isCell()) + out.print(", Proto:", RawPointer(m_prototype.get().asCell())); + + switch (dictionaryKind()) { + case NoneDictionaryKind: + break; + case CachedDictionaryKind: + out.print(", Dictionary"); + break; + case UncachedDictionaryKind: + out.print(", UncacheableDictionary"); + break; + } + + out.print("]"); +} + +void Structure::dumpInContext(PrintStream& out, DumpContext* context) const +{ + if (context) + context->structures.dumpBrief(this, out); + else + dump(out); +} + +void Structure::dumpBrief(PrintStream& out, const CString& string) const +{ + out.print("%", string, ":", classInfo()->className); +} + +void Structure::dumpContextHeader(PrintStream& out) +{ + out.print("Structures:"); +} + +#if DO_PROPERTYMAP_CONSTENCY_CHECK + +void PropertyTable::checkConsistency() +{ + ASSERT(m_indexSize >= PropertyTable::MinimumTableSize); + ASSERT(m_indexMask); + ASSERT(m_indexSize == m_indexMask + 1); + ASSERT(!(m_indexSize & m_indexMask)); + + ASSERT(m_keyCount <= m_indexSize / 2); + ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2); + ASSERT(m_deletedCount <= m_indexSize / 4); + + unsigned indexCount = 0; + unsigned deletedIndexCount = 0; + for (unsigned a = 0; a != m_indexSize; ++a) { + unsigned entryIndex = m_index[a]; + if (entryIndex == PropertyTable::EmptyEntryIndex) + continue; + if (entryIndex == deletedEntryIndex()) { + ++deletedIndexCount; + continue; + } + ASSERT(entryIndex < deletedEntryIndex()); + ASSERT(entryIndex - 1 <= usedCount()); + ++indexCount; + + for (unsigned b = a + 1; b != m_indexSize; ++b) + ASSERT(m_index[b] != entryIndex); + } + ASSERT(indexCount == m_keyCount); + ASSERT(deletedIndexCount == m_deletedCount); + + ASSERT(!table()[deletedEntryIndex() - 1].key); + + unsigned nonEmptyEntryCount = 0; + for (unsigned c = 0; c < usedCount(); ++c) { + StringImpl* rep = table()[c].key; + if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY) + continue; + ++nonEmptyEntryCount; + unsigned i = IdentifierRepHash::hash(rep); + unsigned k = 0; + unsigned entryIndex; + while (1) { + entryIndex = m_index[i & m_indexMask]; + ASSERT(entryIndex != PropertyTable::EmptyEntryIndex); + if (rep == table()[entryIndex - 1].key) + break; + if (k == 0) + k = 1 | doubleHash(IdentifierRepHash::hash(rep)); + i += k; + } + ASSERT(entryIndex == c + 1); + } + + ASSERT(nonEmptyEntryCount == m_keyCount); +} + +void Structure::checkConsistency() +{ + checkOffsetConsistency(); + + if (!propertyTable()) + return; + + if (!hasNonEnumerableProperties()) { + PropertyTable::iterator end = propertyTable()->end(); + for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { + ASSERT(!(iter->attributes & DontEnum)); + } + } + + propertyTable()->checkConsistency(); +} + +#else + +inline void Structure::checkConsistency() +{ + checkOffsetConsistency(); +} + +#endif // DO_PROPERTYMAP_CONSTENCY_CHECK + +bool ClassInfo::hasStaticSetterOrReadonlyProperties() const +{ + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (const HashTable* table = ci->staticPropHashTable) { + if (table->hasSetterOrReadonlyProperties) + return true; + } + } + return false; +} + +void Structure::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator) +{ + ASSERT(!isDictionary()); + if (!hasRareData()) + allocateRareData(vm); + rareData()->setCachedPropertyNameEnumerator(vm, enumerator); +} + +JSPropertyNameEnumerator* Structure::cachedPropertyNameEnumerator() const +{ + if (!hasRareData()) + return nullptr; + return rareData()->cachedPropertyNameEnumerator(); +} + +bool Structure::canCachePropertyNameEnumerator() const +{ + if (isDictionary()) + return false; + + if (hasIndexedProperties(indexingType())) + return false; + + if (typeInfo().overridesGetPropertyNames()) + return false; + + StructureChain* structureChain = m_cachedPrototypeChain.get(); + ASSERT(structureChain); + WriteBarrier<Structure>* structure = structureChain->head(); + while (true) { + if (!structure->get()) + break; + if (structure->get()->typeInfo().overridesGetPropertyNames()) + return false; + structure++; + } + + return true; +} + +bool Structure::canAccessPropertiesQuickly() const +{ + if (hasNonEnumerableProperties()) + return false; + if (hasGetterSetterProperties()) + return false; + if (isUncacheableDictionary()) + return false; + return true; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h new file mode 100644 index 000000000..22838c70d --- /dev/null +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -0,0 +1,668 @@ +/* + * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Structure_h +#define Structure_h + +#include "ClassInfo.h" +#include "ConcurrentJITLock.h" +#include "IndexingType.h" +#include "JSCJSValue.h" +#include "JSCell.h" +#include "JSType.h" +#include "PropertyName.h" +#include "PropertyNameArray.h" +#include "PropertyOffset.h" +#include "Protect.h" +#include "PutPropertySlot.h" +#include "StructureIDBlob.h" +#include "StructureRareData.h" +#include "StructureTransitionTable.h" +#include "JSTypeInfo.h" +#include "Watchpoint.h" +#include "Weak.h" +#include "WriteBarrierInlines.h" +#include <wtf/CompilationThread.h> +#include <wtf/PassRefPtr.h> +#include <wtf/PrintStream.h> +#include <wtf/RefCounted.h> + +namespace WTF { + +class UniquedStringImpl; + +} // namespace WTF + +namespace JSC { + +class DeferGC; +class LLIntOffsetsExtractor; +class PropertyNameArray; +class PropertyNameArrayData; +class PropertyTable; +class StructureChain; +class StructureShape; +class SlotVisitor; +class JSString; +struct DumpContext; + +// The out-of-line property storage capacity to use when first allocating out-of-line +// storage. Note that all objects start out without having any out-of-line storage; +// this comes into play only on the first property store that exhausts inline storage. +static const unsigned initialOutOfLineCapacity = 4; + +// The factor by which to grow out-of-line storage when it is exhausted, after the +// initial allocation. +static const unsigned outOfLineGrowthFactor = 2; + +struct PropertyMapEntry { + UniquedStringImpl* key; + PropertyOffset offset; + unsigned attributes; + + PropertyMapEntry() + : key(nullptr) + , offset(invalidOffset) + , attributes(0) + { + } + + PropertyMapEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes) + : key(key) + , offset(offset) + , attributes(attributes) + { + } +}; + +class StructureFireDetail : public FireDetail { +public: + StructureFireDetail(const Structure* structure) + : m_structure(structure) + { + } + + virtual void dump(PrintStream& out) const override; + +private: + const Structure* m_structure; +}; + +class DeferredStructureTransitionWatchpointFire { + WTF_MAKE_NONCOPYABLE(DeferredStructureTransitionWatchpointFire); +public: + JS_EXPORT_PRIVATE DeferredStructureTransitionWatchpointFire(); + JS_EXPORT_PRIVATE ~DeferredStructureTransitionWatchpointFire(); + + void add(const Structure*); + +private: + const Structure* m_structure; +}; + +class Structure final : public JSCell { +public: + friend class StructureTransitionTable; + + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0); + + ~Structure(); + +protected: + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(m_prototype); + ASSERT(m_prototype.isObject() || m_prototype.isNull()); + } + + void finishCreation(VM& vm, CreatingEarlyCellTag) + { + Base::finishCreation(vm, this, CreatingEarlyCell); + ASSERT(m_prototype); + ASSERT(m_prototype.isNull()); + ASSERT(!vm.structureStructure); + } + +public: + StructureID id() const { return m_blob.structureID(); } + int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); } + int64_t idBlob() const { return m_blob.blob(); } + + bool isProxy() const + { + JSType type = m_blob.type(); + return type == ImpureProxyType || type == PureForwardingProxyType; + } + + static void dumpStatistics(); + + JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr); + static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&); + JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&); + static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&); + JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype); + JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes); + JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr); + static Structure* toUncacheableDictionaryTransition(VM&, Structure*); + JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*); + JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*); + static Structure* preventExtensionsTransition(VM&, Structure*); + JS_EXPORT_PRIVATE static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition); + + JS_EXPORT_PRIVATE bool isSealed(VM&); + JS_EXPORT_PRIVATE bool isFrozen(VM&); + bool isExtensible() const { return !preventExtensions(); } + bool putWillGrowOutOfLineStorage(); + size_t suggestedNewOutOfLineStorageCapacity(); + + JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + // These should be used with caution. + JS_EXPORT_PRIVATE PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes); + PropertyOffset removePropertyWithoutTransition(VM&, PropertyName); + void setPrototypeWithoutTransition(VM& vm, JSValue prototype) { m_prototype.set(vm, this, prototype); } + + bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind; } + bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; } + + bool propertyAccessesAreCacheable() + { + return dictionaryKind() != UncachedDictionaryKind + && !typeInfo().prohibitsPropertyCaching() + && !(typeInfo().hasImpureGetOwnPropertySlot() && !typeInfo().newImpurePropertyFiresWatchpoints()); + } + + bool needImpurePropertyWatchpoint() + { + return propertyAccessesAreCacheable() + && typeInfo().hasImpureGetOwnPropertySlot() + && typeInfo().newImpurePropertyFiresWatchpoints(); + } + + // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent + // DFG from inlining property accesses since structures don't transition when a new impure property appears. + bool takesSlowPathInDFGForImpureProperty() + { + return typeInfo().hasImpureGetOwnPropertySlot(); + } + + // Type accessors. + TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); } + bool isObject() const { return typeInfo().isObject(); } + + IndexingType indexingType() const { return m_blob.indexingType() & AllArrayTypes; } + IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingType(); } + + bool mayInterceptIndexedAccesses() const + { + return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors); + } + + bool anyObjectInChainMayInterceptIndexedAccesses() const; + bool holesMustForwardToPrototype(VM&) const; + + bool needsSlowPutIndexing() const; + NonPropertyTransition suggestedArrayStorageTransition() const; + + JSGlobalObject* globalObject() const { return m_globalObject.get(); } + void setGlobalObject(VM& vm, JSGlobalObject* globalObject) { m_globalObject.set(vm, this, globalObject); } + + JSValue storedPrototype() const { return m_prototype.get(); } + JSObject* storedPrototypeObject() const; + Structure* storedPrototypeStructure() const; + JSValue prototypeForLookup(ExecState*) const; + JSValue prototypeForLookup(JSGlobalObject*) const; + JSValue prototypeForLookup(CodeBlock*) const; + StructureChain* prototypeChain(VM&, JSGlobalObject*) const; + StructureChain* prototypeChain(ExecState*) const; + static void visitChildren(JSCell*, SlotVisitor&); + + // Will just the prototype chain intercept this property access? + bool prototypeChainMayInterceptStoreTo(VM&, PropertyName); + + Structure* previousID() const + { + ASSERT(structure()->classInfo() == info()); + if (hasRareData()) + return rareData()->previousID(); + return previous(); + } + bool transitivelyTransitionedFrom(Structure* structureToFind); + + unsigned outOfLineCapacity() const + { + ASSERT(checkOffsetConsistency()); + + unsigned outOfLineSize = this->outOfLineSize(); + + if (!outOfLineSize) + return 0; + + if (outOfLineSize <= initialOutOfLineCapacity) + return initialOutOfLineCapacity; + + ASSERT(outOfLineSize > initialOutOfLineCapacity); + COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two); + return WTF::roundUpToPowerOfTwo(outOfLineSize); + } + unsigned outOfLineSize() const + { + ASSERT(checkOffsetConsistency()); + ASSERT(structure()->classInfo() == info()); + + return numberOfOutOfLineSlotsForLastOffset(m_offset); + } + bool hasInlineStorage() const + { + return !!m_inlineCapacity; + } + unsigned inlineCapacity() const + { + return m_inlineCapacity; + } + unsigned inlineSize() const + { + return std::min<unsigned>(m_offset + 1, m_inlineCapacity); + } + unsigned totalStorageSize() const + { + return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity); + } + unsigned totalStorageCapacity() const + { + ASSERT(structure()->classInfo() == info()); + return outOfLineCapacity() + inlineCapacity(); + } + + bool isValidOffset(PropertyOffset offset) const + { + return JSC::isValidOffset(offset) + && offset <= m_offset + && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset); + } + + bool couldHaveIndexingHeader() const + { + return hasIndexedProperties(indexingType()) + || isTypedView(m_classInfo->typedArrayStorageType); + } + + bool hasIndexingHeader(const JSCell*) const; + + bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject); + + PropertyOffset get(VM&, PropertyName); + PropertyOffset get(VM&, PropertyName, unsigned& attributes); + + // This is a somewhat internalish method. It will call your functor while possibly holding the + // Structure's lock. There is no guarantee whether the lock is held or not in any particular + // call. So, you have to assume the worst. Also, the functor returns true if it wishes for you + // to continue or false if it's done. + template<typename Functor> + void forEachPropertyConcurrently(const Functor&); + + PropertyOffset getConcurrently(UniquedStringImpl* uid); + PropertyOffset getConcurrently(UniquedStringImpl* uid, unsigned& attributes); + + Vector<PropertyMapEntry> getPropertiesConcurrently(); + + void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__) + { + setHasGetterSetterProperties(true); + if (!is__proto__) + setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); + } + + void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); } + + void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__) + { + setHasCustomGetterSetterProperties(true); + if (!is__proto__) + setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); + } + + bool isEmpty() const + { + ASSERT(checkOffsetConsistency()); + return !JSC::isValidOffset(m_offset); + } + + void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*); + JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const; + bool canCachePropertyNameEnumerator() const; + bool canAccessPropertiesQuickly() const; + + void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode); + + JSString* objectToStringValue() + { + if (!hasRareData()) + return 0; + return rareData()->objectToStringValue(); + } + + void setObjectToStringValue(VM& vm, JSString* value) + { + if (!hasRareData()) + allocateRareData(vm); + rareData()->setObjectToStringValue(vm, value); + } + + const ClassInfo* classInfo() const { return m_classInfo; } + + static ptrdiff_t structureIDOffset() + { + return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset(); + } + + static ptrdiff_t prototypeOffset() + { + return OBJECT_OFFSETOF(Structure, m_prototype); + } + + static ptrdiff_t globalObjectOffset() + { + return OBJECT_OFFSETOF(Structure, m_globalObject); + } + + static ptrdiff_t classInfoOffset() + { + return OBJECT_OFFSETOF(Structure, m_classInfo); + } + + static ptrdiff_t indexingTypeOffset() + { + return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset(); + } + + static Structure* createStructure(VM&); + + bool transitionWatchpointSetHasBeenInvalidated() const + { + return m_transitionWatchpointSet.hasBeenInvalidated(); + } + + bool transitionWatchpointSetIsStillValid() const + { + return m_transitionWatchpointSet.isStillValid(); + } + + bool dfgShouldWatchIfPossible() const + { + // FIXME: We would like to not watch things that are unprofitable to watch, like + // dictionaries. Unfortunately, we can't do such things: a dictionary could get flattened, + // in which case it will start to appear watchable and so the DFG will think that it is + // watching it. We should come up with a comprehensive story for not watching things that + // aren't profitable to watch. + // https://bugs.webkit.org/show_bug.cgi?id=133625 + + // - We don't watch Structures that either decided not to be watched, or whose predecessors + // decided not to be watched. This happens either when a transition is fired while being + // watched, or if a dictionary transition occurs. + if (transitionWatchpointIsLikelyToBeFired()) + return false; + + return true; + } + + bool dfgShouldWatch() const + { + return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid(); + } + + void addTransitionWatchpoint(Watchpoint* watchpoint) const + { + ASSERT(transitionWatchpointSetIsStillValid()); + m_transitionWatchpointSet.add(watchpoint); + } + + void didTransitionFromThisStructure(DeferredStructureTransitionWatchpointFire* = nullptr) const; + + InlineWatchpointSet& transitionWatchpointSet() const + { + return m_transitionWatchpointSet; + } + + WatchpointSet* ensurePropertyReplacementWatchpointSet(VM&, PropertyOffset); + void startWatchingPropertyForReplacements(VM& vm, PropertyOffset offset) + { + ensurePropertyReplacementWatchpointSet(vm, offset); + } + void startWatchingPropertyForReplacements(VM&, PropertyName); + WatchpointSet* propertyReplacementWatchpointSet(PropertyOffset); + void didReplaceProperty(PropertyOffset); + void didCachePropertyReplacement(VM&, PropertyOffset); + + void startWatchingInternalPropertiesIfNecessary(VM& vm) + { + if (LIKELY(didWatchInternalProperties())) + return; + startWatchingInternalProperties(vm); + } + + void startWatchingInternalPropertiesIfNecessaryForEntireChain(VM& vm) + { + for (Structure* structure = this; structure; structure = structure->storedPrototypeStructure()) + structure->startWatchingInternalPropertiesIfNecessary(vm); + } + + PassRefPtr<StructureShape> toStructureShape(JSValue); + + // Determines if the two structures match enough that this one could be used for allocations + // of the other one. + bool canUseForAllocationsOf(Structure*); + + void dump(PrintStream&) const; + void dumpInContext(PrintStream&, DumpContext*) const; + void dumpBrief(PrintStream&, const CString&) const; + + static void dumpContextHeader(PrintStream&); + + DECLARE_EXPORT_INFO; + +private: + typedef enum { + NoneDictionaryKind = 0, + CachedDictionaryKind = 1, + UncachedDictionaryKind = 2 + } DictionaryKind; + +public: +#define DEFINE_BITFIELD(type, lowerName, upperName, width, offset) \ + static const uint32_t s_##lowerName##Shift = offset;\ + static const uint32_t s_##lowerName##Mask = ((1 << (width - 1)) | ((1 << (width - 1)) - 1));\ + type lowerName() const { return static_cast<type>((m_bitField >> offset) & s_##lowerName##Mask); }\ + void set##upperName(type newValue) \ + {\ + m_bitField &= ~(s_##lowerName##Mask << offset);\ + m_bitField |= (newValue & s_##lowerName##Mask) << offset;\ + } + + DEFINE_BITFIELD(DictionaryKind, dictionaryKind, DictionaryKind, 2, 0); + DEFINE_BITFIELD(bool, isPinnedPropertyTable, IsPinnedPropertyTable, 1, 2); + DEFINE_BITFIELD(bool, hasGetterSetterProperties, HasGetterSetterProperties, 1, 3); + DEFINE_BITFIELD(bool, hasReadOnlyOrGetterSetterPropertiesExcludingProto, HasReadOnlyOrGetterSetterPropertiesExcludingProto, 1, 4); + DEFINE_BITFIELD(bool, hasNonEnumerableProperties, HasNonEnumerableProperties, 1, 5); + DEFINE_BITFIELD(unsigned, attributesInPrevious, AttributesInPrevious, 14, 6); + DEFINE_BITFIELD(bool, preventExtensions, PreventExtensions, 1, 20); + DEFINE_BITFIELD(bool, didTransition, DidTransition, 1, 21); + DEFINE_BITFIELD(bool, staticFunctionsReified, StaticFunctionsReified, 1, 22); + DEFINE_BITFIELD(bool, hasRareData, HasRareData, 1, 23); + DEFINE_BITFIELD(bool, hasBeenFlattenedBefore, HasBeenFlattenedBefore, 1, 24); + DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties, HasCustomGetterSetterProperties, 1, 25); + DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 26); + DEFINE_BITFIELD(bool, transitionWatchpointIsLikelyToBeFired, TransitionWatchpointIsLikelyToBeFired, 1, 27); + +private: + friend class LLIntOffsetsExtractor; + + JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity); + Structure(VM&); + Structure(VM&, Structure*, DeferredStructureTransitionWatchpointFire*); + + static Structure* create(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr); + + static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&); + + // This will return the structure that has a usable property table, that property table, + // and the list of structures that we visited before we got to it. If it returns a + // non-null structure, it will also lock the structure that it returns; it is your job + // to unlock it. + void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&); + + static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind, DeferredStructureTransitionWatchpointFire* = nullptr); + + PropertyOffset add(VM&, PropertyName, unsigned attributes); + PropertyOffset remove(PropertyName); + + void createPropertyMap(const GCSafeConcurrentJITLocker&, VM&, unsigned keyCount = 0); + void checkConsistency(); + + WriteBarrier<PropertyTable>& propertyTable(); + PropertyTable* takePropertyTableOrCloneIfPinned(VM&); + PropertyTable* copyPropertyTable(VM&); + PropertyTable* copyPropertyTableForPinning(VM&); + JS_EXPORT_PRIVATE void materializePropertyMap(VM&); + ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, DeferGC&) + { + ASSERT(!isCompilationThread()); + ASSERT(structure()->classInfo() == info()); + ASSERT(checkOffsetConsistency()); + if (!propertyTable() && previousID()) + materializePropertyMap(vm); + } + ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, PropertyTable*& table) + { + ASSERT(!isCompilationThread()); + ASSERT(structure()->classInfo() == info()); + ASSERT(checkOffsetConsistency()); + table = propertyTable().get(); + if (!table && previousID()) { + DeferGC deferGC(vm.heap); + materializePropertyMap(vm); + table = propertyTable().get(); + } + } + void materializePropertyMapIfNecessaryForPinning(VM& vm, DeferGC&) + { + ASSERT(structure()->classInfo() == info()); + checkOffsetConsistency(); + if (!propertyTable()) + materializePropertyMap(vm); + } + + void setPreviousID(VM& vm, Structure* structure) + { + if (hasRareData()) + rareData()->setPreviousID(vm, structure); + else + m_previousOrRareData.set(vm, this, structure); + } + + void clearPreviousID() + { + if (hasRareData()) + rareData()->clearPreviousID(); + else + m_previousOrRareData.clear(); + } + + int transitionCount() const + { + // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both. + return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity); + } + + bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const; + bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; + + void pin(); + + Structure* previous() const + { + ASSERT(!hasRareData()); + return static_cast<Structure*>(m_previousOrRareData.get()); + } + + StructureRareData* rareData() const + { + ASSERT(hasRareData()); + return static_cast<StructureRareData*>(m_previousOrRareData.get()); + } + + bool checkOffsetConsistency() const; + + JS_EXPORT_PRIVATE void allocateRareData(VM&); + + void startWatchingInternalProperties(VM&); + + static const int s_maxTransitionLength = 64; + static const int s_maxTransitionLengthForNonEvalPutById = 512; + + // These need to be properly aligned at the beginning of the 'Structure' + // part of the object. + StructureIDBlob m_blob; + TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags; + + WriteBarrier<JSGlobalObject> m_globalObject; + WriteBarrier<Unknown> m_prototype; + mutable WriteBarrier<StructureChain> m_cachedPrototypeChain; + + WriteBarrier<JSCell> m_previousOrRareData; + + RefPtr<UniquedStringImpl> m_nameInPrevious; + + const ClassInfo* m_classInfo; + + StructureTransitionTable m_transitionTable; + + // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread. + WriteBarrier<PropertyTable> m_propertyTableUnsafe; + + mutable InlineWatchpointSet m_transitionWatchpointSet; + + COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits); + + // m_offset does not account for anonymous slots + PropertyOffset m_offset; + + uint8_t m_inlineCapacity; + + ConcurrentJITLock m_lock; + + uint32_t m_bitField; +}; + +} // namespace JSC + +#endif // Structure_h diff --git a/Source/JavaScriptCore/runtime/StructureChain.cpp b/Source/JavaScriptCore/runtime/StructureChain.cpp new file mode 100644 index 000000000..9a8568b69 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureChain.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "StructureChain.h" + +#include "JSObject.h" +#include "JSCInlines.h" +#include "Structure.h" +#include <wtf/RefPtr.h> + +namespace JSC { + +const ClassInfo StructureChain::s_info = { "StructureChain", 0, 0, CREATE_METHOD_TABLE(StructureChain) }; + +StructureChain::StructureChain(VM& vm, Structure* structure) + : JSCell(vm, structure) +{ +} + +void StructureChain::destroy(JSCell* cell) +{ + static_cast<StructureChain*>(cell)->StructureChain::~StructureChain(); +} + +void StructureChain::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + StructureChain* thisObject = jsCast<StructureChain*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + size_t i = 0; + while (thisObject->m_vector[i]) + visitor.append(&thisObject->m_vector[i++]); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StructureChain.h b/Source/JavaScriptCore/runtime/StructureChain.h new file mode 100644 index 000000000..0fbd1e207 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureChain.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StructureChain_h +#define StructureChain_h + +#include "JSCell.h" +#include "JSObject.h" +#include "Structure.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { + +class LLIntOffsetsExtractor; +class Structure; + +class StructureChain final : public JSCell { + friend class JIT; + +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static StructureChain* create(VM& vm, Structure* head) + { + StructureChain* chain = new (NotNull, allocateCell<StructureChain>(vm.heap)) StructureChain(vm, vm.structureChainStructure.get()); + chain->finishCreation(vm, head); + return chain; + } + WriteBarrier<Structure>* head() { return m_vector.get(); } + static void visitChildren(JSCell*, SlotVisitor&); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); + } + + DECLARE_INFO; + + static const bool needsDestruction = true; + static void destroy(JSCell*); + +protected: + void finishCreation(VM& vm, Structure* head) + { + Base::finishCreation(vm); + size_t size = 0; + for (Structure* current = head; current; current = current->storedPrototype().isNull() ? 0 : asObject(current->storedPrototype())->structure()) + ++size; + + m_vector = std::make_unique<WriteBarrier<Structure>[]>(size + 1); + + size_t i = 0; + for (Structure* current = head; current; current = current->storedPrototype().isNull() ? 0 : asObject(current->storedPrototype())->structure()) + m_vector[i++].set(vm, this, current); + } + +private: + friend class LLIntOffsetsExtractor; + + StructureChain(VM&, Structure*); + std::unique_ptr<WriteBarrier<Structure>[]> m_vector; +}; + +} // namespace JSC + +#endif // StructureChain_h diff --git a/Source/JavaScriptCore/runtime/StructureIDBlob.h b/Source/JavaScriptCore/runtime/StructureIDBlob.h new file mode 100644 index 000000000..d101ed95c --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureIDBlob.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef StructureIDBlob_h +#define StructureIDBlob_h + +#include "IndexingType.h" +#include "JSTypeInfo.h" +#include "StructureIDTable.h" + +namespace JSC { + +class StructureIDBlob { + friend class LLIntOffsetsExtractor; +public: + StructureIDBlob() + { + u.doubleWord = 0xbbadbeef; + } + + StructureIDBlob(StructureID structureID, IndexingType indexingType, const TypeInfo& typeInfo) + { + u.fields.structureID = structureID; + u.fields.indexingType = indexingType; + u.fields.type = typeInfo.type(); + u.fields.inlineTypeFlags = typeInfo.inlineTypeFlags(); + u.fields.defaultGCData = JSCell::NotMarked; + } + + void operator=(const StructureIDBlob& other) { u.doubleWord = other.u.doubleWord; } + + StructureID structureID() const { return u.fields.structureID; } + IndexingType indexingType() const { return u.fields.indexingType; } + void setIndexingType(IndexingType indexingType) { u.fields.indexingType = indexingType; } + JSType type() const { return u.fields.type; } + TypeInfo::InlineTypeFlags inlineTypeFlags() const { return u.fields.inlineTypeFlags; } + + TypeInfo typeInfo(TypeInfo::OutOfLineTypeFlags outOfLineTypeFlags) const { return TypeInfo(type(), inlineTypeFlags(), outOfLineTypeFlags); } + + int32_t blobExcludingStructureID() const { return u.words.word2; } + int64_t blob() const { return u.doubleWord; } + + static ptrdiff_t structureIDOffset() + { + return OBJECT_OFFSETOF(StructureIDBlob, u.fields.structureID); + } + + static ptrdiff_t indexingTypeOffset() + { + return OBJECT_OFFSETOF(StructureIDBlob, u.fields.indexingType); + } + +private: + union { + struct { + StructureID structureID; + IndexingType indexingType; + JSType type; + TypeInfo::InlineTypeFlags inlineTypeFlags; + JSCell::GCData defaultGCData; + } fields; + struct { + int32_t word1; + int32_t word2; + } words; + int64_t doubleWord; + } u; +}; + +} // namespace JSC + +#endif // StructureIDBlob_h diff --git a/Source/JavaScriptCore/runtime/StructureIDTable.cpp b/Source/JavaScriptCore/runtime/StructureIDTable.cpp new file mode 100644 index 000000000..8aa89b066 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureIDTable.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "StructureIDTable.h" + +#include <limits.h> +#include <wtf/Atomics.h> +#include <wtf/DataLog.h> + +namespace JSC { + +StructureIDTable::StructureIDTable() + : m_firstFreeOffset(0) + , m_table(std::make_unique<StructureOrOffset[]>(s_initialSize)) + , m_size(0) + , m_capacity(s_initialSize) +{ + // We pre-allocate the first offset so that the null Structure + // can still be represented as the StructureID '0'. + allocateID(0); +} + +void StructureIDTable::resize(size_t newCapacity) +{ + // Create the new table. + auto newTable = std::make_unique<StructureOrOffset[]>(newCapacity); + + // Copy the contents of the old table to the new table. + memcpy(newTable.get(), table(), m_capacity * sizeof(StructureOrOffset)); + + // Store fence to make sure we've copied everything before doing the swap. + WTF::storeStoreFence(); + + // Swap the old and new tables. + swap(m_table, newTable); + + // Put the old table (now labeled as new) into the list of old tables. + m_oldTables.append(WTF::move(newTable)); + + // Update the capacity. + m_capacity = newCapacity; +} + +void StructureIDTable::flushOldTables() +{ + m_oldTables.clear(); +} + +StructureID StructureIDTable::allocateID(Structure* structure) +{ +#if USE(JSVALUE64) + if (!m_firstFreeOffset) { + RELEASE_ASSERT(m_capacity <= UINT_MAX); + if (m_size == m_capacity) + resize(m_capacity * 2); + ASSERT(m_size < m_capacity); + + StructureOrOffset newEntry; + newEntry.structure = structure; + + if (m_size == s_unusedID) { + m_size++; + return allocateID(structure); + } + + StructureID result = m_size; + table()[result] = newEntry; + m_size++; + return result; + } + + ASSERT(m_firstFreeOffset != s_unusedID); + + StructureID result = m_firstFreeOffset; + m_firstFreeOffset = table()[m_firstFreeOffset].offset; + table()[result].structure = structure; + return result; +#else + return structure; +#endif +} + +void StructureIDTable::deallocateID(Structure* structure, StructureID structureID) +{ +#if USE(JSVALUE64) + ASSERT(structureID != s_unusedID); + RELEASE_ASSERT(table()[structureID].structure == structure); + table()[structureID].offset = m_firstFreeOffset; + m_firstFreeOffset = structureID; +#else + UNUSED_PARAM(structure); + UNUSED_PARAM(structureID); +#endif +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StructureIDTable.h b/Source/JavaScriptCore/runtime/StructureIDTable.h new file mode 100644 index 000000000..630333f0c --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureIDTable.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef StructureIDTable_h +#define StructureIDTable_h + +#include "UnusedPointer.h" +#include <wtf/Vector.h> + +namespace JSC { + +class Structure; + +#if USE(JSVALUE64) +typedef uint32_t StructureID; +#else +typedef Structure* StructureID; +#endif + +class StructureIDTable { + friend class LLIntOffsetsExtractor; +public: + StructureIDTable(); + + void** base() { return reinterpret_cast<void**>(&m_table); } + + Structure* get(StructureID); + void deallocateID(Structure*, StructureID); + StructureID allocateID(Structure*); + + void flushOldTables(); + +private: + void resize(size_t newCapacity); + + union StructureOrOffset { + WTF_MAKE_FAST_ALLOCATED; + public: + Structure* structure; + StructureID offset; + }; + + StructureOrOffset* table() const { return m_table.get(); } + + static const size_t s_initialSize = 256; + + Vector<std::unique_ptr<StructureOrOffset[]>> m_oldTables; + + uint32_t m_firstFreeOffset; + std::unique_ptr<StructureOrOffset[]> m_table; + + size_t m_size; + size_t m_capacity; + +#if USE(JSVALUE64) + static const StructureID s_unusedID = unusedPointer; +#endif +}; + +inline Structure* StructureIDTable::get(StructureID structureID) +{ +#if USE(JSVALUE64) + ASSERT_WITH_SECURITY_IMPLICATION(structureID && structureID < m_capacity); + return table()[structureID].structure; +#else + return structureID; +#endif +} + +} // namespace JSC + +#endif // StructureIDTable_h diff --git a/Source/JavaScriptCore/runtime/StructureInlines.h b/Source/JavaScriptCore/runtime/StructureInlines.h new file mode 100644 index 000000000..f1955f656 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureInlines.h @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StructureInlines_h +#define StructureInlines_h + +#include "JSArrayBufferView.h" +#include "PropertyMapHashTable.h" +#include "Structure.h" + +namespace JSC { + +inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity) +{ + ASSERT(vm.structureStructure); + ASSERT(classInfo); + Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity); + structure->finishCreation(vm); + return structure; +} + +inline Structure* Structure::createStructure(VM& vm) +{ + ASSERT(!vm.structureStructure); + Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm); + structure->finishCreation(vm, CreatingEarlyCell); + return structure; +} + +inline Structure* Structure::create(VM& vm, Structure* structure, DeferredStructureTransitionWatchpointFire* deferred) +{ + ASSERT(vm.structureStructure); + Structure* newStructure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, structure, deferred); + newStructure->finishCreation(vm); + return newStructure; +} + +inline JSObject* Structure::storedPrototypeObject() const +{ + JSValue value = m_prototype.get(); + if (value.isNull()) + return nullptr; + return asObject(value); +} + +inline Structure* Structure::storedPrototypeStructure() const +{ + JSObject* object = storedPrototypeObject(); + if (!object) + return nullptr; + return object->structure(); +} + +ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName) +{ + ASSERT(!isCompilationThread()); + ASSERT(structure()->classInfo() == info()); + PropertyTable* propertyTable; + materializePropertyMapIfNecessary(vm, propertyTable); + if (!propertyTable) + return invalidOffset; + + PropertyMapEntry* entry = propertyTable->get(propertyName.uid()); + return entry ? entry->offset : invalidOffset; +} + +ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes) +{ + ASSERT(!isCompilationThread()); + ASSERT(structure()->classInfo() == info()); + + PropertyTable* propertyTable; + materializePropertyMapIfNecessary(vm, propertyTable); + if (!propertyTable) + return invalidOffset; + + PropertyMapEntry* entry = propertyTable->get(propertyName.uid()); + if (!entry) + return invalidOffset; + + attributes = entry->attributes; + return entry->offset; +} + +template<typename Functor> +void Structure::forEachPropertyConcurrently(const Functor& functor) +{ + Vector<Structure*, 8> structures; + Structure* structure; + PropertyTable* table; + + findStructuresAndMapForMaterialization(structures, structure, table); + + if (table) { + for (auto& entry : *table) { + if (!functor(entry)) { + structure->m_lock.unlock(); + return; + } + } + structure->m_lock.unlock(); + } + + for (unsigned i = structures.size(); i--;) { + structure = structures[i]; + if (!structure->m_nameInPrevious) + continue; + + if (!functor(PropertyMapEntry(structure->m_nameInPrevious.get(), structure->m_offset, structure->attributesInPrevious()))) + return; + } +} + +inline PropertyOffset Structure::getConcurrently(UniquedStringImpl* uid) +{ + unsigned attributesIgnored; + return getConcurrently(uid, attributesIgnored); +} + +inline bool Structure::hasIndexingHeader(const JSCell* cell) const +{ + if (hasIndexedProperties(indexingType())) + return true; + + if (!isTypedView(m_classInfo->typedArrayStorageType)) + return false; + + return jsCast<const JSArrayBufferView*>(cell)->mode() == WastefulTypedArray; +} + +inline bool Structure::masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject) +{ + return typeInfo().masqueradesAsUndefined() && globalObject() == lexicalGlobalObject; +} + +inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind) +{ + for (Structure* current = this; current; current = current->previousID()) { + if (current == structureToFind) + return true; + } + return false; +} + +inline JSValue Structure::prototypeForLookup(JSGlobalObject* globalObject) const +{ + if (isObject()) + return m_prototype.get(); + if (typeInfo().type() == SymbolType) + return globalObject->symbolPrototype(); + + ASSERT(typeInfo().type() == StringType); + return globalObject->stringPrototype(); +} + +inline JSValue Structure::prototypeForLookup(ExecState* exec) const +{ + return prototypeForLookup(exec->lexicalGlobalObject()); +} + +inline StructureChain* Structure::prototypeChain(VM& vm, JSGlobalObject* globalObject) const +{ + // We cache our prototype chain so our clients can share it. + if (!isValid(globalObject, m_cachedPrototypeChain.get())) { + JSValue prototype = prototypeForLookup(globalObject); + m_cachedPrototypeChain.set(vm, this, StructureChain::create(vm, prototype.isNull() ? 0 : asObject(prototype)->structure())); + } + return m_cachedPrototypeChain.get(); +} + +inline StructureChain* Structure::prototypeChain(ExecState* exec) const +{ + return prototypeChain(exec->vm(), exec->lexicalGlobalObject()); +} + +inline bool Structure::isValid(JSGlobalObject* globalObject, StructureChain* cachedPrototypeChain) const +{ + if (!cachedPrototypeChain) + return false; + + JSValue prototype = prototypeForLookup(globalObject); + WriteBarrier<Structure>* cachedStructure = cachedPrototypeChain->head(); + while (*cachedStructure && !prototype.isNull()) { + if (asObject(prototype)->structure() != cachedStructure->get()) + return false; + ++cachedStructure; + prototype = asObject(prototype)->prototype(); + } + return prototype.isNull() && !*cachedStructure; +} + +inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const +{ + return isValid(exec->lexicalGlobalObject(), cachedPrototypeChain); +} + +inline bool Structure::putWillGrowOutOfLineStorage() +{ + checkOffsetConsistency(); + + ASSERT(outOfLineCapacity() >= outOfLineSize()); + + if (!propertyTable()) { + unsigned currentSize = numberOfOutOfLineSlotsForLastOffset(m_offset); + ASSERT(outOfLineCapacity() >= currentSize); + return currentSize == outOfLineCapacity(); + } + + ASSERT(totalStorageCapacity() >= propertyTable()->propertyStorageSize()); + if (propertyTable()->hasDeletedOffset()) + return false; + + ASSERT(totalStorageCapacity() >= propertyTable()->size()); + return propertyTable()->size() == totalStorageCapacity(); +} + +ALWAYS_INLINE WriteBarrier<PropertyTable>& Structure::propertyTable() +{ + ASSERT(!globalObject() || !globalObject()->vm().heap.isCollecting()); + return m_propertyTableUnsafe; +} + +inline void Structure::didReplaceProperty(PropertyOffset offset) +{ + if (LIKELY(!hasRareData())) + return; + StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get(); + if (LIKELY(!map)) + return; + WatchpointSet* set = map->get(offset); + if (LIKELY(!set)) + return; + set->fireAll("Property did get replaced"); +} + +inline WatchpointSet* Structure::propertyReplacementWatchpointSet(PropertyOffset offset) +{ + ConcurrentJITLocker locker(m_lock); + if (!hasRareData()) + return nullptr; + WTF::loadLoadFence(); + StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get(); + if (!map) + return nullptr; + return map->get(offset); +} + +ALWAYS_INLINE bool Structure::checkOffsetConsistency() const +{ + PropertyTable* propertyTable = m_propertyTableUnsafe.get(); + + if (!propertyTable) { + ASSERT(!isPinnedPropertyTable()); + return true; + } + + // We cannot reliably assert things about the property table in the concurrent + // compilation thread. It is possible for the table to be stolen and then have + // things added to it, which leads to the offsets being all messed up. We could + // get around this by grabbing a lock here, but I think that would be overkill. + if (isCompilationThread()) + return true; + + RELEASE_ASSERT(numberOfSlotsForLastOffset(m_offset, m_inlineCapacity) == propertyTable->propertyStorageSize()); + unsigned totalSize = propertyTable->propertyStorageSize(); + RELEASE_ASSERT((totalSize < inlineCapacity() ? 0 : totalSize - inlineCapacity()) == numberOfOutOfLineSlotsForLastOffset(m_offset)); + + return true; +} + +inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity) +{ + if (!currentCapacity) + return initialOutOfLineCapacity; + return currentCapacity * outOfLineGrowthFactor; +} + +inline size_t Structure::suggestedNewOutOfLineStorageCapacity() +{ + return nextOutOfLineStorageCapacity(outOfLineCapacity()); +} + +} // namespace JSC + +#endif // StructureInlines_h + diff --git a/Source/JavaScriptCore/runtime/StructureRareData.cpp b/Source/JavaScriptCore/runtime/StructureRareData.cpp new file mode 100644 index 000000000..20a5371e7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureRareData.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "StructureRareData.h" + +#include "JSPropertyNameEnumerator.h" +#include "JSString.h" +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo StructureRareData::s_info = { "StructureRareData", 0, 0, CREATE_METHOD_TABLE(StructureRareData) }; + +Structure* StructureRareData::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); +} + +StructureRareData* StructureRareData::create(VM& vm, Structure* previous) +{ + StructureRareData* rareData = new (NotNull, allocateCell<StructureRareData>(vm.heap)) StructureRareData(vm, previous); + rareData->finishCreation(vm); + return rareData; +} + +void StructureRareData::destroy(JSCell* cell) +{ + static_cast<StructureRareData*>(cell)->StructureRareData::~StructureRareData(); +} + +StructureRareData::StructureRareData(VM& vm, Structure* previous) + : JSCell(vm, vm.structureRareDataStructure.get()) +{ + if (previous) + m_previous.set(vm, this, previous); +} + +void StructureRareData::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + StructureRareData* thisObject = jsCast<StructureRareData*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + JSCell::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_previous); + visitor.append(&thisObject->m_objectToStringValue); + visitor.append(&thisObject->m_cachedPropertyNameEnumerator); + visitor.append(&thisObject->m_cachedGenericPropertyNameEnumerator); +} + +JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const +{ + return m_cachedPropertyNameEnumerator.get(); +} + +void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator) +{ + m_cachedPropertyNameEnumerator.set(vm, this, enumerator); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/StructureRareData.h b/Source/JavaScriptCore/runtime/StructureRareData.h new file mode 100644 index 000000000..2b11ab833 --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureRareData.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef StructureRareData_h +#define StructureRareData_h + +#include "ClassInfo.h" +#include "JSCell.h" +#include "JSTypeInfo.h" +#include "PropertyOffset.h" + +namespace JSC { + +class JSPropertyNameEnumerator; +class Structure; + +class StructureRareData final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static StructureRareData* create(VM&, Structure*); + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + static void visitChildren(JSCell*, SlotVisitor&); + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); + + Structure* previousID() const; + void setPreviousID(VM&, Structure*); + void clearPreviousID(); + + JSString* objectToStringValue() const; + void setObjectToStringValue(VM&, JSString* value); + + JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const; + void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*); + + DECLARE_EXPORT_INFO; + +private: + friend class Structure; + + StructureRareData(VM&, Structure*); + + WriteBarrier<Structure> m_previous; + WriteBarrier<JSString> m_objectToStringValue; + WriteBarrier<JSPropertyNameEnumerator> m_cachedPropertyNameEnumerator; + WriteBarrier<JSPropertyNameEnumerator> m_cachedGenericPropertyNameEnumerator; + + typedef HashMap<PropertyOffset, RefPtr<WatchpointSet>, WTF::IntHash<PropertyOffset>, WTF::UnsignedWithZeroKeyHashTraits<PropertyOffset>> PropertyWatchpointMap; + std::unique_ptr<PropertyWatchpointMap> m_replacementWatchpointSets; +}; + +} // namespace JSC + +#endif // StructureRareData_h diff --git a/Source/JavaScriptCore/runtime/StructureRareDataInlines.h b/Source/JavaScriptCore/runtime/StructureRareDataInlines.h new file mode 100644 index 000000000..99ac3c0ab --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureRareDataInlines.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef StructureRareDataInlines_h +#define StructureRareDataInlines_h + +#include "StructureRareData.h" + +namespace JSC { + +inline Structure* StructureRareData::previousID() const +{ + return m_previous.get(); +} + +inline void StructureRareData::setPreviousID(VM& vm, Structure* structure) +{ + m_previous.set(vm, this, structure); +} + +inline void StructureRareData::clearPreviousID() +{ + m_previous.clear(); +} + +inline JSString* StructureRareData::objectToStringValue() const +{ + return m_objectToStringValue.get(); +} + +inline void StructureRareData::setObjectToStringValue(VM& vm, JSString* value) +{ + m_objectToStringValue.set(vm, this, value); +} + +} // namespace JSC + +#endif // StructureRareDataInlines_h diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h new file mode 100644 index 000000000..17690a3da --- /dev/null +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2008, 2009, 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StructureTransitionTable_h +#define StructureTransitionTable_h + +#include "IndexingType.h" +#include "WeakGCMap.h" +#include <wtf/HashFunctions.h> +#include <wtf/text/UniquedStringImpl.h> + +namespace JSC { + +class JSCell; +class Structure; + +static const unsigned FirstInternalAttribute = 1 << 6; // Use for transitions that don't have to do with property additions. + +// Support for attributes used to indicate transitions not related to properties. +// If any of these are used, the string portion of the key should be 0. +enum NonPropertyTransition { + AllocateUndecided, + AllocateInt32, + AllocateDouble, + AllocateContiguous, + AllocateArrayStorage, + AllocateSlowPutArrayStorage, + SwitchToSlowPutArrayStorage, + AddIndexedAccessors +}; + +inline unsigned toAttributes(NonPropertyTransition transition) +{ + return transition + FirstInternalAttribute; +} + +inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition) +{ + switch (transition) { + case AllocateUndecided: + ASSERT(!hasIndexedProperties(oldType)); + return oldType | UndecidedShape; + case AllocateInt32: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType)); + return (oldType & ~IndexingShapeMask) | Int32Shape; + case AllocateDouble: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType)); + return (oldType & ~IndexingShapeMask) | DoubleShape; + case AllocateContiguous: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType)); + return (oldType & ~IndexingShapeMask) | ContiguousShape; + case AllocateArrayStorage: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType)); + return (oldType & ~IndexingShapeMask) | ArrayStorageShape; + case AllocateSlowPutArrayStorage: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType) || hasContiguous(oldType)); + return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape; + case SwitchToSlowPutArrayStorage: + ASSERT(hasArrayStorage(oldType)); + return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape; + case AddIndexedAccessors: + return oldType | MayHaveIndexedAccessors; + default: + RELEASE_ASSERT_NOT_REACHED(); + return oldType; + } +} + +class StructureTransitionTable { + static const intptr_t UsingSingleSlotFlag = 1; + + + struct Hash { + typedef std::pair<UniquedStringImpl*, unsigned> Key; + + static unsigned hash(const Key& p) + { + return PtrHash<UniquedStringImpl*>::hash(p.first) + p.second; + } + + static bool equal(const Key& a, const Key& b) + { + return a == b; + } + + static const bool safeToCompareToEmptyOrDeleted = true; + }; + + typedef WeakGCMap<Hash::Key, Structure, Hash> TransitionMap; + +public: + StructureTransitionTable() + : m_data(UsingSingleSlotFlag) + { + } + + ~StructureTransitionTable() + { + if (!isUsingSingleSlot()) { + delete map(); + return; + } + + WeakImpl* impl = this->weakImpl(); + if (!impl) + return; + WeakSet::deallocate(impl); + } + + void add(VM&, Structure*); + bool contains(UniquedStringImpl*, unsigned attributes) const; + Structure* get(UniquedStringImpl*, unsigned attributes) const; + +private: + friend class SingleSlotTransitionWeakOwner; + + bool isUsingSingleSlot() const + { + return m_data & UsingSingleSlotFlag; + } + + TransitionMap* map() const + { + ASSERT(!isUsingSingleSlot()); + return reinterpret_cast<TransitionMap*>(m_data); + } + + WeakImpl* weakImpl() const + { + ASSERT(isUsingSingleSlot()); + return reinterpret_cast<WeakImpl*>(m_data & ~UsingSingleSlotFlag); + } + + void setMap(TransitionMap* map) + { + ASSERT(isUsingSingleSlot()); + + if (WeakImpl* impl = this->weakImpl()) + WeakSet::deallocate(impl); + + // This implicitly clears the flag that indicates we're using a single transition + m_data = reinterpret_cast<intptr_t>(map); + + ASSERT(!isUsingSingleSlot()); + } + + Structure* singleTransition() const; + void setSingleTransition(Structure*); + + intptr_t m_data; +}; + +} // namespace JSC + +#endif // StructureTransitionTable_h diff --git a/Source/JavaScriptCore/runtime/Symbol.cpp b/Source/JavaScriptCore/runtime/Symbol.cpp new file mode 100644 index 000000000..a765609ea --- /dev/null +++ b/Source/JavaScriptCore/runtime/Symbol.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "Symbol.h" + +#include "Error.h" +#include "JSCInlines.h" +#include "SymbolObject.h" + +namespace JSC { + +const ClassInfo Symbol::s_info = { "symbol", nullptr, nullptr, CREATE_METHOD_TABLE(Symbol) }; + +Symbol::Symbol(VM& vm) + : Base(vm, vm.symbolStructure.get()) + , m_privateName() +{ +} + +Symbol::Symbol(VM& vm, const String& string) + : Base(vm, vm.symbolStructure.get()) + , m_privateName(PrivateName::Description, string) +{ +} + +Symbol::Symbol(VM& vm, SymbolImpl& uid) + : Base(vm, vm.symbolStructure.get()) + , m_privateName(uid) +{ +} + +inline SymbolObject* SymbolObject::create(VM& vm, JSGlobalObject* globalObject, Symbol* symbol) +{ + SymbolObject* object = new (NotNull, allocateCell<SymbolObject>(vm.heap)) SymbolObject(vm, globalObject->symbolObjectStructure()); + object->finishCreation(vm, symbol); + return object; +} + +JSValue Symbol::toPrimitive(ExecState*, PreferredPrimitiveType) const +{ + return const_cast<Symbol*>(this); +} + +bool Symbol::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const +{ + result = this; + number = toNumber(exec); + return true; +} + +JSObject* Symbol::toObject(ExecState* exec, JSGlobalObject* globalObject) const +{ + return SymbolObject::create(exec->vm(), globalObject, const_cast<Symbol*>(this)); +} + +double Symbol::toNumber(ExecState* exec) const +{ + throwTypeError(exec); + return 0.0; +} + +void Symbol::destroy(JSCell* cell) +{ + static_cast<Symbol*>(cell)->Symbol::~Symbol(); +} + +String Symbol::descriptiveString() const +{ + return makeString("Symbol(", String(privateName().uid()), ')'); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Symbol.h b/Source/JavaScriptCore/runtime/Symbol.h new file mode 100644 index 000000000..83f5de248 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Symbol.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef Symbol_h +#define Symbol_h + +#include "JSCell.h" +#include "JSString.h" +#include "PrivateName.h" + +namespace JSC { + +class Symbol final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal; + + DECLARE_EXPORT_INFO; + + static const bool needsDestruction = true; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(SymbolType, StructureFlags), info()); + } + + static Symbol* create(VM& vm) + { + Symbol* symbol = new (NotNull, allocateCell<Symbol>(vm.heap)) Symbol(vm); + symbol->finishCreation(vm); + return symbol; + } + + static Symbol* create(ExecState* exec, JSString* description) + { + VM& vm = exec->vm(); + String desc = description->value(exec); + Symbol* symbol = new (NotNull, allocateCell<Symbol>(vm.heap)) Symbol(vm, desc); + symbol->finishCreation(vm); + return symbol; + } + + static Symbol* create(VM& vm, SymbolImpl& uid) + { + Symbol* symbol = new (NotNull, allocateCell<Symbol>(vm.heap)) Symbol(vm, uid); + symbol->finishCreation(vm); + return symbol; + } + + const PrivateName& privateName() const { return m_privateName; } + String descriptiveString() const; + + JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + JSObject* toObject(ExecState*, JSGlobalObject*) const; + double toNumber(ExecState*) const; + + static size_t offsetOfPrivateName() { return OBJECT_OFFSETOF(Symbol, m_privateName); } + +protected: + static void destroy(JSCell*); + + Symbol(VM&); + Symbol(VM&, const String&); + Symbol(VM&, SymbolImpl& uid); + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(inherits(info())); + } + + PrivateName m_privateName; +}; + +Symbol* asSymbol(JSValue); + +inline Symbol* asSymbol(JSValue value) +{ + ASSERT(value.asCell()->isSymbol()); + return jsCast<Symbol*>(value.asCell()); +} + +} // namespace JSC + +#endif // Symbol_h diff --git a/Source/JavaScriptCore/runtime/SymbolConstructor.cpp b/Source/JavaScriptCore/runtime/SymbolConstructor.cpp new file mode 100644 index 000000000..753ce60ee --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolConstructor.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "SymbolConstructor.h" + +#include "Error.h" +#include "JSCInlines.h" +#include "JSGlobalObject.h" +#include "Symbol.h" +#include "SymbolPrototype.h" +#include <wtf/text/SymbolRegistry.h> + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL symbolConstructorFor(ExecState*); +static EncodedJSValue JSC_HOST_CALL symbolConstructorKeyFor(ExecState*); + +} + +#include "SymbolConstructor.lut.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(SymbolConstructor); + +const ClassInfo SymbolConstructor::s_info = { "Function", &Base::s_info, &symbolConstructorTable, CREATE_METHOD_TABLE(SymbolConstructor) }; + +/* Source for SymbolConstructor.lut.h +@begin symbolConstructorTable + for symbolConstructorFor DontEnum|Function 1 + keyFor symbolConstructorKeyFor DontEnum|Function 1 +@end +*/ + +SymbolConstructor::SymbolConstructor(VM& vm, Structure* structure) + : InternalFunction(vm, structure) +{ +} + +#define INITIALIZE_WELL_KNOWN_SYMBOLS(name) \ + putDirectWithoutTransition(vm, Identifier::fromString(&vm, #name), Symbol::create(vm, static_cast<SymbolImpl&>(*vm.propertyNames->name##Symbol.impl())), DontEnum | DontDelete | ReadOnly); + +void SymbolConstructor::finishCreation(VM& vm, SymbolPrototype* prototype) +{ + Base::finishCreation(vm, prototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), DontDelete | ReadOnly | DontEnum); + + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_WELL_KNOWN_SYMBOLS) +} + +bool SymbolConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<Base>(exec, symbolConstructorTable, jsCast<SymbolConstructor*>(object), propertyName, slot); +} + +// ------------------------------ Functions --------------------------- + +static EncodedJSValue JSC_HOST_CALL callSymbol(ExecState* exec) +{ + JSValue description = exec->argument(0); + if (description.isUndefined()) + return JSValue::encode(Symbol::create(exec->vm())); + return JSValue::encode(Symbol::create(exec, description.toString(exec))); +} + +ConstructType SymbolConstructor::getConstructData(JSCell*, ConstructData&) +{ + return ConstructTypeNone; +} + +CallType SymbolConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callSymbol; + return CallTypeHost; +} + +EncodedJSValue JSC_HOST_CALL symbolConstructorFor(ExecState* exec) +{ + JSString* stringKey = exec->argument(0).toString(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + String string = stringKey->value(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + return JSValue::encode(Symbol::create(exec->vm(), exec->vm().symbolRegistry().symbolForKey(string))); +} + +EncodedJSValue JSC_HOST_CALL symbolConstructorKeyFor(ExecState* exec) +{ + JSValue symbolValue = exec->argument(0); + if (!symbolValue.isSymbol()) + return JSValue::encode(throwTypeError(exec)); + + SymbolImpl* uid = asSymbol(symbolValue)->privateName().uid(); + if (!uid->symbolRegistry()) + return JSValue::encode(jsUndefined()); + + ASSERT(uid->symbolRegistry() == &exec->vm().symbolRegistry()); + return JSValue::encode(jsString(exec, exec->vm().symbolRegistry().keyForSymbol(*uid))); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/SymbolConstructor.h b/Source/JavaScriptCore/runtime/SymbolConstructor.h new file mode 100644 index 000000000..344a978ab --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolConstructor.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef SymbolConstructor_h +#define SymbolConstructor_h + +#include "InternalFunction.h" +#include "Symbol.h" + +namespace JSC { + +class SymbolPrototype; + +class SymbolConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | Base::StructureFlags; + + static SymbolConstructor* create(VM& vm, Structure* structure, SymbolPrototype* prototype) + { + SymbolConstructor* constructor = new (NotNull, allocateCell<SymbolConstructor>(vm.heap)) SymbolConstructor(vm, structure); + constructor->finishCreation(vm, prototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + void finishCreation(VM&, SymbolPrototype*); + +private: + SymbolConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // SymbolConstructor_h diff --git a/Source/JavaScriptCore/runtime/SymbolObject.cpp b/Source/JavaScriptCore/runtime/SymbolObject.cpp new file mode 100644 index 000000000..9ab7a7ecb --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolObject.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "SymbolObject.h" + +#include "JSCInlines.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(SymbolObject); + +const ClassInfo SymbolObject::s_info = { "Symbol", &JSWrapperObject::s_info, nullptr, CREATE_METHOD_TABLE(SymbolObject) }; + +SymbolObject::SymbolObject(VM& vm, Structure* structure) + : JSWrapperObject(vm, structure) +{ +} + +void SymbolObject::finishCreation(VM& vm, Symbol* symbol) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + setInternalValue(vm, symbol); +} + +JSValue SymbolObject::defaultValue(const JSObject* object, ExecState*, PreferredPrimitiveType) +{ + const SymbolObject* symbolObject = jsCast<const SymbolObject*>(object); + return symbolObject->internalValue(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/SymbolObject.h b/Source/JavaScriptCore/runtime/SymbolObject.h new file mode 100644 index 000000000..46a049170 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolObject.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef SymbolObject_h +#define SymbolObject_h + +#include "JSWrapperObject.h" +#include "Symbol.h" + +namespace JSC { + +class SymbolObject : public JSWrapperObject { +public: + typedef JSWrapperObject Base; + + static SymbolObject* create(VM& vm, Structure* structure) + { + Symbol* symbol = Symbol::create(vm); + SymbolObject* object = new (NotNull, allocateCell<SymbolObject>(vm.heap)) SymbolObject(vm, structure); + object->finishCreation(vm, symbol); + return object; + } + static SymbolObject* create(VM& vm, Structure* structure, Symbol* symbol) + { + SymbolObject* object = new (NotNull, allocateCell<SymbolObject>(vm.heap)) SymbolObject(vm, structure); + object->finishCreation(vm, symbol); + return object; + } + static SymbolObject* create(VM&, JSGlobalObject*, Symbol*); + + DECLARE_EXPORT_INFO; + + Symbol* internalValue() const { return asSymbol(JSWrapperObject::internalValue());} + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + + static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + +protected: + JS_EXPORT_PRIVATE void finishCreation(VM&, Symbol*); + JS_EXPORT_PRIVATE SymbolObject(VM&, Structure*); +}; + +} // namespace JSC + +#endif // SymbolObject_h diff --git a/Source/JavaScriptCore/runtime/SymbolPrototype.cpp b/Source/JavaScriptCore/runtime/SymbolPrototype.cpp new file mode 100644 index 000000000..2f4ac4dd5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolPrototype.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "SymbolPrototype.h" + +#include "Error.h" +#include "JSCInlines.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL symbolProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL symbolProtoFuncValueOf(ExecState*); + +} + +#include "SymbolPrototype.lut.h" + +namespace JSC { + +const ClassInfo SymbolPrototype::s_info = { "Symbol", &Base::s_info, &symbolPrototypeTable, CREATE_METHOD_TABLE(SymbolPrototype) }; + +/* Source for SymbolPrototype.lut.h +@begin symbolPrototypeTable + toString symbolProtoFuncToString DontEnum|Function 0 + valueOf symbolProtoFuncValueOf DontEnum|Function 0 +@end +*/ + +SymbolPrototype::SymbolPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +void SymbolPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +bool SymbolPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) +{ + return getStaticFunctionSlot<Base>(exec, symbolPrototypeTable, jsCast<SymbolPrototype*>(object), propertyName, slot); +} + +// ------------------------------ Functions --------------------------- + +EncodedJSValue JSC_HOST_CALL symbolProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + Symbol* symbol = nullptr; + if (thisValue.isSymbol()) + symbol = asSymbol(thisValue); + else if (!thisValue.isObject()) + return throwVMTypeError(exec); + else { + JSObject* thisObject = asObject(thisValue); + if (!thisObject->inherits(SymbolObject::info())) + return throwVMTypeError(exec); + symbol = asSymbol(jsCast<SymbolObject*>(thisObject)->internalValue()); + } + + return JSValue::encode(jsNontrivialString(exec, symbol->descriptiveString())); +} + +EncodedJSValue JSC_HOST_CALL symbolProtoFuncValueOf(ExecState* exec) +{ + JSValue thisValue = exec->thisValue(); + if (thisValue.isSymbol()) + return JSValue::encode(thisValue); + + if (!thisValue.isObject()) + return throwVMTypeError(exec); + + JSObject* thisObject = asObject(thisValue); + if (!thisObject->inherits(SymbolObject::info())) + return throwVMTypeError(exec); + + return JSValue::encode(jsCast<SymbolObject*>(thisObject)->internalValue()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/SymbolPrototype.h b/Source/JavaScriptCore/runtime/SymbolPrototype.h new file mode 100644 index 000000000..8b221e519 --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolPrototype.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef SymbolPrototype_h +#define SymbolPrototype_h + +#include "Symbol.h" +#include "SymbolObject.h" + +namespace JSC { + +// In the ES6 spec, Symbol.prototype object is an ordinary JS object, not one of the symbol wrapper object instance. +class SymbolPrototype : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; + + static SymbolPrototype* create(VM& vm, JSGlobalObject*, Structure* structure) + { + SymbolPrototype* prototype = new (NotNull, allocateCell<SymbolPrototype>(vm.heap)) SymbolPrototype(vm, structure); + prototype->finishCreation(vm); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +protected: + SymbolPrototype(VM&, Structure*); + void finishCreation(VM&); + +private: + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); +}; + +} // namespace JSC + +#endif // SymbolPrototype_h diff --git a/Source/JavaScriptCore/runtime/SymbolTable.cpp b/Source/JavaScriptCore/runtime/SymbolTable.cpp new file mode 100644 index 000000000..02f943f0a --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolTable.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2012, 2014, 2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "SymbolTable.h" + +#include "JSDestructibleObject.h" +#include "JSCInlines.h" +#include "SlotVisitorInlines.h" +#include "TypeProfiler.h" + +namespace JSC { + +const ClassInfo SymbolTable::s_info = { "SymbolTable", 0, 0, CREATE_METHOD_TABLE(SymbolTable) }; + +SymbolTableEntry& SymbolTableEntry::copySlow(const SymbolTableEntry& other) +{ + ASSERT(other.isFat()); + FatEntry* newFatEntry = new FatEntry(*other.fatEntry()); + freeFatEntry(); + m_bits = bitwise_cast<intptr_t>(newFatEntry); + return *this; +} + +void SymbolTable::destroy(JSCell* cell) +{ + SymbolTable* thisObject = jsCast<SymbolTable*>(cell); + thisObject->SymbolTable::~SymbolTable(); +} + +void SymbolTableEntry::freeFatEntrySlow() +{ + ASSERT(isFat()); + delete fatEntry(); +} + +void SymbolTableEntry::prepareToWatch() +{ + if (!isWatchable()) + return; + FatEntry* entry = inflate(); + if (entry->m_watchpoints) + return; + entry->m_watchpoints = adoptRef(new WatchpointSet(ClearWatchpoint)); +} + +void SymbolTableEntry::addWatchpoint(Watchpoint* watchpoint) +{ + fatEntry()->m_watchpoints->add(watchpoint); +} + +SymbolTableEntry::FatEntry* SymbolTableEntry::inflateSlow() +{ + FatEntry* entry = new FatEntry(m_bits); + m_bits = bitwise_cast<intptr_t>(entry); + return entry; +} + +SymbolTable::SymbolTable(VM& vm) + : JSCell(vm, vm.symbolTableStructure.get()) + , m_usesNonStrictEval(false) + , m_scopeType(VarScope) +{ +} + +SymbolTable::~SymbolTable() { } + +void SymbolTable::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + m_singletonScope.set(vm, this, InferredValue::create(vm)); +} + +void SymbolTable::visitChildren(JSCell* thisCell, SlotVisitor& visitor) +{ + SymbolTable* thisSymbolTable = jsCast<SymbolTable*>(thisCell); + + visitor.append(&thisSymbolTable->m_arguments); + visitor.append(&thisSymbolTable->m_singletonScope); + + // Save some memory. This is O(n) to rebuild and we do so on the fly. + ConcurrentJITLocker locker(thisSymbolTable->m_lock); + thisSymbolTable->m_localToEntry = nullptr; +} + +const SymbolTable::LocalToEntryVec& SymbolTable::localToEntry(const ConcurrentJITLocker&) +{ + if (UNLIKELY(!m_localToEntry)) { + unsigned size = 0; + for (auto& entry : m_map) { + VarOffset offset = entry.value.varOffset(); + if (offset.isScope()) + size = std::max(size, offset.scopeOffset().offset() + 1); + } + + m_localToEntry = std::make_unique<LocalToEntryVec>(size, nullptr); + for (auto& entry : m_map) { + VarOffset offset = entry.value.varOffset(); + if (offset.isScope()) + m_localToEntry->at(offset.scopeOffset().offset()) = &entry.value; + } + } + + return *m_localToEntry; +} + +SymbolTableEntry* SymbolTable::entryFor(const ConcurrentJITLocker& locker, ScopeOffset offset) +{ + auto& toEntryVector = localToEntry(locker); + if (offset.offset() >= toEntryVector.size()) + return nullptr; + return toEntryVector[offset.offset()]; +} + +SymbolTable* SymbolTable::cloneScopePart(VM& vm) +{ + SymbolTable* result = SymbolTable::create(vm); + + result->m_usesNonStrictEval = m_usesNonStrictEval; + result->m_scopeType = m_scopeType; + + for (auto iter = m_map.begin(), end = m_map.end(); iter != end; ++iter) { + if (!iter->value.varOffset().isScope()) + continue; + result->m_map.add( + iter->key, + SymbolTableEntry(iter->value.varOffset(), iter->value.getAttributes())); + } + + result->m_maxScopeOffset = m_maxScopeOffset; + + if (ScopedArgumentsTable* arguments = this->arguments()) + result->m_arguments.set(vm, result, arguments); + + if (m_typeProfilingRareData) { + result->m_typeProfilingRareData = std::make_unique<TypeProfilingRareData>(); + + { + auto iter = m_typeProfilingRareData->m_uniqueIDMap.begin(); + auto end = m_typeProfilingRareData->m_uniqueIDMap.end(); + for (; iter != end; ++iter) + result->m_typeProfilingRareData->m_uniqueIDMap.set(iter->key, iter->value); + } + + { + auto iter = m_typeProfilingRareData->m_offsetToVariableMap.begin(); + auto end = m_typeProfilingRareData->m_offsetToVariableMap.end(); + for (; iter != end; ++iter) + result->m_typeProfilingRareData->m_offsetToVariableMap.set(iter->key, iter->value); + } + + { + auto iter = m_typeProfilingRareData->m_uniqueTypeSetMap.begin(); + auto end = m_typeProfilingRareData->m_uniqueTypeSetMap.end(); + for (; iter != end; ++iter) + result->m_typeProfilingRareData->m_uniqueTypeSetMap.set(iter->key, iter->value); + } + } + + return result; +} + +void SymbolTable::prepareForTypeProfiling(const ConcurrentJITLocker&) +{ + if (m_typeProfilingRareData) + return; + + m_typeProfilingRareData = std::make_unique<TypeProfilingRareData>(); + + for (auto iter = m_map.begin(), end = m_map.end(); iter != end; ++iter) { + m_typeProfilingRareData->m_uniqueIDMap.set(iter->key, TypeProfilerNeedsUniqueIDGeneration); + m_typeProfilingRareData->m_offsetToVariableMap.set(iter->value.varOffset(), iter->key); + } +} + +GlobalVariableID SymbolTable::uniqueIDForVariable(const ConcurrentJITLocker&, UniquedStringImpl* key, VM& vm) +{ + RELEASE_ASSERT(m_typeProfilingRareData); + + auto iter = m_typeProfilingRareData->m_uniqueIDMap.find(key); + auto end = m_typeProfilingRareData->m_uniqueIDMap.end(); + if (iter == end) + return TypeProfilerNoGlobalIDExists; + + GlobalVariableID id = iter->value; + if (id == TypeProfilerNeedsUniqueIDGeneration) { + id = vm.typeProfiler()->getNextUniqueVariableID(); + m_typeProfilingRareData->m_uniqueIDMap.set(key, id); + m_typeProfilingRareData->m_uniqueTypeSetMap.set(key, TypeSet::create()); // Make a new global typeset for this corresponding ID. + } + + return id; +} + +GlobalVariableID SymbolTable::uniqueIDForOffset(const ConcurrentJITLocker& locker, VarOffset offset, VM& vm) +{ + RELEASE_ASSERT(m_typeProfilingRareData); + + auto iter = m_typeProfilingRareData->m_offsetToVariableMap.find(offset); + auto end = m_typeProfilingRareData->m_offsetToVariableMap.end(); + if (iter == end) + return TypeProfilerNoGlobalIDExists; + + return uniqueIDForVariable(locker, iter->value.get(), vm); +} + +RefPtr<TypeSet> SymbolTable::globalTypeSetForOffset(const ConcurrentJITLocker& locker, VarOffset offset, VM& vm) +{ + RELEASE_ASSERT(m_typeProfilingRareData); + + uniqueIDForOffset(locker, offset, vm); // Lazily create the TypeSet if necessary. + + auto iter = m_typeProfilingRareData->m_offsetToVariableMap.find(offset); + auto end = m_typeProfilingRareData->m_offsetToVariableMap.end(); + if (iter == end) + return nullptr; + + return globalTypeSetForVariable(locker, iter->value.get(), vm); +} + +RefPtr<TypeSet> SymbolTable::globalTypeSetForVariable(const ConcurrentJITLocker& locker, UniquedStringImpl* key, VM& vm) +{ + RELEASE_ASSERT(m_typeProfilingRareData); + + uniqueIDForVariable(locker, key, vm); // Lazily create the TypeSet if necessary. + + auto iter = m_typeProfilingRareData->m_uniqueTypeSetMap.find(key); + auto end = m_typeProfilingRareData->m_uniqueTypeSetMap.end(); + if (iter == end) + return nullptr; + + return iter->value; +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h new file mode 100644 index 000000000..990039c1b --- /dev/null +++ b/Source/JavaScriptCore/runtime/SymbolTable.h @@ -0,0 +1,701 @@ +/* + * Copyright (C) 2007, 2008, 2012-2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef SymbolTable_h +#define SymbolTable_h + +#include "ConcurrentJITLock.h" +#include "ConstantMode.h" +#include "InferredValue.h" +#include "JSObject.h" +#include "ScopedArgumentsTable.h" +#include "TypeLocation.h" +#include "VarOffset.h" +#include "Watchpoint.h" +#include <memory> +#include <wtf/HashTraits.h> +#include <wtf/text/UniquedStringImpl.h> + +namespace JSC { + +class SymbolTable; + +static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>::max(); } + +// The bit twiddling in this class assumes that every register index is a +// reasonably small positive or negative number, and therefore has its high +// four bits all set or all unset. + +// In addition to implementing semantics-mandated variable attributes and +// implementation-mandated variable indexing, this class also implements +// watchpoints to be used for JIT optimizations. Because watchpoints are +// meant to be relatively rare, this class optimizes heavily for the case +// that they are not being used. To that end, this class uses the thin-fat +// idiom: either it is thin, in which case it contains an in-place encoded +// word that consists of attributes, the index, and a bit saying that it is +// thin; or it is fat, in which case it contains a pointer to a malloc'd +// data structure and a bit saying that it is fat. The malloc'd data +// structure will be malloced a second time upon copy, to preserve the +// property that in-place edits to SymbolTableEntry do not manifest in any +// copies. However, the malloc'd FatEntry data structure contains a ref- +// counted pointer to a shared WatchpointSet. Thus, in-place edits of the +// WatchpointSet will manifest in all copies. Here's a picture: +// +// SymbolTableEntry --> FatEntry --> WatchpointSet +// +// If you make a copy of a SymbolTableEntry, you will have: +// +// original: SymbolTableEntry --> FatEntry --> WatchpointSet +// copy: SymbolTableEntry --> FatEntry -----^ + +struct SymbolTableEntry { +private: + static VarOffset varOffsetFromBits(intptr_t bits) + { + VarKind kind; + intptr_t kindBits = bits & KindBitsMask; + if (kindBits <= UnwatchableScopeKindBits) + kind = VarKind::Scope; + else if (kindBits == StackKindBits) + kind = VarKind::Stack; + else + kind = VarKind::DirectArgument; + return VarOffset::assemble(kind, static_cast<int>(bits >> FlagBits)); + } + + static ScopeOffset scopeOffsetFromBits(intptr_t bits) + { + ASSERT((bits & KindBitsMask) <= UnwatchableScopeKindBits); + return ScopeOffset(static_cast<int>(bits >> FlagBits)); + } + +public: + + // Use the SymbolTableEntry::Fast class, either via implicit cast or by calling + // getFast(), when you (1) only care about isNull(), getIndex(), and isReadOnly(), + // and (2) you are in a hot path where you need to minimize the number of times + // that you branch on isFat() when getting the bits(). + class Fast { + public: + Fast() + : m_bits(SlimFlag) + { + } + + ALWAYS_INLINE Fast(const SymbolTableEntry& entry) + : m_bits(entry.bits()) + { + } + + bool isNull() const + { + return !(m_bits & ~SlimFlag); + } + + VarOffset varOffset() const + { + return varOffsetFromBits(m_bits); + } + + // Asserts if the offset is anything but a scope offset. This structures the assertions + // in a way that may result in better code, even in release, than doing + // varOffset().scopeOffset(). + ScopeOffset scopeOffset() const + { + return scopeOffsetFromBits(m_bits); + } + + bool isReadOnly() const + { + return m_bits & ReadOnlyFlag; + } + + bool isDontEnum() const + { + return m_bits & DontEnumFlag; + } + + unsigned getAttributes() const + { + unsigned attributes = 0; + if (isReadOnly()) + attributes |= ReadOnly; + if (isDontEnum()) + attributes |= DontEnum; + return attributes; + } + + bool isFat() const + { + return !(m_bits & SlimFlag); + } + + private: + friend struct SymbolTableEntry; + intptr_t m_bits; + }; + + SymbolTableEntry() + : m_bits(SlimFlag) + { + } + + SymbolTableEntry(VarOffset offset) + : m_bits(SlimFlag) + { + ASSERT(isValidVarOffset(offset)); + pack(offset, true, false, false); + } + + SymbolTableEntry(VarOffset offset, unsigned attributes) + : m_bits(SlimFlag) + { + ASSERT(isValidVarOffset(offset)); + pack(offset, true, attributes & ReadOnly, attributes & DontEnum); + } + + ~SymbolTableEntry() + { + freeFatEntry(); + } + + SymbolTableEntry(const SymbolTableEntry& other) + : m_bits(SlimFlag) + { + *this = other; + } + + SymbolTableEntry& operator=(const SymbolTableEntry& other) + { + if (UNLIKELY(other.isFat())) + return copySlow(other); + freeFatEntry(); + m_bits = other.m_bits; + return *this; + } + + bool isNull() const + { + return !(bits() & ~SlimFlag); + } + + VarOffset varOffset() const + { + return varOffsetFromBits(bits()); + } + + bool isWatchable() const + { + return (m_bits & KindBitsMask) == ScopeKindBits; + } + + // Asserts if the offset is anything but a scope offset. This structures the assertions + // in a way that may result in better code, even in release, than doing + // varOffset().scopeOffset(). + ScopeOffset scopeOffset() const + { + return scopeOffsetFromBits(bits()); + } + + ALWAYS_INLINE Fast getFast() const + { + return Fast(*this); + } + + ALWAYS_INLINE Fast getFast(bool& wasFat) const + { + Fast result; + wasFat = isFat(); + if (wasFat) + result.m_bits = fatEntry()->m_bits | SlimFlag; + else + result.m_bits = m_bits; + return result; + } + + unsigned getAttributes() const + { + return getFast().getAttributes(); + } + + void setAttributes(unsigned attributes) + { + pack(varOffset(), isWatchable(), attributes & ReadOnly, attributes & DontEnum); + } + + bool isReadOnly() const + { + return bits() & ReadOnlyFlag; + } + + ConstantMode constantMode() const + { + return modeForIsConstant(isReadOnly()); + } + + bool isDontEnum() const + { + return bits() & DontEnumFlag; + } + + void disableWatching() + { + if (WatchpointSet* set = watchpointSet()) + set->invalidate("Disabling watching in symbol table"); + if (varOffset().isScope()) + pack(varOffset(), false, isReadOnly(), isDontEnum()); + } + + void prepareToWatch(); + + void addWatchpoint(Watchpoint*); + + // This watchpoint set is initialized clear, and goes through the following state transitions: + // + // First write to this var, in any scope that has this symbol table: Clear->IsWatched. + // + // Second write to this var, in any scope that has this symbol table: IsWatched->IsInvalidated. + // + // We ensure that we touch the set (i.e. trigger its state transition) after we do the write. This + // means that if you're in the compiler thread, and you: + // + // 1) Observe that the set IsWatched and commit to adding your watchpoint. + // 2) Load a value from any scope that has this watchpoint set. + // + // Then you can be sure that that value is either going to be the correct value for that var forever, + // or the watchpoint set will invalidate and you'll get fired. + // + // It's possible to write a program that first creates multiple scopes with the same var, and then + // initializes that var in just one of them. This means that a compilation could constant-fold to one + // of the scopes that still has an undefined value for this variable. That's fine, because at that + // point any write to any of the instances of that variable would fire the watchpoint. + WatchpointSet* watchpointSet() + { + if (!isFat()) + return 0; + return fatEntry()->m_watchpoints.get(); + } + +private: + static const intptr_t SlimFlag = 0x1; + static const intptr_t ReadOnlyFlag = 0x2; + static const intptr_t DontEnumFlag = 0x4; + static const intptr_t NotNullFlag = 0x8; + static const intptr_t KindBitsMask = 0x30; + static const intptr_t ScopeKindBits = 0x00; + static const intptr_t UnwatchableScopeKindBits = 0x10; + static const intptr_t StackKindBits = 0x20; + static const intptr_t DirectArgumentKindBits = 0x30; + static const intptr_t FlagBits = 6; + + class FatEntry { + WTF_MAKE_FAST_ALLOCATED; + public: + FatEntry(intptr_t bits) + : m_bits(bits & ~SlimFlag) + { + } + + intptr_t m_bits; // always has FatFlag set and exactly matches what the bits would have been if this wasn't fat. + + RefPtr<WatchpointSet> m_watchpoints; + }; + + SymbolTableEntry& copySlow(const SymbolTableEntry&); + JS_EXPORT_PRIVATE void notifyWriteSlow(VM&, JSValue, const FireDetail&); + + bool isFat() const + { + return !(m_bits & SlimFlag); + } + + const FatEntry* fatEntry() const + { + ASSERT(isFat()); + return bitwise_cast<const FatEntry*>(m_bits); + } + + FatEntry* fatEntry() + { + ASSERT(isFat()); + return bitwise_cast<FatEntry*>(m_bits); + } + + FatEntry* inflate() + { + if (LIKELY(isFat())) + return fatEntry(); + return inflateSlow(); + } + + FatEntry* inflateSlow(); + + ALWAYS_INLINE intptr_t bits() const + { + if (isFat()) + return fatEntry()->m_bits; + return m_bits; + } + + ALWAYS_INLINE intptr_t& bits() + { + if (isFat()) + return fatEntry()->m_bits; + return m_bits; + } + + void freeFatEntry() + { + if (LIKELY(!isFat())) + return; + freeFatEntrySlow(); + } + + JS_EXPORT_PRIVATE void freeFatEntrySlow(); + + void pack(VarOffset offset, bool isWatchable, bool readOnly, bool dontEnum) + { + ASSERT(!isFat()); + intptr_t& bitsRef = bits(); + bitsRef = + (static_cast<intptr_t>(offset.rawOffset()) << FlagBits) | NotNullFlag | SlimFlag; + if (readOnly) + bitsRef |= ReadOnlyFlag; + if (dontEnum) + bitsRef |= DontEnumFlag; + switch (offset.kind()) { + case VarKind::Scope: + if (isWatchable) + bitsRef |= ScopeKindBits; + else + bitsRef |= UnwatchableScopeKindBits; + break; + case VarKind::Stack: + bitsRef |= StackKindBits; + break; + case VarKind::DirectArgument: + bitsRef |= DirectArgumentKindBits; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + } + + static bool isValidVarOffset(VarOffset offset) + { + return ((static_cast<intptr_t>(offset.rawOffset()) << FlagBits) >> FlagBits) == static_cast<intptr_t>(offset.rawOffset()); + } + + intptr_t m_bits; +}; + +struct SymbolTableIndexHashTraits : HashTraits<SymbolTableEntry> { + static const bool needsDestruction = true; +}; + +class SymbolTable final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + typedef HashMap<RefPtr<UniquedStringImpl>, SymbolTableEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, SymbolTableIndexHashTraits> Map; + typedef HashMap<RefPtr<UniquedStringImpl>, GlobalVariableID, IdentifierRepHash> UniqueIDMap; + typedef HashMap<RefPtr<UniquedStringImpl>, RefPtr<TypeSet>, IdentifierRepHash> UniqueTypeSetMap; + typedef HashMap<VarOffset, RefPtr<UniquedStringImpl>> OffsetToVariableMap; + typedef Vector<SymbolTableEntry*> LocalToEntryVec; + + static SymbolTable* create(VM& vm) + { + SymbolTable* symbolTable = new (NotNull, allocateCell<SymbolTable>(vm.heap)) SymbolTable(vm); + symbolTable->finishCreation(vm); + return symbolTable; + } + + static SymbolTable* createNameScopeTable(VM& vm, const Identifier& ident, unsigned attributes) + { + SymbolTable* result = create(vm); + result->add(ident.impl(), SymbolTableEntry(VarOffset(ScopeOffset(0)), attributes)); + return result; + } + + static const bool needsDestruction = true; + static void destroy(JSCell*); + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); + } + + // You must hold the lock until after you're done with the iterator. + Map::iterator find(const ConcurrentJITLocker&, UniquedStringImpl* key) + { + return m_map.find(key); + } + + Map::iterator find(const GCSafeConcurrentJITLocker&, UniquedStringImpl* key) + { + return m_map.find(key); + } + + SymbolTableEntry get(const ConcurrentJITLocker&, UniquedStringImpl* key) + { + return m_map.get(key); + } + + SymbolTableEntry get(UniquedStringImpl* key) + { + ConcurrentJITLocker locker(m_lock); + return get(locker, key); + } + + SymbolTableEntry inlineGet(const ConcurrentJITLocker&, UniquedStringImpl* key) + { + return m_map.inlineGet(key); + } + + SymbolTableEntry inlineGet(UniquedStringImpl* key) + { + ConcurrentJITLocker locker(m_lock); + return inlineGet(locker, key); + } + + Map::iterator begin(const ConcurrentJITLocker&) + { + return m_map.begin(); + } + + Map::iterator end(const ConcurrentJITLocker&) + { + return m_map.end(); + } + + Map::iterator end(const GCSafeConcurrentJITLocker&) + { + return m_map.end(); + } + + size_t size(const ConcurrentJITLocker&) const + { + return m_map.size(); + } + + size_t size() const + { + ConcurrentJITLocker locker(m_lock); + return size(locker); + } + + ScopeOffset maxScopeOffset() const + { + return m_maxScopeOffset; + } + + void didUseScopeOffset(ScopeOffset offset) + { + if (!m_maxScopeOffset || m_maxScopeOffset < offset) + m_maxScopeOffset = offset; + } + + void didUseVarOffset(VarOffset offset) + { + if (offset.isScope()) + didUseScopeOffset(offset.scopeOffset()); + } + + unsigned scopeSize() const + { + ScopeOffset maxScopeOffset = this->maxScopeOffset(); + + // Do some calculation that relies on invalid scope offset plus one being zero. + unsigned fastResult = maxScopeOffset.offsetUnchecked() + 1; + + // Assert that this works. + ASSERT(fastResult == (!maxScopeOffset ? 0 : maxScopeOffset.offset() + 1)); + + return fastResult; + } + + ScopeOffset nextScopeOffset() const + { + return ScopeOffset(scopeSize()); + } + + ScopeOffset takeNextScopeOffset(const ConcurrentJITLocker&) + { + ScopeOffset result = nextScopeOffset(); + m_maxScopeOffset = result; + return result; + } + + ScopeOffset takeNextScopeOffset() + { + ConcurrentJITLocker locker(m_lock); + return takeNextScopeOffset(locker); + } + + void add(const ConcurrentJITLocker&, UniquedStringImpl* key, const SymbolTableEntry& entry) + { + RELEASE_ASSERT(!m_localToEntry); + didUseVarOffset(entry.varOffset()); + Map::AddResult result = m_map.add(key, entry); + ASSERT_UNUSED(result, result.isNewEntry); + } + + void add(UniquedStringImpl* key, const SymbolTableEntry& entry) + { + ConcurrentJITLocker locker(m_lock); + add(locker, key, entry); + } + + void set(const ConcurrentJITLocker&, UniquedStringImpl* key, const SymbolTableEntry& entry) + { + RELEASE_ASSERT(!m_localToEntry); + didUseVarOffset(entry.varOffset()); + m_map.set(key, entry); + } + + void set(UniquedStringImpl* key, const SymbolTableEntry& entry) + { + ConcurrentJITLocker locker(m_lock); + set(locker, key, entry); + } + + bool contains(const ConcurrentJITLocker&, UniquedStringImpl* key) + { + return m_map.contains(key); + } + + bool contains(UniquedStringImpl* key) + { + ConcurrentJITLocker locker(m_lock); + return contains(locker, key); + } + + // The principle behind ScopedArgumentsTable modifications is that we will create one and + // leave it unlocked - thereby allowing in-place changes - until someone asks for a pointer to + // the table. Then, we will lock it. Then both our future changes and their future changes + // will first have to make a copy. This discipline means that usually when we create a + // ScopedArguments object, we don't have to make a copy of the ScopedArgumentsTable - instead + // we just take a reference to one that we already have. + + uint32_t argumentsLength() const + { + if (!m_arguments) + return 0; + return m_arguments->length(); + } + + void setArgumentsLength(VM& vm, uint32_t length) + { + if (UNLIKELY(!m_arguments)) + m_arguments.set(vm, this, ScopedArgumentsTable::create(vm)); + m_arguments.set(vm, this, m_arguments->setLength(vm, length)); + } + + ScopeOffset argumentOffset(uint32_t i) const + { + ASSERT_WITH_SECURITY_IMPLICATION(m_arguments); + return m_arguments->get(i); + } + + void setArgumentOffset(VM& vm, uint32_t i, ScopeOffset offset) + { + ASSERT_WITH_SECURITY_IMPLICATION(m_arguments); + m_arguments.set(vm, this, m_arguments->set(vm, i, offset)); + } + + ScopedArgumentsTable* arguments() const + { + if (!m_arguments) + return nullptr; + m_arguments->lock(); + return m_arguments.get(); + } + + const LocalToEntryVec& localToEntry(const ConcurrentJITLocker&); + SymbolTableEntry* entryFor(const ConcurrentJITLocker&, ScopeOffset); + + GlobalVariableID uniqueIDForVariable(const ConcurrentJITLocker&, UniquedStringImpl* key, VM&); + GlobalVariableID uniqueIDForOffset(const ConcurrentJITLocker&, VarOffset, VM&); + RefPtr<TypeSet> globalTypeSetForOffset(const ConcurrentJITLocker&, VarOffset, VM&); + RefPtr<TypeSet> globalTypeSetForVariable(const ConcurrentJITLocker&, UniquedStringImpl* key, VM&); + + bool usesNonStrictEval() { return m_usesNonStrictEval; } + void setUsesNonStrictEval(bool usesNonStrictEval) { m_usesNonStrictEval = usesNonStrictEval; } + + enum ScopeType { + VarScope, + LexicalScope, + CatchScope, + FunctionNameScope + }; + void setScopeType(ScopeType type) { m_scopeType = type; } + ScopeType scopeType() const { return static_cast<ScopeType>(m_scopeType); } + + SymbolTable* cloneScopePart(VM&); + + void prepareForTypeProfiling(const ConcurrentJITLocker&); + + InferredValue* singletonScope() { return m_singletonScope.get(); } + + static void visitChildren(JSCell*, SlotVisitor&); + + DECLARE_EXPORT_INFO; + +private: + JS_EXPORT_PRIVATE SymbolTable(VM&); + ~SymbolTable(); + + JS_EXPORT_PRIVATE void finishCreation(VM&); + + Map m_map; + ScopeOffset m_maxScopeOffset; + + struct TypeProfilingRareData { + UniqueIDMap m_uniqueIDMap; + OffsetToVariableMap m_offsetToVariableMap; + UniqueTypeSetMap m_uniqueTypeSetMap; + }; + std::unique_ptr<TypeProfilingRareData> m_typeProfilingRareData; + + bool m_usesNonStrictEval : 1; + unsigned m_scopeType : 2; // ScopeType + + WriteBarrier<ScopedArgumentsTable> m_arguments; + WriteBarrier<InferredValue> m_singletonScope; + + std::unique_ptr<LocalToEntryVec> m_localToEntry; + +public: + mutable ConcurrentJITLock m_lock; +}; + +} // namespace JSC + +#endif // SymbolTable_h diff --git a/Source/JavaScriptCore/runtime/TemplateRegistry.cpp b/Source/JavaScriptCore/runtime/TemplateRegistry.cpp new file mode 100644 index 000000000..7bdac38e3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TemplateRegistry.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "TemplateRegistry.h" + +#include "JSCJSValueInlines.h" +#include "JSGlobalObject.h" +#include "ObjectConstructor.h" +#include "StructureInlines.h" +#include "WeakGCMapInlines.h" + +namespace JSC { + +TemplateRegistry::TemplateRegistry(VM& vm) + : m_templateMap(vm) +{ +} + +JSArray* TemplateRegistry::getTemplateObject(ExecState* exec, const TemplateRegistryKey& templateKey) +{ + JSArray* cached = m_templateMap.get(templateKey); + if (cached) + return cached; + + unsigned count = templateKey.cookedStrings().size(); + JSArray* templateObject = constructEmptyArray(exec, nullptr, count); + JSArray* rawObject = constructEmptyArray(exec, nullptr, count); + + for (unsigned index = 0; index < count; ++index) { + templateObject->putDirectIndex(exec, index, jsString(exec, templateKey.cookedStrings()[index]), ReadOnly | DontDelete, PutDirectIndexLikePutDirect); + rawObject->putDirectIndex(exec, index, jsString(exec, templateKey.rawStrings()[index]), ReadOnly | DontDelete, PutDirectIndexLikePutDirect); + } + + objectConstructorFreeze(exec, rawObject); + ASSERT(!exec->hadException()); + + templateObject->putDirect(exec->vm(), exec->propertyNames().raw, rawObject, ReadOnly | DontEnum | DontDelete); + + objectConstructorFreeze(exec, templateObject); + ASSERT(!exec->hadException()); + + m_templateMap.set(templateKey, templateObject); + + return templateObject; +} + + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/TemplateRegistry.h b/Source/JavaScriptCore/runtime/TemplateRegistry.h new file mode 100644 index 000000000..9cb62ddfd --- /dev/null +++ b/Source/JavaScriptCore/runtime/TemplateRegistry.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef TemplateRegistry_h +#define TemplateRegistry_h + +#include "JSArray.h" +#include "TemplateRegistryKey.h" +#include "WeakGCMap.h" +#include <limits> + +namespace JSC { + +class TemplateRegistry { +public: + TemplateRegistry(VM&); + + JSArray* getTemplateObject(ExecState*, const TemplateRegistryKey&); + +private: + WeakGCMap<TemplateRegistryKey, JSArray> m_templateMap; +}; + +} // namespace JSC + +#endif // TemplateRegistry_h diff --git a/Source/JavaScriptCore/runtime/TemplateRegistryKey.h b/Source/JavaScriptCore/runtime/TemplateRegistryKey.h new file mode 100644 index 000000000..17d882884 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TemplateRegistryKey.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef TemplateRegistryKey_h +#define TemplateRegistryKey_h + +#include <limits> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +class TemplateRegistryKey { +public: + typedef Vector<String, 4> StringVector; + + TemplateRegistryKey(const StringVector& rawStrings, const StringVector& cookedStrings); + enum DeletedValueTag { DeletedValue }; + TemplateRegistryKey(DeletedValueTag); + enum EmptyValueTag { EmptyValue }; + TemplateRegistryKey(EmptyValueTag); + + bool isDeletedValue() const { return m_rawStrings.isEmpty() && m_hash == std::numeric_limits<unsigned>::max(); } + + bool isEmptyValue() const { return m_rawStrings.isEmpty() && !m_hash; } + + unsigned hash() const { return m_hash; } + + const StringVector& rawStrings() const { return m_rawStrings; } + const StringVector& cookedStrings() const { return m_cookedStrings; } + + bool operator==(const TemplateRegistryKey& other) const { return m_hash == other.m_hash && m_rawStrings == other.m_rawStrings; } + bool operator!=(const TemplateRegistryKey& other) const { return m_hash != other.m_hash || m_rawStrings != other.m_rawStrings; } + + struct Hasher { + static unsigned hash(const TemplateRegistryKey& key) { return key.hash(); } + static bool equal(const TemplateRegistryKey& a, const TemplateRegistryKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; + }; + +private: + StringVector m_rawStrings; + StringVector m_cookedStrings; + unsigned m_hash { 0 }; +}; + +inline TemplateRegistryKey::TemplateRegistryKey(const StringVector& rawStrings, const StringVector& cookedStrings) + : m_rawStrings(rawStrings) + , m_cookedStrings(cookedStrings) +{ + m_hash = 0; + for (const String& string : rawStrings) + m_hash += WTF::StringHash::hash(string); +} + +inline TemplateRegistryKey::TemplateRegistryKey(DeletedValueTag) + : m_hash(std::numeric_limits<unsigned>::max()) +{ +} + +inline TemplateRegistryKey::TemplateRegistryKey(EmptyValueTag) + : m_hash(0) +{ +} + +} // namespace JSC + +namespace WTF { +template<typename T> struct DefaultHash; + +template<> struct DefaultHash<JSC::TemplateRegistryKey> { + typedef JSC::TemplateRegistryKey::Hasher Hash; +}; + +template<> struct HashTraits<JSC::TemplateRegistryKey> : CustomHashTraits<JSC::TemplateRegistryKey> { +}; + +} // namespace WTF + +#endif // TemplateRegistryKey_h diff --git a/Source/JavaScriptCore/runtime/TestRunnerUtils.cpp b/Source/JavaScriptCore/runtime/TestRunnerUtils.cpp new file mode 100644 index 000000000..3c5dac7a0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TestRunnerUtils.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "TestRunnerUtils.h" + +#include "CodeBlock.h" +#include "JSCInlines.h" + +namespace JSC { + +FunctionExecutable* getExecutableForFunction(JSValue theFunctionValue) +{ + JSFunction* theFunction = jsDynamicCast<JSFunction*>(theFunctionValue); + if (!theFunction) + return 0; + + FunctionExecutable* executable = jsDynamicCast<FunctionExecutable*>( + theFunction->executable()); + return executable; +} + +CodeBlock* getSomeBaselineCodeBlockForFunction(JSValue theFunctionValue) +{ + FunctionExecutable* executable = getExecutableForFunction(theFunctionValue); + if (!executable) + return 0; + + CodeBlock* baselineCodeBlock = executable->baselineCodeBlockFor(CodeForCall); + + if (!baselineCodeBlock) + baselineCodeBlock = executable->baselineCodeBlockFor(CodeForConstruct); + + return baselineCodeBlock; +} + +JSValue numberOfDFGCompiles(JSValue theFunctionValue) +{ + bool pretendToHaveManyCompiles = false; +#if ENABLE(DFG_JIT) + if (!Options::useJIT() || !Options::useDFGJIT()) + pretendToHaveManyCompiles = true; +#else + pretendToHaveManyCompiles = true; +#endif + + if (CodeBlock* baselineCodeBlock = getSomeBaselineCodeBlockForFunction(theFunctionValue)) { + if (pretendToHaveManyCompiles) + return jsNumber(1000000.0); + return jsNumber(baselineCodeBlock->numberOfDFGCompiles()); + } + + return jsNumber(0); +} + +JSValue setNeverInline(JSValue theFunctionValue) +{ + if (FunctionExecutable* executable = getExecutableForFunction(theFunctionValue)) + executable->setNeverInline(true); + + return jsUndefined(); +} + +JSValue optimizeNextInvocation(JSValue theFunctionValue) +{ +#if ENABLE(JIT) + if (CodeBlock* baselineCodeBlock = getSomeBaselineCodeBlockForFunction(theFunctionValue)) + baselineCodeBlock->optimizeNextInvocation(); +#else + UNUSED_PARAM(theFunctionValue); +#endif + + return jsUndefined(); +} + +JSValue numberOfDFGCompiles(ExecState* exec) +{ + if (exec->argumentCount() < 1) + return jsUndefined(); + return numberOfDFGCompiles(exec->uncheckedArgument(0)); +} + +JSValue setNeverInline(ExecState* exec) +{ + if (exec->argumentCount() < 1) + return jsUndefined(); + return setNeverInline(exec->uncheckedArgument(0)); +} + +JSValue optimizeNextInvocation(ExecState* exec) +{ + if (exec->argumentCount() < 1) + return jsUndefined(); + return optimizeNextInvocation(exec->uncheckedArgument(0)); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/TestRunnerUtils.h b/Source/JavaScriptCore/runtime/TestRunnerUtils.h new file mode 100644 index 000000000..ffb843897 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TestRunnerUtils.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TestRunnerUtils_h +#define TestRunnerUtils_h + +#include "JSCJSValue.h" + +namespace JSC { + +class CodeBlock; +class FunctionExecutable; + +JS_EXPORT_PRIVATE FunctionExecutable* getExecutableForFunction(JSValue theFunctionValue); +JS_EXPORT_PRIVATE CodeBlock* getSomeBaselineCodeBlockForFunction(JSValue theFunctionValue); + +JS_EXPORT_PRIVATE JSValue numberOfDFGCompiles(JSValue function); +JS_EXPORT_PRIVATE JSValue setNeverInline(JSValue function); +JS_EXPORT_PRIVATE JSValue optimizeNextInvocation(JSValue function); + +JS_EXPORT_PRIVATE JSValue numberOfDFGCompiles(ExecState*); +JS_EXPORT_PRIVATE JSValue setNeverInline(ExecState*); +JS_EXPORT_PRIVATE JSValue optimizeNextInvocation(ExecState*); + +JS_EXPORT_PRIVATE unsigned numberOfExceptionFuzzChecks(); +JS_EXPORT_PRIVATE unsigned numberOfExecutableAllocationFuzzChecks(); +JS_EXPORT_PRIVATE unsigned numberOfStaticOSRExitFuzzChecks(); +JS_EXPORT_PRIVATE unsigned numberOfOSRExitFuzzChecks(); + +} // namespace JSC + +#endif // TestRunnerUtils_h diff --git a/Source/JavaScriptCore/runtime/ToNativeFromValue.h b/Source/JavaScriptCore/runtime/ToNativeFromValue.h new file mode 100644 index 000000000..1d871a607 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ToNativeFromValue.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef ToNativeFromValue_h +#define ToNativeFromValue_h + +#include "JSCJSValue.h" + +namespace JSC { + +template<typename Adaptor> +typename Adaptor::Type toNativeFromValue(JSValue value) +{ + if (value.isInt32()) + return Adaptor::toNativeFromInt32(value.asInt32()); + return Adaptor::toNativeFromDouble(value.asDouble()); +} + +template<typename Adaptor> +typename Adaptor::Type toNativeFromValue(ExecState* exec, JSValue value) +{ + if (value.isInt32()) + return Adaptor::toNativeFromInt32(value.asInt32()); + if (value.isNumber()) + return Adaptor::toNativeFromDouble(value.asDouble()); + return Adaptor::toNativeFromDouble(value.toNumber(exec)); +} + +} // namespace JSC + +#endif // ToNativeFromValue_h + diff --git a/Source/JavaScriptCore/runtime/Tracing.d b/Source/JavaScriptCore/runtime/Tracing.d new file mode 100644 index 000000000..ec13625d8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Tracing.d @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +provider JavaScriptCore +{ + probe gc__begin(); + probe gc__marked(); + probe gc__end(); + + probe profile__will_execute(int, char*, char*, int, int); + probe profile__did_execute(int, char*, char*, int, int); +}; + +#pragma D attributes Unstable/Unstable/Common provider JavaScriptCore provider +#pragma D attributes Private/Private/Unknown provider JavaScriptCore module +#pragma D attributes Private/Private/Unknown provider JavaScriptCore function +#pragma D attributes Unstable/Unstable/Common provider JavaScriptCore name +#pragma D attributes Unstable/Unstable/Common provider JavaScriptCore args diff --git a/Source/JavaScriptCore/runtime/Tracing.h b/Source/JavaScriptCore/runtime/Tracing.h new file mode 100644 index 000000000..69ead921c --- /dev/null +++ b/Source/JavaScriptCore/runtime/Tracing.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Tracing_h +#define Tracing_h + +#if HAVE(DTRACE) +#include "TracingDtrace.h" +#else + +#define JAVASCRIPTCORE_GC_BEGIN() +#define JAVASCRIPTCORE_GC_BEGIN_ENABLED() 0 + +#define JAVASCRIPTCORE_GC_END() +#define JAVASCRIPTCORE_GC_END_ENABLED() 0 + +#define JAVASCRIPTCORE_GC_MARKED() +#define JAVASCRIPTCORE_GC_MARKED_ENABLED() 0 + +#define JAVASCRIPTCORE_PROFILE_WILL_EXECUTE(arg0, arg1, arg2, arg3, arg4) +#define JAVASCRIPTCORE_PROFILE_WILL_EXECUTE_ENABLED() 0 + +#define JAVASCRIPTCORE_PROFILE_DID_EXECUTE(arg0, arg1, arg2, arg3, arg4) +#define JAVASCRIPTCORE_PROFILE_DID_EXECUTE_ENABLED() 0 + +#endif + +#endif // Tracing_h diff --git a/Source/JavaScriptCore/runtime/TypeLocationCache.cpp b/Source/JavaScriptCore/runtime/TypeLocationCache.cpp new file mode 100644 index 000000000..4c8069a8c --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeLocationCache.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "TypeLocationCache.h" + +#include "TypeProfiler.h" +#include "VM.h" + +namespace JSC { + +std::pair<TypeLocation*, bool> TypeLocationCache::getTypeLocation(GlobalVariableID globalVariableID, intptr_t sourceID, unsigned start, unsigned end, PassRefPtr<TypeSet> globalTypeSet, VM* vm) +{ + LocationKey key; + key.m_globalVariableID = globalVariableID; + key.m_sourceID = sourceID; + key.m_start = start; + key.m_end = end; + + bool isNewLocation = false; + if (m_locationMap.find(key) == m_locationMap.end()) { + ASSERT(vm->typeProfiler()); + TypeLocation* location = vm->typeProfiler()->nextTypeLocation(); + location->m_globalVariableID = globalVariableID; + location->m_sourceID = sourceID; + location->m_divotStart = start; + location->m_divotEnd = end; + location->m_globalTypeSet = globalTypeSet; + + m_locationMap[key] = location; + isNewLocation = true; + } + + TypeLocation* location = m_locationMap.find(key)->second; + return std::pair<TypeLocation*, bool>(location, isNewLocation); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/TypeLocationCache.h b/Source/JavaScriptCore/runtime/TypeLocationCache.h new file mode 100644 index 000000000..9f856666c --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeLocationCache.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypeLocationCache_h +#define TypeLocationCache_h + +#include "TypeLocation.h" +#include <unordered_map> +#include <wtf/HashMethod.h> + +namespace JSC { + +class VM; + +class TypeLocationCache { +public: + struct LocationKey { + LocationKey() {} + bool operator==(const LocationKey& other) const + { + return m_globalVariableID == other.m_globalVariableID + && m_sourceID == other.m_sourceID + && m_start == other.m_start + && m_end == other.m_end; + } + + unsigned hash() const + { + return m_globalVariableID + m_sourceID + m_start + m_end; + } + + GlobalVariableID m_globalVariableID; + intptr_t m_sourceID; + unsigned m_start; + unsigned m_end; + }; + + std::pair<TypeLocation*, bool> getTypeLocation(GlobalVariableID, intptr_t, unsigned start, unsigned end, PassRefPtr<TypeSet>, VM*); +private: + typedef std::unordered_map<LocationKey, TypeLocation*, HashMethod<LocationKey>> LocationMap; + LocationMap m_locationMap; +}; + +} // namespace JSC + +#endif // TypeLocationCache_h diff --git a/Source/JavaScriptCore/runtime/TypeProfiler.cpp b/Source/JavaScriptCore/runtime/TypeProfiler.cpp new file mode 100644 index 000000000..338a1c219 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeProfiler.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "TypeProfiler.h" + +#include "InspectorProtocolObjects.h" +#include "TypeLocation.h" +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +static const bool verbose = false; + +TypeProfiler::TypeProfiler() + : m_nextUniqueVariableID(1) +{ +} + +void TypeProfiler::logTypesForTypeLocation(TypeLocation* location, VM& vm) +{ + TypeProfilerSearchDescriptor descriptor = location->m_globalVariableID == TypeProfilerReturnStatement ? TypeProfilerSearchDescriptorFunctionReturn : TypeProfilerSearchDescriptorNormal; + + dataLogF("[Start, End]::[%u, %u]\n", location->m_divotStart, location->m_divotEnd); + + if (findLocation(location->m_divotStart, location->m_sourceID, descriptor, vm)) + dataLog("\t\t[Entry IS in System]\n"); + else + dataLog("\t\t[Entry IS NOT in system]\n"); + + dataLog("\t\t", location->m_globalVariableID == TypeProfilerReturnStatement ? "[Return Statement]" : "[Normal Statement]", "\n"); + + dataLog("\t\t#Local#\n\t\t", location->m_instructionTypeSet->dumpTypes().replace("\n", "\n\t\t"), "\n"); + if (location->m_globalTypeSet) + dataLog("\t\t#Global#\n\t\t", location->m_globalTypeSet->dumpTypes().replace("\n", "\n\t\t"), "\n"); +} + +void TypeProfiler::insertNewLocation(TypeLocation* location) +{ + if (verbose) + dataLogF("Registering location:: divotStart:%u, divotEnd:%u\n", location->m_divotStart, location->m_divotEnd); + + if (!m_bucketMap.contains(location->m_sourceID)) { + Vector<TypeLocation*> bucket; + m_bucketMap.set(location->m_sourceID, bucket); + } + + Vector<TypeLocation*>& bucket = m_bucketMap.find(location->m_sourceID)->value; + bucket.append(location); +} + +String TypeProfiler::typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor descriptor, unsigned offset, intptr_t sourceID, VM& vm) +{ + // This returns a JSON string representing an Object with the following properties: + // globalTypeSet: 'JSON<TypeSet> | null' + // instructionTypeSet: 'JSON<TypeSet>' + + TypeLocation* location = findLocation(offset, sourceID, descriptor, vm); + ASSERT(location); + + StringBuilder json; + + json.append('{'); + + json.appendLiteral("\"globalTypeSet\":"); + if (location->m_globalTypeSet && location->m_globalVariableID != TypeProfilerNoGlobalIDExists) + json.append(location->m_globalTypeSet->toJSONString()); + else + json.appendLiteral("null"); + json.append(','); + + json.appendLiteral("\"instructionTypeSet\":"); + json.append(location->m_instructionTypeSet->toJSONString()); + json.append(','); + + json.appendLiteral("\"isOverflown\":"); + if (location->m_instructionTypeSet->isOverflown() || (location->m_globalTypeSet && location->m_globalTypeSet->isOverflown())) + json.appendLiteral("true"); + else + json.appendLiteral("false"); + + json.append('}'); + + return json.toString(); +} + +TypeLocation* TypeProfiler::findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor descriptor, VM& vm) +{ + QueryKey queryKey(sourceID, divot); + auto iter = m_queryCache.find(queryKey); + if (iter != m_queryCache.end()) + return iter->value; + + if (!vm.functionHasExecutedCache()->hasExecutedAtOffset(sourceID, divot)) + return nullptr; + + if (!m_bucketMap.contains(sourceID)) + return nullptr; + + Vector<TypeLocation*>& bucket = m_bucketMap.find(sourceID)->value; + TypeLocation* bestMatch = nullptr; + unsigned distance = UINT_MAX; // Because assignments may be nested, make sure we find the closest enclosing assignment to this character offset. + for (size_t i = 0, size = bucket.size(); i < size; i++) { + TypeLocation* location = bucket.at(i); + // We found the type location that correlates to the convergence of all return statements in a function. + // This text offset is the offset of the opening brace in a function declaration. + if (descriptor == TypeProfilerSearchDescriptorFunctionReturn && location->m_globalVariableID == TypeProfilerReturnStatement && location->m_divotForFunctionOffsetIfReturnStatement == divot) + return location; + + if (descriptor != TypeProfilerSearchDescriptorFunctionReturn && location->m_divotStart <= divot && divot <= location->m_divotEnd && location->m_divotEnd - location->m_divotStart <= distance) { + distance = location->m_divotEnd - location->m_divotStart; + bestMatch = location; + } + } + + if (bestMatch) + m_queryCache.set(queryKey, bestMatch); + // FIXME: BestMatch should never be null past this point. This doesn't hold currently because we ignore var assignments when code contains eval/With (VarInjection). + // https://bugs.webkit.org/show_bug.cgi?id=135184 + return bestMatch; +} + +TypeLocation* TypeProfiler::nextTypeLocation() +{ + return m_typeLocationInfo.add(); +} + +void TypeProfiler::invalidateTypeSetCache() +{ + for (Bag<TypeLocation>::iterator iter = m_typeLocationInfo.begin(); !!iter; ++iter) { + TypeLocation* location = *iter; + location->m_instructionTypeSet->invalidateCache(); + if (location->m_globalTypeSet) + location->m_globalTypeSet->invalidateCache(); + } +} + +void TypeProfiler::dumpTypeProfilerData(VM& vm) +{ + for (Bag<TypeLocation>::iterator iter = m_typeLocationInfo.begin(); !!iter; ++iter) { + TypeLocation* location = *iter; + logTypesForTypeLocation(location, vm); + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/TypeProfiler.h b/Source/JavaScriptCore/runtime/TypeProfiler.h new file mode 100644 index 000000000..6b27bd4c3 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeProfiler.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypeProfiler_h +#define TypeProfiler_h + +#include "CodeBlock.h" +#include "TypeLocation.h" +#include "TypeLocationCache.h" +#include <wtf/Bag.h> +#include <wtf/HashMap.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace Inspector { namespace Protocol { namespace Runtime { +class TypeDescription; +}}} + +namespace JSC { + +struct QueryKey { + QueryKey() + : m_sourceID(0) + , m_divot(0) + { } + + QueryKey(intptr_t sourceID, unsigned divot) + : m_sourceID(sourceID) + , m_divot(divot) + { } + + QueryKey(WTF::HashTableDeletedValueType) + : m_sourceID(INTPTR_MAX) + , m_divot(UINT_MAX) + { } + + bool isHashTableDeletedValue() const { return m_sourceID == INTPTR_MAX && m_divot == UINT_MAX; } + bool operator==(const QueryKey& other) const { return m_sourceID == other.m_sourceID && m_divot == other.m_divot; } + unsigned hash() const { return m_sourceID + m_divot; } + + intptr_t m_sourceID; + unsigned m_divot; +}; + +struct QueryKeyHash { + static unsigned hash(const QueryKey& key) { return key.hash(); } + static bool equal(const QueryKey& a, const QueryKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +} // namespace JSC + +namespace WTF { + +template<typename T> struct DefaultHash; +template<> struct DefaultHash<JSC::QueryKey> { + typedef JSC::QueryKeyHash Hash; +}; + +template<typename T> struct HashTraits; +template<> struct HashTraits<JSC::QueryKey> : SimpleClassHashTraits<JSC::QueryKey> { }; + +} // namespace WTF + +namespace JSC { + +class VM; + +enum TypeProfilerSearchDescriptor { + TypeProfilerSearchDescriptorNormal = 1, + TypeProfilerSearchDescriptorFunctionReturn = 2 +}; + +class TypeProfiler { + WTF_MAKE_FAST_ALLOCATED; +public: + TypeProfiler(); + void logTypesForTypeLocation(TypeLocation*, VM&); + JS_EXPORT_PRIVATE String typeInformationForExpressionAtOffset(TypeProfilerSearchDescriptor, unsigned offset, intptr_t sourceID, VM&); + void insertNewLocation(TypeLocation*); + TypeLocationCache* typeLocationCache() { return &m_typeLocationCache; } + TypeLocation* findLocation(unsigned divot, intptr_t sourceID, TypeProfilerSearchDescriptor, VM&); + GlobalVariableID getNextUniqueVariableID() { return m_nextUniqueVariableID++; } + TypeLocation* nextTypeLocation(); + void invalidateTypeSetCache(); + void dumpTypeProfilerData(VM&); + +private: + typedef HashMap<intptr_t, Vector<TypeLocation*>> SourceIDToLocationBucketMap; + SourceIDToLocationBucketMap m_bucketMap; + TypeLocationCache m_typeLocationCache; + typedef HashMap<QueryKey, TypeLocation*> TypeLocationQueryCache; + TypeLocationQueryCache m_queryCache; + GlobalVariableID m_nextUniqueVariableID; + Bag<TypeLocation> m_typeLocationInfo; +}; + +} // namespace JSC + +#endif // TypeProfiler_h diff --git a/Source/JavaScriptCore/runtime/TypeProfilerLog.cpp b/Source/JavaScriptCore/runtime/TypeProfilerLog.cpp new file mode 100644 index 000000000..9d10aae88 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeProfilerLog.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "TypeProfilerLog.h" + +#include "JSCJSValueInlines.h" +#include "TypeLocation.h" +#include <wtf/CurrentTime.h> + + +namespace JSC { + +static const bool verbose = false; + +void TypeProfilerLog::initializeLog() +{ + ASSERT(!m_logStartPtr); + m_logSize = 50000; + m_logStartPtr = new LogEntry[m_logSize]; + m_currentLogEntryPtr = m_logStartPtr; + m_logEndPtr = m_logStartPtr + m_logSize; +} + +TypeProfilerLog::~TypeProfilerLog() +{ + delete[] m_logStartPtr; +} + +void TypeProfilerLog::processLogEntries(const String& reason) +{ + double before = 0; + if (verbose) { + dataLog("Process caller:'", reason, "'"); + before = currentTimeMS(); + } + + LogEntry* entry = m_logStartPtr; + HashMap<Structure*, RefPtr<StructureShape>> seenShapes; + while (entry != m_currentLogEntryPtr) { + StructureID id = entry->structureID; + RefPtr<StructureShape> shape; + JSValue value = entry->value; + Structure* structure = nullptr; + if (id) { + structure = Heap::heap(value.asCell())->structureIDTable().get(id); + auto iter = seenShapes.find(structure); + if (iter == seenShapes.end()) { + shape = structure->toStructureShape(value); + seenShapes.set(structure, shape); + } else + shape = iter->value; + } + + RuntimeType type = runtimeTypeForValue(value); + TypeLocation* location = entry->location; + location->m_lastSeenType = type; + if (location->m_globalTypeSet) + location->m_globalTypeSet->addTypeInformation(type, shape, structure); + location->m_instructionTypeSet->addTypeInformation(type, shape, structure); + + entry++; + } + + m_currentLogEntryPtr = m_logStartPtr; + + if (verbose) { + double after = currentTimeMS(); + dataLogF(" Processing the log took: '%f' ms\n", after - before); + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/TypeProfilerLog.h b/Source/JavaScriptCore/runtime/TypeProfilerLog.h new file mode 100644 index 000000000..1dd188ab7 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeProfilerLog.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef TypeProfilerLog_h +#define TypeProfilerLog_h + +#include "JSCJSValue.h" +#include "Structure.h" +#include "TypeProfiler.h" + +namespace JSC { + +class TypeLocation; + +class TypeProfilerLog { + WTF_MAKE_FAST_ALLOCATED; +public: + struct LogEntry { + public: + friend class LLIntOffsetsExtractor; + + JSValue value; + TypeLocation* location; + StructureID structureID; + + static ptrdiff_t structureIDOffset() { return OBJECT_OFFSETOF(LogEntry, structureID); } + static ptrdiff_t valueOffset() { return OBJECT_OFFSETOF(LogEntry, value); } + static ptrdiff_t locationOffset() { return OBJECT_OFFSETOF(LogEntry, location); } + }; + + + TypeProfilerLog() + : m_logStartPtr(0) + { + initializeLog(); + } + + ~TypeProfilerLog(); + + JS_EXPORT_PRIVATE void processLogEntries(const String&); + LogEntry* logEndPtr() const { return m_logEndPtr; } + + static ptrdiff_t logStartOffset() { return OBJECT_OFFSETOF(TypeProfilerLog, m_logStartPtr); } + static ptrdiff_t currentLogEntryOffset() { return OBJECT_OFFSETOF(TypeProfilerLog, m_currentLogEntryPtr); } + +private: + friend class LLIntOffsetsExtractor; + + void initializeLog(); + + unsigned m_logSize; + LogEntry* m_logStartPtr; + LogEntry* m_currentLogEntryPtr; + LogEntry* m_logEndPtr; +}; + +} // namespace JSC + +#endif // TypeProfilerLog_h diff --git a/Source/JavaScriptCore/runtime/TypeSet.cpp b/Source/JavaScriptCore/runtime/TypeSet.cpp new file mode 100644 index 000000000..a436cda13 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeSet.cpp @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2014, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "TypeSet.h" + +#include "InspectorProtocolObjects.h" +#include "JSCJSValue.h" +#include "JSCJSValueInlines.h" +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/Vector.h> + +namespace JSC { + +TypeSet::TypeSet() + : m_seenTypes(TypeNothing) + , m_isOverflown(false) +{ +} + +void TypeSet::addTypeInformation(RuntimeType type, PassRefPtr<StructureShape> prpNewShape, Structure* structure) +{ + RefPtr<StructureShape> newShape = prpNewShape; + m_seenTypes = m_seenTypes | type; + + if (structure && newShape && !runtimeTypeIsPrimitive(type)) { + if (!m_structureSet.contains(structure)) { + m_structureSet.add(structure); + // Make one more pass making sure that: + // - We don't have two instances of the same shape. (Same shapes may have different Structures). + // - We don't have two shapes that share the same prototype chain. If these shapes share the same + // prototype chain, they will be merged into one shape. + bool found = false; + String hash = newShape->propertyHash(); + for (size_t i = 0; i < m_structureHistory.size(); i++) { + RefPtr<StructureShape>& seenShape = m_structureHistory.at(i); + if (seenShape->propertyHash() == hash) { + found = true; + break; + } + if (seenShape->hasSamePrototypeChain(newShape)) { + seenShape = StructureShape::merge(seenShape, newShape); + found = true; + break; + } + } + + if (!found) { + if (m_structureHistory.size() < 100) + m_structureHistory.append(newShape); + else if (!m_isOverflown) + m_isOverflown = true; + } + } + } +} + +void TypeSet::invalidateCache() +{ + auto keepMarkedStructuresFilter = [] (Structure* structure) -> bool { return Heap::isMarked(structure); }; + m_structureSet.genericFilter(keepMarkedStructuresFilter); +} + +String TypeSet::dumpTypes() const +{ + if (m_seenTypes == TypeNothing) + return ASCIILiteral("(Unreached Statement)"); + + StringBuilder seen; + + if (m_seenTypes & TypeFunction) + seen.appendLiteral("Function "); + if (m_seenTypes & TypeUndefined) + seen.appendLiteral("Undefined "); + if (m_seenTypes & TypeNull) + seen.appendLiteral("Null "); + if (m_seenTypes & TypeBoolean) + seen.appendLiteral("Boolean "); + if (m_seenTypes & TypeMachineInt) + seen.appendLiteral("MachineInt "); + if (m_seenTypes & TypeNumber) + seen.appendLiteral("Number "); + if (m_seenTypes & TypeString) + seen.appendLiteral("String "); + if (m_seenTypes & TypeObject) + seen.appendLiteral("Object "); + if (m_seenTypes & TypeSymbol) + seen.appendLiteral("Symbol "); + + for (size_t i = 0; i < m_structureHistory.size(); i++) { + RefPtr<StructureShape> shape = m_structureHistory.at(i); + seen.append(shape->m_constructorName); + seen.append(' '); + } + + if (m_structureHistory.size()) + seen.appendLiteral("\nStructures:[ "); + for (size_t i = 0; i < m_structureHistory.size(); i++) { + seen.append(m_structureHistory.at(i)->stringRepresentation()); + seen.append(' '); + } + if (m_structureHistory.size()) + seen.append(']'); + + if (m_structureHistory.size()) { + seen.appendLiteral("\nLeast Common Ancestor: "); + seen.append(leastCommonAncestor()); + } + + return seen.toString(); +} + +bool TypeSet::doesTypeConformTo(RuntimeTypeMask test) const +{ + // This function checks if our seen types conform to the types described by the test bitstring. (i.e we haven't seen more types than test). + // We are <= to those types if ANDing with the bitstring doesn't zero out any of our bits. + + // For example: + + // 0b0110 (seen) + // 0b1111 (test) + // ------ (AND) + // 0b0110 == seen + + // 0b0110 (seen) + // 0b0010 (test) + // ------ (AND) + // 0b0010 != seen + + return m_seenTypes != TypeNothing && (m_seenTypes & test) == m_seenTypes; +} + +String TypeSet::displayName() const +{ + if (m_seenTypes == TypeNothing) + return emptyString(); + + if (m_structureHistory.size() && doesTypeConformTo(TypeObject | TypeNull | TypeUndefined)) { + String ctorName = leastCommonAncestor(); + + if (doesTypeConformTo(TypeObject)) + return ctorName; + if (doesTypeConformTo(TypeObject | TypeNull | TypeUndefined)) + return ctorName + '?'; + } + + // The order of these checks are important. For example, if a value is only a function, it conforms to TypeFunction, but it also conforms to TypeFunction | TypeNull. + // Therefore, more specific types must be checked first. + + if (doesTypeConformTo(TypeFunction)) + return ASCIILiteral("Function"); + if (doesTypeConformTo(TypeUndefined)) + return ASCIILiteral("Undefined"); + if (doesTypeConformTo(TypeNull)) + return ASCIILiteral("Null"); + if (doesTypeConformTo(TypeBoolean)) + return ASCIILiteral("Boolean"); + if (doesTypeConformTo(TypeMachineInt)) + return ASCIILiteral("Integer"); + if (doesTypeConformTo(TypeNumber | TypeMachineInt)) + return ASCIILiteral("Number"); + if (doesTypeConformTo(TypeString)) + return ASCIILiteral("String"); + if (doesTypeConformTo(TypeSymbol)) + return ASCIILiteral("Symbol"); + + if (doesTypeConformTo(TypeNull | TypeUndefined)) + return ASCIILiteral("(?)"); + + if (doesTypeConformTo(TypeFunction | TypeNull | TypeUndefined)) + return ASCIILiteral("Function?"); + if (doesTypeConformTo(TypeBoolean | TypeNull | TypeUndefined)) + return ASCIILiteral("Boolean?"); + if (doesTypeConformTo(TypeMachineInt | TypeNull | TypeUndefined)) + return ASCIILiteral("Integer?"); + if (doesTypeConformTo(TypeNumber | TypeMachineInt | TypeNull | TypeUndefined)) + return ASCIILiteral("Number?"); + if (doesTypeConformTo(TypeString | TypeNull | TypeUndefined)) + return ASCIILiteral("String?"); + if (doesTypeConformTo(TypeSymbol | TypeNull | TypeUndefined)) + return ASCIILiteral("Symbol?"); + + if (doesTypeConformTo(TypeObject | TypeFunction | TypeString)) + return ASCIILiteral("Object"); + if (doesTypeConformTo(TypeObject | TypeFunction | TypeString | TypeNull | TypeUndefined)) + return ASCIILiteral("Object?"); + + return ASCIILiteral("(many)"); +} + +String TypeSet::leastCommonAncestor() const +{ + return StructureShape::leastCommonAncestor(m_structureHistory); +} + +Ref<Inspector::Protocol::Array<Inspector::Protocol::Runtime::StructureDescription>> TypeSet::allStructureRepresentations() const +{ + auto description = Inspector::Protocol::Array<Inspector::Protocol::Runtime::StructureDescription>::create(); + + for (size_t i = 0; i < m_structureHistory.size(); i++) + description->addItem(m_structureHistory.at(i)->inspectorRepresentation()); + + return WTF::move(description); +} + +Ref<Inspector::Protocol::Runtime::TypeSet> TypeSet::inspectorTypeSet() const +{ + return Inspector::Protocol::Runtime::TypeSet::create() + .setIsFunction((m_seenTypes & TypeFunction) != TypeNothing) + .setIsUndefined((m_seenTypes & TypeUndefined) != TypeNothing) + .setIsNull((m_seenTypes & TypeNull) != TypeNothing) + .setIsBoolean((m_seenTypes & TypeBoolean) != TypeNothing) + .setIsInteger((m_seenTypes & TypeMachineInt) != TypeNothing) + .setIsNumber((m_seenTypes & TypeNumber) != TypeNothing) + .setIsString((m_seenTypes & TypeString) != TypeNothing) + .setIsObject((m_seenTypes & TypeObject) != TypeNothing) + .setIsSymbol((m_seenTypes & TypeSymbol) != TypeNothing) + .release(); +} + +String TypeSet::toJSONString() const +{ + // This returns a JSON string representing an Object with the following properties: + // displayTypeName: 'String' + // primitiveTypeNames: 'Array<String>' + // structures: 'Array<JSON<StructureShape>>' + + StringBuilder json; + json.append('{'); + + json.appendLiteral("\"displayTypeName\":"); + json.append('"'); + json.append(displayName()); + json.append('"'); + json.append(','); + + json.appendLiteral("\"primitiveTypeNames\":"); + json.append('['); + bool hasAnItem = false; + if (m_seenTypes & TypeUndefined) { + hasAnItem = true; + json.appendLiteral("\"Undefined\""); + } + if (m_seenTypes & TypeNull) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + json.appendLiteral("\"Null\""); + } + if (m_seenTypes & TypeBoolean) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + json.appendLiteral("\"Boolean\""); + } + if (m_seenTypes & TypeMachineInt) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + json.appendLiteral("\"Integer\""); + } + if (m_seenTypes & TypeNumber) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + json.appendLiteral("\"Number\""); + } + if (m_seenTypes & TypeString) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + json.appendLiteral("\"String\""); + } + if (m_seenTypes & TypeSymbol) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + json.appendLiteral("\"Symbol\""); + } + json.append(']'); + + json.append(','); + + json.appendLiteral("\"structures\":"); + json.append('['); + hasAnItem = false; + for (size_t i = 0; i < m_structureHistory.size(); i++) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + json.append(m_structureHistory[i]->toJSONString()); + } + json.append(']'); + + json.append('}'); + return json.toString(); +} + +StructureShape::StructureShape() + : m_proto(nullptr) + , m_propertyHash(nullptr) + , m_final(false) + , m_isInDictionaryMode(false) +{ +} + +void StructureShape::markAsFinal() +{ + ASSERT(!m_final); + m_final = true; +} + +void StructureShape::addProperty(UniquedStringImpl& uid) +{ + ASSERT(!m_final); + m_fields.add(&uid); +} + +String StructureShape::propertyHash() +{ + ASSERT(m_final); + if (m_propertyHash) + return *m_propertyHash; + + StringBuilder builder; + builder.append(':'); + builder.append(m_constructorName); + builder.append(':'); + for (auto& key : m_fields) { + String property = key.get(); + property.replace(":", "\\:"); // Ensure that hash({"foo:", "bar"}) != hash({"foo", ":bar"}) because we're using colons as a separator and colons are legal characters in field names in JS. + builder.append(property); + } + + if (m_proto) { + builder.append(':'); + builder.appendLiteral("__proto__"); + builder.append(m_proto->propertyHash()); + } + + m_propertyHash = std::make_unique<String>(builder.toString()); + return *m_propertyHash; +} + +String StructureShape::leastCommonAncestor(const Vector<RefPtr<StructureShape>> shapes) +{ + if (!shapes.size()) + return emptyString(); + + RefPtr<StructureShape> origin = shapes.at(0); + for (size_t i = 1; i < shapes.size(); i++) { + bool foundLUB = false; + while (!foundLUB) { + RefPtr<StructureShape> check = shapes.at(i); + String curCtorName = origin->m_constructorName; + while (check) { + if (check->m_constructorName == curCtorName) { + foundLUB = true; + break; + } + check = check->m_proto; + } + if (!foundLUB) { + origin = origin->m_proto; + // All Objects must share the 'Object' Prototype. Therefore, at the very least, we should always converge on 'Object' before reaching a null prototype. + RELEASE_ASSERT(origin); + } + } + + if (origin->m_constructorName == "Object") + break; + } + + return origin->m_constructorName; +} + +String StructureShape::stringRepresentation() +{ + StringBuilder representation; + RefPtr<StructureShape> curShape = this; + + representation.append('{'); + while (curShape) { + for (auto it = curShape->m_fields.begin(), end = curShape->m_fields.end(); it != end; ++it) { + String prop((*it).get()); + representation.append(prop); + representation.appendLiteral(", "); + } + + if (curShape->m_proto) { + representation.appendLiteral("__proto__ ["); + representation.append(curShape->m_proto->m_constructorName); + representation.appendLiteral("], "); + } + + curShape = curShape->m_proto; + } + + if (representation.length() >= 3) + representation.resize(representation.length() - 2); + + representation.append('}'); + + return representation.toString(); +} + +String StructureShape::toJSONString() const +{ + // This returns a JSON string representing an Object with the following properties: + // constructorName: 'String' + // fields: 'Array<String>' + // optionalFields: 'Array<String>' + // proto: 'JSON<StructureShape> | null' + + StringBuilder json; + json.append('{'); + + json.appendLiteral("\"constructorName\":"); + json.append('"'); + json.append(m_constructorName); + json.append('"'); + json.append(','); + + json.appendLiteral("\"isInDictionaryMode\":"); + if (m_isInDictionaryMode) + json.appendLiteral("true"); + else + json.appendLiteral("false"); + json.append(','); + + json.appendLiteral("\"fields\":"); + json.append('['); + bool hasAnItem = false; + for (auto it = m_fields.begin(), end = m_fields.end(); it != end; ++it) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + + String fieldName((*it).get()); + json.append('"'); + json.append(fieldName); + json.append('"'); + } + json.append(']'); + json.append(','); + + json.appendLiteral("\"optionalFields\":"); + json.append('['); + hasAnItem = false; + for (auto it = m_optionalFields.begin(), end = m_optionalFields.end(); it != end; ++it) { + if (hasAnItem) + json.append(','); + hasAnItem = true; + + String fieldName((*it).get()); + json.append('"'); + json.append(fieldName); + json.append('"'); + } + json.append(']'); + json.append(','); + + json.appendLiteral("\"proto\":"); + if (m_proto) + json.append(m_proto->toJSONString()); + else + json.appendLiteral("null"); + + json.append('}'); + + return json.toString(); +} + +Ref<Inspector::Protocol::Runtime::StructureDescription> StructureShape::inspectorRepresentation() +{ + auto base = Inspector::Protocol::Runtime::StructureDescription::create().release(); + Ref<Inspector::Protocol::Runtime::StructureDescription> currentObject = base.copyRef(); + RefPtr<StructureShape> currentShape(this); + + while (currentShape) { + auto fields = Inspector::Protocol::Array<String>::create(); + auto optionalFields = Inspector::Protocol::Array<String>::create(); + for (auto field : currentShape->m_fields) + fields->addItem(field.get()); + for (auto field : currentShape->m_optionalFields) + optionalFields->addItem(field.get()); + + currentObject->setFields(&fields.get()); + currentObject->setOptionalFields(&optionalFields.get()); + currentObject->setConstructorName(currentShape->m_constructorName); + currentObject->setIsImprecise(currentShape->m_isInDictionaryMode); + + if (currentShape->m_proto) { + auto nextObject = Inspector::Protocol::Runtime::StructureDescription::create().release(); + currentObject->setPrototypeStructure(&nextObject.get()); + currentObject = WTF::move(nextObject); + } + + currentShape = currentShape->m_proto; + } + + return WTF::move(base); +} + +bool StructureShape::hasSamePrototypeChain(PassRefPtr<StructureShape> prpOther) +{ + RefPtr<StructureShape> self = this; + RefPtr<StructureShape> other = prpOther; + while (self && other) { + if (self->m_constructorName != other->m_constructorName) + return false; + self = self->m_proto; + other = other->m_proto; + } + + return !self && !other; +} + +PassRefPtr<StructureShape> StructureShape::merge(const PassRefPtr<StructureShape> prpA, const PassRefPtr<StructureShape> prpB) +{ + RefPtr<StructureShape> a = prpA; + RefPtr<StructureShape> b = prpB; + ASSERT(a->hasSamePrototypeChain(b)); + + RefPtr<StructureShape> merged = StructureShape::create(); + for (auto field : a->m_fields) { + if (b->m_fields.contains(field)) + merged->m_fields.add(field); + else + merged->m_optionalFields.add(field); + } + + for (auto field : b->m_fields) { + if (!merged->m_fields.contains(field)) { + auto addResult = merged->m_optionalFields.add(field); + ASSERT_UNUSED(addResult, addResult.isNewEntry); + } + } + + for (auto field : a->m_optionalFields) + merged->m_optionalFields.add(field); + for (auto field : b->m_optionalFields) + merged->m_optionalFields.add(field); + + ASSERT(a->m_constructorName == b->m_constructorName); + merged->setConstructorName(a->m_constructorName); + + if (a->m_proto) { + RELEASE_ASSERT(b->m_proto); + merged->setProto(StructureShape::merge(a->m_proto, b->m_proto)); + } + + merged->markAsFinal(); + + return merged.release(); +} + +void StructureShape::enterDictionaryMode() +{ + m_isInDictionaryMode = true; +} + +} //namespace JSC diff --git a/Source/JavaScriptCore/runtime/TypeSet.h b/Source/JavaScriptCore/runtime/TypeSet.h new file mode 100644 index 000000000..eb29e6c5f --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeSet.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypeSet_h +#define TypeSet_h + +#include "RuntimeType.h" +#include "StructureSet.h" +#include <wtf/HashSet.h> +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> +#include <wtf/Vector.h> + +namespace Inspector { +namespace Protocol { +template<typename T> class Array; + +namespace Runtime { +class StructureDescription; +class TypeSet; +} + +} +} + +namespace JSC { + +class StructureShape : public RefCounted<StructureShape> { + friend class TypeSet; + +public: + StructureShape(); + + static Ref<StructureShape> create() { return adoptRef(*new StructureShape); } + String propertyHash(); + void markAsFinal(); + void addProperty(UniquedStringImpl&); + String stringRepresentation(); + String toJSONString() const; + Ref<Inspector::Protocol::Runtime::StructureDescription> inspectorRepresentation(); + void setConstructorName(String name) { m_constructorName = (name.isEmpty() ? "Object" : name); } + String constructorName() { return m_constructorName; } + void setProto(PassRefPtr<StructureShape> shape) { m_proto = shape; } + void enterDictionaryMode(); + +private: + static String leastCommonAncestor(const Vector<RefPtr<StructureShape>>); + static PassRefPtr<StructureShape> merge(const PassRefPtr<StructureShape>, const PassRefPtr<StructureShape>); + bool hasSamePrototypeChain(PassRefPtr<StructureShape>); + + HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> m_fields; + HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> m_optionalFields; + RefPtr<StructureShape> m_proto; + std::unique_ptr<String> m_propertyHash; + String m_constructorName; + bool m_final; + bool m_isInDictionaryMode; +}; + +class TypeSet : public RefCounted<TypeSet> { + +public: + static Ref<TypeSet> create() { return adoptRef(*new TypeSet); } + TypeSet(); + void addTypeInformation(RuntimeType, PassRefPtr<StructureShape>, Structure*); + void invalidateCache(); + String dumpTypes() const; + String displayName() const; + Ref<Inspector::Protocol::Array<Inspector::Protocol::Runtime::StructureDescription>> allStructureRepresentations() const; + String toJSONString() const; + bool isOverflown() const { return m_isOverflown; } + String leastCommonAncestor() const; + Ref<Inspector::Protocol::Runtime::TypeSet> inspectorTypeSet() const; + bool isEmpty() const { return m_seenTypes == TypeNothing; } + bool doesTypeConformTo(RuntimeTypeMask test) const; + RuntimeTypeMask seenTypes() const { return m_seenTypes; } + StructureSet structureSet() const { return m_structureSet; }; + +private: + RuntimeTypeMask m_seenTypes; + bool m_isOverflown; + Vector<RefPtr<StructureShape>> m_structureHistory; + StructureSet m_structureSet; +}; + +} //namespace JSC + +#endif //TypeSet_h diff --git a/Source/JavaScriptCore/runtime/TypedArrayAdaptors.h b/Source/JavaScriptCore/runtime/TypedArrayAdaptors.h new file mode 100644 index 000000000..d4dc53557 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrayAdaptors.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypedArrayAdaptors_h +#define TypedArrayAdaptors_h + +#include "JSCJSValue.h" +#include "TypedArrayType.h" +#include <wtf/MathExtras.h> + +namespace JSC { + +template< + typename TypeArg, typename ViewTypeArg, typename JSViewTypeArg, + TypedArrayType typeValueArg> +struct IntegralTypedArrayAdaptor { + typedef TypeArg Type; + typedef ViewTypeArg ViewType; + typedef JSViewTypeArg JSViewType; + static const TypedArrayType typeValue = typeValueArg; + + static JSValue toJSValue(Type value) + { + return jsNumber(value); + } + + static double toDouble(Type value) + { + return static_cast<double>(value); + } + + static Type toNativeFromInt32(int32_t value) + { + return static_cast<Type>(value); + } + + static Type toNativeFromUint32(uint32_t value) + { + return static_cast<Type>(value); + } + + static Type toNativeFromDouble(double value) + { + int32_t result = static_cast<int32_t>(value); + if (static_cast<double>(result) != value) + result = toInt32(value); + return static_cast<Type>(result); + } + + template<typename OtherAdaptor> + static typename OtherAdaptor::Type convertTo(Type value) + { + if (typeValue == TypeUint32) + return OtherAdaptor::toNativeFromUint32(value); + return OtherAdaptor::toNativeFromInt32(value); + } +}; + +template< + typename TypeArg, typename ViewTypeArg, typename JSViewTypeArg, + TypedArrayType typeValueArg> +struct FloatTypedArrayAdaptor { + typedef TypeArg Type; + typedef ViewTypeArg ViewType; + typedef JSViewTypeArg JSViewType; + static const TypedArrayType typeValue = typeValueArg; + + static JSValue toJSValue(Type value) + { + return jsDoubleNumber(purifyNaN(value)); + } + + static double toDouble(Type value) + { + return static_cast<double>(value); + } + + static Type toNativeFromInt32(int32_t value) + { + return static_cast<Type>(value); + } + + static Type toNativeFromUint32(uint32_t value) + { + return static_cast<Type>(value); + } + + static Type toNativeFromDouble(double value) + { + return value; + } + + template<typename OtherAdaptor> + static typename OtherAdaptor::Type convertTo(Type value) + { + return OtherAdaptor::toNativeFromDouble(value); + } +}; + +struct Int8Adaptor; +struct Int16Adaptor; +struct Int32Adaptor; +struct Uint8Adaptor; +struct Uint8ClampedAdaptor; +struct Uint16Adaptor; +struct Uint32Adaptor; +struct Float32Adaptor; +struct Float64Adaptor; + +template<typename Adaptor> class GenericTypedArrayView; +typedef GenericTypedArrayView<Int8Adaptor> Int8Array; +typedef GenericTypedArrayView<Int16Adaptor> Int16Array; +typedef GenericTypedArrayView<Int32Adaptor> Int32Array; +typedef GenericTypedArrayView<Uint8Adaptor> Uint8Array; +typedef GenericTypedArrayView<Uint8ClampedAdaptor> Uint8ClampedArray; +typedef GenericTypedArrayView<Uint16Adaptor> Uint16Array; +typedef GenericTypedArrayView<Uint32Adaptor> Uint32Array; +typedef GenericTypedArrayView<Float32Adaptor> Float32Array; +typedef GenericTypedArrayView<Float64Adaptor> Float64Array; + +template<typename Adaptor> class JSGenericTypedArrayView; +typedef JSGenericTypedArrayView<Int8Adaptor> JSInt8Array; +typedef JSGenericTypedArrayView<Int16Adaptor> JSInt16Array; +typedef JSGenericTypedArrayView<Int32Adaptor> JSInt32Array; +typedef JSGenericTypedArrayView<Uint8Adaptor> JSUint8Array; +typedef JSGenericTypedArrayView<Uint8ClampedAdaptor> JSUint8ClampedArray; +typedef JSGenericTypedArrayView<Uint16Adaptor> JSUint16Array; +typedef JSGenericTypedArrayView<Uint32Adaptor> JSUint32Array; +typedef JSGenericTypedArrayView<Float32Adaptor> JSFloat32Array; +typedef JSGenericTypedArrayView<Float64Adaptor> JSFloat64Array; + +struct Int8Adaptor : IntegralTypedArrayAdaptor<int8_t, Int8Array, JSInt8Array, TypeInt8> { }; +struct Int16Adaptor : IntegralTypedArrayAdaptor<int16_t, Int16Array, JSInt16Array, TypeInt16> { }; +struct Int32Adaptor : IntegralTypedArrayAdaptor<int32_t, Int32Array, JSInt32Array, TypeInt32> { }; +struct Uint8Adaptor : IntegralTypedArrayAdaptor<uint8_t, Uint8Array, JSUint8Array, TypeUint8> { }; +struct Uint16Adaptor : IntegralTypedArrayAdaptor<uint16_t, Uint16Array, JSUint16Array, TypeUint16> { }; +struct Uint32Adaptor : IntegralTypedArrayAdaptor<uint32_t, Uint32Array, JSUint32Array, TypeUint32> { }; +struct Float32Adaptor : FloatTypedArrayAdaptor<float, Float32Array, JSFloat32Array, TypeFloat32> { }; +struct Float64Adaptor : FloatTypedArrayAdaptor<double, Float64Array, JSFloat64Array, TypeFloat64> { }; + +struct Uint8ClampedAdaptor { + typedef uint8_t Type; + typedef Uint8ClampedArray ViewType; + typedef JSUint8ClampedArray JSViewType; + static const TypedArrayType typeValue = TypeUint8Clamped; + + static JSValue toJSValue(uint8_t value) + { + return jsNumber(value); + } + + static double toDouble(uint8_t value) + { + return static_cast<double>(value); + } + + static Type toNativeFromInt32(int32_t value) + { + return clamp(value); + } + + static Type toNativeFromUint32(uint32_t value) + { + return std::min(static_cast<uint32_t>(255), value); + } + + static Type toNativeFromDouble(double value) + { + if (std::isnan(value) || value < 0) + return 0; + if (value > 255) + return 255; + return static_cast<uint8_t>(lrint(value)); + } + + template<typename OtherAdaptor> + static typename OtherAdaptor::Type convertTo(uint8_t value) + { + return OtherAdaptor::toNativeFromInt32(value); + } + +private: + static uint8_t clamp(int32_t value) + { + if (value < 0) + return 0; + if (value > 255) + return 255; + return static_cast<uint8_t>(value); + } +}; + +} // namespace JSC + +#endif // TypedArrayAdaptors_h + diff --git a/Source/JavaScriptCore/runtime/TypedArrayBase.h b/Source/JavaScriptCore/runtime/TypedArrayBase.h new file mode 100644 index 000000000..ee2746d78 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrayBase.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypedArrayBase_h +#define TypedArrayBase_h + +#include "ArrayBuffer.h" +#include "ArrayBufferView.h" + +namespace JSC { + +template <typename T> +class TypedArrayBase : public ArrayBufferView { +public: + T* data() const { return static_cast<T*>(baseAddress()); } + + bool set(TypedArrayBase<T>* array, unsigned offset) + { + return setImpl(array, offset * sizeof(T)); + } + + bool setRange(const T* data, size_t dataLength, unsigned offset) + { + return setRangeImpl(reinterpret_cast<const char*>(data), dataLength * sizeof(T), offset * sizeof(T)); + } + + bool zeroRange(unsigned offset, size_t length) + { + return zeroRangeImpl(offset * sizeof(T), length * sizeof(T)); + } + + // Overridden from ArrayBufferView. This must be public because of + // rules about inheritance of members in template classes, and + // because it is accessed via pointers to subclasses. + unsigned length() const + { + return m_length; + } + + virtual unsigned byteLength() const + { + return m_length * sizeof(T); + } + + // Invoked by the indexed getter. Does not perform range checks; caller + // is responsible for doing so and returning undefined as necessary. + T item(unsigned index) const + { + ASSERT_WITH_SECURITY_IMPLICATION(index < TypedArrayBase<T>::m_length); + return TypedArrayBase<T>::data()[index]; + } + + bool checkInboundData(unsigned offset, unsigned pos) const + { + return (offset <= m_length + && offset + pos <= m_length + // check overflow + && offset + pos >= offset); + } + +protected: + TypedArrayBase(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + : ArrayBufferView(buffer, byteOffset) + , m_length(length) + { + } + + template <class Subclass> + static PassRefPtr<Subclass> create(unsigned length) + { + RefPtr<ArrayBuffer> buffer = ArrayBuffer::create(length, sizeof(T)); + if (!buffer.get()) + return 0; + return create<Subclass>(buffer.release(), 0, length); + } + + template <class Subclass> + static PassRefPtr<Subclass> create(const T* array, unsigned length) + { + RefPtr<Subclass> a = create<Subclass>(length); + if (a) + for (unsigned i = 0; i < length; ++i) + a->set(i, array[i]); + return a.release(); + } + + template <class Subclass> + static RefPtr<Subclass> create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length) + { + RefPtr<ArrayBuffer> buf(buffer); + if (!verifySubRange<T>(buf, byteOffset, length)) + return nullptr; + + return adoptRef(new Subclass(buf.release(), byteOffset, length)); + } + + template <class Subclass> + static RefPtr<Subclass> createUninitialized(unsigned length) + { + RefPtr<ArrayBuffer> buffer = ArrayBuffer::createUninitialized(length, sizeof(T)); + if (!buffer.get()) + return nullptr; + return create<Subclass>(buffer.release(), 0, length); + } + + template <class Subclass> + RefPtr<Subclass> subarrayImpl(int start, int end) const + { + unsigned offset, length; + calculateOffsetAndLength(start, end, m_length, &offset, &length); + clampOffsetAndNumElements<T>(buffer(), m_byteOffset, &offset, &length); + return create<Subclass>(buffer(), offset, length); + } + + virtual void neuter() + { + ArrayBufferView::neuter(); + m_length = 0; + } + + // We do not want to have to access this via a virtual function in subclasses, + // which is why it is protected rather than private. + unsigned m_length; +}; + +} // namespace JSC + +using JSC::TypedArrayBase; + +#endif // TypedArrayBase_h diff --git a/Source/JavaScriptCore/runtime/TypedArrayController.cpp b/Source/JavaScriptCore/runtime/TypedArrayController.cpp new file mode 100644 index 000000000..e20b6cd15 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrayController.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "TypedArrayController.h" + +namespace JSC { + +TypedArrayController::TypedArrayController() { } +TypedArrayController::~TypedArrayController() { } + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/TypedArrayController.h b/Source/JavaScriptCore/runtime/TypedArrayController.h new file mode 100644 index 000000000..c8fde1c96 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrayController.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypedArrayController_h +#define TypedArrayController_h + +#include <wtf/RefCounted.h> + +namespace JSC { + +class ArrayBuffer; +class ExecState; +class JSArrayBuffer; +class JSGlobalObject; + +class TypedArrayController : public RefCounted<TypedArrayController> { +public: + JS_EXPORT_PRIVATE TypedArrayController(); + JS_EXPORT_PRIVATE virtual ~TypedArrayController(); + + virtual JSArrayBuffer* toJS(ExecState*, JSGlobalObject*, ArrayBuffer*) = 0; +}; + +} // namespace JSC + +#endif // TypedArrayController_h + diff --git a/Source/JavaScriptCore/runtime/TypedArrayInlines.h b/Source/JavaScriptCore/runtime/TypedArrayInlines.h new file mode 100644 index 000000000..7af1f9fb8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrayInlines.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypedArrayInlines_h +#define TypedArrayInlines_h + +#include "GCIncomingRefCountedInlines.h" +#include "GenericTypedArrayViewInlines.h" +#include "JSArrayBufferViewInlines.h" +#include "JSGenericTypedArrayViewConstructorInlines.h" +#include "JSGenericTypedArrayViewInlines.h" +#include "JSGenericTypedArrayViewPrototypeInlines.h" + +#endif // TypedArrayInlines_h + diff --git a/Source/JavaScriptCore/runtime/TypedArrayType.cpp b/Source/JavaScriptCore/runtime/TypedArrayType.cpp new file mode 100644 index 000000000..60875a209 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrayType.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "TypedArrayType.h" + +#include "JSDataView.h" +#include "JSTypedArrayConstructors.h" +#include "JSTypedArrays.h" + +namespace JSC { + +JSType typeForTypedArrayType(TypedArrayType type) +{ + switch (type) { + case NotTypedArray: + RELEASE_ASSERT_NOT_REACHED(); + return UnspecifiedType; + case TypeInt8: + return Int8ArrayType; + case TypeUint8: + return Uint8ArrayType; + case TypeUint8Clamped: + return Uint8ClampedArrayType; + case TypeInt16: + return Int16ArrayType; + case TypeUint16: + return Uint16ArrayType; + case TypeInt32: + return Int32ArrayType; + case TypeUint32: + return Uint32ArrayType; + case TypeFloat32: + return Float32ArrayType; + case TypeFloat64: + return Float64ArrayType; + case TypeDataView: + return DataViewType; + + default: + RELEASE_ASSERT_NOT_REACHED(); + return UnspecifiedType; + } +} + +const ClassInfo* constructorClassInfoForType(TypedArrayType type) +{ + switch (type) { + case NotTypedArray: + return 0; + case TypeInt8: + return JSInt8ArrayConstructor::info(); + case TypeUint8: + return JSUint8ArrayConstructor::info(); + case TypeUint8Clamped: + return JSUint8ClampedArrayConstructor::info(); + case TypeInt16: + return JSInt16ArrayConstructor::info(); + case TypeUint16: + return JSUint16ArrayConstructor::info(); + case TypeInt32: + return JSInt32ArrayConstructor::info(); + case TypeUint32: + return JSUint32ArrayConstructor::info(); + case TypeFloat32: + return JSFloat32ArrayConstructor::info(); + case TypeFloat64: + return JSFloat64ArrayConstructor::info(); + case TypeDataView: + return JSDataViewConstructor::info(); + } + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} + +} // namespace JSC + +namespace WTF { + +using namespace JSC; + +void printInternal(PrintStream& out, TypedArrayType type) +{ + switch (type) { + case NotTypedArray: + out.print("NotTypedArray"); + return; + case TypeInt8: + out.print("TypeInt8"); + return; + case TypeInt16: + out.print("TypeInt16"); + return; + case TypeInt32: + out.print("TypeInt32"); + return; + case TypeUint8: + out.print("TypeUint8"); + return; + case TypeUint8Clamped: + out.print("TypeUint8Clamped"); + return; + case TypeUint16: + out.print("TypeUint16"); + return; + case TypeUint32: + out.print("TypeUint32"); + return; + case TypeFloat32: + out.print("TypeFloat32"); + return; + case TypeFloat64: + out.print("TypeFloat64"); + return; + case TypeDataView: + out.print("TypeDataView"); + return; + } + + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace WTF + diff --git a/Source/JavaScriptCore/runtime/TypedArrayType.h b/Source/JavaScriptCore/runtime/TypedArrayType.h new file mode 100644 index 000000000..3a08eebf6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrayType.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypedArrayType_h +#define TypedArrayType_h + +#include "JSType.h" +#include <wtf/PrintStream.h> + +namespace JSC { + +struct ClassInfo; + +enum TypedArrayType { + NotTypedArray, + TypeInt8, + TypeUint8, + TypeUint8Clamped, + TypeInt16, + TypeUint16, + TypeInt32, + TypeUint32, + TypeFloat32, + TypeFloat64, + TypeDataView +}; + +#define NUMBER_OF_TYPED_ARRAY_TYPES TypeDataView + +inline unsigned toIndex(TypedArrayType type) +{ + return static_cast<unsigned>(type) - 1; +} + +inline TypedArrayType indexToTypedArrayType(unsigned index) +{ + TypedArrayType result = static_cast<TypedArrayType>(index + 1); + ASSERT(result >= TypeInt8 && result <= TypeDataView); + return result; +} + +inline bool isTypedView(TypedArrayType type) +{ + switch (type) { + case NotTypedArray: + case TypeDataView: + return false; + default: + return true; + } +} + +inline unsigned logElementSize(TypedArrayType type) +{ + switch (type) { + case NotTypedArray: + break; + case TypeInt8: + case TypeUint8: + case TypeUint8Clamped: + case TypeDataView: + return 0; + case TypeInt16: + case TypeUint16: + return 1; + case TypeInt32: + case TypeUint32: + case TypeFloat32: + return 2; + case TypeFloat64: + return 3; + } + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} + +inline size_t elementSize(TypedArrayType type) +{ + return static_cast<size_t>(1) << logElementSize(type); +} + +const ClassInfo* constructorClassInfoForType(TypedArrayType); +JSType typeForTypedArrayType(TypedArrayType); + +inline bool isInt(TypedArrayType type) +{ + switch (type) { + case TypeInt8: + case TypeUint8: + case TypeUint8Clamped: + case TypeInt16: + case TypeUint16: + case TypeInt32: + case TypeUint32: + return true; + default: + return false; + } +} + +inline bool isFloat(TypedArrayType type) +{ + switch (type) { + case TypeFloat32: + case TypeFloat64: + return true; + default: + return false; + } +} + +inline bool isSigned(TypedArrayType type) +{ + switch (type) { + case TypeInt8: + case TypeInt16: + case TypeInt32: + case TypeFloat32: + case TypeFloat64: + return true; + default: + return false; + } +} + +inline bool isClamped(TypedArrayType type) +{ + return type == TypeUint8Clamped; +} + +} // namespace JSC + +namespace WTF { + +void printInternal(PrintStream&, JSC::TypedArrayType); + +} // namespace WTF + +#endif // TypedArrayType_h + diff --git a/Source/JavaScriptCore/runtime/TypedArrays.h b/Source/JavaScriptCore/runtime/TypedArrays.h new file mode 100644 index 000000000..3007d8ab6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrays.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypedArrays_h +#define TypedArrays_h + +#include "GenericTypedArrayView.h" +#include "TypedArrayAdaptors.h" + +namespace JSC { + +typedef GenericTypedArrayView<Int8Adaptor> Int8Array; +typedef GenericTypedArrayView<Int16Adaptor> Int16Array; +typedef GenericTypedArrayView<Int32Adaptor> Int32Array; +typedef GenericTypedArrayView<Uint8Adaptor> Uint8Array; +typedef GenericTypedArrayView<Uint8ClampedAdaptor> Uint8ClampedArray; +typedef GenericTypedArrayView<Uint16Adaptor> Uint16Array; +typedef GenericTypedArrayView<Uint32Adaptor> Uint32Array; +typedef GenericTypedArrayView<Float32Adaptor> Float32Array; +typedef GenericTypedArrayView<Float64Adaptor> Float64Array; + +} // namespace JSC + +#endif // TypedArrays_h + diff --git a/Source/JavaScriptCore/runtime/TypeofType.cpp b/Source/JavaScriptCore/runtime/TypeofType.cpp new file mode 100644 index 000000000..db162b7f4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeofType.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "TypeofType.h" + +namespace WTF { + +using namespace JSC; + +void printInternal(PrintStream& out, TypeofType type) +{ + switch (type) { + case TypeofType::Undefined: + out.print("undefined"); + return; + case TypeofType::Boolean: + out.print("boolean"); + return; + case TypeofType::Number: + out.print("number"); + return; + case TypeofType::String: + out.print("string"); + return; + case TypeofType::Symbol: + out.print("symbol"); + return; + case TypeofType::Object: + out.print("object"); + return; + case TypeofType::Function: + out.print("function"); + return; + } + + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace WTF + diff --git a/Source/JavaScriptCore/runtime/TypeofType.h b/Source/JavaScriptCore/runtime/TypeofType.h new file mode 100644 index 000000000..3eb78d4f9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypeofType.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef TypeofType_h +#define TypeofType_h + +#include <wtf/PrintStream.h> + +namespace JSC { + +enum class TypeofType { + Undefined, + Boolean, + Number, + String, + Symbol, + Object, + Function +}; + +} // namespace JSC + +namespace WTF { + +void printInternal(PrintStream& out, JSC::TypeofType); + +} // namespace WTF + +#endif // TypeofType_h + diff --git a/Source/JavaScriptCore/runtime/Uint16Array.h b/Source/JavaScriptCore/runtime/Uint16Array.h new file mode 100644 index 000000000..68f82b8b8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Uint16Array.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Uint16Array_h +#define Uint16Array_h + +#include "TypedArrays.h" + +using JSC::Uint16Array; + +#endif // Uint16Array_h + diff --git a/Source/JavaScriptCore/runtime/Uint16WithFraction.h b/Source/JavaScriptCore/runtime/Uint16WithFraction.h new file mode 100644 index 000000000..9a30100eb --- /dev/null +++ b/Source/JavaScriptCore/runtime/Uint16WithFraction.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2011 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Uint16WithFraction_h +#define Uint16WithFraction_h + +#include <wtf/MathExtras.h> + +namespace JSC { + +// Would be nice if this was a static const member, but the OS X linker +// seems to want a symbol in the binary in that case... +#define oneGreaterThanMaxUInt16 0x10000 + +// A uint16_t with an infinite precision fraction. Upon overflowing +// the uint16_t range, this class will clamp to oneGreaterThanMaxUInt16. +// This is used in converting the fraction part of a number to a string. +class Uint16WithFraction { +public: + explicit Uint16WithFraction(double number, uint16_t divideByExponent = 0) + { + ASSERT(number && std::isfinite(number) && !std::signbit(number)); + + // Check for values out of uint16_t range. + if (number >= oneGreaterThanMaxUInt16) { + m_values.append(oneGreaterThanMaxUInt16); + m_leadingZeros = 0; + return; + } + + // Append the units to m_values. + double integerPart = floor(number); + m_values.append(static_cast<uint32_t>(integerPart)); + + bool sign; + int32_t exponent; + uint64_t mantissa; + decomposeDouble(number - integerPart, sign, exponent, mantissa); + ASSERT(!sign && exponent < 0); + exponent -= divideByExponent; + + int32_t zeroBits = -exponent; + --zeroBits; + + // Append the append words for to m_values. + while (zeroBits >= 32) { + m_values.append(0); + zeroBits -= 32; + } + + // Left align the 53 bits of the mantissa within 96 bits. + uint32_t values[3]; + values[0] = static_cast<uint32_t>(mantissa >> 21); + values[1] = static_cast<uint32_t>(mantissa << 11); + values[2] = 0; + // Shift based on the remainder of the exponent. + if (zeroBits) { + values[2] = values[1] << (32 - zeroBits); + values[1] = (values[1] >> zeroBits) | (values[0] << (32 - zeroBits)); + values[0] = (values[0] >> zeroBits); + } + m_values.append(values[0]); + m_values.append(values[1]); + m_values.append(values[2]); + + // Canonicalize; remove any trailing zeros. + while (m_values.size() > 1 && !m_values.last()) + m_values.removeLast(); + + // Count the number of leading zero, this is useful in optimizing multiplies. + m_leadingZeros = 0; + while (m_leadingZeros < m_values.size() && !m_values[m_leadingZeros]) + ++m_leadingZeros; + } + + Uint16WithFraction& operator*=(uint16_t multiplier) + { + ASSERT(checkConsistency()); + + // iteratate backwards over the fraction until we reach the leading zeros, + // passing the carry from one calculation into the next. + uint64_t accumulator = 0; + for (size_t i = m_values.size(); i > m_leadingZeros; ) { + --i; + accumulator += static_cast<uint64_t>(m_values[i]) * static_cast<uint64_t>(multiplier); + m_values[i] = static_cast<uint32_t>(accumulator); + accumulator >>= 32; + } + + if (!m_leadingZeros) { + // With a multiplicand and multiplier in the uint16_t range, this cannot carry + // (even allowing for the infinity value). + ASSERT(!accumulator); + // Check for overflow & clamp to 'infinity'. + if (m_values[0] >= oneGreaterThanMaxUInt16) { + m_values.shrink(1); + m_values[0] = oneGreaterThanMaxUInt16; + m_leadingZeros = 0; + return *this; + } + } else if (accumulator) { + // Check for carry from the last multiply, if so overwrite last leading zero. + m_values[--m_leadingZeros] = static_cast<uint32_t>(accumulator); + // The limited range of the multiplier should mean that even if we carry into + // the units, we don't need to check for overflow of the uint16_t range. + ASSERT(m_values[0] < oneGreaterThanMaxUInt16); + } + + // Multiplication by an even value may introduce trailing zeros; if so, clean them + // up. (Keeping the value in a normalized form makes some of the comparison operations + // more efficient). + while (m_values.size() > 1 && !m_values.last()) + m_values.removeLast(); + ASSERT(checkConsistency()); + return *this; + } + + bool operator<(const Uint16WithFraction& other) + { + ASSERT(checkConsistency()); + ASSERT(other.checkConsistency()); + + // Iterate over the common lengths of arrays. + size_t minSize = std::min(m_values.size(), other.m_values.size()); + for (size_t index = 0; index < minSize; ++index) { + // If we find a value that is not equal, compare and return. + uint32_t fromThis = m_values[index]; + uint32_t fromOther = other.m_values[index]; + if (fromThis != fromOther) + return fromThis < fromOther; + } + // If these numbers have the same lengths, they are equal, + // otherwise which ever number has a longer fraction in larger. + return other.m_values.size() > minSize; + } + + // Return the floor (non-fractional portion) of the number, clearing this to zero, + // leaving the fractional part unchanged. + uint32_t floorAndSubtract() + { + // 'floor' is simple the integer portion of the value. + uint32_t floor = m_values[0]; + + // If floor is non-zero, + if (floor) { + m_values[0] = 0; + m_leadingZeros = 1; + while (m_leadingZeros < m_values.size() && !m_values[m_leadingZeros]) + ++m_leadingZeros; + } + + return floor; + } + + // Compare this value to 0.5, returns -1 for less than, 0 for equal, 1 for greater. + int comparePoint5() + { + ASSERT(checkConsistency()); + // If units != 0, this is greater than 0.5. + if (m_values[0]) + return 1; + // If size == 1 this value is 0, hence < 0.5. + if (m_values.size() == 1) + return -1; + // Compare to 0.5. + if (m_values[1] > 0x80000000ul) + return 1; + if (m_values[1] < 0x80000000ul) + return -1; + // Check for more words - since normalized numbers have no trailing zeros, if + // there are more that two digits we can assume at least one more is non-zero, + // and hence the value is > 0.5. + return m_values.size() > 2 ? 1 : 0; + } + + // Return true if the sum of this plus addend would be greater than 1. + bool sumGreaterThanOne(const Uint16WithFraction& addend) + { + ASSERT(checkConsistency()); + ASSERT(addend.checkConsistency()); + + // First, sum the units. If the result is greater than one, return true. + // If equal to one, return true if either number has a fractional part. + uint32_t sum = m_values[0] + addend.m_values[0]; + if (sum) + return sum > 1 || std::max(m_values.size(), addend.m_values.size()) > 1; + + // We could still produce a result greater than zero if addition of the next + // word from the fraction were to carry, leaving a result > 0. + + // Iterate over the common lengths of arrays. + size_t minSize = std::min(m_values.size(), addend.m_values.size()); + for (size_t index = 1; index < minSize; ++index) { + // Sum the next word from this & the addend. + uint32_t fromThis = m_values[index]; + uint32_t fromAddend = addend.m_values[index]; + sum = fromThis + fromAddend; + + // Check for overflow. If so, check whether the remaining result is non-zero, + // or if there are any further words in the fraction. + if (sum < fromThis) + return sum || (index + 1) < std::max(m_values.size(), addend.m_values.size()); + + // If the sum is uint32_t max, then we would carry a 1 if addition of the next + // digits in the number were to overflow. + if (sum != 0xFFFFFFFF) + return false; + } + return false; + } + +private: + bool checkConsistency() const + { + // All values should have at least one value. + return (m_values.size()) + // The units value must be a uint16_t, or the value is the overflow value. + && (m_values[0] < oneGreaterThanMaxUInt16 || (m_values[0] == oneGreaterThanMaxUInt16 && m_values.size() == 1)) + // There should be no trailing zeros (unless this value is zero!). + && (m_values.last() || m_values.size() == 1); + } + + // The internal storage of the number. This vector is always at least one entry in size, + // with the first entry holding the portion of the number greater than zero. The first + // value always hold a value in the uint16_t range, or holds the value oneGreaterThanMaxUInt16 to + // indicate the value has overflowed to >= 0x10000. If the units value is oneGreaterThanMaxUInt16, + // there can be no fraction (size must be 1). + // + // Subsequent values in the array represent portions of the fractional part of this number. + // The total value of the number is the sum of (m_values[i] / pow(2^32, i)), for each i + // in the array. The vector should contain no trailing zeros, except for the value '0', + // represented by a vector contianing a single zero value. These constraints are checked + // by 'checkConsistency()', above. + // + // The inline capacity of the vector is set to be able to contain any IEEE double (1 for + // the units column, 32 for zeros introduced due to an exponent up to -3FE, and 2 for + // bits taken from the mantissa). + Vector<uint32_t, 36> m_values; + + // Cache a count of the number of leading zeros in m_values. We can use this to optimize + // methods that would otherwise need visit all words in the vector, e.g. multiplication. + size_t m_leadingZeros; +}; + +} + +#endif + diff --git a/Source/JavaScriptCore/runtime/Uint32Array.h b/Source/JavaScriptCore/runtime/Uint32Array.h new file mode 100644 index 000000000..0d7c98bf1 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Uint32Array.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Uint32Array_h +#define Uint32Array_h + +#include "TypedArrays.h" + +using JSC::Uint32Array; + +#endif // Uint32Array_h + diff --git a/Source/JavaScriptCore/runtime/Uint8Array.h b/Source/JavaScriptCore/runtime/Uint8Array.h new file mode 100644 index 000000000..1098db3e5 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Uint8Array.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Uint8Array_h +#define Uint8Array_h + +#include "TypedArrays.h" + +using JSC::Uint8Array; + +#endif // Uint8Array_h + diff --git a/Source/JavaScriptCore/runtime/Uint8ClampedArray.h b/Source/JavaScriptCore/runtime/Uint8ClampedArray.h new file mode 100644 index 000000000..a602593cf --- /dev/null +++ b/Source/JavaScriptCore/runtime/Uint8ClampedArray.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Uint8ClampedArray_h +#define Uint8ClampedArray_h + +#include "TypedArrays.h" + +using JSC::Uint8ClampedArray; + +#endif // Uint8ClampedArray_h + diff --git a/Source/JavaScriptCore/runtime/VM.cpp b/Source/JavaScriptCore/runtime/VM.cpp new file mode 100644 index 000000000..9724575cc --- /dev/null +++ b/Source/JavaScriptCore/runtime/VM.cpp @@ -0,0 +1,817 @@ +/* + * Copyright (C) 2008, 2011, 2013-2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "config.h" +#include "VM.h" + +#include "ArgList.h" +#include "ArityCheckFailReturnThunks.h" +#include "ArrayBufferNeuteringWatchpoint.h" +#include "BuiltinExecutables.h" +#include "CodeBlock.h" +#include "CodeCache.h" +#include "CommonIdentifiers.h" +#include "CommonSlowPaths.h" +#include "CustomGetterSetter.h" +#include "DFGLongLivedState.h" +#include "DFGWorklist.h" +#include "Disassembler.h" +#include "ErrorInstance.h" +#include "Exception.h" +#include "FTLThunks.h" +#include "FunctionConstructor.h" +#include "GCActivityCallback.h" +#include "GetterSetter.h" +#include "Heap.h" +#include "HeapIterationScope.h" +#include "HostCallReturnValue.h" +#include "Identifier.h" +#include "IncrementalSweeper.h" +#include "Interpreter.h" +#include "JITCode.h" +#include "JSAPIValueWrapper.h" +#include "JSArray.h" +#include "JSCInlines.h" +#include "JSFunction.h" +#include "JSGlobalObjectFunctions.h" +#include "JSLexicalEnvironment.h" +#include "JSLock.h" +#include "JSNotAnObject.h" +#include "JSPromiseDeferred.h" +#include "JSPropertyNameEnumerator.h" +#include "JSTemplateRegistryKey.h" +#include "JSWithScope.h" +#include "Lexer.h" +#include "Lookup.h" +#include "MapData.h" +#include "Nodes.h" +#include "Parser.h" +#include "ProfilerDatabase.h" +#include "PropertyMapHashTable.h" +#include "RegExpCache.h" +#include "RegExpObject.h" +#include "RuntimeType.h" +#include "SimpleTypedArrayController.h" +#include "SourceProviderCache.h" +#include "StackVisitor.h" +#include "StrictEvalActivation.h" +#include "StrongInlines.h" +#include "StructureInlines.h" +#include "TypeProfiler.h" +#include "TypeProfilerLog.h" +#include "UnlinkedCodeBlock.h" +#include "Watchdog.h" +#include "WeakGCMapInlines.h" +#include "WeakMapData.h" +#include <wtf/CurrentTime.h> +#include <wtf/ProcessID.h> +#include <wtf/RetainPtr.h> +#include <wtf/StringPrintStream.h> +#include <wtf/Threading.h> +#include <wtf/WTFThreadData.h> +#include <wtf/text/AtomicStringTable.h> +#include <wtf/text/SymbolRegistry.h> + +#if ENABLE(DFG_JIT) +#include "ConservativeRoots.h" +#endif + +#if ENABLE(REGEXP_TRACING) +#include "RegExp.h" +#endif + +#if USE(CF) +#include <CoreFoundation/CoreFoundation.h> +#endif + +using namespace WTF; + +namespace JSC { + +// Note: Platform.h will enforce that ENABLE(ASSEMBLER) is true if either +// ENABLE(JIT) or ENABLE(YARR_JIT) or both are enabled. The code below +// just checks for ENABLE(JIT) or ENABLE(YARR_JIT) with this premise in mind. + +#if ENABLE(ASSEMBLER) +static bool enableAssembler(ExecutableAllocator& executableAllocator) +{ + if (!Options::useJIT() && !Options::useRegExpJIT()) + return false; + + if (!executableAllocator.isValid()) { + if (Options::crashIfCantAllocateJITMemory()) + CRASH(); + return false; + } + +#if USE(CF) || OS(UNIX) + char* canUseJITString = getenv("JavaScriptCoreUseJIT"); + return !canUseJITString || atoi(canUseJITString); +#else + return true; +#endif +} +#endif // ENABLE(!ASSEMBLER) + +VM::VM(VMType vmType, HeapType heapType) + : m_apiLock(adoptRef(new JSLock(this))) +#if ENABLE(ASSEMBLER) + , executableAllocator(*this) +#endif + , heap(this, heapType) + , vmType(vmType) + , clientData(0) + , topVMEntryFrame(nullptr) + , topCallFrame(CallFrame::noCaller()) + , m_atomicStringTable(vmType == Default ? wtfThreadData().atomicStringTable() : new AtomicStringTable) + , propertyNames(nullptr) + , emptyList(new MarkedArgumentBuffer) + , stringCache(*this) + , prototypeMap(*this) + , interpreter(0) + , jsArrayClassInfo(JSArray::info()) + , jsFinalObjectClassInfo(JSFinalObject::info()) + , sizeOfLastScratchBuffer(0) + , entryScope(0) + , m_regExpCache(new RegExpCache(this)) +#if ENABLE(REGEXP_TRACING) + , m_rtTraceList(new RTTraceList()) +#endif + , m_newStringsSinceLastHashCons(0) +#if ENABLE(ASSEMBLER) + , m_canUseAssembler(enableAssembler(executableAllocator)) +#endif +#if ENABLE(JIT) + , m_canUseJIT(m_canUseAssembler && Options::useJIT()) +#endif +#if ENABLE(YARR_JIT) + , m_canUseRegExpJIT(m_canUseAssembler && Options::useRegExpJIT()) +#endif +#if ENABLE(GC_VALIDATION) + , m_initializingObjectClass(0) +#endif + , m_stackPointerAtVMEntry(0) + , m_stackLimit(0) +#if !ENABLE(JIT) + , m_jsStackLimit(0) +#endif +#if ENABLE(FTL_JIT) + , m_ftlStackLimit(0) + , m_largestFTLStackSize(0) +#endif + , m_inDefineOwnProperty(false) + , m_codeCache(std::make_unique<CodeCache>()) + , m_enabledProfiler(nullptr) + , m_builtinExecutables(std::make_unique<BuiltinExecutables>(*this)) + , m_typeProfilerEnabledCount(0) + , m_controlFlowProfilerEnabledCount(0) +{ + interpreter = new Interpreter(*this); + StackBounds stack = wtfThreadData().stack(); + updateReservedZoneSize(Options::reservedZoneSize()); +#if !ENABLE(JIT) + interpreter->stack().setReservedZoneSize(Options::reservedZoneSize()); +#endif + setLastStackTop(stack.origin()); + + // Need to be careful to keep everything consistent here + JSLockHolder lock(this); + AtomicStringTable* existingEntryAtomicStringTable = wtfThreadData().setCurrentAtomicStringTable(m_atomicStringTable); + propertyNames = new CommonIdentifiers(this); + structureStructure.set(*this, Structure::createStructure(*this)); + structureRareDataStructure.set(*this, StructureRareData::createStructure(*this, 0, jsNull())); + terminatedExecutionErrorStructure.set(*this, TerminatedExecutionError::createStructure(*this, 0, jsNull())); + stringStructure.set(*this, JSString::createStructure(*this, 0, jsNull())); + notAnObjectStructure.set(*this, JSNotAnObject::createStructure(*this, 0, jsNull())); + propertyNameEnumeratorStructure.set(*this, JSPropertyNameEnumerator::createStructure(*this, 0, jsNull())); + getterSetterStructure.set(*this, GetterSetter::createStructure(*this, 0, jsNull())); + customGetterSetterStructure.set(*this, CustomGetterSetter::createStructure(*this, 0, jsNull())); + scopedArgumentsTableStructure.set(*this, ScopedArgumentsTable::createStructure(*this, 0, jsNull())); + apiWrapperStructure.set(*this, JSAPIValueWrapper::createStructure(*this, 0, jsNull())); + JSScopeStructure.set(*this, JSScope::createStructure(*this, 0, jsNull())); + executableStructure.set(*this, ExecutableBase::createStructure(*this, 0, jsNull())); + nativeExecutableStructure.set(*this, NativeExecutable::createStructure(*this, 0, jsNull())); + evalExecutableStructure.set(*this, EvalExecutable::createStructure(*this, 0, jsNull())); + programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, 0, jsNull())); + functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, 0, jsNull())); + regExpStructure.set(*this, RegExp::createStructure(*this, 0, jsNull())); + symbolStructure.set(*this, Symbol::createStructure(*this, 0, jsNull())); + symbolTableStructure.set(*this, SymbolTable::createStructure(*this, 0, jsNull())); + structureChainStructure.set(*this, StructureChain::createStructure(*this, 0, jsNull())); + sparseArrayValueMapStructure.set(*this, SparseArrayValueMap::createStructure(*this, 0, jsNull())); + templateRegistryKeyStructure.set(*this, JSTemplateRegistryKey::createStructure(*this, 0, jsNull())); + arrayBufferNeuteringWatchpointStructure.set(*this, ArrayBufferNeuteringWatchpoint::createStructure(*this)); + unlinkedFunctionExecutableStructure.set(*this, UnlinkedFunctionExecutable::createStructure(*this, 0, jsNull())); + unlinkedProgramCodeBlockStructure.set(*this, UnlinkedProgramCodeBlock::createStructure(*this, 0, jsNull())); + unlinkedEvalCodeBlockStructure.set(*this, UnlinkedEvalCodeBlock::createStructure(*this, 0, jsNull())); + unlinkedFunctionCodeBlockStructure.set(*this, UnlinkedFunctionCodeBlock::createStructure(*this, 0, jsNull())); + propertyTableStructure.set(*this, PropertyTable::createStructure(*this, 0, jsNull())); + weakMapDataStructure.set(*this, WeakMapData::createStructure(*this, 0, jsNull())); + inferredValueStructure.set(*this, InferredValue::createStructure(*this, 0, jsNull())); + functionRareDataStructure.set(*this, FunctionRareData::createStructure(*this, 0, jsNull())); + exceptionStructure.set(*this, Exception::createStructure(*this, 0, jsNull())); + promiseDeferredStructure.set(*this, JSPromiseDeferred::createStructure(*this, 0, jsNull())); + iterationTerminator.set(*this, JSFinalObject::create(*this, JSFinalObject::createStructure(*this, 0, jsNull(), 1))); + smallStrings.initializeCommonStrings(*this); + + wtfThreadData().setCurrentAtomicStringTable(existingEntryAtomicStringTable); + +#if ENABLE(JIT) + jitStubs = std::make_unique<JITThunks>(); + arityCheckFailReturnThunks = std::make_unique<ArityCheckFailReturnThunks>(); +#endif + arityCheckData = std::make_unique<CommonSlowPaths::ArityCheckData>(); + +#if ENABLE(FTL_JIT) + ftlThunks = std::make_unique<FTL::Thunks>(); +#endif // ENABLE(FTL_JIT) + + interpreter->initialize(this->canUseJIT()); + +#if ENABLE(JIT) + initializeHostCallReturnValue(); // This is needed to convince the linker not to drop host call return support. +#endif + + heap.notifyIsSafeToCollect(); + + LLInt::Data::performAssertions(*this); + + if (Options::enableProfiler()) { + m_perBytecodeProfiler = std::make_unique<Profiler::Database>(*this); + + StringPrintStream pathOut; + const char* profilerPath = getenv("JSC_PROFILER_PATH"); + if (profilerPath) + pathOut.print(profilerPath, "/"); + pathOut.print("JSCProfile-", getCurrentProcessID(), "-", m_perBytecodeProfiler->databaseID(), ".json"); + m_perBytecodeProfiler->registerToSaveAtExit(pathOut.toCString().data()); + } + +#if ENABLE(DFG_JIT) + if (canUseJIT()) + dfgState = std::make_unique<DFG::LongLivedState>(); +#endif + + // Initialize this last, as a free way of asserting that VM initialization itself + // won't use this. + m_typedArrayController = adoptRef(new SimpleTypedArrayController()); + + if (Options::enableTypeProfiler()) + enableTypeProfiler(); + if (Options::enableControlFlowProfiler()) + enableControlFlowProfiler(); + + if (Options::watchdog()) { + std::chrono::milliseconds timeoutMillis(Options::watchdog()); + Watchdog& watchdog = ensureWatchdog(); + watchdog.setTimeLimit(timeoutMillis); + } +} + +VM::~VM() +{ + // Never GC, ever again. + heap.incrementDeferralDepth(); + +#if ENABLE(DFG_JIT) + // Make sure concurrent compilations are done, but don't install them, since there is + // no point to doing so. + for (unsigned i = DFG::numberOfWorklists(); i--;) { + if (DFG::Worklist* worklist = DFG::worklistForIndexOrNull(i)) { + worklist->waitUntilAllPlansForVMAreReady(*this); + worklist->removeAllReadyPlansForVM(*this); + } + } +#endif // ENABLE(DFG_JIT) + + waitForAsynchronousDisassembly(); + + // Clear this first to ensure that nobody tries to remove themselves from it. + m_perBytecodeProfiler = nullptr; + + ASSERT(m_apiLock->currentThreadIsHoldingLock()); + m_apiLock->willDestroyVM(this); + heap.lastChanceToFinalize(); + + delete interpreter; +#ifndef NDEBUG + interpreter = reinterpret_cast<Interpreter*>(0xbbadbeef); +#endif + + delete emptyList; + + delete propertyNames; + if (vmType != Default) + delete m_atomicStringTable; + + delete clientData; + delete m_regExpCache; +#if ENABLE(REGEXP_TRACING) + delete m_rtTraceList; +#endif + +#if ENABLE(DFG_JIT) + for (unsigned i = 0; i < scratchBuffers.size(); ++i) + fastFree(scratchBuffers[i]); +#endif +} + +Ref<VM> VM::createContextGroup(HeapType heapType) +{ + return adoptRef(*new VM(APIContextGroup, heapType)); +} + +Ref<VM> VM::create(HeapType heapType) +{ + return adoptRef(*new VM(Default, heapType)); +} + +Ref<VM> VM::createLeaked(HeapType heapType) +{ + return create(heapType); +} + +bool VM::sharedInstanceExists() +{ + return sharedInstanceInternal(); +} + +VM& VM::sharedInstance() +{ + GlobalJSLock globalLock; + VM*& instance = sharedInstanceInternal(); + if (!instance) + instance = adoptRef(new VM(APIShared, SmallHeap)).leakRef(); + return *instance; +} + +VM*& VM::sharedInstanceInternal() +{ + static VM* sharedInstance; + return sharedInstance; +} + +Watchdog& VM::ensureWatchdog() +{ + if (!watchdog) { + watchdog = adoptRef(new Watchdog()); + + // The LLINT peeks into the Watchdog object directly. In order to do that, + // the LLINT assumes that the internal shape of a std::unique_ptr is the + // same as a plain C++ pointer, and loads the address of Watchdog from it. + RELEASE_ASSERT(*reinterpret_cast<Watchdog**>(&watchdog) == watchdog.get()); + + // And if we've previously compiled any functions, we need to revert + // them because they don't have the needed polling checks for the watchdog + // yet. + deleteAllCode(); + } + return *watchdog; +} + +#if ENABLE(JIT) +static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic) +{ + switch (intrinsic) { + case CharCodeAtIntrinsic: + return charCodeAtThunkGenerator; + case CharAtIntrinsic: + return charAtThunkGenerator; + case Clz32Intrinsic: + return clz32ThunkGenerator; + case FromCharCodeIntrinsic: + return fromCharCodeThunkGenerator; + case SqrtIntrinsic: + return sqrtThunkGenerator; + case PowIntrinsic: + return powThunkGenerator; + case AbsIntrinsic: + return absThunkGenerator; + case FloorIntrinsic: + return floorThunkGenerator; + case CeilIntrinsic: + return ceilThunkGenerator; + case RoundIntrinsic: + return roundThunkGenerator; + case ExpIntrinsic: + return expThunkGenerator; + case LogIntrinsic: + return logThunkGenerator; + case IMulIntrinsic: + return imulThunkGenerator; + default: + return 0; + } +} + +NativeExecutable* VM::getHostFunction(NativeFunction function, NativeFunction constructor) +{ + return jitStubs->hostFunctionStub(this, function, constructor); +} +NativeExecutable* VM::getHostFunction(NativeFunction function, Intrinsic intrinsic) +{ + ASSERT(canUseJIT()); + return jitStubs->hostFunctionStub(this, function, intrinsic != NoIntrinsic ? thunkGeneratorForIntrinsic(intrinsic) : 0, intrinsic); +} + +#else // !ENABLE(JIT) + +NativeExecutable* VM::getHostFunction(NativeFunction function, NativeFunction constructor) +{ + return NativeExecutable::create(*this, + adoptRef(new NativeJITCode(MacroAssemblerCodeRef::createLLIntCodeRef(llint_native_call_trampoline), JITCode::HostCallThunk)), function, + adoptRef(new NativeJITCode(MacroAssemblerCodeRef::createLLIntCodeRef(llint_native_construct_trampoline), JITCode::HostCallThunk)), constructor, + NoIntrinsic); +} + +#endif // !ENABLE(JIT) + +VM::ClientData::~ClientData() +{ +} + +void VM::resetDateCache() +{ + localTimeOffsetCache.reset(); + cachedDateString = String(); + cachedDateStringValue = std::numeric_limits<double>::quiet_NaN(); + dateInstanceCache.reset(); +} + +void VM::startSampling() +{ + interpreter->startSampling(); +} + +void VM::stopSampling() +{ + interpreter->stopSampling(); +} + +void VM::prepareToDeleteCode() +{ +#if ENABLE(DFG_JIT) + for (unsigned i = DFG::numberOfWorklists(); i--;) { + if (DFG::Worklist* worklist = DFG::worklistForIndexOrNull(i)) + worklist->completeAllPlansForVM(*this); + } +#endif // ENABLE(DFG_JIT) +} + +void VM::deleteAllCode() +{ + prepareToDeleteCode(); + m_codeCache->clear(); + m_regExpCache->deleteAllCode(); + heap.deleteAllCompiledCode(); + heap.deleteAllUnlinkedFunctionCode(); + heap.reportAbandonedObjectGraph(); +} + +void VM::dumpSampleData(ExecState* exec) +{ + interpreter->dumpSampleData(exec); +#if ENABLE(ASSEMBLER) + ExecutableAllocator::dumpProfile(); +#endif +} + +SourceProviderCache* VM::addSourceProviderCache(SourceProvider* sourceProvider) +{ + auto addResult = sourceProviderCacheMap.add(sourceProvider, nullptr); + if (addResult.isNewEntry) + addResult.iterator->value = adoptRef(new SourceProviderCache); + return addResult.iterator->value.get(); +} + +void VM::clearSourceProviderCaches() +{ + sourceProviderCacheMap.clear(); +} + +void VM::throwException(ExecState* exec, Exception* exception) +{ + if (Options::breakOnThrow()) { + dataLog("In call frame ", RawPointer(exec), " for code block ", *exec->codeBlock(), "\n"); + CRASH(); + } + + ASSERT(exec == topCallFrame || exec == exec->lexicalGlobalObject()->globalExec() || exec == exec->vmEntryGlobalObject()->globalExec()); + setException(exception); +} + +JSValue VM::throwException(ExecState* exec, JSValue thrownValue) +{ + Exception* exception = jsDynamicCast<Exception*>(thrownValue); + if (!exception) + exception = Exception::create(*this, thrownValue); + + throwException(exec, exception); + return JSValue(exception); +} + +JSObject* VM::throwException(ExecState* exec, JSObject* error) +{ + return asObject(throwException(exec, JSValue(error))); +} + +void VM::setStackPointerAtVMEntry(void* sp) +{ + m_stackPointerAtVMEntry = sp; + updateStackLimit(); +} + +size_t VM::updateReservedZoneSize(size_t reservedZoneSize) +{ + size_t oldReservedZoneSize = m_reservedZoneSize; + m_reservedZoneSize = reservedZoneSize; + + updateStackLimit(); + + return oldReservedZoneSize; +} + +#if PLATFORM(WIN) +// On Windows the reserved stack space consists of committed memory, a guard page, and uncommitted memory, +// where the guard page is a barrier between committed and uncommitted memory. +// When data from the guard page is read or written, the guard page is moved, and memory is committed. +// This is how the system grows the stack. +// When using the C stack on Windows we need to precommit the needed stack space. +// Otherwise we might crash later if we access uncommitted stack memory. +// This can happen if we allocate stack space larger than the page guard size (4K). +// The system does not get the chance to move the guard page, and commit more memory, +// and we crash if uncommitted memory is accessed. +// The MSVC compiler fixes this by inserting a call to the _chkstk() function, +// when needed, see http://support.microsoft.com/kb/100775. +// By touching every page up to the stack limit with a dummy operation, +// we force the system to move the guard page, and commit memory. + +static void preCommitStackMemory(void* stackLimit) +{ + const int pageSize = 4096; + for (volatile char* p = reinterpret_cast<char*>(&stackLimit); p > stackLimit; p -= pageSize) { + char ch = *p; + *p = ch; + } +} +#endif + +inline void VM::updateStackLimit() +{ +#if PLATFORM(WIN) + void* lastStackLimit = m_stackLimit; +#endif + + if (m_stackPointerAtVMEntry) { + ASSERT(wtfThreadData().stack().isGrowingDownward()); + char* startOfStack = reinterpret_cast<char*>(m_stackPointerAtVMEntry); +#if ENABLE(FTL_JIT) + m_stackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize + m_largestFTLStackSize); + m_ftlStackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize + 2 * m_largestFTLStackSize); +#else + m_stackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize); +#endif + } else { +#if ENABLE(FTL_JIT) + m_stackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize + m_largestFTLStackSize); + m_ftlStackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize + 2 * m_largestFTLStackSize); +#else + m_stackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize); +#endif + } + +#if PLATFORM(WIN) + if (lastStackLimit != m_stackLimit) + preCommitStackMemory(m_stackLimit); +#endif +} + +#if ENABLE(FTL_JIT) +void VM::updateFTLLargestStackSize(size_t stackSize) +{ + if (stackSize > m_largestFTLStackSize) { + m_largestFTLStackSize = stackSize; + updateStackLimit(); + } +} +#endif + +#if ENABLE(DFG_JIT) +void VM::gatherConservativeRoots(ConservativeRoots& conservativeRoots) +{ + for (size_t i = 0; i < scratchBuffers.size(); i++) { + ScratchBuffer* scratchBuffer = scratchBuffers[i]; + if (scratchBuffer->activeLength()) { + void* bufferStart = scratchBuffer->dataBuffer(); + conservativeRoots.add(bufferStart, static_cast<void*>(static_cast<char*>(bufferStart) + scratchBuffer->activeLength())); + } + } +} +#endif + +void logSanitizeStack(VM* vm) +{ + if (Options::verboseSanitizeStack() && vm->topCallFrame) { + int dummy; + dataLog( + "Sanitizing stack with top call frame at ", RawPointer(vm->topCallFrame), + ", current stack pointer at ", RawPointer(&dummy), ", in ", + pointerDump(vm->topCallFrame->codeBlock()), " and last code origin = ", + vm->topCallFrame->codeOrigin(), "\n"); + } +} + +#if ENABLE(REGEXP_TRACING) +void VM::addRegExpToTrace(RegExp* regExp) +{ + gcProtect(regExp); + m_rtTraceList->add(regExp); +} + +void VM::dumpRegExpTrace() +{ + // The first RegExp object is ignored. It is create by the RegExpPrototype ctor and not used. + RTTraceList::iterator iter = ++m_rtTraceList->begin(); + + if (iter != m_rtTraceList->end()) { + dataLogF("\nRegExp Tracing\n"); + dataLogF("Regular Expression 8 Bit 16 Bit match() Matches Average\n"); + dataLogF(" <Match only / Match> JIT Addr JIT Address calls found String len\n"); + dataLogF("----------------------------------------+----------------+----------------+----------+----------+-----------\n"); + + unsigned reCount = 0; + + for (; iter != m_rtTraceList->end(); ++iter, ++reCount) { + (*iter)->printTraceData(); + gcUnprotect(*iter); + } + + dataLogF("%d Regular Expressions\n", reCount); + } + + m_rtTraceList->clear(); +} +#else +void VM::dumpRegExpTrace() +{ +} +#endif + +void VM::registerWatchpointForImpureProperty(const Identifier& propertyName, Watchpoint* watchpoint) +{ + auto result = m_impurePropertyWatchpointSets.add(propertyName.string(), nullptr); + if (result.isNewEntry) + result.iterator->value = adoptRef(new WatchpointSet(IsWatched)); + result.iterator->value->add(watchpoint); +} + +void VM::addImpureProperty(const String& propertyName) +{ + if (RefPtr<WatchpointSet> watchpointSet = m_impurePropertyWatchpointSets.take(propertyName)) + watchpointSet->fireAll("Impure property added"); +} + +class SetEnabledProfilerFunctor { +public: + bool operator()(CodeBlock* codeBlock) + { + if (JITCode::isOptimizingJIT(codeBlock->jitType())) + codeBlock->jettison(Profiler::JettisonDueToLegacyProfiler); + return false; + } +}; + +void VM::setEnabledProfiler(LegacyProfiler* profiler) +{ + m_enabledProfiler = profiler; + if (m_enabledProfiler) { + prepareToDeleteCode(); + SetEnabledProfilerFunctor functor; + heap.forEachCodeBlock(functor); + } +} + +static bool enableProfilerWithRespectToCount(unsigned& counter, std::function<void()> doEnableWork) +{ + bool needsToRecompile = false; + if (!counter) { + doEnableWork(); + needsToRecompile = true; + } + counter++; + + return needsToRecompile; +} + +static bool disableProfilerWithRespectToCount(unsigned& counter, std::function<void()> doDisableWork) +{ + RELEASE_ASSERT(counter > 0); + bool needsToRecompile = false; + counter--; + if (!counter) { + doDisableWork(); + needsToRecompile = true; + } + + return needsToRecompile; +} + +bool VM::enableTypeProfiler() +{ + auto enableTypeProfiler = [this] () { + this->m_typeProfiler = std::make_unique<TypeProfiler>(); + this->m_typeProfilerLog = std::make_unique<TypeProfilerLog>(); + }; + + return enableProfilerWithRespectToCount(m_typeProfilerEnabledCount, enableTypeProfiler); +} + +bool VM::disableTypeProfiler() +{ + auto disableTypeProfiler = [this] () { + this->m_typeProfiler.reset(nullptr); + this->m_typeProfilerLog.reset(nullptr); + }; + + return disableProfilerWithRespectToCount(m_typeProfilerEnabledCount, disableTypeProfiler); +} + +bool VM::enableControlFlowProfiler() +{ + auto enableControlFlowProfiler = [this] () { + this->m_controlFlowProfiler = std::make_unique<ControlFlowProfiler>(); + }; + + return enableProfilerWithRespectToCount(m_controlFlowProfilerEnabledCount, enableControlFlowProfiler); +} + +bool VM::disableControlFlowProfiler() +{ + auto disableControlFlowProfiler = [this] () { + this->m_controlFlowProfiler.reset(nullptr); + }; + + return disableProfilerWithRespectToCount(m_controlFlowProfilerEnabledCount, disableControlFlowProfiler); +} + +void VM::dumpTypeProfilerData() +{ + if (!typeProfiler()) + return; + + typeProfilerLog()->processLogEntries(ASCIILiteral("VM Dump Types")); + typeProfiler()->dumpTypeProfilerData(*this); +} + +void VM::queueMicrotask(JSGlobalObject* globalObject, PassRefPtr<Microtask> task) +{ + m_microtaskQueue.append(std::make_unique<QueuedTask>(*this, globalObject, task)); +} + +void VM::drainMicrotasks() +{ + while (!m_microtaskQueue.isEmpty()) + m_microtaskQueue.takeFirst()->run(); +} + +void QueuedTask::run() +{ + m_microtask->run(m_globalObject->globalExec()); +} + +void sanitizeStackForVM(VM* vm) +{ + logSanitizeStack(vm); +#if !ENABLE(JIT) + vm->interpreter->stack().sanitizeStack(); +#else + sanitizeStackForVMImpl(vm); +#endif +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h new file mode 100644 index 000000000..0634a3bba --- /dev/null +++ b/Source/JavaScriptCore/runtime/VM.h @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2008, 2009, 2013-2015 Apple Inc. 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef VM_h +#define VM_h + +#include "ControlFlowProfiler.h" +#include "DateInstanceCache.h" +#include "ExecutableAllocator.h" +#include "FunctionHasExecutedCache.h" +#include "Heap.h" +#include "Intrinsic.h" +#include "JITThunks.h" +#include "JSCJSValue.h" +#include "JSLock.h" +#include "LLIntData.h" +#include "MacroAssemblerCodeRef.h" +#include "Microtask.h" +#include "NumericStrings.h" +#include "PrivateName.h" +#include "PrototypeMap.h" +#include "SmallStrings.h" +#include "SourceCode.h" +#include "Strong.h" +#include "ThunkGenerators.h" +#include "TypedArrayController.h" +#include "VMEntryRecord.h" +#include "Watchpoint.h" +#include "WeakRandom.h" +#include <wtf/Bag.h> +#include <wtf/BumpPointerAllocator.h> +#include <wtf/DateMath.h> +#include <wtf/Deque.h> +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/SimpleStats.h> +#include <wtf/StackBounds.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/ThreadSpecific.h> +#include <wtf/WTFThreadData.h> +#include <wtf/text/SymbolRegistry.h> +#include <wtf/text/WTFString.h> +#if ENABLE(REGEXP_TRACING) +#include <wtf/ListHashSet.h> +#endif + +namespace JSC { + +class ArityCheckFailReturnThunks; +class BuiltinExecutables; +class CodeBlock; +class CodeCache; +class CommonIdentifiers; +class ExecState; +class Exception; +class HandleStack; +class TypeProfiler; +class TypeProfilerLog; +class Identifier; +class Interpreter; +class JSGlobalObject; +class JSObject; +class LLIntOffsetsExtractor; +class LegacyProfiler; +class NativeExecutable; +class RegExpCache; +class ScriptExecutable; +class SourceProvider; +class SourceProviderCache; +struct StackFrame; +class Stringifier; +class Structure; +#if ENABLE(REGEXP_TRACING) +class RegExp; +#endif +class UnlinkedCodeBlock; +class UnlinkedEvalCodeBlock; +class UnlinkedFunctionExecutable; +class UnlinkedProgramCodeBlock; +class VirtualRegister; +class VMEntryScope; +class Watchdog; +class Watchpoint; +class WatchpointSet; + +#if ENABLE(DFG_JIT) +namespace DFG { +class LongLivedState; +} +#endif // ENABLE(DFG_JIT) +#if ENABLE(FTL_JIT) +namespace FTL { +class Thunks; +} +#endif // ENABLE(FTL_JIT) +namespace CommonSlowPaths { +struct ArityCheckData; +} +namespace Profiler { +class Database; +} + +struct HashTable; +struct Instruction; + +struct LocalTimeOffsetCache { + LocalTimeOffsetCache() + : start(0.0) + , end(-1.0) + , increment(0.0) + , timeType(WTF::UTCTime) + { + } + + void reset() + { + offset = LocalTimeOffset(); + start = 0.0; + end = -1.0; + increment = 0.0; + timeType = WTF::UTCTime; + } + + LocalTimeOffset offset; + double start; + double end; + double increment; + WTF::TimeType timeType; +}; + +class QueuedTask { + WTF_MAKE_NONCOPYABLE(QueuedTask); + WTF_MAKE_FAST_ALLOCATED; +public: + void run(); + + QueuedTask(VM& vm, JSGlobalObject* globalObject, PassRefPtr<Microtask> microtask) + : m_globalObject(vm, globalObject) + , m_microtask(microtask) + { + } + +private: + Strong<JSGlobalObject> m_globalObject; + RefPtr<Microtask> m_microtask; +}; + +class ConservativeRoots; + +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning +#endif +struct ScratchBuffer { + ScratchBuffer() + { + u.m_activeLength = 0; + } + + static ScratchBuffer* create(size_t size) + { + ScratchBuffer* result = new (fastMalloc(ScratchBuffer::allocationSize(size))) ScratchBuffer; + + return result; + } + + static size_t allocationSize(size_t bufferSize) { return sizeof(ScratchBuffer) + bufferSize; } + void setActiveLength(size_t activeLength) { u.m_activeLength = activeLength; } + size_t activeLength() const { return u.m_activeLength; }; + size_t* activeLengthPtr() { return &u.m_activeLength; }; + void* dataBuffer() { return m_buffer; } + + union { + size_t m_activeLength; + double pad; // Make sure m_buffer is double aligned. + } u; +#if CPU(MIPS) && (defined WTF_MIPS_ARCH_REV && WTF_MIPS_ARCH_REV == 2) + void* m_buffer[0] __attribute__((aligned(8))); +#else + void* m_buffer[0]; +#endif +}; +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + +class VM : public ThreadSafeRefCounted<VM> { +public: + // WebCore has a one-to-one mapping of threads to VMs; + // either create() or createLeaked() should only be called once + // on a thread, this is the 'default' VM (it uses the + // thread's default string uniquing table from wtfThreadData). + // API contexts created using the new context group aware interface + // create APIContextGroup objects which require less locking of JSC + // than the old singleton APIShared VM created for use by + // the original API. + enum VMType { Default, APIContextGroup, APIShared }; + + struct ClientData { + JS_EXPORT_PRIVATE virtual ~ClientData() = 0; + }; + + bool isSharedInstance() { return vmType == APIShared; } + bool usingAPI() { return vmType != Default; } + JS_EXPORT_PRIVATE static bool sharedInstanceExists(); + JS_EXPORT_PRIVATE static VM& sharedInstance(); + + JS_EXPORT_PRIVATE static Ref<VM> create(HeapType = SmallHeap); + JS_EXPORT_PRIVATE static Ref<VM> createLeaked(HeapType = SmallHeap); + static Ref<VM> createContextGroup(HeapType = SmallHeap); + JS_EXPORT_PRIVATE ~VM(); + + JS_EXPORT_PRIVATE Watchdog& ensureWatchdog(); + +private: + RefPtr<JSLock> m_apiLock; + +public: +#if ENABLE(ASSEMBLER) + // executableAllocator should be destructed after the heap, as the heap can call executableAllocator + // in its destructor. + ExecutableAllocator executableAllocator; +#endif + + // The heap should be just after executableAllocator and before other members to ensure that it's + // destructed after all the objects that reference it. + Heap heap; + +#if ENABLE(DFG_JIT) + std::unique_ptr<DFG::LongLivedState> dfgState; +#endif // ENABLE(DFG_JIT) + + VMType vmType; + ClientData* clientData; + VMEntryFrame* topVMEntryFrame; + ExecState* topCallFrame; + RefPtr<Watchdog> watchdog; + + Strong<Structure> structureStructure; + Strong<Structure> structureRareDataStructure; + Strong<Structure> terminatedExecutionErrorStructure; + Strong<Structure> stringStructure; + Strong<Structure> notAnObjectStructure; + Strong<Structure> propertyNameIteratorStructure; + Strong<Structure> propertyNameEnumeratorStructure; + Strong<Structure> getterSetterStructure; + Strong<Structure> customGetterSetterStructure; + Strong<Structure> scopedArgumentsTableStructure; + Strong<Structure> apiWrapperStructure; + Strong<Structure> JSScopeStructure; + Strong<Structure> executableStructure; + Strong<Structure> nativeExecutableStructure; + Strong<Structure> evalExecutableStructure; + Strong<Structure> programExecutableStructure; + Strong<Structure> functionExecutableStructure; + Strong<Structure> regExpStructure; + Strong<Structure> symbolStructure; + Strong<Structure> symbolTableStructure; + Strong<Structure> structureChainStructure; + Strong<Structure> sparseArrayValueMapStructure; + Strong<Structure> templateRegistryKeyStructure; + Strong<Structure> arrayBufferNeuteringWatchpointStructure; + Strong<Structure> unlinkedFunctionExecutableStructure; + Strong<Structure> unlinkedProgramCodeBlockStructure; + Strong<Structure> unlinkedEvalCodeBlockStructure; + Strong<Structure> unlinkedFunctionCodeBlockStructure; + Strong<Structure> propertyTableStructure; + Strong<Structure> weakMapDataStructure; + Strong<Structure> inferredValueStructure; + Strong<Structure> functionRareDataStructure; + Strong<Structure> exceptionStructure; + Strong<Structure> promiseDeferredStructure; + Strong<JSCell> iterationTerminator; + Strong<JSCell> emptyPropertyNameEnumerator; + + AtomicStringTable* m_atomicStringTable; + WTF::SymbolRegistry m_symbolRegistry; + CommonIdentifiers* propertyNames; + const MarkedArgumentBuffer* emptyList; // Lists are supposed to be allocated on the stack to have their elements properly marked, which is not the case here - but this list has nothing to mark. + SmallStrings smallStrings; + NumericStrings numericStrings; + DateInstanceCache dateInstanceCache; + WTF::SimpleStats machineCodeBytesPerBytecodeWordForBaselineJIT; + WeakGCMap<StringImpl*, JSString, PtrHash<StringImpl*>> stringCache; + Strong<JSString> lastCachedString; + + AtomicStringTable* atomicStringTable() const { return m_atomicStringTable; } + WTF::SymbolRegistry& symbolRegistry() { return m_symbolRegistry; } + + void setInDefineOwnProperty(bool inDefineOwnProperty) + { + m_inDefineOwnProperty = inDefineOwnProperty; + } + + bool isInDefineOwnProperty() + { + return m_inDefineOwnProperty; + } + + LegacyProfiler* enabledProfiler() { return m_enabledProfiler; } + void setEnabledProfiler(LegacyProfiler*); + + void* enabledProfilerAddress() { return &m_enabledProfiler; } + +#if ENABLE(JIT) + bool canUseJIT() { return m_canUseJIT; } +#else + bool canUseJIT() { return false; } // interpreter only +#endif + +#if ENABLE(YARR_JIT) + bool canUseRegExpJIT() { return m_canUseRegExpJIT; } +#else + bool canUseRegExpJIT() { return false; } // interpreter only +#endif + + SourceProviderCache* addSourceProviderCache(SourceProvider*); + void clearSourceProviderCaches(); + + PrototypeMap prototypeMap; + + typedef HashMap<RefPtr<SourceProvider>, RefPtr<SourceProviderCache>> SourceProviderCacheMap; + SourceProviderCacheMap sourceProviderCacheMap; + Interpreter* interpreter; +#if ENABLE(JIT) + std::unique_ptr<JITThunks> jitStubs; + MacroAssemblerCodeRef getCTIStub(ThunkGenerator generator) + { + return jitStubs->ctiStub(this, generator); + } + NativeExecutable* getHostFunction(NativeFunction, Intrinsic); + + std::unique_ptr<ArityCheckFailReturnThunks> arityCheckFailReturnThunks; +#endif // ENABLE(JIT) + std::unique_ptr<CommonSlowPaths::ArityCheckData> arityCheckData; +#if ENABLE(FTL_JIT) + std::unique_ptr<FTL::Thunks> ftlThunks; +#endif + NativeExecutable* getHostFunction(NativeFunction, NativeFunction constructor); + + static ptrdiff_t exceptionOffset() + { + return OBJECT_OFFSETOF(VM, m_exception); + } + + static ptrdiff_t vmEntryFrameForThrowOffset() + { + return OBJECT_OFFSETOF(VM, vmEntryFrameForThrow); + } + + static ptrdiff_t topVMEntryFrameOffset() + { + return OBJECT_OFFSETOF(VM, topVMEntryFrame); + } + + static ptrdiff_t callFrameForThrowOffset() + { + return OBJECT_OFFSETOF(VM, callFrameForThrow); + } + + static ptrdiff_t targetMachinePCForThrowOffset() + { + return OBJECT_OFFSETOF(VM, targetMachinePCForThrow); + } + + void clearException() { m_exception = nullptr; } + void clearLastException() { m_lastException = nullptr; } + + void setException(Exception* exception) + { + m_exception = exception; + m_lastException = exception; + } + + Exception* exception() const { return m_exception; } + JSCell** addressOfException() { return reinterpret_cast<JSCell**>(&m_exception); } + + Exception* lastException() const { return m_lastException; } + JSCell** addressOfLastException() { return reinterpret_cast<JSCell**>(&m_lastException); } + + JS_EXPORT_PRIVATE void throwException(ExecState*, Exception*); + JS_EXPORT_PRIVATE JSValue throwException(ExecState*, JSValue); + JS_EXPORT_PRIVATE JSObject* throwException(ExecState*, JSObject*); + + void* stackPointerAtVMEntry() const { return m_stackPointerAtVMEntry; } + void setStackPointerAtVMEntry(void*); + + size_t reservedZoneSize() const { return m_reservedZoneSize; } + size_t updateReservedZoneSize(size_t reservedZoneSize); + +#if ENABLE(FTL_JIT) + void updateFTLLargestStackSize(size_t); + void** addressOfFTLStackLimit() { return &m_ftlStackLimit; } +#endif + +#if !ENABLE(JIT) + void* jsStackLimit() { return m_jsStackLimit; } + void setJSStackLimit(void* limit) { m_jsStackLimit = limit; } +#endif + void* stackLimit() { return m_stackLimit; } + void** addressOfStackLimit() { return &m_stackLimit; } + + bool isSafeToRecurse(size_t neededStackInBytes = 0) const + { + ASSERT(wtfThreadData().stack().isGrowingDownward()); + int8_t* curr = reinterpret_cast<int8_t*>(&curr); + int8_t* limit = reinterpret_cast<int8_t*>(m_stackLimit); + return curr >= limit && static_cast<size_t>(curr - limit) >= neededStackInBytes; + } + + void* lastStackTop() { return m_lastStackTop; } + void setLastStackTop(void* lastStackTop) { m_lastStackTop = lastStackTop; } + + const ClassInfo* const jsArrayClassInfo; + const ClassInfo* const jsFinalObjectClassInfo; + + JSValue hostCallReturnValue; + unsigned varargsLength; + ExecState* newCallFrameReturnValue; + VMEntryFrame* vmEntryFrameForThrow; + ExecState* callFrameForThrow; + void* targetMachinePCForThrow; + Instruction* targetInterpreterPCForThrow; + uint32_t osrExitIndex; + void* osrExitJumpDestination; + Vector<ScratchBuffer*> scratchBuffers; + size_t sizeOfLastScratchBuffer; + + ScratchBuffer* scratchBufferForSize(size_t size) + { + if (!size) + return 0; + + if (size > sizeOfLastScratchBuffer) { + // Protect against a N^2 memory usage pathology by ensuring + // that at worst, we get a geometric series, meaning that the + // total memory usage is somewhere around + // max(scratch buffer size) * 4. + sizeOfLastScratchBuffer = size * 2; + + ScratchBuffer* newBuffer = ScratchBuffer::create(sizeOfLastScratchBuffer); + RELEASE_ASSERT(newBuffer); + scratchBuffers.append(newBuffer); + } + + ScratchBuffer* result = scratchBuffers.last(); + result->setActiveLength(0); + return result; + } + + void gatherConservativeRoots(ConservativeRoots&); + + VMEntryScope* entryScope; + + JSObject* stringRecursionCheckFirstObject { nullptr }; + HashSet<JSObject*> stringRecursionCheckVisitedObjects; + + LocalTimeOffsetCache localTimeOffsetCache; + + String cachedDateString; + double cachedDateStringValue; + + std::unique_ptr<Profiler::Database> m_perBytecodeProfiler; + RefPtr<TypedArrayController> m_typedArrayController; + RegExpCache* m_regExpCache; + BumpPointerAllocator m_regExpAllocator; + +#if ENABLE(REGEXP_TRACING) + typedef ListHashSet<RegExp*> RTTraceList; + RTTraceList* m_rtTraceList; +#endif + + bool hasExclusiveThread() const { return m_apiLock->hasExclusiveThread(); } + std::thread::id exclusiveThread() const { return m_apiLock->exclusiveThread(); } + void setExclusiveThread(std::thread::id threadId) { m_apiLock->setExclusiveThread(threadId); } + + JS_EXPORT_PRIVATE void resetDateCache(); + + JS_EXPORT_PRIVATE void startSampling(); + JS_EXPORT_PRIVATE void stopSampling(); + JS_EXPORT_PRIVATE void dumpSampleData(ExecState*); + RegExpCache* regExpCache() { return m_regExpCache; } +#if ENABLE(REGEXP_TRACING) + void addRegExpToTrace(RegExp*); +#endif + JS_EXPORT_PRIVATE void dumpRegExpTrace(); + + bool isCollectorBusy() { return heap.isBusy(); } + +#if ENABLE(GC_VALIDATION) + bool isInitializingObject() const; + void setInitializingObjectClass(const ClassInfo*); +#endif + + unsigned m_newStringsSinceLastHashCons; + + static const unsigned s_minNumberOfNewStringsToHashCons = 100; + + bool haveEnoughNewStringsToHashCons() { return m_newStringsSinceLastHashCons > s_minNumberOfNewStringsToHashCons; } + void resetNewStringsSinceLastHashCons() { m_newStringsSinceLastHashCons = 0; } + + bool currentThreadIsHoldingAPILock() const { return m_apiLock->currentThreadIsHoldingLock(); } + + JSLock& apiLock() { return *m_apiLock; } + CodeCache* codeCache() { return m_codeCache.get(); } + + void prepareToDeleteCode(); + + JS_EXPORT_PRIVATE void deleteAllCode(); + + void registerWatchpointForImpureProperty(const Identifier&, Watchpoint*); + + // FIXME: Use AtomicString once it got merged with Identifier. + JS_EXPORT_PRIVATE void addImpureProperty(const String&); + + BuiltinExecutables* builtinExecutables() { return m_builtinExecutables.get(); } + + bool enableTypeProfiler(); + bool disableTypeProfiler(); + TypeProfilerLog* typeProfilerLog() { return m_typeProfilerLog.get(); } + TypeProfiler* typeProfiler() { return m_typeProfiler.get(); } + JS_EXPORT_PRIVATE void dumpTypeProfilerData(); + + FunctionHasExecutedCache* functionHasExecutedCache() { return &m_functionHasExecutedCache; } + + ControlFlowProfiler* controlFlowProfiler() { return m_controlFlowProfiler.get(); } + bool enableControlFlowProfiler(); + bool disableControlFlowProfiler(); + + JS_EXPORT_PRIVATE void queueMicrotask(JSGlobalObject*, PassRefPtr<Microtask>); + JS_EXPORT_PRIVATE void drainMicrotasks(); + + inline bool shouldTriggerTermination(ExecState*); + +private: + friend class LLIntOffsetsExtractor; + friend class ClearExceptionScope; + friend class RecursiveAllocationScope; + + VM(VMType, HeapType); + static VM*& sharedInstanceInternal(); + void createNativeThunk(); + + void updateStackLimit(); + +#if ENABLE(ASSEMBLER) + bool m_canUseAssembler; +#endif +#if ENABLE(JIT) + bool m_canUseJIT; +#endif +#if ENABLE(YARR_JIT) + bool m_canUseRegExpJIT; +#endif +#if ENABLE(GC_VALIDATION) + const ClassInfo* m_initializingObjectClass; +#endif + void* m_stackPointerAtVMEntry; + size_t m_reservedZoneSize; +#if !ENABLE(JIT) + struct { + void* m_stackLimit; + void* m_jsStackLimit; + }; +#else + union { + void* m_stackLimit; + void* m_jsStackLimit; + }; +#if ENABLE(FTL_JIT) + void* m_ftlStackLimit; + size_t m_largestFTLStackSize; +#endif +#endif + void* m_lastStackTop; + Exception* m_exception { nullptr }; + Exception* m_lastException { nullptr }; + bool m_inDefineOwnProperty; + std::unique_ptr<CodeCache> m_codeCache; + LegacyProfiler* m_enabledProfiler; + std::unique_ptr<BuiltinExecutables> m_builtinExecutables; + HashMap<String, RefPtr<WatchpointSet>> m_impurePropertyWatchpointSets; + std::unique_ptr<TypeProfiler> m_typeProfiler; + std::unique_ptr<TypeProfilerLog> m_typeProfilerLog; + unsigned m_typeProfilerEnabledCount; + FunctionHasExecutedCache m_functionHasExecutedCache; + std::unique_ptr<ControlFlowProfiler> m_controlFlowProfiler; + unsigned m_controlFlowProfilerEnabledCount; + Deque<std::unique_ptr<QueuedTask>> m_microtaskQueue; +}; + +#if ENABLE(GC_VALIDATION) +inline bool VM::isInitializingObject() const +{ + return !!m_initializingObjectClass; +} + +inline void VM::setInitializingObjectClass(const ClassInfo* initializingObjectClass) +{ + m_initializingObjectClass = initializingObjectClass; +} +#endif + +inline Heap* WeakSet::heap() const +{ + return &m_vm->heap; +} + +#if ENABLE(JIT) +extern "C" void sanitizeStackForVMImpl(VM*); +#endif + +void sanitizeStackForVM(VM*); +void logSanitizeStack(VM*); + +} // namespace JSC + +#endif // VM_h diff --git a/Source/JavaScriptCore/runtime/VMEntryScope.cpp b/Source/JavaScriptCore/runtime/VMEntryScope.cpp new file mode 100644 index 000000000..bc901b215 --- /dev/null +++ b/Source/JavaScriptCore/runtime/VMEntryScope.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013-2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "VMEntryScope.h" + +#include "Debugger.h" +#include "Options.h" +#include "VM.h" +#include "Watchdog.h" +#include <wtf/StackBounds.h> + +namespace JSC { + +VMEntryScope::VMEntryScope(VM& vm, JSGlobalObject* globalObject) + : m_vm(vm) + , m_globalObject(globalObject) +{ + ASSERT(wtfThreadData().stack().isGrowingDownward()); + if (!vm.entryScope) { +#if ENABLE(ASSEMBLER) + if (ExecutableAllocator::underMemoryPressure()) + vm.heap.deleteAllCompiledCode(); +#endif + vm.entryScope = this; + + // Reset the date cache between JS invocations to force the VM to + // observe time xone changes. + vm.resetDateCache(); + + if (vm.watchdog) + vm.watchdog->enteredVM(); + } + + vm.clearLastException(); +} + +void VMEntryScope::setEntryScopeDidPopListener(void* key, EntryScopeDidPopListener listener) +{ + m_allEntryScopeDidPopListeners.set(key, listener); +} + +VMEntryScope::~VMEntryScope() +{ + if (m_vm.entryScope != this) + return; + + if (m_vm.watchdog) + m_vm.watchdog->exitedVM(); + + m_vm.entryScope = nullptr; + + for (auto& listener : m_allEntryScopeDidPopListeners.values()) + listener(m_vm, m_globalObject); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/VMEntryScope.h b/Source/JavaScriptCore/runtime/VMEntryScope.h new file mode 100644 index 000000000..6a62831b8 --- /dev/null +++ b/Source/JavaScriptCore/runtime/VMEntryScope.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef VMEntryScope_h +#define VMEntryScope_h + +#include "Interpreter.h" +#include <wtf/HashMap.h> +#include <wtf/StackBounds.h> +#include <wtf/StackStats.h> + +namespace JSC { + +class JSGlobalObject; +class VM; + +class VMEntryScope { +public: + JS_EXPORT_PRIVATE VMEntryScope(VM&, JSGlobalObject*); + JS_EXPORT_PRIVATE ~VMEntryScope(); + + JSGlobalObject* globalObject() const { return m_globalObject; } + + typedef std::function<void (VM&, JSGlobalObject*)> EntryScopeDidPopListener; + void setEntryScopeDidPopListener(void*, EntryScopeDidPopListener); + +private: + VM& m_vm; + JSGlobalObject* m_globalObject; + HashMap<void*, EntryScopeDidPopListener> m_allEntryScopeDidPopListeners; +}; + +} // namespace JSC + +#endif // VMEntryScope_h diff --git a/Source/JavaScriptCore/runtime/VMInlines.h b/Source/JavaScriptCore/runtime/VMInlines.h new file mode 100644 index 000000000..fcc93888e --- /dev/null +++ b/Source/JavaScriptCore/runtime/VMInlines.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef VMInlines_h +#define VMInlines_h + +#include "VM.h" +#include "Watchdog.h" + +namespace JSC { + +bool VM::shouldTriggerTermination(ExecState* exec) +{ + if (!watchdog) + return false; + return watchdog->didFire(exec); +} + +} // namespace JSC + +#endif // LLIntData_h + diff --git a/Source/JavaScriptCore/runtime/VarOffset.cpp b/Source/JavaScriptCore/runtime/VarOffset.cpp new file mode 100644 index 000000000..e0d65e54f --- /dev/null +++ b/Source/JavaScriptCore/runtime/VarOffset.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "VarOffset.h" + +namespace JSC { + +void VarOffset::dump(PrintStream& out) const +{ + switch (m_kind) { + case VarKind::Invalid: + out.print("invalid"); + return; + case VarKind::Scope: + out.print(scopeOffset()); + return; + case VarKind::Stack: + out.print(stackOffset()); + return; + case VarKind::DirectArgument: + out.print(capturedArgumentsOffset()); + return; + } + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace JSC + +namespace WTF { + +using namespace JSC; + +void printInternal(PrintStream& out, VarKind varKind) +{ + switch (varKind) { + case VarKind::Invalid: + out.print("Invalid"); + return; + case VarKind::Scope: + out.print("Scope"); + return; + case VarKind::Stack: + out.print("Stack"); + return; + case VarKind::DirectArgument: + out.print("DirectArgument"); + return; + } + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace WTF + diff --git a/Source/JavaScriptCore/runtime/VarOffset.h b/Source/JavaScriptCore/runtime/VarOffset.h new file mode 100644 index 000000000..b844f57e4 --- /dev/null +++ b/Source/JavaScriptCore/runtime/VarOffset.h @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef VarOffset_h +#define VarOffset_h + +#include "DirectArgumentsOffset.h" +#include "ScopeOffset.h" +#include "VirtualRegister.h" +#include <wtf/HashMap.h> + +namespace JSC { + +enum class VarKind : uint8_t { + Invalid, + Scope, + Stack, + DirectArgument +}; + +class VarOffset { +public: + VarOffset() + : m_kind(VarKind::Invalid) + , m_offset(UINT_MAX) + { + } + + VarOffset(WTF::HashTableDeletedValueType) + : m_kind(VarKind::Invalid) + , m_offset(0) + { + } + + explicit VarOffset(VirtualRegister stackOffset) + { + if (!stackOffset.isValid()) { + m_kind = VarKind::Invalid; + m_offset = UINT_MAX; + } else { + m_kind = VarKind::Stack; + m_offset = stackOffset.offset(); + } + } + + explicit VarOffset(ScopeOffset scopeOffset) + { + if (!scopeOffset) { + m_kind = VarKind::Invalid; + m_offset = UINT_MAX; + } else { + m_kind = VarKind::Scope; + m_offset = scopeOffset.offset(); + } + } + + explicit VarOffset(DirectArgumentsOffset capturedArgumentsOffset) + { + if (!capturedArgumentsOffset) { + m_kind = VarKind::Invalid; + m_offset = UINT_MAX; + } else { + m_kind = VarKind::DirectArgument; + m_offset = capturedArgumentsOffset.offset(); + } + } + + static VarOffset assemble(VarKind kind, unsigned offset) + { + VarOffset result; + result.m_kind = kind; + result.m_offset = offset; + result.checkSanity(); + return result; + } + + bool isValid() const + { + return m_kind != VarKind::Invalid; + } + + bool operator!() const + { + return !isValid(); + } + + VarKind kind() const { return m_kind; } + + bool isStack() const + { + return m_kind == VarKind::Stack; + } + + bool isScope() const + { + return m_kind == VarKind::Scope; + } + + bool isDirectArgument() const + { + return m_kind == VarKind::DirectArgument; + } + + VirtualRegister stackOffsetUnchecked() const + { + if (!isStack()) + return VirtualRegister(); + return VirtualRegister(m_offset); + } + + ScopeOffset scopeOffsetUnchecked() const + { + if (!isScope()) + return ScopeOffset(); + return ScopeOffset(m_offset); + } + + DirectArgumentsOffset capturedArgumentsOffsetUnchecked() const + { + if (!isDirectArgument()) + return DirectArgumentsOffset(); + return DirectArgumentsOffset(m_offset); + } + + VirtualRegister stackOffset() const + { + ASSERT(isStack()); + return VirtualRegister(m_offset); + } + + ScopeOffset scopeOffset() const + { + ASSERT(isScope()); + return ScopeOffset(m_offset); + } + + DirectArgumentsOffset capturedArgumentsOffset() const + { + ASSERT(isDirectArgument()); + return DirectArgumentsOffset(m_offset); + } + + unsigned rawOffset() const + { + ASSERT(isValid()); + return m_offset; + } + + void checkSanity() const + { + if (ASSERT_DISABLED) + return; + + switch (m_kind) { + case VarKind::Invalid: + ASSERT(m_offset == UINT_MAX); + return; + case VarKind::Scope: + ASSERT(scopeOffset()); + return; + case VarKind::Stack: + ASSERT(stackOffset().isValid()); + return; + case VarKind::DirectArgument: + ASSERT(capturedArgumentsOffset()); + return; + } + + ASSERT_NOT_REACHED(); + } + + bool operator==(const VarOffset& other) const + { + return m_kind == other.m_kind + && m_offset == other.m_offset; + } + + bool operator!=(const VarOffset& other) const + { + return !(*this == other); + } + + unsigned hash() const + { + return WTF::IntHash<unsigned>::hash((static_cast<unsigned>(m_kind) << 20) + m_offset); + } + + bool isHashTableDeletedValue() const + { + return m_kind == VarKind::Invalid && !m_offset; + } + + void dump(PrintStream&) const; + +private: + VarKind m_kind; + unsigned m_offset; +}; + +struct VarOffsetHash { + static unsigned hash(const VarOffset& key) { return key.hash(); } + static bool equal(const VarOffset& a, const VarOffset& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +} // namespace JSC + +namespace WTF { + +void printInternal(PrintStream&, JSC::VarKind); + +template<typename T> struct DefaultHash; +template<> struct DefaultHash<JSC::VarOffset> { + typedef JSC::VarOffsetHash Hash; +}; + +template<typename T> struct HashTraits; +template<> struct HashTraits<JSC::VarOffset> : SimpleClassHashTraits<JSC::VarOffset> { + static const bool emptyValueIsZero = false; +}; + +} // namespace WTF + +#endif // VarOffset_h + diff --git a/Source/JavaScriptCore/runtime/Watchdog.cpp b/Source/JavaScriptCore/runtime/Watchdog.cpp new file mode 100644 index 000000000..044552cf9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Watchdog.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2013, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "Watchdog.h" + +#include "CallFrame.h" +#include <wtf/CurrentTime.h> +#include <wtf/MathExtras.h> + +namespace JSC { + +const std::chrono::microseconds Watchdog::noTimeLimit = std::chrono::microseconds::max(); + +static std::chrono::microseconds currentWallClockTime() +{ + auto steadyTimeSinceEpoch = std::chrono::steady_clock::now().time_since_epoch(); + return std::chrono::duration_cast<std::chrono::microseconds>(steadyTimeSinceEpoch); +} + +Watchdog::Watchdog() + : m_timerDidFire(false) + , m_timeLimit(noTimeLimit) + , m_cpuDeadline(noTimeLimit) + , m_wallClockDeadline(noTimeLimit) + , m_callback(0) + , m_callbackData1(0) + , m_callbackData2(0) + , m_timerQueue(WorkQueue::create("jsc.watchdog.queue", WorkQueue::Type::Serial, WorkQueue::QOS::Utility)) +{ + m_timerHandler = [this] { + { + LockHolder locker(m_lock); + this->m_timerDidFire = true; + } + this->deref(); + }; +} + +void Watchdog::setTimeLimit(std::chrono::microseconds limit, + ShouldTerminateCallback callback, void* data1, void* data2) +{ + LockHolder locker(m_lock); + + m_timeLimit = limit; + m_callback = callback; + m_callbackData1 = data1; + m_callbackData2 = data2; + + if (m_hasEnteredVM && hasTimeLimit()) + startTimer(locker, m_timeLimit); +} + +JS_EXPORT_PRIVATE void Watchdog::terminateSoon() +{ + LockHolder locker(m_lock); + + m_timeLimit = std::chrono::microseconds(0); + m_cpuDeadline = std::chrono::microseconds(0); + m_wallClockDeadline = std::chrono::microseconds(0); + m_timerDidFire = true; +} + +bool Watchdog::didFireSlow(ExecState* exec) +{ + { + LockHolder locker(m_lock); + + ASSERT(m_timerDidFire); + m_timerDidFire = false; + + if (currentWallClockTime() < m_wallClockDeadline) + return false; // Just a stale timer firing. Nothing to do. + + // Set m_wallClockDeadline to noTimeLimit here so that we can reject all future + // spurious wakes. + m_wallClockDeadline = noTimeLimit; + + auto cpuTime = currentCPUTime(); + if (cpuTime < m_cpuDeadline) { + auto remainingCPUTime = m_cpuDeadline - cpuTime; + startTimer(locker, remainingCPUTime); + return false; + } + } + + // Note: we should not be holding the lock while calling the callbacks. The callbacks may + // call setTimeLimit() which will try to lock as well. + + // If m_callback is not set, then we terminate by default. + // Else, we let m_callback decide if we should terminate or not. + bool needsTermination = !m_callback + || m_callback(exec, m_callbackData1, m_callbackData2); + if (needsTermination) + return true; + + { + LockHolder locker(m_lock); + + // If we get here, then the callback above did not want to terminate execution. As a + // result, the callback may have done one of the following: + // 1. cleared the time limit (i.e. watchdog is disabled), + // 2. set a new time limit via Watchdog::setTimeLimit(), or + // 3. did nothing (i.e. allow another cycle of the current time limit). + // + // In the case of 1, we don't have to do anything. + // In the case of 2, Watchdog::setTimeLimit() would already have started the timer. + // In the case of 3, we need to re-start the timer here. + + ASSERT(m_hasEnteredVM); + bool callbackAlreadyStartedTimer = (m_cpuDeadline != noTimeLimit); + if (hasTimeLimit() && !callbackAlreadyStartedTimer) + startTimer(locker, m_timeLimit); + } + return false; +} + +bool Watchdog::hasTimeLimit() +{ + return (m_timeLimit != noTimeLimit); +} + +void Watchdog::enteredVM() +{ + m_hasEnteredVM = true; + if (hasTimeLimit()) { + LockHolder locker(m_lock); + startTimer(locker, m_timeLimit); + } +} + +void Watchdog::exitedVM() +{ + ASSERT(m_hasEnteredVM); + LockHolder locker(m_lock); + stopTimer(locker); + m_hasEnteredVM = false; +} + +void Watchdog::startTimer(LockHolder&, std::chrono::microseconds timeLimit) +{ + ASSERT(m_hasEnteredVM); + ASSERT(hasTimeLimit()); + ASSERT(timeLimit <= m_timeLimit); + + m_cpuDeadline = currentCPUTime() + timeLimit; + auto wallClockTime = currentWallClockTime(); + auto wallClockDeadline = wallClockTime + timeLimit; + + if ((wallClockTime < m_wallClockDeadline) + && (m_wallClockDeadline <= wallClockDeadline)) + return; // Wait for the current active timer to expire before starting a new one. + + // Else, the current active timer won't fire soon enough. So, start a new timer. + this->ref(); // m_timerHandler will deref to match later. + m_wallClockDeadline = wallClockDeadline; + + m_timerQueue->dispatchAfter(std::chrono::nanoseconds(timeLimit), m_timerHandler); +} + +void Watchdog::stopTimer(LockHolder&) +{ + m_cpuDeadline = noTimeLimit; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Watchdog.h b/Source/JavaScriptCore/runtime/Watchdog.h new file mode 100644 index 000000000..cfd9fb14b --- /dev/null +++ b/Source/JavaScriptCore/runtime/Watchdog.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2013, 2015 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Watchdog_h +#define Watchdog_h + +#include <wtf/Lock.h> +#include <wtf/Ref.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/WorkQueue.h> + +namespace JSC { + +class ExecState; +class VM; + +class Watchdog : public WTF::ThreadSafeRefCounted<Watchdog> { + WTF_MAKE_FAST_ALLOCATED; +public: + class Scope; + + Watchdog(); + + typedef bool (*ShouldTerminateCallback)(ExecState*, void* data1, void* data2); + void setTimeLimit(std::chrono::microseconds limit, ShouldTerminateCallback = 0, void* data1 = 0, void* data2 = 0); + JS_EXPORT_PRIVATE void terminateSoon(); + + bool didFire(ExecState* exec) + { + if (!m_timerDidFire) + return false; + return didFireSlow(exec); + } + + bool hasTimeLimit(); + void enteredVM(); + void exitedVM(); + + void* timerDidFireAddress() { return &m_timerDidFire; } + + static const std::chrono::microseconds noTimeLimit; + +private: + void startTimer(LockHolder&, std::chrono::microseconds timeLimit); + void stopTimer(LockHolder&); + + bool didFireSlow(ExecState*); + + // m_timerDidFire indicates whether the timer fired. The Watchdog + // still needs to check if the allowed CPU time has elapsed. If so, then + // the Watchdog fires and m_didFire will be set. + // NOTE: m_timerDidFire is only set by the platform specific timer + // (probably from another thread) but is only cleared in the script thread. + bool m_timerDidFire; + + std::chrono::microseconds m_timeLimit; + + std::chrono::microseconds m_cpuDeadline; + std::chrono::microseconds m_wallClockDeadline; + + // Writes to m_timerDidFire and m_timeLimit, and Reads+Writes to m_cpuDeadline and m_wallClockDeadline + // must be guarded by this lock. + Lock m_lock; + + bool m_hasEnteredVM { false }; + + ShouldTerminateCallback m_callback; + void* m_callbackData1; + void* m_callbackData2; + + Ref<WorkQueue> m_timerQueue; + std::function<void ()> m_timerHandler; + + friend class LLIntOffsetsExtractor; +}; + +} // namespace JSC + +#endif // Watchdog_h diff --git a/Source/JavaScriptCore/runtime/WatchdogMac.cpp b/Source/JavaScriptCore/runtime/WatchdogMac.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/Source/JavaScriptCore/runtime/WatchdogMac.cpp diff --git a/Source/JavaScriptCore/runtime/WatchdogNone.cpp b/Source/JavaScriptCore/runtime/WatchdogNone.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/Source/JavaScriptCore/runtime/WatchdogNone.cpp diff --git a/Source/JavaScriptCore/runtime/WeakGCMap.h b/Source/JavaScriptCore/runtime/WeakGCMap.h new file mode 100644 index 000000000..16d3d4400 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakGCMap.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2009, 2015 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef WeakGCMap_h +#define WeakGCMap_h + +#include "Weak.h" +#include "WeakInlines.h" +#include <wtf/HashMap.h> + +namespace JSC { + +// A HashMap with Weak<JSCell> values, which automatically removes values once they're garbage collected. + +template<typename KeyArg, typename ValueArg, typename HashArg = typename DefaultHash<KeyArg>::Hash, + typename KeyTraitsArg = HashTraits<KeyArg>> +class WeakGCMap { + WTF_MAKE_FAST_ALLOCATED; + typedef Weak<ValueArg> ValueType; + typedef HashMap<KeyArg, ValueType, HashArg, KeyTraitsArg> HashMapType; + +public: + typedef typename HashMapType::KeyType KeyType; + typedef typename HashMapType::AddResult AddResult; + typedef typename HashMapType::iterator iterator; + typedef typename HashMapType::const_iterator const_iterator; + + explicit WeakGCMap(VM&); + ~WeakGCMap(); + + ValueArg* get(const KeyType& key) const + { + return m_map.get(key); + } + + AddResult set(const KeyType& key, ValueType value) + { + return m_map.set(key, WTF::move(value)); + } + + bool remove(const KeyType& key) + { + return m_map.remove(key); + } + + void clear() + { + m_map.clear(); + } + + bool isEmpty() const + { + const_iterator it = m_map.begin(); + const_iterator end = m_map.end(); + while (it != end) { + if (it->value) + return true; + } + return false; + } + + iterator find(const KeyType& key) + { + iterator it = m_map.find(key); + iterator end = m_map.end(); + if (it != end && !it->value) // Found a zombie value. + return end; + return it; + } + + const_iterator find(const KeyType& key) const + { + return const_cast<WeakGCMap*>(this)->find(key); + } + + template<typename Functor> + void forEach(Functor functor) + { + for (auto& pair : m_map) { + if (pair.value) + functor(pair.key, pair.value.get()); + } + } + + bool contains(const KeyType& key) const + { + return find(key) != m_map.end(); + } + + void pruneStaleEntries(); + +private: + HashMapType m_map; + VM& m_vm; +}; + +} // namespace JSC + +#endif // WeakGCMap_h diff --git a/Source/JavaScriptCore/runtime/WeakGCMapInlines.h b/Source/JavaScriptCore/runtime/WeakGCMapInlines.h new file mode 100644 index 000000000..b90acc089 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakGCMapInlines.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef WeakGCMapInlines_h +#define WeakGCMapInlines_h + +#include "HeapInlines.h" +#include "WeakGCMap.h" + +namespace JSC { + +template<typename KeyArg, typename ValueArg, typename HashArg, typename KeyTraitsArg> +inline WeakGCMap<KeyArg, ValueArg, HashArg, KeyTraitsArg>::WeakGCMap(VM& vm) + : m_vm(vm) +{ + vm.heap.registerWeakGCMap(this, [this]() { + pruneStaleEntries(); + }); +} + +template<typename KeyArg, typename ValueArg, typename HashArg, typename KeyTraitsArg> +inline WeakGCMap<KeyArg, ValueArg, HashArg, KeyTraitsArg>::~WeakGCMap() +{ + m_vm.heap.unregisterWeakGCMap(this); +} + +template<typename KeyArg, typename ValueArg, typename HashArg, typename KeyTraitsArg> +NEVER_INLINE void WeakGCMap<KeyArg, ValueArg, HashArg, KeyTraitsArg>::pruneStaleEntries() +{ + m_map.removeIf([](typename HashMapType::KeyValuePairType& entry) { + return !entry.value; + }); +} + +} // namespace JSC + +#endif // WeakGCMapInlines_h diff --git a/Source/JavaScriptCore/runtime/WeakMapConstructor.cpp b/Source/JavaScriptCore/runtime/WeakMapConstructor.cpp new file mode 100644 index 000000000..23c43330d --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakMapConstructor.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "WeakMapConstructor.h" + +#include "Error.h" +#include "IteratorOperations.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "JSWeakMap.h" +#include "StructureInlines.h" +#include "WeakMapPrototype.h" + +namespace JSC { + +const ClassInfo WeakMapConstructor::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(WeakMapConstructor) }; + +void WeakMapConstructor::finishCreation(VM& vm, WeakMapPrototype* prototype) +{ + Base::finishCreation(vm, prototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), ReadOnly | DontEnum | DontDelete); +} + +static EncodedJSValue JSC_HOST_CALL callWeakMap(ExecState* exec) +{ + return JSValue::encode(throwTypeError(exec, ASCIILiteral("WeakMap cannot be called as a function"))); +} + +static EncodedJSValue JSC_HOST_CALL constructWeakMap(ExecState* exec) +{ + JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject(); + Structure* weakMapStructure = globalObject->weakMapStructure(); + JSWeakMap* weakMap = JSWeakMap::create(exec, weakMapStructure); + JSValue iterable = exec->argument(0); + if (iterable.isUndefinedOrNull()) + return JSValue::encode(weakMap); + + JSValue adderFunction = weakMap->JSObject::get(exec, exec->propertyNames().set); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData adderFunctionCallData; + CallType adderFunctionCallType = getCallData(adderFunction, adderFunctionCallData); + if (adderFunctionCallType == CallTypeNone) + return JSValue::encode(throwTypeError(exec)); + + JSValue iteratorFunction = iterable.get(exec, exec->propertyNames().iteratorSymbol); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData iteratorFunctionCallData; + CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData); + if (iteratorFunctionCallType == CallTypeNone) + return JSValue::encode(throwTypeError(exec)); + + ArgList iteratorFunctionArguments; + JSValue iterator = call(exec, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (!iterator.isObject()) + return JSValue::encode(throwTypeError(exec)); + + while (true) { + JSValue next = iteratorStep(exec, iterator); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (next.isFalse()) + return JSValue::encode(weakMap); + + JSValue nextItem = iteratorValue(exec, next); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (!nextItem.isObject()) { + throwTypeError(exec); + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + + JSValue key = nextItem.get(exec, 0); + if (exec->hadException()) { + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + + JSValue value = nextItem.get(exec, 1); + if (exec->hadException()) { + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + + MarkedArgumentBuffer arguments; + arguments.append(key); + arguments.append(value); + call(exec, adderFunction, adderFunctionCallType, adderFunctionCallData, weakMap, arguments); + if (exec->hadException()) { + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + } + RELEASE_ASSERT_NOT_REACHED(); + return JSValue::encode(weakMap); +} + +ConstructType WeakMapConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWeakMap; + return ConstructTypeHost; +} + +CallType WeakMapConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callWeakMap; + return CallTypeHost; +} + +} diff --git a/Source/JavaScriptCore/runtime/WeakMapConstructor.h b/Source/JavaScriptCore/runtime/WeakMapConstructor.h new file mode 100644 index 000000000..66edbcb16 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakMapConstructor.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WeakMapConstructor_h +#define WeakMapConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class WeakMapPrototype; + +class WeakMapConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + + static WeakMapConstructor* create(VM& vm, Structure* structure, WeakMapPrototype* prototype) + { + WeakMapConstructor* constructor = new (NotNull, allocateCell<WeakMapConstructor>(vm.heap)) WeakMapConstructor(vm, structure); + constructor->finishCreation(vm, prototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + WeakMapConstructor(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, WeakMapPrototype*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} + +#endif // !defined(WeakMapConstructor_h) diff --git a/Source/JavaScriptCore/runtime/WeakMapData.cpp b/Source/JavaScriptCore/runtime/WeakMapData.cpp new file mode 100644 index 000000000..8ee9de74a --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakMapData.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "WeakMapData.h" + +#include "CopiedAllocator.h" +#include "CopyVisitorInlines.h" +#include "ExceptionHelpers.h" +#include "JSCJSValueInlines.h" +#include "SlotVisitorInlines.h" + +#include <wtf/MathExtras.h> + +namespace JSC { + +const ClassInfo WeakMapData::s_info = { "WeakMapData", 0, 0, CREATE_METHOD_TABLE(WeakMapData) }; + +WeakMapData::WeakMapData(VM& vm) + : Base(vm, vm.weakMapDataStructure.get()) + , m_deadKeyCleaner(this) +{ +} + +void WeakMapData::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +void WeakMapData::destroy(JSCell* cell) +{ + static_cast<WeakMapData*>(cell)->~WeakMapData(); +} + +void WeakMapData::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + Base::visitChildren(cell, visitor); + WeakMapData* thisObj = jsCast<WeakMapData*>(cell); + visitor.addUnconditionalFinalizer(&thisObj->m_deadKeyCleaner); + visitor.addWeakReferenceHarvester(&thisObj->m_deadKeyCleaner); + + // Rough approximation of the external storage needed for the hashtable. + // This isn't exact, but it is close enough, and proportional to the actual + // external mermory usage. + visitor.reportExtraMemoryVisited(thisObj, thisObj->m_map.capacity() * (sizeof(JSObject*) + sizeof(WriteBarrier<Unknown>))); +} + +void WeakMapData::set(VM& vm, JSObject* key, JSValue value) +{ + // Here we force the write barrier on the key. + auto result = m_map.add(WriteBarrier<JSObject>(vm, this, key).get(), WriteBarrier<Unknown>()); + result.iterator->value.set(vm, this, value); +} + +JSValue WeakMapData::get(JSObject* key) +{ + auto iter = m_map.find(key); + if (iter == m_map.end()) + return jsUndefined(); + return iter->value.get(); +} + +bool WeakMapData::remove(JSObject* key) +{ + auto iter = m_map.find(key); + if (iter == m_map.end()) + return false; + + m_map.remove(iter); + return true; +} + +bool WeakMapData::contains(JSObject* key) +{ + return m_map.contains(key); +} + +void WeakMapData::clear() +{ + m_map.clear(); +} + +void WeakMapData::DeadKeyCleaner::visitWeakReferences(SlotVisitor& visitor) +{ + m_liveKeyCount = 0; + for (auto it = m_target->m_map.begin(), end = m_target->m_map.end(); it != end; ++it) { + if (!Heap::isMarked(it->key)) + continue; + m_liveKeyCount++; + visitor.append(&it->value); + } + RELEASE_ASSERT(m_liveKeyCount <= m_target->m_map.size()); +} + +void WeakMapData::DeadKeyCleaner::finalizeUnconditionally() +{ + if (m_liveKeyCount > m_target->m_map.size() / 2) { + RELEASE_ASSERT(m_liveKeyCount <= m_target->m_map.size()); + int deadCount = m_target->m_map.size() - m_liveKeyCount; + if (!deadCount) + return; + Vector<JSObject*> deadEntries; + deadEntries.reserveCapacity(deadCount); + for (auto it = m_target->m_map.begin(), end = m_target->m_map.end(); it != end; ++it) { + if (Heap::isMarked(it->key)) + continue; + deadEntries.uncheckedAppend(it->key); + } + for (size_t i = 0; i < deadEntries.size(); i++) + m_target->m_map.remove(deadEntries[i]); + } else { + MapType newMap; + for (auto it = m_target->m_map.begin(), end = m_target->m_map.end(); it != end; ++it) { + if (!Heap::isMarked(it->key)) + continue; + newMap.add(it->key, it->value); + } + m_target->m_map.swap(newMap); + } +} + +} diff --git a/Source/JavaScriptCore/runtime/WeakMapData.h b/Source/JavaScriptCore/runtime/WeakMapData.h new file mode 100644 index 000000000..cfcd85355 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakMapData.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef WeakMapData_h +#define WeakMapData_h + +#include "JSCell.h" +#include "Structure.h" +#include <wtf/HashFunctions.h> +#include <wtf/HashMap.h> +#include <wtf/MathExtras.h> + +namespace JSC { + +class WeakMapData final : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static WeakMapData* create(VM& vm) + { + WeakMapData* weakMapData = new (NotNull, allocateCell<WeakMapData>(vm.heap)) WeakMapData(vm); + weakMapData->finishCreation(vm); + return weakMapData; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); + } + + static const bool needsDestruction = true; + + void set(VM&, JSObject*, JSValue); + JSValue get(JSObject*); + bool remove(JSObject*); + bool contains(JSObject*); + void clear(); + + DECLARE_INFO; + + typedef HashMap<JSObject*, WriteBarrier<Unknown>> MapType; + MapType::const_iterator begin() const { return m_map.begin(); } + MapType::const_iterator end() const { return m_map.end(); } + + int size() const { return m_map.size(); } + +private: + WeakMapData(VM&); + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + void finishCreation(VM&); + + class DeadKeyCleaner : public UnconditionalFinalizer, public WeakReferenceHarvester { + public: + DeadKeyCleaner(WeakMapData* target) + : m_target(target) + { + } + private: + virtual void visitWeakReferences(SlotVisitor&) override; + virtual void finalizeUnconditionally() override; + unsigned m_liveKeyCount; + WeakMapData* m_target; + }; + DeadKeyCleaner m_deadKeyCleaner; + MapType m_map; +}; + +} + +#endif /* !defined(WeakMapData_h) */ diff --git a/Source/JavaScriptCore/runtime/WeakMapPrototype.cpp b/Source/JavaScriptCore/runtime/WeakMapPrototype.cpp new file mode 100644 index 000000000..71c4c4055 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakMapPrototype.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "WeakMapPrototype.h" + +#include "JSCJSValueInlines.h" +#include "JSWeakMap.h" +#include "StructureInlines.h" +#include "WeakMapData.h" + +namespace JSC { + +const ClassInfo WeakMapPrototype::s_info = { "WeakMap", &Base::s_info, 0, CREATE_METHOD_TABLE(WeakMapPrototype) }; + +static EncodedJSValue JSC_HOST_CALL protoFuncWeakMapDelete(ExecState*); +static EncodedJSValue JSC_HOST_CALL protoFuncWeakMapGet(ExecState*); +static EncodedJSValue JSC_HOST_CALL protoFuncWeakMapHas(ExecState*); +static EncodedJSValue JSC_HOST_CALL protoFuncWeakMapSet(ExecState*); + +void WeakMapPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + JSC_NATIVE_FUNCTION(vm.propertyNames->deleteKeyword, protoFuncWeakMapDelete, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->get, protoFuncWeakMapGet, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->has, protoFuncWeakMapHas, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->set, protoFuncWeakMapSet, DontEnum, 2); +} + +static WeakMapData* getWeakMapData(CallFrame* callFrame, JSValue value) +{ + if (!value.isObject()) { + throwTypeError(callFrame, WTF::ASCIILiteral("Called WeakMap function on non-object")); + return nullptr; + } + + if (JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(value)) + return weakMap->weakMapData(); + + throwTypeError(callFrame, WTF::ASCIILiteral("Called WeakMap function on a non-WeakMap object")); + return nullptr; +} + +EncodedJSValue JSC_HOST_CALL protoFuncWeakMapDelete(CallFrame* callFrame) +{ + WeakMapData* map = getWeakMapData(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + JSValue key = callFrame->argument(0); + return JSValue::encode(jsBoolean(key.isObject() && map->remove(asObject(key)))); +} + +EncodedJSValue JSC_HOST_CALL protoFuncWeakMapGet(CallFrame* callFrame) +{ + WeakMapData* map = getWeakMapData(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + JSValue key = callFrame->argument(0); + if (!key.isObject()) + return JSValue::encode(jsUndefined()); + return JSValue::encode(map->get(asObject(key))); +} + +EncodedJSValue JSC_HOST_CALL protoFuncWeakMapHas(CallFrame* callFrame) +{ + WeakMapData* map = getWeakMapData(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + JSValue key = callFrame->argument(0); + return JSValue::encode(jsBoolean(key.isObject() && map->contains(asObject(key)))); +} + +EncodedJSValue JSC_HOST_CALL protoFuncWeakMapSet(CallFrame* callFrame) +{ + WeakMapData* map = getWeakMapData(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + JSValue key = callFrame->argument(0); + if (!key.isObject()) + return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral("Attempted to set a non-object key in a WeakMap"))); + map->set(callFrame->vm(), asObject(key), callFrame->argument(1)); + return JSValue::encode(callFrame->thisValue()); +} + +} diff --git a/Source/JavaScriptCore/runtime/WeakMapPrototype.h b/Source/JavaScriptCore/runtime/WeakMapPrototype.h new file mode 100644 index 000000000..bbf81ca6d --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakMapPrototype.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WeakMapPrototype_h +#define WeakMapPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class WeakMapPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static WeakMapPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + WeakMapPrototype* prototype = new (NotNull, allocateCell<WeakMapPrototype>(vm.heap)) WeakMapPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + WeakMapPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); +}; + +} + +#endif // !defined(WeakMapPrototype_h) diff --git a/Source/JavaScriptCore/runtime/WeakRandom.h b/Source/JavaScriptCore/runtime/WeakRandom.h new file mode 100644 index 000000000..98b23c70d --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakRandom.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2009 Apple Inc. 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 APPLE INC. ``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 APPLE INC. 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. + * + * + * Copyright (c) 2009 Ian C. Bullard + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef WeakRandom_h +#define WeakRandom_h + +#include <limits.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { + +class WeakRandom { +friend class JSGlobalObject; // For access to initializeSeed() during replay. +public: + WeakRandom(unsigned seed) + { + initializeSeed(seed); + } + + // Returns the seed provided that you've never called get() or getUint32(). + unsigned seedUnsafe() const { return m_high; } + + double get() + { + return advance() / (UINT_MAX + 1.0); + } + + unsigned getUint32() + { + return advance(); + } + +private: + unsigned advance() + { + m_high = (m_high << 16) + (m_high >> 16); + m_high += m_low; + m_low += m_high; + return m_high; + } + + void initializeSeed(unsigned seed) + { + m_low = seed ^ 0x49616E42; + m_high = seed; + } + + unsigned m_low; + unsigned m_high; +}; + +} // namespace JSC + +#endif // WeakRandom_h diff --git a/Source/JavaScriptCore/runtime/WeakSetConstructor.cpp b/Source/JavaScriptCore/runtime/WeakSetConstructor.cpp new file mode 100644 index 000000000..f24ea8136 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakSetConstructor.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "WeakSetConstructor.h" + +#include "Error.h" +#include "IteratorOperations.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "JSWeakSet.h" +#include "StructureInlines.h" +#include "WeakSetPrototype.h" + +namespace JSC { + +const ClassInfo WeakSetConstructor::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(WeakSetConstructor) }; + +void WeakSetConstructor::finishCreation(VM& vm, WeakSetPrototype* prototype) +{ + Base::finishCreation(vm, prototype->classInfo()->className); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), ReadOnly | DontEnum | DontDelete); +} + +static EncodedJSValue JSC_HOST_CALL callWeakSet(ExecState* exec) +{ + return JSValue::encode(throwTypeError(exec, ASCIILiteral("WeakSet cannot be called as a function"))); +} + +static EncodedJSValue JSC_HOST_CALL constructWeakSet(ExecState* exec) +{ + JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject(); + Structure* weakSetStructure = globalObject->weakSetStructure(); + JSWeakSet* weakSet = JSWeakSet::create(exec, weakSetStructure); + JSValue iterable = exec->argument(0); + if (iterable.isUndefinedOrNull()) + return JSValue::encode(weakSet); + + JSValue adderFunction = weakSet->JSObject::get(exec, exec->propertyNames().add); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData adderFunctionCallData; + CallType adderFunctionCallType = getCallData(adderFunction, adderFunctionCallData); + if (adderFunctionCallType == CallTypeNone) + return JSValue::encode(throwTypeError(exec)); + + JSValue iteratorFunction = iterable.get(exec, exec->propertyNames().iteratorSymbol); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + CallData iteratorFunctionCallData; + CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData); + if (iteratorFunctionCallType == CallTypeNone) + return JSValue::encode(throwTypeError(exec)); + + ArgList iteratorFunctionArguments; + JSValue iterator = call(exec, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (!iterator.isObject()) + return JSValue::encode(throwTypeError(exec)); + + while (true) { + JSValue next = iteratorStep(exec, iterator); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + if (next.isFalse()) + return JSValue::encode(weakSet); + + JSValue nextValue = iteratorValue(exec, next); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + + MarkedArgumentBuffer arguments; + arguments.append(nextValue); + call(exec, adderFunction, adderFunctionCallType, adderFunctionCallData, weakSet, arguments); + if (exec->hadException()) { + iteratorClose(exec, iterator); + return JSValue::encode(jsUndefined()); + } + } + RELEASE_ASSERT_NOT_REACHED(); + return JSValue::encode(weakSet); +} + +ConstructType WeakSetConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructWeakSet; + return ConstructTypeHost; +} + +CallType WeakSetConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callWeakSet; + return CallTypeHost; +} + +} diff --git a/Source/JavaScriptCore/runtime/WeakSetConstructor.h b/Source/JavaScriptCore/runtime/WeakSetConstructor.h new file mode 100644 index 000000000..7f6fb6a5e --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakSetConstructor.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WeakSetConstructor_h +#define WeakSetConstructor_h + +#include "InternalFunction.h" + +namespace JSC { + +class WeakSetPrototype; + +class WeakSetConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + + static WeakSetConstructor* create(VM& vm, Structure* structure, WeakSetPrototype* prototype) + { + WeakSetConstructor* constructor = new (NotNull, allocateCell<WeakSetConstructor>(vm.heap)) WeakSetConstructor(vm, structure); + constructor->finishCreation(vm, prototype); + return constructor; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + WeakSetConstructor(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, WeakSetPrototype*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} + +#endif // !defined(WeakSetConstructor_h) diff --git a/Source/JavaScriptCore/runtime/WeakSetPrototype.cpp b/Source/JavaScriptCore/runtime/WeakSetPrototype.cpp new file mode 100644 index 000000000..b718af74e --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakSetPrototype.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "WeakSetPrototype.h" + +#include "JSCJSValueInlines.h" +#include "JSWeakSet.h" +#include "StructureInlines.h" +#include "WeakMapData.h" + +namespace JSC { + +const ClassInfo WeakSetPrototype::s_info = { "WeakSet", &Base::s_info, 0, CREATE_METHOD_TABLE(WeakSetPrototype) }; + +static EncodedJSValue JSC_HOST_CALL protoFuncWeakSetDelete(ExecState*); +static EncodedJSValue JSC_HOST_CALL protoFuncWeakSetHas(ExecState*); +static EncodedJSValue JSC_HOST_CALL protoFuncWeakSetAdd(ExecState*); + +void WeakSetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + vm.prototypeMap.addPrototype(this); + + JSC_NATIVE_FUNCTION(vm.propertyNames->deleteKeyword, protoFuncWeakSetDelete, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->has, protoFuncWeakSetHas, DontEnum, 1); + JSC_NATIVE_FUNCTION(vm.propertyNames->add, protoFuncWeakSetAdd, DontEnum, 1); +} + +static WeakMapData* getWeakMapData(CallFrame* callFrame, JSValue value) +{ + if (!value.isObject()) { + throwTypeError(callFrame, WTF::ASCIILiteral("Called WeakSet function on non-object")); + return nullptr; + } + + if (JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(value)) + return weakSet->weakMapData(); + + throwTypeError(callFrame, WTF::ASCIILiteral("Called WeakSet function on a non-WeakSet object")); + return nullptr; +} + +EncodedJSValue JSC_HOST_CALL protoFuncWeakSetDelete(CallFrame* callFrame) +{ + WeakMapData* map = getWeakMapData(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + JSValue key = callFrame->argument(0); + return JSValue::encode(jsBoolean(key.isObject() && map->remove(asObject(key)))); +} + +EncodedJSValue JSC_HOST_CALL protoFuncWeakSetHas(CallFrame* callFrame) +{ + WeakMapData* map = getWeakMapData(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + JSValue key = callFrame->argument(0); + return JSValue::encode(jsBoolean(key.isObject() && map->contains(asObject(key)))); +} + +EncodedJSValue JSC_HOST_CALL protoFuncWeakSetAdd(CallFrame* callFrame) +{ + WeakMapData* map = getWeakMapData(callFrame, callFrame->thisValue()); + if (!map) + return JSValue::encode(jsUndefined()); + JSValue key = callFrame->argument(0); + if (!key.isObject()) + return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral("Attempted to add a non-object key to a WeakSet"))); + map->set(callFrame->vm(), asObject(key), jsUndefined()); + return JSValue::encode(callFrame->thisValue()); +} + +} diff --git a/Source/JavaScriptCore/runtime/WeakSetPrototype.h b/Source/JavaScriptCore/runtime/WeakSetPrototype.h new file mode 100644 index 000000000..94d90f9c9 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WeakSetPrototype.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Apple, Inc. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef WeakSetPrototype_h +#define WeakSetPrototype_h + +#include "JSObject.h" + +namespace JSC { + +class WeakSetPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static WeakSetPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure) + { + WeakSetPrototype* prototype = new (NotNull, allocateCell<WeakSetPrototype>(vm.heap)) WeakSetPrototype(vm, structure); + prototype->finishCreation(vm, globalObject); + return prototype; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } + +private: + WeakSetPrototype(VM& vm, Structure* structure) + : Base(vm, structure) + { + } + void finishCreation(VM&, JSGlobalObject*); +}; + +} + +#endif // !defined(WeakSetPrototype_h) diff --git a/Source/JavaScriptCore/runtime/WriteBarrier.h b/Source/JavaScriptCore/runtime/WriteBarrier.h new file mode 100644 index 000000000..0f70e1ea0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WriteBarrier.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2011, 2013 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef WriteBarrier_h +#define WriteBarrier_h + +#include "GCAssertions.h" +#include "HandleTypes.h" +#include "Heap.h" +#include "SamplingCounter.h" + +namespace JSC { + +namespace DFG { +class DesiredWriteBarrier; +} + +class JSCell; +class VM; +class JSGlobalObject; + +template<class T> class WriteBarrierBase; +template<> class WriteBarrierBase<JSValue>; + +JS_EXPORT_PRIVATE void slowValidateCell(JSCell*); +JS_EXPORT_PRIVATE void slowValidateCell(JSGlobalObject*); + +#if ENABLE(GC_VALIDATION) +template<class T> inline void validateCell(T cell) +{ + ASSERT_GC_OBJECT_INHERITS(cell, std::remove_pointer<T>::type::info()); +} + +template<> inline void validateCell<JSCell*>(JSCell* cell) +{ + slowValidateCell(cell); +} + +template<> inline void validateCell<JSGlobalObject*>(JSGlobalObject* globalObject) +{ + slowValidateCell(globalObject); +} +#else +template<class T> inline void validateCell(T) +{ +} +#endif + +// We have a separate base class with no constructors for use in Unions. +template <typename T> class WriteBarrierBase { +public: + void set(VM&, const JSCell* owner, T* value); + + // This is meant to be used like operator=, but is called copyFrom instead, in + // order to kindly inform the C++ compiler that its advice is not appreciated. + void copyFrom(const WriteBarrierBase<T>& other) + { + m_cell = other.m_cell; + } + + void setMayBeNull(VM&, const JSCell* owner, T* value); + + // Should only be used by JSCell during early initialisation + // when some basic types aren't yet completely instantiated + void setEarlyValue(VM&, const JSCell* owner, T* value); + + T* get() const + { + // Copy m_cell to a local to avoid multiple-read issues. (See <http://webkit.org/b/110854>) + JSCell* cell = m_cell; + if (cell) + validateCell(cell); + return reinterpret_cast<T*>(static_cast<void*>(cell)); + } + + T* operator*() const + { + ASSERT(m_cell); + validateCell<T>(static_cast<T*>(m_cell)); + return static_cast<T*>(m_cell); + } + + T* operator->() const + { + ASSERT(m_cell); + validateCell(static_cast<T*>(m_cell)); + return static_cast<T*>(m_cell); + } + + void clear() { m_cell = 0; } + + T** slot() { return reinterpret_cast<T**>(&m_cell); } + + explicit operator bool() const { return m_cell; } + + bool operator!() const { return !m_cell; } + + void setWithoutWriteBarrier(T* value) + { +#if ENABLE(WRITE_BARRIER_PROFILING) + WriteBarrierCounters::usesWithoutBarrierFromCpp.count(); +#endif + this->m_cell = reinterpret_cast<JSCell*>(value); + } + + T* unvalidatedGet() const { return reinterpret_cast<T*>(static_cast<void*>(m_cell)); } + +private: + JSCell* m_cell; +}; + +template <> class WriteBarrierBase<Unknown> { +public: + void set(VM&, const JSCell* owner, JSValue); + void setWithoutWriteBarrier(JSValue value) + { + m_value = JSValue::encode(value); + } + + JSValue get() const + { + return JSValue::decode(m_value); + } + void clear() { m_value = JSValue::encode(JSValue()); } + void setUndefined() { m_value = JSValue::encode(jsUndefined()); } + void setStartingValue(JSValue value) { m_value = JSValue::encode(value); } + bool isNumber() const { return get().isNumber(); } + bool isObject() const { return get().isObject(); } + bool isNull() const { return get().isNull(); } + bool isGetterSetter() const { return get().isGetterSetter(); } + bool isCustomGetterSetter() const { return get().isCustomGetterSetter(); } + + JSValue* slot() + { + union { + EncodedJSValue* v; + JSValue* slot; + } u; + u.v = &m_value; + return u.slot; + } + + int32_t* tagPointer() { return &bitwise_cast<EncodedValueDescriptor*>(&m_value)->asBits.tag; } + int32_t* payloadPointer() { return &bitwise_cast<EncodedValueDescriptor*>(&m_value)->asBits.payload; } + + explicit operator bool() const { return !!get(); } + bool operator!() const { return !get(); } + +private: + EncodedJSValue m_value; +}; + +template <typename T> class WriteBarrier : public WriteBarrierBase<T> { + WTF_MAKE_FAST_ALLOCATED; +public: + WriteBarrier() + { + this->setWithoutWriteBarrier(0); + } + + WriteBarrier(VM& vm, const JSCell* owner, T* value) + { + this->set(vm, owner, value); + } + + WriteBarrier(DFG::DesiredWriteBarrier&, T* value) + { + ASSERT(isCompilationThread()); + this->setWithoutWriteBarrier(value); + } + + enum MayBeNullTag { MayBeNull }; + WriteBarrier(VM& vm, const JSCell* owner, T* value, MayBeNullTag) + { + this->setMayBeNull(vm, owner, value); + } +}; + +enum UndefinedWriteBarrierTagType { UndefinedWriteBarrierTag }; +template <> class WriteBarrier<Unknown> : public WriteBarrierBase<Unknown> { + WTF_MAKE_FAST_ALLOCATED; +public: + WriteBarrier() + { + this->setWithoutWriteBarrier(JSValue()); + } + WriteBarrier(UndefinedWriteBarrierTagType) + { + this->setWithoutWriteBarrier(jsUndefined()); + } + + WriteBarrier(VM& vm, const JSCell* owner, JSValue value) + { + this->set(vm, owner, value); + } + + WriteBarrier(DFG::DesiredWriteBarrier&, JSValue value) + { + ASSERT(isCompilationThread()); + this->setWithoutWriteBarrier(value); + } +}; + +template <typename U, typename V> inline bool operator==(const WriteBarrierBase<U>& lhs, const WriteBarrierBase<V>& rhs) +{ + return lhs.get() == rhs.get(); +} + +} // namespace JSC + +#endif // WriteBarrier_h diff --git a/Source/JavaScriptCore/runtime/WriteBarrierInlines.h b/Source/JavaScriptCore/runtime/WriteBarrierInlines.h new file mode 100644 index 000000000..8271e48d6 --- /dev/null +++ b/Source/JavaScriptCore/runtime/WriteBarrierInlines.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#ifndef WriteBarrierInlines_h +#define WriteBarrierInlines_h + +#include "VM.h" +#include "WriteBarrier.h" + +namespace JSC { + +template <typename T> +inline void WriteBarrierBase<T>::set(VM& vm, const JSCell* owner, T* value) +{ + ASSERT(value); + ASSERT(!Options::enableConcurrentJIT() || !isCompilationThread()); + validateCell(value); + setEarlyValue(vm, owner, value); +} + +template <typename T> +inline void WriteBarrierBase<T>::setMayBeNull(VM& vm, const JSCell* owner, T* value) +{ + if (value) + validateCell(value); + setEarlyValue(vm, owner, value); +} + +template <typename T> +inline void WriteBarrierBase<T>::setEarlyValue(VM& vm, const JSCell* owner, T* value) +{ + this->m_cell = reinterpret_cast<JSCell*>(value); + vm.heap.writeBarrier(owner, this->m_cell); +} + +inline void WriteBarrierBase<Unknown>::set(VM& vm, const JSCell* owner, JSValue value) +{ + ASSERT(!Options::enableConcurrentJIT() || !isCompilationThread()); + m_value = JSValue::encode(value); + vm.heap.writeBarrier(owner, value); +} + +} // namespace JSC + +#endif // WriteBarrierInlines_h |
