Title: [121605] trunk/Source/_javascript_Core
Revision
121605
Author
[email protected]
Date
2012-06-29 17:25:01 -0700 (Fri, 29 Jun 2012)

Log Message

JSObject wastes too much memory on unused property slots
https://bugs.webkit.org/show_bug.cgi?id=90255

Reviewed by Mark Hahnenberg.
        
This does a few things:
        
- JSNonFinalObject no longer has inline property storage.
        
- Initial out-of-line property storage size is 4 slots for JSNonFinalObject,
  or 2x the inline storage for JSFinalObject.
        
- Property storage is only reallocated if it needs to be. Previously, we
  would reallocate the property storage on any transition where the original
  structure said shouldGrowProperyStorage(), but this led to spurious
  reallocations when doing transitionless property adds and there are
  deleted property slots available. That in turn led to crashes, because we
  would switch to out-of-line storage even if the capacity matched the
  criteria for inline storage.
        
- Inline JSFunction allocation is killed off because we don't have a good
  way of inlining property storage allocation. This didn't hurt performance.
  Killing off code is better than fixing it if that code wasn't doing any
  good.
        
This looks like a 1% progression on V8.

* interpreter/Interpreter.cpp:
(JSC::Interpreter::privateExecute):
* jit/JIT.cpp:
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
* jit/JITInlineMethods.h:
(JSC::JIT::emitAllocateBasicJSObject):
(JSC):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_new_func):
(JSC):
(JSC::JIT::emit_op_new_func_exp):
* runtime/JSFunction.cpp:
(JSC::JSFunction::finishCreation):
* runtime/JSObject.h:
(JSC::JSObject::isUsingInlineStorage):
(JSObject):
(JSC::JSObject::finishCreation):
(JSC):
(JSC::JSNonFinalObject::hasInlineStorage):
(JSNonFinalObject):
(JSC::JSNonFinalObject::JSNonFinalObject):
(JSC::JSNonFinalObject::finishCreation):
(JSC::JSFinalObject::hasInlineStorage):
(JSC::JSFinalObject::finishCreation):
(JSC::JSObject::offsetOfInlineStorage):
(JSC::JSObject::setPropertyStorage):
(JSC::Structure::inlineStorageCapacity):
(JSC::Structure::isUsingInlineStorage):
(JSC::JSObject::putDirectInternal):
(JSC::JSObject::setStructureAndReallocateStorageIfNecessary):
(JSC::JSObject::putDirectWithoutTransition):
* runtime/Structure.cpp:
(JSC::Structure::Structure):
(JSC::nextPropertyStorageCapacity):
(JSC):
(JSC::Structure::growPropertyStorageCapacity):
(JSC::Structure::suggestedNewPropertyStorageSize):
* runtime/Structure.h:
(JSC::Structure::putWillGrowPropertyStorage):
(Structure):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (121604 => 121605)


