Changeset 146558 in webkit
- Timestamp:
- Mar 21, 2013 8:12:49 PM (11 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/API/JSAPIWrapperObject.h
r146494 r146558 56 56 } // namespace JSC 57 57 58 #endif 58 #endif // JSC_OBJC_API_ENABLED 59 59 60 60 #endif // JSAPIWrapperObject_h -
trunk/Source/JavaScriptCore/API/JSAPIWrapperObject.mm
r146494 r146558 30 30 #include "JSCallbackObject.h" 31 31 #include "JSCellInlines.h" 32 #include "JSVirtualMachineInternal.h" 32 33 #include "SlotVisitorInlines.h" 33 34 #include "Structure.h" … … 39 40 public: 40 41 virtual void finalize(JSC::Handle<JSC::Unknown>, void*); 42 virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&); 41 43 }; 42 44 … … 49 51 void JSAPIWrapperObjectHandleOwner::finalize(JSC::Handle<JSC::Unknown> handle, void*) 50 52 { 51 JSC::WeakSet::deallocate(JSC::WeakImpl::asWeakImpl(handle.slot()));52 53 JSC::JSAPIWrapperObject* wrapperObject = JSC::jsCast<JSC::JSAPIWrapperObject*>(handle.get().asCell()); 53 54 if (!wrapperObject->wrappedObject()) 54 55 return; 55 56 [static_cast<id>(wrapperObject->wrappedObject()) release]; 57 JSC::WeakSet::deallocate(JSC::WeakImpl::asWeakImpl(handle.slot())); 58 } 59 60 bool JSAPIWrapperObjectHandleOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, JSC::SlotVisitor& visitor) 61 { 62 JSC::JSAPIWrapperObject* wrapperObject = JSC::jsCast<JSC::JSAPIWrapperObject*>(handle.get().asCell()); 63 // We use the JSGlobalObject when processing weak handles to prevent the situation where using 64 // the same Objective-C object in multiple global objects keeps all of the global objects alive. 65 if (!wrapperObject->wrappedObject()) 66 return false; 67 return JSC::Heap::isMarked(wrapperObject->structure()->globalObject()) && visitor.containsOpaqueRoot(wrapperObject->wrappedObject()); 56 68 } 57 69 … … 93 105 94 106 if (thisObject->wrappedObject()) 95 visitor.addOpaqueRoot(thisObject->wrappedObject());107 scanExternalObjectGraph(cell->structure()->globalObject()->globalData(), visitor, thisObject->wrappedObject()); 96 108 } 97 109 -
trunk/Source/JavaScriptCore/API/JSContext.mm
r145119 r146558 79 79 } 80 80 81 - (void)dealloc 82 { 83 [m_wrapperMap release]; 84 JSGlobalContextRelease(m_context); 85 [m_virtualMachine release]; 86 [self.exceptionHandler release]; 87 [super dealloc]; 88 } 89 81 90 - (JSValue *)evaluateScript:(NSString *)script 82 91 { … … 196 205 } 197 206 198 - (void)dealloc199 {200 [m_wrapperMap release];201 JSGlobalContextRelease(m_context);202 [m_virtualMachine release];203 [self.exceptionHandler release];204 [super dealloc];205 }206 207 207 - (void)notifyException:(JSValueRef)exceptionValue 208 208 { -
trunk/Source/JavaScriptCore/API/JSVirtualMachine.h
r143637 r146558 38 38 - (id)init; 39 39 40 - (void)addManagedReference:(id)object withOwner:(id)owner; 41 - (void)removeManagedReference:(id)object withOwner:(id)owner; 42 40 43 @end 41 44 -
trunk/Source/JavaScriptCore/API/JSVirtualMachine.mm
r145119 r146558 31 31 32 32 #import "APICast.h" 33 #import "APIShims.h" 34 #import "JSVirtualMachine.h" 33 35 #import "JSVirtualMachineInternal.h" 36 #import "JSWrapperMap.h" 34 37 35 38 static NSMapTable *globalWrapperCache = 0; … … 80 83 JSContextGroupRef m_group; 81 84 NSMapTable *m_contextCache; 85 NSMapTable *m_externalObjectGraph; 82 86 } 83 87 … … 102 106 NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; 103 107 m_contextCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0]; 108 109 NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; 110 NSPointerFunctionsOptions strongIDOptions = NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality; 111 m_externalObjectGraph = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:strongIDOptions capacity:0]; 104 112 105 113 [JSVMWrapperCache addWrapper:self forJSContextGroupRef:group]; … … 108 116 } 109 117 118 - (void)dealloc 119 { 120 JSContextGroupRelease(m_group); 121 [m_contextCache release]; 122 [m_externalObjectGraph release]; 123 [super dealloc]; 124 } 125 126 static id getInternalObjcObject(id object) 127 { 128 if ([object isKindOfClass:[JSManagedValue class]]) 129 object = [static_cast<JSManagedValue *>(object) value]; 130 131 if ([object isKindOfClass:[JSValue class]]) { 132 JSValue *value = static_cast<JSValue *>(object); 133 object = tryUnwrapObjcObject([value.context globalContextRef], [value JSValueRef]); 134 } 135 136 return object; 137 } 138 139 - (void)addManagedReference:(id)object withOwner:(id)owner 140 { 141 object = getInternalObjcObject(object); 142 owner = getInternalObjcObject(owner); 143 144 if (!object || !owner) 145 return; 146 147 JSC::APIEntryShim shim(toJS(m_group)); 148 149 NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner]; 150 if (!ownedObjects) { 151 NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; 152 NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality; 153 ownedObjects = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1]; 154 155 [m_externalObjectGraph setObject:ownedObjects forKey:owner]; 156 [ownedObjects release]; 157 } 158 NSMapInsert(ownedObjects, object, reinterpret_cast<void*>(reinterpret_cast<size_t>(NSMapGet(ownedObjects, object)) + 1)); 159 } 160 161 - (void)removeManagedReference:(id)object withOwner:(id)owner 162 { 163 object = getInternalObjcObject(object); 164 owner = getInternalObjcObject(owner); 165 166 if (!object || !owner) 167 return; 168 169 JSC::APIEntryShim shim(toJS(m_group)); 170 171 NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner]; 172 if (!ownedObjects) 173 return; 174 175 size_t count = reinterpret_cast<size_t>(NSMapGet(ownedObjects, object)); 176 if (count > 1) { 177 NSMapInsert(ownedObjects, object, reinterpret_cast<void*>(count - 1)); 178 return; 179 } 180 181 if (count == 1) 182 NSMapRemove(ownedObjects, object); 183 184 if (![ownedObjects count]) 185 [m_externalObjectGraph removeObjectForKey:owner]; 186 } 187 110 188 @end 111 189 112 190 @implementation JSVirtualMachine(Internal) 113 114 - (void)dealloc115 {116 JSContextGroupRelease(m_group);117 [super dealloc];118 }119 191 120 192 JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine) … … 141 213 } 142 214 143 @end 144 215 - (NSMapTable *)externalObjectGraph 216 { 217 return m_externalObjectGraph; 218 } 219 220 @end 221 222 void scanExternalObjectGraph(JSC::JSGlobalData& globalData, JSC::SlotVisitor& visitor, void* root) 223 { 224 @autoreleasepool { 225 JSVirtualMachine *virtualMachine = [JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&globalData)]; 226 NSMapTable *externalObjectGraph = [virtualMachine externalObjectGraph]; 227 Vector<void*> stack; 228 stack.append(root); 229 while (!stack.isEmpty()) { 230 void* nextRoot = stack.last(); 231 stack.removeLast(); 232 if (visitor.containsOpaqueRootTriState(nextRoot) == TrueTriState) 233 continue; 234 visitor.addOpaqueRoot(nextRoot); 235 236 NSMapTable *ownedObjects = [externalObjectGraph objectForKey:static_cast<id>(nextRoot)]; 237 id ownedObject; 238 NSEnumerator *enumerator = [ownedObjects keyEnumerator]; 239 while ((ownedObject = [enumerator nextObject])) { 240 ASSERT(reinterpret_cast<size_t>(NSMapGet(ownedObjects, ownedObject)) == 1); 241 stack.append(static_cast<void*>(ownedObject)); 242 } 243 } 244 } 245 } 145 246 146 247 #endif -
trunk/Source/JavaScriptCore/API/JSVirtualMachineInternal.h
r143637 r146558 27 27 #define JSVirtualMachineInternal_h 28 28 29 #import <JavaScriptCore/JSVirtualMachine.h>30 29 #import <JavaScriptCore/JavaScriptCore.h> 31 30 32 31 #if JSC_OBJC_API_ENABLED 33 32 33 namespace JSC { 34 class JSGlobalData; 35 class SlotVisitor; 36 } 37 38 #if defined(__OBJC__) 34 39 @interface JSVirtualMachine(Internal) 35 40 … … 41 46 - (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext; 42 47 48 - (NSMapTable *)externalObjectGraph; 49 43 50 @end 51 #endif // defined(__OBJC__) 52 53 void scanExternalObjectGraph(JSC::JSGlobalData&, JSC::SlotVisitor&, void* root); 44 54 45 55 #endif -
trunk/Source/JavaScriptCore/API/JSWrapperMap.mm
r146494 r146558 435 435 - (void)dealloc 436 436 { 437 [m_cachedObjCWrappers release]; 437 438 [m_classMap release]; 438 439 [super dealloc]; -
trunk/Source/JavaScriptCore/API/tests/testapi.mm
r146494 r146558 154 154 @end 155 155 156 @class TinyDOMNode; 157 158 @protocol TinyDOMNode <JSExport> 159 - (void)appendChild:(TinyDOMNode *)child; 160 - (NSUInteger)numberOfChildren; 161 - (TinyDOMNode *)childAtIndex:(NSUInteger)index; 162 - (void)removeChildAtIndex:(NSUInteger)index; 163 @end 164 165 @interface TinyDOMNode : NSObject<TinyDOMNode> 166 + (JSVirtualMachine *)sharedVirtualMachine; 167 + (void)clearSharedVirtualMachine; 168 @end 169 170 @implementation TinyDOMNode { 171 NSMutableArray *m_children; 172 } 173 174 static JSVirtualMachine *sharedInstance = nil; 175 176 + (JSVirtualMachine *)sharedVirtualMachine 177 { 178 if (!sharedInstance) 179 sharedInstance = [[JSVirtualMachine alloc] init]; 180 return sharedInstance; 181 } 182 183 + (void)clearSharedVirtualMachine 184 { 185 sharedInstance = nil; 186 } 187 188 - (id)init 189 { 190 self = [super init]; 191 if (!self) 192 return nil; 193 194 m_children = [[NSMutableArray alloc] initWithCapacity:0]; 195 196 return self; 197 } 198 199 - (void)dealloc 200 { 201 NSEnumerator *enumerator = [m_children objectEnumerator]; 202 id nextChild; 203 while ((nextChild = [enumerator nextObject])) 204 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:nextChild withOwner:self]; 205 } 206 207 - (void)appendChild:(TinyDOMNode *)child 208 { 209 [[TinyDOMNode sharedVirtualMachine] addManagedReference:child withOwner:self]; 210 [m_children addObject:child]; 211 } 212 213 - (NSUInteger)numberOfChildren 214 { 215 return [m_children count]; 216 } 217 218 - (TinyDOMNode *)childAtIndex:(NSUInteger)index 219 { 220 if (index >= [m_children count]) 221 return nil; 222 return [m_children objectAtIndex:index]; 223 } 224 225 - (void)removeChildAtIndex:(NSUInteger)index 226 { 227 if (index >= [m_children count]) 228 return; 229 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:[m_children objectAtIndex:index] withOwner:self]; 230 [m_children removeObjectAtIndex:index]; 231 } 232 233 @end 234 156 235 static void checkResult(NSString *description, bool passed) 157 236 { … … 649 728 } 650 729 730 @autoreleasepool { 731 JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine]; 732 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm]; 733 TinyDOMNode *root = [[TinyDOMNode alloc] init]; 734 TinyDOMNode *lastNode = root; 735 for (NSUInteger i = 0; i < 3; i++) { 736 TinyDOMNode *newNode = [[TinyDOMNode alloc] init]; 737 [lastNode appendChild:newNode]; 738 lastNode = newNode; 739 } 740 741 @autoreleasepool { 742 context[@"root"] = root; 743 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){ 744 TinyDOMNode *lastNode = nil; 745 while (head) { 746 lastNode = head; 747 head = [lastNode childAtIndex:0]; 748 } 749 return lastNode; 750 }; 751 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"]; 752 } 753 754 JSSynchronousGarbageCollectForDebugging([context globalContextRef]); 755 756 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"]; 757 checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42); 758 759 [TinyDOMNode clearSharedVirtualMachine]; 760 } 761 762 @autoreleasepool { 763 JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine]; 764 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm]; 765 TinyDOMNode *root = [[TinyDOMNode alloc] init]; 766 TinyDOMNode *lastNode = root; 767 for (NSUInteger i = 0; i < 3; i++) { 768 TinyDOMNode *newNode = [[TinyDOMNode alloc] init]; 769 [lastNode appendChild:newNode]; 770 lastNode = newNode; 771 } 772 773 @autoreleasepool { 774 context[@"root"] = root; 775 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){ 776 TinyDOMNode *lastNode = nil; 777 while (head) { 778 lastNode = head; 779 head = [lastNode childAtIndex:0]; 780 } 781 return lastNode; 782 }; 783 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"]; 784 785 [root appendChild:[root childAtIndex:0]]; 786 [root removeChildAtIndex:0]; 787 } 788 789 JSSynchronousGarbageCollectForDebugging([context globalContextRef]); 790 791 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"]; 792 checkResult(@"duplicate calls to addManagedReference don't cause things to die", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42); 793 794 [TinyDOMNode clearSharedVirtualMachine]; 795 } 651 796 } 652 797 -
trunk/Source/JavaScriptCore/ChangeLog
r146552 r146558 1 2013-03-21 Mark Hahnenberg <mhahnenberg@apple.com> 2 3 Objective-C API: Need a good way to preserve custom properties on JS wrappers 4 https://bugs.webkit.org/show_bug.cgi?id=112608 5 6 Reviewed by Geoffrey Garen. 7 8 Currently, we just use a weak map, which means that garbage collection can cause a wrapper to 9 disappear if it isn't directly exported to JavaScript. 10 11 The most straightforward and safe way (with respect to garbage collection and concurrency) is to have 12 clients add and remove their external references along with their owners. Effectively, the client is 13 recording the structure of the external object graph so that the garbage collector can make sure to 14 mark any wrappers that are reachable through either the JS object graph of the external Obj-C object 15 graph. By keeping these wrappers alive, this has the effect that custom properties on these wrappers 16 will also remain alive. 17 18 The rule for if an object needs to be tracked by the runtime (and therefore whether the client should report it) is as follows: 19 For a particular object, its references to its children should be added if: 20 1. The child is referenced from JavaScript. 21 2. The child contains references to other objects for which (1) or (2) are true. 22 23 * API/JSAPIWrapperObject.mm: 24 (JSAPIWrapperObjectHandleOwner::finalize): 25 (JSAPIWrapperObjectHandleOwner::isReachableFromOpaqueRoots): A wrapper object is kept alive only if its JSGlobalObject 26 is marked and its corresponding Objective-C object was added to the set of opaque roots. 27 (JSC::JSAPIWrapperObject::visitChildren): We now call out to scanExternalObjectGraph, which handles adding all Objective-C 28 objects to the set of opaque roots. 29 * API/JSAPIWrapperObject.h: 30 (JSAPIWrapperObject): 31 * API/JSContext.mm: Moved dealloc to its proper place in the main implementation. 32 (-[JSContext dealloc]): 33 * API/JSVirtualMachine.h: 34 * API/JSVirtualMachine.mm: 35 (-[JSVirtualMachine initWithContextGroupRef:]): 36 (-[JSVirtualMachine dealloc]): 37 (getInternalObjcObject): Helper funciton to get the Objective-C object out of JSManagedValues or JSValues if there is one. 38 (-[JSVirtualMachine addManagedReference:withOwner:]): Adds the Objective-C object to the set of objects 39 owned by the owner object in that particular virtual machine. 40 (-[JSVirtualMachine removeManagedReference:withOwner:]): Removes the relationship between the two objects. 41 (-[JSVirtualMachine externalObjectGraph]): 42 (scanExternalObjectGraph): Does a depth-first search of the external object graph in a particular virtual machine starting at 43 the specified root. Each new object it encounters it adds to the set of opaque roots. These opaque roots will keep their 44 corresponding wrapper objects alive if they have them. 45 * API/JSManagedReferenceInternal.h: Added. 46 * API/JSVirtualMachine.mm: Added the per-JSVirtualMachine map between objects and the objects they own, which is more formally 47 known as that virtual machine's external object graph. 48 * API/JSWrapperMap.mm: 49 (-[JSWrapperMap dealloc]): We were leaking this before :-( 50 (-[JSVirtualMachine initWithContextGroupRef:]): 51 (-[JSVirtualMachine dealloc]): 52 (-[JSVirtualMachine externalObjectGraph]): 53 * API/JSVirtualMachineInternal.h: 54 * API/tests/testapi.mm: Added two new tests using the TinyDOMNode class. The first tests that a custom property added to a wrapper 55 doesn't vanish after GC, even though that wrapper isn't directly accessible to the JS garbage collector but is accessible through 56 the external Objective-C object graph. The second test makes sure that adding an object to the external object graph with the same 57 owner doesn't cause any sort of problems. 58 (+[TinyDOMNode sharedVirtualMachine]): 59 (-[TinyDOMNode init]): 60 (-[TinyDOMNode dealloc]): 61 (-[TinyDOMNode appendChild:]): 62 (-[TinyDOMNode numberOfChildren]): 63 (-[TinyDOMNode childAtIndex:]): 64 (-[TinyDOMNode removeChildAtIndex:]): 65 * JavaScriptCore.xcodeproj/project.pbxproj: 66 * heap/SlotVisitor.h: 67 (SlotVisitor): 68 * heap/SlotVisitorInlines.h: 69 (JSC::SlotVisitor::containsOpaqueRootTriState): Added a new method to SlotVisitor to allow scanExternalObjectGraph to have a 70 thread-safe view of opaque roots during parallel marking. The set of opaque roots available to any one SlotVisitor isn't guaranteed 71 to be 100% correct, but that just results in a small duplication of work in scanExternalObjectGraph. To indicate this change for 72 false negatives we return a TriState that's either true or mixed, but never false. 73 1 74 2013-03-21 Mark Lam <mark.lam@apple.com> 2 75 -
trunk/Source/JavaScriptCore/heap/SlotVisitor.h
r146383 r146558 63 63 void addOpaqueRoot(void*); 64 64 bool containsOpaqueRoot(void*); 65 TriState containsOpaqueRootTriState(void*); 65 66 int opaqueRootCount(); 66 67 -
trunk/Source/JavaScriptCore/heap/SlotVisitorInlines.h
r138067 r146558 122 122 } 123 123 124 inline TriState SlotVisitor::containsOpaqueRootTriState(void* root) 125 { 126 if (m_opaqueRoots.contains(root)) 127 return TrueTriState; 128 MutexLocker locker(m_shared.m_opaqueRootsLock); 129 if (m_shared.m_opaqueRoots.contains(root)) 130 return TrueTriState; 131 return MixedTriState; 132 } 133 124 134 inline int SlotVisitor::opaqueRootCount() 125 135 {
Note: See TracChangeset
for help on using the changeset viewer.