/ API / JSCallbackObjectFunctions.h
JSCallbackObjectFunctions.h
  1  /*
  2   * Copyright (C) 2006-2020 Apple Inc. All rights reserved.
  3   * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
  4   *
  5   * Redistribution and use in source and binary forms, with or without
  6   * modification, are permitted provided that the following conditions
  7   * are met:
  8   * 1. Redistributions of source code must retain the above copyright
  9   *    notice, this list of conditions and the following disclaimer.
 10   * 2. Redistributions in binary form must reproduce the above copyright
 11   *    notice, this list of conditions and the following disclaimer in the
 12   *    documentation and/or other materials provided with the distribution.
 13   *
 14   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 15   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 16   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 17   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 18   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 19   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 20   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 21   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 22   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 23   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 24   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 25   */
 26  
 27  #pragma once
 28  
 29  #include "APICast.h"
 30  #include "Error.h"
 31  #include "ExceptionHelpers.h"
 32  #include "JSCallbackFunction.h"
 33  #include "JSClassRef.h"
 34  #include "JSFunction.h"
 35  #include "JSGlobalObject.h"
 36  #include "JSLock.h"
 37  #include "JSObjectRef.h"
 38  #include "JSString.h"
 39  #include "OpaqueJSString.h"
 40  #include "PropertyNameArray.h"
 41  #include <wtf/Vector.h>
 42  
 43  namespace JSC {
 44  
 45  template <class Parent>
 46  inline JSCallbackObject<Parent>* JSCallbackObject<Parent>::asCallbackObject(JSValue value)
 47  {
 48      ASSERT(asObject(value)->inherits(value.getObject()->vm(), info()));
 49      return jsCast<JSCallbackObject*>(asObject(value));
 50  }
 51  
 52  template <class Parent>
 53  inline JSCallbackObject<Parent>* JSCallbackObject<Parent>::asCallbackObject(EncodedJSValue encodedValue)
 54  {
 55      JSValue value = JSValue::decode(encodedValue);
 56      ASSERT(asObject(value)->inherits(value.getObject()->vm(), info()));
 57      return jsCast<JSCallbackObject*>(asObject(value));
 58  }
 59  
 60  template <class Parent>
 61  JSCallbackObject<Parent>::JSCallbackObject(JSGlobalObject* globalObject, Structure* structure, JSClassRef jsClass, void* data)
 62      : Parent(getVM(globalObject), structure)
 63      , m_callbackObjectData(makeUnique<JSCallbackObjectData>(data, jsClass))
 64  {
 65  }
 66  
 67  // Global object constructor.
 68  // FIXME: Move this into a separate JSGlobalCallbackObject class derived from this one.
 69  template <class Parent>
 70  JSCallbackObject<Parent>::JSCallbackObject(VM& vm, JSClassRef jsClass, Structure* structure)
 71      : Parent(vm, structure)
 72      , m_callbackObjectData(makeUnique<JSCallbackObjectData>(nullptr, jsClass))
 73  {
 74  }
 75  
 76  template <class Parent>
 77  JSCallbackObject<Parent>::~JSCallbackObject()
 78  {
 79      VM& vm = this->HeapCell::vm();
 80      vm.currentlyDestructingCallbackObject = this;
 81      ASSERT(m_classInfo);
 82      vm.currentlyDestructingCallbackObjectClassInfo = m_classInfo;
 83      JSObjectRef thisRef = toRef(static_cast<JSObject*>(this));
 84      for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
 85          if (JSObjectFinalizeCallback finalize = jsClass->finalize)
 86              finalize(thisRef);
 87      }
 88      vm.currentlyDestructingCallbackObject = nullptr;
 89      vm.currentlyDestructingCallbackObjectClassInfo = nullptr;
 90  }
 91      
 92  template <class Parent>
 93  void JSCallbackObject<Parent>::finishCreation(JSGlobalObject* globalObject)
 94  {
 95      VM& vm = getVM(globalObject);
 96      Base::finishCreation(vm);
 97      ASSERT(Parent::inherits(vm, info()));
 98      init(globalObject);
 99  }
