Changeset 243672 in webkit
- Timestamp:
- Mar 29, 2019, 6:30:16 PM (7 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 7 edited
-
API/JSContext.mm (modified) (1 diff)
-
API/JSContextInternal.h (modified) (1 diff)
-
API/JSValue.mm (modified) (2 diffs)
-
API/JSWrapperMap.h (modified) (1 diff)
-
API/JSWrapperMap.mm (modified) (4 diffs)
-
API/tests/testapi.mm (modified) (1 diff)
-
ChangeLog (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/API/JSContext.mm
r243617 r243672 370 370 } 371 371 372 - (void)removeWrapper:(JSValue *)value 373 { 374 return [[self wrapperMap] removeWrapper:value]; 375 } 376 372 377 + (JSContext *)contextWithJSGlobalContextRef:(JSGlobalContextRef)globalContext 373 378 { -
trunk/Source/JavaScriptCore/API/JSContextInternal.h
r240511 r243672 55 55 - (JSValue *)wrapperForObjCObject:(id)object; 56 56 - (JSValue *)wrapperForJSObject:(JSValueRef)value; 57 - (void)removeWrapper:(JSValue *)value; 57 58 58 59 @end -
trunk/Source/JavaScriptCore/API/JSValue.mm
r238074 r243672 72 72 - (void)dealloc 73 73 { 74 [_context removeWrapper:self]; 74 75 JSValueUnprotect([_context JSGlobalContextRef], m_value); 75 76 [_context release]; … … 1076 1077 return nil; 1077 1078 1079 ASSERT(context); 1078 1080 _context = [context retain]; 1079 1081 m_value = value; -
trunk/Source/JavaScriptCore/API/JSWrapperMap.h
r218379 r243672 38 38 - (JSValue *)objcWrapperForJSValueRef:(JSValueRef)value inContext:(JSContext *)context; 39 39 40 - (void)removeWrapper:(JSValue *)wrapper; 41 40 42 @end 41 43 -
trunk/Source/JavaScriptCore/API/JSWrapperMap.mm
r241956 r243672 582 582 @end 583 583 584 struct WrapperKey { 585 static constexpr uintptr_t hashTableDeletedValue() { return 1; } 586 587 WrapperKey() = default; 588 589 explicit WrapperKey(WTF::HashTableDeletedValueType) 590 : m_wrapper(reinterpret_cast<JSValue *>(hashTableDeletedValue())) 591 { 592 } 593 594 explicit WrapperKey(JSValue *wrapper) 595 : m_wrapper(wrapper) 596 { 597 } 598 599 bool isHashTableDeletedValue() const 600 { 601 return reinterpret_cast<uintptr_t>(m_wrapper) == hashTableDeletedValue(); 602 } 603 604 __unsafe_unretained JSValue *m_wrapper { nil }; 605 606 struct Hash { 607 static unsigned hash(const WrapperKey& key) 608 { 609 return DefaultHash<JSValueRef>::Hash::hash([key.m_wrapper JSValueRef]); 610 } 611 612 static bool equal(const WrapperKey& lhs, const WrapperKey& rhs) 613 { 614 return lhs.m_wrapper == rhs.m_wrapper; 615 } 616 617 static const bool safeToCompareToEmptyOrDeleted = false; 618 }; 619 620 struct Traits : public SimpleClassHashTraits<WrapperKey> { 621 static const bool hasIsEmptyValueFunction = true; 622 static bool isEmptyValue(const WrapperKey& key) 623 { 624 return key.m_wrapper == nullptr; 625 } 626 }; 627 628 struct Translator { 629 struct ValueAndContext { 630 __unsafe_unretained JSContext *m_context; 631 JSValueRef m_value; 632 }; 633 634 static unsigned hash(const ValueAndContext& value) 635 { 636 return DefaultHash<JSValueRef>::Hash::hash(value.m_value); 637 } 638 639 static bool equal(const WrapperKey& lhs, const ValueAndContext& value) 640 { 641 return [lhs.m_wrapper JSValueRef] == value.m_value; 642 } 643 644 static void translate(WrapperKey& result, const ValueAndContext& value, unsigned) 645 { 646 result = WrapperKey([[[JSValue alloc] initWithValue:value.m_value inContext:value.m_context] autorelease]); 647 } 648 }; 649 }; 650 584 651 @implementation JSWrapperMap { 585 652 NSMutableDictionary *m_classMap; 586 653 std::unique_ptr<JSC::WeakGCMap<__unsafe_unretained id, JSC::JSObject>> m_cachedJSWrappers; 587 NSMapTable *m_cachedObjCWrappers;654 HashSet<WrapperKey, WrapperKey::Hash, WrapperKey::Traits> m_cachedObjCWrappers; 588 655 } 589 656 … … 593 660 if (!self) 594 661 return nil; 595 596 NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;597 NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;598 m_cachedObjCWrappers = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];599 662 600 663 m_cachedJSWrappers = std::make_unique<JSC::WeakGCMap<__unsafe_unretained id, JSC::JSObject>>(toJS(context)->vm()); … … 608 671 - (void)dealloc 609 672 { 610 [m_cachedObjCWrappers release];611 673 [m_classMap release]; 612 674 [super dealloc]; … … 663 725 { 664 726 ASSERT(toJSGlobalObject([context JSGlobalContextRef])->wrapperMap() == self); 665 JSValue *wrapper = (__bridge JSValue *)NSMapGet(m_cachedObjCWrappers, value); 666 if (!wrapper) { 667 wrapper = [[[JSValue alloc] initWithValue:value inContext:context] autorelease]; 668 NSMapInsert(m_cachedObjCWrappers, value, (__bridge void*)wrapper); 669 } 670 return wrapper; 727 WrapperKey::Translator::ValueAndContext valueAndContext { context, value }; 728 auto addResult = m_cachedObjCWrappers.add<WrapperKey::Translator>(valueAndContext); 729 return addResult.iterator->m_wrapper; 730 } 731 732 - (void)removeWrapper:(JSValue *)wrapper 733 { 734 m_cachedObjCWrappers.remove(WrapperKey(wrapper)); 671 735 } 672 736 -
trunk/Source/JavaScriptCore/API/tests/testapi.mm
r242982 r243672 566 566 { 567 567 @autoreleasepool { 568 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];569 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];568 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init]; 569 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm]; 570 570 [context evaluateScript:@"bad"]; 571 } 572 573 @autoreleasepool { 574 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init]; 575 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm]; 576 JSValue *number1 = [context evaluateScript:@"42092389"]; 577 JSValue *number2 = [context evaluateScript:@"42092389"]; 578 checkResult(@"wrapper cache for numbers", number1 == number2 && number1.isNumber && [number1 toInt32] == 42092389); 579 } 580 581 @autoreleasepool { 582 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init]; 583 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm]; 584 JSValue *object1 = [context evaluateScript:@"({})"]; 585 JSValue *object2 = [context evaluateScript:@"({})"]; 586 checkResult(@"wrapper cache for objects", object1 != object2); 571 587 } 572 588 -
trunk/Source/JavaScriptCore/ChangeLog
r243670 r243672 1 2019-03-29 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] JSWrapperMap should not use Objective-C Weak map (NSMapTable with NSPointerFunctionsWeakMemory) for m_cachedObjCWrappers 4 https://bugs.webkit.org/show_bug.cgi?id=196392 5 6 Reviewed by Saam Barati. 7 8 Weak representation in Objective-C is surprisingly costly in terms of memory. We can see that very easy program shows 10KB memory consumption due to 9 this weak wrapper map in JavaScriptCore.framework. But we do not need this weak map since Objective-C JSValue has a dealloc. We can unregister itself 10 from the map when it is deallocated without using Objective-C weak mechanism. And since Objective-C JSValue is tightly coupled to a specific JSContext, 11 and wrapper map is created per JSContext, JSValue wrapper and actual JavaScriptCore value is one-on-one, and [JSValue dealloc] knows which JSContext's 12 wrapper map holds itself. 13 14 1. We do not use Objective-C weak mechanism. We use WTF::HashSet instead. When JSValue is allocated, we register it to JSWrapperMap's HashSet. And unregister 15 JSValue from this map when JSValue is deallocated. 16 2. We use HashSet<JSValue> (logically) instead of HashMap<JSValueRef, JSValue> to keep JSValueRef and JSValue relationship. We can achieve it because JSValue 17 holds JSValueRef inside it. 18 19 * API/JSContext.mm: 20 (-[JSContext removeWrapper:]): 21 * API/JSContextInternal.h: 22 * API/JSValue.mm: 23 (-[JSValue dealloc]): 24 (-[JSValue initWithValue:inContext:]): 25 * API/JSWrapperMap.h: 26 * API/JSWrapperMap.mm: 27 (WrapperKey::hashTableDeletedValue): 28 (WrapperKey::WrapperKey): 29 (WrapperKey::isHashTableDeletedValue const): 30 (WrapperKey::Hash::hash): 31 (WrapperKey::Hash::equal): 32 (WrapperKey::Traits::isEmptyValue): 33 (WrapperKey::Translator::hash): 34 (WrapperKey::Translator::equal): 35 (WrapperKey::Translator::translate): 36 (-[JSWrapperMap initWithGlobalContextRef:]): 37 (-[JSWrapperMap dealloc]): 38 (-[JSWrapperMap objcWrapperForJSValueRef:inContext:]): 39 (-[JSWrapperMap removeWrapper:]): 40 * API/tests/testapi.mm: 41 (testObjectiveCAPIMain): 42 1 43 2019-03-29 Robin Morisset <rmorisset@apple.com> 2 44
Note:
See TracChangeset
for help on using the changeset viewer.