--- trunk/Source/_javascript_Core/ChangeLog	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/ChangeLog	2012-06-30 00:25:01 UTC (rev 121605)
@@ -1,3 +1,74 @@
+2012-06-29  Filip Pizlo  <[email protected]>
+
+        JSObject wastes too much memory on unused property slots
+        https://bugs.webkit.org/show_bug.cgi?id=90255
+
+        Reviewed by Mark Hahnenberg.
+        
+        This does a few things:
+        
+        - JSNonFinalObject no longer has inline property storage.
+        
+        - Initial out-of-line property storage size is 4 slots for JSNonFinalObject,
+          or 2x the inline storage for JSFinalObject.
+        
+        - Property storage is only reallocated if it needs to be. Previously, we
+          would reallocate the property storage on any transition where the original
+          structure said shouldGrowProperyStorage(), but this led to spurious
+          reallocations when doing transitionless property adds and there are
+          deleted property slots available. That in turn led to crashes, because we
+          would switch to out-of-line storage even if the capacity matched the
+          criteria for inline storage.
+        
+        - Inline JSFunction allocation is killed off because we don't have a good
+          way of inlining property storage allocation. This didn't hurt performance.
+          Killing off code is better than fixing it if that code wasn't doing any
+          good.
+        
+        This looks like a 1% progression on V8.
+
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::privateExecute):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileSlowCases):
+        * jit/JIT.h:
+        * jit/JITInlineMethods.h:
+        (JSC::JIT::emitAllocateBasicJSObject):
+        (JSC):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_new_func):
+        (JSC):
+        (JSC::JIT::emit_op_new_func_exp):
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::finishCreation):
+        * runtime/JSObject.h:
+        (JSC::JSObject::isUsingInlineStorage):
+        (JSObject):
+        (JSC::JSObject::finishCreation):
+        (JSC):
+        (JSC::JSNonFinalObject::hasInlineStorage):
+        (JSNonFinalObject):
+        (JSC::JSNonFinalObject::JSNonFinalObject):
+        (JSC::JSNonFinalObject::finishCreation):
+        (JSC::JSFinalObject::hasInlineStorage):
+        (JSC::JSFinalObject::finishCreation):
+        (JSC::JSObject::offsetOfInlineStorage):
+        (JSC::JSObject::setPropertyStorage):
+        (JSC::Structure::inlineStorageCapacity):
+        (JSC::Structure::isUsingInlineStorage):
+        (JSC::JSObject::putDirectInternal):
+        (JSC::JSObject::setStructureAndReallocateStorageIfNecessary):
+        (JSC::JSObject::putDirectWithoutTransition):
+        * runtime/Structure.cpp:
+        (JSC::Structure::Structure):
+        (JSC::nextPropertyStorageCapacity):
+        (JSC):
+        (JSC::Structure::growPropertyStorageCapacity):
+        (JSC::Structure::suggestedNewPropertyStorageSize):
+        * runtime/Structure.h:
+        (JSC::Structure::putWillGrowPropertyStorage):
+        (Structure):
+
 2012-06-28  Filip Pizlo  <[email protected]>
 
         DFG recompilation heuristics should be based on count, not rate

Modified: trunk/Source/_javascript_Core/interpreter/Interpreter.cpp (121604 => 121605)


--- trunk/Source/_javascript_Core/interpreter/Interpreter.cpp	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/interpreter/Interpreter.cpp	2012-06-30 00:25:01 UTC (rev 121605)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2010, 2012 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Cameron Zwarich <[email protected]>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -3602,7 +3602,7 @@
                         proto = asObject(proto)->structure()->prototypeForLookup(callFrame);
                     }
                 }
-                baseObject->transitionTo(*globalData, newStructure);
+                baseObject->setStructureAndReallocateStorageIfNecessary(*globalData, newStructure);
 
                 int value = vPC[3].u.operand;
                 unsigned offset = vPC[7].u.operand;

Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (121604 => 121605)


--- trunk/Source/_javascript_Core/jit/JIT.cpp	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp	2012-06-30 00:25:01 UTC (rev 121605)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -472,8 +472,6 @@
         DEFINE_SLOWCASE_OP(op_neq)
         DEFINE_SLOWCASE_OP(op_new_array)
         DEFINE_SLOWCASE_OP(op_new_object)
-        DEFINE_SLOWCASE_OP(op_new_func)
-        DEFINE_SLOWCASE_OP(op_new_func_exp)
         DEFINE_SLOWCASE_OP(op_not)
         DEFINE_SLOWCASE_OP(op_nstricteq)
         DEFINE_SLOWCASE_OP(op_post_dec)

Modified: trunk/Source/_javascript_Core/jit/JIT.h (121604 => 121605)


--- trunk/Source/_javascript_Core/jit/JIT.h	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/jit/JIT.h	2012-06-30 00:25:01 UTC (rev 121605)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -423,7 +423,6 @@
         template<typename ClassType, bool destructor, typename StructureType> void emitAllocateBasicJSObject(StructureType, RegisterID result, RegisterID storagePtr);
         void emitAllocateBasicStorage(size_t, RegisterID result, RegisterID storagePtr);
         template<typename T> void emitAllocateJSFinalObject(T structure, RegisterID result, RegisterID storagePtr);
