Ignore:
Timestamp:
Jan 17, 2017, 3:52:55 PM (8 years ago)
Author:
[email protected]
Message:

JSCell::classInfo() shouldn't have a bunch of mitigations for being called during destruction
https://bugs.webkit.org/show_bug.cgi?id=167066

Reviewed by Keith Miller and Michael Saboff.
Source/JavaScriptCore:


This reduces the size of JSCell::classInfo() by half and removes some checks that
this function previously had to do in case it was called from destructors.

I changed all of the destructors so that they don't call JSCell::classInfo() and I
added an assertion to JSCell::classInfo() to catch cases where someone called it
from a destructor accidentally.

This means that we only have one place in destruction that needs to know the class:
the sweeper's call to the destructor.

One of the trickiest outcomes of this is the need to support inherits() tests in
JSObjectGetPrivate(), when it is called from the destructor callback on the object
being destructed. JSObjectGetPrivate() is undefined behavior anyway if you use it
on any dead-but-not-destructed object other than the one being destructed right
now. The purpose of the inherits() tests is to distinguish between different kinds
of CallbackObjects, which may have different kinds of base classes. I think that
this was always subtly wrong - for example, if the object being destructed is a
JSGlobalObject then it's not a DestructibleObject, is not in a destructor block,
but does not have an immortal Structure - so classInfo() is not valid. This fixes
the issue by having ~JSCallbackObject know its classInfo. It now stashes its
classInfo in VM so that JSObjectGetPrivate can use that classInfo if it detects
that it's being used on a currently-destructing object.

That was the only really weird part of this patch. The rest is mostly removing
illegal uses of jsCast<> in destructors. There were a few other genuine uses of
classInfo() but they were in code that already knew how to get its classInfo()
using other means:

  • You can still say structure()->classInfo(), and I use this form in code that knows that its StructureIsImmortal.


  • You can use this->classInfo() if it's overridden, like in subclasses of JSDestructibleObject.


Rolling this back in because I think I fixed the crashes.

  • API/JSAPIWrapperObject.mm:

(JSAPIWrapperObjectHandleOwner::finalize):

  • API/JSCallbackObject.h:
  • API/JSCallbackObjectFunctions.h:

(JSC::JSCallbackObject<Parent>::~JSCallbackObject):
(JSC::JSCallbackObject<Parent>::init):

  • API/JSObjectRef.cpp:

(classInfoPrivate):
(JSObjectGetPrivate):
(JSObjectSetPrivate):

  • bytecode/EvalCodeBlock.cpp:

(JSC::EvalCodeBlock::destroy):

  • bytecode/FunctionCodeBlock.cpp:

(JSC::FunctionCodeBlock::destroy):

  • bytecode/ModuleProgramCodeBlock.cpp:

(JSC::ModuleProgramCodeBlock::destroy):

  • bytecode/ProgramCodeBlock.cpp:

(JSC::ProgramCodeBlock::destroy):

  • bytecode/UnlinkedEvalCodeBlock.cpp:

(JSC::UnlinkedEvalCodeBlock::destroy):

  • bytecode/UnlinkedFunctionCodeBlock.cpp:

(JSC::UnlinkedFunctionCodeBlock::destroy):

  • bytecode/UnlinkedFunctionExecutable.cpp:

(JSC::UnlinkedFunctionExecutable::destroy):

  • bytecode/UnlinkedModuleProgramCodeBlock.cpp:

(JSC::UnlinkedModuleProgramCodeBlock::destroy):

  • bytecode/UnlinkedProgramCodeBlock.cpp:

(JSC::UnlinkedProgramCodeBlock::destroy):

  • heap/CodeBlockSet.cpp:

(JSC::CodeBlockSet::lastChanceToFinalize):
(JSC::CodeBlockSet::deleteUnmarkedAndUnreferenced):

  • heap/MarkedAllocator.cpp:

(JSC::MarkedAllocator::allocateSlowCaseImpl):

  • heap/MarkedBlock.cpp:

(JSC::MarkedBlock::Handle::sweep):

  • jit/JITThunks.cpp:

(JSC::JITThunks::finalize):

  • runtime/AbstractModuleRecord.cpp:

(JSC::AbstractModuleRecord::destroy):

  • runtime/ExecutableBase.cpp:

(JSC::ExecutableBase::clearCode):

  • runtime/JSCellInlines.h:

(JSC::JSCell::classInfo):
(JSC::JSCell::callDestructor):

  • runtime/JSLock.h:

(JSC::JSLock::ownerThread):

  • runtime/JSModuleNamespaceObject.cpp:

(JSC::JSModuleNamespaceObject::destroy):

  • runtime/JSModuleRecord.cpp:

(JSC::JSModuleRecord::destroy):

  • runtime/JSPropertyNameEnumerator.cpp:

(JSC::JSPropertyNameEnumerator::destroy):

  • runtime/JSSegmentedVariableObject.h:
  • runtime/SymbolTable.cpp:

(JSC::SymbolTable::destroy):

  • runtime/VM.h:
  • wasm/js/JSWebAssemblyCallee.cpp:

(JSC::JSWebAssemblyCallee::destroy):

  • wasm/js/WebAssemblyModuleRecord.cpp:

