summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-09-14 16:29:47 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-09-14 16:29:47 +0200
commitd0424a769059c84ae20beb3c217812792ea6726b (patch)
tree6f94a5c3db8c52c6694ee56498542a6c35417350 /Source/JavaScriptCore/runtime
parent88a04ac016f57c2d78e714682445dff2e7db4ade (diff)
downloadqtwebkit-d0424a769059c84ae20beb3c217812792ea6726b.tar.gz
Imported WebKit commit 37c5e5041d39a14ea0d429a77ebd352e4bd26516 (http://svn.webkit.org/repository/webkit/trunk@128608)
New snapshot that enables WebKit2 build on Windows (still some bugs) and allows for WebKit to be built with qmake && make
Diffstat (limited to 'Source/JavaScriptCore/runtime')
-rw-r--r--Source/JavaScriptCore/runtime/Arguments.cpp4
-rw-r--r--Source/JavaScriptCore/runtime/Arguments.h1
-rw-r--r--Source/JavaScriptCore/runtime/ArrayConstructor.cpp2
-rw-r--r--Source/JavaScriptCore/runtime/ArrayConventions.h103
-rw-r--r--Source/JavaScriptCore/runtime/ArrayPrototype.cpp60
-rw-r--r--Source/JavaScriptCore/runtime/ArrayPrototype.h11
-rw-r--r--Source/JavaScriptCore/runtime/ArrayStorage.h106
-rw-r--r--Source/JavaScriptCore/runtime/Butterfly.h106
-rw-r--r--Source/JavaScriptCore/runtime/ButterflyInlineMethods.h179
-rw-r--r--Source/JavaScriptCore/runtime/ClassInfo.h4
-rw-r--r--Source/JavaScriptCore/runtime/Executable.h8
-rw-r--r--Source/JavaScriptCore/runtime/IndexingHeader.h122
-rw-r--r--Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h54
-rw-r--r--Source/JavaScriptCore/runtime/IndexingType.h61
-rw-r--r--Source/JavaScriptCore/runtime/JSActivation.cpp64
-rw-r--r--Source/JavaScriptCore/runtime/JSActivation.h156
-rw-r--r--Source/JavaScriptCore/runtime/JSArray.cpp1807
-rw-r--r--Source/JavaScriptCore/runtime/JSArray.h319
-rw-r--r--Source/JavaScriptCore/runtime/JSBoundFunction.cpp8
-rw-r--r--Source/JavaScriptCore/runtime/JSCell.cpp5
-rw-r--r--Source/JavaScriptCore/runtime/JSCell.h23
-rw-r--r--Source/JavaScriptCore/runtime/JSFunction.cpp66
-rw-r--r--Source/JavaScriptCore/runtime/JSFunction.h16
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalData.cpp4
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalData.h1
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalObject.cpp2
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp1
-rw-r--r--Source/JavaScriptCore/runtime/JSONObject.cpp8
-rw-r--r--Source/JavaScriptCore/runtime/JSObject.cpp1042
-rw-r--r--Source/JavaScriptCore/runtime/JSObject.h398
-rw-r--r--Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp3
-rw-r--r--Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp4
-rw-r--r--Source/JavaScriptCore/runtime/JSSymbolTableObject.h13
-rw-r--r--Source/JavaScriptCore/runtime/JSTypeInfo.h10
-rw-r--r--Source/JavaScriptCore/runtime/JSVariableObject.h11
-rw-r--r--Source/JavaScriptCore/runtime/LiteralParser.cpp10
-rw-r--r--Source/JavaScriptCore/runtime/ObjectConstructor.cpp2
-rw-r--r--Source/JavaScriptCore/runtime/ObjectPrototype.cpp29
-rw-r--r--Source/JavaScriptCore/runtime/ObjectPrototype.h6
-rw-r--r--Source/JavaScriptCore/runtime/PropertyOffset.h2
-rw-r--r--Source/JavaScriptCore/runtime/PropertyStorage.h (renamed from Source/JavaScriptCore/runtime/StorageBarrier.h)49
-rw-r--r--Source/JavaScriptCore/runtime/PutDirectIndexMode.h36
-rw-r--r--Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp24
-rw-r--r--Source/JavaScriptCore/runtime/RegExpMatchesArray.h20
-rw-r--r--Source/JavaScriptCore/runtime/RegExpObject.cpp6
-rw-r--r--Source/JavaScriptCore/runtime/RegExpObject.h2
-rw-r--r--Source/JavaScriptCore/runtime/Reject.h44
-rw-r--r--Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp37
-rw-r--r--Source/JavaScriptCore/runtime/SparseArrayValueMap.h134
-rw-r--r--Source/JavaScriptCore/runtime/SparseArrayValueMapInlineMethods.h203
-rw-r--r--Source/JavaScriptCore/runtime/StringObject.cpp19
-rw-r--r--Source/JavaScriptCore/runtime/StringObject.h4
-rw-r--r--Source/JavaScriptCore/runtime/StringPrototype.cpp2
-rw-r--r--Source/JavaScriptCore/runtime/Structure.cpp39
-rw-r--r--Source/JavaScriptCore/runtime/Structure.h20
-rw-r--r--Source/JavaScriptCore/runtime/StructureTransitionTable.h30
-rw-r--r--Source/JavaScriptCore/runtime/SymbolTable.h32
57 files changed, 3526 insertions, 2006 deletions
diff --git a/Source/JavaScriptCore/runtime/Arguments.cpp b/Source/JavaScriptCore/runtime/Arguments.cpp
index fe79f740e..47795edb2 100644
--- a/Source/JavaScriptCore/runtime/Arguments.cpp
+++ b/Source/JavaScriptCore/runtime/Arguments.cpp
@@ -255,7 +255,7 @@ bool Arguments::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
}
}
- return JSObject::deleteProperty(thisObject, exec, Identifier(exec, String::number(i)));
+ return JSObject::deletePropertyByIndex(thisObject, exec, i);
}
bool Arguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
@@ -308,7 +308,7 @@ bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyNam
// If the property is not yet present on the object, and is not yet marked as deleted, then add it now.
PropertySlot slot;
if ((!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
- object->putDirect(exec->globalData(), propertyName, thisObject->argument(i).get(), 0);
+ object->putDirectMayBeIndex(exec, propertyName, thisObject->argument(i).get());
if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow))
return false;
diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h
index 1d0bffd6b..c3d25f962 100644
--- a/Source/JavaScriptCore/runtime/Arguments.h
+++ b/Source/JavaScriptCore/runtime/Arguments.h
@@ -110,7 +110,6 @@ namespace JSC {
d->activation.set(globalData, this, activation);
d->registers = &activation->registerAt(0);
}
- void setRegisters(WriteBarrierBase<Unknown>* registers) { d->registers = registers; }
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
{
diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp
index c9a6dc600..eda35e146 100644
--- a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp
+++ b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp
@@ -25,6 +25,8 @@
#include "ArrayConstructor.h"
#include "ArrayPrototype.h"
+#include "ButterflyInlineMethods.h"
+#include "CopiedSpaceInlineMethods.h"
#include "Error.h"
#include "ExceptionHelpers.h"
#include "JSArray.h"
diff --git a/Source/JavaScriptCore/runtime/ArrayConventions.h b/Source/JavaScriptCore/runtime/ArrayConventions.h
new file mode 100644
index 000000000..af98e15fa
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/ArrayConventions.h
@@ -0,0 +1,103 @@
+/*
+ * 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 {
+
+#define CHECK_ARRAY_CONSISTENCY 0
+
+// 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 identifer, 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>.
+#define MIN_SPARSE_ARRAY_INDEX 10000U
+#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
+
+// 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 <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues;
+}
+
+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/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
index 4b13f993c..503aecda8 100644
--- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
@@ -24,8 +24,10 @@
#include "config.h"
#include "ArrayPrototype.h"
+#include "ButterflyInlineMethods.h"
#include "CachedCall.h"
#include "CodeBlock.h"
+#include "CopiedSpaceInlineMethods.h"
#include "Interpreter.h"
#include "JIT.h"
#include "JSStringBuilder.h"
@@ -114,9 +116,17 @@ const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, 0, ExecStat
@end
*/
+ArrayPrototype* ArrayPrototype::create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
+{
+ Butterfly* butterfly = createArrayButterfly(exec->globalData(), 0);
+ ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure, butterfly);
+ prototype->finishCreation(globalObject);
+ return prototype;
+}
+
// ECMA 15.4.4
-ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure)
- : JSArray(globalObject->globalData(), structure)
+ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure, Butterfly* butterfly)
+ : JSArray(globalObject->globalData(), structure, butterfly)
{
}
@@ -292,8 +302,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
for (unsigned k = 0; k < length; k++) {
JSValue element;
- if (thisObj->canGetIndex(k))
- element = thisObj->getIndex(k);
+ if (thisObj->canGetIndexQuickly(k))
+ element = thisObj->getIndexQuickly(k);
else
element = thisObj->get(exec, k);
@@ -413,10 +423,10 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
JSArray* array = asArray(thisObj);
for (; k < length; k++) {
- if (!array->canGetIndex(k))
+ if (!array->canGetIndexQuickly(k))
break;
- JSValue element = array->getIndex(k);
+ JSValue element = array->getIndexQuickly(k);
if (!element.isUndefinedOrNull())
stringJoiner.append(element.toWTFStringInline(exec));
else
@@ -628,7 +638,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec)
CallData callData;
CallType callType = getCallData(function, callData);
- if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseMode()) {
+ if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseIndexingMode()) {
if (isNumericCompareFunction(exec, callType, callData))
asArray(thisObj)->sortNumeric(exec, function, callType, callData);
else if (callType != CallTypeNone)
@@ -788,9 +798,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec)
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
- if (!array->canGetIndex(k))
+ if (!array->canGetIndexQuickly(k))
break;
- JSValue v = array->getIndex(k);
+ JSValue v = array->getIndexQuickly(k);
cachedCall.setThis(applyThis);
cachedCall.setArgument(0, v);
cachedCall.setArgument(1, jsNumber(k));
@@ -846,11 +856,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec)
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
- if (UNLIKELY(!array->canGetIndex(k)))
+ if (UNLIKELY(!array->canGetIndexQuickly(k)))
break;
cachedCall.setThis(applyThis);
- cachedCall.setArgument(0, array->getIndex(k));
+ cachedCall.setArgument(0, array->getIndexQuickly(k));
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
@@ -909,11 +919,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec)
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
- if (UNLIKELY(!array->canGetIndex(k)))
+ if (UNLIKELY(!array->canGetIndexQuickly(k)))
break;
cachedCall.setThis(applyThis);
- cachedCall.setArgument(0, array->getIndex(k));
+ cachedCall.setArgument(0, array->getIndexQuickly(k));
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
JSValue result = cachedCall.call();
@@ -965,11 +975,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec)
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
- if (UNLIKELY(!array->canGetIndex(k)))
+ if (UNLIKELY(!array->canGetIndexQuickly(k)))
break;
cachedCall.setThis(applyThis);
- cachedCall.setArgument(0, array->getIndex(k));
+ cachedCall.setArgument(0, array->getIndexQuickly(k));
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
@@ -1017,11 +1027,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec)
JSArray* array = asArray(thisObj);
CachedCall cachedCall(exec, f, 3);
for (; k < length && !exec->hadException(); ++k) {
- if (UNLIKELY(!array->canGetIndex(k)))
+ if (UNLIKELY(!array->canGetIndexQuickly(k)))
break;
cachedCall.setThis(applyThis);
- cachedCall.setArgument(0, array->getIndex(k));
+ cachedCall.setArgument(0, array->getIndexQuickly(k));
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
JSValue result = cachedCall.call();
@@ -1075,8 +1085,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec)
if (exec->argumentCount() >= 2)
rv = exec->argument(1);
- else if (array && array->canGetIndex(0)){
- rv = array->getIndex(0);
+ else if (array && array->canGetIndexQuickly(0)) {
+ rv = array->getIndexQuickly(0);
i = 1;
} else {
for (i = 0; i < length; i++) {
@@ -1097,8 +1107,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec)
cachedCall.setThis(jsUndefined());
cachedCall.setArgument(0, rv);
JSValue v;
- if (LIKELY(array->canGetIndex(i)))
- v = array->getIndex(i);
+ if (LIKELY(array->canGetIndexQuickly(i)))
+ v = array->getIndexQuickly(i);
else
break; // length has been made unsafe while we enumerate fallback to slow path
cachedCall.setArgument(1, v);
@@ -1152,8 +1162,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec)
if (exec->argumentCount() >= 2)
rv = exec->argument(1);
- else if (array && array->canGetIndex(length - 1)){
- rv = array->getIndex(length - 1);
+ else if (array && array->canGetIndexQuickly(length - 1)) {
+ rv = array->getIndexQuickly(length - 1);
i = 1;
} else {
for (i = 0; i < length; i++) {
@@ -1174,9 +1184,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec)
unsigned idx = length - i - 1;
cachedCall.setThis(jsUndefined());
cachedCall.setArgument(0, rv);
- if (UNLIKELY(!array->canGetIndex(idx)))
+ if (UNLIKELY(!array->canGetIndexQuickly(idx)))
break; // length has been made unsafe while we enumerate fallback to slow path
- cachedCall.setArgument(1, array->getIndex(idx));
+ cachedCall.setArgument(1, array->getIndexQuickly(idx));
cachedCall.setArgument(2, jsNumber(idx));
cachedCall.setArgument(3, array);
rv = cachedCall.call();
diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.h b/Source/JavaScriptCore/runtime/ArrayPrototype.h
index 4f52fb61f..b33021121 100644
--- a/Source/JavaScriptCore/runtime/ArrayPrototype.h
+++ b/Source/JavaScriptCore/runtime/ArrayPrototype.h
@@ -28,17 +28,12 @@ namespace JSC {
class ArrayPrototype : public JSArray {
private:
- ArrayPrototype(JSGlobalObject*, Structure*);
+ ArrayPrototype(JSGlobalObject*, Structure*, Butterfly*);
public:
typedef JSArray Base;
- static ArrayPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
- {
- ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure);
- prototype->finishCreation(globalObject);
- return prototype;
- }
+ static ArrayPrototype* create(ExecState*, JSGlobalObject*, Structure*);
static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
@@ -47,7 +42,7 @@ namespace JSC {
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
{
- return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
+ return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage);
}
protected:
diff --git a/Source/JavaScriptCore/runtime/ArrayStorage.h b/Source/JavaScriptCore/runtime/ArrayStorage.h
new file mode 100644
index 000000000..d967d0b5a
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/ArrayStorage.h
@@ -0,0 +1,106 @@
+/*
+ * 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>
+#include <wtf/Platform.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)
+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<ArrayStorage*>(butterfly); }
+ static ArrayStorage* from(IndexingHeader* indexingHeader) { return indexingHeader->arrayStorage(); }
+
+ Butterfly* butterfly() { return reinterpret_cast<Butterfly*>(this); }
+ IndexingHeader* indexingHeader() { return IndexingHeader::from(this); }
+
+ // We steal two fields from the indexing header: vectorLength and length.
+ unsigned length() { 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;
+#if CHECK_ARRAY_CONSISTENCY
+ m_initializationIndex = other.m_initializationIndex;
+ m_inCompactInitialization = other.m_inCompactInitialization;
+#endif
+ }
+
+ bool inSparseMode()
+ {
+ return m_sparseMap && m_sparseMap->sparseMode();
+ }
+
+ WriteBarrier<SparseArrayValueMap> m_sparseMap;
+ unsigned m_indexBias;
+ unsigned m_numValuesInVector;
+#if CHECK_ARRAY_CONSISTENCY
+ // Needs to be a uintptr_t for alignment purposes.
+ uintptr_t m_initializationIndex;
+ uintptr_t m_inCompactInitialization;
+#elif 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/Butterfly.h b/Source/JavaScriptCore/runtime/Butterfly.h
new file mode 100644
index 000000000..1926169ba
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/Butterfly.h
@@ -0,0 +1,106 @@
+/*
+ * 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>
+#include <wtf/Platform.h>
+
+namespace JSC {
+
+class JSGlobalData;
+class SlotVisitor;
+struct ArrayStorage;
+
+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);
+ }
+
+ static ptrdiff_t offsetOfIndexingHeader() { return IndexingHeader::offsetOfIndexingHeader(); }
+ static ptrdiff_t offsetOfPublicLength() { return offsetOfIndexingHeader() + IndexingHeader::offsetOfPublicLength(); }
+ static ptrdiff_t offsetOfVectorLength() { return offsetOfIndexingHeader() + IndexingHeader::offsetOfVectorLength(); }
+
+ static Butterfly* createUninitialized(JSGlobalData&, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes);
+
+ static Butterfly* create(JSGlobalData&, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, const IndexingHeader&, size_t indexingPayloadSizeInBytes);
+ static Butterfly* create(JSGlobalData&, Structure*);
+ static Butterfly* createUninitializedDuringCollection(SlotVisitor&, 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(); }
+ template<typename T>
+ T* indexingPayload() { return reinterpret_cast<T*>(this); }
+ ArrayStorage* arrayStorage() { return indexingPayload<ArrayStorage>(); }
+
+ 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*);
+
+ // 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.
+ Butterfly* growPropertyStorage(JSGlobalData&, size_t preCapacity, size_t oldPropertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes, size_t newPropertyCapacity);
+ Butterfly* growPropertyStorage(JSGlobalData&, Structure* oldStructure, size_t oldPropertyCapacity, size_t newPropertyCapacity);
+ Butterfly* growPropertyStorage(JSGlobalData&, Structure* oldStructure, size_t newPropertyCapacity);
+ Butterfly* growArrayRight(JSGlobalData&, Structure* oldStructure, size_t propertyCapacity, bool hadIndexingHeader, size_t oldIndexingPayloadSizeInBytes, size_t newIndexingPayloadSizeInBytes); // Assumes that preCapacity is zero, and asserts as much.
+ Butterfly* growArrayRight(JSGlobalData&, Structure*, size_t newIndexingPayloadSizeInBytes);
+ Butterfly* resizeArray(JSGlobalData&, size_t propertyCapacity, bool oldHasIndexingHeader, size_t oldIndexingPayloadSizeInBytes, size_t newPreCapacity, bool newHasIndexingHeader, size_t newIndexingPayloadSizeInBytes);
+ Butterfly* resizeArray(JSGlobalData&, 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/ButterflyInlineMethods.h b/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h
new file mode 100644
index 000000000..049350342
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h
@@ -0,0 +1,179 @@
+/*
+ * 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 ButterflyInlineMethods_h
+#define ButterflyInlineMethods_h
+
+#include "ArrayStorage.h"
+#include "Butterfly.h"
+#include "CopiedSpaceInlineMethods.h"
+#include "JSGlobalData.h"
+#include "SlotVisitor.h"
+#include "Structure.h"
+
+namespace JSC {
+
+inline Butterfly* Butterfly::createUninitialized(JSGlobalData& globalData, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes)
+{
+ void* temp;
+ size_t size = totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
+ if (!globalData.heap.tryAllocateStorage(size, &temp))
+ CRASH();
+ Butterfly* result = fromBase(temp, preCapacity, propertyCapacity);
+ return result;
+}
+
+inline Butterfly* Butterfly::create(JSGlobalData& globalData, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, const IndexingHeader& indexingHeader, size_t indexingPayloadSizeInBytes)
+{
+ Butterfly* result = createUninitialized(
+ globalData, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
+ if (hasIndexingHeader)
+ *result->indexingHeader() = indexingHeader;
+ return result;
+}
+
+inline Butterfly* Butterfly::create(JSGlobalData& globalData, Structure* structure)
+{
+ return create(globalData, 0, structure->outOfLineCapacity(), hasIndexingHeader(structure->indexingType()), IndexingHeader(), 0);
+}
+
+inline Butterfly* Butterfly::createUninitializedDuringCollection(SlotVisitor& visitor, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes)
+{
+ Butterfly* result = fromBase(
+ visitor.allocateNewSpace(totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes)),
+ preCapacity, propertyCapacity);
+ return result;
+}
+
+inline void* Butterfly::base(Structure* structure)
+{
+ return base(indexingHeader()->preCapacity(structure), structure->outOfLineCapacity());
+}
+
+inline Butterfly* Butterfly::growPropertyStorage(JSGlobalData& globalData, size_t preCapacity, size_t oldPropertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes, size_t newPropertyCapacity)
+{
+ ASSERT(newPropertyCapacity > oldPropertyCapacity);
+ Butterfly* result = createUninitialized(
+ globalData, preCapacity, newPropertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
+ memcpy(
+ result->propertyStorage() - oldPropertyCapacity,
+ propertyStorage() - oldPropertyCapacity,
+ totalSize(0, oldPropertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes));
+ return result;
+}
+
+inline Butterfly* Butterfly::growPropertyStorage(JSGlobalData& globalData, Structure* structure, size_t oldPropertyCapacity, size_t newPropertyCapacity)
+{
+ return growPropertyStorage(
+ globalData, indexingHeader()->preCapacity(structure), oldPropertyCapacity,
+ hasIndexingHeader(structure->indexingType()),
+ indexingHeader()->indexingPayloadSizeInBytes(structure), newPropertyCapacity);
+}
+
+inline Butterfly* Butterfly::growPropertyStorage(JSGlobalData& globalData, Structure* oldStructure, size_t newPropertyCapacity)
+{
+ return growPropertyStorage(
+ globalData, oldStructure, oldStructure->outOfLineCapacity(), newPropertyCapacity);
+}
+
+inline Butterfly* Butterfly::growArrayRight(JSGlobalData& globalData, Structure* oldStructure, size_t propertyCapacity, bool hadIndexingHeader, size_t oldIndexingPayloadSizeInBytes, size_t newIndexingPayloadSizeInBytes)
+{
+ ASSERT_UNUSED(oldStructure, !indexingHeader()->preCapacity(oldStructure));
+ ASSERT_UNUSED(oldStructure, hadIndexingHeader == hasIndexingHeader(oldStructure->indexingType()));
+ void* theBase = base(0, propertyCapacity);
+ size_t oldSize = totalSize(0, propertyCapacity, hadIndexingHeader, oldIndexingPayloadSizeInBytes);
+ size_t newSize = totalSize(0, propertyCapacity, true, newIndexingPayloadSizeInBytes);
+ if (!globalData.heap.tryReallocateStorage(&theBase, oldSize, newSize))
+ return 0;
+ return fromBase(theBase, 0, propertyCapacity);
+}
+
+inline Butterfly* Butterfly::growArrayRight(JSGlobalData& globalData, Structure* oldStructure, size_t newIndexingPayloadSizeInBytes)
+{
+ return growArrayRight(
+ globalData, oldStructure, oldStructure->outOfLineCapacity(),
+ hasIndexingHeader(oldStructure->indexingType()),
+ indexingHeader()->indexingPayloadSizeInBytes(oldStructure), newIndexingPayloadSizeInBytes);
+}
+
+inline Butterfly* Butterfly::resizeArray(JSGlobalData& globalData, size_t propertyCapacity, bool oldHasIndexingHeader, size_t oldIndexingPayloadSizeInBytes, size_t newPreCapacity, bool newHasIndexingHeader, size_t newIndexingPayloadSizeInBytes)
+{
+ Butterfly* result = createUninitialized(
+ globalData, 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(JSGlobalData& globalData, Structure* structure, size_t newPreCapacity, size_t newIndexingPayloadSizeInBytes)
+{
+ bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType());
+ return resizeArray(
+ globalData, structure->outOfLineCapacity(), hasIndexingHeader,
+ indexingHeader()->indexingPayloadSizeInBytes(structure), newPreCapacity,
+ hasIndexingHeader, newIndexingPayloadSizeInBytes);
+}
+
+inline Butterfly* Butterfly::unshift(Structure* structure, size_t numberOfSlots)
+{
+ ASSERT(hasIndexingHeader(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(hasIndexingHeader(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 // ButterflyInlineMethods_h
+
diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h
index 4c72f3ed1..0e1747b24 100644
--- a/Source/JavaScriptCore/runtime/ClassInfo.h
+++ b/Source/JavaScriptCore/runtime/ClassInfo.h
@@ -72,6 +72,9 @@ namespace JSC {
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;
@@ -124,6 +127,7 @@ struct MemberCheck##member { \
&ClassName::toThisObject, \
&ClassName::defaultValue, \
&ClassName::getOwnPropertyNames, \
+ &ClassName::getOwnNonIndexPropertyNames, \
&ClassName::getPropertyNames, \
&ClassName::className, \
&ClassName::hasInstance, \
diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h
index f63cc7f99..76ed21d8b 100644
--- a/Source/JavaScriptCore/runtime/Executable.h
+++ b/Source/JavaScriptCore/runtime/Executable.h
@@ -31,6 +31,7 @@
#include "HandlerInfo.h"
#include "JSFunction.h"
#include "Interpreter.h"
+#include "JSGlobalObject.h"
#include "LLIntCLoop.h"
#include "Nodes.h"
#include "SamplingTool.h"
@@ -754,6 +755,13 @@ namespace JSC {
WriteBarrier<SharedSymbolTable> m_symbolTable;
};
+ inline JSFunction::JSFunction(JSGlobalData& globalData, FunctionExecutable* executable, JSScope* scope)
+ : Base(globalData, scope->globalObject()->functionStructure())
+ , m_executable(globalData, this, executable)
+ , m_scope(globalData, this, scope)
+ {
+ }
+
inline FunctionExecutable* JSFunction::jsExecutable() const
{
ASSERT(!isHostFunctionNonInline());
diff --git a/Source/JavaScriptCore/runtime/IndexingHeader.h b/Source/JavaScriptCore/runtime/IndexingHeader.h
new file mode 100644
index 000000000..caa18183a
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/IndexingHeader.h
@@ -0,0 +1,122 @@
+/*
+ * 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 IndexingHeader_h
+#define IndexingHeader_h
+
+#include "PropertyStorage.h"
+#include <wtf/Platform.h>
+
+namespace JSC {
+
+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 offsetOfPublicLength() { return OBJECT_OFFSETOF(IndexingHeader, m_publicLength); }
+ static ptrdiff_t offsetOfVectorLength() { return OBJECT_OFFSETOF(IndexingHeader, m_vectorLength); }
+
+ IndexingHeader()
+ : m_publicLength(0)
+ , m_vectorLength(0)
+ {
+ }
+
+ uint32_t vectorLength() const { return m_vectorLength; }
+
+ void setVectorLength(uint32_t length)
+ {
+ ASSERT(length <= maximumLength);
+ m_vectorLength = length;
+ }
+
+ uint32_t publicLength() { return m_publicLength; }
+ void setPublicLength(uint32_t auxWord) { m_publicLength = auxWord; }
+
+ 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 reinterpret_cast<IndexingHeader*>(arrayStorage) - 1;
+ }
+
+ static IndexingHeader* fromEndOf(PropertyStorage propertyStorage)
+ {
+ return reinterpret_cast<IndexingHeader*>(propertyStorage);
+ }
+
+ PropertyStorage propertyStorage()
+ {
+ return reinterpret_cast<PropertyStorage>(this);
+ }
+
+ ConstPropertyStorage propertyStorage() const
+ {
+ return reinterpret_cast<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;
+
+ uint32_t m_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 m_vectorLength; // The length of the indexed property storage. The actual size of the storage depends on this, and the type.
+};
+
+} // namespace JSC
+
+#endif // IndexingHeader_h
+
diff --git a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h b/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h
new file mode 100644
index 000000000..8d6e08256
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h
@@ -0,0 +1,54 @@
+/*
+ * 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 IndexingHeaderInlineMethods_h
+#define IndexingHeaderInlineMethods_h
+
+#include "ArrayStorage.h"
+#include "IndexingHeader.h"
+#include "Structure.h"
+
+namespace JSC {
+
+inline size_t IndexingHeader::preCapacity(Structure* structure)
+{
+ if (LIKELY(!(structure->indexingType() & HasArrayStorage)))
+ return 0;
+
+ return arrayStorage()->m_indexBias;
+}
+
+inline size_t IndexingHeader::indexingPayloadSizeInBytes(Structure* structure)
+{
+ if (LIKELY(!(structure->indexingType() & HasArrayStorage)))
+ return 0;
+
+ return ArrayStorage::sizeFor(arrayStorage()->vectorLength());
+}
+
+} // namespace JSC
+
+#endif // IndexingHeaderInlineMethods_h
+
diff --git a/Source/JavaScriptCore/runtime/IndexingType.h b/Source/JavaScriptCore/runtime/IndexingType.h
new file mode 100644
index 000000000..cd8d71dfe
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/IndexingType.h
@@ -0,0 +1,61 @@
+/*
+ * 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 IndexingType_h
+#define IndexingType_h
+
+namespace JSC {
+
+typedef uint8_t IndexingType;
+
+// Flags for testing the presence of capabilities.
+static const IndexingType IsArray = 1;
+static const IndexingType HasArrayStorage = 8;
+
+// Additional flags for tracking the history of the type. These are usually
+// masked off unless you ask for them directly.
+static const IndexingType HadArrayStorage = 16; // Means that this object did have array storage in the past.
+
+// List of acceptable array types.
+static const IndexingType NonArray = 0;
+static const IndexingType NonArrayWithArrayStorage = HasArrayStorage;
+static const IndexingType ArrayClass = IsArray; // I'd want to call this "Array" but this would lead to disastrous namespace pollution.
+static const IndexingType ArrayWithArrayStorage = IsArray | HasArrayStorage;
+
+// Mask of all possible types.
+static const IndexingType AllArrayTypes = 15;
+
+// Mask of all possible types including the history.
+static const IndexingType AllArrayTypesAndHistory = 31;
+
+inline bool hasIndexingHeader(IndexingType type)
+{
+ return !!(type & HasArrayStorage);
+}
+
+} // namespace JSC
+
+#endif // IndexingType_h
+
diff --git a/Source/JavaScriptCore/runtime/JSActivation.cpp b/Source/JavaScriptCore/runtime/JSActivation.cpp
index ae403ce46..dc061bc57 100644
--- a/Source/JavaScriptCore/runtime/JSActivation.cpp
+++ b/Source/JavaScriptCore/runtime/JSActivation.cpp
@@ -41,28 +41,6 @@ ASSERT_CLASS_FITS_IN_CELL(JSActivation);
const ClassInfo JSActivation::s_info = { "JSActivation", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSActivation) };
-JSActivation::JSActivation(CallFrame* callFrame, FunctionExecutable* functionExecutable)
- : Base(
- callFrame->globalData(),
- callFrame->lexicalGlobalObject()->activationStructure(),
- callFrame->registers(),
- callFrame->scope()
- )
- , m_registerArray(callFrame->globalData(), this, 0)
- , m_numCapturedArgs(max(callFrame->argumentCount(), functionExecutable->parameterCount()))
- , m_numCapturedVars(functionExecutable->capturedVariableCount())
- , m_isTornOff(false)
- , m_requiresDynamicChecks(functionExecutable->usesEval() && !functionExecutable->isStrictMode())
- , m_argumentsRegister(functionExecutable->generatedBytecode().argumentsRegister())
-{
-}
-
-void JSActivation::finishCreation(CallFrame* callFrame, FunctionExecutable* functionExecutable)
-{
- Base::finishCreation(callFrame->globalData(), functionExecutable->symbolTable());
- ASSERT(inherits(&s_info));
-}
-
void JSActivation::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
JSActivation* thisObject = jsCast<JSActivation*>(cell);
@@ -72,18 +50,11 @@ void JSActivation::visitChildren(JSCell* cell, SlotVisitor& visitor)
Base::visitChildren(thisObject, visitor);
// No need to mark our registers if they're still in the RegisterFile.
- PropertyStorage registerArray = thisObject->m_registerArray.get();
- if (!registerArray)
+ if (!thisObject->isTornOff())
return;
- visitor.copyAndAppend(bitwise_cast<void**>(&registerArray), thisObject->registerArraySizeInBytes(), reinterpret_cast<JSValue*>(registerArray), thisObject->registerArraySize());
- thisObject->m_registerArray.set(registerArray, StorageBarrier::Unchecked);
- thisObject->m_registers = registerArray + thisObject->registerOffset();
-
- // Update the arguments object, since it points at our buffer.
- CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(thisObject->m_registers));
- if (JSValue v = callFrame->uncheckedR(unmodifiedArgumentsRegister(thisObject->m_argumentsRegister)).jsValue())
- jsCast<Arguments*>(v)->setRegisters(thisObject->m_registers);
+ for (size_t i = 0; i < thisObject->storageSize(); ++i)
+ visitor.append(&thisObject->storage()[i]);
}
inline bool JSActivation::symbolTableGet(PropertyName propertyName, PropertySlot& slot)
@@ -93,7 +64,7 @@ inline bool JSActivation::symbolTableGet(PropertyName propertyName, PropertySlot
return false;
// Defend against the inspector asking for a var after it has been optimized out.
- if (m_isTornOff && entry.getIndex() >= m_numCapturedVars)
+ if (isTornOff() && !isValid(entry))
return false;
slot.setValue(registerAt(entry.getIndex()).get());
@@ -107,7 +78,7 @@ inline bool JSActivation::symbolTableGet(PropertyName propertyName, PropertyDesc
return false;
// Defend against the inspector asking for a var after it has been optimized out.
- if (m_isTornOff && entry.getIndex() >= m_numCapturedVars)
+ if (isTornOff() && !isValid(entry))
return false;
descriptor.setDescriptor(registerAt(entry.getIndex()).get(), entry.getAttributes());
@@ -129,30 +100,30 @@ inline bool JSActivation::symbolTablePut(ExecState* exec, PropertyName propertyN
}
// Defend against the inspector asking for a var after it has been optimized out.
- if (m_isTornOff && entry.getIndex() >= m_numCapturedVars)
+ if (isTornOff() && !isValid(entry))
return false;
registerAt(entry.getIndex()).set(globalData, this, value);
return true;
}
-void JSActivation::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+void JSActivation::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
JSActivation* thisObject = jsCast<JSActivation*>(object);
- if (mode == IncludeDontEnumProperties)
+ if (mode == IncludeDontEnumProperties && !thisObject->isTornOff())
propertyNames.add(exec->propertyNames().arguments);
SymbolTable::const_iterator end = thisObject->symbolTable()->end();
for (SymbolTable::const_iterator it = thisObject->symbolTable()->begin(); it != end; ++it) {
if (it->second.getAttributes() & DontEnum && mode != IncludeDontEnumProperties)
continue;
- if (it->second.getIndex() >= thisObject->m_numCapturedVars)
+ if (!thisObject->isValid(it->second))
continue;
propertyNames.add(Identifier(exec, it->first.get()));
}
- // Skip the JSVariableObject implementation of getOwnPropertyNames
- JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
+ // Skip the JSVariableObject implementation of getOwnNonIndexPropertyNames
+ JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
}
inline bool JSActivation::symbolTablePutWithAttributes(JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)
@@ -164,7 +135,7 @@ inline bool JSActivation::symbolTablePutWithAttributes(JSGlobalData& globalData,
return false;
SymbolTableEntry& entry = iter->second;
ASSERT(!entry.isNull());
- if (entry.getIndex() >= m_numCapturedVars)
+ if (!isValid(entry))
return false;
entry.setAttributes(attributes);
@@ -178,7 +149,7 @@ bool JSActivation::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyNam
if (propertyName == exec->propertyNames().arguments) {
// Defend against the inspector asking for the arguments object after it has been optimized out.
- if (!thisObject->m_isTornOff) {
+ if (!thisObject->isTornOff()) {
slot.setCustom(thisObject, thisObject->getArgumentsGetter());
return true;
}
@@ -205,7 +176,7 @@ bool JSActivation::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, P
if (propertyName == exec->propertyNames().arguments) {
// Defend against the inspector asking for the arguments object after it has been optimized out.
- if (!thisObject->m_isTornOff) {
+ if (!thisObject->isTornOff()) {
PropertySlot slot;
JSActivation::getOwnPropertySlot(thisObject, exec, propertyName, slot);
descriptor.setDescriptor(slot.getValue(exec, propertyName), DontEnum);
@@ -265,9 +236,12 @@ JSObject* JSActivation::toThisObject(JSCell*, ExecState* exec)
JSValue JSActivation::argumentsGetter(ExecState*, JSValue slotBase, PropertyName)
{
- JSActivation* activation = asActivation(slotBase);
+ JSActivation* activation = jsCast<JSActivation*>(slotBase);
+ if (activation->isTornOff())
+ return jsUndefined();
+
CallFrame* callFrame = CallFrame::create(reinterpret_cast<Register*>(activation->m_registers));
- int argumentsRegister = activation->m_argumentsRegister;
+ int argumentsRegister = callFrame->codeBlock()->argumentsRegister();
if (JSValue arguments = callFrame->uncheckedR(argumentsRegister).jsValue())
return arguments;
int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister);
diff --git a/Source/JavaScriptCore/runtime/JSActivation.h b/Source/JavaScriptCore/runtime/JSActivation.h
index 3abe5f54b..df59c3d94 100644
--- a/Source/JavaScriptCore/runtime/JSActivation.h
+++ b/Source/JavaScriptCore/runtime/JSActivation.h
@@ -37,20 +37,26 @@
namespace JSC {
- class Arguments;
class Register;
class JSActivation : public JSVariableObject {
private:
- JSActivation(CallFrame*, FunctionExecutable*);
+ JSActivation(JSGlobalData& globalData, CallFrame*, SharedSymbolTable*, size_t storageSize);
public:
typedef JSVariableObject Base;
- static JSActivation* create(JSGlobalData& globalData, CallFrame* callFrame, FunctionExecutable* funcExec)
+ static JSActivation* create(JSGlobalData& globalData, CallFrame* callFrame, FunctionExecutable* functionExecutable)
{
- JSActivation* activation = new (NotNull, allocateCell<JSActivation>(globalData.heap)) JSActivation(callFrame, funcExec);
- activation->finishCreation(callFrame, funcExec);
+ size_t storageSize = JSActivation::storageSize(callFrame, functionExecutable->symbolTable());
+ JSActivation* activation = new (
+ NotNull,
+ allocateCell<JSActivation>(
+ globalData.heap,
+ allocationSize(storageSize)
+ )
+ ) JSActivation(globalData, callFrame, functionExecutable->symbolTable(), storageSize);
+ activation->finishCreation(globalData);
return activation;
}
@@ -59,7 +65,7 @@ namespace JSC {
bool isDynamicScope(bool& requiresDynamicChecks) const;
static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
- static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+ static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
@@ -75,10 +81,10 @@ namespace JSC {
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(globalData, globalObject, proto, TypeInfo(ActivationObjectType, StructureFlags), &s_info); }
- bool isValidScopedLookup(int index) { return index < m_numCapturedVars; }
+ bool isValid(const SymbolTableEntry&);
+ bool isTornOff();
protected:
- void finishCreation(CallFrame*, FunctionExecutable*);
static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | Base::StructureFlags;
private:
@@ -91,18 +97,32 @@ namespace JSC {
static JSValue argumentsGetter(ExecState*, JSValue, PropertyName);
NEVER_INLINE PropertySlot::GetValueFunc getArgumentsGetter();
- size_t registerOffset();
- size_t registerArraySize();
- size_t registerArraySizeInBytes();
+ static size_t allocationSize(size_t storageSize);
+ static size_t storageSize(CallFrame*, SharedSymbolTable*);
+ static int captureStart(CallFrame*, SharedSymbolTable*);
- StorageBarrier m_registerArray; // Independent copy of registers, used when a variable object copies its registers out of the register file.
- int m_numCapturedArgs;
- int m_numCapturedVars : 30;
- bool m_isTornOff : 1;
- bool m_requiresDynamicChecks : 1;
- int m_argumentsRegister;
+ int registerOffset();
+ size_t storageSize();
+ WriteBarrier<Unknown>* storage(); // storageSize() number of registers.
};
+ extern int activationCount;
+ extern int allTheThingsCount;
+
+ inline JSActivation::JSActivation(JSGlobalData& globalData, CallFrame* callFrame, SharedSymbolTable* symbolTable, size_t storageSize)
+ : Base(
+ globalData,
+ callFrame->lexicalGlobalObject()->activationStructure(),
+ callFrame->registers(),
+ callFrame->scope(),
+ symbolTable
+ )
+ {
+ WriteBarrier<Unknown>* storage = this->storage();
+ for (size_t i = 0; i < storageSize; ++i)
+ new(&storage[i]) WriteBarrier<Unknown>;
+ }
+
JSActivation* asActivation(JSValue);
inline JSActivation* asActivation(JSValue value)
@@ -118,55 +138,89 @@ namespace JSC {
inline bool JSActivation::isDynamicScope(bool& requiresDynamicChecks) const
{
- requiresDynamicChecks = m_requiresDynamicChecks;
+ requiresDynamicChecks = symbolTable()->usesNonStrictEval();
return false;
}
- inline size_t JSActivation::registerOffset()
+ inline int JSActivation::captureStart(CallFrame* callFrame, SharedSymbolTable* symbolTable)
{
- if (!m_numCapturedArgs)
- return 0;
+ if (symbolTable->captureMode() == SharedSymbolTable::AllOfTheThings)
+ return -CallFrame::offsetFor(std::max<size_t>(callFrame->argumentCountIncludingThis(), symbolTable->parameterCountIncludingThis()));
+ return symbolTable->captureStart();
+ }
- size_t capturedArgumentCountIncludingThis = m_numCapturedArgs + 1;
- return CallFrame::offsetFor(capturedArgumentCountIncludingThis);
+ inline size_t JSActivation::storageSize(CallFrame* callFrame, SharedSymbolTable* symbolTable)
+ {
+ return symbolTable->captureEnd() - captureStart(callFrame, symbolTable);
}
- inline size_t JSActivation::registerArraySize()
+ inline int JSActivation::registerOffset()
{
- return registerOffset() + m_numCapturedVars;
+ return -captureStart(CallFrame::create(reinterpret_cast<Register*>(m_registers)), symbolTable());
}
- inline size_t JSActivation::registerArraySizeInBytes()
+ inline size_t JSActivation::storageSize()
{
- return registerArraySize() * sizeof(WriteBarrierBase<Unknown>);
+ return storageSize(CallFrame::create(reinterpret_cast<Register*>(m_registers)), symbolTable());
}
inline void JSActivation::tearOff(JSGlobalData& globalData)
{
- ASSERT(!m_registerArray);
- ASSERT(m_numCapturedVars + m_numCapturedArgs);
-
- void* allocation = 0;
- if (!globalData.heap.tryAllocateStorage(registerArraySizeInBytes(), &allocation))
- CRASH();
- PropertyStorage registerArray = static_cast<PropertyStorage>(allocation);
- PropertyStorage registers = registerArray + registerOffset();
-
- // arguments
- int from = CallFrame::argumentOffset(m_numCapturedArgs - 1);
- int to = CallFrame::thisArgumentOffset(); // Skip 'this' because it's not lexically accessible.
- for (int i = from; i < to; ++i)
- registers[i].set(globalData, this, m_registers[i].get());
-
- // vars
- from = 0;
- to = m_numCapturedVars;
- for (int i = from; i < to; ++i)
- registers[i].set(globalData, this, m_registers[i].get());
-
- m_registerArray.set(globalData, this, registerArray);
- m_registers = registers;
- m_isTornOff = true;
+ ASSERT(!isTornOff());
+
+ int registerOffset = this->registerOffset();
+ WriteBarrierBase<Unknown>* dst = storage() + registerOffset;
+ WriteBarrierBase<Unknown>* src = m_registers;
+
+ if (symbolTable()->captureMode() == SharedSymbolTable::AllOfTheThings) {
+ int from = -registerOffset;
+ int to = CallFrame::thisArgumentOffset(); // Skip 'this' because it's not lexically accessible.
+ for (int i = from; i < to; ++i)
+ dst[i].set(globalData, this, src[i].get());
+
+ dst[RegisterFile::ArgumentCount].set(globalData, this, JSValue(
+ CallFrame::create(reinterpret_cast<Register*>(src))->argumentCountIncludingThis()));
+
+ int captureEnd = symbolTable()->captureEnd();
+ for (int i = 0; i < captureEnd; ++i)
+ dst[i].set(globalData, this, src[i].get());
+ } else {
+ int captureEnd = symbolTable()->captureEnd();
+ for (int i = symbolTable()->captureStart(); i < captureEnd; ++i)
+ dst[i].set(globalData, this, src[i].get());
+ }
+
+ m_registers = dst;
+ ASSERT(isTornOff());
+ }
+
+ inline bool JSActivation::isTornOff()
+ {
+ return m_registers == storage() + registerOffset();
+ }
+
+ inline WriteBarrier<Unknown>* JSActivation::storage()
+ {
+ return reinterpret_cast<WriteBarrier<Unknown>*>(
+ reinterpret_cast<char*>(this) +
+ WTF::roundUpToMultipleOf<sizeof(WriteBarrier<Unknown>)>(sizeof(JSActivation))
+ );
+ }
+
+ inline size_t JSActivation::allocationSize(size_t storageSize)
+ {
+ size_t objectSizeInBytes = WTF::roundUpToMultipleOf<sizeof(WriteBarrier<Unknown>)>(sizeof(JSActivation));
+ size_t storageSizeInBytes = storageSize * sizeof(WriteBarrier<Unknown>);
+ return objectSizeInBytes + storageSizeInBytes;
+ }
+
+ inline bool JSActivation::isValid(const SymbolTableEntry& entry)
+ {
+ if (entry.getIndex() < captureStart(CallFrame::create(reinterpret_cast<Register*>(m_registers)), symbolTable()))
+ return false;
+ if (entry.getIndex() >= symbolTable()->captureEnd())
+ return false;
+ return true;
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp
index 8e1606fd8..241049dce 100644
--- a/Source/JavaScriptCore/runtime/JSArray.cpp
+++ b/Source/JavaScriptCore/runtime/JSArray.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
* Copyright (C) 2003 Peter Kelly (pmk@post.com)
* Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
*
@@ -24,13 +24,17 @@
#include "JSArray.h"
#include "ArrayPrototype.h"
+#include "ButterflyInlineMethods.h"
#include "CopiedSpace.h"
#include "CopiedSpaceInlineMethods.h"
#include "CachedCall.h"
#include "Error.h"
#include "Executable.h"
#include "GetterSetter.h"
+#include "IndexingHeaderInlineMethods.h"
#include "PropertyNameArray.h"
+#include "Reject.h"
+#include "SparseArrayValueMapInlineMethods.h"
#include <wtf/AVLTree.h>
#include <wtf/Assertions.h>
#include <wtf/OwnPtr.h>
@@ -45,480 +49,23 @@ namespace JSC {
ASSERT_CLASS_FITS_IN_CELL(JSArray);
ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray);
-// 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 identifer, 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.
-
-// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
-// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
-// size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +
-// (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
-#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))
-
-// 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>.
-#define MIN_SPARSE_ARRAY_INDEX 10000U
-#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
-
-// 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;
-
const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)};
-// 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 above.
-static unsigned lastArraySize = 0;
-
-static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
-{
- return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues;
-}
-
-static bool reject(ExecState* exec, bool throwException, const char* message)
-{
- if (throwException)
- throwTypeError(exec, ASCIILiteral(message));
- return false;
-}
-
-#if !CHECK_ARRAY_CONSISTENCY
-
-inline void JSArray::checkConsistency(ConsistencyCheckType)
-{
-}
-
-#endif
-
-void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength)
-{
- Base::finishCreation(globalData);
- ASSERT(inherits(&s_info));
-
- unsigned initialVectorLength = BASE_VECTOR_LEN;
- unsigned initialStorageSize = storageSize(initialVectorLength);
-
- void* newStorage = 0;
- if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage))
- CRASH();
-
- m_storage = static_cast<ArrayStorage*>(newStorage);
- m_storage->m_allocBase = m_storage;
- m_storage->m_length = initialLength;
- m_vectorLength = initialVectorLength;
- m_storage->m_numValuesInVector = 0;
-#if CHECK_ARRAY_CONSISTENCY
- m_storage->m_inCompactInitialization = false;
-#endif
-
- checkConsistency();
-}
-
-JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsigned initialLength)
+Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData& globalData, unsigned initialLength)
{
- Base::finishCreation(globalData);
- ASSERT(inherits(&s_info));
-
- // Check for lengths larger than we can handle with a vector.
- if (initialLength > MAX_STORAGE_VECTOR_LENGTH)
- return 0;
-
- unsigned initialVectorLength = max(initialLength, BASE_VECTOR_LEN);
- unsigned initialStorageSize = storageSize(initialVectorLength);
-
- void* newStorage = 0;
- if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage))
- CRASH();
-
- m_storage = static_cast<ArrayStorage*>(newStorage);
- m_storage->m_allocBase = m_storage;
- m_storage->m_length = initialLength;
- m_vectorLength = initialVectorLength;
- m_storage->m_numValuesInVector = initialLength;
-
+ Butterfly* butterfly = Butterfly::create(
+ globalData, 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;
#if CHECK_ARRAY_CONSISTENCY
- m_storage->m_initializationIndex = 0;
- m_storage->m_inCompactInitialization = true;
+ storage->m_initializationIndex = 0;
+ storage->m_inCompactInitialization = 0;
#endif
-
- return this;
-}
-
-// This function can be called multiple times on the same object.
-void JSArray::finalize(JSCell* cell)
-{
- JSArray* thisObject = jsCast<JSArray*>(cell);
- thisObject->checkConsistency(DestructorConsistencyCheck);
- thisObject->deallocateSparseMap();
-}
-
-inline SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSArray* 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) {
- Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>)));
- m_reportedCapacity = capacity;
- }
- return result;
-}
-
-inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow)
-{
- AddResult result = add(array, i);
- SparseArrayEntry& entry = result.iterator->second;
-
- // 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, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
- return;
- }
-
- if (!(entry.attributes & Accessor)) {
- if (entry.attributes & ReadOnly) {
- if (shouldThrow)
- throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
- return;
- }
-
- entry.set(exec->globalData(), array, value);
- return;
- }
-
- JSValue accessor = entry.Base::get();
- ASSERT(accessor.isGetterSetter());
- JSObject* setter = asGetterSetter(accessor)->setter();
-
- if (!setter) {
- if (shouldThrow)
- throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
- return;
- }
-
- CallData callData;
- CallType callType = setter->methodTable()->getCallData(setter, callData);
- MarkedArgumentBuffer args;
- args.append(value);
- call(exec, setter, callType, callData, array, args);
-}
-
-inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, PutDirectIndexMode mode)
-{
- AddResult result = add(array, i);
- SparseArrayEntry& entry = result.iterator->second;
-
- // 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 = 0;
- entry.set(exec->globalData(), array, value);
- return true;
-}
-
-inline void SparseArrayEntry::get(PropertySlot& slot) const
-{
- JSValue value = Base::get();
- ASSERT(value);
-
- if (LIKELY(!value.isGetterSetter())) {
- slot.setValue(value);
- return;
- }
-
- JSObject* getter = asGetterSetter(value)->getter();
- if (!getter) {
- slot.setUndefined();
- return;
- }
-
- slot.setGetterSlot(getter);
-}
-
-inline void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
-{
- descriptor.setDescriptor(Base::get(), attributes);
-}
-
-inline JSValue SparseArrayEntry::get(ExecState* exec, JSArray* array) const
-{
- JSValue result = Base::get();
- ASSERT(result);
-
- if (LIKELY(!result.isGetterSetter()))
- return result;
-
- JSObject* getter = asGetterSetter(result)->getter();
- if (!getter)
- return jsUndefined();
-
- CallData callData;
- CallType callType = getter->methodTable()->getCallData(getter, callData);
- return call(exec, getter, callType, callData, array, exec->emptyList());
-}
-
-inline JSValue SparseArrayEntry::getNonSparseMode() const
-{
- ASSERT(!attributes);
- return Base::get();
-}
-
-inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor)
-{
- iterator end = m_map.end();
- for (iterator it = m_map.begin(); it != end; ++it)
- visitor.append(&it->second);
-}
-
-void JSArray::allocateSparseMap(JSGlobalData& globalData)
-{
- m_sparseValueMap = new SparseArrayValueMap;
- globalData.heap.addFinalizer(this, finalize);
-}
-
-void JSArray::deallocateSparseMap()
-{
- delete m_sparseValueMap;
- m_sparseValueMap = 0;
-}
-
-void JSArray::enterDictionaryMode(JSGlobalData& globalData)
-{
- ArrayStorage* storage = m_storage;
- SparseArrayValueMap* map = m_sparseValueMap;
-
- if (!map) {
- allocateSparseMap(globalData);
- map = m_sparseValueMap;
- }
-
- if (map->sparseMode())
- return;
-
- map->setSparseMode();
-
- unsigned usedVectorLength = min(storage->m_length, m_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->second.set(globalData, this, value);
- }
-
- void* newRawStorage = 0;
- if (!globalData.heap.tryAllocateStorage(storageSize(0), &newRawStorage))
- CRASH();
-
- ArrayStorage* newStorage = static_cast<ArrayStorage*>(newRawStorage);
- memcpy(newStorage, m_storage, storageSize(0));
- newStorage->m_allocBase = newStorage;
- m_storage = newStorage;
- m_indexBias = 0;
- m_vectorLength = 0;
-}
-
-void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
-{
- if (descriptor.isDataDescriptor()) {
- if (descriptor.value())
- entryInMap->set(exec->globalData(), this, descriptor.value());
- else if (oldDescriptor.isAccessorDescriptor())
- entryInMap->set(exec->globalData(), this, 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(exec);
- if (getter)
- accessor->setGetter(exec->globalData(), getter);
- if (setter)
- accessor->setSetter(exec->globalData(), setter);
-
- entryInMap->set(exec->globalData(), this, accessor);
- entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
- return;
- }
-
- ASSERT(descriptor.isGenericDescriptor());
- entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
-}
-
-// Defined in ES5.1 8.12.9
-bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException)
-{
- ASSERT(index != 0xFFFFFFFF);
-
- if (!inSparseMode()) {
- // 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(), throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
- }
-
- enterDictionaryMode(exec->globalData());
- }
-
- SparseArrayValueMap* map = m_sparseValueMap;
- 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->second;
-
- // 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);
-
- putDescriptor(exec, entryInMap, descriptor, defaults);
- if (index >= m_storage->m_length)
- m_storage->m_length = 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.
- putDescriptor(exec, entryInMap, descriptor, current);
- // 13. Return true.
- return true;
+ return butterfly;
}
void JSArray::setLengthWritable(ExecState* exec, bool writable)
@@ -527,9 +74,9 @@ void JSArray::setLengthWritable(ExecState* exec, bool writable)
if (!isLengthWritable() || writable)
return;
- enterDictionaryMode(exec->globalData());
+ enterDictionaryIndexingMode(exec->globalData());
- SparseArrayValueMap* map = m_sparseValueMap;
+ SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
ASSERT(map);
map->setLengthIsReadOnly();
}
@@ -630,38 +177,10 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
// 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->defineOwnNumericProperty(exec, index, descriptor, throwException);
- }
-
- return JSObject::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
-}
-
-bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot)
-{
- JSArray* thisObject = jsCast<JSArray*>(cell);
- ArrayStorage* storage = thisObject->m_storage;
-
- if (i >= storage->m_length) {
- if (i > MAX_ARRAY_INDEX)
- return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
- return false;
+ return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
}
- if (i < thisObject->m_vectorLength) {
- JSValue value = storage->m_vector[i].get();
- if (value) {
- slot.setValue(value);
- return true;
- }
- } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
- SparseArrayValueMap::iterator it = map->find(i);
- if (it != map->notFound()) {
- it->second.get(slot);
- return true;
- }
- }
-
- return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
+ return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
}
bool JSArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
@@ -672,10 +191,6 @@ bool JSArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName pro
return true;
}
- unsigned i = propertyName.asIndex();
- if (i != PropertyName::NotAnIndex)
- return JSArray::getOwnPropertySlotByIndex(thisObject, exec, i, slot);
-
return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
}
@@ -687,26 +202,6 @@ bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Proper
return true;
}
- ArrayStorage* storage = thisObject->m_storage;
-
- unsigned i = propertyName.asIndex();
- if (i != PropertyName::NotAnIndex) {
- if (i >= storage->m_length)
- return false;
- if (i < thisObject->m_vectorLength) {
- WriteBarrier<Unknown>& value = storage->m_vector[i];
- if (value) {
- descriptor.setDescriptor(value.get(), 0);
- return true;
- }
- } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
- SparseArrayValueMap::iterator it = map->find(i);
- if (it != map->notFound()) {
- it->second.get(descriptor);
- return true;
- }
- }
- }
return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
}
@@ -714,11 +209,6 @@ bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Proper
void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
{
JSArray* thisObject = jsCast<JSArray*>(cell);
- unsigned i = propertyName.asIndex();
- if (i != PropertyName::NotAnIndex) {
- putByIndex(thisObject, exec, i, value, slot.isStrictMode());
- return;
- }
if (propertyName == exec->propertyNames().length) {
unsigned newLength = value.toUInt32(exec);
@@ -733,196 +223,9 @@ void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSVa
JSObject::put(thisObject, exec, propertyName, value, slot);
}
-void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
-{
- JSArray* thisObject = jsCast<JSArray*>(cell);
- thisObject->checkConsistency();
-
- ArrayStorage* storage = thisObject->m_storage;
-
- // Fast case - store to the vector.
- if (i < thisObject->m_vectorLength) {
- WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
- unsigned length = storage->m_length;
-
- // Update m_length & m_numValuesInVector as necessary.
- if (i >= length) {
- length = i + 1;
- storage->m_length = length;
- ++storage->m_numValuesInVector;
- } else if (!valueSlot)
- ++storage->m_numValuesInVector;
-
- valueSlot.set(exec->globalData(), thisObject, value);
- thisObject->checkConsistency();
- return;
- }
-
- // Handle 2^32-1 - this is not an array index (see ES5.1 15.4), and is treated as a regular property.
- if (UNLIKELY(i > MAX_ARRAY_INDEX)) {
- PutPropertySlot slot(shouldThrow);
- thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, i), value, slot);
- return;
- }
-
- // For all other cases, call putByIndexBeyondVectorLength.
- thisObject->putByIndexBeyondVectorLength(exec, i, value, shouldThrow);
- thisObject->checkConsistency();
-}
-
-void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
-{
- JSGlobalData& globalData = exec->globalData();
-
- // i should be a valid array index that is outside of the current vector.
- ASSERT(i >= m_vectorLength);
- ASSERT(i <= MAX_ARRAY_INDEX);
-
- ArrayStorage* storage = m_storage;
- SparseArrayValueMap* map = m_sparseValueMap;
-
- // 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->m_length)
- storage->m_length = i + 1;
-
- // Check that it is sensible to still be using a vector, and then try to grow the vector.
- if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {
- // success! - reread m_storage since it has likely been reallocated, and store to the vector.
- storage = m_storage;
- storage->m_vector[i].set(globalData, 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.
- allocateSparseMap(exec->globalData());
- map = m_sparseValueMap;
- map->put(exec, this, i, value, shouldThrow);
- return;
- }
-
- // Update m_length if necessary.
- unsigned length = storage->m_length;
- if (i >= length) {
- // Prohibit growing the array if length is not writable.
- if (map->lengthIsReadOnly() || !isExtensible()) {
- if (shouldThrow)
- throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
- return;
- }
- length = i + 1;
- storage->m_length = 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->globalData(), length)) {
- map->put(exec, this, i, value, shouldThrow);
- return;
- }
-
- // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.
- storage = m_storage;
- 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->first].set(globalData, this, it->second.getNonSparseMode());
- deallocateSparseMap();
-
- // Store the new property into the vector.
- WriteBarrier<Unknown>& valueSlot = vector[i];
- if (!valueSlot)
- ++storage->m_numValuesInVector;
- valueSlot.set(globalData, this, value);
-}
-
-bool JSArray::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, PutDirectIndexMode mode)
-{
- JSGlobalData& globalData = exec->globalData();
-
- // i should be a valid array index that is outside of the current vector.
- ASSERT(i >= m_vectorLength);
- ASSERT(i <= MAX_ARRAY_INDEX);
-
- ArrayStorage* storage = m_storage;
- SparseArrayValueMap* map = m_sparseValueMap;
-
- // 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->m_length)
- storage->m_length = i + 1;
-
- // Check that it is sensible to still be using a vector, and then try to grow the vector.
- if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {
- // success! - reread m_storage since it has likely been reallocated, and store to the vector.
- storage = m_storage;
- storage->m_vector[i].set(globalData, 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.
- allocateSparseMap(exec->globalData());
- map = m_sparseValueMap;
- return map->putDirect(exec, this, i, value, mode);
- }
-
- // Update m_length if necessary.
- unsigned length = storage->m_length;
- if (i >= length) {
- // Prohibit growing the array if length is not writable.
- if (mode != PutDirectIndexLikePutDirect) {
- 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->m_length = 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->globalData(), length))
- return map->putDirect(exec, this, i, value, mode);
-
- // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.
- storage = m_storage;
- 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->first].set(globalData, this, it->second.getNonSparseMode());
- deallocateSparseMap();
-
- // Store the new property into the vector.
- WriteBarrier<Unknown>& valueSlot = vector[i];
- if (!valueSlot)
- ++storage->m_numValuesInVector;
- valueSlot.set(globalData, this, value);
- return true;
-}
-
bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
{
JSArray* thisObject = jsCast<JSArray*>(cell);
- unsigned i = propertyName.asIndex();
- if (i != PropertyName::NotAnIndex)
- return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i);
if (propertyName == exec->propertyNames().length)
return false;
@@ -930,35 +233,6 @@ bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propert
return JSObject::deleteProperty(thisObject, exec, propertyName);
}
-bool JSArray::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
-{
- JSArray* thisObject = jsCast<JSArray*>(cell);
- thisObject->checkConsistency();
-
- if (i > MAX_ARRAY_INDEX)
- return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i));
-
- ArrayStorage* storage = thisObject->m_storage;
-
- if (i < thisObject->m_vectorLength) {
- WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
- if (valueSlot) {
- valueSlot.clear();
- --storage->m_numValuesInVector;
- }
- } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
- SparseArrayValueMap::iterator it = map->find(i);
- if (it != map->notFound()) {
- if (it->second.attributes & DontDelete)
- return false;
- map->remove(it);
- }
- }
-
- thisObject->checkConsistency();
- return true;
-}
-
static int compareKeysForQSort(const void* a, const void* b)
{
unsigned da = *static_cast<const unsigned*>(a);
@@ -966,127 +240,26 @@ static int compareKeysForQSort(const void* a, const void* b)
return (da > db) - (da < db);
}
-void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
JSArray* thisObject = jsCast<JSArray*>(object);
- // 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.
-
- ArrayStorage* storage = thisObject->m_storage;
-
- unsigned usedVectorLength = min(storage->m_length, thisObject->m_vectorLength);
- for (unsigned i = 0; i < usedVectorLength; ++i) {
- if (storage->m_vector[i])
- propertyNames.add(Identifier::from(exec, i));
- }
-
- if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
- Vector<unsigned> keys;
- keys.reserveCapacity(map->size());
-
- SparseArrayValueMap::const_iterator end = map->end();
- for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
- if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum))
- keys.append(static_cast<unsigned>(it->first));
- }
-
- qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
- for (unsigned i = 0; i < keys.size(); ++i)
- propertyNames.add(Identifier::from(exec, keys[i]));
- }
if (mode == IncludeDontEnumProperties)
propertyNames.add(exec->propertyNames().length);
- JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
-}
-
-ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength)
-{
- ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
-
- unsigned increasedLength;
- unsigned maxInitLength = min(m_storage->m_length, 100000U);
-
- if (desiredLength < maxInitLength)
- increasedLength = maxInitLength;
- else if (!m_vectorLength)
- increasedLength = max(desiredLength, lastArraySize);
- else {
- // Mathematically equivalent to:
- // increasedLength = (newLength * 3 + 1) / 2;
- // or:
- // increasedLength = (unsigned)ceil(newLength * 1.5));
- // This form is not prone to internal overflow.
- increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1);
- }
-
- ASSERT(increasedLength >= desiredLength);
-
- lastArraySize = min(increasedLength, FIRST_VECTOR_GROW);
-
- return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
-}
-
-bool JSArray::increaseVectorLength(JSGlobalData& globalData, 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 = m_storage;
-
- unsigned vectorLength = m_vectorLength;
- ASSERT(newLength > vectorLength);
- unsigned newVectorLength = getNewVectorLength(newLength);
-
- // Fast case - there is no precapacity. In these cases a realloc makes sense.
- if (LIKELY(!m_indexBias)) {
- void* newStorage = storage->m_allocBase;
- if (!globalData.heap.tryReallocateStorage(&newStorage, storageSize(vectorLength), storageSize(newVectorLength)))
- return false;
-
- storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newStorage));
- m_storage->m_allocBase = newStorage;
- ASSERT(m_storage->m_allocBase);
-
- m_vectorLength = newVectorLength;
-
- return true;
- }
-
- // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
- unsigned newIndexBias = min(m_indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
- // Calculate new stoarge capcity, allowing room for the pre-capacity.
- unsigned newStorageCapacity = newVectorLength + newIndexBias;
- void* newAllocBase = 0;
- if (!globalData.heap.tryAllocateStorage(storageSize(newStorageCapacity), &newAllocBase))
- return false;
- // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
- ASSERT(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias);
-
- m_vectorLength = newVectorLength;
- m_indexBias = newIndexBias;
- m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias);
-
- // Copy the ArrayStorage header & current contents of the vector.
- memmove(m_storage, storage, storageSize(vectorLength));
-
- // Free the old allocation, update m_allocBase.
- m_storage->m_allocBase = newAllocBase;
-
- return true;
+ JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
}
// This method makes room in the vector, but leaves the new space uncleared.
bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count)
{
- // If not, we should have handled this on the fast path.
- ASSERT(count > m_indexBias);
+ ArrayStorage* storage = ensureArrayStorage(globalData);
+ Butterfly* butterfly = storage->butterfly();
+ unsigned propertyCapacity = structure()->outOfLineCapacity();
+ unsigned propertySize = structure()->outOfLineSize();
- ArrayStorage* storage = m_storage;
+ // If not, we should have handled this on the fast path.
+ ASSERT(count > storage->m_indexBias);
// Step 1:
// Gather 4 key metrics:
@@ -1095,8 +268,8 @@ bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count)
// * 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->m_length;
- unsigned usedVectorLength = min(m_vectorLength, length);
+ 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)
@@ -1104,8 +277,8 @@ bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count)
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(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias);
- unsigned currentCapacity = m_vectorLength + m_indexBias;
+ 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);
@@ -1116,10 +289,11 @@ bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count)
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 = storage->m_allocBase;
+ newAllocBase = butterfly->base(structure());
newStorageCapacity = currentCapacity;
} else {
- if (!globalData.heap.tryAllocateStorage(storageSize(desiredCapacity), &newAllocBase))
+ size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
+ if (!globalData.heap.tryAllocateStorage(newSize, &newAllocBase))
return false;
newStorageCapacity = desiredCapacity;
}
@@ -1132,45 +306,40 @@ bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count)
// 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 (length < m_vectorLength) {
+ if (length < storage->vectorLength()) {
// Atomic decay, + the post-capacity cannot be greater than what is available.
- postCapacity = min((m_vectorLength - length) >> 1, newStorageCapacity - requiredVectorLength);
+ 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 != storage->m_allocBase || postCapacity < m_vectorLength - length);
+ ASSERT(newAllocBase != butterfly->base(structure()) || postCapacity < storage->vectorLength() - length);
}
+
+ unsigned newVectorLength = requiredVectorLength + postCapacity;
+ unsigned newIndexBias = newStorageCapacity - newVectorLength;
- m_vectorLength = requiredVectorLength + postCapacity;
- m_indexBias = newStorageCapacity - m_vectorLength;
- m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias);
-
- // Step 4:
- // Copy array data / header into their new locations, clear post-capacity & free any old allocation.
-
- // If this is being moved within the existing buffer of memory, we are always shifting data
- // to the right (since count > m_indexBias). As such this memmove cannot trample the header.
- memmove(m_storage->m_vector + count, storage->m_vector, sizeof(WriteBarrier<Unknown>) * usedVectorLength);
- memmove(m_storage, storage, storageSize(0));
-
- // Are we copying into a new allocation?
- if (newAllocBase != m_storage->m_allocBase) {
- // Free the old allocation, update m_allocBase.
- m_storage->m_allocBase = newAllocBase;
- }
+ Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity);
+
+ 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));
+
+ newButterfly->arrayStorage()->setVectorLength(newVectorLength);
+ newButterfly->arrayStorage()->m_indexBias = newIndexBias;
+
+ m_butterfly = newButterfly;
return true;
}
bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
{
- checkConsistency();
+ checkIndexingConsistency();
- ArrayStorage* storage = m_storage;
- unsigned length = storage->m_length;
+ ArrayStorage* storage = ensureArrayStorage(exec->globalData());
+ unsigned length = storage->length();
// If the length is read only then we enter sparse mode, so should enter the following 'if'.
- ASSERT(isLengthWritable() || m_sparseValueMap);
+ ASSERT(isLengthWritable() || storage->m_sparseMap);
- if (SparseArrayValueMap* map = m_sparseValueMap) {
+ if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
// Fail if the length is not writable.
if (map->lengthIsReadOnly())
return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);
@@ -1197,7 +366,7 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
SparseArrayValueMap::iterator it = map->find(index);
ASSERT(it != map->notFound());
if (it->second.attributes & DontDelete) {
- storage->m_length = index + 1;
+ storage->setLength(index + 1);
return reject(exec, throwException, "Unable to delete property.");
}
map->remove(it);
@@ -1206,14 +375,14 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
for (unsigned i = 0; i < keys.size(); ++i)
map->remove(keys[i]);
if (map->isEmpty())
- deallocateSparseMap();
+ deallocateSparseIndexMap();
}
}
}
if (newLength < length) {
// Delete properties from the vector.
- unsigned usedVectorLength = min(length, m_vectorLength);
+ unsigned usedVectorLength = min(length, storage->vectorLength());
for (unsigned i = newLength; i < usedVectorLength; ++i) {
WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
bool hadValue = valueSlot;
@@ -1222,53 +391,65 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
}
}
- storage->m_length = newLength;
+ storage->setLength(newLength);
- checkConsistency();
+ checkIndexingConsistency();
return true;
}
JSValue JSArray::pop(ExecState* exec)
{
- checkConsistency();
- ArrayStorage* storage = m_storage;
+ checkIndexingConsistency();
- unsigned length = storage->m_length;
- if (!length) {
- if (!isLengthWritable())
- throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
+ switch (structure()->indexingType()) {
+ case ArrayClass:
return jsUndefined();
- }
+
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+
+ unsigned length = storage->length();
+ if (!length) {
+ if (!isLengthWritable())
+ throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+ return jsUndefined();
+ }
- unsigned index = length - 1;
- if (index < m_vectorLength) {
- WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
- if (valueSlot) {
- --storage->m_numValuesInVector;
- JSValue element = valueSlot.get();
- valueSlot.clear();
+ 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();
- ASSERT(isLengthWritable());
- storage->m_length = index;
- checkConsistency();
- return element;
+ ASSERT(isLengthWritable());
+ storage->setLength(index);
+ checkIndexingConsistency();
+ return element;
+ }
}
- }
- // 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();
+ // 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, "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.
+ checkIndexingConsistency();
+ return element;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ return JSValue();
}
- // Call the [[Put]] internal method of O with arguments "length", indx, and true.
- setLength(exec, index, true);
- // Return element.
- checkConsistency();
- return element;
}
// Push & putIndex are almost identical, with two small differences.
@@ -1276,63 +457,77 @@ JSValue JSArray::pop(ExecState* exec)
// - pushing to an array of length 2^32-1 stores the property, but throws a range error.
void JSArray::push(ExecState* exec, JSValue value)
{
- checkConsistency();
- ArrayStorage* storage = m_storage;
-
- // Fast case - push within vector, always update m_length & m_numValuesInVector.
- unsigned length = storage->m_length;
- if (length < m_vectorLength) {
- storage->m_vector[length].set(exec->globalData(), this, value);
- storage->m_length = length + 1;
- ++storage->m_numValuesInVector;
- checkConsistency();
- return;
+ checkIndexingConsistency();
+
+ switch (structure()->indexingType()) {
+ case ArrayClass: {
+ putByIndexBeyondVectorLengthWithArrayStorage(exec, 0, value, true, createInitialArrayStorage(exec->globalData()));
+ break;
}
+
+ 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->globalData(), this, value);
+ storage->setLength(length + 1);
+ ++storage->m_numValuesInVector;
+ checkIndexingConsistency();
+ return;
+ }
- // Pushing to an array of length 2^32-1 stores the property, but throws a range error.
- if (UNLIKELY(storage->m_length == 0xFFFFFFFFu)) {
- methodTable()->putByIndex(this, exec, storage->m_length, value, true);
- // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
- if (!exec->hadException())
- throwError(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
- return;
- }
+ // Pushing to an array of length 2^32-1 stores the property, but throws a range error.
+ if (UNLIKELY(storage->length() == 0xFFFFFFFFu)) {
+ methodTable()->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())
+ throwError(exec, createRangeError(exec, "Invalid array length"));
+ return;
+ }
- // Handled the same as putIndex.
- putByIndexBeyondVectorLength(exec, storage->m_length, value, true);
- checkConsistency();
+ // Handled the same as putIndex.
+ putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
+ checkIndexingConsistency();
+ break;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
}
-bool JSArray::shiftCount(ExecState*, unsigned count)
+bool JSArray::shiftCount(ExecState* exec, unsigned count)
{
ASSERT(count > 0);
- ArrayStorage* storage = m_storage;
+ ArrayStorage* storage = ensureArrayStorage(exec->globalData());
- unsigned oldLength = storage->m_length;
+ unsigned oldLength = storage->length();
// If the array contains holes or is otherwise in an abnormal state,
// use the generic algorithm in ArrayPrototype.
- if (oldLength != storage->m_numValuesInVector || inSparseMode())
+ if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode())
return false;
if (!oldLength)
return true;
storage->m_numValuesInVector -= count;
- storage->m_length -= count;
+ storage->setLength(oldLength - count);
- if (m_vectorLength) {
- count = min(m_vectorLength, (unsigned)count);
+ unsigned vectorLength = storage->vectorLength();
+ if (vectorLength) {
+ count = min(vectorLength, (unsigned)count);
- m_vectorLength -= count;
+ vectorLength -= count;
+ storage->setVectorLength(vectorLength);
- if (m_vectorLength) {
- char* newBaseStorage = reinterpret_cast<char*>(storage) + count * sizeof(WriteBarrier<Unknown>);
- memmove(newBaseStorage, storage, storageSize(0));
- m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage);
-
- m_indexBias += count;
+ if (vectorLength) {
+ m_butterfly = m_butterfly->shift(structure(), count);
+ storage = m_butterfly->arrayStorage();
+ storage->m_indexBias += count;
}
}
return true;
@@ -1341,59 +536,30 @@ bool JSArray::shiftCount(ExecState*, unsigned count)
// Returns true if the unshift can be handled, false to fallback.
bool JSArray::unshiftCount(ExecState* exec, unsigned count)
{
- ArrayStorage* storage = m_storage;
- unsigned length = storage->m_length;
+ ArrayStorage* storage = ensureArrayStorage(exec->globalData());
+ unsigned length = storage->length();
// If the array contains holes or is otherwise in an abnormal state,
// use the generic algorithm in ArrayPrototype.
- if (length != storage->m_numValuesInVector || inSparseMode())
+ if (length != storage->m_numValuesInVector || storage->inSparseMode())
return false;
- if (m_indexBias >= count) {
- m_indexBias -= count;
- char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(WriteBarrier<Unknown>);
- memmove(newBaseStorage, storage, storageSize(0));
- m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage);
- m_vectorLength += count;
+ if (storage->m_indexBias >= count) {
+ m_butterfly = storage->butterfly()->unshift(structure(), count);
+ storage = m_butterfly->arrayStorage();
+ storage->m_indexBias -= count;
+ storage->setVectorLength(storage->vectorLength() + count);
} else if (!unshiftCountSlowCase(exec->globalData(), count)) {
throwOutOfMemoryError(exec);
return true;
}
- WriteBarrier<Unknown>* vector = m_storage->m_vector;
+ WriteBarrier<Unknown>* vector = storage->m_vector;
for (unsigned i = 0; i < count; i++)
vector[i].clear();
return true;
}
-void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor)
-{
- JSArray* thisObject = jsCast<JSArray*>(cell);
- ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
- COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
- ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
-
- JSNonFinalObject::visitChildren(thisObject, visitor);
-
- if (thisObject->m_storage) {
- MARK_LOG_MESSAGE1("[%u]: ", thisObject->length());
-
- ArrayStorage* storage = thisObject->m_storage;
- void* baseStorage = storage->m_allocBase;
-
- visitor.copyAndAppend(reinterpret_cast<void**>(&baseStorage), storageSize(thisObject->m_vectorLength + thisObject->m_indexBias), storage->m_vector->slot(), thisObject->m_vectorLength);
-
- if (baseStorage != thisObject->m_storage->m_allocBase) {
- thisObject->m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + sizeof(JSValue) * thisObject->m_indexBias);
- thisObject->m_storage->m_allocBase = baseStorage;
- ASSERT(thisObject->m_storage->m_allocBase);
- }
- }
-
- if (SparseArrayValueMap* map = thisObject->m_sparseValueMap)
- map->visitChildren(visitor);
-}
-
static int compareNumbersForQSort(const void* a, const void* b)
{
double da = static_cast<const JSValue*>(a)->asNumber();
@@ -1410,112 +576,137 @@ static int compareByStringPairForQSort(const void* a, const void* b)
void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
{
- ASSERT(!inSparseMode());
-
- ArrayStorage* storage = m_storage;
-
- unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
- if (m_sparseValueMap) {
- throwOutOfMemoryError(exec);
- return;
- }
+ ASSERT(!inSparseIndexingMode());
- if (!lengthNotIncludingUndefined)
+ switch (structure()->indexingType()) {
+ case ArrayClass:
return;
- bool allValuesAreNumbers = true;
- size_t size = storage->m_numValuesInVector;
- for (size_t i = 0; i < size; ++i) {
- if (!storage->m_vector[i].isNumber()) {
- allValuesAreNumbers = false;
- break;
+ case ArrayWithArrayStorage: {
+ unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+
+ if (storage->m_sparseMap.get()) {
+ throwOutOfMemoryError(exec);
+ return;
}
+
+ if (!lengthNotIncludingUndefined)
+ return;
+
+ bool allValuesAreNumbers = true;
+ size_t size = storage->m_numValuesInVector;
+ for (size_t i = 0; i < size; ++i) {
+ if (!storage->m_vector[i].isNumber()) {
+ allValuesAreNumbers = false;
+ break;
+ }
+ }
+
+ if (!allValuesAreNumbers)
+ return sort(exec, compareFunction, callType, callData);
+
+ // For numeric comparison, which is fast, qsort is faster than mergesort. We
+ // also don't require mergesort's stability, since there's no user visible
+ // side-effect from swapping the order of equal primitive values.
+ qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort);
+
+ checkIndexingConsistency(SortConsistencyCheck);
+ return;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
}
-
- if (!allValuesAreNumbers)
- return sort(exec, compareFunction, callType, callData);
-
- // For numeric comparison, which is fast, qsort is faster than mergesort. We
- // also don't require mergesort's stability, since there's no user visible
- // side-effect from swapping the order of equal primitive values.
- qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort);
-
- checkConsistency(SortConsistencyCheck);
}
void JSArray::sort(ExecState* exec)
{
- ASSERT(!inSparseMode());
-
- unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
- if (m_sparseValueMap) {
- throwOutOfMemoryError(exec);
- return;
- }
-
- if (!lengthNotIncludingUndefined)
- return;
-
- // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
- // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
- // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
- // random or otherwise changing results, effectively making compare function inconsistent.
-
- Vector<ValueStringPair> values(lengthNotIncludingUndefined);
- if (!values.begin()) {
- throwOutOfMemoryError(exec);
- return;
- }
+ ASSERT(!inSparseIndexingMode());
- Heap::heap(this)->pushTempSortVector(&values);
-
- bool isSortingPrimitiveValues = true;
- for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
- JSValue value = m_storage->m_vector[i].get();
- ASSERT(!value.isUndefined());
- values[i].first = value;
- isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
- }
-
- // FIXME: The following loop continues to call toString on subsequent values even after
- // a toString call raises an exception.
-
- for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
- values[i].second = values[i].first.toWTFStringInline(exec);
-
- if (exec->hadException()) {
- Heap::heap(this)->popTempSortVector(&values);
+ switch (structure()->indexingType()) {
+ case ArrayClass:
return;
- }
-
- // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
- // than O(N log N).
-
+
+ case ArrayWithArrayStorage: {
+ unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+ if (storage->m_sparseMap.get()) {
+ throwOutOfMemoryError(exec);
+ return;
+ }
+
+ if (!lengthNotIncludingUndefined)
+ return;
+
+ // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
+ // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
+ // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
+ // random or otherwise changing results, effectively making compare function inconsistent.
+
+ Vector<ValueStringPair> values(lengthNotIncludingUndefined);
+ if (!values.begin()) {
+ throwOutOfMemoryError(exec);
+ return;
+ }
+
+ Heap::heap(this)->pushTempSortVector(&values);
+
+ bool isSortingPrimitiveValues = true;
+ for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
+ JSValue value = storage->m_vector[i].get();
+ ASSERT(!value.isUndefined());
+ values[i].first = value;
+ isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
+ }
+
+ // FIXME: The following loop continues to call toString on subsequent values even after
+ // a toString call raises an exception.
+
+ for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
+ values[i].second = values[i].first.toWTFStringInline(exec);
+
+ if (exec->hadException()) {
+ Heap::heap(this)->popTempSortVector(&values);
+ return;
+ }
+
+ // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
+ // than O(N log N).
+
#if HAVE(MERGESORT)
- if (isSortingPrimitiveValues)
- qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
- else
- mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
+ if (isSortingPrimitiveValues)
+ qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
+ else
+ mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
#else
- // FIXME: The qsort library function is likely to not be a stable sort.
- // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
- qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
+ // FIXME: The qsort library function is likely to not be a stable sort.
+ // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
+ qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
#endif
-
- // If the toString function changed the length of the array or vector storage,
- // increase the length to handle the orignal number of actual values.
- if (m_vectorLength < lengthNotIncludingUndefined)
- increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined);
- if (m_storage->m_length < lengthNotIncludingUndefined)
- m_storage->m_length = lengthNotIncludingUndefined;
-
- JSGlobalData& globalData = exec->globalData();
- for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
- m_storage->m_vector[i].set(globalData, this, values[i].first);
-
- Heap::heap(this)->popTempSortVector(&values);
-
- checkConsistency(SortConsistencyCheck);
+
+ // If the toString function changed the length of the array or vector storage,
+ // increase the length to handle the orignal number of actual values.
+ if (storage->vectorLength() < lengthNotIncludingUndefined) {
+ increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined);
+ storage = m_butterfly->arrayStorage();
+ }
+ if (storage->length() < lengthNotIncludingUndefined)
+ storage->setLength(lengthNotIncludingUndefined);
+
+ JSGlobalData& globalData = exec->globalData();
+ for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
+ storage->m_vector[i].set(globalData, this, values[i].first);
+
+ Heap::heap(this)->popTempSortVector(&values);
+
+ checkIndexingConsistency(SortConsistencyCheck);
+ return;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
}
struct AVLTreeNodeForArrayCompare {
@@ -1597,253 +788,257 @@ struct AVLTreeAbstractorForArrayCompare {
void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
{
- ASSERT(!inSparseMode());
-
- checkConsistency();
-
- // FIXME: This ignores exceptions raised in the compare function or in toNumber.
-
- // The maximum tree depth is compiled in - but the caller is clearly up to no good
- // if a larger array is passed.
- ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
- if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
- return;
-
- unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength);
- unsigned nodeCount = usedVectorLength + (m_sparseValueMap ? m_sparseValueMap->size() : 0);
-
- if (!nodeCount)
- return;
-
- AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
- tree.abstractor().m_exec = exec;
- tree.abstractor().m_compareFunction = compareFunction;
- tree.abstractor().m_compareCallType = callType;
- tree.abstractor().m_compareCallData = &callData;
- tree.abstractor().m_nodes.grow(nodeCount);
-
- if (callType == CallTypeJS)
- tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
-
- if (!tree.abstractor().m_nodes.begin()) {
- throwOutOfMemoryError(exec);
+ ASSERT(!inSparseIndexingMode());
+
+ switch (structure()->indexingType()) {
+ case ArrayClass:
return;
- }
-
- // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
- // right out from under us while we're building the tree here.
-
- unsigned numDefined = 0;
- unsigned numUndefined = 0;
-
- // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
- for (; numDefined < usedVectorLength; ++numDefined) {
- JSValue v = m_storage->m_vector[numDefined].get();
- if (!v || v.isUndefined())
- break;
- tree.abstractor().m_nodes[numDefined].value = v;
- tree.insert(numDefined);
- }
- for (unsigned i = numDefined; i < usedVectorLength; ++i) {
- JSValue v = m_storage->m_vector[i].get();
- if (v) {
- if (v.isUndefined())
- ++numUndefined;
- else {
- tree.abstractor().m_nodes[numDefined].value = v;
- tree.insert(numDefined);
- ++numDefined;
+
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+ checkIndexingConsistency();
+
+ // FIXME: This ignores exceptions raised in the compare function or in toNumber.
+
+ // The maximum tree depth is compiled in - but the caller is clearly up to no good
+ // if a larger array is passed.
+ ASSERT(storage->length() <= static_cast<unsigned>(std::numeric_limits<int>::max()));
+ if (storage->length() > static_cast<unsigned>(std::numeric_limits<int>::max()))
+ return;
+
+ unsigned usedVectorLength = min(storage->length(), storage->vectorLength());
+ unsigned nodeCount = usedVectorLength + (storage->m_sparseMap ? storage->m_sparseMap->size() : 0);
+
+ if (!nodeCount)
+ return;
+
+ AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
+ tree.abstractor().m_exec = exec;
+ tree.abstractor().m_compareFunction = compareFunction;
+ tree.abstractor().m_compareCallType = callType;
+ tree.abstractor().m_compareCallData = &callData;
+ tree.abstractor().m_nodes.grow(nodeCount);
+
+ if (callType == CallTypeJS)
+ tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
+
+ if (!tree.abstractor().m_nodes.begin()) {
+ throwOutOfMemoryError(exec);
+ return;
+ }
+
+ // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
+ // right out from under us while we're building the tree here.
+
+ unsigned numDefined = 0;
+ unsigned numUndefined = 0;
+
+ // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
+ for (; numDefined < usedVectorLength; ++numDefined) {
+ JSValue v = storage->m_vector[numDefined].get();
+ if (!v || v.isUndefined())
+ break;
+ tree.abstractor().m_nodes[numDefined].value = v;
+ tree.insert(numDefined);
+ }
+ for (unsigned i = numDefined; i < usedVectorLength; ++i) {
+ JSValue v = storage->m_vector[i].get();
+ if (v) {
+ if (v.isUndefined())
+ ++numUndefined;
+ else {
+ tree.abstractor().m_nodes[numDefined].value = v;
+ tree.insert(numDefined);
+ ++numDefined;
+ }
}
}
- }
-
- unsigned newUsedVectorLength = numDefined + numUndefined;
-
- if (SparseArrayValueMap* map = m_sparseValueMap) {
- newUsedVectorLength += map->size();
- if (newUsedVectorLength > m_vectorLength) {
- // Check that it is possible to allocate an array large enough to hold all the entries.
- if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) {
- throwOutOfMemoryError(exec);
- return;
+
+ unsigned newUsedVectorLength = numDefined + numUndefined;
+
+ if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
+ newUsedVectorLength += map->size();
+ if (newUsedVectorLength > storage->vectorLength()) {
+ // Check that it is possible to allocate an array large enough to hold all the entries.
+ if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) {
+ throwOutOfMemoryError(exec);
+ return;
+ }
+ storage = m_butterfly->arrayStorage();
}
+
+ SparseArrayValueMap::const_iterator end = map->end();
+ for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
+ tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode();
+ tree.insert(numDefined);
+ ++numDefined;
+ }
+
+ deallocateSparseIndexMap();
}
-
- SparseArrayValueMap::const_iterator end = map->end();
- for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
- tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode();
- tree.insert(numDefined);
- ++numDefined;
+
+ ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
+
+ // FIXME: If the compare function changed the length of the array, the following might be
+ // modifying the vector incorrectly.
+
+ // Copy the values back into m_storage.
+ AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
+ iter.start_iter_least(tree);
+ JSGlobalData& globalData = exec->globalData();
+ for (unsigned i = 0; i < numDefined; ++i) {
+ storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value);
+ ++iter;
}
-
- deallocateSparseMap();
+
+ // Put undefined values back in.
+ for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
+ storage->m_vector[i].setUndefined();
+
+ // Ensure that unused values in the vector are zeroed out.
+ for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
+ storage->m_vector[i].clear();
+
+ storage->m_numValuesInVector = newUsedVectorLength;
+
+ checkIndexingConsistency(SortConsistencyCheck);
+ return;
}
-
- ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
-
- // FIXME: If the compare function changed the length of the array, the following might be
- // modifying the vector incorrectly.
-
- // Copy the values back into m_storage.
- AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
- iter.start_iter_least(tree);
- JSGlobalData& globalData = exec->globalData();
- for (unsigned i = 0; i < numDefined; ++i) {
- m_storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value);
- ++iter;
+
+ default:
+ ASSERT_NOT_REACHED();
}
-
- // Put undefined values back in.
- for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
- m_storage->m_vector[i].setUndefined();
-
- // Ensure that unused values in the vector are zeroed out.
- for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
- m_storage->m_vector[i].clear();
-
- m_storage->m_numValuesInVector = newUsedVectorLength;
-
- checkConsistency(SortConsistencyCheck);
}
void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
{
- ArrayStorage* storage = m_storage;
-
- WriteBarrier<Unknown>* vector = storage->m_vector;
- unsigned vectorEnd = min(storage->m_length, m_vectorLength);
- unsigned i = 0;
- for (; i < vectorEnd; ++i) {
- WriteBarrier<Unknown>& v = vector[i];
- if (!v)
- break;
- args.append(v.get());
+ switch (structure()->indexingType()) {
+ case ArrayClass:
+ return;
+
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+
+ WriteBarrier<Unknown>* vector = storage->m_vector;
+ unsigned vectorEnd = min(storage->length(), storage->vectorLength());
+ unsigned i = 0;
+ for (; i < vectorEnd; ++i) {
+ WriteBarrier<Unknown>& v = vector[i];
+ if (!v)
+ break;
+ args.append(v.get());
+ }
+
+ for (; i < storage->length(); ++i)
+ args.append(get(exec, i));
+ return;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
}
-
- for (; i < storage->m_length; ++i)
- args.append(get(exec, i));
}
void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
{
ASSERT(length == this->length());
- UNUSED_PARAM(length);
- unsigned i = 0;
- WriteBarrier<Unknown>* vector = m_storage->m_vector;
- unsigned vectorEnd = min(length, m_vectorLength);
- for (; i < vectorEnd; ++i) {
- WriteBarrier<Unknown>& v = vector[i];
- if (!v)
- break;
- callFrame->setArgument(i, v.get());
+ switch (structure()->indexingType()) {
+ case ArrayClass:
+ return;
+
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+ unsigned i = 0;
+ WriteBarrier<Unknown>* vector = storage->m_vector;
+ unsigned vectorEnd = min(length, storage->vectorLength());
+ for (; i < vectorEnd; ++i) {
+ WriteBarrier<Unknown>& v = vector[i];
+ if (!v)
+ break;
+ callFrame->setArgument(i, v.get());
+ }
+
+ for (; i < length; ++i)
+ callFrame->setArgument(i, get(exec, i));
+ return;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
}
-
- for (; i < length; ++i)
- callFrame->setArgument(i, get(exec, i));
}
unsigned JSArray::compactForSorting(JSGlobalData& globalData)
{
- ASSERT(!inSparseMode());
-
- checkConsistency();
-
- ArrayStorage* storage = m_storage;
+ ASSERT(!inSparseIndexingMode());
- unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
-
- unsigned numDefined = 0;
- unsigned numUndefined = 0;
-
- for (; numDefined < usedVectorLength; ++numDefined) {
- JSValue v = storage->m_vector[numDefined].get();
- if (!v || v.isUndefined())
- break;
- }
+ checkIndexingConsistency();
+
+ switch (structure()->indexingType()) {
+ case ArrayClass:
+ return 0;
- for (unsigned i = numDefined; i < usedVectorLength; ++i) {
- JSValue v = storage->m_vector[i].get();
- if (v) {
- if (v.isUndefined())
- ++numUndefined;
- else
- storage->m_vector[numDefined++].setWithoutWriteBarrier(v);
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+
+ unsigned usedVectorLength = min(storage->length(), storage->vectorLength());
+
+ unsigned numDefined = 0;
+ unsigned numUndefined = 0;
+
+ for (; numDefined < usedVectorLength; ++numDefined) {
+ JSValue v = storage->m_vector[numDefined].get();
+ if (!v || v.isUndefined())
+ break;
}
- }
-
- unsigned newUsedVectorLength = numDefined + numUndefined;
-
- if (SparseArrayValueMap* map = m_sparseValueMap) {
- newUsedVectorLength += map->size();
- if (newUsedVectorLength > m_vectorLength) {
- // Check that it is possible to allocate an array large enough to hold all the entries - if not,
- // exception is thrown by caller.
- if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength))
- return 0;
-
- storage = m_storage;
+
+ for (unsigned i = numDefined; i < usedVectorLength; ++i) {
+ JSValue v = storage->m_vector[i].get();
+ if (v) {
+ if (v.isUndefined())
+ ++numUndefined;
+ else
+ storage->m_vector[numDefined++].setWithoutWriteBarrier(v);
+ }
}
-
- SparseArrayValueMap::const_iterator end = map->end();
- for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
- storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode());
-
- deallocateSparseMap();
- }
-
- for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
- storage->m_vector[i].setUndefined();
- for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
- storage->m_vector[i].clear();
-
- storage->m_numValuesInVector = newUsedVectorLength;
-
- checkConsistency(SortConsistencyCheck);
-
- return numDefined;
-}
-
-#if CHECK_ARRAY_CONSISTENCY
-
-void JSArray::checkConsistency(ConsistencyCheckType type)
-{
- ArrayStorage* storage = m_storage;
-
- ASSERT(!storage->m_inCompactInitialization);
-
- ASSERT(storage);
- if (type == SortConsistencyCheck)
- ASSERT(!m_sparseValueMap);
-
- unsigned numValuesInVector = 0;
- for (unsigned i = 0; i < m_vectorLength; ++i) {
- if (JSValue value = storage->m_vector[i].get()) {
- ASSERT(i < storage->m_length);
- if (type != DestructorConsistencyCheck)
- value.isUndefined(); // Likely to crash if the object was deallocated.
- ++numValuesInVector;
- } else {
- if (type == SortConsistencyCheck)
- ASSERT(i >= storage->m_numValuesInVector);
+
+ unsigned newUsedVectorLength = numDefined + numUndefined;
+
+ if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
+ newUsedVectorLength += map->size();
+ if (newUsedVectorLength > storage->vectorLength()) {
+ // Check that it is possible to allocate an array large enough to hold all the entries - if not,
+ // exception is thrown by caller.
+ if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength))
+ return 0;
+
+ storage = m_butterfly->arrayStorage();
+ }
+
+ SparseArrayValueMap::const_iterator end = map->end();
+ for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
+ storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode());
+
+ deallocateSparseIndexMap();
}
+
+ for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
+ storage->m_vector[i].setUndefined();
+ for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
+ storage->m_vector[i].clear();
+
+ storage->m_numValuesInVector = newUsedVectorLength;
+
+ checkIndexingConsistency(SortConsistencyCheck);
+
+ return numDefined;
}
- ASSERT(numValuesInVector == storage->m_numValuesInVector);
- ASSERT(numValuesInVector <= storage->m_length);
-
- if (m_sparseValueMap) {
- SparseArrayValueMap::const_iterator end = m_sparseValueMap->end();
- for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) {
- unsigned index = it->first;
- ASSERT(index < storage->m_length);
- ASSERT(index >= m_vectorLength);
- ASSERT(index <= MAX_ARRAY_INDEX);
- ASSERT(it->second);
- if (type != DestructorConsistencyCheck)
- it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated.
- }
+
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
}
}
-#endif
} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h
index 2aab8c683..d382f64a9 100644
--- a/Source/JavaScriptCore/runtime/JSArray.h
+++ b/Source/JavaScriptCore/runtime/JSArray.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * 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
@@ -21,142 +21,30 @@
#ifndef JSArray_h
#define JSArray_h
+#include "ArrayConventions.h"
+#include "ButterflyInlineMethods.h"
#include "JSObject.h"
-#define CHECK_ARRAY_CONSISTENCY 0
-
namespace JSC {
class JSArray;
class LLIntOffsetsExtractor;
- enum PutDirectIndexMode { PutDirectIndexLikePutDirect, PutDirectIndexShouldNotThrow, PutDirectIndexShouldThrow };
-
- struct SparseArrayEntry : public WriteBarrier<Unknown> {
- typedef WriteBarrier<Unknown> Base;
-
- SparseArrayEntry() : attributes(0) {}
-
- JSValue get(ExecState*, JSArray*) const;
- void get(PropertySlot&) const;
- void get(PropertyDescriptor&) const;
- JSValue getNonSparseMode() const;
-
- unsigned attributes;
- };
-
- class SparseArrayValueMap {
- typedef HashMap<uint64_t, SparseArrayEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > Map;
-
- enum Flags {
- Normal = 0,
- SparseMode = 1,
- LengthIsReadOnly = 2,
- };
-
- public:
- typedef Map::iterator iterator;
- typedef Map::const_iterator const_iterator;
- typedef Map::AddResult AddResult;
-
- SparseArrayValueMap()
- : m_flags(Normal)
- , m_reportedCapacity(0)
- {
- }
-
- void visitChildren(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 put(ExecState*, JSArray*, unsigned, JSValue, bool shouldThrow);
- bool putDirect(ExecState*, JSArray*, unsigned, JSValue, PutDirectIndexMode);
- AddResult add(JSArray*, 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;
- };
-
- // This struct holds the actual data values of an array. A JSArray object points to it's 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)
- struct ArrayStorage {
- unsigned m_length; // The "length" property on the array
- unsigned m_numValuesInVector;
- void* m_allocBase; // Pointer to base address returned by malloc(). Keeping this pointer does eliminate false positives from the leak detector.
-#if CHECK_ARRAY_CONSISTENCY
- // Needs to be a uintptr_t for alignment purposes.
- uintptr_t m_initializationIndex;
- uintptr_t m_inCompactInitialization;
-#else
- uintptr_t m_padding;
-#endif
- WriteBarrier<Unknown> m_vector[1];
-
- static ptrdiff_t lengthOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_length); }
- static ptrdiff_t numValuesInVectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector); }
- static ptrdiff_t allocBaseOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_allocBase); }
- static ptrdiff_t vectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_vector); }
- };
-
class JSArray : public JSNonFinalObject {
friend class LLIntOffsetsExtractor;
friend class Walker;
friend class JIT;
+ public:
+ typedef JSNonFinalObject Base;
+
protected:
- explicit JSArray(JSGlobalData& globalData, Structure* structure)
- : JSNonFinalObject(globalData, structure)
- , m_indexBias(0)
- , m_storage(0)
- , m_sparseValueMap(0)
+ explicit JSArray(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly)
+ : JSNonFinalObject(globalData, structure, butterfly)
{
}
- JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&, unsigned initialLength = 0);
- JS_EXPORT_PRIVATE JSArray* tryFinishCreationUninitialized(JSGlobalData&, unsigned initialLength);
-
public:
- typedef JSNonFinalObject Base;
-
- static void finalize(JSCell*);
-
static JSArray* create(JSGlobalData&, Structure*, unsigned initialLength = 0);
// tryCreateUninitialized is used for fast construction of arrays whose size and
@@ -169,26 +57,11 @@ namespace JSC {
JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool throwException);
static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
- JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
- static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
- // This is similar to the JSObject::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, PutDirectIndexMode mode = PutDirectIndexLikePutDirect)
- {
- if (canSetIndex(propertyName)) {
- setIndex(exec->globalData(), propertyName, value);
- return true;
- }
- return putDirectIndexBeyondVectorLength(exec, propertyName, value, mode);
- }
static JS_EXPORTDATA const ClassInfo s_info;
- unsigned length() const { return m_storage->m_length; }
+ unsigned length() const { return getArrayLength(); }
// OK to use on new arrays, but not if it might be a RegExpMatchArray.
bool setLength(ExecState*, unsigned, bool throwException = false);
@@ -202,142 +75,86 @@ namespace JSC {
bool shiftCount(ExecState*, unsigned count);
bool unshiftCount(ExecState*, unsigned count);
- bool canGetIndex(unsigned i) { return i < m_vectorLength && m_storage->m_vector[i]; }
- JSValue getIndex(unsigned i)
- {
- ASSERT(canGetIndex(i));
- return m_storage->m_vector[i].get();
- }
-
- bool canSetIndex(unsigned i) { return i < m_vectorLength; }
- void setIndex(JSGlobalData& globalData, unsigned i, JSValue v)
- {
- ASSERT(canSetIndex(i));
-
- WriteBarrier<Unknown>& x = m_storage->m_vector[i];
- if (!x) {
- ArrayStorage *storage = m_storage;
- ++storage->m_numValuesInVector;
- if (i >= storage->m_length)
- storage->m_length = i + 1;
- }
- x.set(globalData, this, v);
- }
-
- inline void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v)
- {
- ASSERT(canSetIndex(i));
- ArrayStorage *storage = m_storage;
-#if CHECK_ARRAY_CONSISTENCY
- ASSERT(storage->m_inCompactInitialization);
- // Check that we are initializing the next index in sequence.
- ASSERT(i == storage->m_initializationIndex);
- // tryCreateUninitialized set m_numValuesInVector to the initialLength,
- // check we do not try to initialize more than this number of properties.
- ASSERT(storage->m_initializationIndex < storage->m_numValuesInVector);
- storage->m_initializationIndex++;
-#endif
- ASSERT(i < storage->m_length);
- ASSERT(i < storage->m_numValuesInVector);
- storage->m_vector[i].set(globalData, this, v);
- }
-
- inline void completeInitialization(unsigned newLength)
- {
- // Check that we have initialized as meny properties as we think we have.
- ASSERT_UNUSED(newLength, newLength == m_storage->m_length);
-#if CHECK_ARRAY_CONSISTENCY
- // Check that the number of propreties initialized matches the initialLength.
- ASSERT(m_storage->m_initializationIndex == m_storage->m_numValuesInVector);
- ASSERT(m_storage->m_inCompactInitialization);
- m_storage->m_inCompactInitialization = false;
-#endif
- }
-
- bool inSparseMode()
- {
- SparseArrayValueMap* map = m_sparseValueMap;
- return map && map->sparseMode();
- }
-
void fillArgList(ExecState*, MarkedArgumentBuffer&);
void copyToArguments(ExecState*, CallFrame*, uint32_t length);
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
{
- return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
+ return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage);
}
- static ptrdiff_t storageOffset()
- {
- return OBJECT_OFFSETOF(JSArray, m_storage);
- }
-
- static ptrdiff_t vectorLengthOffset()
- {
- return OBJECT_OFFSETOF(JSArray, m_vectorLength);
- }
-
- JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
-
- void enterDictionaryMode(JSGlobalData&);
-
protected:
- static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;
+ static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags;
static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
static bool deleteProperty(JSCell*, ExecState*, PropertyName);
- static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
- static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+ JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
private:
- static size_t storageSize(unsigned vectorLength);
bool isLengthWritable()
{
- SparseArrayValueMap* map = m_sparseValueMap;
+ ArrayStorage* storage = arrayStorageOrNull();
+ if (!storage)
+ return false;
+ SparseArrayValueMap* map = storage->m_sparseMap.get();
return !map || !map->lengthIsReadOnly();
}
void setLengthWritable(ExecState*, bool writable);
- void putDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old);
- bool defineOwnNumericProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException);
- void allocateSparseMap(JSGlobalData&);
- void deallocateSparseMap();
- void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
- JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, PutDirectIndexMode);
-
- unsigned getNewVectorLength(unsigned desiredLength);
- bool increaseVectorLength(JSGlobalData&, unsigned newLength);
bool unshiftCountSlowCase(JSGlobalData&, unsigned count);
unsigned compactForSorting(JSGlobalData&);
+ };
- enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck };
- void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck);
-
- unsigned m_vectorLength; // The valid length of m_vector
- unsigned m_indexBias; // The number of JSValue sized blocks before ArrayStorage.
- ArrayStorage *m_storage;
-
- // FIXME: Maybe SparseArrayValueMap should be put into its own JSCell?
- SparseArrayValueMap* m_sparseValueMap;
+ inline Butterfly* createArrayButterfly(JSGlobalData& globalData, unsigned initialLength)
+ {
+ Butterfly* butterfly = Butterfly::create(
+ globalData, 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;
+#if CHECK_ARRAY_CONSISTENCY
+ storage->m_initializationIndex = 0;
+ storage->m_inCompactInitialization = 0;
+#endif
+ return butterfly;
+ }
- static ptrdiff_t sparseValueMapOffset() { return OBJECT_OFFSETOF(JSArray, m_sparseValueMap); }
- static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(JSArray, m_indexBias); }
- };
+ Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData&, unsigned initialLength);
inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength)
{
- JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure);
- array->finishCreation(globalData, initialLength);
+ Butterfly* butterfly = createArrayButterfly(globalData, initialLength);
+ JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly);
+ array->finishCreation(globalData);
return array;
}
inline JSArray* JSArray::tryCreateUninitialized(JSGlobalData& globalData, Structure* structure, unsigned initialLength)
{
- JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure);
- return array->tryFinishCreationUninitialized(globalData, initialLength);
+ unsigned vectorLength = std::max(BASE_VECTOR_LEN, initialLength);
+ if (vectorLength > MAX_STORAGE_VECTOR_LENGTH)
+ return 0;
+
+ void* temp;
+ if (!globalData.heap.tryAllocateStorage(Butterfly::totalSize(0, 0, true, ArrayStorage::sizeFor(vectorLength)), &temp))
+ return 0;
+ Butterfly* 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;
+#if CHECK_ARRAY_CONSISTENCY
+ storage->m_initializationIndex = 0;
+ storage->m_inCompactInitialization = true;
+#endif
+
+ JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly);
+ array->finishCreation(globalData);
+ return array;
}
JSArray* asArray(JSValue);
@@ -356,30 +173,6 @@ namespace JSC {
inline bool isJSArray(JSCell* cell) { return cell->classInfo() == &JSArray::s_info; }
inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); }
-// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
-// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
-// size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +
-// (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
-#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))
-
-// 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>.
-#define MIN_SPARSE_ARRAY_INDEX 10000U
-#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
- inline size_t JSArray::storageSize(unsigned vectorLength)
- {
- ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
-
- // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
- // - as asserted above - the following calculation cannot overflow.
- size_t size = (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>));
- // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
- // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
- ASSERT(((size - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))));
-
- return size;
- }
-
inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values)
{
JSGlobalData& globalData = exec->globalData();
diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
index 7540d4394..08d6831c0 100644
--- a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
+++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
@@ -45,7 +45,7 @@ EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec)
MarkedArgumentBuffer args;
for (unsigned i = 0; i < boundArgs->length(); ++i)
- args.append(boundArgs->getIndex(i));
+ args.append(boundArgs->getIndexQuickly(i));
for (unsigned i = 0; i < exec->argumentCount(); ++i)
args.append(exec->argument(i));
@@ -65,7 +65,7 @@ EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState* exec)
MarkedArgumentBuffer args;
for (unsigned i = 0; i < boundArgs->length(); ++i)
- args.append(boundArgs->getIndex(i));
+ args.append(boundArgs->getIndexQuickly(i));
for (unsigned i = 0; i < exec->argumentCount(); ++i)
args.append(exec->argument(i));
@@ -112,8 +112,8 @@ void JSBoundFunction::finishCreation(ExecState* exec, NativeExecutable* executab
Base::finishCreation(exec, executable, length, name);
ASSERT(inherits(&s_info));
- putDirectAccessor(exec->globalData(), exec->propertyNames().arguments, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
- putDirectAccessor(exec->globalData(), exec->propertyNames().caller, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
+ putDirectAccessor(exec, exec->propertyNames().arguments, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
+ putDirectAccessor(exec, exec->propertyNames().caller, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
}
void JSBoundFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
diff --git a/Source/JavaScriptCore/runtime/JSCell.cpp b/Source/JavaScriptCore/runtime/JSCell.cpp
index ffb76ff95..e050a53ef 100644
--- a/Source/JavaScriptCore/runtime/JSCell.cpp
+++ b/Source/JavaScriptCore/runtime/JSCell.cpp
@@ -178,6 +178,11 @@ void JSCell::getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, Enum
ASSERT_NOT_REACHED();
}
+void JSCell::getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode)
+{
+ ASSERT_NOT_REACHED();
+}
+
String JSCell::className(const JSObject*)
{
ASSERT_NOT_REACHED();
diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h
index a63e08e33..94f08f31b 100644
--- a/Source/JavaScriptCore/runtime/JSCell.h
+++ b/Source/JavaScriptCore/runtime/JSCell.h
@@ -64,6 +64,7 @@ namespace JSC {
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;
@@ -137,7 +138,7 @@ namespace JSC {
{
return &m_structure;
}
-
+
#if ENABLE(GC_VALIDATION)
Structure* unvalidatedStructure() { return m_structure.unvalidatedGet(); }
#endif
@@ -155,6 +156,7 @@ namespace JSC {
// 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_ASSERT void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+ static NO_RETURN_DUE_TO_ASSERT void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
static NO_RETURN_DUE_TO_ASSERT void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
static String className(const JSObject*);
static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue prototypeProperty);
@@ -337,6 +339,25 @@ namespace JSC {
return result;
}
+ template<typename T>
+ void* allocateCell(Heap& heap, size_t size)
+ {
+ ASSERT(size >= sizeof(T));
+#if ENABLE(GC_VALIDATION)
+ ASSERT(!heap.globalData()->isInitializingObject());
+ heap.globalData()->setInitializingObjectClass(&T::s_info);
+#endif
+ JSCell* result = 0;
+ if (NeedsDestructor<T>::value)
+ result = static_cast<JSCell*>(heap.allocateWithDestructor(size));
+ else {
+ ASSERT(T::s_info.methodTable.destroy == JSCell::destroy);
+ result = static_cast<JSCell*>(heap.allocateWithoutDestructor(size));
+ }
+ result->clearStructure();
+ return result;
+ }
+
inline bool isZapped(const JSCell* cell)
{
return cell->isZapped();
diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp
index e5cb8cf38..4afe63216 100644
--- a/Source/JavaScriptCore/runtime/JSFunction.cpp
+++ b/Source/JavaScriptCore/runtime/JSFunction.cpp
@@ -84,13 +84,6 @@ JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure*
{
}
-JSFunction::JSFunction(ExecState* exec, FunctionExecutable* executable, JSScope* scope)
- : Base(exec->globalData(), scope->globalObject()->functionStructure())
- , m_executable(exec->globalData(), this, executable)
- , m_scope(exec->globalData(), this, scope)
-{
-}
-
void JSFunction::finishCreation(ExecState* exec, NativeExecutable* executable, int length, const String& name)
{
Base::finishCreation(exec->globalData());
@@ -100,20 +93,6 @@ void JSFunction::finishCreation(ExecState* exec, NativeExecutable* executable, i
putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
}
-void JSFunction::finishCreation(ExecState* exec, FunctionExecutable* executable, JSScope* scope)
-{
- JSGlobalData& globalData = exec->globalData();
- Base::finishCreation(globalData);
- ASSERT(inherits(&s_info));
-
- // Switching the structure here is only safe if we currently have the function structure!
- ASSERT(structure() == scope->globalObject()->functionStructure());
- setStructureAndReallocateStorageIfNecessary(
- globalData,
- scope->globalObject()->namedFunctionStructure());
- putDirectOffset(globalData, scope->globalObject()->functionNameOffset(), executable->nameValue());
-}
-
Structure* JSFunction::cacheInheritorID(ExecState* exec)
{
JSValue prototype = get(exec, exec->globalData().propertyNames->prototype);
@@ -124,12 +103,12 @@ Structure* JSFunction::cacheInheritorID(ExecState* exec)
return m_cachedInheritorID.get();
}
-const String& JSFunction::name(ExecState* exec)
+String JSFunction::name(ExecState* exec)
{
- return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue();
+ return get(exec, exec->globalData().propertyNames->name).toWTFString(exec);
}
-const String JSFunction::displayName(ExecState* exec)
+String JSFunction::displayName(ExecState* exec)
{
JSValue displayName = getDirect(exec->globalData(), exec->globalData().propertyNames->displayName);
@@ -213,6 +192,13 @@ JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, PropertyName)
return jsNumber(thisObj->jsExecutable()->parameterCount());
}
+JSValue JSFunction::nameGetter(ExecState*, JSValue slotBase, PropertyName)
+{
+ JSFunction* thisObj = jsCast<JSFunction*>(slotBase);
+ ASSERT(!thisObj->isHostFunction());
+ return thisObj->jsExecutable()->nameValue();
+}
+
bool JSFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
{
JSFunction* thisObject = jsCast<JSFunction*>(cell);
@@ -236,7 +222,7 @@ bool JSFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName
if (thisObject->jsExecutable()->isStrictMode()) {
bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
if (!result) {
- thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
+ thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
ASSERT(result);
}
@@ -251,11 +237,16 @@ bool JSFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName
return true;
}
+ if (propertyName == exec->propertyNames().name) {
+ slot.setCacheableCustom(thisObject, 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->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
+ thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
ASSERT(result);
}
@@ -284,7 +275,7 @@ bool JSFunction::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Pro
if (thisObject->jsExecutable()->isStrictMode()) {
bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
if (!result) {
- thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
+ thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
ASSERT(result);
}
@@ -299,11 +290,16 @@ bool JSFunction::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Pro
return true;
}
+ if (propertyName == exec->propertyNames().name) {
+ descriptor.setDescriptor(thisObject->jsExecutable()->nameValue(), ReadOnly | DontEnum | DontDelete);
+ return true;
+ }
+
if (propertyName == exec->propertyNames().caller) {
if (thisObject->jsExecutable()->isStrictMode()) {
bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
if (!result) {
- thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
+ thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
ASSERT(result);
}
@@ -316,7 +312,7 @@ bool JSFunction::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Pro
return Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
}
-void JSFunction::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
JSFunction* thisObject = jsCast<JSFunction*>(object);
if (!thisObject->isHostFunction() && (mode == IncludeDontEnumProperties)) {
@@ -327,8 +323,9 @@ void JSFunction::getOwnPropertyNames(JSObject* object, ExecState* exec, Property
propertyNames.add(exec->propertyNames().arguments);
propertyNames.add(exec->propertyNames().caller);
propertyNames.add(exec->propertyNames().length);
+ propertyNames.add(exec->propertyNames().name);
}
- Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
+ Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
}
void JSFunction::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
@@ -356,7 +353,7 @@ void JSFunction::put(JSCell* cell, ExecState* exec, PropertyName propertyName, J
Base::put(thisObject, exec, propertyName, value, slot);
return;
}
- if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().caller) {
+ if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().name || propertyName == exec->propertyNames().caller) {
if (slot.isStrictMode())
throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
return;
@@ -371,6 +368,7 @@ bool JSFunction::deleteProperty(JSCell* cell, ExecState* exec, PropertyName prop
if (!thisObject->isHostFunction() && !exec->globalData().isInDefineOwnProperty()
&& (propertyName == exec->propertyNames().arguments
|| propertyName == exec->propertyNames().length
+ || propertyName == exec->propertyNames().name
|| propertyName == exec->propertyNames().prototype
|| propertyName == exec->propertyNames().caller))
return false;
@@ -396,19 +394,21 @@ bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, PropertyNa
if (propertyName == exec->propertyNames().arguments) {
if (thisObject->jsExecutable()->isStrictMode()) {
if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor))
- thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
+ thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
}
valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), exec->interpreter()->retrieveArgumentsFromVMCode(exec, thisObject));
} else if (propertyName == exec->propertyNames().caller) {
if (thisObject->jsExecutable()->isStrictMode()) {
if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor))
- thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
+ thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
}
valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), exec->interpreter()->retrieveCallerFromVMCode(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);
diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h
index 4bd5f46fa..c1f066585 100644
--- a/Source/JavaScriptCore/runtime/JSFunction.h
+++ b/Source/JavaScriptCore/runtime/JSFunction.h
@@ -59,14 +59,15 @@ namespace JSC {
static JSFunction* create(ExecState* exec, FunctionExecutable* executable, JSScope* scope)
{
- JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, executable, scope);
+ JSGlobalData& globalData = exec->globalData();
+ JSFunction* function = new (NotNull, allocateCell<JSFunction>(globalData.heap)) JSFunction(globalData, executable, scope);
ASSERT(function->structure()->globalObject());
- function->finishCreation(exec, executable, scope);
+ function->finishCreation(globalData);
return function;
}
- JS_EXPORT_PRIVATE const String& name(ExecState*);
- JS_EXPORT_PRIVATE const String displayName(ExecState*);
+ JS_EXPORT_PRIVATE String name(ExecState*);
+ JS_EXPORT_PRIVATE String displayName(ExecState*);
const String calculatedDisplayName(ExecState*);
JSScope* scope()
@@ -137,16 +138,16 @@ namespace JSC {
const static unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;
JS_EXPORT_PRIVATE JSFunction(ExecState*, JSGlobalObject*, Structure*);
- JSFunction(ExecState*, FunctionExecutable*, JSScope*);
+ JSFunction(JSGlobalData&, FunctionExecutable*, JSScope*);
void finishCreation(ExecState*, NativeExecutable*, int length, const String& name);
- void finishCreation(ExecState*, FunctionExecutable*, JSScope*);
+ using Base::finishCreation;
Structure* cacheInheritorID(ExecState*);
static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
- static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties);
+ static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties);
static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
@@ -163,6 +164,7 @@ namespace JSC {
static JSValue argumentsGetter(ExecState*, JSValue, PropertyName);
static JSValue callerGetter(ExecState*, JSValue, PropertyName);
static JSValue lengthGetter(ExecState*, JSValue, PropertyName);
+ static JSValue nameGetter(ExecState*, JSValue, PropertyName);
WriteBarrier<ExecutableBase> m_executable;
WriteBarrier<JSScope> m_scope;
diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp
index 217526f6a..e409c8219 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp
@@ -55,6 +55,7 @@
#include "ParserArena.h"
#include "RegExpCache.h"
#include "RegExpObject.h"
+#include "SparseArrayValueMapInlineMethods.h"
#include "StrictEvalActivation.h"
#include "StrongInlines.h"
#include <wtf/RetainPtr.h>
@@ -213,6 +214,7 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread
regExpStructure.set(*this, RegExp::createStructure(*this, 0, jsNull()));
sharedSymbolTableStructure.set(*this, SharedSymbolTable::createStructure(*this, 0, jsNull()));
structureChainStructure.set(*this, StructureChain::createStructure(*this, 0, jsNull()));
+ sparseArrayValueMapStructure.set(*this, SparseArrayValueMap::createStructure(*this, 0, jsNull()));
wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable);
@@ -457,7 +459,7 @@ void JSGlobalData::releaseExecutableMemory()
recompiler.currentlyExecutingFunctions.add(static_cast<FunctionExecutable*>(executable));
}
- heap.objectSpace().forEachCell<StackPreservingRecompiler>(recompiler);
+ heap.objectSpace().forEachLiveCell<StackPreservingRecompiler>(recompiler);
}
m_regExpCache->invalidateCode();
heap.collectAllGarbage();
diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h
index e4e6784da..68e0e25d1 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalData.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalData.h
@@ -244,6 +244,7 @@ namespace JSC {
Strong<Structure> regExpStructure;
Strong<Structure> sharedSymbolTableStructure;
Strong<Structure> structureChainStructure;
+ Strong<Structure> sparseArrayValueMapStructure;
IdentifierTable* identifierTable;
CommonIdentifiers* propertyNames;
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
index 97e9153cb..8ee8e1498 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
@@ -213,7 +213,7 @@ void JSGlobalObject::reset(JSValue prototype)
GetterSetter* protoAccessor = GetterSetter::create(exec);
protoAccessor->setGetter(exec->globalData(), JSFunction::create(exec, this, 0, String(), globalFuncProtoGetter));
protoAccessor->setSetter(exec->globalData(), JSFunction::create(exec, this, 0, String(), globalFuncProtoSetter));
- m_objectPrototype->putDirectAccessor(exec->globalData(), exec->propertyNames().underscoreProto, protoAccessor, Accessor | DontEnum);
+ m_objectPrototype->putDirectAccessor(exec, exec->propertyNames().underscoreProto, protoAccessor, Accessor | DontEnum);
m_functionPrototype->structure()->setPrototypeWithoutTransition(exec->globalData(), m_objectPrototype.get());
m_nameScopeStructure.set(exec->globalData(), this, JSNameScope::createStructure(exec->globalData(), this, jsNull()));
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
index 4588ec2d9..e9a3e2836 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
@@ -27,6 +27,7 @@
#include "CallFrame.h"
#include "Interpreter.h"
+#include "JSFunction.h"
#include "JSGlobalObject.h"
#include "JSString.h"
#include "JSStringBuilder.h"
diff --git a/Source/JavaScriptCore/runtime/JSONObject.cpp b/Source/JavaScriptCore/runtime/JSONObject.cpp
index 45854dcea..fe894afba 100644
--- a/Source/JavaScriptCore/runtime/JSONObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSONObject.cpp
@@ -521,8 +521,8 @@ bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBui
if (m_isArray) {
// Get the value.
JSValue value;
- if (m_isJSArray && asArray(m_object.get())->canGetIndex(index))
- value = asArray(m_object.get())->getIndex(index);
+ 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))
@@ -687,8 +687,8 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered)
indexStack.removeLast();
break;
}
- if (isJSArray(array) && array->canGetIndex(index))
- inValue = array->getIndex(index);
+ if (isJSArray(array) && array->canGetIndexQuickly(index))
+ inValue = array->getIndexQuickly(index);
else {
PropertySlot slot;
if (array->methodTable()->getOwnPropertySlotByIndex(array, m_exec, index, slot))
diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp
index 812ba3bc8..6eac0d1cb 100644
--- a/Source/JavaScriptCore/runtime/JSObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSObject.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
- * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012 Apple Inc. All rights reserved.
* Copyright (C) 2007 Eric Seidel (eric@webkit.org)
*
* This library is free software; you can redistribute it and/or
@@ -24,10 +24,12 @@
#include "config.h"
#include "JSObject.h"
+#include "ButterflyInlineMethods.h"
#include "CopiedSpaceInlineMethods.h"
#include "DatePrototype.h"
#include "ErrorConstructor.h"
#include "GetterSetter.h"
+#include "IndexingHeaderInlineMethods.h"
#include "JSFunction.h"
#include "JSGlobalObject.h"
#include "JSGlobalThis.h"
@@ -38,12 +40,19 @@
#include "Operations.h"
#include "PropertyDescriptor.h"
#include "PropertyNameArray.h"
+#include "Reject.h"
#include "SlotVisitorInlineMethods.h"
+#include "SparseArrayValueMapInlineMethods.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 above.
+static unsigned lastArraySize = 0;
+
JSCell* getCallableObjectSlow(JSCell* cell)
{
Structure* structure = cell->structure();
@@ -86,29 +95,81 @@ static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* class
}
}
-ALWAYS_INLINE void JSObject::visitOutOfLineStorage(SlotVisitor& visitor, PropertyStorage storage, size_t storageSize)
+ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize)
{
- ASSERT(storage);
- ASSERT(storageSize);
-
- size_t capacity = structure()->outOfLineCapacity();
- ASSERT(capacity);
- size_t capacityInBytes = capacity * sizeof(WriteBarrierBase<Unknown>);
- PropertyStorage baseOfStorage = storage - capacity - 1;
- if (visitor.checkIfShouldCopyAndPinOtherwise(baseOfStorage, capacityInBytes)) {
- PropertyStorage newBaseOfStorage = static_cast<PropertyStorage>(visitor.allocateNewSpace(capacityInBytes));
- PropertyStorage currentTarget = newBaseOfStorage + capacity;
- PropertyStorage newStorage = currentTarget + 1;
- PropertyStorage currentSource = storage - 1;
+ ASSERT(butterfly);
+
+ Structure* structure = this->structure();
+
+ size_t propertyCapacity = structure->outOfLineCapacity();
+ size_t preCapacity;
+ size_t indexingPayloadSizeInBytes;
+ bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType());
+ 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.checkIfShouldCopyAndPinOtherwise(
+ butterfly->base(preCapacity, propertyCapacity), capacityInBytes)) {
+ Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
+
+ // Mark and copy the properties.
+ PropertyStorage currentTarget = newButterfly->propertyStorage();
+ PropertyStorage currentSource = butterfly->propertyStorage();
for (size_t count = storageSize; count--;) {
JSValue value = (--currentSource)->get();
ASSERT(value);
visitor.appendUnbarrieredValue(&value);
(--currentTarget)->setWithoutWriteBarrier(value);
}
- m_outOfLineStorage.set(newStorage, StorageBarrier::Unchecked);
- } else
- visitor.appendValues(storage - storageSize - 1, storageSize);
+
+ if (UNLIKELY(hasIndexingHeader)) {
+ *newButterfly->indexingHeader() = *butterfly->indexingHeader();
+
+ // Mark and copy the array if appropriate.
+ switch (structure->indexingType()) {
+ case ArrayWithArrayStorage:
+ case NonArrayWithArrayStorage: {
+ newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage());
+ WriteBarrier<Unknown>* currentTarget = newButterfly->arrayStorage()->m_vector;
+ WriteBarrier<Unknown>* currentSource = butterfly->arrayStorage()->m_vector;
+ for (size_t count = newButterfly->arrayStorage()->vectorLength(); count--;) {
+ JSValue value = (currentSource++)->get();
+ if (value)
+ visitor.appendUnbarrieredValue(&value);
+ (currentTarget++)->setWithoutWriteBarrier(value);
+ }
+ if (newButterfly->arrayStorage()->m_sparseMap)
+ visitor.append(&newButterfly->arrayStorage()->m_sparseMap);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ m_butterfly = newButterfly;
+ } else {
+ // Mark the properties.
+ visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize);
+
+ // Mark the array if appropriate.
+ switch (structure->indexingType()) {
+ case ArrayWithArrayStorage:
+ case NonArrayWithArrayStorage:
+ 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)
@@ -122,9 +183,9 @@ void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
JSCell::visitChildren(thisObject, visitor);
- PropertyStorage storage = thisObject->outOfLineStorage();
- if (storage)
- thisObject->visitOutOfLineStorage(visitor, storage, thisObject->structure()->outOfLineSizeForKnownNonFinalObject());
+ Butterfly* butterfly = thisObject->butterfly();
+ if (butterfly)
+ thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownNonFinalObject());
#if !ASSERT_DISABLED
visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
@@ -142,9 +203,9 @@ void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
JSCell::visitChildren(thisObject, visitor);
- PropertyStorage storage = thisObject->outOfLineStorage();
- if (storage)
- thisObject->visitOutOfLineStorage(visitor, storage, thisObject->structure()->outOfLineSizeForKnownFinalObject());
+ Butterfly* butterfly = thisObject->butterfly();
+ if (butterfly)
+ thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownFinalObject());
size_t storageSize = thisObject->structure()->inlineSizeForKnownFinalObject();
visitor.appendValues(thisObject->inlineStorage(), storageSize);
@@ -161,10 +222,50 @@ String JSObject::className(const JSObject* object)
return info->className;
}
-bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
+bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, 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().
+
JSObject* thisObject = jsCast<JSObject*>(cell);
- return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot);
+
+ if (i > MAX_ARRAY_INDEX)
+ return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
+
+ switch (thisObject->structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ break;
+
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage: {
+ 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(value);
+ return true;
+ }
+ } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
+ SparseArrayValueMap::iterator it = map->find(i);
+ if (it != map->notFound()) {
+ it->second.get(slot);
+ return true;
+ }
+ }
+ break;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ return false;
}
// ECMA 8.6.2.2
@@ -174,6 +275,14 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV
ASSERT(value);
ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
JSGlobalData& globalData = exec->globalData();
+
+ // 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.
+ unsigned i = propertyName.asIndex();
+ if (i != PropertyName::NotAnIndex) {
+ putByIndex(thisObject, exec, i, value, slot.isStrictMode());
+ return;
+ }
// Check if there are any setters or getters in the prototype chain
JSValue prototype;
@@ -235,9 +344,145 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV
void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
{
- PutPropertySlot slot(shouldThrow);
JSObject* thisObject = jsCast<JSObject*>(cell);
- thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
+ thisObject->checkIndexingConsistency();
+
+ if (UNLIKELY(propertyName > MAX_ARRAY_INDEX)) {
+ PutPropertySlot slot(shouldThrow);
+ thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
+ return;
+ }
+
+ switch (thisObject->structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ break;
+
+ 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->globalData(), thisObject, value);
+ thisObject->checkIndexingConsistency();
+ return;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
+ thisObject->checkIndexingConsistency();
+}
+
+ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData& globalData, ArrayStorage* storage)
+{
+ SparseArrayValueMap* map = storage->m_sparseMap.get();
+
+ if (!map)
+ map = allocateSparseIndexMap(globalData);
+
+ 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->second.set(globalData, this, value);
+ }
+
+ Butterfly* newButterfly = storage->butterfly()->resizeArray(globalData, structure(), 0, ArrayStorage::sizeFor(0));
+ if (!newButterfly)
+ CRASH();
+
+ m_butterfly = newButterfly;
+ newButterfly->arrayStorage()->m_indexBias = 0;
+ newButterfly->arrayStorage()->setVectorLength(0);
+ newButterfly->arrayStorage()->m_sparseMap.set(globalData, this, map);
+
+ return newButterfly->arrayStorage();
+}
+
+void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData)
+{
+ switch (structure()->indexingType()) {
+ case ArrayWithArrayStorage:
+ case NonArrayWithArrayStorage:
+ enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage());
+ break;
+
+ default:
+ break;
+ }
+}
+
+ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned length, unsigned vectorLength)
+{
+ IndexingType oldType = structure()->indexingType();
+ ASSERT_UNUSED(oldType, oldType == NonArray || oldType == ArrayClass);
+ Butterfly* newButterfly = m_butterfly->growArrayRight(
+ globalData, structure(), structure()->outOfLineCapacity(), false, 0,
+ ArrayStorage::sizeFor(vectorLength));
+ if (!newButterfly)
+ CRASH();
+ ArrayStorage* result = newButterfly->arrayStorage();
+ result->setLength(length);
+ result->setVectorLength(vectorLength);
+ result->m_sparseMap.clear();
+ result->m_numValuesInVector = 0;
+ result->m_indexBias = 0;
+#if CHECK_ARRAY_CONSISTENCY
+ result->m_initializationIndex = 0;
+ result->m_inCompactInitialization = 0;
+#endif
+ Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateArrayStorage);
+ setButterfly(globalData, newButterfly, newStructure);
+ return result;
+}
+
+ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData)
+{
+ return createArrayStorage(globalData, 0, BASE_VECTOR_LEN);
+}
+
+ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData)
+{
+ switch (structure()->indexingType()) {
+ case ArrayWithArrayStorage:
+ case NonArrayWithArrayStorage:
+ return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage());
+
+ case ArrayClass:
+ case NonArray: {
+ createArrayStorage(globalData, 0, 0);
+ SparseArrayValueMap* map = allocateSparseIndexMap(globalData);
+ map->setSparseMode();
+ return arrayStorage();
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
}
void JSObject::putDirectVirtual(JSObject* object, ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
@@ -269,10 +514,18 @@ bool JSObject::allowsAccessFrom(ExecState* exec)
return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
}
-void JSObject::putDirectAccessor(JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)
+void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
{
ASSERT(value.isGetterSetter() && (attributes & Accessor));
+ unsigned index = propertyName.asIndex();
+ if (index != PropertyName::NotAnIndex) {
+ putDirectIndex(exec, index, value, attributes, PutDirectIndexLikePutDirect);
+ return;
+ }
+
+ JSGlobalData& globalData = exec->globalData();
+
PutPropertySlot slot;
putDirectInternal<PutModeDefineOwnProperty>(globalData, propertyName, value, attributes, slot, getCallableObject(value));
@@ -304,6 +557,10 @@ bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
{
JSObject* thisObject = jsCast<JSObject*>(cell);
+
+ unsigned i = propertyName.asIndex();
+ if (i != PropertyName::NotAnIndex)
+ return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i);
if (!thisObject->staticFunctionsReified())
thisObject->reifyStaticFunctionsForDelete(exec);
@@ -332,10 +589,45 @@ bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
return const_cast<JSObject*>(this)->methodTable()->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
}
-bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
+bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
{
JSObject* thisObject = jsCast<JSObject*>(cell);
- return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, propertyName));
+
+ if (i > MAX_ARRAY_INDEX)
+ return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i));
+
+ switch (thisObject->structure()->indexingType()) {
+ case ArrayClass:
+ case NonArray:
+ return true;
+
+ case ArrayWithArrayStorage:
+ case NonArrayWithArrayStorage: {
+ 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->second.attributes & DontDelete)
+ return false;
+ map->remove(it);
+ }
+ }
+
+ thisObject->checkIndexingConsistency();
+ return true;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
}
static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, PropertyName propertyName)
@@ -465,6 +757,51 @@ void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameA
void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
+ // 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->structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ break;
+
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage: {
+ 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(Identifier::from(exec, i));
+ }
+
+ if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
+ Vector<unsigned> keys;
+ keys.reserveCapacity(map->size());
+
+ SparseArrayValueMap::const_iterator end = map->end();
+ for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
+ if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum))
+ keys.append(static_cast<unsigned>(it->first));
+ }
+
+ std::sort(keys.begin(), keys.end());
+ for (unsigned i = 0; i < keys.size(); ++i)
+ propertyNames.add(Identifier::from(exec, keys[i]));
+ }
+ break;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ object->methodTable()->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
+}
+
+void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified());
object->structure()->getPropertyNamesFromStructure(exec->globalData(), propertyNames, mode);
}
@@ -515,8 +852,7 @@ void JSObject::freeze(JSGlobalData& globalData)
void JSObject::preventExtensions(JSGlobalData& globalData)
{
- if (isJSArray(this))
- asArray(this)->enterDictionaryMode(globalData);
+ enterDictionaryIndexingMode(globalData);
if (isExtensible())
setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure()));
}
@@ -573,11 +909,11 @@ bool JSObject::removeDirect(JSGlobalData& globalData, PropertyName propertyName)
return true;
}
-NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
+NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, PropertyOffset offset)
{
- if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
+ if (JSObject* getterFunction = asGetterSetter(getDirectOffset(offset))->getter()) {
if (!structure()->isDictionary())
- slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
+ slot.setCacheableGetterSlot(this, getterFunction, offset);
else
slot.setGetterSlot(getterFunction);
} else
@@ -603,26 +939,528 @@ Structure* JSObject::createInheritorID(JSGlobalData& globalData)
return inheritorID;
}
-PropertyStorage JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize)
+void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
{
- ASSERT(newSize > oldSize);
+ if (descriptor.isDataDescriptor()) {
+ if (descriptor.value())
+ entryInMap->set(exec->globalData(), this, descriptor.value());
+ else if (oldDescriptor.isAccessorDescriptor())
+ entryInMap->set(exec->globalData(), this, jsUndefined());
+ entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor;
+ return;
+ }
- // It's important that this function not rely on structure(), since
- // we might be in the middle of a transition.
+ 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(exec);
+ if (getter)
+ accessor->setGetter(exec->globalData(), getter);
+ if (setter)
+ accessor->setSetter(exec->globalData(), setter);
+
+ entryInMap->set(exec->globalData(), this, 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, PropertyDescriptor& descriptor, bool throwException)
+{
+ ASSERT(index != 0xFFFFFFFF);
+
+ 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->globalData());
+ }
+
+ SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get();
+ 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->second;
+
+ // 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(JSGlobalData& globalData)
+{
+ SparseArrayValueMap* result = SparseArrayValueMap::create(globalData);
+ arrayStorage()->m_sparseMap.set(globalData, this, result);
+ return result;
+}
+
+void JSObject::deallocateSparseIndexMap()
+{
+ if (ArrayStorage* arrayStorage = arrayStorageOrNull())
+ arrayStorage->m_sparseMap.clear();
+}
+
+void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
+{
+ JSGlobalData& globalData = exec->globalData();
+
+ // 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 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((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {
+ // success! - reread m_storage since it has likely been reallocated, and store to the vector.
+ storage = arrayStorage();
+ storage->m_vector[i].set(globalData, 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->globalData());
+ 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->globalData(), 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->first].set(globalData, this, it->second.getNonSparseMode());
+ deallocateSparseIndexMap();
+
+ // Store the new property into the vector.
+ WriteBarrier<Unknown>& valueSlot = vector[i];
+ if (!valueSlot)
+ ++storage->m_numValuesInVector;
+ valueSlot.set(globalData, this, value);
+}
+
+void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
+{
+ JSGlobalData& globalData = exec->globalData();
+
+ // i should be a valid array index that is outside of the current vector.
+ ASSERT(i <= MAX_ARRAY_INDEX);
- PropertyStorage oldPropertyStorage = m_outOfLineStorage.get();
- PropertyStorage newPropertyStorage = 0;
+ switch (structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass: {
+ if (indexingShouldBeSparse()) {
+ putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData));
+ break;
+ }
+ if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) {
+ putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, createArrayStorage(globalData, 0, 0));
+ break;
+ }
+
+ ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1));
+ storage->m_vector[i].set(globalData, this, value);
+ storage->m_numValuesInVector = 1;
+ break;
+ }
+
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
+ break;
+
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
+{
+ JSGlobalData& globalData = exec->globalData();
+
+ // i should be a valid array index that is outside of the current vector.
+ 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))
+ && increaseVectorLength(globalData, i + 1))) {
+ // success! - reread m_storage since it has likely been reallocated, and store to the vector.
+ storage = arrayStorage();
+ storage->m_vector[i].set(globalData, 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->globalData());
+ 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->globalData(), 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->first].set(globalData, this, it->second.getNonSparseMode());
+ deallocateSparseIndexMap();
+
+ // Store the new property into the vector.
+ WriteBarrier<Unknown>& valueSlot = vector[i];
+ if (!valueSlot)
+ ++storage->m_numValuesInVector;
+ valueSlot.set(globalData, this, value);
+ return true;
+}
+
+bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
+{
+ JSGlobalData& globalData = exec->globalData();
+
+ // i should be a valid array index that is outside of the current vector.
+ ASSERT(i <= MAX_ARRAY_INDEX);
+
+ switch (structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass: {
+ if (indexingShouldBeSparse() || attributes)
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData));
+ if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH)
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, createArrayStorage(globalData, 0, 0));
+
+ ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1));
+ storage->m_vector[i].set(globalData, this, value);
+ storage->m_numValuesInVector = 1;
+ return true;
+ }
+
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
+
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+}
+
+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 {
+ // Mathematically equivalent to:
+ // increasedLength = (newLength * 3 + 1) / 2;
+ // or:
+ // increasedLength = (unsigned)ceil(newLength * 1.5));
+ // This form is not prone to internal overflow.
+ increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1);
+ }
+
+ ASSERT(increasedLength >= desiredLength);
+
+ lastArraySize = std::min(increasedLength, FIRST_VECTOR_GROW);
+
+ return std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
+}
- // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
- void* temp = newPropertyStorage;
- if (!globalData.heap.tryAllocateStorage(sizeof(WriteBarrierBase<Unknown>) * newSize, &temp))
+ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength)
+{
+ unsigned vectorLength;
+ unsigned length;
+
+ switch (structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ vectorLength = 0;
+ length = 0;
+ break;
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ vectorLength = m_butterfly->arrayStorage()->vectorLength();
+ length = m_butterfly->arrayStorage()->length();
+ break;
+ default:
CRASH();
- newPropertyStorage = static_cast<PropertyStorage>(temp) + newSize + 1;
+ return 0;
+ }
+ return getNewVectorLength(vectorLength, length, desiredLength);
+}
+
+bool JSObject::increaseVectorLength(JSGlobalData& globalData, 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();
+
+ 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.
+ if (LIKELY(!indexBias)) {
+ Butterfly* newButterfly = storage->butterfly()->growArrayRight(globalData, structure(), structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength));
+ if (!newButterfly)
+ return false;
+ m_butterfly = newButterfly;
+ newButterfly->arrayStorage()->setVectorLength(newVectorLength);
+ return true;
+ }
- memcpy(newPropertyStorage - oldSize - 1, oldPropertyStorage - oldSize - 1, sizeof(WriteBarrierBase<Unknown>) * oldSize);
+ // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
+ unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
+ Butterfly* newButterfly = storage->butterfly()->resizeArray(
+ globalData,
+ structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength),
+ newIndexBias, true, ArrayStorage::sizeFor(newVectorLength));
+ if (!newButterfly)
+ return false;
+
+ m_butterfly = newButterfly;
+ newButterfly->arrayStorage()->setVectorLength(newVectorLength);
+ newButterfly->arrayStorage()->m_indexBias = newIndexBias;
+ return true;
+}
- ASSERT(newPropertyStorage);
- return newPropertyStorage;
+#if CHECK_ARRAY_CONSISTENCY
+void JSObject::checkIndexingConsistency(ConsistencyCheckType type)
+{
+ ArrayStorage* storage = arrayStorageOrNull();
+ if (!storage)
+ return;
+
+ ASSERT(!storage->m_inCompactInitialization);
+
+ ASSERT(storage);
+ if (type == SortConsistencyCheck)
+ ASSERT(!storage->m_sparseMap);
+
+ unsigned numValuesInVector = 0;
+ for (unsigned i = 0; i < storage->vectorLength(); ++i) {
+ if (JSValue value = storage->m_vector[i].get()) {
+ ASSERT(i < storage->length());
+ if (type != DestructorConsistencyCheck)
+ value.isUndefined(); // Likely to crash if the object was deallocated.
+ ++numValuesInVector;
+ } else {
+ if (type == SortConsistencyCheck)
+ ASSERT(i >= storage->m_numValuesInVector);
+ }
+ }
+ ASSERT(numValuesInVector == storage->m_numValuesInVector);
+ ASSERT(numValuesInVector <= storage->length());
+
+ if (m_sparseValueMap) {
+ SparseArrayValueMap::const_iterator end = m_sparseValueMap->end();
+ for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) {
+ unsigned index = it->first;
+ ASSERT(index < storage->length());
+ ASSERT(index >= storage->vectorLength());
+ ASSERT(index <= MAX_ARRAY_INDEX);
+ ASSERT(it->second);
+ if (type != DestructorConsistencyCheck)
+ it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated.
+ }
+ }
+}
+#endif // CHECK_ARRAY_CONSISTENCY
+
+Butterfly* JSObject::growOutOfLineStorage(JSGlobalData& globalData, 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 m_butterfly->growPropertyStorage(globalData, structure(), oldSize, newSize);
}
bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
@@ -630,10 +1468,46 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope
unsigned attributes = 0;
JSCell* cell = 0;
PropertyOffset offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell);
- if (offset == invalidOffset)
+ if (isValidOffset(offset)) {
+ descriptor.setDescriptor(object->getDirectOffset(offset), attributes);
+ return true;
+ }
+
+ unsigned i = propertyName.asIndex();
+ if (i == PropertyName::NotAnIndex)
return false;
- descriptor.setDescriptor(object->getDirectOffset(offset), attributes);
- return true;
+
+ switch (object->structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ return false;
+
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = object->m_butterfly->arrayStorage();
+ if (i >= storage->length())
+ return false;
+ if (i < storage->vectorLength()) {
+ WriteBarrier<Unknown>& value = storage->m_vector[i];
+ if (!value)
+ return false;
+ descriptor.setDescriptor(value.get(), 0);
+ return true;
+ }
+ if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
+ SparseArrayValueMap::iterator it = map->find(i);
+ if (it == map->notFound())
+ return false;
+ it->second.get(descriptor);
+ return true;
+ }
+ return false;
+ }
+
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
}
bool JSObject::getPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
@@ -658,7 +1532,7 @@ static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName proper
accessor->setGetter(exec->globalData(), oldDescriptor.getterObject());
if (oldDescriptor.setterPresent())
accessor->setSetter(exec->globalData(), oldDescriptor.setterObject());
- target->putDirectAccessor(exec->globalData(), propertyName, accessor, attributes | Accessor);
+ target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
return true;
}
JSValue newValue = jsUndefined();
@@ -683,10 +1557,19 @@ static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName proper
else if (oldDescriptor.setterPresent())
accessor->setSetter(exec->globalData(), oldDescriptor.setterObject());
- target->putDirectAccessor(exec->globalData(), propertyName, accessor, attributes | Accessor);
+ target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
return true;
}
+void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
+{
+ unsigned asIndex = propertyName.asIndex();
+ if (asIndex == PropertyName::NotAnIndex)
+ putDirect(exec->globalData(), propertyName, value);
+ else
+ putDirectIndex(exec, asIndex, value);
+}
+
class DefineOwnPropertyScope {
public:
DefineOwnPropertyScope(ExecState* exec)
@@ -704,26 +1587,26 @@ private:
JSGlobalData& m_globalData;
};
-bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
+bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, 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 (!object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) {
+ if (!methodTable()->getOwnPropertyDescriptor(this, exec, propertyName, current)) {
// unless extensions are prevented!
- if (!object->isExtensible()) {
+ if (!isExtensible()) {
if (throwException)
throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible.")));
return false;
}
PropertyDescriptor oldDescriptor;
oldDescriptor.setValue(jsUndefined());
- return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
+ return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
}
if (descriptor.isEmpty())
@@ -749,8 +1632,8 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
// A generic descriptor is simply changing the attributes of an existing property
if (descriptor.isGenericDescriptor()) {
if (!current.attributesEqual(descriptor)) {
- object->methodTable()->deleteProperty(object, exec, propertyName);
- return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
+ methodTable()->deleteProperty(this, exec, propertyName);
+ return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
}
return true;
}
@@ -762,8 +1645,8 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
return false;
}
- object->methodTable()->deleteProperty(object, exec, propertyName);
- return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
+ methodTable()->deleteProperty(this, exec, propertyName);
+ return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
}
// Changing the value and attributes of an existing property
@@ -784,8 +1667,8 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
}
if (current.attributesEqual(descriptor) && !descriptor.value())
return true;
- object->methodTable()->deleteProperty(object, exec, propertyName);
- return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
+ methodTable()->deleteProperty(this, exec, propertyName);
+ return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
}
// Changing the accessor functions of an existing accessor property
@@ -802,7 +1685,7 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
return false;
}
}
- JSValue accessor = object->getDirect(exec->globalData(), propertyName);
+ JSValue accessor = getDirect(exec->globalData(), propertyName);
if (!accessor)
return false;
GetterSetter* getterSetter = asGetterSetter(accessor);
@@ -812,12 +1695,37 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
getterSetter->setGetter(exec->globalData(), descriptor.getterObject());
if (current.attributesEqual(descriptor))
return true;
- object->methodTable()->deleteProperty(object, exec, propertyName);
+ methodTable()->deleteProperty(this, exec, propertyName);
unsigned attrs = descriptor.attributesOverridingCurrent(current);
- object->putDirectAccessor(exec->globalData(), propertyName, getterSetter, attrs | Accessor);
+ putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor);
return true;
}
+bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
+{
+ // If it's an array index, then use the indexed property storage.
+ unsigned index = propertyName.asIndex();
+ if (index != PropertyName::NotAnIndex) {
+ // 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, descriptor, throwException);
+ }
+
+ return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
+}
+
+bool JSObject::getOwnPropertySlotSlow(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+ unsigned i = propertyName.asIndex();
+ if (i != PropertyName::NotAnIndex)
+ return getOwnPropertySlotByIndex(this, exec, i, slot);
+ return false;
+}
+
JSObject* throwTypeError(ExecState* exec, const String& message)
{
return throwError(exec, createTypeError(exec, message));
diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h
index 5e2c12f2f..8df521b75 100644
--- a/Source/JavaScriptCore/runtime/JSObject.h
+++ b/Source/JavaScriptCore/runtime/JSObject.h
@@ -1,7 +1,7 @@
/*
* 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 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2004, 2005, 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
@@ -24,17 +24,22 @@
#define JSObject_h
#include "ArgList.h"
+#include "ArrayConventions.h"
+#include "ArrayStorage.h"
+#include "Butterfly.h"
#include "ClassInfo.h"
#include "CommonIdentifiers.h"
#include "CallFrame.h"
#include "JSCell.h"
#include "PropertySlot.h"
+#include "PropertyStorage.h"
+#include "PutDirectIndexMode.h"
#include "PutPropertySlot.h"
-#include "StorageBarrier.h"
#include "Structure.h"
#include "JSGlobalData.h"
#include "JSString.h"
+#include "SparseArrayValueMap.h"
#include <wtf/StdLibExtras.h>
namespace JSC {
@@ -79,6 +84,13 @@ namespace JSC {
Accessor = 1 << 5, // property is a getter/setter
};
+ 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 {
@@ -96,7 +108,7 @@ namespace JSC {
public:
typedef JSCell Base;
-
+
JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
JS_EXPORT_PRIVATE static String className(const JSObject*);
@@ -120,18 +132,196 @@ namespace JSC {
bool allowsAccessFrom(ExecState*);
+ unsigned getArrayLength() const
+ {
+ switch (structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ return 0;
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ return m_butterfly->arrayStorage()->length();
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+ }
+
+ unsigned getVectorLength()
+ {
+ switch (structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ return 0;
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ return m_butterfly->arrayStorage()->vectorLength();
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+ }
+
JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool 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 && canSetIndexQuickly(propertyName)) {
+ setIndexQuickly(exec->globalData(), 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.
+ void putDirectMayBeIndex(ExecState*, PropertyName, JSValue);
+
+ bool canGetIndexQuickly(unsigned i)
+ {
+ switch (structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ return false;
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i];
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ }
+
+ JSValue getIndexQuickly(unsigned i)
+ {
+ switch (structure()->indexingType()) {
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ return m_butterfly->arrayStorage()->m_vector[i].get();
+ default:
+ ASSERT_NOT_REACHED();
+ return JSValue();
+ }
+ }
+
+ bool canSetIndexQuickly(unsigned i)
+ {
+ switch (structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ return false;
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ return i < m_butterfly->arrayStorage()->vectorLength();
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ }
+
+ void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v)
+ {
+ switch (structure()->indexingType()) {
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage: {
+ WriteBarrier<Unknown>& x = m_butterfly->arrayStorage()->m_vector[i];
+ if (!x) {
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+ ++storage->m_numValuesInVector;
+ if (i >= storage->length())
+ storage->setLength(i + 1);
+ }
+ x.set(globalData, this, v);
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ }
+
+ void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v)
+ {
+ switch (structure()->indexingType()) {
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+#if CHECK_ARRAY_CONSISTENCY
+ ASSERT(storage->m_inCompactInitialization);
+ // Check that we are initializing the next index in sequence.
+ ASSERT(i == storage->m_initializationIndex);
+ // tryCreateUninitialized set m_numValuesInVector to the initialLength,
+ // check we do not try to initialize more than this number of properties.
+ ASSERT(storage->m_initializationIndex < storage->m_numValuesInVector);
+ storage->m_initializationIndex++;
+#endif
+ ASSERT(i < storage->length());
+ ASSERT(i < storage->m_numValuesInVector);
+ storage->m_vector[i].set(globalData, this, v);
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ }
+
+ void completeInitialization(unsigned newLength)
+ {
+ switch (structure()->indexingType()) {
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+ // Check that we have initialized as meny properties as we think we have.
+ UNUSED_PARAM(storage);
+ ASSERT_UNUSED(newLength, newLength == storage->length());
+#if CHECK_ARRAY_CONSISTENCY
+ // Check that the number of propreties initialized matches the initialLength.
+ ASSERT(storage->m_initializationIndex == m_storage->m_numValuesInVector);
+ ASSERT(storage->m_inCompactInitialization);
+ storage->m_inCompactInitialization = false;
+#endif
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ }
+
+ bool inSparseIndexingMode()
+ {
+ switch (structure()->indexingType()) {
+ case NonArray:
+ case ArrayClass:
+ return false;
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ return m_butterfly->arrayStorage()->inSparseMode();
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ }
+
+ void enterDictionaryIndexingMode(JSGlobalData&);
// 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.
JS_EXPORT_PRIVATE static void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes);
void putDirect(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0);
void putDirect(JSGlobalData&, PropertyName, JSValue, PutPropertySlot&);
void putDirectWithoutTransition(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0);
- void putDirectAccessor(JSGlobalData&, PropertyName, JSValue, unsigned attributes);
+ void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes);
bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
@@ -147,6 +337,7 @@ namespace JSC {
JS_EXPORT_PRIVATE static bool hasInstance(JSObject*, 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);
JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
@@ -203,8 +394,11 @@ namespace JSC {
return inlineStorageUnsafe();
}
- ConstPropertyStorage outOfLineStorage() const { return m_outOfLineStorage.get(); }
- PropertyStorage outOfLineStorage() { return m_outOfLineStorage.get(); }
+ const Butterfly* butterfly() const { return m_butterfly; }
+ Butterfly* butterfly() { return m_butterfly; }
+
+ ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); }
+ PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); }
const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const
{
@@ -227,7 +421,7 @@ namespace JSC {
if (offsetInInlineStorage < static_cast<size_t>(inlineStorageCapacity))
result = offsetInInlineStorage;
else
- result = outOfLineStorage() - location + (inlineStorageCapacity - 2);
+ result = outOfLineStorage() - location + (inlineStorageCapacity - 1);
validateOffset(result, structure()->typeInfo().type());
return result;
}
@@ -265,21 +459,22 @@ namespace JSC {
bool isSealed(JSGlobalData& globalData) { return structure()->isSealed(globalData); }
bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); }
bool isExtensible() { return structure()->isExtensible(); }
+ bool indexingShouldBeSparse()
+ {
+ return !isExtensible()
+ || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero();
+ }
bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
void reifyStaticFunctionsForDelete(ExecState* exec);
- JS_EXPORT_PRIVATE PropertyStorage growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize);
- void setOutOfLineStorage(JSGlobalData&, PropertyStorage, Structure*);
+ JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize);
+ void setButterfly(JSGlobalData&, Butterfly*, Structure*);
+ void setButterflyWithoutChangingStructure(Butterfly*); // You probably don't want to call this.
void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*);
void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, Structure*);
- void* addressOfOutOfLineStorage()
- {
- return &m_outOfLineStorage;
- }
-
void flattenDictionaryObject(JSGlobalData& globalData)
{
structure()->flattenDictionaryStructure(globalData, this);
@@ -293,7 +488,16 @@ namespace JSC {
}
static size_t offsetOfInlineStorage();
- static size_t offsetOfOutOfLineStorage();
+
+ static ptrdiff_t butterflyOffset()
+ {
+ return OBJECT_OFFSETOF(JSObject, m_butterfly);
+ }
+
+ void* butterflyAddress()
+ {
+ return &m_butterfly;
+ }
static JS_EXPORTDATA const ClassInfo s_info;
@@ -316,14 +520,78 @@ namespace JSC {
// To instantiate objects you likely want JSFinalObject, below.
// To create derived types you likely want JSNonFinalObject, below.
- JSObject(JSGlobalData&, Structure*);
+ JSObject(JSGlobalData&, Structure*, Butterfly* = 0);
void resetInheritorID(JSGlobalData& globalData)
{
removeDirect(globalData, globalData.m_inheritorIDKey);
}
- void visitOutOfLineStorage(SlotVisitor&, PropertyStorage, size_t storageSize);
+ void visitButterfly(SlotVisitor&, 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(structure()->indexingType() | HasArrayStorage);
+ 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 (structure()->indexingType()) {
+ case ArrayWithArrayStorage:
+ case NonArrayWithArrayStorage:
+ return m_butterfly->arrayStorage();
+
+ default:
+ return 0;
+ }
+ }
+
+ // 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(JSGlobalData& globalData)
+ {
+ switch (structure()->indexingType()) {
+ case ArrayWithArrayStorage:
+ case NonArrayWithArrayStorage:
+ return m_butterfly->arrayStorage();
+
+ case NonArray:
+ case ArrayClass:
+ return createInitialArrayStorage(globalData);
+
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+ }
+
+ ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength);
+ ArrayStorage* createInitialArrayStorage(JSGlobalData&);
+
+ ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData&);
+
+ bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException);
+
+ enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck };
+#if !CHECK_ARRAY_CONSISTENCY
+ void checkIndexingConsistency(ConsistencyCheckType = NormalConsistencyCheck) { }
+#else
+ void checkIndexingConsistency(ConsistencyCheckType = NormalConsistencyCheck);
+#endif
+
+ void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*);
+
+ bool increaseVectorLength(JSGlobalData&, unsigned newLength);
+ void deallocateSparseIndexMap();
+ bool defineOwnIndexedProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException);
+ SparseArrayValueMap* allocateSparseIndexMap(JSGlobalData&);
private:
friend class LLIntOffsetsExtractor;
@@ -336,16 +604,30 @@ namespace JSC {
void isObject();
void isString();
+ ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData&, ArrayStorage*);
+
template<PutMode>
bool putDirectInternal(JSGlobalData&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*);
bool inlineGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&);
- JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location);
+ JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, PropertyOffset);
const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const;
Structure* createInheritorID(JSGlobalData&);
+
+ void putIndexedDescriptor(ExecState*, SparseArrayEntry*, 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);
- StorageBarrier m_outOfLineStorage;
+ JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&);
+
+ protected:
+ Butterfly* m_butterfly;
};
@@ -369,8 +651,8 @@ namespace JSC {
}
protected:
- explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
- : JSObject(globalData, structure)
+ explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly = 0)
+ : JSObject(globalData, structure, butterfly)
{
}
@@ -453,11 +735,6 @@ inline size_t JSObject::offsetOfInlineStorage()
return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
}
-inline size_t JSObject::offsetOfOutOfLineStorage()
-{
- return OBJECT_OFFSETOF(JSObject, m_outOfLineStorage);
-}
-
inline bool JSObject::isGlobalObject() const
{
return structure()->typeInfo().type() == GlobalObjectType;
@@ -488,18 +765,17 @@ inline bool JSObject::isGlobalThis() const
return structure()->typeInfo().type() == GlobalThisType;
}
-inline void JSObject::setOutOfLineStorage(JSGlobalData& globalData, PropertyStorage storage, Structure* structure)
+inline void JSObject::setButterfly(JSGlobalData& globalData, Butterfly* butterfly, Structure* structure)
{
ASSERT(structure);
- if (!storage) {
- ASSERT(!structure->outOfLineCapacity());
- ASSERT(!structure->outOfLineSize());
- } else {
- ASSERT(structure->outOfLineCapacity());
- ASSERT(structure->outOfLineSize());
- }
+ ASSERT(!butterfly == (!structure->outOfLineCapacity() && !hasIndexingHeader(structure->indexingType())));
setStructure(globalData, structure);
- m_outOfLineStorage.set(globalData, this, storage);
+ m_butterfly = butterfly;
+}
+
+inline void JSObject::setButterflyWithoutChangingStructure(Butterfly* butterfly)
+{
+ m_butterfly = butterfly;
}
inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
@@ -537,9 +813,9 @@ inline JSObject* asObject(JSValue value)
return asObject(value.asCell());
}
-inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure)
+inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly)
: JSCell(globalData, structure)
- , m_outOfLineStorage(globalData, this, 0)
+ , m_butterfly(butterfly)
{
}
@@ -587,15 +863,17 @@ inline JSObject* JSValue::toThisObject(ExecState* exec) const
ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
{
- if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) {
- if (structure()->hasGetterSetterProperties() && location->isGetterSetter())
- fillGetterPropertySlot(slot, location);
+ PropertyOffset offset = structure()->get(exec->globalData(), propertyName);
+ if (LIKELY(isValidOffset(offset))) {
+ JSValue value = getDirectOffset(offset);
+ if (structure()->hasGetterSetterProperties() && value.isGetterSetter())
+ fillGetterPropertySlot(slot, offset);
else
- slot.setValue(this, location->get(), offsetForLocation(location));
+ slot.setValue(this, value, offset);
return true;
}
- return false;
+ return getOwnPropertySlotSlow(exec, propertyName, slot);
}
// It may seem crazy to inline a function this large, especially a virtual function,
@@ -681,6 +959,7 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
ASSERT(value);
ASSERT(value.isGetterSetter() == !!(attributes & Accessor));
ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
+ ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex);
if (structure()->isDictionary()) {
unsigned currentAttributes;
@@ -709,11 +988,11 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
if ((mode == PutModePut) && !isExtensible())
return false;
- PropertyStorage newStorage = outOfLineStorage();
+ Butterfly* newButterfly = m_butterfly;
if (structure()->putWillGrowOutOfLineStorage())
- newStorage = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
+ newButterfly = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
- setOutOfLineStorage(globalData, newStorage, structure());
+ setButterfly(globalData, newButterfly, structure());
validateOffset(offset);
ASSERT(structure()->isValidOffset(offset));
@@ -727,13 +1006,13 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
PropertyOffset offset;
size_t currentCapacity = structure()->outOfLineCapacity();
if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) {
- PropertyStorage newStorage = outOfLineStorage();
+ Butterfly* newButterfly = m_butterfly;
if (currentCapacity != structure->outOfLineCapacity())
- newStorage = growOutOfLineStorage(globalData, currentCapacity, structure->outOfLineCapacity());
+ newButterfly = growOutOfLineStorage(globalData, currentCapacity, structure->outOfLineCapacity());
validateOffset(offset);
ASSERT(structure->isValidOffset(offset));
- setOutOfLineStorage(globalData, newStorage, structure);
+ setButterfly(globalData, newButterfly, structure);
putDirectOffset(globalData, offset, value);
// This is a new property; transitions with specific values are not currently cachable,
// so leave the slot in an uncachable state.
@@ -800,9 +1079,9 @@ inline void JSObject::setStructureAndReallocateStorageIfNecessary(JSGlobalData&
return;
}
- PropertyStorage newStorage = growOutOfLineStorage(
+ Butterfly* newButterfly = growOutOfLineStorage(
globalData, oldCapacity, newStructure->outOfLineCapacity());
- setOutOfLineStorage(globalData, newStorage, newStructure);
+ setButterfly(globalData, newButterfly, newStructure);
}
inline void JSObject::setStructureAndReallocateStorageIfNecessary(JSGlobalData& globalData, Structure* newStructure)
@@ -836,11 +1115,11 @@ inline void JSObject::putDirect(JSGlobalData& globalData, PropertyName propertyN
inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)
{
ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
- PropertyStorage newStorage = outOfLineStorage();
+ Butterfly* newButterfly = m_butterfly;
if (structure()->putWillGrowOutOfLineStorage())
- newStorage = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
+ newButterfly = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
PropertyOffset offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getCallableObject(value));
- setOutOfLineStorage(globalData, newStorage, structure());
+ setButterfly(globalData, newButterfly, structure());
putDirectOffset(globalData, offset, value);
}
@@ -932,6 +1211,11 @@ ALWAYS_INLINE Register Register::withCallee(JSObject* callee)
return r;
}
+inline size_t offsetInButterfly(PropertyOffset offset)
+{
+ return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
+}
+
// This is a helper 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.
@@ -939,14 +1223,14 @@ ALWAYS_INLINE Register Register::withCallee(JSObject* callee)
inline size_t offsetRelativeToPatchedStorage(PropertyOffset offset)
{
if (isOutOfLineOffset(offset))
- return sizeof(EncodedJSValue) * offsetInOutOfLineStorage(offset);
- return JSObject::offsetOfInlineStorage() - JSObject::offsetOfOutOfLineStorage() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset);
+ return sizeof(EncodedJSValue) * offsetInButterfly(offset);
+ return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset);
}
inline int indexRelativeToBase(PropertyOffset offset)
{
if (isOutOfLineOffset(offset))
- return offsetInOutOfLineStorage(offset);
+ return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue)));
return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset);
}
@@ -954,7 +1238,7 @@ inline int indexRelativeToBase(PropertyOffset offset)
inline int offsetRelativeToBase(PropertyOffset offset)
{
if (isOutOfLineOffset(offset))
- return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue);
+ return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage();
return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue);
}
diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
index b1376c5e8..897ceff8c 100644
--- a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
+++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
@@ -67,6 +67,9 @@ JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, JSObject
if (o->structure()->typeInfo().overridesGetPropertyNames())
return jsPropertyNameIterator;
+ if (hasIndexingHeader(o->structure()->indexingType()))
+ return jsPropertyNameIterator;
+
size_t count = normalizePrototypeChain(exec, o);
StructureChain* structureChain = o->structure()->prototypeChain(exec);
WriteBarrier<Structure>* structure = structureChain->head();
diff --git a/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp b/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp
index 72caa33db..765e1d3d4 100644
--- a/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp
@@ -56,7 +56,7 @@ bool JSSymbolTableObject::deleteProperty(JSCell* cell, ExecState* exec, Property
return JSObject::deleteProperty(thisObject, exec, propertyName);
}
-void JSSymbolTableObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+void JSSymbolTableObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
JSSymbolTableObject* thisObject = jsCast<JSSymbolTableObject*>(object);
SymbolTable::const_iterator end = thisObject->symbolTable()->end();
@@ -65,7 +65,7 @@ void JSSymbolTableObject::getOwnPropertyNames(JSObject* object, ExecState* exec,
propertyNames.add(Identifier(exec, it->first.get()));
}
- JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
+ JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
}
void JSSymbolTableObject::putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned)
diff --git a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h
index 1913d018b..b4d313c19 100644
--- a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h
+++ b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h
@@ -44,22 +44,23 @@ public:
static NO_RETURN_DUE_TO_ASSERT void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes);
JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
- JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+ JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
protected:
static const unsigned StructureFlags = IsEnvironmentRecord | OverridesVisitChildren | OverridesGetPropertyNames | Base::StructureFlags;
- JSSymbolTableObject(JSGlobalData& globalData, Structure* structure, JSScope* scope)
+ JSSymbolTableObject(JSGlobalData& globalData, Structure* structure, JSScope* scope, SharedSymbolTable* symbolTable = 0)
: Base(globalData, structure, scope)
{
+ if (symbolTable)
+ m_symbolTable.set(globalData, this, symbolTable);
}
- void finishCreation(JSGlobalData& globalData, SharedSymbolTable* symbolTable = 0)
+ void finishCreation(JSGlobalData& globalData)
{
Base::finishCreation(globalData);
- if (!symbolTable)
- symbolTable = SharedSymbolTable::create(globalData);
- m_symbolTable.set(globalData, this, symbolTable);
+ if (!m_symbolTable)
+ m_symbolTable.set(globalData, this, SharedSymbolTable::create(globalData));
}
static void visitChildren(JSCell*, SlotVisitor&);
diff --git a/Source/JavaScriptCore/runtime/JSTypeInfo.h b/Source/JavaScriptCore/runtime/JSTypeInfo.h
index 8c62fa166..d9b3585a0 100644
--- a/Source/JavaScriptCore/runtime/JSTypeInfo.h
+++ b/Source/JavaScriptCore/runtime/JSTypeInfo.h
@@ -42,9 +42,10 @@ namespace JSC {
static const unsigned ImplementsDefaultHasInstance = 1 << 3;
static const unsigned IsEnvironmentRecord = 1 << 4;
static const unsigned OverridesGetOwnPropertySlot = 1 << 5;
- static const unsigned OverridesVisitChildren = 1 << 6;
- static const unsigned OverridesGetPropertyNames = 1 << 7;
- static const unsigned ProhibitsPropertyCaching = 1 << 8;
+ static const unsigned InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero = 1 << 6;
+ static const unsigned OverridesVisitChildren = 1 << 7;
+ static const unsigned OverridesGetPropertyNames = 1 << 8;
+ static const unsigned ProhibitsPropertyCaching = 1 << 9;
class TypeInfo {
public:
@@ -75,8 +76,9 @@ namespace JSC {
bool overridesHasInstance() const { return isSetOnFlags1(OverridesHasInstance); }
bool implementsDefaultHasInstance() const { return isSetOnFlags1(ImplementsDefaultHasInstance); }
bool overridesGetOwnPropertySlot() const { return isSetOnFlags1(OverridesGetOwnPropertySlot); }
+ bool interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero() const { return isSetOnFlags1(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero); }
bool overridesVisitChildren() const { return isSetOnFlags1(OverridesVisitChildren); }
- bool overridesGetPropertyNames() const { return isSetOnFlags1(OverridesGetPropertyNames); }
+ bool overridesGetPropertyNames() const { return isSetOnFlags2(OverridesGetPropertyNames); }
bool prohibitsPropertyCaching() const { return isSetOnFlags2(ProhibitsPropertyCaching); }
static ptrdiff_t flagsOffset()
diff --git a/Source/JavaScriptCore/runtime/JSVariableObject.h b/Source/JavaScriptCore/runtime/JSVariableObject.h
index c9f989e68..55952820e 100644
--- a/Source/JavaScriptCore/runtime/JSVariableObject.h
+++ b/Source/JavaScriptCore/runtime/JSVariableObject.h
@@ -60,19 +60,14 @@ namespace JSC {
JSGlobalData& globalData,
Structure* structure,
Register* registers,
- JSScope* scope
+ JSScope* scope,
+ SharedSymbolTable* symbolTable = 0
)
- : Base(globalData, structure, scope)
+ : Base(globalData, structure, scope, symbolTable)
, m_registers(reinterpret_cast<WriteBarrierBase<Unknown>*>(registers))
{
}
- void finishCreation(JSGlobalData& globalData, SharedSymbolTable* symbolTable = 0)
- {
- Base::finishCreation(globalData, symbolTable);
- COMPILE_ASSERT(sizeof(WriteBarrierBase<Unknown>) == sizeof(Register), Register_should_be_same_size_as_WriteBarrierBase);
- }
-
WriteBarrierBase<Unknown>* m_registers; // "r" in the register file.
};
diff --git a/Source/JavaScriptCore/runtime/LiteralParser.cpp b/Source/JavaScriptCore/runtime/LiteralParser.cpp
index 30a478d48..732d818bd 100644
--- a/Source/JavaScriptCore/runtime/LiteralParser.cpp
+++ b/Source/JavaScriptCore/runtime/LiteralParser.cpp
@@ -27,6 +27,8 @@
#include "config.h"
#include "LiteralParser.h"
+#include "ButterflyInlineMethods.h"
+#include "CopiedSpaceInlineMethods.h"
#include "JSArray.h"
#include "JSString.h"
#include "Lexer.h"
@@ -640,7 +642,13 @@ JSValue LiteralParser<CharType>::parse(ParserState initialState)
}
case DoParseObjectEndExpression:
{
- asObject(objectStack.last())->putDirect(m_exec->globalData(), identifierStack.last(), lastValue);
+ JSObject* object = asObject(objectStack.last());
+ PropertyName ident = identifierStack.last();
+ unsigned i = ident.asIndex();
+ if (i != PropertyName::NotAnIndex)
+ object->putDirectIndex(m_exec, i, lastValue);
+ else
+ object->putDirect(m_exec->globalData(), ident, lastValue);
identifierStack.removeLast();
if (m_lexer.currentToken().type == TokComma)
goto doParseObjectStartExpression;
diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp
index ed0d0cfc7..5ac2d8788 100644
--- a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp
+++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp
@@ -21,6 +21,8 @@
#include "config.h"
#include "ObjectConstructor.h"
+#include "ButterflyInlineMethods.h"
+#include "CopiedSpaceInlineMethods.h"
#include "Error.h"
#include "ExceptionHelpers.h"
#include "JSFunction.h"
diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp
index 800909385..84c60a69c 100644
--- a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp
@@ -67,7 +67,6 @@ ASSERT_CLASS_FITS_IN_CELL(ObjectPrototype);
ObjectPrototype::ObjectPrototype(ExecState* exec, Structure* stucture)
: JSNonFinalObject(exec->globalData(), stucture)
- , m_hasNoPropertiesWithUInt32Names(true)
{
}
@@ -77,34 +76,6 @@ void ObjectPrototype::finishCreation(JSGlobalData& globalData, JSGlobalObject*)
ASSERT(inherits(&s_info));
}
-void ObjectPrototype::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
-{
- ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(cell);
- Base::put(cell, exec, propertyName, value, slot);
-
- if (thisObject->m_hasNoPropertiesWithUInt32Names && propertyName.asIndex() != PropertyName::NotAnIndex)
- thisObject->m_hasNoPropertiesWithUInt32Names = false;
-}
-
-bool ObjectPrototype::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
-{
- ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(object);
- bool result = Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
-
- if (thisObject->m_hasNoPropertiesWithUInt32Names && propertyName.asIndex() != PropertyName::NotAnIndex)
- thisObject->m_hasNoPropertiesWithUInt32Names = false;
-
- return result;
-}
-
-bool ObjectPrototype::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
-{
- ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(cell);
- if (thisObject->m_hasNoPropertiesWithUInt32Names)
- return false;
- return Base::getOwnPropertySlotByIndex(thisObject, exec, propertyName, slot);
-}
-
bool ObjectPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
{
return getStaticFunctionSlot<JSNonFinalObject>(exec, ExecState::objectPrototypeTable(exec), jsCast<ObjectPrototype*>(cell), propertyName, slot);
diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.h b/Source/JavaScriptCore/runtime/ObjectPrototype.h
index d46cb91c3..e3551d6fd 100644
--- a/Source/JavaScriptCore/runtime/ObjectPrototype.h
+++ b/Source/JavaScriptCore/runtime/ObjectPrototype.h
@@ -50,14 +50,8 @@ namespace JSC {
private:
ObjectPrototype(ExecState*, Structure*);
- static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
- static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
-
static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
- static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
-
- bool m_hasNoPropertiesWithUInt32Names;
};
JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState*);
diff --git a/Source/JavaScriptCore/runtime/PropertyOffset.h b/Source/JavaScriptCore/runtime/PropertyOffset.h
index aa82eb468..2aea2981e 100644
--- a/Source/JavaScriptCore/runtime/PropertyOffset.h
+++ b/Source/JavaScriptCore/runtime/PropertyOffset.h
@@ -118,7 +118,7 @@ inline size_t offsetInOutOfLineStorage(PropertyOffset offset)
{
validateOffset(offset);
ASSERT(isOutOfLineOffset(offset));
- return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 2;
+ return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 1;
}
inline size_t offsetInRespectiveStorage(PropertyOffset offset)
diff --git a/Source/JavaScriptCore/runtime/StorageBarrier.h b/Source/JavaScriptCore/runtime/PropertyStorage.h
index 1fae82091..3d7bb020c 100644
--- a/Source/JavaScriptCore/runtime/StorageBarrier.h
+++ b/Source/JavaScriptCore/runtime/PropertyStorage.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * 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
@@ -23,10 +23,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef StorageBarrier_h
-#define StorageBarrier_h
+#ifndef PropertyStorage_h
+#define PropertyStorage_h
-#include "JSGlobalData.h"
#include "WriteBarrier.h"
namespace JSC {
@@ -34,45 +33,7 @@ namespace JSC {
typedef WriteBarrierBase<Unknown>* PropertyStorage;
typedef const WriteBarrierBase<Unknown>* ConstPropertyStorage;
-class StorageBarrier {
-public:
- enum UncheckedTag { Unchecked };
- StorageBarrier(JSGlobalData& globalData, JSCell* owner, PropertyStorage storage)
- {
- set(globalData, owner, storage);
- }
-
- StorageBarrier(PropertyStorage storage, UncheckedTag)
- {
- set(storage, Unchecked);
- }
-
- void set(JSGlobalData&, JSCell*, PropertyStorage newStorage)
- {
- m_storage = newStorage;
- }
-
- void set(PropertyStorage newStorage, UncheckedTag)
- {
- m_storage = newStorage;
- }
-
- WriteBarrierBase<Unknown>* operator->() const { return m_storage; }
- WriteBarrierBase<Unknown>* operator->() { return m_storage; }
- WriteBarrierBase<Unknown> operator*() const { return *m_storage; }
- WriteBarrierBase<Unknown> operator*() { return *m_storage; }
- const WriteBarrierBase<Unknown>& operator[](size_t i) const { return m_storage[i]; }
- WriteBarrierBase<Unknown>& operator[](size_t i) { return m_storage[i]; }
-
- ConstPropertyStorage get() const { return m_storage; }
- PropertyStorage get() { return m_storage; }
+} // namespace JSC
- bool operator!() { return !m_storage; }
-
-private:
- PropertyStorage m_storage;
-};
+#endif // PropertyStorage_h
-}
-
-#endif
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/RegExpMatchesArray.cpp b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp
index b2c3027b5..ed8aace66 100644
--- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp
+++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp
@@ -26,15 +26,37 @@
#include "config.h"
#include "RegExpMatchesArray.h"
+#include "ButterflyInlineMethods.h"
+#include "SparseArrayValueMapInlineMethods.h"
+
namespace JSC {
ASSERT_CLASS_FITS_IN_CELL(RegExpMatchesArray);
const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, CREATE_METHOD_TABLE(RegExpMatchesArray)};
+RegExpMatchesArray::RegExpMatchesArray(JSGlobalData& globalData, Butterfly* butterfly, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result)
+ : JSArray(globalData, globalObject->regExpMatchesArrayStructure(), butterfly)
+ , m_result(result)
+ , m_state(ReifiedNone)
+{
+ m_input.set(globalData, this, input);
+ m_regExp.set(globalData, this, regExp);
+}
+
+RegExpMatchesArray* RegExpMatchesArray::create(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result)
+{
+ ASSERT(result);
+ JSGlobalData& globalData = exec->globalData();
+ Butterfly* butterfly = createArrayButterfly(globalData, regExp->numSubpatterns() + 1);
+ RegExpMatchesArray* array = new (NotNull, allocateCell<RegExpMatchesArray>(globalData.heap)) RegExpMatchesArray(globalData, butterfly, exec->lexicalGlobalObject(), input, regExp, result);
+ array->finishCreation(globalData);
+ return array;
+}
+
void RegExpMatchesArray::finishCreation(JSGlobalData& globalData)
{
- Base::finishCreation(globalData, m_regExp->numSubpatterns() + 1);
+ Base::finishCreation(globalData);
}
void RegExpMatchesArray::visitChildren(JSCell* cell, SlotVisitor& visitor)
diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
index f26411f5f..a5b860b9d 100644
--- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
+++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
@@ -28,28 +28,14 @@ namespace JSC {
class RegExpMatchesArray : public JSArray {
private:
- RegExpMatchesArray(JSGlobalData& globalData, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result)
- : JSArray(globalData, globalObject->regExpMatchesArrayStructure())
- , m_result(result)
- , m_state(ReifiedNone)
- {
- m_input.set(globalData, this, input);
- m_regExp.set(globalData, this, regExp);
- }
+ RegExpMatchesArray(JSGlobalData&, Butterfly*, JSGlobalObject*, JSString*, RegExp*, MatchResult);
enum ReifiedState { ReifiedNone, ReifiedMatch, ReifiedAll };
public:
typedef JSArray Base;
- static RegExpMatchesArray* create(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result)
- {
- ASSERT(result);
- JSGlobalData& globalData = exec->globalData();
- RegExpMatchesArray* array = new (NotNull, allocateCell<RegExpMatchesArray>(globalData.heap)) RegExpMatchesArray(globalData, exec->lexicalGlobalObject(), input, regExp, result);
- array->finishCreation(globalData);
- return array;
- }
+ static RegExpMatchesArray* create(ExecState*, JSString*, RegExp*, MatchResult);
JSString* leftContext(ExecState*);
JSString* rightContext(ExecState*);
@@ -58,7 +44,7 @@ namespace JSC {
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
{
- return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
+ return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage);
}
static void visitChildren(JSCell*, SlotVisitor&);
diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp
index b346c7769..bed44f22c 100644
--- a/Source/JavaScriptCore/runtime/RegExpObject.cpp
+++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp
@@ -21,6 +21,8 @@
#include "config.h"
#include "RegExpObject.h"
+#include "ButterflyInlineMethods.h"
+#include "CopiedSpaceInlineMethods.h"
#include "Error.h"
#include "ExceptionHelpers.h"
#include "JSArray.h"
@@ -113,11 +115,11 @@ bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName pr
return Base::deleteProperty(cell, exec, propertyName);
}
-void RegExpObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
if (mode == IncludeDontEnumProperties)
propertyNames.add(exec->propertyNames().lastIndex);
- Base::getOwnPropertyNames(object, exec, propertyNames, mode);
+ Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
}
void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
diff --git a/Source/JavaScriptCore/runtime/RegExpObject.h b/Source/JavaScriptCore/runtime/RegExpObject.h
index d1df2b202..f43d6bc79 100644
--- a/Source/JavaScriptCore/runtime/RegExpObject.h
+++ b/Source/JavaScriptCore/runtime/RegExpObject.h
@@ -90,7 +90,7 @@ namespace JSC {
static void visitChildren(JSCell*, SlotVisitor&);
JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
- 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 bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
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/SparseArrayValueMap.cpp b/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp
new file mode 100644
index 000000000..40c4ed26e
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "SparseArrayValueMapInlineMethods.h"
+
+namespace JSC {
+
+const ClassInfo SparseArrayValueMap::s_info = { "SparseArrayValueMap", 0, 0, 0, CREATE_METHOD_TABLE(SparseArrayValueMap) };
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/runtime/SparseArrayValueMap.h b/Source/JavaScriptCore/runtime/SparseArrayValueMap.h
new file mode 100644
index 000000000..aafdf974f
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/SparseArrayValueMap.h
@@ -0,0 +1,134 @@
+/*
+ * 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>
+#include <wtf/Platform.h>
+
+namespace JSC {
+
+struct SparseArrayEntry : public WriteBarrier<Unknown> {
+ typedef WriteBarrier<Unknown> Base;
+
+ SparseArrayEntry() : attributes(0) { }
+
+ JSValue get(ExecState*, JSObject*) const;
+ void get(PropertySlot&) const;
+ void get(PropertyDescriptor&) const;
+ JSValue getNonSparseMode() const;
+
+ unsigned attributes;
+};
+
+class SparseArrayValueMap : public JSCell {
+public:
+ typedef JSCell Base;
+
+private:
+ typedef HashMap<uint64_t, SparseArrayEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > Map;
+
+ enum Flags {
+ Normal = 0,
+ SparseMode = 1,
+ LengthIsReadOnly = 2,
+ };
+
+ SparseArrayValueMap(JSGlobalData&);
+ ~SparseArrayValueMap();
+
+ void finishCreation(JSGlobalData&);
+
+ static const unsigned StructureFlags = OverridesVisitChildren | JSCell::StructureFlags;
+
+public:
+ static JS_EXPORTDATA const ClassInfo s_info;
+
+ typedef Map::iterator iterator;
+ typedef Map::const_iterator const_iterator;
+ typedef Map::AddResult AddResult;
+
+ static SparseArrayValueMap* create(JSGlobalData&);
+
+ static void destroy(JSCell*);
+
+ static Structure* createStructure(JSGlobalData&, 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/SparseArrayValueMapInlineMethods.h b/Source/JavaScriptCore/runtime/SparseArrayValueMapInlineMethods.h
new file mode 100644
index 000000000..f3ef32f46
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/SparseArrayValueMapInlineMethods.h
@@ -0,0 +1,203 @@
+/*
+ * 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 SparseArrayValueMapInlineMethods_h
+#define SparseArrayValueMapInlineMethods_h
+
+#include "GetterSetter.h"
+#include "Reject.h"
+#include "SparseArrayValueMap.h"
+
+namespace JSC {
+
+inline SparseArrayValueMap::SparseArrayValueMap(JSGlobalData& globalData)
+ : Base(globalData, globalData.sparseArrayValueMapStructure.get())
+ , m_flags(Normal)
+ , m_reportedCapacity(0)
+{
+}
+
+inline SparseArrayValueMap::~SparseArrayValueMap()
+{
+}
+
+inline void SparseArrayValueMap::finishCreation(JSGlobalData& globalData)
+{
+ Base::finishCreation(globalData);
+}
+
+inline SparseArrayValueMap* SparseArrayValueMap::create(JSGlobalData& globalData)
+{
+ SparseArrayValueMap* result = new (NotNull, allocateCell<SparseArrayValueMap>(globalData.heap)) SparseArrayValueMap(globalData);
+ result->finishCreation(globalData);
+ return result;
+}
+
+inline void SparseArrayValueMap::destroy(JSCell* cell)
+{
+ static_cast<SparseArrayValueMap*>(cell)->SparseArrayValueMap::~SparseArrayValueMap();
+}
+
+inline Structure* SparseArrayValueMap::createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
+{
+ return Structure::create(globalData, globalObject, prototype, TypeInfo(CompoundType, StructureFlags), &s_info);
+}
+
+inline 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) {
+ Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>)));
+ m_reportedCapacity = capacity;
+ }
+ return result;
+}
+
+inline void SparseArrayValueMap::putEntry(ExecState* exec, JSObject* array, unsigned i, JSValue value, bool shouldThrow)
+{
+ AddResult result = add(array, i);
+ SparseArrayEntry& entry = result.iterator->second;
+
+ // 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;
+ }
+
+ if (!(entry.attributes & Accessor)) {
+ if (entry.attributes & ReadOnly) {
+ if (shouldThrow)
+ throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+ return;
+ }
+
+ entry.set(exec->globalData(), this, value);
+ return;
+ }
+
+ JSValue accessor = entry.SparseArrayEntry::Base::get();
+ ASSERT(accessor.isGetterSetter());
+ JSObject* setter = asGetterSetter(accessor)->setter();
+
+ if (!setter) {
+ if (shouldThrow)
+ throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+ return;
+ }
+
+ CallData callData;
+ CallType callType = setter->methodTable()->getCallData(setter, callData);
+ MarkedArgumentBuffer args;
+ args.append(value);
+ call(exec, setter, callType, callData, array, args);
+}
+
+inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSObject* array, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
+{
+ AddResult result = add(array, i);
+ SparseArrayEntry& entry = result.iterator->second;
+
+ // 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->globalData(), this, value);
+ return true;
+}
+
+inline void SparseArrayEntry::get(PropertySlot& slot) const
+{
+ JSValue value = Base::get();
+ ASSERT(value);
+
+ if (LIKELY(!value.isGetterSetter())) {
+ slot.setValue(value);
+ return;
+ }
+
+ JSObject* getter = asGetterSetter(value)->getter();
+ if (!getter) {
+ slot.setUndefined();
+ return;
+ }
+
+ slot.setGetterSlot(getter);
+}
+
+inline void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
+{
+ descriptor.setDescriptor(Base::get(), attributes);
+}
+
+inline JSValue SparseArrayEntry::get(ExecState* exec, JSObject* array) const
+{
+ JSValue result = Base::get();
+ ASSERT(result);
+
+ if (LIKELY(!result.isGetterSetter()))
+ return result;
+
+ JSObject* getter = asGetterSetter(result)->getter();
+ if (!getter)
+ return jsUndefined();
+
+ CallData callData;
+ CallType callType = getter->methodTable()->getCallData(getter, callData);
+ return call(exec, getter, callType, callData, array, exec->emptyList());
+}
+
+inline JSValue SparseArrayEntry::getNonSparseMode() const
+{
+ ASSERT(!attributes);
+ return Base::get();
+}
+
+inline 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->second);
+}
+
+} // namespace JSC
+
+#endif // SparseArrayValueMapInlineMethods_h
+
diff --git a/Source/JavaScriptCore/runtime/StringObject.cpp b/Source/JavaScriptCore/runtime/StringObject.cpp
index 113dee165..15900913d 100644
--- a/Source/JavaScriptCore/runtime/StringObject.cpp
+++ b/Source/JavaScriptCore/runtime/StringObject.cpp
@@ -78,6 +78,17 @@ void StringObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName,
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, PropertyDescriptor& descriptor, bool throwException)
{
StringObject* thisObject = jsCast<StringObject*>(object);
@@ -133,6 +144,14 @@ bool StringObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName pr
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);
diff --git a/Source/JavaScriptCore/runtime/StringObject.h b/Source/JavaScriptCore/runtime/StringObject.h
index f0c445e91..4369eace1 100644
--- a/Source/JavaScriptCore/runtime/StringObject.h
+++ b/Source/JavaScriptCore/runtime/StringObject.h
@@ -50,8 +50,10 @@ namespace JSC {
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
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);
static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
@@ -66,7 +68,7 @@ namespace JSC {
protected:
JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&, JSString*);
- static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSWrapperObject::StructureFlags;
+ static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | JSWrapperObject::StructureFlags;
JS_EXPORT_PRIVATE StringObject(JSGlobalData&, Structure*);
};
diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp
index 73633a60b..8daa0f335 100644
--- a/Source/JavaScriptCore/runtime/StringPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp
@@ -22,7 +22,9 @@
#include "config.h"
#include "StringPrototype.h"
+#include "ButterflyInlineMethods.h"
#include "CachedCall.h"
+#include "CopiedSpaceInlineMethods.h"
#include "Error.h"
#include "Executable.h"
#include "JSGlobalObjectFunctions.h"
diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp
index 4c8ee8741..c99c6dda4 100644
--- a/Source/JavaScriptCore/runtime/Structure.cpp
+++ b/Source/JavaScriptCore/runtime/Structure.cpp
@@ -149,9 +149,10 @@ void Structure::dumpStatistics()
#endif
}
-Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo)
+Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType)
: JSCell(globalData, globalData.structureStructure.get())
, m_typeInfo(typeInfo)
+ , m_indexingType(indexingType)
, m_globalObject(globalData, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull)
, m_prototype(globalData, this, prototype)
, m_classInfo(classInfo)
@@ -176,6 +177,7 @@ const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(
Structure::Structure(JSGlobalData& globalData)
: JSCell(CreatingEarlyCell)
, m_typeInfo(CompoundType, OverridesVisitChildren)
+ , m_indexingType(0)
, m_prototype(globalData, this, jsNull())
, m_classInfo(&s_info)
, m_transitionWatchpointSet(InitializedWatching)
@@ -197,6 +199,7 @@ Structure::Structure(JSGlobalData& globalData)
Structure::Structure(JSGlobalData& globalData, const Structure* previous)
: JSCell(globalData, globalData.structureStructure.get())
, m_typeInfo(previous->typeInfo())
+ , m_indexingType(previous->indexingTypeIncludingHistory())
, m_prototype(globalData, this, previous->storedPrototype())
, m_classInfo(previous->m_classInfo)
, m_transitionWatchpointSet(InitializedWatching)
@@ -251,6 +254,8 @@ void Structure::materializePropertyMap(JSGlobalData& globalData)
for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
structure = structures[i];
+ if (!structure->m_nameInPrevious)
+ continue;
PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get());
m_propertyTable->add(entry);
}
@@ -502,6 +507,38 @@ Structure* Structure::preventExtensionsTransition(JSGlobalData& globalData, Stru
return transition;
}
+Structure* Structure::nonPropertyTransition(JSGlobalData& globalData, Structure* structure, NonPropertyTransition transitionKind)
+{
+ unsigned attributes = toAttributes(transitionKind);
+ IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind);
+
+ if (Structure* existingTransition = structure->m_transitionTable.get(0, attributes)) {
+ ASSERT(existingTransition->m_attributesInPrevious == attributes);
+ ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType);
+ return existingTransition;
+ }
+
+ Structure* transition = create(globalData, structure);
+ transition->m_previous.set(globalData, transition, structure);
+ transition->m_attributesInPrevious = attributes;
+ transition->m_indexingType = indexingType;
+
+ if (structure->m_propertyTable) {
+ if (structure->m_isPinnedPropertyTable)
+ transition->m_propertyTable = structure->m_propertyTable->copy(globalData, transition, structure->m_propertyTable->size() + 1);
+ else
+ transition->m_propertyTable = structure->m_propertyTable.release();
+ } else {
+ if (structure->m_previous)
+ transition->materializePropertyMap(globalData);
+ else
+ transition->createPropertyMap();
+ }
+
+ structure->m_transitionTable.add(globalData, transition);
+ return transition;
+}
+
// In future we may want to cache this property.
bool Structure::isSealed(JSGlobalData& globalData)
{
diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h
index 73ec0789e..9303a0dbb 100644
--- a/Source/JavaScriptCore/runtime/Structure.h
+++ b/Source/JavaScriptCore/runtime/Structure.h
@@ -27,6 +27,7 @@
#define Structure_h
#include "ClassInfo.h"
+#include "IndexingType.h"
#include "JSCell.h"
#include "JSType.h"
#include "JSValue.h"
@@ -68,7 +69,7 @@ namespace JSC {
typedef JSCell Base;
- static Structure* create(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*);
+ static Structure* create(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = 0);
protected:
void finishCreation(JSGlobalData& globalData)
@@ -100,6 +101,7 @@ namespace JSC {
static Structure* sealTransition(JSGlobalData&, Structure*);
static Structure* freezeTransition(JSGlobalData&, Structure*);
static Structure* preventExtensionsTransition(JSGlobalData&, Structure*);
+ static Structure* nonPropertyTransition(JSGlobalData&, Structure*, NonPropertyTransition);
bool isSealed(JSGlobalData&);
bool isFrozen(JSGlobalData&);
@@ -140,6 +142,8 @@ namespace JSC {
const TypeInfo& typeInfo() const { ASSERT(structure()->classInfo() == &s_info); return m_typeInfo; }
bool isObject() const { return typeInfo().isObject(); }
+ IndexingType indexingType() const { return m_indexingType & AllArrayTypes; }
+ IndexingType indexingTypeIncludingHistory() const { return m_indexingType; }
JSGlobalObject* globalObject() const { return m_globalObject.get(); }
void setGlobalObject(JSGlobalData& globalData, JSGlobalObject* globalObject) { m_globalObject.set(globalData, this, globalObject); }
@@ -334,6 +338,11 @@ namespace JSC {
{
return OBJECT_OFFSETOF(Structure, m_classInfo);
}
+
+ static ptrdiff_t indexingTypeOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_indexingType);
+ }
static Structure* createStructure(JSGlobalData&);
@@ -363,7 +372,7 @@ namespace JSC {
private:
friend class LLIntOffsetsExtractor;
- JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*);
+ JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = 0);
Structure(JSGlobalData&);
Structure(JSGlobalData&, const Structure*);
@@ -416,6 +425,7 @@ namespace JSC {
static const unsigned maxSpecificFunctionThrashCount = 3;
TypeInfo m_typeInfo;
+ IndexingType m_indexingType;
WriteBarrier<JSGlobalObject> m_globalObject;
WriteBarrier<Unknown> m_prototype;
@@ -447,7 +457,7 @@ namespace JSC {
bool m_hasGetterSetterProperties : 1;
bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1;
bool m_hasNonEnumerableProperties : 1;
- unsigned m_attributesInPrevious : 7;
+ unsigned m_attributesInPrevious : 22;
unsigned m_specificFunctionThrashCount : 2;
unsigned m_preventExtensions : 1;
unsigned m_didTransition : 1;
@@ -465,11 +475,11 @@ namespace JSC {
return result;
}
- inline Structure* Structure::create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo)
+ inline Structure* Structure::create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType)
{
ASSERT(globalData.structureStructure);
ASSERT(classInfo);
- Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo);
+ Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo, indexingType);
structure->finishCreation(globalData);
return structure;
}
diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h
index 7c9d50894..59e7e94f3 100644
--- a/Source/JavaScriptCore/runtime/StructureTransitionTable.h
+++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h
@@ -26,6 +26,7 @@
#ifndef StructureTransitionTable_h
#define StructureTransitionTable_h
+#include "IndexingType.h"
#include "WeakGCMap.h"
#include <wtf/HashFunctions.h>
#include <wtf/OwnPtr.h>
@@ -37,6 +38,30 @@ 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 {
+ AllocateArrayStorage
+};
+
+inline unsigned toAttributes(NonPropertyTransition transition)
+{
+ return transition + FirstInternalAttribute;
+}
+
+inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition)
+{
+ switch (transition) {
+ case AllocateArrayStorage:
+ return oldType | HasArrayStorage;
+ default:
+ ASSERT_NOT_REACHED();
+ return oldType;
+ }
+}
+
class StructureTransitionTable {
static const intptr_t UsingSingleSlotFlag = 1;
@@ -44,7 +69,10 @@ class StructureTransitionTable {
typedef std::pair<RefPtr<StringImpl>, unsigned> Key;
static unsigned hash(const Key& p)
{
- return p.first->existingHash();
+ unsigned result = p.second;
+ if (p.first)
+ result += p.first->existingHash();
+ return result;
}
static bool equal(const Key& a, const Key& b)
diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h
index 08ea8c163..f6f70c6b9 100644
--- a/Source/JavaScriptCore/runtime/SymbolTable.h
+++ b/Source/JavaScriptCore/runtime/SymbolTable.h
@@ -340,13 +340,45 @@ namespace JSC {
return Structure::create(globalData, globalObject, prototype, TypeInfo(LeafType, StructureFlags), &s_info);
}
+ bool usesNonStrictEval() { return m_usesNonStrictEval; }
+ void setUsesNonStrictEval(bool usesNonStrictEval) { m_usesNonStrictEval = usesNonStrictEval; }
+
+ enum CaptureMode {
+ SomeOfTheThings,
+ AllOfTheThings
+ };
+
+ CaptureMode captureMode() { return m_captureMode; }
+ void setCaptureMode(CaptureMode captureMode) { m_captureMode = captureMode; }
+
+ int captureStart() { return m_captureStart; }
+ void setCaptureStart(int captureStart) { m_captureStart = captureStart; }
+
+ int captureEnd() { return m_captureEnd; }
+ void setCaptureEnd(int captureEnd) { m_captureEnd = captureEnd; }
+
+ int parameterCountIncludingThis() { return m_parameterCountIncludingThis; }
+ void setParameterCountIncludingThis(int parameterCountIncludingThis) { m_parameterCountIncludingThis = parameterCountIncludingThis; }
+
static JS_EXPORTDATA const ClassInfo s_info;
private:
SharedSymbolTable(JSGlobalData& globalData)
: JSCell(globalData, globalData.sharedSymbolTableStructure.get())
+ , m_parameterCountIncludingThis(0)
+ , m_usesNonStrictEval(false)
+ , m_captureMode(SomeOfTheThings)
+ , m_captureStart(0)
+ , m_captureEnd(0)
{
}
+
+ int m_parameterCountIncludingThis;
+ bool m_usesNonStrictEval;
+
+ CaptureMode m_captureMode;
+ int m_captureStart;
+ int m_captureEnd;
};
} // namespace JSC