-        void emitAllocateJSFunction(FunctionExecutable*, RegisterID scopeChain, RegisterID result, RegisterID storagePtr);
         void emitAllocateJSArray(unsigned valuesRegister, unsigned length, RegisterID cellResult, RegisterID storageResult, RegisterID storagePtr);
         
 #if ENABLE(VALUE_PROFILER)
@@ -750,8 +749,6 @@
         void emitSlow_op_to_jsnumber(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_to_primitive(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_urshift(Instruction*, Vector<SlowCaseEntry>::iterator&);
-        void emitSlow_op_new_func(Instruction*, Vector<SlowCaseEntry>::iterator&);
-        void emitSlow_op_new_func_exp(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_new_array(Instruction*, Vector<SlowCaseEntry>::iterator&);
         
         void emitRightShift(Instruction*, bool isUnsigned);

Modified: trunk/Source/_javascript_Core/jit/JITInlineMethods.h (121604 => 121605)


--- trunk/Source/_javascript_Core/jit/JITInlineMethods.h	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/jit/JITInlineMethods.h	2012-06-30 00:25:01 UTC (rev 121605)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -429,8 +429,11 @@
     storePtr(TrustedImmPtr(0), Address(result, JSObject::offsetOfInheritorID()));
 
     // initialize the object's property storage pointer
-    addPtr(TrustedImm32(sizeof(JSObject)), result, storagePtr);
-    storePtr(storagePtr, Address(result, ClassType::offsetOfPropertyStorage()));
+    if (ClassType::hasInlineStorage()) {
+        addPtr(TrustedImm32(sizeof(JSObject)), result, storagePtr);
+        storePtr(storagePtr, Address(result, ClassType::offsetOfPropertyStorage()));
+    } else
+        storePtr(TrustedImmPtr(0), Address(result, ClassType::offsetOfPropertyStorage()));
 }
 
 template <typename T> inline void JIT::emitAllocateJSFinalObject(T structure, RegisterID result, RegisterID scratch)
@@ -438,28 +441,6 @@
     emitAllocateBasicJSObject<JSFinalObject, false, T>(structure, result, scratch);
 }
 
-inline void JIT::emitAllocateJSFunction(FunctionExecutable* executable, RegisterID scopeChain, RegisterID result, RegisterID storagePtr)
-{
-    emitAllocateBasicJSObject<JSFunction, true>(TrustedImmPtr(m_codeBlock->globalObject()->namedFunctionStructure()), result, storagePtr);
-
-    // store the function's scope chain
-    storePtr(scopeChain, Address(result, JSFunction::offsetOfScopeChain()));
-
-    // store the function's executable member
-    storePtr(TrustedImmPtr(executable), Address(result, JSFunction::offsetOfExecutable()));
-
-    // clear the function's inheritorID
-    storePtr(TrustedImmPtr(0), Address(result, JSFunction::offsetOfCachedInheritorID()));
-
-    // store the function's name
-    ASSERT(executable->nameValue());
-    int functionNameOffset = sizeof(JSValue) * m_codeBlock->globalObject()->functionNameOffset();
-    storePtr(TrustedImmPtr(executable->nameValue()), Address(regT1, functionNameOffset + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
-#if USE(JSVALUE32_64)
-    store32(TrustedImm32(JSValue::CellTag), Address(regT1, functionNameOffset + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
-#endif
-}
-
 inline void JIT::emitAllocateBasicStorage(size_t size, RegisterID result, RegisterID storagePtr)
 {
     CopiedAllocator* allocator = &m_globalData->heap.storageAllocator();

Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (121604 => 121605)


--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp	2012-06-30 00:25:01 UTC (rev 121605)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2012 Apple Inc. All rights reserved.
  * Copyright (C) 2010 Patrick Gansterer <[email protected]>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1618,12 +1618,10 @@
 #endif
     }
 
-    FunctionExecutable* executable = m_codeBlock->functionDecl(currentInstruction[2].u.operand);
-    emitGetFromCallFrameHeaderPtr(RegisterFile::ScopeChain, regT2);
-    emitAllocateJSFunction(executable, regT2, regT0, regT1);
+    JITStubCall stubCall(this, cti_op_new_func);
+    stubCall.addArgument(TrustedImmPtr(m_codeBlock->functionDecl(currentInstruction[2].u.operand)));
+    stubCall.call(dst);
 
-    emitStoreCell(dst, regT0);
-
     if (currentInstruction[3].u.operand) {
 #if USE(JSVALUE32_64)        
         unmap();
@@ -1634,44 +1632,13 @@
     }
 }
 
-void JIT::emitSlow_op_new_func(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
-{
-    linkSlowCase(iter);
-    JITStubCall stubCall(this, cti_op_new_func);
-    stubCall.addArgument(TrustedImmPtr(m_codeBlock->functionDecl(currentInstruction[2].u.operand)));
-    stubCall.call(currentInstruction[1].u.operand);
-}
-
 void JIT::emit_op_new_func_exp(Instruction* currentInstruction)
 {
-    FunctionExecutable* executable = m_codeBlock->functionExpr(currentInstruction[2].u.operand);
-
-    // We only inline the allocation of a anonymous function expressions
-    // If we want to be able to allocate a named function _expression_, we would
-    // need to be able to do inline allocation of a JSStaticScopeObject.
-    if (executable->name().isNull()) {
-        emitGetFromCallFrameHeaderPtr(RegisterFile::ScopeChain, regT2);
-        emitAllocateJSFunction(executable, regT2, regT0, regT1);
-        emitStoreCell(currentInstruction[1].u.operand, regT0);
-        return;
-    }
-
     JITStubCall stubCall(this, cti_op_new_func_exp);
     stubCall.addArgument(TrustedImmPtr(m_codeBlock->functionExpr(currentInstruction[2].u.operand)));
     stubCall.call(currentInstruction[1].u.operand);
 }
 
-void JIT::emitSlow_op_new_func_exp(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
-{
-    FunctionExecutable* executable = m_codeBlock->functionExpr(currentInstruction[2].u.operand);
-    if (!executable->name().isNull())
-        return;
-    linkSlowCase(iter);
-    JITStubCall stubCall(this, cti_op_new_func_exp);
-    stubCall.addArgument(TrustedImmPtr(executable));
-    stubCall.call(currentInstruction[1].u.operand);
-}
-
 void JIT::emit_op_new_array(Instruction* currentInstruction)
 {
     int length = currentInstruction[3].u.operand;

Modified: trunk/Source/_javascript_Core/runtime/JSFunction.cpp (121604 => 121605)


--- trunk/Source/_javascript_Core/runtime/JSFunction.cpp	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/runtime/JSFunction.cpp	2012-06-30 00:25:01 UTC (rev 121605)
@@ -103,13 +103,16 @@
 
 void JSFunction::finishCreation(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode)
 {
-    Base::finishCreation(exec->globalData());
+    JSGlobalData& globalData = exec->globalData();
+    Base::finishCreation(globalData);
     ASSERT(inherits(&s_info));
 
     // Switching the structure here is only safe if we currently have the function structure!
     ASSERT(structure() == scopeChainNode->globalObject->functionStructure());
-    setStructure(exec->globalData(), scopeChainNode->globalObject->namedFunctionStructure());
-    putDirectOffset(exec->globalData(), scopeChainNode->globalObject->functionNameOffset(), executable->nameValue());
+    setStructureAndReallocateStorageIfNecessary(
+        globalData,
+        scopeChainNode->globalObject->namedFunctionStructure());
+    putDirectOffset(globalData, scopeChainNode->globalObject->functionNameOffset(), executable->nameValue());
 }
 
 Structure* JSFunction::cacheInheritorID(ExecState* exec)

Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (121604 => 121605)


--- trunk/Source/_javascript_Core/runtime/JSObject.h	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h	2012-06-30 00:25:01 UTC (rev 121605)
@@ -79,6 +79,14 @@
         Accessor     = 1 << 5,  // property is a getter/setter
     };
 
+#if USE(JSVALUE32_64)
+#define JSFinalObject_inlineStorageCapacity 6
+#else
+#define JSFinalObject_inlineStorageCapacity 4
+#endif
+
+COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= 0), final_storage_non_negative);
+
     class JSObject : public JSCell {
         friend class BatchedTransitionOptimizer;
         friend class JIT;
@@ -221,16 +229,25 @@
         void reifyStaticFunctionsForDelete(ExecState* exec);
 
         JS_EXPORT_PRIVATE PropertyStorage growPropertyStorage(JSGlobalData&, size_t oldSize, size_t newSize);
-        bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage.get()) == static_cast<const void*>(this + 1); }
+        bool isUsingInlineStorage() const
+        {
+            bool result =
+                !m_propertyStorage.get()
+                || static_cast<const void*>(m_propertyStorage.get()) == static_cast<const void*>(this + 1);
+            ASSERT(result == structure()->isUsingInlineStorage());
+            return result;
+        }
         void setPropertyStorage(JSGlobalData&, PropertyStorage, Structure*);
+        
+        bool reallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*);
+        void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*);
+        void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, Structure*);
 
         void* addressOfPropertyStorage()
         {
             return &m_propertyStorage;
         }
 