(JSC::WebAssemblyModuleRecord::destroy):

  • wasm/js/WebAssemblyToJSCallee.cpp:

(JSC::WebAssemblyToJSCallee::WebAssemblyToJSCallee):
(JSC::WebAssemblyToJSCallee::destroy):

Source/WebCore:

No new tests because no new behavior.

It's now necessary to avoid jsCast in destructors and finalizers. This was an easy
rule to introduce because this used to always be the rule.

  • bindings/js/JSCSSValueCustom.cpp:

(WebCore::JSDeprecatedCSSOMValueOwner::finalize):

  • bindings/js/JSDOMIterator.h:

(WebCore::IteratorTraits>::destroy):

  • bindings/scripts/CodeGeneratorJS.pm:

(GenerateImplementation):

Source/WebKit2:


Just remove now-erroneous use of jsCast<>.

  • WebProcess/Plugins/Netscape/NPRuntimeObjectMap.cpp:

(WebKit::NPRuntimeObjectMap::finalize):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/JSObjectRef.cpp

    r210824 r210829  
    381381}
    382382
     383// API objects have private properties, which may get accessed during destruction. This
     384// helper lets us get the ClassInfo of an API object from a function that may get called
     385// during destruction.
     386static const ClassInfo* classInfoPrivate(JSObject* jsObject)
     387{
     388    VM* vm = jsObject->vm();
     389   
     390    if (vm->currentlyDestructingCallbackObject != jsObject)
     391        return jsObject->classInfo();
     392
     393    return vm->currentlyDestructingCallbackObjectClassInfo;
     394}
     395
    383396void* JSObjectGetPrivate(JSObjectRef object)
    384397{
    385398    JSObject* jsObject = uncheckedToJS(object);
    386399
     400    const ClassInfo* classInfo = classInfoPrivate(jsObject);
     401   
    387402    // Get wrapped object if proxied
    388     if (jsObject->inherits(JSProxy::info()))
     403    if (classInfo->isSubClassOf(JSProxy::info())) {
     404        jsObject = static_cast<JSProxy*>(jsObject)->target();
     405        classInfo = jsObject->classInfo();
     406    }
     407
     408    if (classInfo->isSubClassOf(JSCallbackObject<JSGlobalObject>::info()))
     409        return static_cast<JSCallbackObject<JSGlobalObject>*>(jsObject)->getPrivate();
     410    if (classInfo->isSubClassOf(JSCallbackObject<JSDestructibleObject>::info()))
     411        return static_cast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->getPrivate();
     412#if JSC_OBJC_API_ENABLED
     413    if (classInfo->isSubClassOf(JSCallbackObject<JSAPIWrapperObject>::info()))
     414        return static_cast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->getPrivate();
     415#endif
     416   
     417    return 0;
     418}
     419
     420bool JSObjectSetPrivate(JSObjectRef object, void* data)
     421{
     422    JSObject* jsObject = uncheckedToJS(object);
     423
     424    const ClassInfo* classInfo = classInfoPrivate(jsObject);
     425   
     426    // Get wrapped object if proxied
     427    if (classInfo->isSubClassOf(JSProxy::info())) {
    389428        jsObject = jsCast<JSProxy*>(jsObject)->target();
    390 
    391     if (jsObject->inherits(JSCallbackObject<JSGlobalObject>::info()))
    392         return jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->getPrivate();
    393     if (jsObject->inherits(JSCallbackObject<JSDestructibleObject>::info()))
    394         return jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->getPrivate();
     429        classInfo = jsObject->classInfo();
     430    }
     431
     432    if (classInfo->isSubClassOf(JSCallbackObject<JSGlobalObject>::info())) {
     433        jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->setPrivate(data);
     434        return true;
     435    }
     436    if (classInfo->isSubClassOf(JSCallbackObject<JSDestructibleObject>::info())) {
     437        jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->setPrivate(data);
     438        return true;
     439    }
    395440#if JSC_OBJC_API_ENABLED
    396     if (jsObject->inherits(JSCallbackObject<JSAPIWrapperObject>::info()))
    397         return jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->getPrivate();
    398 #endif
    399    
    400     return 0;
    401 }
    402 
    403 bool JSObjectSetPrivate(JSObjectRef object, void* data)
    404 {
    405     JSObject* jsObject = uncheckedToJS(object);
    406 
    407     // Get wrapped object if proxied
    408     if (jsObject->inherits(JSProxy::info()))
    409         jsObject = jsCast<JSProxy*>(jsObject)->target();
    410 
    411     if (jsObject->inherits(JSCallbackObject<JSGlobalObject>::info())) {
    412         jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->setPrivate(data);
    413         return true;
    414     }
    415     if (jsObject->inherits(JSCallbackObject<JSDestructibleObject>::info())) {
    416         jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->setPrivate(data);
    417         return true;
    418     }
    419 #if JSC_OBJC_API_ENABLED
    420     if (jsObject->inherits(JSCallbackObject<JSAPIWrapperObject>::info())) {
     441    if (classInfo->isSubClassOf(JSCallbackObject<JSAPIWrapperObject>::info())) {
    421442        jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->setPrivate(data);
    422443        return true;
Note: See TracChangeset for help on using the changeset viewer.