summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/JSObjectInlines.h
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/runtime/JSObjectInlines.h
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSObjectInlines.h')
-rw-r--r--Source/JavaScriptCore/runtime/JSObjectInlines.h363
1 files changed, 363 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/JSObjectInlines.h b/Source/JavaScriptCore/runtime/JSObjectInlines.h
new file mode 100644
index 000000000..2cb9dde2f
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/JSObjectInlines.h
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2003-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Eric Seidel (eric@webkit.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#pragma once
+
+#include "AuxiliaryBarrierInlines.h"
+#include "Error.h"
+#include "JSObject.h"
+#include "Lookup.h"
+
+namespace JSC {
+
+// Section 7.3.17 of the spec.
+template <typename AddFunction> // Add function should have a type like: (JSValue, RuntimeType) -> bool
+void createListFromArrayLike(ExecState* exec, JSValue arrayLikeValue, RuntimeTypeMask legalTypesFilter, const String& errorMessage, AddFunction addFunction)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Vector<JSValue> result;
+ JSValue lengthProperty = arrayLikeValue.get(exec, vm.propertyNames->length);
+ RETURN_IF_EXCEPTION(scope, void());
+ double lengthAsDouble = lengthProperty.toLength(exec);
+ RETURN_IF_EXCEPTION(scope, void());
+ RELEASE_ASSERT(lengthAsDouble >= 0.0 && lengthAsDouble == std::trunc(lengthAsDouble));
+ uint64_t length = static_cast<uint64_t>(lengthAsDouble);
+ for (uint64_t index = 0; index < length; index++) {
+ JSValue next = arrayLikeValue.get(exec, index);
+ RETURN_IF_EXCEPTION(scope, void());
+
+ RuntimeType type = runtimeTypeForValue(next);
+ if (!(type & legalTypesFilter)) {
+ throwTypeError(exec, scope, errorMessage);
+ return;
+ }
+
+ bool exitEarly = addFunction(next, type);
+ if (exitEarly)
+ return;
+ }
+}
+
+ALWAYS_INLINE bool JSObject::canPerformFastPutInline(ExecState* exec, VM& vm, PropertyName propertyName)
+{
+ if (UNLIKELY(propertyName == exec->propertyNames().underscoreProto))
+ return false;
+
+ // Check if there are any setters or getters in the prototype chain
+ JSValue prototype;
+ JSObject* obj = this;
+ while (true) {
+ if (obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || obj->type() == ProxyObjectType)
+ return false;
+
+ prototype = obj->getPrototypeDirect();
+ if (prototype.isNull())
+ return true;
+
+ obj = asObject(prototype);
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+template<typename CallbackWhenNoException>
+ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, CallbackWhenNoException callback) const
+{
+ PropertySlot slot(this, PropertySlot::InternalMethodType::Get);
+ return getPropertySlot(exec, propertyName, slot, callback);
+}
+
+template<typename CallbackWhenNoException>
+ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot, CallbackWhenNoException callback) const
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ bool found = const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
+ RETURN_IF_EXCEPTION(scope, { });
+ return callback(found, slot);
+}
+
+ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ auto& structureIDTable = vm.heap.structureIDTable();
+ JSObject* object = this;
+ MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
+ while (true) {
+ Structure* structure = structureIDTable.get(object->structureID());
+ if (structure->classInfo()->methodTable.getOwnPropertySlotByIndex(object, exec, propertyName, slot))
+ return true;
+ RETURN_IF_EXCEPTION(scope, false);
+ JSValue prototype;
+ if (LIKELY(structure->classInfo()->methodTable.getPrototype == defaultGetPrototype || slot.internalMethodType() == PropertySlot::InternalMethodType::VMInquiry))
+ prototype = structure->storedPrototype();
+ else {
+ prototype = object->getPrototype(vm, exec);
+ RETURN_IF_EXCEPTION(scope, false);
+ }
+ if (!prototype.isObject())
+ return false;
+ object = asObject(prototype);
+ }
+}
+
+ALWAYS_INLINE bool JSObject::getNonIndexPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+ // This method only supports non-index PropertyNames.
+ ASSERT(!parseIndex(propertyName));
+
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ auto& structureIDTable = vm.heap.structureIDTable();
+ JSObject* object = this;
+ MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
+ while (true) {
+ Structure* structure = structureIDTable.get(object->structureID());
+ if (LIKELY(!TypeInfo::overridesGetOwnPropertySlot(object->inlineTypeFlags()))) {
+ if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot))
+ return true;
+ } else {
+ if (structure->classInfo()->methodTable.getOwnPropertySlot(object, exec, propertyName, slot))
+ return true;
+ RETURN_IF_EXCEPTION(scope, false);
+ }
+ JSValue prototype;
+ if (LIKELY(structure->classInfo()->methodTable.getPrototype == defaultGetPrototype || slot.internalMethodType() == PropertySlot::InternalMethodType::VMInquiry))
+ prototype = structure->storedPrototype();
+ else {
+ prototype = object->getPrototype(vm, exec);
+ RETURN_IF_EXCEPTION(scope, false);
+ }
+ if (!prototype.isObject())
+ return false;
+ object = asObject(prototype);
+ }
+}
+
+inline void JSObject::putDirectWithoutTransition(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
+{
+ ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
+ ASSERT(!value.isCustomGetterSetter());
+ StructureID structureID = this->structureID();
+ Structure* structure = vm.heap.structureIDTable().get(structureID);
+ PropertyOffset offset = prepareToPutDirectWithoutTransition(vm, propertyName, attributes, structureID, structure);
+ bool shouldOptimize = false;
+ structure->willStoreValueForNewTransition(vm, propertyName, value, shouldOptimize);
+ putDirect(vm, offset, value);
+ if (attributes & ReadOnly)
+ structure->setContainsReadOnlyProperties();
+}
+
+ALWAYS_INLINE PropertyOffset JSObject::prepareToPutDirectWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, StructureID structureID, Structure* structure)
+{
+ unsigned oldOutOfLineCapacity = structure->outOfLineCapacity();
+ PropertyOffset result;
+ structure->addPropertyWithoutTransition(
+ vm, propertyName, attributes,
+ [&] (const GCSafeConcurrentJSLocker&, PropertyOffset offset, PropertyOffset newLastOffset) {
+ unsigned newOutOfLineCapacity = Structure::outOfLineCapacity(newLastOffset);
+ if (newOutOfLineCapacity != oldOutOfLineCapacity) {
+ Butterfly* butterfly = allocateMoreOutOfLineStorage(vm, oldOutOfLineCapacity, newOutOfLineCapacity);
+ nukeStructureAndSetButterfly(vm, structureID, butterfly);
+ structure->setLastOffset(newLastOffset);
+ WTF::storeStoreFence();
+ setStructureIDDirectly(structureID);
+ } else
+ structure->setLastOffset(newLastOffset);
+ result = offset;
+ });
+ return result;
+}
+
+// ECMA 8.6.2.2
+ALWAYS_INLINE bool JSObject::putInline(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSObject* thisObject = jsCast<JSObject*>(cell);
+ ASSERT(value);
+ ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
+
+ if (UNLIKELY(isThisValueAltered(slot, thisObject)))
+ return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
+
+ // Try indexed put first. This is required for correctness, since loads on property names that appear like
+ // valid indices will never look in the named property storage.
+ if (std::optional<uint32_t> index = parseIndex(propertyName))
+ return putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode());
+
+ if (thisObject->canPerformFastPutInline(exec, vm, propertyName)) {
+ ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(vm, propertyName));
+ if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot))
+ return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
+ return true;
+ }
+
+ return thisObject->putInlineSlow(exec, propertyName, value, slot);
+}
+
+// HasOwnProperty(O, P) from section 7.3.11 in the spec.
+// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasownproperty
+ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const
+{
+ ASSERT(slot.internalMethodType() == PropertySlot::InternalMethodType::GetOwnProperty);
+ if (LIKELY(const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot == JSObject::getOwnPropertySlot))
+ return JSObject::getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
+ return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
+}
+
+ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
+{
+ PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
+ return hasOwnProperty(exec, propertyName, slot);
+}
+
+ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, unsigned propertyName) const
+{
+ PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
+ return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlotByIndex(const_cast<JSObject*>(this), exec, propertyName, slot);
+}
+
+template<JSObject::PutMode mode>
+ALWAYS_INLINE bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot)
+{
+ ASSERT(value);
+ ASSERT(value.isGetterSetter() == !!(attributes & Accessor));
+ ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
+ ASSERT(!parseIndex(propertyName));
+
+ StructureID structureID = this->structureID();
+ Structure* structure = vm.heap.structureIDTable().get(structureID);
+ if (structure->isDictionary()) {
+ ASSERT(!structure->hasInferredTypes());
+
+ unsigned currentAttributes;
+ PropertyOffset offset = structure->get(vm, propertyName, currentAttributes);
+ if (offset != invalidOffset) {
+ if ((mode == PutModePut) && currentAttributes & ReadOnly)
+ return false;
+
+ putDirect(vm, offset, value);
+ structure->didReplaceProperty(offset);
+ slot.setExistingProperty(this, offset);
+
+ if ((attributes & Accessor) != (currentAttributes & Accessor) || (attributes & CustomAccessor) != (currentAttributes & CustomAccessor)) {
+ ASSERT(!(attributes & ReadOnly));
+ setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes));
+ }
+ return true;
+ }
+
+ if ((mode == PutModePut) && !isStructureExtensible())
+ return false;
+
+ offset = prepareToPutDirectWithoutTransition(vm, propertyName, attributes, structureID, structure);
+ validateOffset(offset);
+ putDirect(vm, offset, value);
+ slot.setNewProperty(this, offset);
+ if (attributes & ReadOnly)
+ this->structure()->setContainsReadOnlyProperties();
+ return true;
+ }
+
+ PropertyOffset offset;
+ size_t currentCapacity = this->structure()->outOfLineCapacity();
+ Structure* newStructure = Structure::addPropertyTransitionToExistingStructure(
+ structure, propertyName, attributes, offset);
+ if (newStructure) {
+ newStructure->willStoreValueForExistingTransition(
+ vm, propertyName, value, slot.context() == PutPropertySlot::PutById);
+
+ Butterfly* newButterfly = butterfly();
+ if (currentCapacity != newStructure->outOfLineCapacity()) {
+ ASSERT(newStructure != this->structure());
+ newButterfly = allocateMoreOutOfLineStorage(vm, currentCapacity, newStructure->outOfLineCapacity());
+ nukeStructureAndSetButterfly(vm, structureID, newButterfly);
+ }
+
+ validateOffset(offset);
+ ASSERT(newStructure->isValidOffset(offset));
+ putDirect(vm, offset, value);
+ setStructure(vm, newStructure);
+ slot.setNewProperty(this, offset);
+ return true;
+ }
+
+ unsigned currentAttributes;
+ bool hasInferredType;
+ offset = structure->get(vm, propertyName, currentAttributes, hasInferredType);
+ if (offset != invalidOffset) {
+ if ((mode == PutModePut) && currentAttributes & ReadOnly)
+ return false;
+
+ structure->didReplaceProperty(offset);
+ if (UNLIKELY(hasInferredType)) {
+ structure->willStoreValueForReplace(
+ vm, propertyName, value, slot.context() == PutPropertySlot::PutById);
+ }
+
+ slot.setExistingProperty(this, offset);
+ putDirect(vm, offset, value);
+
+ if ((attributes & Accessor) != (currentAttributes & Accessor) || (attributes & CustomAccessor) != (currentAttributes & CustomAccessor)) {
+ ASSERT(!(attributes & ReadOnly));
+ setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes));
+ }
+ return true;
+ }
+
+ if ((mode == PutModePut) && !isStructureExtensible())
+ return false;
+
+ // We want the structure transition watchpoint to fire after this object has switched
+ // structure. This allows adaptive watchpoints to observe if the new structure is the one
+ // we want.
+ DeferredStructureTransitionWatchpointFire deferredWatchpointFire;
+
+ newStructure = Structure::addNewPropertyTransition(
+ vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire);
+ newStructure->willStoreValueForNewTransition(
+ vm, propertyName, value, slot.context() == PutPropertySlot::PutById);
+
+ validateOffset(offset);
+ ASSERT(newStructure->isValidOffset(offset));
+ size_t oldCapacity = structure->outOfLineCapacity();
+ size_t newCapacity = newStructure->outOfLineCapacity();
+ ASSERT(oldCapacity <= newCapacity);
+ if (oldCapacity != newCapacity) {
+ Butterfly* newButterfly = allocateMoreOutOfLineStorage(vm, oldCapacity, newCapacity);
+ nukeStructureAndSetButterfly(vm, structureID, newButterfly);
+ }
+ putDirect(vm, offset, value);
+ setStructure(vm, newStructure);
+ slot.setNewProperty(this, offset);
+ if (attributes & ReadOnly)
+ newStructure->setContainsReadOnlyProperties();
+ return true;
+}
+
+} // namespace JSC