summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/runtime')
-rw-r--r--Source/JavaScriptCore/runtime/ArrayPrototype.cpp6
-rw-r--r--Source/JavaScriptCore/runtime/CommonSlowPaths.h204
-rw-r--r--Source/JavaScriptCore/runtime/Executable.cpp4
-rw-r--r--Source/JavaScriptCore/runtime/JSArray.cpp580
-rw-r--r--Source/JavaScriptCore/runtime/JSArray.h52
-rw-r--r--Source/JavaScriptCore/runtime/JSByteArray.cpp2
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalData.h3
-rw-r--r--Source/JavaScriptCore/runtime/JSObject.cpp4
-rw-r--r--Source/JavaScriptCore/runtime/JSString.h2
-rw-r--r--Source/JavaScriptCore/runtime/PropertyDescriptor.cpp36
-rw-r--r--Source/JavaScriptCore/runtime/PropertyDescriptor.h5
11 files changed, 810 insertions, 88 deletions
diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
index d972693dd..dcf7a2fae 100644
--- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
@@ -380,7 +380,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
curArg = (exec->argument(i));
++i;
}
- arr->setLength(n);
+ arr->setLength(exec, n);
return JSValue::encode(arr);
}
@@ -389,7 +389,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
JSValue thisValue = exec->hostThisValue();
if (isJSArray(thisValue))
- return JSValue::encode(asArray(thisValue)->pop());
+ return JSValue::encode(asArray(thisValue)->pop(exec));
JSObject* thisObj = thisValue.toObject(exec);
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
@@ -523,7 +523,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
if (v)
resObj->methodTable()->putByIndex(resObj, exec, n, v);
}
- resObj->setLength(n);
+ resObj->setLength(exec, n);
return JSValue::encode(result);
}
diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h
new file mode 100644
index 000000000..ab4de7da8
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h
@@ -0,0 +1,204 @@
+/*
+ * 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 CommonSlowPaths_h
+#define CommonSlowPaths_h
+
+#include "CodeBlock.h"
+#include "ExceptionHelpers.h"
+#include "JSArray.h"
+
+namespace JSC {
+
+// The purpose of this namespace is to include slow paths that are shared
+// between the interpreter and baseline JIT. They are written to be agnostic
+// with respect to the slow-path calling convention, but they do rely on the
+// JS code being executed more-or-less directly from bytecode (so the call
+// frame layout is unmodified, making it potentially awkward to use these
+// from any optimizing JIT, like the DFG).
+
+namespace CommonSlowPaths {
+
+ALWAYS_INLINE bool opInstanceOfSlow(ExecState* exec, JSValue value, JSValue baseVal, JSValue proto)
+{
+ ASSERT(!value.isCell() || !baseVal.isCell() || !proto.isCell()
+ || !value.isObject() || !baseVal.isObject() || !proto.isObject()
+ || !asObject(baseVal)->structure()->typeInfo().implementsDefaultHasInstance());
+
+
+ // ECMA-262 15.3.5.3:
+ // Throw an exception either if baseVal is not an object, or if it does not implement 'HasInstance' (i.e. is a function).
+ TypeInfo typeInfo(UnspecifiedType);
+ if (!baseVal.isObject() || !(typeInfo = asObject(baseVal)->structure()->typeInfo()).implementsHasInstance()) {
+ exec->globalData().exception = createInvalidParamError(exec, "instanceof", baseVal);
+ return false;
+ }
+ ASSERT(typeInfo.type() != UnspecifiedType);
+
+ if (!typeInfo.overridesHasInstance() && !value.isObject())
+ return false;
+
+ return asObject(baseVal)->methodTable()->hasInstance(asObject(baseVal), exec, value, proto);
+}
+
+inline bool opIn(ExecState* exec, JSValue propName, JSValue baseVal)
+{
+ if (!baseVal.isObject()) {
+ exec->globalData().exception = createInvalidParamError(exec, "in", baseVal);
+ return false;
+ }
+
+ JSObject* baseObj = asObject(baseVal);
+
+ uint32_t i;
+ if (propName.getUInt32(i))
+ return baseObj->hasProperty(exec, i);
+
+ Identifier property(exec, propName.toString(exec));
+ if (exec->globalData().exception)
+ return false;
+ return baseObj->hasProperty(exec, property);
+}
+
+ALWAYS_INLINE JSValue opResolve(ExecState* exec, Identifier& ident)
+{
+ ScopeChainNode* scopeChain = exec->scopeChain();
+
+ ScopeChainIterator iter = scopeChain->begin();
+ ScopeChainIterator end = scopeChain->end();
+ ASSERT(iter != end);
+
+ do {
+ JSObject* o = iter->get();
+ PropertySlot slot(o);
+ if (o->getPropertySlot(exec, ident, slot))
+ return slot.getValue(exec, ident);
+ } while (++iter != end);
+
+ exec->globalData().exception = createUndefinedVariableError(exec, ident);
+ return JSValue();
+}
+
+ALWAYS_INLINE JSValue opResolveSkip(ExecState* exec, Identifier& ident, int skip)
+{
+ ScopeChainNode* scopeChain = exec->scopeChain();
+
+ ScopeChainIterator iter = scopeChain->begin();
+ ScopeChainIterator end = scopeChain->end();
+ ASSERT(iter != end);
+ CodeBlock* codeBlock = exec->codeBlock();
+ bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain();
+ ASSERT(skip || !checkTopLevel);
+ if (checkTopLevel && skip--) {
+ if (exec->uncheckedR(codeBlock->activationRegister()).jsValue())
+ ++iter;
+ }
+ while (skip--) {
+ ++iter;
+ ASSERT(iter != end);
+ }
+ do {
+ JSObject* o = iter->get();
+ PropertySlot slot(o);
+ if (o->getPropertySlot(exec, ident, slot))
+ return slot.getValue(exec, ident);
+ } while (++iter != end);
+
+ exec->globalData().exception = createUndefinedVariableError(exec, ident);
+ return JSValue();
+}
+
+ALWAYS_INLINE JSValue opResolveWithBase(ExecState* exec, Identifier& ident, Register& baseSlot)
+{
+ ScopeChainNode* scopeChain = exec->scopeChain();
+
+ ScopeChainIterator iter = scopeChain->begin();
+ ScopeChainIterator end = scopeChain->end();
+
+ // FIXME: add scopeDepthIsZero optimization
+
+ ASSERT(iter != end);
+
+ JSObject* base;
+ do {
+ base = iter->get();
+ PropertySlot slot(base);
+ if (base->getPropertySlot(exec, ident, slot)) {
+ JSValue result = slot.getValue(exec, ident);
+ if (exec->globalData().exception)
+ return JSValue();
+
+ baseSlot = JSValue(base);
+ return result;
+ }
+ ++iter;
+ } while (iter != end);
+
+ exec->globalData().exception = createUndefinedVariableError(exec, ident);
+ return JSValue();
+}
+
+ALWAYS_INLINE JSValue opResolveWithThis(ExecState* exec, Identifier& ident, Register& baseSlot)
+{
+ ScopeChainNode* scopeChain = exec->scopeChain();
+
+ ScopeChainIterator iter = scopeChain->begin();
+ ScopeChainIterator end = scopeChain->end();
+
+ // FIXME: add scopeDepthIsZero optimization
+
+ ASSERT(iter != end);
+
+ JSObject* base;
+ do {
+ base = iter->get();
+ ++iter;
+ PropertySlot slot(base);
+ if (base->getPropertySlot(exec, ident, slot)) {
+ JSValue result = slot.getValue(exec, ident);
+ if (exec->globalData().exception)
+ return JSValue();
+
+ // All entries on the scope chain should be EnvironmentRecords (activations etc),
+ // other then 'with' object, which are directly referenced from the scope chain,
+ // and the global object. If we hit either an EnvironmentRecord or a global
+ // object at the end of the scope chain, this is undefined. If we hit a non-
+ // EnvironmentRecord within the scope chain, pass the base as the this value.
+ if (iter == end || base->structure()->typeInfo().isEnvironmentRecord())
+ baseSlot = jsUndefined();
+ else
+ baseSlot = JSValue(base);
+ return result;
+ }
+ } while (iter != end);
+
+ exec->globalData().exception = createUndefinedVariableError(exec, ident);
+ return JSValue();
+}
+
+} } // namespace JSC::CommonSlowPaths
+
+#endif // CommonSlowPaths_h
+
diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp
index ad86463db..a364e84da 100644
--- a/Source/JavaScriptCore/runtime/Executable.cpp
+++ b/Source/JavaScriptCore/runtime/Executable.cpp
@@ -534,7 +534,7 @@ JSObject* FunctionExecutable::compileForCallInternal(ExecState* exec, ScopeChain
newCodeBlock->setAlternative(static_pointer_cast<CodeBlock>(m_codeBlockForCall.release()));
m_codeBlockForCall = newCodeBlock.release();
- m_numParametersForCall = m_codeBlockForCall->m_numParameters;
+ m_numParametersForCall = m_codeBlockForCall->numParameters();
ASSERT(m_numParametersForCall);
m_numCapturedVariables = m_codeBlockForCall->m_numCapturedVars;
m_symbolTable = m_codeBlockForCall->sharedSymbolTable();
@@ -597,7 +597,7 @@ JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, Scope
newCodeBlock->setAlternative(static_pointer_cast<CodeBlock>(m_codeBlockForConstruct.release()));
m_codeBlockForConstruct = newCodeBlock.release();
- m_numParametersForConstruct = m_codeBlockForConstruct->m_numParameters;
+ m_numParametersForConstruct = m_codeBlockForConstruct->numParameters();
ASSERT(m_numParametersForConstruct);
m_numCapturedVariables = m_codeBlockForConstruct->m_numCapturedVars;
m_symbolTable = m_codeBlockForConstruct->sharedSymbolTable();
diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp
index da7be8564..b3210083d 100644
--- a/Source/JavaScriptCore/runtime/JSArray.cpp
+++ b/Source/JavaScriptCore/runtime/JSArray.cpp
@@ -27,6 +27,7 @@
#include "CachedCall.h"
#include "Error.h"
#include "Executable.h"
+#include "GetterSetter.h"
#include "PropertyNameArray.h"
#include <wtf/AVLTree.h>
#include <wtf/Assertions.h>
@@ -212,24 +213,94 @@ void JSArray::destroy(JSCell* cell)
jsCast<JSArray*>(cell)->JSArray::~JSArray();
}
-SparseArrayValueMap::iterator SparseArrayValueMap::find(unsigned i)
+inline std::pair<SparseArrayValueMap::iterator, bool> SparseArrayValueMap::add(JSArray* array, unsigned i)
{
- return m_map.find(i);
+ SparseArrayEntry entry;
+ std::pair<iterator, bool> 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(JSGlobalData& globalData, JSArray* array, unsigned i, JSValue value)
+inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value)
{
- SparseArrayEntry temp;
- pair<Map::iterator, bool> result = m_map.add(i, temp);
- result.first->second.set(globalData, array, value);
- if (!result.second) // pre-existing entry
+ SparseArrayEntry& entry = add(array, i).first->second;
+
+ if (!(entry.attributes & (Getter | Setter))) {
+ if (entry.attributes & ReadOnly) {
+ // FIXME: should throw if being called from strict mode.
+ // throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+ return;
+ }
+
+ entry.set(exec->globalData(), array, value);
return;
+ }
- 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;
+ JSValue accessor = entry.Base::get();
+ ASSERT(accessor.isGetterSetter());
+ JSObject* setter = asGetterSetter(accessor)->setter();
+
+ if (!setter) {
+ throwTypeError(exec, "setting a property that has only a getter");
+ return;
}
+
+ CallData callData;
+ CallType callType = setter->methodTable()->getCallData(setter, callData);
+ MarkedArgumentBuffer args;
+ args.append(value);
+ call(exec, setter, callType, callData, array, args);
+}
+
+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)
@@ -239,6 +310,315 @@ inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor)
visitor.append(&it->second);
}
+void JSArray::enterSparseMode(JSGlobalData& globalData)
+{
+ ArrayStorage* storage = m_storage;
+ SparseArrayValueMap* map = storage->m_sparseValueMap;
+
+ if (!map)
+ map = storage->m_sparseValueMap = new SparseArrayValueMap;
+
+ 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).first->second.set(globalData, this, value);
+ }
+
+ ArrayStorage* newStorage = static_cast<ArrayStorage*>(fastMalloc(storageSize(0)));
+ memcpy(newStorage, m_storage, storageSize(0));
+ newStorage->m_allocBase = newStorage;
+ fastFree(m_storage);
+ 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) & ~(Getter | Setter);
+ return;
+ }
+
+ if (descriptor.isAccessorDescriptor()) {
+ JSObject* getter = 0;
+ if (descriptor.getter() && descriptor.getter().isObject())
+ getter = asObject(descriptor.getter());
+ if (!getter && oldDescriptor.isAccessorDescriptor()) {
+ if (oldDescriptor.getter() && oldDescriptor.getter().isObject())
+ getter = asObject(oldDescriptor.getter());
+ }
+ JSObject* setter = 0;
+ if (descriptor.setter() && descriptor.setter().isObject())
+ setter = asObject(descriptor.setter());
+ if (!setter && oldDescriptor.isAccessorDescriptor()) {
+ if (oldDescriptor.setter() && oldDescriptor.setter().isObject())
+ setter = asObject(oldDescriptor.setter());
+ }
+
+ 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) & ~DontDelete;
+ return;
+ }
+
+ ASSERT(descriptor.isGenericDescriptor());
+ entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
+}
+
+static bool reject(ExecState* exec, bool throwException, const char* message)
+{
+ if (throwException)
+ throwTypeError(exec, message);
+ return false;
+}
+
+// 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());
+ putByIndex(this, exec, index, descriptor.value());
+ return true;
+ }
+
+ enterSparseMode(exec->globalData());
+ }
+
+ SparseArrayValueMap* map = m_storage->m_sparseValueMap;
+ ASSERT(map);
+
+ // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
+ std::pair<SparseArrayValueMap::iterator, bool> result = map->add(this, index);
+ SparseArrayEntry* entryInMap = &result.first->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.second) {
+ if (!isExtensible()) {
+ map->remove(result.first);
+ 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 {
+ // 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;
+}
+
+void JSArray::setLengthWritable(ExecState* exec, bool writable)
+{
+ ASSERT(isLengthWritable() || !writable);
+ if (!isLengthWritable() || writable)
+ return;
+
+ enterSparseMode(exec->globalData());
+
+ SparseArrayValueMap* map = m_storage->m_sparseValueMap;
+ ASSERT(map);
+ map->setLengthIsReadOnly();
+}
+
+// Defined in ES5.1 15.4.5.1
+bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
+{
+ JSArray* array = static_cast<JSArray*>(object);
+
+ // 3. If P is "length", then
+ if (propertyName == exec->propertyNames().length) {
+ // All paths through length definition call the default [[DefineOwnProperty]], hence:
+ // from ES5.1 8.12.9 7.a.
+ if (descriptor.configurable())
+ return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
+ // from ES5.1 8.12.9 7.b.
+ if (descriptor.enumerable())
+ return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
+
+ // a. If the [[Value]] field of Desc is absent, then
+ // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
+ if (descriptor.isAccessorDescriptor())
+ return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
+ // from ES5.1 8.12.9 10.a.
+ if (!array->isLengthWritable() && descriptor.writable())
+ return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
+ // This descriptor is either just making length read-only, or changing nothing!
+ if (!descriptor.value()) {
+ array->setLengthWritable(exec, descriptor.writable());
+ return true;
+ }
+
+ // b. Let newLenDesc be a copy of Desc.
+ // c. Let newLen be ToUint32(Desc.[[Value]]).
+ unsigned newLen = descriptor.value().toUInt32(exec);
+ // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
+ if (newLen != descriptor.value().toNumber(exec)) {
+ throwError(exec, createRangeError(exec, "Invalid array length"));
+ return false;
+ }
+
+ // Based on SameValue check in 8.12.9, this is always okay.
+ if (newLen == array->length()) {
+ array->setLengthWritable(exec, descriptor.writable());
+ return true;
+ }
+
+ // e. Set newLenDesc.[[Value] to newLen.
+ // f. If newLen >= oldLen, then
+ // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
+ // g. Reject if oldLenDesc.[[Writable]] is false.
+ if (!array->isLengthWritable())
+ return reject(exec, throwException, "Attempting to change value of a readonly property.");
+
+ // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
+ // i. Else,
+ // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
+ // i.ii. Let newWritable be false.
+ // i.iii. Set newLenDesc.[[Writable] to true.
+ // j. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
+ // k. If succeeded is false, return false.
+ // l. While newLen < oldLen repeat,
+ // l.i. Set oldLen to oldLen – 1.
+ // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
+ // l.iii. If deleteSucceeded is false, then
+ if (!array->setLength(exec, newLen, throwException)) {
+ // 1. Set newLenDesc.[[Value] to oldLen+1.
+ // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
+ // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
+ // 4. Reject.
+ array->setLengthWritable(exec, descriptor.writable());
+ return false;
+ }
+
+ // m. If newWritable is false, then
+ // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always return true.
+ array->setLengthWritable(exec, descriptor.writable());
+ // n. Return true.
+ return true;
+ }
+
+ // 4. Else if P is an array index (15.4), then
+ bool isArrayIndex;
+ // a. Let index be ToUint32(P).
+ unsigned index = propertyName.toArrayIndex(isArrayIndex);
+ if (isArrayIndex) {
+ // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
+ if (index >= array->length() && !array->isLengthWritable())
+ return reject(exec, throwException, "Attempting to define numeric property on array with non-writable length property.");
+ // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
+ // d. Reject if succeeded is false.
+ // e. If index >= oldLen
+ // e.i. Set oldLenDesc.[[Value]] to index + 1.
+ // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
+ // f. Return true.
+ return array->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);
@@ -259,7 +639,7 @@ bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned
} else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
SparseArrayValueMap::iterator it = map->find(i);
if (it != map->notFound()) {
- slot.setValue(it->second.get());
+ it->second.get(slot);
return true;
}
}
@@ -307,7 +687,7 @@ bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const
} else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
SparseArrayValueMap::iterator it = map->find(i);
if (it != map->notFound()) {
- descriptor.setDescriptor(it->second.get(), 0);
+ it->second.get(descriptor);
return true;
}
}
@@ -332,7 +712,7 @@ void JSArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName,
throwError(exec, createRangeError(exec, "Invalid array length"));
return;
}
- thisObject->setLength(newLength);
+ thisObject->setLength(exec, newLength, slot.isStrictMode());
return;
}
@@ -372,12 +752,14 @@ void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue valu
}
// For all other cases, call putByIndexBeyondVectorLength.
- thisObject->putByIndexBeyondVectorLength(exec->globalData(), i, value);
+ thisObject->putByIndexBeyondVectorLength(exec, i, value);
thisObject->checkConsistency();
}
-NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(JSGlobalData& globalData, unsigned i, JSValue value)
+NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value)
{
+ 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);
@@ -385,15 +767,12 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(JSGlobalData& globalData
ArrayStorage* storage = m_storage;
SparseArrayValueMap* map = storage->m_sparseValueMap;
- // Update m_length if necessary.
- unsigned length = storage->m_length;
- if (i >= length) {
- length = i + 1;
- storage->m_length = length;
- }
-
// First, handle cases where we don't currently have a sparse map.
if (LIKELY(!map)) {
+ // 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(i + 1))) {
// success! - reread m_storage since it has likely been reallocated, and store to the vector.
@@ -405,15 +784,27 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(JSGlobalData& globalData
// We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
map = new SparseArrayValueMap;
storage->m_sparseValueMap = map;
- map->put(globalData, this, i, value);
+ map->put(exec, this, i, value);
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()) {
+ // FIXME: should throw in strict mode.
+ 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(length)) {
- map->put(globalData, this, i, value);
+ map->put(exec, this, i, value);
return;
}
@@ -425,7 +816,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(JSGlobalData& globalData
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.get());
+ vector[it->first].set(globalData, this, it->second.getNonSparseMode());
delete map;
storage->m_sparseValueMap = 0;
@@ -455,35 +846,35 @@ 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) {
- thisObject->checkConsistency();
- return false;
+ if (valueSlot) {
+ valueSlot.clear();
+ --storage->m_numValuesInVector;
}
- valueSlot.clear();
- --storage->m_numValuesInVector;
- thisObject->checkConsistency();
- return true;
- }
-
- if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
+ } else if (SparseArrayValueMap* map = storage->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;
}
}
thisObject->checkConsistency();
+ return true;
+}
- if (i > MAX_ARRAY_INDEX)
- return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i));
-
- return false;
+static int compareKeysForQSort(const void* a, const void* b)
+{
+ unsigned da = *static_cast<const unsigned*>(a);
+ unsigned db = *static_cast<const unsigned*>(b);
+ return (da > db) - (da < db);
}
void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
@@ -502,9 +893,18 @@ void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNam
}
if (SparseArrayValueMap* map = storage->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)
- propertyNames.add(Identifier::from(exec, it->first));
+ 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)
@@ -693,15 +1093,61 @@ bool JSArray::unshiftCountSlowCase(unsigned count)
return true;
}
-void JSArray::setLength(unsigned newLength)
+bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
{
checkConsistency();
ArrayStorage* storage = m_storage;
-
unsigned length = storage->m_length;
+ // If the length is read only then we enter sparse mode, so should enter the following 'if'.
+ ASSERT(isLengthWritable() || storage->m_sparseValueMap);
+
+ if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
+ // Fail if the length is not writable.
+ if (map->lengthIsReadOnly())
+ return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);
+
+ if (newLength < length) {
+ // Copy any keys we might be interested in into a vector.
+ Vector<unsigned> keys;
+ keys.reserveCapacity(min(map->size(), static_cast<size_t>(length - newLength)));
+ SparseArrayValueMap::const_iterator end = map->end();
+ for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
+ unsigned index = static_cast<unsigned>(it->first);
+ if (index < length && index >= newLength)
+ keys.append(index);
+ }
+
+ // Check if the array is in sparse mode. If so there may be non-configurable
+ // properties, so we have to perform deletion with caution, if not we can
+ // delete values in any order.
+ if (map->sparseMode()) {
+ qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
+ unsigned i = keys.size();
+ while (i) {
+ unsigned index = keys[--i];
+ SparseArrayValueMap::iterator it = map->find(index);
+ ASSERT(it != map->notFound());
+ if (it->second.attributes & DontDelete) {
+ storage->m_length = index + 1;
+ return reject(exec, throwException, "Unable to delete property.");
+ }
+ map->remove(it);
+ }
+ } else {
+ for (unsigned i = 0; i < keys.size(); ++i)
+ map->remove(keys[i]);
+ if (map->isEmpty()) {
+ delete map;
+ storage->m_sparseValueMap = 0;
+ }
+ }
+ }
+ }
+
if (newLength < length) {
+ // Delete properties from the vector.
unsigned usedVectorLength = min(length, m_vectorLength);
for (unsigned i = newLength; i < usedVectorLength; ++i) {
WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
@@ -709,35 +1155,26 @@ void JSArray::setLength(unsigned newLength)
valueSlot.clear();
storage->m_numValuesInVector -= hadValue;
}
-
- if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
- SparseArrayValueMap copy = *map;
- SparseArrayValueMap::const_iterator end = copy.end();
- for (SparseArrayValueMap::const_iterator it = copy.begin(); it != end; ++it) {
- if (it->first >= newLength)
- map->remove(it->first);
- }
- if (map->isEmpty() && !map->sparseMode()) {
- delete map;
- storage->m_sparseValueMap = 0;
- }
- }
}
storage->m_length = newLength;
checkConsistency();
+ return true;
}
-JSValue JSArray::pop()
+JSValue JSArray::pop(ExecState* exec)
{
checkConsistency();
ArrayStorage* storage = m_storage;
unsigned length = storage->m_length;
- if (!length)
+ if (!length) {
+ if (!isLengthWritable())
+ throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
return jsUndefined();
+ }
--length;
@@ -755,8 +1192,19 @@ JSValue JSArray::pop()
result = jsUndefined();
if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
SparseArrayValueMap::iterator it = map->find(length);
- if (it != map->end()) {
- result = it->second.get();
+ if (it != map->notFound()) {
+ unsigned attributes = it->second.attributes;
+
+ result = it->second.get(exec, this);
+ if (exec->hadException())
+ return jsUndefined();
+
+ if (attributes & DontDelete) {
+ throwError(exec, createTypeError(exec, "Unable to delete property."));
+ checkConsistency();
+ return result;
+ }
+
map->remove(it);
if (map->isEmpty() && !map->sparseMode()) {
delete map;
@@ -800,7 +1248,7 @@ void JSArray::push(ExecState* exec, JSValue value)
}
// Handled the same as putIndex.
- putByIndexBeyondVectorLength(exec->globalData(), storage->m_length, value);
+ putByIndexBeyondVectorLength(exec, storage->m_length, value);
checkConsistency();
}
@@ -1189,7 +1637,7 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType,
SparseArrayValueMap::const_iterator end = map->end();
for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
- tree.abstractor().m_nodes[numDefined].value = it->second.get();
+ tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode();
tree.insert(numDefined);
++numDefined;
}
@@ -1305,7 +1753,7 @@ unsigned JSArray::compactForSorting()
SparseArrayValueMap::const_iterator end = map->end();
for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
- storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.get());
+ storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode());
delete map;
storage->m_sparseValueMap = 0;
diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h
index 9102c8724..871cfc882 100644
--- a/Source/JavaScriptCore/runtime/JSArray.h
+++ b/Source/JavaScriptCore/runtime/JSArray.h
@@ -30,16 +30,25 @@ namespace JSC {
class JSArray;
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<unsigned, SparseArrayEntry> Map;
+ typedef HashMap<uint64_t, SparseArrayEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > Map;
enum Flags {
Normal = 0,
- SparseMode = 1
+ SparseMode = 1,
+ LengthIsReadOnly = 2,
};
public:
@@ -61,12 +70,23 @@ namespace JSC {
void setSparseMode()
{
- m_flags = (Flags)(m_flags | SparseMode);
+ 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(JSGlobalData&, JSArray*, unsigned, JSValue);
- iterator find(unsigned);
+ void put(ExecState*, JSArray*, unsigned, JSValue);
+ std::pair<iterator, bool> 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); }
@@ -136,7 +156,9 @@ namespace JSC {
return array->tryFinishCreationUninitialized(globalData, initialLength);
}
- static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
+ static bool defineOwnProperty(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&, bool throwException);
+
+ static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&);
static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&);
static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue);
@@ -144,14 +166,15 @@ namespace JSC {
static JS_EXPORTDATA const ClassInfo s_info;
unsigned length() const { return m_storage->m_length; }
- void setLength(unsigned); // OK to use on new arrays, but not if it might be a RegExpMatchArray.
+ // OK to use on new arrays, but not if it might be a RegExpMatchArray.
+ bool setLength(ExecState*, unsigned, bool throwException = false);
void sort(ExecState*);
void sort(ExecState*, JSValue compareFunction, CallType, const CallData&);
void sortNumeric(ExecState*, JSValue compareFunction, CallType, const CallData&);
void push(ExecState*, JSValue);
- JSValue pop();
+ JSValue pop(ExecState*);
void shiftCount(ExecState*, unsigned count);
void unshiftCount(ExecState*, unsigned count);
@@ -245,8 +268,19 @@ namespace JSC {
void setSubclassData(void*);
private:
+ bool isLengthWritable()
+ {
+ SparseArrayValueMap* map = m_storage->m_sparseValueMap;
+ return !map || !map->lengthIsReadOnly();
+ }
+
+ void setLengthWritable(ExecState*, bool writable);
+ void putDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old);
+ bool defineOwnNumericProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException);
+ void enterSparseMode(JSGlobalData&);
+
bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&);
- void putByIndexBeyondVectorLength(JSGlobalData&, unsigned propertyName, JSValue);
+ void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue);
unsigned getNewVectorLength(unsigned desiredLength);
bool increaseVectorLength(unsigned newLength);
diff --git a/Source/JavaScriptCore/runtime/JSByteArray.cpp b/Source/JavaScriptCore/runtime/JSByteArray.cpp
index 7478a08fb..3df21e6f5 100644
--- a/Source/JavaScriptCore/runtime/JSByteArray.cpp
+++ b/Source/JavaScriptCore/runtime/JSByteArray.cpp
@@ -33,7 +33,7 @@ using namespace WTF;
namespace JSC {
-const ClassInfo JSByteArray::s_info = { "ByteArray", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSByteArray) };
+const ClassInfo JSByteArray::s_info = { "Uint8ClampedArray", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSByteArray) };
JSByteArray::JSByteArray(ExecState* exec, Structure* structure, ByteArray* storage)
: JSNonFinalObject(exec->globalData(), structure)
diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h
index 1619f9297..a6ad8a747 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalData.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalData.h
@@ -251,6 +251,9 @@ namespace JSC {
#if ENABLE(JIT)
ReturnAddressPtr exceptionLocation;
JSValue hostCallReturnValue;
+ CallFrame* callFrameForThrow;
+ void* targetMachinePCForThrow;
+ Instruction* targetInterpreterPCForThrow;
#if ENABLE(DFG_JIT)
uint32_t osrExitIndex;
void* osrExitJumpDestination;
diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp
index 9cc29617a..a813e8416 100644
--- a/Source/JavaScriptCore/runtime/JSObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSObject.cpp
@@ -761,7 +761,7 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identi
if (descriptor.isGenericDescriptor()) {
if (!current.attributesEqual(descriptor)) {
object->methodTable()->deleteProperty(object, exec, propertyName);
- putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
+ return putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
}
return true;
}
@@ -786,7 +786,7 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identi
return false;
}
if (!current.writable()) {
- if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
+ if (descriptor.value() || !sameValue(exec, current.value(), descriptor.value())) {
if (throwException)
throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
return false;
diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h
index 6700e2e41..f40455571 100644
--- a/Source/JavaScriptCore/runtime/JSString.h
+++ b/Source/JavaScriptCore/runtime/JSString.h
@@ -59,7 +59,7 @@ namespace JSC {
JSString* jsStringBuilder(JSGlobalData*);
- class JS_EXPORTCLASS JSString : public JSCell {
+ class JSString : public JSCell {
public:
friend class JIT;
friend class JSGlobalData;
diff --git a/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp
index 9d62dfc98..c664952a5 100644
--- a/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp
+++ b/Source/JavaScriptCore/runtime/PropertyDescriptor.cpp
@@ -162,16 +162,30 @@ void PropertyDescriptor::setGetter(JSValue getter)
m_attributes &= ~ReadOnly;
}
+// See ES5.1 9.12
+bool sameValue(ExecState* exec, JSValue a, JSValue b)
+{
+ if (!a.isNumber())
+ return JSValue::strictEqual(exec, a, b);
+ if (!b.isNumber())
+ return false;
+ double x = a.asNumber();
+ double y = b.asNumber();
+ if (isnan(x))
+ return isnan(y);
+ return bitwise_cast<uint64_t>(x) == bitwise_cast<uint64_t>(y);
+}
+
bool PropertyDescriptor::equalTo(ExecState* exec, const PropertyDescriptor& other) const
{
if (!other.m_value == m_value ||
!other.m_getter == m_getter ||
!other.m_setter == m_setter)
return false;
- return (!m_value || JSValue::strictEqual(exec, other.m_value, m_value)) &&
- (!m_getter || JSValue::strictEqual(exec, other.m_getter, m_getter)) &&
- (!m_setter || JSValue::strictEqual(exec, other.m_setter, m_setter)) &&
- attributesEqual(other);
+ return (!m_value || sameValue(exec, other.m_value, m_value))
+ && (!m_getter || JSValue::strictEqual(exec, other.m_getter, m_getter))
+ && (!m_setter || JSValue::strictEqual(exec, other.m_setter, m_setter))
+ && attributesEqual(other);
}
bool PropertyDescriptor::attributesEqual(const PropertyDescriptor& other) const
@@ -201,4 +215,18 @@ unsigned PropertyDescriptor::attributesWithOverride(const PropertyDescriptor& ot
return newAttributes;
}
+unsigned PropertyDescriptor::attributesOverridingCurrent(const PropertyDescriptor& current) const
+{
+ unsigned overrideMask = 0;
+ if (writablePresent())
+ overrideMask |= ReadOnly;
+ if (enumerablePresent())
+ overrideMask |= DontEnum;
+ if (configurablePresent())
+ overrideMask |= DontDelete;
+ if (isAccessorDescriptor())
+ overrideMask |= (Getter | Setter);
+ return (m_attributes & overrideMask) | (current.m_attributes & ~overrideMask);
+}
+
}
diff --git a/Source/JavaScriptCore/runtime/PropertyDescriptor.h b/Source/JavaScriptCore/runtime/PropertyDescriptor.h
index bebf5e826..3d481b25e 100644
--- a/Source/JavaScriptCore/runtime/PropertyDescriptor.h
+++ b/Source/JavaScriptCore/runtime/PropertyDescriptor.h
@@ -31,6 +31,9 @@
namespace JSC {
class GetterSetter;
+ // See ES5.1 9.12
+ bool sameValue(ExecState*, JSValue, JSValue);
+
class PropertyDescriptor {
public:
PropertyDescriptor()
@@ -66,6 +69,8 @@ namespace JSC {
bool equalTo(ExecState* exec, const PropertyDescriptor& other) const;
bool attributesEqual(const PropertyDescriptor& other) const;
unsigned attributesWithOverride(const PropertyDescriptor& other) const;
+ unsigned attributesOverridingCurrent(const PropertyDescriptor& current) const;
+
private:
static unsigned defaultAttributes;
bool operator==(const PropertyDescriptor&){ return false; }