-        static const unsigned baseExternalStorageCapacity = 16;
-
         void flattenDictionaryObject(JSGlobalData& globalData)
         {
             structure()->flattenDictionaryStructure(globalData, this);
@@ -250,14 +267,13 @@
         static JS_EXPORTDATA const ClassInfo s_info;
 
     protected:
-        void finishCreation(JSGlobalData& globalData, PropertyStorage inlineStorage)
+        void finishCreation(JSGlobalData& globalData)
         {
             Base::finishCreation(globalData);
             ASSERT(inherits(&s_info));
-            ASSERT(structure()->propertyStorageCapacity() < baseExternalStorageCapacity);
+            ASSERT(structure()->isUsingInlineStorage());
             ASSERT(structure()->isEmpty());
             ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
-            ASSERT_UNUSED(inlineStorage, static_cast<void*>(inlineStorage) == static_cast<void*>(this + 1));
             ASSERT(structure()->isObject());
             ASSERT(classInfo());
         }
@@ -316,16 +332,6 @@
     };
 
 
-#if USE(JSVALUE32_64)
-#define JSNonFinalObject_inlineStorageCapacity 4
-#define JSFinalObject_inlineStorageCapacity 6
-#else
-#define JSNonFinalObject_inlineStorageCapacity 2
-#define JSFinalObject_inlineStorageCapacity 4
-#endif
-
-COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final);
-
     // JSNonFinalObject is a type of JSObject that has some internal storage,
     // but also preserves some space in the collector cell for additional
     // data members in derived types.
