objc_instance.mm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*
  2. * Copyright (C) 2004, 2008, 2009 Apple Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
  17. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  20. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  21. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  23. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #import "config.h"
  26. #import "objc_instance.h"
  27. #import "runtime_method.h"
  28. #import <runtime/ObjectPrototype.h>
  29. #import "JSDOMBinding.h"
  30. #import "ObjCRuntimeObject.h"
  31. #import "WebScriptObject.h"
  32. #import <objc/objc-auto.h>
  33. #import <runtime/Error.h>
  34. #import <runtime/JSLock.h>
  35. #import "runtime/FunctionPrototype.h"
  36. #import <wtf/Assertions.h>
  37. #ifdef NDEBUG
  38. #define OBJC_LOG(formatAndArgs...) ((void)0)
  39. #else
  40. #define OBJC_LOG(formatAndArgs...) { \
  41. fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \
  42. fprintf(stderr, formatAndArgs); \
  43. }
  44. #endif
  45. using namespace JSC::Bindings;
  46. using namespace JSC;
  47. static NSString *s_exception;
  48. static JSGlobalObject* s_exceptionEnvironment; // No need to protect this value, since we just use it for a pointer comparison.
  49. static NSMapTable *s_instanceWrapperCache;
  50. #if COMPILER(CLANG)
  51. #pragma clang diagnostic push
  52. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  53. #endif
  54. static NSMapTable *createInstanceWrapperCache()
  55. {
  56. // NSMapTable with zeroing weak pointers is the recommended way to build caches like this under garbage collection.
  57. NSPointerFunctionsOptions keyOptions = NSPointerFunctionsZeroingWeakMemory | NSPointerFunctionsOpaquePersonality;
  58. NSPointerFunctionsOptions valueOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
  59. return [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
  60. }
  61. #if COMPILER(CLANG)
  62. #pragma clang diagnostic pop
  63. #endif
  64. RuntimeObject* ObjcInstance::newRuntimeObject(ExecState* exec)
  65. {
  66. return ObjCRuntimeObject::create(exec, exec->lexicalGlobalObject(), this);
  67. }
  68. void ObjcInstance::setGlobalException(NSString* exception, JSGlobalObject* exceptionEnvironment)
  69. {
  70. NSString *oldException = s_exception;
  71. s_exception = [exception copy];
  72. [oldException release];
  73. s_exceptionEnvironment = exceptionEnvironment;
  74. }
  75. void ObjcInstance::moveGlobalExceptionToExecState(ExecState* exec)
  76. {
  77. if (!s_exception) {
  78. ASSERT(!s_exceptionEnvironment);
  79. return;
  80. }
  81. if (!s_exceptionEnvironment || s_exceptionEnvironment == exec->dynamicGlobalObject()) {
  82. JSLockHolder lock(exec);
  83. throwError(exec, s_exception);
  84. }
  85. [s_exception release];
  86. s_exception = nil;
  87. s_exceptionEnvironment = 0;
  88. }
  89. ObjcInstance::ObjcInstance(id instance, PassRefPtr<RootObject> rootObject)
  90. : Instance(rootObject)
  91. , _instance(instance)
  92. , _class(0)
  93. , _pool(0)
  94. , _beginCount(0)
  95. {
  96. }
  97. PassRefPtr<ObjcInstance> ObjcInstance::create(id instance, PassRefPtr<RootObject> rootObject)
  98. {
  99. if (!s_instanceWrapperCache)
  100. s_instanceWrapperCache = createInstanceWrapperCache();
  101. if (void* existingWrapper = NSMapGet(s_instanceWrapperCache, instance))
  102. return static_cast<ObjcInstance*>(existingWrapper);
  103. RefPtr<ObjcInstance> wrapper = adoptRef(new ObjcInstance(instance, rootObject));
  104. NSMapInsert(s_instanceWrapperCache, instance, wrapper.get());
  105. return wrapper.release();
  106. }
  107. ObjcInstance::~ObjcInstance()
  108. {
  109. // Both -finalizeForWebScript and -dealloc/-finalize of _instance may require autorelease pools.
  110. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  111. ASSERT(s_instanceWrapperCache);
  112. ASSERT(_instance);
  113. NSMapRemove(s_instanceWrapperCache, _instance.get());
  114. if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)])
  115. [_instance.get() performSelector:@selector(finalizeForWebScript)];
  116. _instance = 0;
  117. [pool drain];
  118. }
  119. static NSAutoreleasePool* allocateAutoReleasePool()
  120. {
  121. // If GC is enabled an autorelease pool is unnecessary, and the
  122. // pool cannot be protected from GC so may be collected leading
  123. // to a crash when we try to drain the release pool.
  124. if (objc_collectingEnabled())
  125. return nil;
  126. return [[NSAutoreleasePool alloc] init];
  127. }
  128. void ObjcInstance::virtualBegin()
  129. {
  130. if (!_pool)
  131. _pool = allocateAutoReleasePool();
  132. _beginCount++;
  133. }
  134. void ObjcInstance::virtualEnd()
  135. {
  136. _beginCount--;
  137. ASSERT(_beginCount >= 0);
  138. if (!_beginCount) {
  139. [_pool drain];
  140. _pool = 0;
  141. }
  142. }
  143. Bindings::Class* ObjcInstance::getClass() const
  144. {
  145. if (!_instance)
  146. return 0;
  147. if (!_class)
  148. _class = ObjcClass::classForIsA(object_getClass(_instance.get()));
  149. return static_cast<Bindings::Class*>(_class);
  150. }
  151. bool ObjcInstance::supportsInvokeDefaultMethod() const
  152. {
  153. return [_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)];
  154. }
  155. class ObjCRuntimeMethod : public RuntimeMethod {
  156. public:
  157. static ObjCRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const String& name, Bindings::Method* method)
  158. {
  159. // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
  160. // We need to pass in the right global object for "i".
  161. Structure* domStructure = WebCore::deprecatedGetDOMStructure<ObjCRuntimeMethod>(exec);
  162. ObjCRuntimeMethod* runtimeMethod = new (NotNull, allocateCell<ObjCRuntimeMethod>(*exec->heap())) ObjCRuntimeMethod(globalObject, domStructure, method);
  163. runtimeMethod->finishCreation(exec->vm(), name);
  164. return runtimeMethod;
  165. }
  166. static Structure* createStructure(VM& vm, JSC::JSGlobalObject* globalObject, JSValue prototype)
  167. {
  168. return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
  169. }
  170. static const ClassInfo s_info;
  171. private:
  172. typedef RuntimeMethod Base;
  173. ObjCRuntimeMethod(JSGlobalObject* globalObject, Structure* structure, Bindings::Method* method)
  174. : RuntimeMethod(globalObject, structure, method)
  175. {
  176. }
  177. void finishCreation(VM& vm, const String& name)
  178. {
  179. Base::finishCreation(vm, name);
  180. ASSERT(inherits(&s_info));
  181. }
  182. };
  183. const ClassInfo ObjCRuntimeMethod::s_info = { "ObjCRuntimeMethod", &RuntimeMethod::s_info, 0, 0, CREATE_METHOD_TABLE(ObjCRuntimeMethod) };
  184. JSValue ObjcInstance::getMethod(ExecState* exec, PropertyName propertyName)
  185. {
  186. Method* method = getClass()->methodNamed(propertyName, this);
  187. return ObjCRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName.publicName(), method);
  188. }
  189. JSValue ObjcInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod)
  190. {
  191. if (!asObject(runtimeMethod)->inherits(&ObjCRuntimeMethod::s_info))
  192. return throwError(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object."));
  193. ObjcMethod *method = static_cast<ObjcMethod*>(runtimeMethod->method());
  194. ASSERT(method);
  195. return invokeObjcMethod(exec, method);
  196. }
  197. JSValue ObjcInstance::invokeObjcMethod(ExecState* exec, ObjcMethod* method)
  198. {
  199. JSValue result = jsUndefined();
  200. JSLock::DropAllLocks dropAllLocks(exec); // Can't put this inside the @try scope because it unwinds incorrectly.
  201. setGlobalException(nil);
  202. @try {
  203. NSMethodSignature* signature = method->getMethodSignature();
  204. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
  205. [invocation setSelector:method->selector()];
  206. [invocation setTarget:_instance.get()];
  207. if (method->isFallbackMethod()) {
  208. if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
  209. NSLog(@"Incorrect signature for invokeUndefinedMethodFromWebScript:withArguments: -- return type must be object.");
  210. return result;
  211. }
  212. // Invoke invokeUndefinedMethodFromWebScript:withArguments:, pass JavaScript function
  213. // name as first (actually at 2) argument and array of args as second.
  214. NSString* jsName = (NSString* )method->javaScriptName();
  215. [invocation setArgument:&jsName atIndex:2];
  216. NSMutableArray* objcArgs = [NSMutableArray array];
  217. int count = exec->argumentCount();
  218. for (int i = 0; i < count; i++) {
  219. ObjcValue value = convertValueToObjcValue(exec, exec->argument(i), ObjcObjectType);
  220. [objcArgs addObject:value.objectValue];
  221. }
  222. [invocation setArgument:&objcArgs atIndex:3];
  223. } else {
  224. unsigned count = [signature numberOfArguments];
  225. for (unsigned i = 2; i < count ; i++) {
  226. const char* type = [signature getArgumentTypeAtIndex:i];
  227. ObjcValueType objcValueType = objcValueTypeForType(type);
  228. // Must have a valid argument type. This method signature should have
  229. // been filtered already to ensure that it has acceptable argument
  230. // types.
  231. ASSERT(objcValueType != ObjcInvalidType && objcValueType != ObjcVoidType);
  232. ObjcValue value = convertValueToObjcValue(exec, exec->argument(i-2), objcValueType);
  233. switch (objcValueType) {
  234. case ObjcObjectType:
  235. [invocation setArgument:&value.objectValue atIndex:i];
  236. break;
  237. case ObjcCharType:
  238. case ObjcUnsignedCharType:
  239. [invocation setArgument:&value.charValue atIndex:i];
  240. break;
  241. case ObjcShortType:
  242. case ObjcUnsignedShortType:
  243. [invocation setArgument:&value.shortValue atIndex:i];
  244. break;
  245. case ObjcBoolType:
  246. [invocation setArgument:&value.booleanValue atIndex:i];
  247. break;
  248. case ObjcIntType:
  249. case ObjcUnsignedIntType:
  250. [invocation setArgument:&value.intValue atIndex:i];
  251. break;
  252. case ObjcLongType:
  253. case ObjcUnsignedLongType:
  254. [invocation setArgument:&value.longValue atIndex:i];
  255. break;
  256. case ObjcLongLongType:
  257. case ObjcUnsignedLongLongType:
  258. [invocation setArgument:&value.longLongValue atIndex:i];
  259. break;
  260. case ObjcFloatType:
  261. [invocation setArgument:&value.floatValue atIndex:i];
  262. break;
  263. case ObjcDoubleType:
  264. [invocation setArgument:&value.doubleValue atIndex:i];
  265. break;
  266. default:
  267. // Should never get here. Argument types are filtered (and
  268. // the assert above should have fired in the impossible case
  269. // of an invalid type anyway).
  270. fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)objcValueType);
  271. ASSERT_NOT_REACHED();
  272. }
  273. }
  274. }
  275. [invocation invoke];
  276. // Get the return value type.
  277. const char* type = [signature methodReturnType];
  278. ObjcValueType objcValueType = objcValueTypeForType(type);
  279. // Must have a valid return type. This method signature should have
  280. // been filtered already to ensure that it have an acceptable return
  281. // type.
  282. ASSERT(objcValueType != ObjcInvalidType);
  283. // Get the return value and convert it to a JavaScript value. Length
  284. // of return value will never exceed the size of largest scalar
  285. // or a pointer.
  286. char buffer[1024];
  287. ASSERT([signature methodReturnLength] < 1024);
  288. if (*type != 'v') {
  289. [invocation getReturnValue:buffer];
  290. result = convertObjcValueToValue(exec, buffer, objcValueType, m_rootObject.get());
  291. }
  292. } @catch(NSException* localException) {
  293. }
  294. moveGlobalExceptionToExecState(exec);
  295. // Work around problem in some versions of GCC where result gets marked volatile and
  296. // it can't handle copying from a volatile to non-volatile.
  297. return const_cast<JSValue&>(result);
  298. }
  299. JSValue ObjcInstance::invokeDefaultMethod(ExecState* exec)
  300. {
  301. JSValue result = jsUndefined();
  302. JSLock::DropAllLocks dropAllLocks(exec); // Can't put this inside the @try scope because it unwinds incorrectly.
  303. setGlobalException(nil);
  304. @try {
  305. if (![_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)])
  306. return result;
  307. NSMethodSignature* signature = [_instance.get() methodSignatureForSelector:@selector(invokeDefaultMethodWithArguments:)];
  308. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
  309. [invocation setSelector:@selector(invokeDefaultMethodWithArguments:)];
  310. [invocation setTarget:_instance.get()];
  311. if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
  312. NSLog(@"Incorrect signature for invokeDefaultMethodWithArguments: -- return type must be object.");
  313. return result;
  314. }
  315. NSMutableArray* objcArgs = [NSMutableArray array];
  316. unsigned count = exec->argumentCount();
  317. for (unsigned i = 0; i < count; i++) {
  318. ObjcValue value = convertValueToObjcValue(exec, exec->argument(i), ObjcObjectType);
  319. [objcArgs addObject:value.objectValue];
  320. }
  321. [invocation setArgument:&objcArgs atIndex:2];
  322. [invocation invoke];
  323. // Get the return value type, should always be "@" because of
  324. // check above.
  325. const char* type = [signature methodReturnType];
  326. ObjcValueType objcValueType = objcValueTypeForType(type);
  327. // Get the return value and convert it to a JavaScript value. Length
  328. // of return value will never exceed the size of a pointer, so we're
  329. // OK with 32 here.
  330. char buffer[32];
  331. [invocation getReturnValue:buffer];
  332. result = convertObjcValueToValue(exec, buffer, objcValueType, m_rootObject.get());
  333. } @catch(NSException* localException) {
  334. }
  335. moveGlobalExceptionToExecState(exec);
  336. // Work around problem in some versions of GCC where result gets marked volatile and
  337. // it can't handle copying from a volatile to non-volatile.
  338. return const_cast<JSValue&>(result);
  339. }
  340. bool ObjcInstance::setValueOfUndefinedField(ExecState* exec, PropertyName propertyName, JSValue aValue)
  341. {
  342. String name(propertyName.publicName());
  343. if (name.isNull())
  344. return false;
  345. id targetObject = getObject();
  346. if (![targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)])
  347. return false;
  348. JSLock::DropAllLocks dropAllLocks(exec); // Can't put this inside the @try scope because it unwinds incorrectly.
  349. // This check is not really necessary because NSObject implements
  350. // setValue:forUndefinedKey:, and unfortunately the default implementation
  351. // throws an exception.
  352. if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]){
  353. setGlobalException(nil);
  354. ObjcValue objcValue = convertValueToObjcValue(exec, aValue, ObjcObjectType);
  355. @try {
  356. [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:name.ascii().data() encoding:NSASCIIStringEncoding]];
  357. } @catch(NSException* localException) {
  358. // Do nothing. Class did not override valueForUndefinedKey:.
  359. }
  360. moveGlobalExceptionToExecState(exec);
  361. }
  362. return true;
  363. }
  364. JSValue ObjcInstance::getValueOfUndefinedField(ExecState* exec, PropertyName propertyName) const
  365. {
  366. String name(propertyName.publicName());
  367. if (name.isNull())
  368. return jsUndefined();
  369. JSValue result = jsUndefined();
  370. id targetObject = getObject();
  371. JSLock::DropAllLocks dropAllLocks(exec); // Can't put this inside the @try scope because it unwinds incorrectly.
  372. // This check is not really necessary because NSObject implements
  373. // valueForUndefinedKey:, and unfortunately the default implementation
  374. // throws an exception.
  375. if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){
  376. setGlobalException(nil);
  377. @try {
  378. id objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:name.ascii().data() encoding:NSASCIIStringEncoding]];
  379. result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, m_rootObject.get());
  380. } @catch(NSException* localException) {
  381. // Do nothing. Class did not override valueForUndefinedKey:.
  382. }
  383. moveGlobalExceptionToExecState(exec);
  384. }
  385. // Work around problem in some versions of GCC where result gets marked volatile and
  386. // it can't handle copying from a volatile to non-volatile.
  387. return const_cast<JSValue&>(result);
  388. }
  389. JSValue ObjcInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
  390. {
  391. if (hint == PreferString)
  392. return stringValue(exec);
  393. if (hint == PreferNumber)
  394. return numberValue(exec);
  395. if ([_instance.get() isKindOfClass:[NSString class]])
  396. return stringValue(exec);
  397. if ([_instance.get() isKindOfClass:[NSNumber class]])
  398. return numberValue(exec);
  399. return valueOf(exec);
  400. }
  401. JSValue ObjcInstance::stringValue(ExecState* exec) const
  402. {
  403. return convertNSStringToString(exec, [getObject() description]);
  404. }
  405. JSValue ObjcInstance::numberValue(ExecState*) const
  406. {
  407. // FIXME: Implement something sensible
  408. return jsNumber(0);
  409. }
  410. JSValue ObjcInstance::booleanValue() const
  411. {
  412. // FIXME: Implement something sensible
  413. return jsBoolean(false);
  414. }
  415. JSValue ObjcInstance::valueOf(ExecState* exec) const
  416. {
  417. return stringValue(exec);
  418. }