ObjectPrototype.cpp
1 /* 2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 3 * Copyright (C) 2008-2020 Apple Inc. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 */ 20 21 #include "config.h" 22 #include "ObjectPrototype.h" 23 24 #include "GetterSetter.h" 25 #include "HasOwnPropertyCache.h" 26 #include "IntegrityInlines.h" 27 #include "JSCInlines.h" 28 #include "PropertySlot.h" 29 30 namespace JSC { 31 32 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncValueOf); 33 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncHasOwnProperty); 34 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncIsPrototypeOf); 35 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncDefineGetter); 36 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncDefineSetter); 37 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncLookupGetter); 38 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncLookupSetter); 39 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncPropertyIsEnumerable); 40 static JSC_DECLARE_HOST_FUNCTION(objectProtoFuncToLocaleString); 41 42 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectPrototype); 43 44 const ClassInfo ObjectPrototype::s_info = { "Object", &JSNonFinalObject::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ObjectPrototype) }; 45 46 ObjectPrototype::ObjectPrototype(VM& vm, Structure* stucture) 47 : JSNonFinalObject(vm, stucture) 48 { 49 } 50 51 void ObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) 52 { 53 Base::finishCreation(vm); 54 ASSERT(inherits(vm, info())); 55 56 putDirectWithoutTransition(vm, vm.propertyNames->toString, globalObject->objectProtoToStringFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum)); 57 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, objectProtoFuncToLocaleString, static_cast<unsigned>(PropertyAttribute::DontEnum), 0); 58 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, objectProtoFuncValueOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 0); 59 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->hasOwnProperty, objectProtoFuncHasOwnProperty, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, HasOwnPropertyIntrinsic); 60 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable, static_cast<unsigned>(PropertyAttribute::DontEnum), 1); 61 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->isPrototypeOf, objectProtoFuncIsPrototypeOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1); 62 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__defineGetter__, objectProtoFuncDefineGetter, static_cast<unsigned>(PropertyAttribute::DontEnum), 2); 63 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__defineSetter__, objectProtoFuncDefineSetter, static_cast<unsigned>(PropertyAttribute::DontEnum), 2); 64 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__lookupGetter__, objectProtoFuncLookupGetter, static_cast<unsigned>(PropertyAttribute::DontEnum), 1); 65 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__lookupSetter__, objectProtoFuncLookupSetter, static_cast<unsigned>(PropertyAttribute::DontEnum), 1); 66 } 67 68 ObjectPrototype* ObjectPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) 69 { 70 ObjectPrototype* prototype = new (NotNull, allocateCell<ObjectPrototype>(vm.heap)) ObjectPrototype(vm, structure); 71 prototype->finishCreation(vm, globalObject); 72 return prototype; 73 } 74 75 // ------------------------------ Functions -------------------------------- 76 77 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncValueOf, (JSGlobalObject* globalObject, CallFrame* callFrame)) 78 { 79 JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()); 80 JSObject* valueObj = thisValue.toObject(globalObject); 81 if (UNLIKELY(!valueObj)) 82 return encodedJSValue(); 83 Integrity::auditStructureID(globalObject->vm(), valueObj->structureID()); 84 return JSValue::encode(valueObj); 85 } 86 87 bool objectPrototypeHasOwnProperty(JSGlobalObject* globalObject, JSValue base, const Identifier& propertyName) 88 { 89 VM& vm = globalObject->vm(); 90 auto scope = DECLARE_THROW_SCOPE(vm); 91 JSValue thisValue = base.toThis(globalObject, ECMAMode::strict()); 92 JSObject* thisObject = thisValue.toObject(globalObject); 93 EXCEPTION_ASSERT(!!scope.exception() == !thisObject); 94 if (UNLIKELY(!thisObject)) 95 return false; 96 97 Structure* structure = thisObject->structure(vm); 98 HasOwnPropertyCache* hasOwnPropertyCache = vm.ensureHasOwnPropertyCache(); 99 if (Optional<bool> result = hasOwnPropertyCache->get(structure, propertyName)) { 100 ASSERT(*result == thisObject->hasOwnProperty(globalObject, propertyName)); 101 scope.assertNoException(); 102 return *result; 103 } 104 105 PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty); 106 bool result = thisObject->hasOwnProperty(globalObject, propertyName, slot); 107 RETURN_IF_EXCEPTION(scope, false); 108 109 hasOwnPropertyCache->tryAdd(vm, slot, thisObject, propertyName, result); 110 return result; 111 } 112 113 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncHasOwnProperty, (JSGlobalObject* globalObject, CallFrame* callFrame)) 114 { 115 VM& vm = globalObject->vm(); 116 auto scope = DECLARE_THROW_SCOPE(vm); 117 118 JSValue base = callFrame->thisValue(); 119 auto propertyName = callFrame->argument(0).toPropertyKey(globalObject); 120 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 121 scope.release(); 122 return JSValue::encode(jsBoolean(objectPrototypeHasOwnProperty(globalObject, base, propertyName))); 123 } 124 125 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncIsPrototypeOf, (JSGlobalObject* globalObject, CallFrame* callFrame)) 126 { 127 VM& vm = globalObject->vm(); 128 auto scope = DECLARE_THROW_SCOPE(vm); 129 130 if (!callFrame->argument(0).isObject()) 131 return JSValue::encode(jsBoolean(false)); 132 133 JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()); 134 JSObject* thisObj = thisValue.toObject(globalObject); 135 EXCEPTION_ASSERT(!!scope.exception() == !thisObj); 136 if (UNLIKELY(!thisObj)) 137 return encodedJSValue(); 138 139 JSValue v = asObject(callFrame->argument(0))->getPrototype(vm, globalObject); 140 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 141 142 while (true) { 143 if (!v.isObject()) 144 return JSValue::encode(jsBoolean(false)); 145 if (v == thisObj) 146 return JSValue::encode(jsBoolean(true)); 147 v = asObject(v)->getPrototype(vm, globalObject); 148 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 149 } 150 } 151 152 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncDefineGetter, (JSGlobalObject* globalObject, CallFrame* callFrame)) 153 { 154 VM& vm = globalObject->vm(); 155 auto scope = DECLARE_THROW_SCOPE(vm); 156 157 JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject); 158 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 159 160 JSValue get = callFrame->argument(1); 161 if (!get.isCallable(vm)) 162 return throwVMTypeError(globalObject, scope, "invalid getter usage"_s); 163 164 auto propertyName = callFrame->argument(0).toPropertyKey(globalObject); 165 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 166 167 PropertyDescriptor descriptor; 168 descriptor.setGetter(get); 169 descriptor.setEnumerable(true); 170 descriptor.setConfigurable(true); 171 172 bool shouldThrow = true; 173 scope.release(); 174 thisObject->methodTable(vm)->defineOwnProperty(thisObject, globalObject, propertyName, descriptor, shouldThrow); 175 176 return JSValue::encode(jsUndefined()); 177 } 178 179 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncDefineSetter, (JSGlobalObject* globalObject, CallFrame* callFrame)) 180 { 181 VM& vm = globalObject->vm(); 182 auto scope = DECLARE_THROW_SCOPE(vm); 183 184 JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject); 185 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 186 187 JSValue set = callFrame->argument(1); 188 if (!set.isCallable(vm)) 189 return throwVMTypeError(globalObject, scope, "invalid setter usage"_s); 190 191 auto propertyName = callFrame->argument(0).toPropertyKey(globalObject); 192 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 193 194 PropertyDescriptor descriptor; 195 descriptor.setSetter(set); 196 descriptor.setEnumerable(true); 197 descriptor.setConfigurable(true); 198 199 bool shouldThrow = true; 200 scope.release(); 201 thisObject->methodTable(vm)->defineOwnProperty(thisObject, globalObject, propertyName, descriptor, shouldThrow); 202 203 return JSValue::encode(jsUndefined()); 204 } 205 206 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncLookupGetter, (JSGlobalObject* globalObject, CallFrame* callFrame)) 207 { 208 VM& vm = globalObject->vm(); 209 auto scope = DECLARE_THROW_SCOPE(vm); 210 211 JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject); 212 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 213 214 auto propertyName = callFrame->argument(0).toPropertyKey(globalObject); 215 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 216 217 PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty); 218 bool hasProperty = thisObject->getPropertySlot(globalObject, propertyName, slot); 219 EXCEPTION_ASSERT(!scope.exception() || !hasProperty); 220 if (hasProperty) { 221 if (slot.isAccessor()) { 222 GetterSetter* getterSetter = slot.getterSetter(); 223 return getterSetter->isGetterNull() ? JSValue::encode(jsUndefined()) : JSValue::encode(getterSetter->getter()); 224 } 225 if (slot.attributes() & PropertyAttribute::CustomAccessor) { 226 PropertyDescriptor descriptor; 227 ASSERT(slot.slotBase()); 228 if (slot.slotBase()->getOwnPropertyDescriptor(globalObject, propertyName, descriptor)) 229 return descriptor.getterPresent() ? JSValue::encode(descriptor.getter()) : JSValue::encode(jsUndefined()); 230 } 231 } 232 233 return JSValue::encode(jsUndefined()); 234 } 235 236 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncLookupSetter, (JSGlobalObject* globalObject, CallFrame* callFrame)) 237 { 238 VM& vm = globalObject->vm(); 239 auto scope = DECLARE_THROW_SCOPE(vm); 240 241 JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject); 242 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 243 244 auto propertyName = callFrame->argument(0).toPropertyKey(globalObject); 245 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 246 247 PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty); 248 bool hasProperty = thisObject->getPropertySlot(globalObject, propertyName, slot); 249 EXCEPTION_ASSERT(!scope.exception() || !hasProperty); 250 if (hasProperty) { 251 if (slot.isAccessor()) { 252 GetterSetter* getterSetter = slot.getterSetter(); 253 return getterSetter->isSetterNull() ? JSValue::encode(jsUndefined()) : JSValue::encode(getterSetter->setter()); 254 } 255 if (slot.attributes() & PropertyAttribute::CustomAccessor) { 256 PropertyDescriptor descriptor; 257 ASSERT(slot.slotBase()); 258 if (slot.slotBase()->getOwnPropertyDescriptor(globalObject, propertyName, descriptor)) 259 return descriptor.setterPresent() ? JSValue::encode(descriptor.setter()) : JSValue::encode(jsUndefined()); 260 } 261 } 262 263 return JSValue::encode(jsUndefined()); 264 } 265 266 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncPropertyIsEnumerable, (JSGlobalObject* globalObject, CallFrame* callFrame)) 267 { 268 VM& vm = globalObject->vm(); 269 auto scope = DECLARE_THROW_SCOPE(vm); 270 271 auto propertyName = callFrame->argument(0).toPropertyKey(globalObject); 272 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 273 274 JSObject* thisObject = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()).toObject(globalObject); 275 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 276 277 scope.release(); 278 PropertyDescriptor descriptor; 279 bool enumerable = thisObject->getOwnPropertyDescriptor(globalObject, propertyName, descriptor) && descriptor.enumerable(); 280 return JSValue::encode(jsBoolean(enumerable)); 281 } 282 283 // 15.2.4.3 Object.prototype.toLocaleString() 284 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncToLocaleString, (JSGlobalObject* globalObject, CallFrame* callFrame)) 285 { 286 VM& vm = globalObject->vm(); 287 auto scope = DECLARE_THROW_SCOPE(vm); 288 289 // 1. Let V be the this value. 290 JSValue thisValue = callFrame->thisValue(); 291 292 // 2. Invoke(V, "toString") 293 294 // Let O be the result of calling ToObject passing the this value as the argument. 295 JSObject* object = thisValue.toThis(globalObject, ECMAMode::strict()).toObject(globalObject); 296 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 297 298 // Let toString be the O.[[Get]]("toString", V) 299 PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get); 300 bool hasProperty = object->getPropertySlot(globalObject, vm.propertyNames->toString, slot); 301 EXCEPTION_ASSERT(!scope.exception() || !hasProperty); 302 JSValue toString = hasProperty ? slot.getValue(globalObject, vm.propertyNames->toString) : jsUndefined(); 303 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 304 305 // If IsCallable(toString) is false, throw a TypeError exception. 306 auto callData = getCallData(vm, toString); 307 if (callData.type == CallData::Type::None) 308 return throwVMTypeError(globalObject, scope); 309 310 // Return the result of calling the [[Call]] internal method of toString passing the this value and no arguments. 311 RELEASE_AND_RETURN(scope, JSValue::encode(call(globalObject, toString, callData, thisValue, *vm.emptyList))); 312 } 313 314 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncToString, (JSGlobalObject* globalObject, CallFrame* callFrame)) 315 { 316 VM& vm = globalObject->vm(); 317 auto scope = DECLARE_THROW_SCOPE(vm); 318 319 JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict()); 320 if (thisValue.isUndefinedOrNull()) 321 return JSValue::encode(thisValue.isUndefined() ? vm.smallStrings.undefinedObjectString() : vm.smallStrings.nullObjectString()); 322 JSObject* thisObject = thisValue.toObject(globalObject); 323 EXCEPTION_ASSERT(!!scope.exception() == !thisObject); 324 if (!thisObject) 325 return JSValue::encode(jsUndefined()); 326 327 Integrity::auditStructureID(vm, thisObject->structureID()); 328 auto result = thisObject->structure(vm)->cachedSpecialProperty(CachedSpecialPropertyKey::ToStringTag); 329 if (result) 330 return JSValue::encode(result); 331 332 String tag = thisObject->methodTable(vm)->toStringName(thisObject, globalObject); 333 RETURN_IF_EXCEPTION(scope, { }); 334 JSString* jsTag = nullptr; 335 336 PropertySlot slot(thisObject, PropertySlot::InternalMethodType::Get); 337 bool hasProperty = thisObject->getPropertySlot(globalObject, vm.propertyNames->toStringTagSymbol, slot); 338 EXCEPTION_ASSERT(!scope.exception() || !hasProperty); 339 if (hasProperty) { 340 JSValue tagValue = slot.getValue(globalObject, vm.propertyNames->toStringTagSymbol); 341 RETURN_IF_EXCEPTION(scope, { }); 342 if (tagValue.isString()) 343 jsTag = asString(tagValue); 344 } 345 346 if (!jsTag) { 347 ASSERT_WITH_MESSAGE(tag.length() > 1, "toStringName() should return strings two or more characters long."); 348 jsTag = jsNontrivialString(vm, WTFMove(tag)); 349 } 350 351 JSString* jsResult = jsString(globalObject, vm.smallStrings.objectStringStart(), jsTag, vm.smallStrings.singleCharacterString(']')); 352 RETURN_IF_EXCEPTION(scope, { }); 353 thisObject->structure(vm)->cacheSpecialProperty(globalObject, vm, jsResult, CachedSpecialPropertyKey::ToStringTag, slot); 354 return JSValue::encode(jsResult); 355 } 356 357 } // namespace JSC