1 /* 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Eric Seidel <eric (at) 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 COMPUTER, 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 COMPUTER, 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 #include "APIShims.h" 28 #include "APICast.h" 29 #include "Error.h" 30 #include "JSCallbackFunction.h" 31 #include "JSClassRef.h" 32 #include "JSGlobalObject.h" 33 #include "JSLock.h" 34 #include "JSObjectRef.h" 35 #include "JSString.h" 36 #include "JSStringRef.h" 37 #include "OpaqueJSString.h" 38 #include "PropertyNameArray.h" 39 #include <wtf/Vector.h> 40 41 namespace JSC { 42 43 template <class Base> 44 inline JSCallbackObject<Base>* JSCallbackObject<Base>::asCallbackObject(JSValue value) 45 { 46 ASSERT(asObject(value)->inherits(&info)); 47 return static_cast<JSCallbackObject*>(asObject(value)); 48 } 49 50 template <class Base> 51 JSCallbackObject<Base>::JSCallbackObject(ExecState* exec, NonNullPassRefPtr<Structure> structure, JSClassRef jsClass, void* data) 52 : Base(structure) 53 , m_callbackObjectData(new JSCallbackObjectData(data, jsClass)) 54 { 55 init(exec); 56 } 57 58 // Global object constructor. 59 // FIXME: Move this into a separate JSGlobalCallbackObject class derived from this one. 60 template <class Base> 61 JSCallbackObject<Base>::JSCallbackObject(JSClassRef jsClass) 62 : Base() 63 , m_callbackObjectData(new JSCallbackObjectData(0, jsClass)) 64 { 65 ASSERT(Base::isGlobalObject()); 66 init(static_cast<JSGlobalObject*>(this)->globalExec()); 67 } 68 69 template <class Base> 70 void JSCallbackObject<Base>::init(ExecState* exec) 71 { 72 ASSERT(exec); 73 74 Vector<JSObjectInitializeCallback, 16> initRoutines; 75 JSClassRef jsClass = classRef(); 76 do { 77 if (JSObjectInitializeCallback initialize = jsClass->initialize) 78 initRoutines.append(initialize); 79 } while ((jsClass = jsClass->parentClass)); 80 81 // initialize from base to derived 82 for (int i = static_cast<int>(initRoutines.size()) - 1; i >= 0; i--) { 83 APICallbackShim callbackShim(exec); 84 JSObjectInitializeCallback initialize = initRoutines[i]; 85 initialize(toRef(exec), toRef(this)); 86 } 87 } 88 89 template <class Base> 90 JSCallbackObject<Base>::~JSCallbackObject() 91 { 92 JSObjectRef thisRef = toRef(this); 93 94 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) 95 if (JSObjectFinalizeCallback finalize = jsClass->finalize) 96 finalize(thisRef); 97 } 98 99 template <class Base> 100 UString JSCallbackObject<Base>::className() const 101 { 102 UString thisClassName = classRef()->className(); 103 if (!thisClassName.isEmpty()) 104 return thisClassName; 105 106 return Base::className(); 107 } 108 109 template <class Base> 110 bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 111 { 112 JSContextRef ctx = toRef(exec); 113 JSObjectRef thisRef = toRef(this); 114 RefPtr<OpaqueJSString> propertyNameRef; 115 116 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 117 // optional optimization to bypass getProperty in cases when we only need to know if the property exists 118 if (JSObjectHasPropertyCallback hasProperty = jsClass->hasProperty) { 119 if (!propertyNameRef) 120 propertyNameRef = OpaqueJSString::create(propertyName.ustring()); 121 APICallbackShim callbackShim(exec); 122 if (hasProperty(ctx, thisRef, propertyNameRef.get())) { 123 slot.setCustom(this, callbackGetter); 124 return true; 125 } 126 } else if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { 127 if (!propertyNameRef) 128 propertyNameRef = OpaqueJSString::create(propertyName.ustring()); 129 JSValueRef exception = 0; 130 JSValueRef value; 131 { 132 APICallbackShim callbackShim(exec); 133 value = getProperty(ctx, thisRef, propertyNameRef.get(), &exception); 134 } 135 if (exception) { 136 exec->setException(toJS(exec, exception)); 137 slot.setValue(jsUndefined()); 138 return true; 139 } 140 if (value) { 141 slot.setValue(toJS(exec, value)); 142 return true; 143 } 144 } 145 146 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { 147 if (staticValues->contains(propertyName.ustring().rep())) { 148 slot.setCustom(this, staticValueGetter); 149 return true; 150 } 151 } 152 153 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { 154 if (staticFunctions->contains(propertyName.ustring().rep())) { 155 slot.setCustom(this, staticFunctionGetter); 156 return true; 157 } 158 } 159 } 160 161 return Base::getOwnPropertySlot(exec, propertyName, slot); 162 } 163 164 template <class Base> 165 bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) 166 { 167 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); 168 } 169 170 template <class Base> 171 bool JSCallbackObject<Base>::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) 172 { 173 PropertySlot slot; 174 if (getOwnPropertySlot(exec, propertyName, slot)) { 175 // Ideally we should return an access descriptor, but returning a value descriptor is better than nothing. 176 JSValue value = slot.getValue(exec, propertyName); 177 if (!exec->hadException()) 178 descriptor.setValue(value); 179 // We don't know whether the property is configurable, but assume it is. 180 descriptor.setConfigurable(true); 181 // We don't know whether the property is enumerable (we could call getOwnPropertyNames() to find out), but assume it isn't. 182 descriptor.setEnumerable(false); 183 return true; 184 } 185 186 return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); 187 } 188 189 template <class Base> 190 void JSCallbackObject<Base>::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) 191 { 192 JSContextRef ctx = toRef(exec); 193 JSObjectRef thisRef = toRef(this); 194 RefPtr<OpaqueJSString> propertyNameRef; 195 JSValueRef valueRef = toRef(exec, value); 196 197 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 198 if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) { 199 if (!propertyNameRef) 200 propertyNameRef = OpaqueJSString::create(propertyName.ustring()); 201 JSValueRef exception = 0; 202 bool result; 203 { 204 APICallbackShim callbackShim(exec); 205 result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); 206 } 207 if (exception) 208 exec->setException(toJS(exec, exception)); 209 if (result || exception) 210 return; 211 } 212 213 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { 214 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) { 215 if (entry->attributes & kJSPropertyAttributeReadOnly) 216 return; 217 if (JSObjectSetPropertyCallback setProperty = entry->setProperty) { 218 if (!propertyNameRef) 219 propertyNameRef = OpaqueJSString::create(propertyName.ustring()); 220 JSValueRef exception = 0; 221 bool result; 222 { 223 APICallbackShim callbackShim(exec); 224 result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); 225 } 226 if (exception) 227 exec->setException(toJS(exec, exception)); 228 if (result || exception) 229 return; 230 } else 231 throwError(exec, ReferenceError, "Attempt to set a property that is not settable."); 232 } 233 } 234 235 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { 236 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) { 237 if (entry->attributes & kJSPropertyAttributeReadOnly) 238 return; 239 JSCallbackObject<Base>::putDirect(propertyName, value); // put as override property 240 return; 241 } 242 } 243 } 244 245 return Base::put(exec, propertyName, value, slot); 246 } 247 248 template <class Base> 249 bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, const Identifier& propertyName) 250 { 251 JSContextRef ctx = toRef(exec); 252 JSObjectRef thisRef = toRef(this); 253 RefPtr<OpaqueJSString> propertyNameRef; 254 255 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 256 if (JSObjectDeletePropertyCallback deleteProperty = jsClass->deleteProperty) { 257 if (!propertyNameRef) 258 propertyNameRef = OpaqueJSString::create(propertyName.ustring()); 259 JSValueRef exception = 0; 260 bool result; 261 { 262 APICallbackShim callbackShim(exec); 263 result = deleteProperty(ctx, thisRef, propertyNameRef.get(), &exception); 264 } 265 if (exception) 266 exec->setException(toJS(exec, exception)); 267 if (result || exception) 268 return true; 269 } 270 271 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { 272 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) { 273 if (entry->attributes & kJSPropertyAttributeDontDelete) 274 return false; 275 return true; 276 } 277 } 278 279 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { 280 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) { 281 if (entry->attributes & kJSPropertyAttributeDontDelete) 282 return false; 283 return true; 284 } 285 } 286 } 287 288 return Base::deleteProperty(exec, propertyName); 289 } 290 291 template <class Base> 292 bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, unsigned propertyName) 293 { 294 return deleteProperty(exec, Identifier::from(exec, propertyName)); 295 } 296 297 template <class Base> 298 ConstructType JSCallbackObject<Base>::getConstructData(ConstructData& constructData) 299 { 300 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 301 if (jsClass->callAsConstructor) { 302 constructData.native.function = construct; 303 return ConstructTypeHost; 304 } 305 } 306 return ConstructTypeNone; 307 } 308 309 template <class Base> 310 JSObject* JSCallbackObject<Base>::construct(ExecState* exec, JSObject* constructor, const ArgList& args) 311 { 312 JSContextRef execRef = toRef(exec); 313 JSObjectRef constructorRef = toRef(constructor); 314 315 for (JSClassRef jsClass = static_cast<JSCallbackObject<Base>*>(constructor)->classRef(); jsClass; jsClass = jsClass->parentClass) { 316 if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) { 317 int argumentCount = static_cast<int>(args.size()); 318 Vector<JSValueRef, 16> arguments(argumentCount); 319 for (int i = 0; i < argumentCount; i++) 320 arguments[i] = toRef(exec, args.at(i)); 321 JSValueRef exception = 0; 322 JSObject* result; 323 { 324 APICallbackShim callbackShim(exec); 325 result = toJS(callAsConstructor(execRef, constructorRef, argumentCount, arguments.data(), &exception)); 326 } 327 if (exception) 328 exec->setException(toJS(exec, exception)); 329 return result; 330 } 331 } 332 333 ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here 334 return 0; 335 } 336 337 template <class Base> 338 bool JSCallbackObject<Base>::hasInstance(ExecState* exec, JSValue value, JSValue) 339 { 340 JSContextRef execRef = toRef(exec); 341 JSObjectRef thisRef = toRef(this); 342 343 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 344 if (JSObjectHasInstanceCallback hasInstance = jsClass->hasInstance) { 345 JSValueRef valueRef = toRef(exec, value); 346 JSValueRef exception = 0; 347 bool result; 348 { 349 APICallbackShim callbackShim(exec); 350 result = hasInstance(execRef, thisRef, valueRef, &exception); 351 } 352 if (exception) 353 exec->setException(toJS(exec, exception)); 354 return result; 355 } 356 } 357 return false; 358 } 359 360 template <class Base> 361 CallType JSCallbackObject<Base>::getCallData(CallData& callData) 362 { 363 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 364 if (jsClass->callAsFunction) { 365 callData.native.function = call; 366 return CallTypeHost; 367 } 368 } 369 return CallTypeNone; 370 } 371 372 template <class Base> 373 JSValue JSCallbackObject<Base>::call(ExecState* exec, JSObject* functionObject, JSValue thisValue, const ArgList& args) 374 { 375 JSContextRef execRef = toRef(exec); 376 JSObjectRef functionRef = toRef(functionObject); 377 JSObjectRef thisObjRef = toRef(thisValue.toThisObject(exec)); 378 379 for (JSClassRef jsClass = static_cast<JSCallbackObject<Base>*>(functionObject)->classRef(); jsClass; jsClass = jsClass->parentClass) { 380 if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) { 381 int argumentCount = static_cast<int>(args.size()); 382 Vector<JSValueRef, 16> arguments(argumentCount); 383 for (int i = 0; i < argumentCount; i++) 384 arguments[i] = toRef(exec, args.at(i)); 385 JSValueRef exception = 0; 386 JSValue result; 387 { 388 APICallbackShim callbackShim(exec); 389 result = toJS(exec, callAsFunction(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception)); 390 } 391 if (exception) 392 exec->setException(toJS(exec, exception)); 393 return result; 394 } 395 } 396 397 ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here 398 return JSValue(); 399 } 400 401 template <class Base> 402 void JSCallbackObject<Base>::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 403 { 404 JSContextRef execRef = toRef(exec); 405 JSObjectRef thisRef = toRef(this); 406 407 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) { 408 if (JSObjectGetPropertyNamesCallback getPropertyNames = jsClass->getPropertyNames) { 409 APICallbackShim callbackShim(exec); 410 getPropertyNames(execRef, thisRef, toRef(&propertyNames)); 411 } 412 413 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { 414 typedef OpaqueJSClassStaticValuesTable::const_iterator iterator; 415 iterator end = staticValues->end(); 416 for (iterator it = staticValues->begin(); it != end; ++it) { 417 UString::Rep* name = it->first.get(); 418 StaticValueEntry* entry = it->second; 419 if (entry->getProperty && (!(entry->attributes & kJSPropertyAttributeDontEnum) || (mode == IncludeDontEnumProperties))) 420 propertyNames.add(Identifier(exec, name)); 421 } 422 } 423 424 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { 425 typedef OpaqueJSClassStaticFunctionsTable::const_iterator iterator; 426 iterator end = staticFunctions->end(); 427 for (iterator it = staticFunctions->begin(); it != end; ++it) { 428 UString::Rep* name = it->first.get(); 429 StaticFunctionEntry* entry = it->second; 430 if (!(entry->attributes & kJSPropertyAttributeDontEnum) || (mode == IncludeDontEnumProperties)) 431 propertyNames.add(Identifier(exec, name)); 432 } 433 } 434 } 435 436 Base::getOwnPropertyNames(exec, propertyNames, mode); 437 } 438 439 template <class Base> 440 double JSCallbackObject<Base>::toNumber(ExecState* exec) const 441 { 442 // We need this check to guard against the case where this object is rhs of 443 // a binary expression where lhs threw an exception in its conversion to 444 // primitive 445 if (exec->hadException()) 446 return NaN; 447 JSContextRef ctx = toRef(exec); 448 JSObjectRef thisRef = toRef(this); 449 450 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) 451 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) { 452 JSValueRef exception = 0; 453 JSValueRef value; 454 { 455 APICallbackShim callbackShim(exec); 456 value = convertToType(ctx, thisRef, kJSTypeNumber, &exception); 457 } 458 if (exception) { 459 exec->setException(toJS(exec, exception)); 460 return 0; 461 } 462 463 double dValue; 464 if (value) 465 return toJS(exec, value).getNumber(dValue) ? dValue : NaN; 466 } 467 468 return Base::toNumber(exec); 469 } 470 471 template <class Base> 472 UString JSCallbackObject<Base>::toString(ExecState* exec) const 473 { 474 JSContextRef ctx = toRef(exec); 475 JSObjectRef thisRef = toRef(this); 476 477 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) 478 if (JSObjectConvertToTypeCallback convertToType = jsClass->convertToType) { 479 JSValueRef exception = 0; 480 JSValueRef value; 481 { 482 APICallbackShim callbackShim(exec); 483 value = convertToType(ctx, thisRef, kJSTypeString, &exception); 484 } 485 if (exception) { 486 exec->setException(toJS(exec, exception)); 487 return ""; 488 } 489 if (value) 490 return toJS(exec, value).getString(exec); 491 } 492 493 return Base::toString(exec); 494 } 495 496 template <class Base> 497 void JSCallbackObject<Base>::setPrivate(void* data) 498 { 499 m_callbackObjectData->privateData = data; 500 } 501 502 template <class Base> 503 void* JSCallbackObject<Base>::getPrivate() 504 { 505 return m_callbackObjectData->privateData; 506 } 507 508 template <class Base> 509 bool JSCallbackObject<Base>::inherits(JSClassRef c) const 510 { 511 for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) 512 if (jsClass == c) 513 return true; 514 515 return false; 516 } 517 518 template <class Base> 519 JSValue JSCallbackObject<Base>::staticValueGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) 520 { 521 JSCallbackObject* thisObj = asCallbackObject(slot.slotBase()); 522 523 JSObjectRef thisRef = toRef(thisObj); 524 RefPtr<OpaqueJSString> propertyNameRef; 525 526 for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) 527 if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) 528 if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) 529 if (JSObjectGetPropertyCallback getProperty = entry->getProperty) { 530 if (!propertyNameRef) 531 propertyNameRef = OpaqueJSString::create(propertyName.ustring()); 532 JSValueRef exception = 0; 533 JSValueRef value; 534 { 535 APICallbackShim callbackShim(exec); 536 value = getProperty(toRef(exec), thisRef, propertyNameRef.get(), &exception); 537 } 538 if (exception) { 539 exec->setException(toJS(exec, exception)); 540 return jsUndefined(); 541 } 542 if (value) 543 return toJS(exec, value); 544 } 545 546 return throwError(exec, ReferenceError, "Static value property defined with NULL getProperty callback."); 547 } 548 549 template <class Base> 550 JSValue JSCallbackObject<Base>::staticFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) 551 { 552 JSCallbackObject* thisObj = asCallbackObject(slot.slotBase()); 553 554 // Check for cached or override property. 555 PropertySlot slot2(thisObj); 556 if (thisObj->Base::getOwnPropertySlot(exec, propertyName, slot2)) 557 return slot2.getValue(exec, propertyName); 558 559 for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) { 560 if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) { 561 if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.ustring().rep())) { 562 if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) { 563 JSObject* o = new (exec) JSCallbackFunction(exec, callAsFunction, propertyName); 564 thisObj->putDirect(propertyName, o, entry->attributes); 565 return o; 566 } 567 } 568 } 569 } 570 571 return throwError(exec, ReferenceError, "Static function property defined with NULL callAsFunction callback."); 572 } 573 574 template <class Base> 575 JSValue JSCallbackObject<Base>::callbackGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) 576 { 577 JSCallbackObject* thisObj = asCallbackObject(slot.slotBase()); 578 579 JSObjectRef thisRef = toRef(thisObj); 580 RefPtr<OpaqueJSString> propertyNameRef; 581 582 for (JSClassRef jsClass = thisObj->classRef(); jsClass; jsClass = jsClass->parentClass) 583 if (JSObjectGetPropertyCallback getProperty = jsClass->getProperty) { 584 if (!propertyNameRef) 585 propertyNameRef = OpaqueJSString::create(propertyName.ustring()); 586 JSValueRef exception = 0; 587 JSValueRef value; 588 { 589 APICallbackShim callbackShim(exec); 590 value = getProperty(toRef(exec), thisRef, propertyNameRef.get(), &exception); 591 } 592 if (exception) { 593 exec->setException(toJS(exec, exception)); 594 return jsUndefined(); 595 } 596 if (value) 597 return toJS(exec, value); 598 } 599 600 return throwError(exec, ReferenceError, "hasProperty callback returned true for a property that doesn't exist."); 601 } 602 603 } // namespace JSC 604