@@ -340,22 +346,23 @@
             return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
         }
 
+        static bool hasInlineStorage()
+        {
+            return false;
+        }
+
     protected:
         explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
-            : JSObject(globalData, structure, m_inlineStorage)
+            : JSObject(globalData, structure, 0)
         {
         }
 
         void finishCreation(JSGlobalData& globalData)
         {
-            Base::finishCreation(globalData, m_inlineStorage);
-            ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double)));
-            ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity);
+            Base::finishCreation(globalData);
+            ASSERT(!this->structure()->propertyStorageCapacity());
             ASSERT(classInfo());
         }
-
-    private:
-        WriteBarrier<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity];
     };
 
     class JSFinalObject;
@@ -376,10 +383,14 @@
 
         static JS_EXPORTDATA const ClassInfo s_info;
 
+        static bool hasInlineStorage()
+        {
+            return true;
+        }
     protected:
         void finishCreation(JSGlobalData& globalData)
         {
-            Base::finishCreation(globalData, m_inlineStorage);
+            Base::finishCreation(globalData);
             ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double)));
             ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity);
             ASSERT(classInfo());
@@ -417,7 +428,6 @@
 
 inline size_t JSObject::offsetOfInlineStorage()
 {
-    ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage));
     return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
 }
 
@@ -465,6 +475,8 @@
 {
     ASSERT(storage);
     ASSERT(structure);
+    ASSERT(!structure->isUsingInlineStorage()
+           || (classInfo() == &JSFinalObject::s_info && static_cast<void*>(storage) == static_cast<void*>(this + 1)));
     setStructure(globalData, structure);
     m_propertyStorage.set(globalData, this, storage);
 }
