From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- .../JavaScriptCore/runtime/ObjectConstructor.cpp | 743 ++++++++++++++------- 1 file changed, 491 insertions(+), 252 deletions(-) (limited to 'Source/JavaScriptCore/runtime/ObjectConstructor.cpp') diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp index 72169e7d2..73011a0c9 100644 --- a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2016 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,36 +21,37 @@ #include "config.h" #include "ObjectConstructor.h" +#include "BuiltinNames.h" #include "ButterflyInlines.h" -#include "CallFrameInlines.h" -#include "CopiedSpaceInlines.h" #include "Error.h" #include "ExceptionHelpers.h" -#include "JSFunction.h" #include "JSArray.h" +#include "JSCInlines.h" +#include "JSFunction.h" #include "JSGlobalObject.h" +#include "JSGlobalObjectFunctions.h" #include "Lookup.h" #include "ObjectPrototype.h" -#include "Operations.h" #include "PropertyDescriptor.h" #include "PropertyNameArray.h" #include "StackVisitor.h" +#include "Symbol.h" namespace JSC { -static EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState*); -static EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState*); +EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState*); } @@ -60,13 +61,16 @@ namespace JSC { STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectConstructor); -const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::objectConstructorTable, CREATE_METHOD_TABLE(ObjectConstructor) }; +const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_info, &objectConstructorTable, CREATE_METHOD_TABLE(ObjectConstructor) }; /* Source for ObjectConstructor.lut.h @begin objectConstructorTable getPrototypeOf objectConstructorGetPrototypeOf DontEnum|Function 1 + setPrototypeOf objectConstructorSetPrototypeOf DontEnum|Function 2 getOwnPropertyDescriptor objectConstructorGetOwnPropertyDescriptor DontEnum|Function 2 + getOwnPropertyDescriptors objectConstructorGetOwnPropertyDescriptors DontEnum|Function 1 getOwnPropertyNames objectConstructorGetOwnPropertyNames DontEnum|Function 1 + getOwnPropertySymbols objectConstructorGetOwnPropertySymbols DontEnum|Function 1 keys objectConstructorKeys DontEnum|Function 1 defineProperty objectConstructorDefineProperty DontEnum|Function 3 defineProperties objectConstructorDefineProperties DontEnum|Function 2 @@ -77,6 +81,10 @@ const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_i isSealed objectConstructorIsSealed DontEnum|Function 1 isFrozen objectConstructorIsFrozen DontEnum|Function 1 isExtensible objectConstructorIsExtensible DontEnum|Function 1 + is objectConstructorIs DontEnum|Function 2 + assign JSBuiltin DontEnum|Function 2 + values JSBuiltin DontEnum|Function 1 + entries JSBuiltin DontEnum|Function 1 @end */ @@ -85,228 +93,311 @@ ObjectConstructor::ObjectConstructor(VM& vm, Structure* structure) { } -void ObjectConstructor::finishCreation(VM& vm, ObjectPrototype* objectPrototype) +void ObjectConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, ObjectPrototype* objectPrototype) { - Base::finishCreation(vm, Identifier(&vm, "Object").string()); + Base::finishCreation(vm, objectPrototype->classInfo(vm)->className); // ECMA 15.2.3.1 putDirectWithoutTransition(vm, vm.propertyNames->prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); // no. of arguments for constructor putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); -} -bool ObjectConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot) -{ - return getStaticFunctionSlot(exec, ExecState::objectConstructorTable(exec->vm()), jsCast(object), propertyName, slot); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().createPrivateName(), objectConstructorCreate, DontEnum, 2); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().definePropertyPrivateName(), objectConstructorDefineProperty, DontEnum, 3); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getPrototypeOfPrivateName(), objectConstructorGetPrototypeOf, DontEnum, 1); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getOwnPropertyNamesPrivateName(), objectConstructorGetOwnPropertyNames, DontEnum, 1); } -static ALWAYS_INLINE JSObject* constructObject(ExecState* exec) +// ES 19.1.1.1 Object([value]) +static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, JSValue newTarget) { - JSGlobalObject* globalObject = exec->callee()->globalObject(); + ObjectConstructor* objectConstructor = jsCast(exec->jsCallee()); + JSGlobalObject* globalObject = objectConstructor->globalObject(); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // We need to check newTarget condition in this caller side instead of InternalFunction::createSubclassStructure side. + // Since if we found this condition is met, we should not fall into the type conversion in the step 3. + + // 1. If NewTarget is neither undefined nor the active function, then + if (newTarget && newTarget != objectConstructor) { + // a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%"). + Structure* objectStructure = InternalFunction::createSubclassStructure(exec, newTarget, globalObject->objectStructureForObjectConstructor()); + RETURN_IF_EXCEPTION(scope, nullptr); + return constructEmptyObject(exec, objectStructure); + } + + // 2. If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%). ArgList args(exec); JSValue arg = args.at(0); if (arg.isUndefinedOrNull()) - return constructEmptyObject(exec, globalObject->objectPrototype()); + return constructEmptyObject(exec, globalObject->objectStructureForObjectConstructor()); + + // 3. Return ToObject(value). + scope.release(); return arg.toObject(exec, globalObject); } static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec) { - return JSValue::encode(constructObject(exec)); + return JSValue::encode(constructObject(exec, exec->newTarget())); } ConstructType ObjectConstructor::getConstructData(JSCell*, ConstructData& constructData) { constructData.native.function = constructWithObjectConstructor; - return ConstructTypeHost; + return ConstructType::Host; } static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec) { - return JSValue::encode(constructObject(exec)); + return JSValue::encode(constructObject(exec, JSValue())); } CallType ObjectConstructor::getCallData(JSCell*, CallData& callData) { callData.native.function = callObjectConstructor; - return CallTypeHost; + return CallType::Host; } -class ObjectConstructorGetPrototypeOfFunctor { -public: - ObjectConstructorGetPrototypeOfFunctor(JSObject* object) - : m_hasSkippedFirstFrame(false) - , m_object(object) - , m_result(JSValue::encode(jsUndefined())) - { - } +EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSObject* object = exec->argument(0).toObject(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + scope.release(); + return JSValue::encode(object->getPrototype(vm, exec)); +} - EncodedJSValue result() const { return m_result; } +EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); - StackVisitor::Status operator()(StackVisitor& visitor) - { - if (!m_hasSkippedFirstFrame) { - m_hasSkippedFirstFrame = true; - return StackVisitor::Continue; - } + JSValue objectValue = exec->argument(0); + if (objectValue.isUndefinedOrNull()) + return throwVMTypeError(exec, scope, ASCIILiteral("Cannot set prototype of undefined or null")); + + JSValue protoValue = exec->argument(1); + if (!protoValue.isObject() && !protoValue.isNull()) + return throwVMTypeError(exec, scope, ASCIILiteral("Prototype value can only be an object or null")); + + JSObject* object = objectValue.toObject(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); - if (m_object->allowsAccessFrom(visitor->callFrame())) - m_result = JSValue::encode(m_object->prototype()); - return StackVisitor::Done; + bool shouldThrowIfCantSet = true; + bool didSetPrototype = object->setPrototype(vm, exec, protoValue, shouldThrowIfCantSet); + ASSERT_UNUSED(didSetPrototype, scope.exception() || didSetPrototype); + return JSValue::encode(objectValue); } -private: - bool m_hasSkippedFirstFrame; - JSObject* m_object; - EncodedJSValue m_result; -}; +JSValue objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject* object, const Identifier& propertyName) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + PropertyDescriptor descriptor; + if (!object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) { + scope.release(); + return jsUndefined(); + } + RETURN_IF_EXCEPTION(scope, { }); -EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec) + JSObject* result = constructObjectFromPropertyDescriptor(exec, descriptor); + ASSERT(!!scope.exception() == !result); + if (!result) + return jsUndefined(); + return result; +} + +JSValue objectConstructorGetOwnPropertyDescriptors(ExecState* exec, JSObject* object) { - if (!exec->argument(0).isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested prototype of a value that is not an object."))); - JSObject* object = asObject(exec->argument(0)); - ObjectConstructorGetPrototypeOfFunctor functor(object); - exec->iterate(functor); - return functor.result(); + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols); + object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include)); + RETURN_IF_EXCEPTION(scope, { }); + + JSObject* descriptors = constructEmptyObject(exec); + RETURN_IF_EXCEPTION(scope, { }); + + for (auto& propertyName : properties) { + PropertyDescriptor descriptor; + bool didGetDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, descriptor); + RETURN_IF_EXCEPTION(scope, { }); + + if (!didGetDescriptor) + continue; + + JSObject* fromDescriptor = constructObjectFromPropertyDescriptor(exec, descriptor); + ASSERT(!!scope.exception() == !fromDescriptor); + if (!fromDescriptor) + return jsUndefined(); + + PutPropertySlot slot(descriptors); + descriptors->putOwnDataPropertyMayBeIndex(exec, propertyName, fromDescriptor, slot); + ASSERT(!scope.exception()); + } + + return descriptors; } EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec) { - if (!exec->argument(0).isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested property descriptor of a value that is not an object."))); - String propertyName = exec->argument(1).toString(exec)->value(exec); - if (exec->hadException()) - return JSValue::encode(jsNull()); - JSObject* object = asObject(exec->argument(0)); - PropertyDescriptor descriptor; - if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor)) - return JSValue::encode(jsUndefined()); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - - JSObject* description = constructEmptyObject(exec); - if (!descriptor.isAccessorDescriptor()) { - description->putDirect(exec->vm(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); - description->putDirect(exec->vm(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); - } else { - ASSERT(descriptor.getter()); - ASSERT(descriptor.setter()); - description->putDirect(exec->vm(), exec->propertyNames().get, descriptor.getter(), 0); - description->putDirect(exec->vm(), exec->propertyNames().set, descriptor.setter(), 0); - } - - description->putDirect(exec->vm(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); - description->putDirect(exec->vm(), exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSObject* object = exec->argument(0).toObject(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + auto propertyName = exec->argument(1).toPropertyKey(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + scope.release(); + return JSValue::encode(objectConstructorGetOwnPropertyDescriptor(exec, object, propertyName)); +} - return JSValue::encode(description); +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptors(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSObject* object = exec->argument(0).toObject(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + scope.release(); + return JSValue::encode(objectConstructorGetOwnPropertyDescriptors(exec, object)); } // FIXME: Use the enumeration cache. EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec) { - if (!exec->argument(0).isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested property names of a value that is not an object."))); - PropertyNameArray properties(exec); - asObject(exec->argument(0))->methodTable()->getOwnPropertyNames(asObject(exec->argument(0)), exec, properties, IncludeDontEnumProperties); - JSArray* names = constructEmptyArray(exec, 0); - size_t numProperties = properties.size(); - for (size_t i = 0; i < numProperties; i++) - names->push(exec, jsOwnedString(exec, properties[i].string())); - return JSValue::encode(names); + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSObject* object = exec->argument(0).toObject(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + scope.release(); + return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Include)); +} + +// FIXME: Use the enumeration cache. +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertySymbols(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSObject* object = exec->argument(0).toObject(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + scope.release(); + return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Symbols, DontEnumPropertiesMode::Include)); } // FIXME: Use the enumeration cache. EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec) { - if (!exec->argument(0).isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested keys of a value that is not an object."))); - PropertyNameArray properties(exec); - asObject(exec->argument(0))->methodTable()->getOwnPropertyNames(asObject(exec->argument(0)), exec, properties, ExcludeDontEnumProperties); - JSArray* keys = constructEmptyArray(exec, 0); - size_t numProperties = properties.size(); - for (size_t i = 0; i < numProperties; i++) - keys->push(exec, jsOwnedString(exec, properties[i].string())); - return JSValue::encode(keys); + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSObject* object = exec->argument(0).toObject(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + scope.release(); + return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude)); } -// ES5 8.10.5 ToPropertyDescriptor -static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) +EncodedJSValue JSC_HOST_CALL ownEnumerablePropertyKeys(ExecState* exec) { + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSObject* object = exec->argument(0).toObject(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + scope.release(); + return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Exclude)); +} + +// ES6 6.2.4.5 ToPropertyDescriptor +// https://tc39.github.io/ecma262/#sec-topropertydescriptor +bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (!in.isObject()) { - exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Property description must be an object."))); + throwTypeError(exec, scope, ASCIILiteral("Property description must be an object.")); return false; } JSObject* description = asObject(in); - PropertySlot enumerableSlot(description); - if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { - desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); - if (exec->hadException()) - return false; - } - - PropertySlot configurableSlot(description); - if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { - desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); - if (exec->hadException()) - return false; - } + bool hasProperty = description->hasProperty(exec, vm.propertyNames->enumerable); + ASSERT(!scope.exception() || !hasProperty); + if (hasProperty) { + JSValue value = description->get(exec, vm.propertyNames->enumerable); + RETURN_IF_EXCEPTION(scope, false); + desc.setEnumerable(value.toBoolean(exec)); + } else + RETURN_IF_EXCEPTION(scope, false); + + hasProperty = description->hasProperty(exec, vm.propertyNames->configurable); + ASSERT(!scope.exception() || !hasProperty); + if (hasProperty) { + JSValue value = description->get(exec, vm.propertyNames->configurable); + RETURN_IF_EXCEPTION(scope, false); + desc.setConfigurable(value.toBoolean(exec)); + } else + RETURN_IF_EXCEPTION(scope, false); JSValue value; - PropertySlot valueSlot(description); - if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { - desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); - if (exec->hadException()) - return false; - } - - PropertySlot writableSlot(description); - if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { - desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); - if (exec->hadException()) - return false; - } - - PropertySlot getSlot(description); - if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { - JSValue get = getSlot.getValue(exec, exec->propertyNames().get); - if (exec->hadException()) - return false; + hasProperty = description->hasProperty(exec, vm.propertyNames->value); + ASSERT(!scope.exception() || !hasProperty); + if (hasProperty) { + JSValue value = description->get(exec, vm.propertyNames->value); + RETURN_IF_EXCEPTION(scope, false); + desc.setValue(value); + } else + RETURN_IF_EXCEPTION(scope, false); + + hasProperty = description->hasProperty(exec, vm.propertyNames->writable); + ASSERT(!scope.exception() || !hasProperty); + if (hasProperty) { + JSValue value = description->get(exec, vm.propertyNames->writable); + RETURN_IF_EXCEPTION(scope, false); + desc.setWritable(value.toBoolean(exec)); + } else + RETURN_IF_EXCEPTION(scope, false); + + hasProperty = description->hasProperty(exec, vm.propertyNames->get); + ASSERT(!scope.exception() || !hasProperty); + if (hasProperty) { + JSValue get = description->get(exec, vm.propertyNames->get); + RETURN_IF_EXCEPTION(scope, false); if (!get.isUndefined()) { CallData callData; - if (getCallData(get, callData) == CallTypeNone) { - exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Getter must be a function."))); + if (getCallData(get, callData) == CallType::None) { + throwTypeError(exec, scope, ASCIILiteral("Getter must be a function.")); return false; } } desc.setGetter(get); - } - - PropertySlot setSlot(description); - if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { - JSValue set = setSlot.getValue(exec, exec->propertyNames().set); - if (exec->hadException()) - return false; + } else + RETURN_IF_EXCEPTION(scope, false); + + hasProperty = description->hasProperty(exec, vm.propertyNames->set); + ASSERT(!scope.exception() || !hasProperty); + if (hasProperty) { + JSValue set = description->get(exec, vm.propertyNames->set); + RETURN_IF_EXCEPTION(scope, false); if (!set.isUndefined()) { CallData callData; - if (getCallData(set, callData) == CallTypeNone) { - exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Setter must be a function."))); + if (getCallData(set, callData) == CallType::None) { + throwTypeError(exec, scope, ASCIILiteral("Setter must be a function.")); return false; } } desc.setSetter(set); - } + } else + RETURN_IF_EXCEPTION(scope, false); if (!desc.isAccessorDescriptor()) return true; if (desc.value()) { - exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Invalid property. 'value' present on property with getter or setter."))); + throwTypeError(exec, scope, ASCIILiteral("Invalid property. 'value' present on property with getter or setter.")); return false; } if (desc.writablePresent()) { - exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Invalid property. 'writable' present on property with getter or setter."))); + throwTypeError(exec, scope, ASCIILiteral("Invalid property. 'writable' present on property with getter or setter.")); return false; } return true; @@ -314,34 +405,44 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec) { + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (!exec->argument(0).isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Properties can only be defined on Objects."))); - JSObject* O = asObject(exec->argument(0)); - String propertyName = exec->argument(1).toString(exec)->value(exec); - if (exec->hadException()) - return JSValue::encode(jsNull()); + return throwVMTypeError(exec, scope, ASCIILiteral("Properties can only be defined on Objects.")); + JSObject* obj = asObject(exec->argument(0)); + auto propertyName = exec->argument(1).toPropertyKey(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); PropertyDescriptor descriptor; - if (!toPropertyDescriptor(exec, exec->argument(2), descriptor)) + auto success = toPropertyDescriptor(exec, exec->argument(2), descriptor); + ASSERT(!scope.exception() == success); + if (!success) return JSValue::encode(jsNull()); ASSERT((descriptor.attributes() & Accessor) || (!descriptor.isAccessorDescriptor())); - ASSERT(!exec->hadException()); - O->methodTable()->defineOwnProperty(O, exec, Identifier(exec, propertyName), descriptor, true); - return JSValue::encode(O); + ASSERT(!scope.exception()); + obj->methodTable(vm)->defineOwnProperty(obj, exec, propertyName, descriptor, true); + scope.release(); + return JSValue::encode(obj); } static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) { - PropertyNameArray propertyNames(exec); - asObject(properties)->methodTable()->getOwnPropertyNames(asObject(properties), exec, propertyNames, ExcludeDontEnumProperties); + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + PropertyNameArray propertyNames(exec, PropertyNameMode::StringsAndSymbols); + asObject(properties)->methodTable(vm)->getOwnPropertyNames(asObject(properties), exec, propertyNames, EnumerationMode(DontEnumPropertiesMode::Exclude)); + RETURN_IF_EXCEPTION(scope, { }); size_t numProperties = propertyNames.size(); Vector descriptors; MarkedArgumentBuffer markBuffer; for (size_t i = 0; i < numProperties; i++) { JSValue prop = properties->get(exec, propertyNames[i]); - if (exec->hadException()) - return jsNull(); + RETURN_IF_EXCEPTION(scope, { }); PropertyDescriptor descriptor; - if (!toPropertyDescriptor(exec, prop, descriptor)) + bool success = toPropertyDescriptor(exec, prop, descriptor); + ASSERT(!scope.exception() || !success); + if (UNLIKELY(!success)) return jsNull(); descriptors.append(descriptor); // Ensure we mark all the values that we're accumulating @@ -355,141 +456,192 @@ static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* pro } } for (size_t i = 0; i < numProperties; i++) { - object->methodTable()->defineOwnProperty(object, exec, propertyNames[i], descriptors[i], true); - if (exec->hadException()) - return jsNull(); + Identifier propertyName = propertyNames[i]; + if (vm.propertyNames->isPrivateName(propertyName)) + continue; + object->methodTable(vm)->defineOwnProperty(object, exec, propertyName, descriptors[i], true); + RETURN_IF_EXCEPTION(scope, { }); } return object; } EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec) { + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (!exec->argument(0).isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Properties can only be defined on Objects."))); - return JSValue::encode(defineProperties(exec, asObject(exec->argument(0)), exec->argument(1).toObject(exec))); + return throwVMTypeError(exec, scope, ASCIILiteral("Properties can only be defined on Objects.")); + JSObject* targetObj = asObject(exec->argument(0)); + JSObject* props = exec->argument(1).toObject(exec); + ASSERT(!!scope.exception() == !props); + if (UNLIKELY(!props)) + return encodedJSValue(); + scope.release(); + return JSValue::encode(defineProperties(exec, targetObj, props)); } EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec) { + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue proto = exec->argument(0); if (!proto.isObject() && !proto.isNull()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object prototype may only be an Object or null."))); + return throwVMTypeError(exec, scope, ASCIILiteral("Object prototype may only be an Object or null.")); JSObject* newObject = proto.isObject() ? constructEmptyObject(exec, asObject(proto)) : constructEmptyObject(exec, exec->lexicalGlobalObject()->nullPrototypeObjectStructure()); if (exec->argument(1).isUndefined()) return JSValue::encode(newObject); if (!exec->argument(1).isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Property descriptor list must be an Object."))); + return throwVMTypeError(exec, scope, ASCIILiteral("Property descriptor list must be an Object.")); + scope.release(); return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1)))); } -EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec) +enum class IntegrityLevel { + Sealed, + Frozen +}; + +template +bool setIntegrityLevel(ExecState* exec, VM& vm, JSObject* object) { - // 1. If Type(O) is not Object throw a TypeError exception. - JSValue obj = exec->argument(0); - if (!obj.isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object.seal can only be called on Objects."))); - JSObject* object = asObject(obj); + // See https://tc39.github.io/ecma262/#sec-setintegritylevel. + auto scope = DECLARE_THROW_SCOPE(vm); - if (isJSFinalObject(object)) { - object->seal(exec->vm()); - return JSValue::encode(obj); - } + bool success = object->methodTable(vm)->preventExtensions(object, exec); + RETURN_IF_EXCEPTION(scope, false); + if (UNLIKELY(!success)) + return false; + + PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols); + object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include)); + RETURN_IF_EXCEPTION(scope, false); - // 2. For each named own property name P of O, - PropertyNameArray properties(exec); - object->methodTable()->getOwnPropertyNames(object, exec, properties, IncludeDontEnumProperties); PropertyNameArray::const_iterator end = properties.end(); for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { - // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. - PropertyDescriptor desc; - if (!object->getOwnPropertyDescriptor(exec, *iter, desc)) + Identifier propertyName = *iter; + if (vm.propertyNames->isPrivateName(propertyName)) continue; - // b. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false. - desc.setConfigurable(false); - // c. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments. - object->methodTable()->defineOwnProperty(object, exec, *iter, desc, true); - if (exec->hadException()) - return JSValue::encode(obj); - } - // 3. Set the [[Extensible]] internal property of O to false. - object->preventExtensions(exec->vm()); + PropertyDescriptor desc; + if (level == IntegrityLevel::Sealed) + desc.setConfigurable(false); + else { + bool hasPropertyDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc); + RETURN_IF_EXCEPTION(scope, false); + if (!hasPropertyDescriptor) + continue; + + if (desc.isDataDescriptor()) + desc.setWritable(false); + + desc.setConfigurable(false); + } - // 4. Return O. - return JSValue::encode(obj); + object->methodTable(vm)->defineOwnProperty(object, exec, propertyName, desc, true); + RETURN_IF_EXCEPTION(scope, false); + } + return true; } - -EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec) + +EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec) { - // 1. If Type(O) is not Object throw a TypeError exception. + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // 1. If Type(O) is not Object, return O. JSValue obj = exec->argument(0); if (!obj.isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object.freeze can only be called on Objects."))); + return JSValue::encode(obj); JSObject* object = asObject(obj); - if (isJSFinalObject(object) && !hasIndexedProperties(object->structure()->indexingType())) { - object->freeze(exec->vm()); + if (isJSFinalObject(object)) { + object->seal(vm); return JSValue::encode(obj); } - // 2. For each named own property name P of O, - PropertyNameArray properties(exec); - object->methodTable()->getOwnPropertyNames(object, exec, properties, IncludeDontEnumProperties); - PropertyNameArray::const_iterator end = properties.end(); - for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { - // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. - PropertyDescriptor desc; - if (!object->getOwnPropertyDescriptor(exec, *iter, desc)) - continue; - // b. If IsDataDescriptor(desc) is true, then - // i. If desc.[[Writable]] is true, set desc.[[Writable]] to false. - if (desc.isDataDescriptor()) - desc.setWritable(false); - // c. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false. - desc.setConfigurable(false); - // d. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments. - object->methodTable()->defineOwnProperty(object, exec, *iter, desc, true); - if (exec->hadException()) - return JSValue::encode(obj); + bool success = setIntegrityLevel(exec, vm, object); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + if (UNLIKELY(!success)) { + throwTypeError(exec, scope, ASCIILiteral("Unable to prevent extension in Object.seal")); + return encodedJSValue(); } - // 3. Set the [[Extensible]] internal property of O to false. - object->preventExtensions(exec->vm()); - - // 4. Return O. return JSValue::encode(obj); } -EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec) +JSObject* objectConstructorFreeze(ExecState* exec, JSObject* object) { + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (isJSFinalObject(object) && !hasIndexedProperties(object->indexingType())) { + object->freeze(vm); + return object; + } + + bool success = setIntegrityLevel(exec, vm, object); + RETURN_IF_EXCEPTION(scope, nullptr); + if (!success) + return throwTypeError(exec, scope, ASCIILiteral("Unable to prevent extension in Object.freeze")); + return object; +} + +EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + // 1. If Type(O) is not Object, return O. JSValue obj = exec->argument(0); if (!obj.isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object.preventExtensions can only be called on Objects."))); - asObject(obj)->preventExtensions(exec->vm()); - return JSValue::encode(obj); + return JSValue::encode(obj); + JSObject* result = objectConstructorFreeze(exec, asObject(obj)); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + return JSValue::encode(result); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec) +{ + VM& vm = exec->vm(); + JSValue argument = exec->argument(0); + if (!argument.isObject()) + return JSValue::encode(argument); + JSObject* object = asObject(argument); + object->methodTable(vm)->preventExtensions(object, exec); + return JSValue::encode(object); } EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec) { - // 1. If Type(O) is not Object throw a TypeError exception. + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // 1. If Type(O) is not Object, return true. JSValue obj = exec->argument(0); if (!obj.isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object.isSealed can only be called on Objects."))); + return JSValue::encode(jsBoolean(true)); JSObject* object = asObject(obj); if (isJSFinalObject(object)) - return JSValue::encode(jsBoolean(object->isSealed(exec->vm()))); + return JSValue::encode(jsBoolean(object->isSealed(vm))); // 2. For each named own property name P of O, - PropertyNameArray properties(exec); - object->methodTable()->getOwnPropertyNames(object, exec, properties, IncludeDontEnumProperties); + PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols); + object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include)); + RETURN_IF_EXCEPTION(scope, { }); PropertyNameArray::const_iterator end = properties.end(); for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { + Identifier propertyName = *iter; + if (vm.propertyNames->isPrivateName(propertyName)) + continue; // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. PropertyDescriptor desc; - if (!object->getOwnPropertyDescriptor(exec, *iter, desc)) + bool didGetDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc); + RETURN_IF_EXCEPTION(scope, { }); + if (!didGetDescriptor) continue; // b. If desc.[[Configurable]] is true, then return false. if (desc.configurable()) @@ -498,28 +650,39 @@ EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec) // 3. If the [[Extensible]] internal property of O is false, then return true. // 4. Otherwise, return false. - return JSValue::encode(jsBoolean(!object->isExtensible())); + bool isExtensible = object->isExtensible(exec); + RETURN_IF_EXCEPTION(scope, { }); + return JSValue::encode(jsBoolean(!isExtensible)); } EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec) { - // 1. If Type(O) is not Object throw a TypeError exception. + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // 1. If Type(O) is not Object, return true. JSValue obj = exec->argument(0); if (!obj.isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object.isFrozen can only be called on Objects."))); + return JSValue::encode(jsBoolean(true)); JSObject* object = asObject(obj); if (isJSFinalObject(object)) - return JSValue::encode(jsBoolean(object->isFrozen(exec->vm()))); + return JSValue::encode(jsBoolean(object->isFrozen(vm))); // 2. For each named own property name P of O, - PropertyNameArray properties(exec); - object->methodTable()->getOwnPropertyNames(object, exec, properties, IncludeDontEnumProperties); + PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols); + object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include)); + RETURN_IF_EXCEPTION(scope, { }); PropertyNameArray::const_iterator end = properties.end(); for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { + Identifier propertyName = *iter; + if (vm.propertyNames->isPrivateName(propertyName)) + continue; // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. PropertyDescriptor desc; - if (!object->getOwnPropertyDescriptor(exec, *iter, desc)) + bool didGetDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc); + RETURN_IF_EXCEPTION(scope, { }); + if (!didGetDescriptor) continue; // b. If IsDataDescriptor(desc) is true then // i. If desc.[[Writable]] is true, return false. c. If desc.[[Configurable]] is true, then return false. @@ -529,15 +692,91 @@ EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec) // 3. If the [[Extensible]] internal property of O is false, then return true. // 4. Otherwise, return false. - return JSValue::encode(jsBoolean(!object->isExtensible())); + bool isExtensible = object->isExtensible(exec); + RETURN_IF_EXCEPTION(scope, { }); + return JSValue::encode(jsBoolean(!isExtensible)); } EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec) { + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); JSValue obj = exec->argument(0); if (!obj.isObject()) - return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object.isExtensible can only be called on Objects."))); - return JSValue::encode(jsBoolean(asObject(obj)->isExtensible())); + return JSValue::encode(jsBoolean(false)); + JSObject* object = asObject(obj); + bool isExtensible = object->isExtensible(exec); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + return JSValue::encode(jsBoolean(isExtensible)); +} + +EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState* exec) +{ + return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1)))); +} + +// FIXME: Use the enumeration cache. +JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + PropertyNameArray properties(exec, propertyNameMode); + object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode)); + RETURN_IF_EXCEPTION(scope, nullptr); + + JSArray* keys = constructEmptyArray(exec, 0); + RETURN_IF_EXCEPTION(scope, nullptr); + + switch (propertyNameMode) { + case PropertyNameMode::Strings: { + size_t numProperties = properties.size(); + for (size_t i = 0; i < numProperties; i++) { + const auto& identifier = properties[i]; + ASSERT(!identifier.isSymbol()); + keys->push(exec, jsOwnedString(exec, identifier.string())); + RETURN_IF_EXCEPTION(scope, nullptr); + } + break; + } + + case PropertyNameMode::Symbols: { + size_t numProperties = properties.size(); + for (size_t i = 0; i < numProperties; i++) { + const auto& identifier = properties[i]; + ASSERT(identifier.isSymbol()); + if (!vm.propertyNames->isPrivateName(identifier)) { + keys->push(exec, Symbol::create(vm, static_cast(*identifier.impl()))); + RETURN_IF_EXCEPTION(scope, nullptr); + } + } + break; + } + + case PropertyNameMode::StringsAndSymbols: { + Vector propertySymbols; + size_t numProperties = properties.size(); + for (size_t i = 0; i < numProperties; i++) { + const auto& identifier = properties[i]; + if (identifier.isSymbol()) { + if (!vm.propertyNames->isPrivateName(identifier)) + propertySymbols.append(identifier); + } else { + keys->push(exec, jsOwnedString(exec, identifier.string())); + RETURN_IF_EXCEPTION(scope, nullptr); + } + } + + // To ensure the order defined in the spec (9.1.12), we append symbols at the last elements of keys. + for (const auto& identifier : propertySymbols) { + keys->push(exec, Symbol::create(vm, static_cast(*identifier.impl()))); + RETURN_IF_EXCEPTION(scope, nullptr); + } + + break; + } + } + + return keys; } } // namespace JSC -- cgit v1.2.1