// 1. C++ Code class MyClass { public: MyClass() {} ~MyClass() {} }; JSValueRef GetX(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) { // do nothing. return nullptr; } bool SetX(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) { return true; } JSStaticValue fields[] = { { "X", GetX, SetX, kJSPropertyAttributeNone }, { 0, 0, 0, 0 } }; void JSObjectFinalizeCallback(JSObjectRef object) { delete static_cast<MyClass*>(JSObjectGetPrivate(object)); } JSValueRef CreateObjectCallback(JSContextRef context, JSObjectRef function, JSObjectRef thiz, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { JSClassDefinition definition = kJSClassDefinitionEmpty; definition.attributes = kJSClassAttributeNoAutomaticPrototype; definition.staticValues = fields; definition.finalize = &JSObjectFinalizeCallback; JSClassRef clazz = JSClassCreate(&definition); JSObjectRef obj = JSObjectMake(context, clazz, new MyClass()); JSClassRelease(clazz); return obj; } void Init(JSContxtRef context) { // register JS global function 'createMyObject()' JSStringRef name = JSStringCreateWithUTF8CString("createMyObject"); JSObjectRef function = JSObjectMakeFunctionWithCallback( context, name, &CreateObjectCallback); JSObjectRef global = JSContextGetGlobalObject(context); JSObjectSetProperty(context, global, name, function, kJSPropertyAttributeNone, nullptr); JSStringRelease(name); } // 2. JS Code for (var i = 0; i < 100000; ++i) { createMyObject().X; } // %DoJavaScriptGC()% // 3. Result We have 100000 JSClassRef objects, they are all referenced by JSGlobalObject::m_rareData::opaqueJSClassData, even though all the binding JS Objects have been GC-ed. The bugly reference call stack is: #0 WTF::ThreadSafeRefCountedBase::ref (this=0xe50a4120) at WTF/wtf/ThreadSafeRefCounted.h:44 #1 0xd334d3f2 in WTF::refIfNotNull<OpaqueJSClass> (ptr=0xe50a4120) at WTF/wtf/RefPtr.h:38 #2 0xd3347732 in WTF::RefPtr<OpaqueJSClass, WTF::DumbPtrTraits<OpaqueJSClass> >::RefPtr (this=0xe508bb60, ptr=0xe50a4120) at WTF/wtf/RefPtr.h:57 #3 0xd3346e9a in OpaqueJSClassContextData::OpaqueJSClassContextData (this=0xe508bb60, jsClass=0xe50a4120) at JavaScriptCore/API/JSClassRef.cpp:128 #4 0xd3347938 in make_unique<OpaqueJSClassContextData, JSC::VM&, OpaqueJSClass*> (...) at memory:3118 #5 OpaqueJSClass::contextData (this=0xe50a4120, exec=0xd64b0968) at JavaScriptCore/API/JSClassRef.cpp:154 #6 0xd3347b98 in OpaqueJSClass::staticValues (this=0xe50a4120, exec=0xd64b0968) at JavaScriptCore/API/JSClassRef.cpp:166 #7 0xd36e9704 in JSC::JSCallbackObject<JSC::JSDestructibleObject>::getOwnPropertySlot (object=0xcfc540a0, exec=0xd64b0968, propertyName=..., slot=...) at JavaScriptCore/API/JSCallbackObjectFunctions.h:193 #8 0xd335d184 in JSC::JSObject::getNonIndexPropertySlot (this=0xcfc540a0, exec=0xd64b0968, propertyName=..., slot=...) at JavaScriptCore/runtime/JSObjectInlines.h:155 #9 0xd335cc6e in JSC::JSObject::getPropertySlot<false> (this=0xcfc540a0, exec=0xd64b0968, propertyName=..., slot=...) at JavaScriptCore/runtime/JSObject.h:1425 #10 0xd3486442 in JSC::JSValue::getPropertySlot (this=0xd64b0870, exec=0xd64b0968, propertyName=..., slot=...) at JavaScriptCore/runtime/JSCJSValueInlines.h:912 #11 0xd347bab6 in JSC::JSValue::get (this=0xd64b0870, exec=0xd64b0968, propertyName=..., slot=...) at JavaScriptCore/runtime/JSCJSValueInlines.h:869 #12 0xd3949d12 in llint_slow_path_get_by_id (exec=0xd64b0968, pc=0xe50bbfe7) at JavaScriptCore/llint/LLIntSlowPaths.cpp:762 // 4. Possible Solution: Change the type of JSGlobalObject::m_rareData::opaqueJSClassData from HashMap to WeakHashMap.
<rdar://problem/55540351>