@@ -530,9 +542,17 @@
     return createInheritorID(globalData);
 }
 
+inline size_t Structure::inlineStorageCapacity() const
+{
+    if (classInfo() == &JSFinalObject::s_info)
+        return JSFinalObject_inlineStorageCapacity;
+    return 0;
+}
+
 inline bool Structure::isUsingInlineStorage() const
 {
-    return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity;
+    ASSERT(propertyStorageCapacity() >= inlineStorageCapacity());
+    return propertyStorageCapacity() == inlineStorageCapacity();
 }
 
 inline bool JSCell::inherits(const ClassInfo* info) const
@@ -681,7 +701,7 @@
             return false;
 
         PropertyStorage newStorage = propertyStorage();
-        if (structure()->shouldGrowPropertyStorage())
+        if (structure()->putWillGrowPropertyStorage())
             newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
         offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
         setPropertyStorage(globalData, newStorage, structure());
@@ -746,14 +766,11 @@
     if ((mode == PutModePut) && !isExtensible())
         return false;
 
-    PropertyStorage newStorage = propertyStorage();
-    if (structure()->shouldGrowPropertyStorage())
-        newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
-
     Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset);
+    
+    ASSERT(offset < structure->propertyStorageCapacity());
+    setStructureAndReallocateStorageIfNecessary(globalData, structure);
 
-    ASSERT(offset < structure->propertyStorageCapacity());
-    setPropertyStorage(globalData, newStorage, structure);
     putDirectOffset(globalData, offset, value);
     // This is a new property; transitions with specific values are not currently cachable,
     // so leave the slot in an uncachable state.
@@ -762,6 +779,26 @@
     return true;
 }
 
+inline void JSObject::setStructureAndReallocateStorageIfNecessary(JSGlobalData& globalData, unsigned oldCapacity, Structure* newStructure)
+{
+    ASSERT(oldCapacity <= newStructure->propertyStorageCapacity());
+    
+    if (oldCapacity == newStructure->propertyStorageCapacity()) {
+        setStructure(globalData, newStructure);
+        return;
+    }
+    
+    PropertyStorage newStorage = growPropertyStorage(
+        globalData, oldCapacity, newStructure->suggestedNewPropertyStorageSize());
+    setPropertyStorage(globalData, newStorage, newStructure);
+}
+
+inline void JSObject::setStructureAndReallocateStorageIfNecessary(JSGlobalData& globalData, Structure* newStructure)
+{
+    setStructureAndReallocateStorageIfNecessary(
+        globalData, structure()->propertyStorageCapacity(), newStructure);
+}
+
 inline bool JSObject::putOwnDataProperty(JSGlobalData& globalData, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
 {
     ASSERT(value);
@@ -788,21 +825,13 @@
 {
     ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
     PropertyStorage newStorage = propertyStorage();
-    if (structure()->shouldGrowPropertyStorage())
+    if (structure()->putWillGrowPropertyStorage())
         newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
     size_t offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getCallableObject(value));
     setPropertyStorage(globalData, newStorage, structure());
     putDirectOffset(globalData, offset, value);
 }
 
-inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure)
-{
-    PropertyStorage newStorage = propertyStorage();
-    if (structure()->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
-        newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
-    setPropertyStorage(globalData, newStorage, newStructure);
-}
-
 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
 {
     return methodTable()->defaultValue(this, exec, preferredType);

Modified: trunk/Source/_javascript_Core/runtime/Structure.cpp (121604 => 121605)


--- trunk/Source/_javascript_Core/runtime/Structure.cpp	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/runtime/Structure.cpp	2012-06-30 00:25:01 UTC (rev 121605)
@@ -156,7 +156,7 @@
     , m_prototype(globalData, this, prototype)
     , m_classInfo(classInfo)
     , m_transitionWatchpointSet(InitializedWatching)
-    , m_propertyStorageCapacity(typeInfo.isFinalObject() ? JSFinalObject_inlineStorageCapacity : JSNonFinalObject_inlineStorageCapacity)
+    , m_propertyStorageCapacity(typeInfo.isFinalObject() ? JSFinalObject_inlineStorageCapacity : 0)
     , m_offset(noOffset)
     , m_dictionaryKind(NoneDictionaryKind)
     , m_isPinnedPropertyTable(false)
@@ -256,19 +256,21 @@
     }
 }
 
