JSValue.mm 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132
  1. /*
  2. * Copyright (C) 2013 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 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 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. #include "config.h"
  26. #import "APICast.h"
  27. #import "APIShims.h"
  28. #import "DateInstance.h"
  29. #import "Error.h"
  30. #import "JavaScriptCore.h"
  31. #import "JSContextInternal.h"
  32. #import "JSVirtualMachineInternal.h"
  33. #import "JSValueInternal.h"
  34. #import "JSWrapperMap.h"
  35. #import "ObjcRuntimeExtras.h"
  36. #import "Operations.h"
  37. #import "JSCJSValue.h"
  38. #import <wtf/HashMap.h>
  39. #import <wtf/HashSet.h>
  40. #import <wtf/Vector.h>
  41. #import <wtf/TCSpinLock.h>
  42. #import <wtf/text/WTFString.h>
  43. #import <wtf/text/StringHash.h>
  44. #if JSC_OBJC_API_ENABLED
  45. NSString * const JSPropertyDescriptorWritableKey = @"writable";
  46. NSString * const JSPropertyDescriptorEnumerableKey = @"enumerable";
  47. NSString * const JSPropertyDescriptorConfigurableKey = @"configurable";
  48. NSString * const JSPropertyDescriptorValueKey = @"value";
  49. NSString * const JSPropertyDescriptorGetKey = @"get";
  50. NSString * const JSPropertyDescriptorSetKey = @"set";
  51. @implementation JSValue {
  52. JSValueRef m_value;
  53. }
  54. - (JSValueRef)JSValueRef
  55. {
  56. return m_value;
  57. }
  58. + (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context
  59. {
  60. return [JSValue valueWithJSValueRef:objectToValue(context, value) inContext:context];
  61. }
  62. + (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context
  63. {
  64. return [JSValue valueWithJSValueRef:JSValueMakeBoolean([context JSGlobalContextRef], value) inContext:context];
  65. }
  66. + (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context
  67. {
  68. return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
  69. }
  70. + (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context
  71. {
  72. return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
  73. }
  74. + (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context
  75. {
  76. return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
  77. }
  78. + (JSValue *)valueWithNewObjectInContext:(JSContext *)context
  79. {
  80. return [JSValue valueWithJSValueRef:JSObjectMake([context JSGlobalContextRef], 0, 0) inContext:context];
  81. }
  82. + (JSValue *)valueWithNewArrayInContext:(JSContext *)context
  83. {
  84. return [JSValue valueWithJSValueRef:JSObjectMakeArray([context JSGlobalContextRef], 0, NULL, 0) inContext:context];
  85. }
  86. + (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context
  87. {
  88. JSStringRef patternString = JSStringCreateWithCFString((CFStringRef)pattern);
  89. JSStringRef flagsString = JSStringCreateWithCFString((CFStringRef)flags);
  90. JSValueRef arguments[2] = { JSValueMakeString([context JSGlobalContextRef], patternString), JSValueMakeString([context JSGlobalContextRef], flagsString) };
  91. JSStringRelease(patternString);
  92. JSStringRelease(flagsString);
  93. return [JSValue valueWithJSValueRef:JSObjectMakeRegExp([context JSGlobalContextRef], 2, arguments, 0) inContext:context];
  94. }
  95. + (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context
  96. {
  97. JSStringRef string = JSStringCreateWithCFString((CFStringRef)message);
  98. JSValueRef argument = JSValueMakeString([context JSGlobalContextRef], string);
  99. JSStringRelease(string);
  100. return [JSValue valueWithJSValueRef:JSObjectMakeError([context JSGlobalContextRef], 1, &argument, 0) inContext:context];
  101. }
  102. + (JSValue *)valueWithNullInContext:(JSContext *)context
  103. {
  104. return [JSValue valueWithJSValueRef:JSValueMakeNull([context JSGlobalContextRef]) inContext:context];
  105. }
  106. + (JSValue *)valueWithUndefinedInContext:(JSContext *)context
  107. {
  108. return [JSValue valueWithJSValueRef:JSValueMakeUndefined([context JSGlobalContextRef]) inContext:context];
  109. }
  110. - (id)toObject
  111. {
  112. return valueToObject(_context, m_value);
  113. }
  114. - (id)toObjectOfClass:(Class)expectedClass
  115. {
  116. id result = [self toObject];
  117. return [result isKindOfClass:expectedClass] ? result : nil;
  118. }
  119. - (BOOL)toBool
  120. {
  121. return JSValueToBoolean([_context JSGlobalContextRef], m_value);
  122. }
  123. - (double)toDouble
  124. {
  125. JSValueRef exception = 0;
  126. double result = JSValueToNumber([_context JSGlobalContextRef], m_value, &exception);
  127. if (exception) {
  128. [_context notifyException:exception];
  129. return std::numeric_limits<double>::quiet_NaN();
  130. }
  131. return result;
  132. }
  133. - (int32_t)toInt32
  134. {
  135. return JSC::toInt32([self toDouble]);
  136. }
  137. - (uint32_t)toUInt32
  138. {
  139. return JSC::toUInt32([self toDouble]);
  140. }
  141. - (NSNumber *)toNumber
  142. {
  143. JSValueRef exception = 0;
  144. id result = valueToNumber([_context JSGlobalContextRef], m_value, &exception);
  145. if (exception)
  146. [_context notifyException:exception];
  147. return result;
  148. }
  149. - (NSString *)toString
  150. {
  151. JSValueRef exception = 0;
  152. id result = valueToString([_context JSGlobalContextRef], m_value, &exception);
  153. if (exception)
  154. [_context notifyException:exception];
  155. return result;
  156. }
  157. - (NSDate *)toDate
  158. {
  159. JSValueRef exception = 0;
  160. id result = valueToDate([_context JSGlobalContextRef], m_value, &exception);
  161. if (exception)
  162. [_context notifyException:exception];
  163. return result;
  164. }
  165. - (NSArray *)toArray
  166. {
  167. JSValueRef exception = 0;
  168. id result = valueToArray([_context JSGlobalContextRef], m_value, &exception);
  169. if (exception)
  170. [_context notifyException:exception];
  171. return result;
  172. }
  173. - (NSDictionary *)toDictionary
  174. {
  175. JSValueRef exception = 0;
  176. id result = valueToDictionary([_context JSGlobalContextRef], m_value, &exception);
  177. if (exception)
  178. [_context notifyException:exception];
  179. return result;
  180. }
  181. - (JSValue *)valueForProperty:(NSString *)propertyName
  182. {
  183. JSValueRef exception = 0;
  184. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  185. if (exception)
  186. return [_context valueFromNotifyException:exception];
  187. JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
  188. JSValueRef result = JSObjectGetProperty([_context JSGlobalContextRef], object, name, &exception);
  189. JSStringRelease(name);
  190. if (exception)
  191. return [_context valueFromNotifyException:exception];
  192. return [JSValue valueWithJSValueRef:result inContext:_context];
  193. }
  194. - (void)setValue:(id)value forProperty:(NSString *)propertyName
  195. {
  196. JSValueRef exception = 0;
  197. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  198. if (exception) {
  199. [_context notifyException:exception];
  200. return;
  201. }
  202. JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
  203. JSObjectSetProperty([_context JSGlobalContextRef], object, name, objectToValue(_context, value), 0, &exception);
  204. JSStringRelease(name);
  205. if (exception) {
  206. [_context notifyException:exception];
  207. return;
  208. }
  209. }
  210. - (BOOL)deleteProperty:(NSString *)propertyName
  211. {
  212. JSValueRef exception = 0;
  213. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  214. if (exception)
  215. return [_context boolFromNotifyException:exception];
  216. JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
  217. BOOL result = JSObjectDeleteProperty([_context JSGlobalContextRef], object, name, &exception);
  218. JSStringRelease(name);
  219. if (exception)
  220. return [_context boolFromNotifyException:exception];
  221. return result;
  222. }
  223. - (BOOL)hasProperty:(NSString *)propertyName
  224. {
  225. JSValueRef exception = 0;
  226. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  227. if (exception)
  228. return [_context boolFromNotifyException:exception];
  229. JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
  230. BOOL result = JSObjectHasProperty([_context JSGlobalContextRef], object, name);
  231. JSStringRelease(name);
  232. return result;
  233. }
  234. - (void)defineProperty:(NSString *)property descriptor:(id)descriptor
  235. {
  236. [[_context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, property, descriptor ]];
  237. }
  238. - (JSValue *)valueAtIndex:(NSUInteger)index
  239. {
  240. // Properties that are higher than an unsigned value can hold are converted to a double then inserted as a normal property.
  241. // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in get().
  242. if (index != (unsigned)index)
  243. return [self valueForProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
  244. JSValueRef exception = 0;
  245. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  246. if (exception)
  247. return [_context valueFromNotifyException:exception];
  248. JSValueRef result = JSObjectGetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, &exception);
  249. if (exception)
  250. return [_context valueFromNotifyException:exception];
  251. return [JSValue valueWithJSValueRef:result inContext:_context];
  252. }
  253. - (void)setValue:(id)value atIndex:(NSUInteger)index
  254. {
  255. // Properties that are higher than an unsigned value can hold are converted to a double, then inserted as a normal property.
  256. // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in putByIndex().
  257. if (index != (unsigned)index)
  258. return [self setValue:value forProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
  259. JSValueRef exception = 0;
  260. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  261. if (exception) {
  262. [_context notifyException:exception];
  263. return;
  264. }
  265. JSObjectSetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, objectToValue(_context, value), &exception);
  266. if (exception) {
  267. [_context notifyException:exception];
  268. return;
  269. }
  270. }
  271. - (BOOL)isUndefined
  272. {
  273. return JSValueIsUndefined([_context JSGlobalContextRef], m_value);
  274. }
  275. - (BOOL)isNull
  276. {
  277. return JSValueIsNull([_context JSGlobalContextRef], m_value);
  278. }
  279. - (BOOL)isBoolean
  280. {
  281. return JSValueIsBoolean([_context JSGlobalContextRef], m_value);
  282. }
  283. - (BOOL)isNumber
  284. {
  285. return JSValueIsNumber([_context JSGlobalContextRef], m_value);
  286. }
  287. - (BOOL)isString
  288. {
  289. return JSValueIsString([_context JSGlobalContextRef], m_value);
  290. }
  291. - (BOOL)isObject
  292. {
  293. return JSValueIsObject([_context JSGlobalContextRef], m_value);
  294. }
  295. - (BOOL)isEqualToObject:(id)value
  296. {
  297. return JSValueIsStrictEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value));
  298. }
  299. - (BOOL)isEqualWithTypeCoercionToObject:(id)value
  300. {
  301. JSValueRef exception = 0;
  302. BOOL result = JSValueIsEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value), &exception);
  303. if (exception)
  304. return [_context boolFromNotifyException:exception];
  305. return result;
  306. }
  307. - (BOOL)isInstanceOf:(id)value
  308. {
  309. JSValueRef exception = 0;
  310. JSObjectRef constructor = JSValueToObject([_context JSGlobalContextRef], objectToValue(_context, value), &exception);
  311. if (exception)
  312. return [_context boolFromNotifyException:exception];
  313. BOOL result = JSValueIsInstanceOfConstructor([_context JSGlobalContextRef], m_value, constructor, &exception);
  314. if (exception)
  315. return [_context boolFromNotifyException:exception];
  316. return result;
  317. }
  318. - (JSValue *)callWithArguments:(NSArray *)argumentArray
  319. {
  320. NSUInteger argumentCount = [argumentArray count];
  321. JSValueRef arguments[argumentCount];
  322. for (unsigned i = 0; i < argumentCount; ++i)
  323. arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
  324. JSValueRef exception = 0;
  325. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  326. if (exception)
  327. return [_context valueFromNotifyException:exception];
  328. JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, 0, argumentCount, arguments, &exception);
  329. if (exception)
  330. return [_context valueFromNotifyException:exception];
  331. return [JSValue valueWithJSValueRef:result inContext:_context];
  332. }
  333. - (JSValue *)constructWithArguments:(NSArray *)argumentArray
  334. {
  335. NSUInteger argumentCount = [argumentArray count];
  336. JSValueRef arguments[argumentCount];
  337. for (unsigned i = 0; i < argumentCount; ++i)
  338. arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
  339. JSValueRef exception = 0;
  340. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  341. if (exception)
  342. return [_context valueFromNotifyException:exception];
  343. JSObjectRef result = JSObjectCallAsConstructor([_context JSGlobalContextRef], object, argumentCount, arguments, &exception);
  344. if (exception)
  345. return [_context valueFromNotifyException:exception];
  346. return [JSValue valueWithJSValueRef:result inContext:_context];
  347. }
  348. - (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments
  349. {
  350. NSUInteger argumentCount = [arguments count];
  351. JSValueRef argumentArray[argumentCount];
  352. for (unsigned i = 0; i < argumentCount; ++i)
  353. argumentArray[i] = objectToValue(_context, [arguments objectAtIndex:i]);
  354. JSValueRef exception = 0;
  355. JSObjectRef thisObject = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
  356. if (exception)
  357. return [_context valueFromNotifyException:exception];
  358. JSStringRef name = JSStringCreateWithCFString((CFStringRef)method);
  359. JSValueRef function = JSObjectGetProperty([_context JSGlobalContextRef], thisObject, name, &exception);
  360. JSStringRelease(name);
  361. if (exception)
  362. return [_context valueFromNotifyException:exception];
  363. JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], function, &exception);
  364. if (exception)
  365. return [_context valueFromNotifyException:exception];
  366. JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, thisObject, argumentCount, argumentArray, &exception);
  367. if (exception)
  368. return [_context valueFromNotifyException:exception];
  369. return [JSValue valueWithJSValueRef:result inContext:_context];
  370. }
  371. @end
  372. @implementation JSValue(StructSupport)
  373. - (CGPoint)toPoint
  374. {
  375. return (CGPoint){
  376. [self[@"x"] toDouble],
  377. [self[@"y"] toDouble]
  378. };
  379. }
  380. - (NSRange)toRange
  381. {
  382. return (NSRange){
  383. [[self[@"location"] toNumber] unsignedIntegerValue],
  384. [[self[@"length"] toNumber] unsignedIntegerValue]
  385. };
  386. }
  387. - (CGRect)toRect
  388. {
  389. return (CGRect){
  390. [self toPoint],
  391. [self toSize]
  392. };
  393. }
  394. - (CGSize)toSize
  395. {
  396. return (CGSize){
  397. [self[@"width"] toDouble],
  398. [self[@"height"] toDouble]
  399. };
  400. }
  401. + (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context
  402. {
  403. return [JSValue valueWithObject:@{
  404. @"x":@(point.x),
  405. @"y":@(point.y)
  406. } inContext:context];
  407. }
  408. + (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context
  409. {
  410. return [JSValue valueWithObject:@{
  411. @"location":@(range.location),
  412. @"length":@(range.length)
  413. } inContext:context];
  414. }
  415. + (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context
  416. {
  417. return [JSValue valueWithObject:@{
  418. @"x":@(rect.origin.x),
  419. @"y":@(rect.origin.y),
  420. @"width":@(rect.size.width),
  421. @"height":@(rect.size.height)
  422. } inContext:context];
  423. }
  424. + (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context
  425. {
  426. return [JSValue valueWithObject:@{
  427. @"width":@(size.width),
  428. @"height":@(size.height)
  429. } inContext:context];
  430. }
  431. @end
  432. @implementation JSValue(SubscriptSupport)
  433. - (JSValue *)objectForKeyedSubscript:(id)key
  434. {
  435. if (![key isKindOfClass:[NSString class]]) {
  436. key = [[JSValue valueWithObject:key inContext:_context] toString];
  437. if (!key)
  438. return [JSValue valueWithUndefinedInContext:_context];
  439. }
  440. return [self valueForProperty:(NSString *)key];
  441. }
  442. - (JSValue *)objectAtIndexedSubscript:(NSUInteger)index
  443. {
  444. return [self valueAtIndex:index];
  445. }
  446. - (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key
  447. {
  448. if (![key isKindOfClass:[NSString class]]) {
  449. key = [[JSValue valueWithObject:key inContext:_context] toString];
  450. if (!key)
  451. return;
  452. }
  453. [self setValue:object forProperty:(NSString *)key];
  454. }
  455. - (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index
  456. {
  457. [self setValue:object atIndex:index];
  458. }
  459. @end
  460. inline bool isDate(JSObjectRef object, JSGlobalContextRef context)
  461. {
  462. JSC::APIEntryShim entryShim(toJS(context));
  463. return toJS(object)->inherits(&JSC::DateInstance::s_info);
  464. }
  465. inline bool isArray(JSObjectRef object, JSGlobalContextRef context)
  466. {
  467. JSC::APIEntryShim entryShim(toJS(context));
  468. return toJS(object)->inherits(&JSC::JSArray::s_info);
  469. }
  470. @implementation JSValue(Internal)
  471. enum ConversionType {
  472. ContainerNone,
  473. ContainerArray,
  474. ContainerDictionary
  475. };
  476. class JSContainerConvertor {
  477. public:
  478. struct Task {
  479. JSValueRef js;
  480. id objc;
  481. ConversionType type;
  482. };
  483. JSContainerConvertor(JSGlobalContextRef context)
  484. : m_context(context)
  485. {
  486. }
  487. id convert(JSValueRef property);
  488. void add(Task);
  489. Task take();
  490. bool isWorkListEmpty() const { return !m_worklist.size(); }
  491. private:
  492. JSGlobalContextRef m_context;
  493. HashMap<JSValueRef, id> m_objectMap;
  494. Vector<Task> m_worklist;
  495. };
  496. inline id JSContainerConvertor::convert(JSValueRef value)
  497. {
  498. HashMap<JSValueRef, id>::iterator iter = m_objectMap.find(value);
  499. if (iter != m_objectMap.end())
  500. return iter->value;
  501. Task result = valueToObjectWithoutCopy(m_context, value);
  502. if (result.js)
  503. add(result);
  504. return result.objc;
  505. }
  506. void JSContainerConvertor::add(Task task)
  507. {
  508. m_objectMap.add(task.js, task.objc);
  509. if (task.type != ContainerNone)
  510. m_worklist.append(task);
  511. }
  512. JSContainerConvertor::Task JSContainerConvertor::take()
  513. {
  514. ASSERT(!isWorkListEmpty());
  515. Task last = m_worklist.last();
  516. m_worklist.removeLast();
  517. return last;
  518. }
  519. static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef context, JSValueRef value)
  520. {
  521. if (!JSValueIsObject(context, value)) {
  522. id primitive;
  523. if (JSValueIsBoolean(context, value))
  524. primitive = JSValueToBoolean(context, value) ? @YES : @NO;
  525. else if (JSValueIsNumber(context, value)) {
  526. // Normalize the number, so it will unique correctly in the hash map -
  527. // it's nicer not to leak this internal implementation detail!
  528. value = JSValueMakeNumber(context, JSValueToNumber(context, value, 0));
  529. primitive = [NSNumber numberWithDouble:JSValueToNumber(context, value, 0)];
  530. } else if (JSValueIsString(context, value)) {
  531. // Would be nice to unique strings, too.
  532. JSStringRef jsstring = JSValueToStringCopy(context, value, 0);
  533. NSString * stringNS = (NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring);
  534. JSStringRelease(jsstring);
  535. primitive = [stringNS autorelease];
  536. } else if (JSValueIsNull(context, value))
  537. primitive = [NSNull null];
  538. else {
  539. ASSERT(JSValueIsUndefined(context, value));
  540. primitive = nil;
  541. }
  542. return (JSContainerConvertor::Task){ value, primitive, ContainerNone };
  543. }
  544. JSObjectRef object = JSValueToObject(context, value, 0);
  545. if (id wrapped = tryUnwrapObjcObject(context, object))
  546. return (JSContainerConvertor::Task){ object, wrapped, ContainerNone };
  547. if (isDate(object, context))
  548. return (JSContainerConvertor::Task){ object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0)], ContainerNone };
  549. if (isArray(object, context))
  550. return (JSContainerConvertor::Task){ object, [NSMutableArray array], ContainerArray };
  551. return (JSContainerConvertor::Task){ object, [NSMutableDictionary dictionary], ContainerDictionary };
  552. }
  553. static id containerValueToObject(JSGlobalContextRef context, JSContainerConvertor::Task task)
  554. {
  555. ASSERT(task.type != ContainerNone);
  556. JSContainerConvertor convertor(context);
  557. convertor.add(task);
  558. ASSERT(!convertor.isWorkListEmpty());
  559. do {
  560. JSContainerConvertor::Task current = convertor.take();
  561. ASSERT(JSValueIsObject(context, current.js));
  562. JSObjectRef js = JSValueToObject(context, current.js, 0);
  563. if (current.type == ContainerArray) {
  564. ASSERT([current.objc isKindOfClass:[NSMutableArray class]]);
  565. NSMutableArray *array = (NSMutableArray *)current.objc;
  566. JSStringRef lengthString = JSStringCreateWithUTF8CString("length");
  567. unsigned length = JSC::toUInt32(JSValueToNumber(context, JSObjectGetProperty(context, js, lengthString, 0), 0));
  568. JSStringRelease(lengthString);
  569. for (unsigned i = 0; i < length; ++i) {
  570. id objc = convertor.convert(JSObjectGetPropertyAtIndex(context, js, i, 0));
  571. [array addObject:objc ? objc : [NSNull null]];
  572. }
  573. } else {
  574. ASSERT([current.objc isKindOfClass:[NSMutableDictionary class]]);
  575. NSMutableDictionary *dictionary = (NSMutableDictionary *)current.objc;
  576. JSPropertyNameArrayRef propertyNameArray = JSObjectCopyPropertyNames(context, js);
  577. size_t length = JSPropertyNameArrayGetCount(propertyNameArray);
  578. for (size_t i = 0; i < length; ++i) {
  579. JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNameArray, i);
  580. if (id objc = convertor.convert(JSObjectGetProperty(context, js, propertyName, 0)))
  581. dictionary[[(NSString *)JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]] = objc;
  582. }
  583. JSPropertyNameArrayRelease(propertyNameArray);
  584. }
  585. } while (!convertor.isWorkListEmpty());
  586. return task.objc;
  587. }
  588. id valueToObject(JSContext *context, JSValueRef value)
  589. {
  590. JSContainerConvertor::Task result = valueToObjectWithoutCopy([context JSGlobalContextRef], value);
  591. if (result.type == ContainerNone)
  592. return result.objc;
  593. return containerValueToObject([context JSGlobalContextRef], result);
  594. }
  595. id valueToNumber(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
  596. {
  597. ASSERT(!*exception);
  598. if (id wrapped = tryUnwrapObjcObject(context, value)) {
  599. if ([wrapped isKindOfClass:[NSNumber class]])
  600. return wrapped;
  601. }
  602. if (JSValueIsBoolean(context, value))
  603. return JSValueToBoolean(context, value) ? @YES : @NO;
  604. double result = JSValueToNumber(context, value, exception);
  605. return [NSNumber numberWithDouble:*exception ? std::numeric_limits<double>::quiet_NaN() : result];
  606. }
  607. id valueToString(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
  608. {
  609. ASSERT(!*exception);
  610. if (id wrapped = tryUnwrapObjcObject(context, value)) {
  611. if ([wrapped isKindOfClass:[NSString class]])
  612. return wrapped;
  613. }
  614. JSStringRef jsstring = JSValueToStringCopy(context, value, exception);
  615. if (*exception) {
  616. ASSERT(!jsstring);
  617. return nil;
  618. }
  619. NSString *stringNS = [(NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring) autorelease];
  620. JSStringRelease(jsstring);
  621. return stringNS;
  622. }
  623. id valueToDate(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
  624. {
  625. ASSERT(!*exception);
  626. if (id wrapped = tryUnwrapObjcObject(context, value)) {
  627. if ([wrapped isKindOfClass:[NSDate class]])
  628. return wrapped;
  629. }
  630. double result = JSValueToNumber(context, value, exception);
  631. return *exception ? nil : [NSDate dateWithTimeIntervalSince1970:result];
  632. }
  633. id valueToArray(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
  634. {
  635. ASSERT(!*exception);
  636. if (id wrapped = tryUnwrapObjcObject(context, value)) {
  637. if ([wrapped isKindOfClass:[NSArray class]])
  638. return wrapped;
  639. }
  640. if (JSValueIsObject(context, value))
  641. return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableArray array], ContainerArray});
  642. if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value)))
  643. *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSArray"));
  644. return nil;
  645. }
  646. id valueToDictionary(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
  647. {
  648. ASSERT(!*exception);
  649. if (id wrapped = tryUnwrapObjcObject(context, value)) {
  650. if ([wrapped isKindOfClass:[NSDictionary class]])
  651. return wrapped;
  652. }
  653. if (JSValueIsObject(context, value))
  654. return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableDictionary dictionary], ContainerDictionary});
  655. if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value)))
  656. *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSDictionary"));
  657. return nil;
  658. }
  659. class ObjcContainerConvertor {
  660. public:
  661. struct Task {
  662. id objc;
  663. JSValueRef js;
  664. ConversionType type;
  665. };
  666. ObjcContainerConvertor(JSContext *context)
  667. : m_context(context)
  668. {
  669. }
  670. JSValueRef convert(id object);
  671. void add(Task);
  672. Task take();
  673. bool isWorkListEmpty() const { return !m_worklist.size(); }
  674. private:
  675. JSContext *m_context;
  676. HashMap<id, JSValueRef> m_objectMap;
  677. Vector<Task> m_worklist;
  678. };
  679. JSValueRef ObjcContainerConvertor::convert(id object)
  680. {
  681. ASSERT(object);
  682. auto it = m_objectMap.find(object);
  683. if (it != m_objectMap.end())
  684. return it->value;
  685. ObjcContainerConvertor::Task task = objectToValueWithoutCopy(m_context, object);
  686. add(task);
  687. return task.js;
  688. }
  689. void ObjcContainerConvertor::add(ObjcContainerConvertor::Task task)
  690. {
  691. m_objectMap.add(task.objc, task.js);
  692. if (task.type != ContainerNone)
  693. m_worklist.append(task);
  694. }
  695. ObjcContainerConvertor::Task ObjcContainerConvertor::take()
  696. {
  697. ASSERT(!isWorkListEmpty());
  698. Task last = m_worklist.last();
  699. m_worklist.removeLast();
  700. return last;
  701. }
  702. inline bool isNSBoolean(id object)
  703. {
  704. ASSERT([@YES class] == [@NO class]);
  705. ASSERT([@YES class] != [NSNumber class]);
  706. ASSERT([[@YES class] isSubclassOfClass:[NSNumber class]]);
  707. return [object isKindOfClass:[@YES class]];
  708. }
  709. static ObjcContainerConvertor::Task objectToValueWithoutCopy(JSContext *context, id object)
  710. {
  711. JSGlobalContextRef contextRef = [context JSGlobalContextRef];
  712. if (!object)
  713. return (ObjcContainerConvertor::Task){ object, JSValueMakeUndefined(contextRef), ContainerNone };
  714. if (!class_conformsToProtocol(object_getClass(object), getJSExportProtocol())) {
  715. if ([object isKindOfClass:[NSArray class]])
  716. return (ObjcContainerConvertor::Task){ object, JSObjectMakeArray(contextRef, 0, NULL, 0), ContainerArray };
  717. if ([object isKindOfClass:[NSDictionary class]])
  718. return (ObjcContainerConvertor::Task){ object, JSObjectMake(contextRef, 0, 0), ContainerDictionary };
  719. if ([object isKindOfClass:[NSNull class]])
  720. return (ObjcContainerConvertor::Task){ object, JSValueMakeNull(contextRef), ContainerNone };
  721. if ([object isKindOfClass:[JSValue class]])
  722. return (ObjcContainerConvertor::Task){ object, ((JSValue *)object)->m_value, ContainerNone };
  723. if ([object isKindOfClass:[NSString class]]) {
  724. JSStringRef string = JSStringCreateWithCFString((CFStringRef)object);
  725. JSValueRef js = JSValueMakeString(contextRef, string);
  726. JSStringRelease(string);
  727. return (ObjcContainerConvertor::Task){ object, js, ContainerNone };
  728. }
  729. if ([object isKindOfClass:[NSNumber class]]) {
  730. if (isNSBoolean(object))
  731. return (ObjcContainerConvertor::Task){ object, JSValueMakeBoolean(contextRef, [object boolValue]), ContainerNone };
  732. return (ObjcContainerConvertor::Task){ object, JSValueMakeNumber(contextRef, [object doubleValue]), ContainerNone };
  733. }
  734. if ([object isKindOfClass:[NSDate class]]) {
  735. JSValueRef argument = JSValueMakeNumber(contextRef, [object timeIntervalSince1970]);
  736. JSObjectRef result = JSObjectMakeDate(contextRef, 1, &argument, 0);
  737. return (ObjcContainerConvertor::Task){ object, result, ContainerNone };
  738. }
  739. if ([object isKindOfClass:[JSManagedValue class]]) {
  740. JSValue *value = [static_cast<JSManagedValue *>(object) value];
  741. if (!value)
  742. return (ObjcContainerConvertor::Task) { object, JSValueMakeUndefined(contextRef), ContainerNone };
  743. return (ObjcContainerConvertor::Task){ object, value->m_value, ContainerNone };
  744. }
  745. }
  746. return (ObjcContainerConvertor::Task){ object, valueInternalValue([context wrapperForObjCObject:object]), ContainerNone };
  747. }
  748. JSValueRef objectToValue(JSContext *context, id object)
  749. {
  750. JSGlobalContextRef contextRef = [context JSGlobalContextRef];
  751. ObjcContainerConvertor::Task task = objectToValueWithoutCopy(context, object);
  752. if (task.type == ContainerNone)
  753. return task.js;
  754. ObjcContainerConvertor convertor(context);
  755. convertor.add(task);
  756. ASSERT(!convertor.isWorkListEmpty());
  757. do {
  758. ObjcContainerConvertor::Task current = convertor.take();
  759. ASSERT(JSValueIsObject(contextRef, current.js));
  760. JSObjectRef js = JSValueToObject(contextRef, current.js, 0);
  761. if (current.type == ContainerArray) {
  762. ASSERT([current.objc isKindOfClass:[NSArray class]]);
  763. NSArray *array = (NSArray *)current.objc;
  764. NSUInteger count = [array count];
  765. for (NSUInteger index = 0; index < count; ++index)
  766. JSObjectSetPropertyAtIndex(contextRef, js, index, convertor.convert([array objectAtIndex:index]), 0);
  767. } else {
  768. ASSERT(current.type == ContainerDictionary);
  769. ASSERT([current.objc isKindOfClass:[NSDictionary class]]);
  770. NSDictionary *dictionary = (NSDictionary *)current.objc;
  771. for (id key in [dictionary keyEnumerator]) {
  772. if ([key isKindOfClass:[NSString class]]) {
  773. JSStringRef propertyName = JSStringCreateWithCFString((CFStringRef)key);
  774. JSObjectSetProperty(contextRef, js, propertyName, convertor.convert([dictionary objectForKey:key]), 0, 0);
  775. JSStringRelease(propertyName);
  776. }
  777. }
  778. }
  779. } while (!convertor.isWorkListEmpty());
  780. return task.js;
  781. }
  782. JSValueRef valueInternalValue(JSValue * value)
  783. {
  784. return value->m_value;
  785. }
  786. + (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context
  787. {
  788. return [context wrapperForJSObject:value];
  789. }
  790. - (JSValue *)init
  791. {
  792. return nil;
  793. }
  794. - (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context
  795. {
  796. if (!value || !context)
  797. return nil;
  798. self = [super init];
  799. if (!self)
  800. return nil;
  801. _context = [context retain];
  802. m_value = value;
  803. JSValueProtect([_context JSGlobalContextRef], m_value);
  804. return self;
  805. }
  806. struct StructTagHandler {
  807. SEL typeToValueSEL;
  808. SEL valueToTypeSEL;
  809. };
  810. typedef HashMap<String, StructTagHandler> StructHandlers;
  811. static StructHandlers* createStructHandlerMap()
  812. {
  813. StructHandlers* structHandlers = new StructHandlers();
  814. size_t valueWithXinContextLength = strlen("valueWithX:inContext:");
  815. size_t toXLength = strlen("toX");
  816. // Step 1: find all valueWith<Foo>:inContext: class methods in JSValue.
  817. forEachMethodInClass(object_getClass([JSValue class]), ^(Method method){
  818. SEL selector = method_getName(method);
  819. const char* name = sel_getName(selector);
  820. size_t nameLength = strlen(name);
  821. // Check for valueWith<Foo>:context:
  822. if (nameLength < valueWithXinContextLength || memcmp(name, "valueWith", 9) || memcmp(name + nameLength - 11, ":inContext:", 11))
  823. return;
  824. // Check for [ id, SEL, <type>, <contextType> ]
  825. if (method_getNumberOfArguments(method) != 4)
  826. return;
  827. char idType[3];
  828. // Check 2nd argument type is "@"
  829. char* secondType = method_copyArgumentType(method, 3);
  830. if (strcmp(secondType, "@") != 0) {
  831. free(secondType);
  832. return;
  833. }
  834. free(secondType);
  835. // Check result type is also "@"
  836. method_getReturnType(method, idType, 3);
  837. if (strcmp(idType, "@") != 0)
  838. return;
  839. char* type = method_copyArgumentType(method, 2);
  840. structHandlers->add(StringImpl::create(type), (StructTagHandler){ selector, 0 });
  841. free(type);
  842. });
  843. // Step 2: find all to<Foo> instance methods in JSValue.
  844. forEachMethodInClass([JSValue class], ^(Method method){
  845. SEL selector = method_getName(method);
  846. const char* name = sel_getName(selector);
  847. size_t nameLength = strlen(name);
  848. // Check for to<Foo>
  849. if (nameLength < toXLength || memcmp(name, "to", 2))
  850. return;
  851. // Check for [ id, SEL ]
  852. if (method_getNumberOfArguments(method) != 2)
  853. return;
  854. // Try to find a matching valueWith<Foo>:context: method.
  855. char* type = method_copyReturnType(method);
  856. StructHandlers::iterator iter = structHandlers->find(type);
  857. free(type);
  858. if (iter == structHandlers->end())
  859. return;
  860. StructTagHandler& handler = iter->value;
  861. // check that strlen(<foo>) == strlen(<Foo>)
  862. const char* valueWithName = sel_getName(handler.typeToValueSEL);
  863. size_t valueWithLength = strlen(valueWithName);
  864. if (valueWithLength - valueWithXinContextLength != nameLength - toXLength)
  865. return;
  866. // Check that <Foo> == <Foo>
  867. if (memcmp(valueWithName + 9, name + 2, nameLength - toXLength - 1))
  868. return;
  869. handler.valueToTypeSEL = selector;
  870. });
  871. // Step 3: clean up - remove entries where we found prospective valueWith<Foo>:inContext: conversions, but no matching to<Foo> methods.
  872. typedef HashSet<String> RemoveSet;
  873. RemoveSet removeSet;
  874. for (StructHandlers::iterator iter = structHandlers->begin(); iter != structHandlers->end(); ++iter) {
  875. StructTagHandler& handler = iter->value;
  876. if (!handler.valueToTypeSEL)
  877. removeSet.add(iter->key);
  878. }
  879. for (RemoveSet::iterator iter = removeSet.begin(); iter != removeSet.end(); ++iter)
  880. structHandlers->remove(*iter);
  881. return structHandlers;
  882. }
  883. static StructTagHandler* handerForStructTag(const char* encodedType)
  884. {
  885. static SpinLock handerForStructTagLock = SPINLOCK_INITIALIZER;
  886. SpinLockHolder lockHolder(&handerForStructTagLock);
  887. static StructHandlers* structHandlers = createStructHandlerMap();
  888. StructHandlers::iterator iter = structHandlers->find(encodedType);
  889. if (iter == structHandlers->end())
  890. return 0;
  891. return &iter->value;
  892. }
  893. + (SEL)selectorForStructToValue:(const char *)structTag
  894. {
  895. StructTagHandler* handler = handerForStructTag(structTag);
  896. return handler ? handler->typeToValueSEL : nil;
  897. }
  898. + (SEL)selectorForValueToStruct:(const char *)structTag
  899. {
  900. StructTagHandler* handler = handerForStructTag(structTag);
  901. return handler ? handler->valueToTypeSEL : nil;
  902. }
  903. - (void)dealloc
  904. {
  905. JSValueUnprotect([_context JSGlobalContextRef], m_value);
  906. [_context release];
  907. _context = nil;
  908. [super dealloc];
  909. }
  910. - (NSString *)description
  911. {
  912. if (id wrapped = tryUnwrapObjcObject([_context JSGlobalContextRef], m_value))
  913. return [wrapped description];
  914. return [self toString];
  915. }
  916. NSInvocation *typeToValueInvocationFor(const char* encodedType)
  917. {
  918. SEL selector = [JSValue selectorForStructToValue:encodedType];
  919. if (!selector)
  920. return 0;
  921. const char* methodTypes = method_getTypeEncoding(class_getClassMethod([JSValue class], selector));
  922. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
  923. [invocation setSelector:selector];
  924. return invocation;
  925. }
  926. NSInvocation *valueToTypeInvocationFor(const char* encodedType)
  927. {
  928. SEL selector = [JSValue selectorForValueToStruct:encodedType];
  929. if (!selector)
  930. return 0;
  931. const char* methodTypes = method_getTypeEncoding(class_getInstanceMethod([JSValue class], selector));
  932. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
  933. [invocation setSelector:selector];
  934. return invocation;
  935. }
  936. @end
  937. #endif