100  
101  // This is just for Global object, so we can assume that Base::finishCreation is JSGlobalObject::finishCreation.
102  template <class Parent>
103  void JSCallbackObject<Parent>::finishCreation(VM& vm)
104  {
105      ASSERT(Parent::inherits(vm, info()));
106      ASSERT(Parent::isGlobalObject());
107      Base::finishCreation(vm);
108      init(jsCast<JSGlobalObject*>(this));
109  }
110  
111  template <class Parent>
112  void JSCallbackObject<Parent>::init(JSGlobalObject* globalObject)
113  {
114      ASSERT(globalObject);
115      
116      Vector<JSObjectInitializeCallback, 16> initRoutines;
117      JSClassRef jsClass = classRef();
118      do {
119          if (JSObjectInitializeCallback initialize = jsClass->initialize)
120              initRoutines.append(initialize);
121      } while ((jsClass = jsClass->parentClass));
122      
123      // initialize from base to derived
124      for (int i = static_cast<int>(initRoutines.size()) - 1; i >= 0; i--) {
125          JSLock::DropAllLocks dropAllLocks(globalObject);
126          JSObjectInitializeCallback initialize = initRoutines[i];
127          initialize(toRef(globalObject), toRef(jsCast<JSObject*>(this)));
128      }
129      
130      m_classInfo = this->classInfo(getVM(globalObject));
131  }
132  
133  template <class Parent>
134  String JSCallbackObject<Parent>::className(const JSObject* object, VM& vm)
135  {
136      const JSCallbackObject* thisObject = jsCast<const JSCallbackObject*>(object);
137      String thisClassName = thisObject->classRef()->className();
138      if (!thisClassName.isEmpty())
139          return thisClassName;
140      
141      return Parent::className(object, vm);
142  }
143  
144  template <class Parent>
145  String JSCallbackObject<Parent>::toStringName(const JSObject* object, JSGlobalObject* globalObject)
146  {
147      VM& vm = getVM(globalObject);
148      const ClassInfo* info = object->classInfo(vm);
149      ASSERT(info);
150      return info->methodTable.className(object, vm);
151  }
152  
153  template <class Parent>
154  bool JSCallbackObject<Parent>::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
155  {
156      VM& vm = getVM(globalObject);
157      auto scope = DECLARE_THROW_SCOPE(vm);
158  
159      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object);
160      JSContextRef ctx = toRef(globalObject);
161      JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject));
162      RefPtr<OpaqueJSString> propertyNameRef;
163      
164      if (StringImpl* name = propertyName.uid()) {
165          unsigned attributes = PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum;
166          for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
167              // optional optimization to bypass getProperty in cases when we only need to know if the property exists
168              if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) {
169                  if (!propertyNameRef)
170                      propertyNameRef = OpaqueJSString::tryCreate(name);
171                  JSLock::DropAllLocks dropAllLocks(globalObject);
172                  if (hasProperty(ctx, thisRef, propertyNameRef.get())) {
173                      slot.setCustom(thisObject, attributes, getCallbackGetter());
174                      return true;
175                  }
176              } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) {
177                  if (!propertyNameRef)
178                      propertyNameRef = OpaqueJSString::tryCreate(name);
179                  JSValueRef exception = nullptr;
180                  JSValueRef value;
181                  {
182                      JSLock::DropAllLocks dropAllLocks(globalObject);
183                      value = getProperty(ctx, thisRef, propertyNameRef.get(), &exception);
184                  }
185                  if (exception) {
186                      throwException(globalObject, scope, toJS(globalObject, exception));
187                      slot.setValue(thisObject, attributes, jsUndefined());
188                      return true;
189                  }
190                  if (value) {
191                      slot.setValue(thisObject, attributes, toJS(globalObject, value));
192                      return true;
193                  }
194              }
195              
196              if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) {
197                  if (staticValues->contains(name)) {
198                      JSValue value = thisObject->getStaticValue(globalObject, propertyName);
199                      RETURN_IF_EXCEPTION(scope, false);
200                      if (value) {
201                          slot.setValue(thisObject, attributes, value);
202                          return true;
203                      }
204                  }
205              }
206              
207              if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) {
208                  if (staticFunctions->contains(name)) {
209                      slot.setCustom(thisObject, attributes, getStaticFunctionGetter());
210                      return true;
211                  }
212              }
213          }
214      }
215  
216      RELEASE_AND_RETURN(scope, Parent::getOwnPropertySlot(thisObject, globalObject, propertyName, slot));
217  }
218  
219  template <class Parent>
220  bool JSCallbackObject<Parent>::getOwnPropertySlotByIndex(JSObject* object, JSGlobalObject* globalObject, unsigned propertyName, PropertySlot& slot)
221  {
222      VM& vm = getVM(globalObject);
223      return object->methodTable(vm)->getOwnPropertySlot(object, globalObject, Identifier::from(vm, propertyName), slot);
224  }
225  
226  template <class Parent>
227  JSValue JSCallbackObject<Parent>::defaultValue(const JSObject* object, JSGlobalObject* globalObject, PreferredPrimitiveType hint)
228  {
229      VM& vm = getVM(globalObject);
230      auto scope = DECLARE_THROW_SCOPE(vm);
231  
232      const JSCallbackObject* thisObject = jsCast<const JSCallbackObject*>(object);
233      JSContextRef ctx = toRef(globalObject);
234      JSObjectRef thisRef = toRef(jsCast<const JSObject*>(thisObject));
235      ::JSType jsHint = hint == PreferString ? kJSTypeString : kJSTypeNumber;
236  
237      for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
238          if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) {
239              JSValueRef exception = nullptr;
240              JSValueRef result = convertToType(ctx, thisRef, jsHint, &exception);
241              if (exception) {
242                  throwException(globalObject, scope, toJS(globalObject, exception));
243                  return jsUndefined();
244              }
245              if (result)
246                  return toJS(globalObject, result);
247          }
248      }
249      
250      RELEASE_AND_RETURN(scope, Parent::defaultValue(object, globalObject, hint));
251  }
252  
253  template <class Parent>
254  bool JSCallbackObject<Parent>::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
255  {
256      VM& vm = getVM(globalObject);
257      auto scope = DECLARE_THROW_SCOPE(vm);
258  
259      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell);
260      JSContextRef ctx = toRef(globalObject);
261      JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject));
262      RefPtr<OpaqueJSString> propertyNameRef;
263      JSValueRef valueRef = toRef(globalObject, value);
264      
265      if (StringImpl* name = propertyName.uid()) {
266          for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
267              if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) {
268                  if (!propertyNameRef)
269                      propertyNameRef = OpaqueJSString::tryCreate(name);
270                  JSValueRef exception = nullptr;
271                  bool result;
272                  {
273                      JSLock::DropAllLocks dropAllLocks(globalObject);
274                      result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception);
275                  }
276                  if (exception)
277                      throwException(globalObject, scope, toJS(globalObject, exception));
278                  if (result || exception)
279                      return result;
280              }
281              
282              if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) {
283                  if (StaticValueEntry* entry = staticValues->get(name)) {
284                      if (entry->attributes & kJSPropertyAttributeReadOnly)
285                          return false;
286                      if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
287                          JSValueRef exception = nullptr;
288                          bool result;
289                          {
290                              JSLock::DropAllLocks dropAllLocks(globalObject);
291                              result = setProperty(ctx, thisRef, entry->propertyNameRef.get(), valueRef, &exception);
292                          }
293                          if (exception)
294                              throwException(globalObject, scope, toJS(globalObject, exception));
295                          if (result || exception)
296                              return result;
297                      }
298                  }
299              }
300              
301              if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) {
302                  if (StaticFunctionEntry* entry = staticFunctions->get(name)) {
303                      PropertySlot getSlot(thisObject, PropertySlot::InternalMethodType::VMInquiry, &vm);
304                      bool found = Parent::getOwnPropertySlot(thisObject, globalObject, propertyName, getSlot);
305                      RETURN_IF_EXCEPTION(scope, false);
306                      getSlot.disallowVMEntry.reset();
307                      if (found)
308                          RELEASE_AND_RETURN(scope, Parent::put(thisObject, globalObject, propertyName, value, slot));
309                      if (entry->attributes & kJSPropertyAttributeReadOnly)
310                          return false;
311                      return thisObject->JSCallbackObject<Parent>::putDirect(vm, propertyName, value); // put as override property
312                  }
313              }
314          }
315      }
316  
317      RELEASE_AND_RETURN(scope, Parent::put(thisObject, globalObject, propertyName, value, slot));
318  }
319  
320  template <class Parent>
321  bool JSCallbackObject<Parent>::putByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned propertyIndex, JSValue value, bool shouldThrow)
322  {
323      VM& vm = getVM(globalObject);
324      auto scope = DECLARE_THROW_SCOPE(vm);
325  
326      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell);
327      JSContextRef ctx = toRef(globalObject);
328      JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject));
329      RefPtr<OpaqueJSString> propertyNameRef;
330      JSValueRef valueRef = toRef(globalObject, value);
331      Identifier propertyName = Identifier::from(vm, propertyIndex);
332  
333      for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
334          if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) {
335              if (!propertyNameRef)
336                  propertyNameRef = OpaqueJSString::tryCreate(propertyName.impl());
337              JSValueRef exception = nullptr;
338              bool result;
339              {
340                  JSLock::DropAllLocks dropAllLocks(globalObject);
341                  result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception);
342              }
343              if (exception)
344                  throwException(globalObject, scope, toJS(globalObject, exception));
345              if (result || exception)
346                  return result;
347          }
348  
349          if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) {
350              if (StaticValueEntry* entry = staticValues->get(propertyName.impl())) {
351                  if (entry->attributes & kJSPropertyAttributeReadOnly)
352                      return false;
353                  if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
354                      JSValueRef exception = nullptr;
355                      bool result;
356                      {
357                          JSLock::DropAllLocks dropAllLocks(globalObject);
358                          result = setProperty(ctx, thisRef, entry->propertyNameRef.get(), valueRef, &exception);
359                      }
360                      if (exception)
361                          throwException(globalObject, scope, toJS(globalObject, exception));
362                      if (result || exception)
363                          return result;
364                  }
365              }
366          }
367  
368          if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) {
369              if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.impl())) {
370                  if (entry->attributes & kJSPropertyAttributeReadOnly)
371                      return false;
372                  break;
373              }
374          }
375      }
376  
377      RELEASE_AND_RETURN(scope, Parent::putByIndex(thisObject, globalObject, propertyIndex, value, shouldThrow));
378  }
379  
380  template <class Parent>
381  bool JSCallbackObject<Parent>::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, DeletePropertySlot& slot)
382  {
383      VM& vm = getVM(globalObject);
384      auto scope = DECLARE_THROW_SCOPE(vm);
385  
386      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell);
387      JSContextRef ctx = toRef(globalObject);
388      JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject));
389      RefPtr<OpaqueJSString> propertyNameRef;
390      
391      if (StringImpl* name = propertyName.uid()) {
392          for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
393              if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) {
394                  if (!propertyNameRef)
395                      propertyNameRef = OpaqueJSString::tryCreate(name);
396                  JSValueRef exception = nullptr;
397                  bool result;
398                  {
399                      JSLock::DropAllLocks dropAllLocks(globalObject);
400                      result = deleteProperty(ctx, thisRef, propertyNameRef.get(), &exception);
401                  }
402                  if (exception)
403                      throwException(globalObject, scope, toJS(globalObject, exception));
404                  if (result || exception)
405                      return true;
406              }
407              
408              if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) {
409                  if (StaticValueEntry* entry = staticValues->get(name)) {
410                      if (entry->attributes & kJSPropertyAttributeDontDelete)
411                          return false;
412                      return true;
413                  }
414              }
415              
416              if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) {
417                  if (StaticFunctionEntry* entry = staticFunctions->get(name)) {
418                      if (entry->attributes & kJSPropertyAttributeDontDelete)
419                          return false;
420                      return true;
421                  }
422              }
423          }
424      }
425  
426      static_assert(std::is_final_v<JSCallbackObject<Parent>>, "Ensure no derived classes have custom deletePropertyByIndex implementation");
427      if (Optional<uint32_t> index = parseIndex(propertyName))
428          RELEASE_AND_RETURN(scope, Parent::deletePropertyByIndex(thisObject, globalObject, index.value()));
429      RELEASE_AND_RETURN(scope, Parent::deleteProperty(thisObject, globalObject, propertyName, slot));
430  }
431  
432  template <class Parent>
433  bool JSCallbackObject<Parent>::deletePropertyByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned propertyName)
434  {
435      VM& vm = getVM(globalObject);
436      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell);
437      return JSCell::deleteProperty(thisObject, globalObject, Identifier::from(vm, propertyName));
438  }
439  
440  template <class Parent>
441  CallData JSCallbackObject<Parent>::getConstructData(JSCell* cell)
442  {
443      CallData constructData;
444      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell);
445      for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
446          if (jsClass->callAsConstructor) {
447              constructData.type = CallData::Type::Native;
448              constructData.native.function = getConstructFunction();
449              break;
450          }
451      }
452      return constructData;
453  }
454  
455  template <class Parent>
456  EncodedJSValue JSCallbackObject<Parent>::constructImpl(JSGlobalObject* globalObject, CallFrame* callFrame)
457  {
458      VM& vm = getVM(globalObject);
459      auto scope = DECLARE_THROW_SCOPE(vm);
460  
461      JSObject* constructor = callFrame->jsCallee();
462      JSContextRef execRef = toRef(globalObject);
463      JSObjectRef constructorRef = toRef(constructor);
464      
465      for (JSClassRef jsClass = jsCast<JSCallbackObject<Parent>*>(constructor)->classRef(); jsClass; jsClass = jsClass->parentClass) {
466          if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) {
467              size_t argumentCount = callFrame->argumentCount();
468              Vector<JSValueRef, 16> arguments;
469              arguments.reserveInitialCapacity(argumentCount);
470              for (size_t i = 0; i < argumentCount; ++i)
471                  arguments.uncheckedAppend(toRef(globalObject, callFrame->uncheckedArgument(i)));
472              JSValueRef exception = nullptr;
473              JSObject* result;
474              {
475                  JSLock::DropAllLocks dropAllLocks(globalObject);
476                  result = toJS(callAsConstructor(execRef, constructorRef, argumentCount, arguments.data(), &exception));
477              }
478              if (exception) {
479                  throwException(globalObject, scope, toJS(globalObject, exception));
480                  return JSValue::encode(jsUndefined());
481              }
482              return JSValue::encode(result);
483          }
484      }
485      
486      RELEASE_ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here
487      return JSValue::encode(JSValue());
488  }
489  
490  template <class Parent>
491  bool JSCallbackObject<Parent>::customHasInstance(JSObject* object, JSGlobalObject* globalObject, JSValue value)
492  {
493      VM& vm = getVM(globalObject);
494      auto scope = DECLARE_THROW_SCOPE(vm);
495  
496      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object);
497      JSContextRef execRef = toRef(globalObject);
498      JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject));
499      
500      for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
501          if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance) {
502              JSValueRef valueRef = toRef(globalObject, value);
503              JSValueRef exception = nullptr;
504              bool result;
505              {
506                  JSLock::DropAllLocks dropAllLocks(globalObject);
507                  result = hasInstance(execRef, thisRef, valueRef, &exception);
508              }
509              if (exception)
510                  throwException(globalObject, scope, toJS(globalObject, exception));
511              return result;
512          }
513      }
514      return false;
515  }
516  
517  template <class Parent>
518  CallData JSCallbackObject<Parent>::getCallData(JSCell* cell)
519  {
520      CallData callData;
521      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell);
522      for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
523          if (jsClass->callAsFunction) {
524              callData.type = CallData::Type::Native;
525              callData.native.function = getCallFunction();
526              break;
527          }
528      }
529      return callData;
530  }
531  
532  template <class Parent>
533  EncodedJSValue JSCallbackObject<Parent>::callImpl(JSGlobalObject* globalObject, CallFrame* callFrame)
534  {
535      VM& vm = getVM(globalObject);
536      auto scope = DECLARE_THROW_SCOPE(vm);
537  
538      JSContextRef execRef = toRef(globalObject);
539      JSObjectRef functionRef = toRef(callFrame->jsCallee());
540      JSObjectRef thisObjRef = toRef(jsCast<JSObject*>(callFrame->thisValue().toThis(globalObject, ECMAMode::sloppy())));
541      
542      for (JSClassRef jsClass = jsCast<JSCallbackObject<Parent>*>(toJS(functionRef))->classRef(); jsClass; jsClass = jsClass->parentClass) {
543          if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) {
544              size_t argumentCount = callFrame->argumentCount();
545              Vector<JSValueRef, 16> arguments;
546              arguments.reserveInitialCapacity(argumentCount);
547              for (size_t i = 0; i < argumentCount; ++i)
548                  arguments.uncheckedAppend(toRef(globalObject, callFrame->uncheckedArgument(i)));
549              JSValueRef exception = nullptr;
550              JSValue result;
551              {
552                  JSLock::DropAllLocks dropAllLocks(globalObject);
553                  result = toJS(globalObject, callAsFunction(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception));
554              }
555              if (exception) {
556                  throwException(globalObject, scope, toJS(globalObject, exception));
557                  return JSValue::encode(jsUndefined());
558              }
559              return JSValue::encode(result);
560          }
561      }
562      
563      RELEASE_ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here
564      return JSValue::encode(JSValue());
565  }
566  
567  template <class Parent>
568  void JSCallbackObject<Parent>::getOwnSpecialPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, DontEnumPropertiesMode mode)
569  {
570      VM& vm = getVM(globalObject);
571      JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object);
572      JSContextRef execRef = toRef(globalObject);
573      JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObject));
574      
575      for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
576          if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) {
577              JSLock::DropAllLocks dropAllLocks(globalObject);
578              getPropertyNames(execRef, thisRef, toRef(&propertyNames));
579          }
580          
581          if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) {
582              typedef OpaqueJSClassStaticValuesTable::const_iterator iterator;
583              iterator end = staticValues->end();
584              for (iterator it = staticValues->begin(); it != end; ++it) {
585                  StringImpl* name = it->key.get();
586                  StaticValueEntry* entry = it->value.get();
587                  if (entry->getProperty && (mode == DontEnumPropertiesMode::Include || !(entry->attributes & kJSPropertyAttributeDontEnum))) {
588                      ASSERT(!name->isSymbol());
589                      propertyNames.add(Identifier::fromString(vm, String(name)));
590                  }
591              }
592          }
593          
594          if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) {
595              typedef OpaqueJSClassStaticFunctionsTable::const_iterator iterator;
596              iterator end = staticFunctions->end();
597              for (iterator it = staticFunctions->begin(); it != end; ++it) {
598                  StringImpl* name = it->key.get();
599                  StaticFunctionEntry* entry = it->value.get();
600                  if (mode == DontEnumPropertiesMode::Include || !(entry->attributes & kJSPropertyAttributeDontEnum)) {
601                      ASSERT(!name->isSymbol());
602                      propertyNames.add(Identifier::fromString(vm, String(name)));
603                  }
604              }
605          }
606      }
607  }
608  
609  template <class Parent>
610  void JSCallbackObject<Parent>::setPrivate(void* data)
611  {
612      m_callbackObjectData->privateData = data;
613  }
614  
615  template <class Parent>
616  void* JSCallbackObject<Parent>::getPrivate()
617  {
618      return m_callbackObjectData->privateData;
619  }
620  
621  template <class Parent>
622  bool JSCallbackObject<Parent>::inherits(JSClassRef c) const
623  {
624      for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
625          if (jsClass == c)
626              return true;
627      }
628      return false;
629  }
630  
631  template <class Parent>
632  JSValue JSCallbackObject<Parent>::getStaticValue(JSGlobalObject* globalObject, PropertyName propertyName)
633  {
634      VM& vm = getVM(globalObject);
635      auto scope = DECLARE_THROW_SCOPE(vm);
636  
637      JSObjectRef thisRef = toRef(jsCast<JSObject*>(this));
638      
639      if (StringImpl* name = propertyName.uid()) {
640          for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
641              if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(globalObject)) {
642                  if (StaticValueEntry* entry = staticValues->get(name)) {
643                      if (JSObjectGetPropertyCallback getProperty = entry->getProperty) {
644                          JSValueRef exception = nullptr;
645                          JSValueRef value;
646                          {
647                              JSLock::DropAllLocks dropAllLocks(globalObject);
648                              value = getProperty(toRef(globalObject), thisRef, entry->propertyNameRef.get(), &exception);
649                          }
650                          if (exception) {
651                              throwException(globalObject, scope, toJS(globalObject, exception));
652                              return jsUndefined();
653                          }
654                          if (value)
655                              return toJS(globalObject, value);
656                      }
657                  }
658              }
659          }
660      }
661  
662      return JSValue();
663  }
664  
665  template <class Parent>
666  EncodedJSValue JSCallbackObject<Parent>::staticFunctionGetterImpl(JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName propertyName)
667  {
668      VM& vm = getVM(globalObject);
669      auto scope = DECLARE_THROW_SCOPE(vm);
670  
671      JSCallbackObject* thisObj = asCallbackObject(thisValue);
672      
673      // Check for cached or override property.
674      PropertySlot slot2(thisObj, PropertySlot::InternalMethodType::VMInquiry, &vm);
675      bool found = Parent::getOwnPropertySlot(thisObj, globalObject, propertyName, slot2);
676      RETURN_IF_EXCEPTION(scope, { });
677      slot2.disallowVMEntry.reset();
678      if (found)
679          return JSValue::encode(slot2.getValue(globalObject, propertyName));
680  
681      if (StringImpl* name = propertyName.uid()) {
682          for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) {
683              if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(globalObject)) {
684                  if (StaticFunctionEntry* entry = staticFunctions->get(name)) {
685                      if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) {
686                          JSObject* o = JSCallbackFunction::create(vm, thisObj->globalObject(vm), callAsFunction, name);
687                          thisObj->putDirect(vm, propertyName, o, entry->attributes);
688                          return JSValue::encode(o);
689                      }
690                  }
691              }
692          }
693      }
694  
695      return JSValue::encode(throwException(globalObject, scope, createReferenceError(globalObject, "Static function property defined with NULL callAsFunction callback."_s)));
696  }
697  
698  template <class Parent>
699  EncodedJSValue JSCallbackObject<Parent>::callbackGetterImpl(JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName propertyName)
700  {
701      VM& vm = getVM(globalObject);
702      auto scope = DECLARE_THROW_SCOPE(vm);
703  
704      JSCallbackObject* thisObj = asCallbackObject(thisValue);
705      
706      JSObjectRef thisRef = toRef(jsCast<JSObject*>(thisObj));
707      RefPtr<OpaqueJSString> propertyNameRef;
708      
709      if (StringImpl* name = propertyName.uid()) {
710          for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) {
711              if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) {
712                  if (!propertyNameRef)
713                      propertyNameRef = OpaqueJSString::tryCreate(name);
714                  JSValueRef exception = nullptr;
715                  JSValueRef value;
716                  {
717                      JSLock::DropAllLocks dropAllLocks(globalObject);
718                      value = getProperty(toRef(globalObject), thisRef, propertyNameRef.get(), &exception);
719                  }
720                  if (exception) {
721                      throwException(globalObject, scope, toJS(globalObject, exception));
722                      return JSValue::encode(jsUndefined());
723                  }
724                  if (value)
725                      return JSValue::encode(toJS(globalObject, value));
726              }
727          }
728      }
729  
730      return JSValue::encode(throwException(globalObject, scope, createReferenceError(globalObject, "hasProperty callback returned true for a property that doesn't exist."_s)));
731  }
732  
733  } // namespace JSC