+inline size_t nextPropertyStorageCapacity(size_t currentCapacity)
+{
+    if (!currentCapacity)
+        return 4;
+    return currentCapacity * 2;
+}
+
 void Structure::growPropertyStorageCapacity()
 {
-    if (isUsingInlineStorage())
-        m_propertyStorageCapacity = JSObject::baseExternalStorageCapacity;
-    else
-        m_propertyStorageCapacity *= 2;
+    m_propertyStorageCapacity = nextPropertyStorageCapacity(m_propertyStorageCapacity);
 }
 
 size_t Structure::suggestedNewPropertyStorageSize()
 {
-    if (isUsingInlineStorage())
-        return JSObject::baseExternalStorageCapacity;
-    return m_propertyStorageCapacity * 2;
+    return nextPropertyStorageCapacity(m_propertyStorageCapacity);
 }
  
 void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, PropertyName propertyName)

Modified: trunk/Source/_javascript_Core/runtime/Structure.h (121604 => 121605)


--- trunk/Source/_javascript_Core/runtime/Structure.h	2012-06-30 00:20:21 UTC (rev 121604)
+++ trunk/Source/_javascript_Core/runtime/Structure.h	2012-06-30 00:25:01 UTC (rev 121605)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -103,7 +103,23 @@
         bool isFrozen(JSGlobalData&);
         bool isExtensible() const { return !m_preventExtensions; }
         bool didTransition() const { return m_didTransition; }
-        bool shouldGrowPropertyStorage() { return propertyStorageCapacity() == propertyStorageSize(); }
+        bool putWillGrowPropertyStorage()
+        {
+            ASSERT(propertyStorageCapacity() >= propertyStorageSize());
+            
+            if (!m_propertyTable) {
+                unsigned currentSize = static_cast<unsigned>(m_offset + 1);
+                ASSERT(propertyStorageCapacity() >= currentSize);
+                return currentSize == propertyStorageCapacity();
+            }
+            
+            ASSERT(propertyStorageCapacity() >= m_propertyTable->propertyStorageSize());
+            if (m_propertyTable->hasDeletedOffset())
+                return false;
+            
+            ASSERT(propertyStorageCapacity() >= m_propertyTable->size());
+            return m_propertyTable->size() == propertyStorageCapacity();
+        }
         JS_EXPORT_PRIVATE size_t suggestedNewPropertyStorageSize(); 
 
         Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*);
@@ -139,6 +155,7 @@
         void growPropertyStorageCapacity();
         unsigned propertyStorageCapacity() const { ASSERT(structure()->classInfo() == &s_info); return m_propertyStorageCapacity; }
         unsigned propertyStorageSize() const { ASSERT(structure()->classInfo() == &s_info); return (m_propertyTable ? m_propertyTable->propertyStorageSize() : static_cast<unsigned>(m_offset + 1)); }
+        size_t inlineStorageCapacity() const;
         bool isUsingInlineStorage() const;
 
         size_t get(JSGlobalData&, PropertyName);
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to