/ runtime / ObjectPrototype.cpp
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