JavaScriptCore:
authorggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Dec 2007 09:32:06 +0000 (09:32 +0000)
committerggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Dec 2007 09:32:06 +0000 (09:32 +0000)
        Reviewed by Oliver Hunt.

        Optimized global access to global variables, using a symbol table.

        SunSpider reports a 1.5% overall speedup, a 6.2% speedup on 3d-morph,
        and a whopping 33.1% speedup on bitops-bitwise-and.

        * API/JSCallbackObjectFunctions.h: Replaced calls to JSObject:: with
        calls to Base::, since JSObject is not always our base class. This
        was always a bug, but the bug is even more apparent after some of my
        changes.

        (KJS::::staticFunctionGetter): Replaced use of getDirect with call to
        getOwnPropertySlot. Global declarations are no longer stored in the
        property map, so a call to getDirect is insufficient for finding
        override properties.

        * API/testapi.c:
        * API/testapi.js: Added test for the getDirect change mentioned above.

        * kjs/ExecState.cpp:
        * kjs/ExecState.h: Dialed back the optimization to store a direct
        pointer to the localStorage buffer. One ExecState can grow the global
        object's localStorage without another ExecState's knowledge, so
        ExecState can't store a direct pointer to the localStorage buffer
        unless/until we invent a way to update all the relevant ExecStates.

        * kjs/JSGlobalObject.cpp: Inserted the symbol table into get and put
        operations.
        (KJS::JSGlobalObject::reset): Reset the symbol table and local storage,
        too. Also, clear the property map here, removing the need for a
        separate call.

        * kjs/JSVariableObject.cpp:
        * kjs/JSVariableObject.h: Added support for saving localStorage and the
        symbol table to the back/forward cache, and restoring them.

        * kjs/function.cpp:
        (KJS::GlobalFuncImp::callAsFunction): Renamed progNode to evalNode
        because it's an EvalNode, not a ProgramNode.

        * kjs/lookup.h:
        (KJS::cacheGlobalObject): Replaced put with faster putDirect, since
        that's how the rest of lookup.h works. putDirect is safe here because
        cacheGlobalObject is only used for objects whose names are not valid
        identifiers.

        * kjs/nodes.cpp: The good stuff!

        (KJS::EvalNode::processDeclarations): Replaced hasProperty with
        the new hasOwnProperty, which is slightly faster.

        * kjs/object.h: Nixed clearProperties because clear() does this job now.

        * kjs/property_map.cpp:
        * kjs/property_map.h: More back/forward cache support.

        * wtf/Vector.h:
        (WTF::::grow): Added fast non-branching grow function. I used it in
        an earlier version of this patch, even though it's not used anymore.

JavaScriptGlue:

        Build fix.

        * ForwardingHeaders/wtf/VectorTraits.h: Added.

WebCore:

        Reviewed by Oliver Hunt.

        Build support:
        * ForwardingHeaders/kjs/SymbolTable.h: Added.
        * ForwardingHeaders/wtf/VectorTraits.h: Added.

        * bindings/js/JSDOMWindowCustom.cpp:
        (WebCore::JSDOMWindow::customGetOwnPropertySlot): Replaced use of
        getDirectLocation with getOwnPropertySlot. getDirectLocation is no
        longer valid, since global declarations are not stored in the property
        map.

        (WebCore::JSDOMWindow::customPut): Replaced use of JSObject::put with
        JSGlobalObject::put. JSObject::put is no longer valid, since global
        declarations are not stored in the property map.

        * bindings/js/kjs_window.cpp: Replaced JSObject:: calls with Base::
        calls, since JSObject is not our base class. This was always a bug, but
        the bug is even more apparent after some of my changes.

        (KJS::Window::clear): Removed call to clearProperties because
        JSGlobalObject::reset takes care of that now.

        * history/CachedPage.cpp:
        * history/CachedPage.h: Added support for saving a symbol table and
        localStorage to the page cache, and restoring it.

WebKit/mac:

        Reviewed by Oliver Hunt.

        Build fix.

        * ForwardingHeaders/kjs/SymbolTable.h: Added.
        * ForwardingHeaders/wtf/VectorTraits.h: Added.

LayoutTests:

        Reviewed by Oliver Hunt.

        Added some tests to verify some of the changes I made while optimizing
        global access to global variables.

        * fast/dom/Window/resources/window-property-clearing-iframe0.html: Added.
        * fast/dom/Window/resources/window-property-clearing-iframe1.html: Added.
        * fast/dom/Window/window-property-clearing-expected.txt: Added.
        * fast/dom/Window/window-property-clearing.html: Added.
        * fast/dom/getter-on-window-object2-expected.txt: Added.
        * fast/dom/getter-on-window-object2.html: Added.

        Checked in failing results for these const tests. The symbol table
        optimization broke const. (We didn't know this before because our only
        tests used global variables.)

        * fast/js/const-expected.txt:
        * fast/js/kde/const-expected.txt:

        * fast/js/resources/for-in-avoid-duplicates.js: Fixed a typo I noticed.
        Not related to this patch.

        * fast/dom/Window/window-property-shadowing.html: Changed this test to
        use "this" instead of "window". The fact that "window" worked before,
        despite an overriding / shadowing var declaration, was a bug.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@28884 268f45cc-cd09-0410-ab3c-d52691b4dbfc

46 files changed:
JavaScriptCore/API/JSCallbackObjectFunctions.h
JavaScriptCore/API/testapi.c
JavaScriptCore/API/testapi.js
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp
JavaScriptCore/kjs/ExecState.cpp
JavaScriptCore/kjs/ExecState.h
JavaScriptCore/kjs/JSGlobalObject.cpp
JavaScriptCore/kjs/JSGlobalObject.h
JavaScriptCore/kjs/JSVariableObject.cpp
JavaScriptCore/kjs/JSVariableObject.h
JavaScriptCore/kjs/LocalStorage.h
JavaScriptCore/kjs/function.cpp
JavaScriptCore/kjs/lookup.h
JavaScriptCore/kjs/nodes.cpp
JavaScriptCore/kjs/nodes.h
JavaScriptCore/kjs/object.cpp
JavaScriptCore/kjs/object.h
JavaScriptCore/kjs/object_object.cpp
JavaScriptCore/kjs/property_map.cpp
JavaScriptCore/kjs/property_map.h
JavaScriptCore/wtf/Vector.h
JavaScriptGlue/ChangeLog
JavaScriptGlue/ForwardingHeaders/wtf/VectorTraits.h [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/fast/dom/Window/resources/window-property-clearing-iframe0.html [new file with mode: 0644]
LayoutTests/fast/dom/Window/resources/window-property-clearing-iframe1.html [new file with mode: 0644]
LayoutTests/fast/dom/Window/window-property-clearing-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Window/window-property-clearing.html [new file with mode: 0644]
LayoutTests/fast/dom/Window/window-property-shadowing.html
LayoutTests/fast/dom/getter-on-window-object2-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/getter-on-window-object2.html [new file with mode: 0644]
LayoutTests/fast/js/const-expected.txt
LayoutTests/fast/js/kde/const-expected.txt
LayoutTests/fast/js/resources/for-in-avoid-duplicates.js
WebCore/ChangeLog
WebCore/ForwardingHeaders/kjs/SymbolTable.h [new file with mode: 0644]
WebCore/ForwardingHeaders/wtf/VectorTraits.h [new file with mode: 0644]
WebCore/bindings/js/JSDOMWindowCustom.cpp
WebCore/bindings/js/kjs_window.cpp
WebCore/bindings/js/kjs_window.h
WebCore/history/CachedPage.cpp
WebCore/history/CachedPage.h
WebKit/mac/ChangeLog
WebKit/mac/ForwardingHeaders/kjs/SymbolTable.h [new file with mode: 0644]
WebKit/mac/ForwardingHeaders/wtf/VectorTraits.h [new file with mode: 0644]

index 0218790edc97e4198b7219569213210ed257ae97..f987dd8ccf32a41b71e886c418d69b2c8bb735ef 100644 (file)
@@ -96,7 +96,7 @@ UString JSCallbackObject<Base>::className() const
     if (!m_class->className.isNull())
         return m_class->className;
     
-    return JSObject::className();
+    return Base::className();
 }
 
 template <class Base>
@@ -140,7 +140,7 @@ bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, const Identifie
         }
     }
     
-    return JSObject::getOwnPropertySlot(exec, propertyName, slot);
+    return Base::getOwnPropertySlot(exec, propertyName, slot);
 }
 
 template <class Base>
@@ -187,7 +187,7 @@ void JSCallbackObject<Base>::put(ExecState* exec, const Identifier& propertyName
         }
     }
     
-    return JSObject::put(exec, propertyName, value, attr);
+    return Base::put(exec, propertyName, value, attr);
 }
 
 template <class Base>
@@ -227,7 +227,7 @@ bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, const Identifier& p
         }
     }
     
-    return JSObject::deleteProperty(exec, propertyName);
+    return Base::deleteProperty(exec, propertyName);
 }
 
 template <class Base>
@@ -361,7 +361,7 @@ void JSCallbackObject<Base>::getPropertyNames(ExecState* exec, PropertyNameArray
         }
     }
     
-    JSObject::getPropertyNames(exec, propertyNames);
+    Base::getPropertyNames(exec, propertyNames);
 }
 
 template <class Base>
@@ -377,7 +377,7 @@ double JSCallbackObject<Base>::toNumber(ExecState* exec) const
                 return toJS(value)->getNumber();
         }
             
-    return JSObject::toNumber(exec);
+    return Base::toNumber(exec);
 }
 
 template <class Base>
@@ -393,7 +393,7 @@ UString JSCallbackObject<Base>::toString(ExecState* exec) const
                 return toJS(value)->getString();
         }
             
-    return JSObject::toString(exec);
+    return Base::toString(exec);
 }
 
 template <class Base>
@@ -453,8 +453,10 @@ JSValue* JSCallbackObject<Base>::staticFunctionGetter(ExecState* exec, JSObject*
     ASSERT(slot.slotBase()->inherits(&JSCallbackObject::info));
     JSCallbackObject* thisObj = static_cast<JSCallbackObject*>(slot.slotBase());
     
-    if (JSValue* cachedOrOverrideValue = thisObj->getDirect(propertyName))
-        return cachedOrOverrideValue;
+    // Check for cached or override property.
+    PropertySlot slot2;
+    if (thisObj->Base::getOwnPropertySlot(exec, propertyName, slot2))
+        return slot2.getValue(exec, thisObj, propertyName);
     
     for (JSClassRef jsClass = thisObj->m_class; jsClass; jsClass = jsClass->parentClass) {
         if (OpaqueJSClass::StaticFunctionsTable* staticFunctions = jsClass->staticFunctions) {
index 3794c7160e7301c3c0d9905a419027454f17b7ec..850aed857177aa79f116af89b6c819903f097fae 100644 (file)
@@ -507,12 +507,27 @@ static bool globalObject_set(JSContextRef ctx, JSObjectRef object, JSStringRef p
     return true;
 }
 
+static JSValueRef globalObject_call(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    UNUSED_PARAM(function);
+    UNUSED_PARAM(thisObject);
+    UNUSED_PARAM(argumentCount);
+    UNUSED_PARAM(arguments);
+    UNUSED_PARAM(exception);
+
+    return JSValueMakeNumber(ctx, 3);
+}
 
 static JSStaticValue globalObject_staticValues[] = {
     { "globalStaticValue", globalObject_get, globalObject_set, kJSPropertyAttributeNone },
     { 0, 0, 0, 0 }
 };
 
+static JSStaticFunction globalObject_staticFunctions[] = {
+    { "globalStaticFunction", globalObject_call, kJSPropertyAttributeNone },
+    { 0, 0, 0 }
+};
+
 static char* createStringWithContentsOfFile(const char* fileName);
 
 static void testInitializeFinalize()
@@ -540,6 +555,8 @@ int main(int argc, char* argv[])
     JSClassDefinition globalObjectClassDefinition = kJSClassDefinitionEmpty;
     globalObjectClassDefinition.initialize = globalObject_initialize;
     globalObjectClassDefinition.staticValues = globalObject_staticValues;
+    globalObjectClassDefinition.staticFunctions = globalObject_staticFunctions;
+    globalObjectClassDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
     JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition);
     context = JSGlobalContextCreate(globalObjectClass);
     
index 556ef60796cbabda3da9487c97518a05702a5b38..52bcb71a13cb91a156e2119ffa93f33d02545612 100644 (file)
@@ -53,7 +53,13 @@ function shouldThrow(a)
     print(result);
 }
 
+function globalStaticFunction()
+{
+    return 4;
+}
+
 shouldBe("globalStaticValue", 3);
+shouldBe("globalStaticFunction()", 4);
 
 shouldBe("typeof MyObject", "function"); // our object implements 'call'
 MyObject.cantFind = 1;
index 2bddebd428164573167a7d1c2d7f5472d1d81ff7..727d26ae03c4c2e4e0a5bd5c2b139d9a328f3acd 100644 (file)
@@ -1,3 +1,66 @@
+2007-12-19  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by Oliver Hunt.
+        
+        Optimized global access to global variables, using a symbol table.
+        
+        SunSpider reports a 1.5% overall speedup, a 6.2% speedup on 3d-morph,
+        and a whopping 33.1% speedup on bitops-bitwise-and.
+
+        * API/JSCallbackObjectFunctions.h: Replaced calls to JSObject:: with
+        calls to Base::, since JSObject is not always our base class. This
+        was always a bug, but the bug is even more apparent after some of my
+        changes.
+
+        (KJS::::staticFunctionGetter): Replaced use of getDirect with call to
+        getOwnPropertySlot. Global declarations are no longer stored in the
+        property map, so a call to getDirect is insufficient for finding
+        override properties.
+
+        * API/testapi.c: 
+        * API/testapi.js: Added test for the getDirect change mentioned above.
+
+        * kjs/ExecState.cpp: 
+        * kjs/ExecState.h: Dialed back the optimization to store a direct
+        pointer to the localStorage buffer. One ExecState can grow the global
+        object's localStorage without another ExecState's knowledge, so
+        ExecState can't store a direct pointer to the localStorage buffer
+        unless/until we invent a way to update all the relevant ExecStates.
+
+        * kjs/JSGlobalObject.cpp: Inserted the symbol table into get and put
+        operations.
+        (KJS::JSGlobalObject::reset): Reset the symbol table and local storage,
+        too. Also, clear the property map here, removing the need for a
+        separate call.
+
+        * kjs/JSVariableObject.cpp:
+        * kjs/JSVariableObject.h: Added support for saving localStorage and the
+        symbol table to the back/forward cache, and restoring them.
+
+        * kjs/function.cpp:
+        (KJS::GlobalFuncImp::callAsFunction): Renamed progNode to evalNode
+        because it's an EvalNode, not a ProgramNode.
+
+        * kjs/lookup.h:
+        (KJS::cacheGlobalObject): Replaced put with faster putDirect, since
+        that's how the rest of lookup.h works. putDirect is safe here because
+        cacheGlobalObject is only used for objects whose names are not valid
+        identifiers.
+
+        * kjs/nodes.cpp: The good stuff!
+
+        (KJS::EvalNode::processDeclarations): Replaced hasProperty with
+        the new hasOwnProperty, which is slightly faster.
+
+        * kjs/object.h: Nixed clearProperties because clear() does this job now.
+
+        * kjs/property_map.cpp:
+        * kjs/property_map.h: More back/forward cache support.
+        
+        * wtf/Vector.h:
+        (WTF::::grow): Added fast non-branching grow function. I used it in
+        an earlier version of this patch, even though it's not used anymore.
+
 2007-12-09  Mark Rowe  <mrowe@apple.com>
 
         Reviewed by Oliver Hunt.
index e3129ba9a0d5b2281f2f8aee78c32eb688b993f4..db6e2c7c4637fe9d72af3165e7d6a8dcd8ec2716 100644 (file)
@@ -135,6 +135,8 @@ __ZN3KJS14JSGlobalObject10globalExecEv
 __ZN3KJS14JSGlobalObject15restoreBuiltinsERKNS_13SavedBuiltinsE
 __ZN3KJS14JSGlobalObject16stopTimeoutCheckEv
 __ZN3KJS14JSGlobalObject17startTimeoutCheckEv
+__ZN3KJS14JSGlobalObject18getOwnPropertySlotEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE
+__ZN3KJS14JSGlobalObject3putEPNS_9ExecStateERKNS_10IdentifierEPNS_7JSValueEi
 __ZN3KJS14JSGlobalObject4initEv
 __ZN3KJS14JSGlobalObject4markEv
 __ZN3KJS14JSGlobalObject5resetEPNS_7JSValueE
@@ -242,6 +244,10 @@ __ZNK3KJS11PropertyMap4saveERNS_15SavedPropertiesE
 __ZNK3KJS12DateInstance7getTimeERdRi
 __ZNK3KJS13ArrayInstance7getItemEj
 __ZNK3KJS14JSGlobalObject12saveBuiltinsERNS_13SavedBuiltinsE
+__ZNK3KJS16JSVariableObject15saveSymbolTableERN3WTF7HashMapINS1_6RefPtrINS_7UString3RepEEEmNS_17IdentifierRepHashENS_23IdentifierRepHashTraitsENS_26SymbolTableIndexHashTraitsEEE
+__ZNK3KJS16JSVariableObject16saveLocalStorageERNS_15SavedPropertiesE
+__ZNK3KJS16JSVariableObject18restoreSymbolTableERN3WTF7HashMapINS1_6RefPtrINS_7UString3RepEEEmNS_17IdentifierRepHashENS_23IdentifierRepHashTraitsENS_26SymbolTableIndexHashTraitsEEE
+__ZNK3KJS16JSVariableObject19restoreLocalStorageERNS_15SavedPropertiesE
 __ZNK3KJS19InternalFunctionImp14implementsCallEv
 __ZNK3KJS19InternalFunctionImp21implementsHasInstanceEv
 __ZNK3KJS4List8getSliceEiRS0_
index f6d80d27917d156cc271efcc9009d9b6e9022595..30c113dc9632b50ae58e275c63a448cd05fa47a0 100644 (file)
@@ -76,6 +76,8 @@ ExecState::ExecState(JSGlobalObject* globalObject, JSObject* thisV,
         m_thisVal = thisV;
         break;
     }
+    
+    m_localStorage = &m_variableObject->localStorage();
 
     if (scopeNode)
         m_globalObject->setCurrentExec(this);
@@ -103,10 +105,5 @@ JSGlobalObject* ExecState::lexicalGlobalObject() const
 
     return dynamicGlobalObject();
 }
-    
-void ExecState::updateLocalStorage() 
-{
-    m_localStorageBuffer = m_activation->localStorage().data(); 
-}
 
 } // namespace KJS
index 322170da591b41823dc42f84bad0760413de3f71..277e5d0f1fa26f02106c2a32579a1d1b06333647 100644 (file)
 #ifndef ExecState_H
 #define ExecState_H
 
-#include "value.h"
-#include "types.h"
-#include "CommonIdentifiers.h"
 #include "LabelStack.h"
+#include "LocalStorage.h"
 #include "scope_chain.h"
+#include "types.h"
 
 namespace KJS  {
 
@@ -39,6 +38,7 @@ namespace KJS  {
     };
     
     class ActivationImp;
+    class CommonIdentifiers;
     class FunctionImp;
     class GlobalFuncImp;
     class Interpreter;
@@ -81,6 +81,7 @@ namespace KJS  {
         JSObject* thisValue() const { return m_thisVal; }
         
         ExecState* callingExecState() { return m_callingExec; }
+        ExecState* savedExec() { return m_savedExec; }
         
         ActivationImp* activationObject() { return m_activation; }
         CodeType codeType() { return m_codeType; }
@@ -106,8 +107,7 @@ namespace KJS  {
         // important property lookup functions, to avoid taking PIC branches in Mach-O binaries
         const CommonIdentifiers& propertyNames() const { return *m_propertyNames; }
 
-        LocalStorageEntry* localStorage() { return m_localStorageBuffer; }
-        void updateLocalStorage();
+        LocalStorage& localStorage() { return *m_localStorage; }
     
     public:
         ExecState(JSGlobalObject* glob, JSObject* thisV,
@@ -131,7 +131,7 @@ namespace KJS  {
         FunctionImp* m_function;
         const List* m_arguments;
         ActivationImp* m_activation;
-        LocalStorageEntry* m_localStorageBuffer;
+        LocalStorage* m_localStorage;
 
         ScopeChain m_scopeChain;
         JSVariableObject* m_variableObject;
index 702dbd255a9678004811b240120910520675bb7e..7a188967049b3badcc77f5501139976a4d99fe75 100644 (file)
@@ -129,6 +129,20 @@ void JSGlobalObject::init()
     reset(prototype());
 }
 
+bool JSGlobalObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
+{
+    if (symbolTableGet(propertyName, slot))
+        return true;
+    return JSVariableObject::getOwnPropertySlot(exec, propertyName, slot);
+}
+
+void JSGlobalObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
+{
+    if (symbolTablePut(propertyName, value, attr))
+        return;
+    return JSVariableObject::put(exec, propertyName, value, attr);
+}
+
 static inline JSObject* lastInPrototypeChain(JSObject* object)
 {
     JSObject* o = object;
@@ -139,11 +153,13 @@ static inline JSObject* lastInPrototypeChain(JSObject* object)
 
 void JSGlobalObject::reset(JSValue* prototype)
 {
-    // Clear before inititalizing, to avoid marking uninitialized (dangerous) or 
-    // stale (wasteful) pointers during possible garbage collection while creating
-    // new objects below.
+    // Clear before inititalizing, to avoid calling mark() on stale pointers --
+    // which would be wasteful -- or uninitialized pointers -- which would be
+    // dangerous. (The allocations below may cause a GC.)
 
-    ExecState* exec = &d()->globalExec;
+    _prop.clear();
+    localStorage().clear();
+    symbolTable().clear();
 
     // Prototypes
     d()->functionPrototype = 0;
@@ -182,6 +198,8 @@ void JSGlobalObject::reset(JSValue* prototype)
     d()->typeErrorConstructor = 0;
     d()->URIErrorConstructor = 0;
 
+    ExecState* exec = &d()->globalExec;
+
     // Prototypes
     d()->functionPrototype = new FunctionPrototype(exec);
     d()->objectPrototype = new ObjectPrototype(exec, d()->functionPrototype);
index eb43b0088508639f88bb87955c5e3ecb0beea8ee..34dfe28224697129fb801adb3dc02f757bf5d13e 100644 (file)
@@ -147,6 +147,9 @@ namespace KJS {
     public:
         virtual ~JSGlobalObject();
 
+        virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
+        virtual void put(ExecState*, const Identifier&, JSValue*, int attr = None);
+
         // Linked list of all global objects.
         static JSGlobalObject* head() { return s_head; }
         JSGlobalObject* next() { return d()->next; }
index 6f4cd3caae2e0c2c00ebc606a68317bf1ba0c342..a1820c57874fc4d611b37d4f19cd08fd568811cc 100644 (file)
 #include "JSVariableObject.h"
 
 #include "PropertyNameArray.h"
+#include "property_map.h"
 
 namespace KJS {
 
 UString::Rep* IdentifierRepHashTraits::nullRepPtr = &UString::Rep::null; // Didn't want to make a whole source file for just this.
 
+void JSVariableObject::saveSymbolTable(SymbolTable& s) const
+{
+    s = *d->symbolTable;
+}
+
+void JSVariableObject::restoreSymbolTable(SymbolTable& s) const
+{
+    *d->symbolTable = s;
+}
+
+void JSVariableObject::saveLocalStorage(SavedProperties& p) const
+{
+    unsigned count = d->localStorage.size();
+
+    p.m_properties.clear();
+    p.m_count = count;
+
+    if (!count)
+        return;
+
+    p.m_properties.set(new SavedProperty[count]);
+    
+    SavedProperty* prop = p.m_properties.get();
+    for (size_t i = 0; i < count; ++i, ++prop) {
+        LocalStorageEntry& entry = d->localStorage[i];
+        prop->value = entry.value;
+        prop->attributes = entry.attributes;
+    }
+}
+
+void JSVariableObject::restoreLocalStorage(SavedProperties& p) const
+{
+    unsigned count = p.m_count;
+    d->localStorage.resize(count);
+    SavedProperty* prop = p.m_properties.get();
+    for (size_t i = 0; i < count; ++i, ++prop)
+        d->localStorage[i] = LocalStorageEntry(prop->value, prop->attributes);
+}
+
 bool JSVariableObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
 {
     if (symbolTable().contains(propertyName.ustring().rep()))
index 1b7b12fcb0150ca7202839440131035e2891b1ad..7834359b09e0835bd88196c54b6dae31e0227b96 100644 (file)
@@ -39,7 +39,13 @@ namespace KJS {
     public:
         SymbolTable& symbolTable() { return *d->symbolTable; }
         LocalStorage& localStorage() { return d->localStorage; }
+        
+        void saveSymbolTable(SymbolTable& s) const;
+        void restoreSymbolTable(SymbolTable& s) const;
 
+        void saveLocalStorage(SavedProperties& s) const;
+        void restoreLocalStorage(SavedProperties& s) const;
+        
         virtual bool deleteProperty(ExecState*, const Identifier&);
         virtual void getPropertyNames(ExecState*, PropertyNameArray&);
         
index 9203251d45c7a7616cc11a3a594d955427a72eba..bcd2112776c22f309d25e13d76bb0cc5df2d9fa1 100644 (file)
@@ -25,7 +25,8 @@
 #ifndef KJS_LOCAL_STORAGE_H
 #define KJS_LOCAL_STORAGE_H
 
-#include <wtf/Vector.h>
+#include <wtf/Forward.h>
+#include <wtf/VectorTraits.h>
 
 namespace KJS {
     class JSValue;
index 0846c2a5e6e061913e0442520d6911c09ec42ff9..04b9afaa496494bd4a4315496413c9de17ed9fc5 100644 (file)
@@ -699,7 +699,7 @@ JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const
         int sourceId;
         int errLine;
         UString errMsg;
-        RefPtr<EvalNode> progNode(parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg));
+        RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg);
 
         Debugger* dbg = exec->dynamicGlobalObject()->debugger();
         if (dbg) {
@@ -709,7 +709,7 @@ JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const
         }
 
         // no program node means a syntax occurred
-        if (!progNode)
+        if (!evalNode)
           return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
 
         bool switchGlobal = thisObj && thisObj != exec->dynamicGlobalObject() && thisObj->isGlobalObject();
@@ -717,7 +717,7 @@ JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const
         // enter a new execution context
         JSGlobalObject* globalObject = switchGlobal ? static_cast<JSGlobalObject*>(thisObj) : exec->dynamicGlobalObject();
         JSObject* thisVal = static_cast<JSObject*>(exec->thisValue());
-        ExecState newExec(globalObject, thisVal, progNode.get(), EvalCode, exec, globalObject->currentExec());
+        ExecState newExec(globalObject, thisVal, evalNode.get(), EvalCode, exec, globalObject->currentExec());
         if (exec->hadException())
             newExec.setException(exec->exception());
           
@@ -726,7 +726,7 @@ JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const
             newExec.setVariableObject(static_cast<JSGlobalObject*>(thisObj));
         }
         
-        Completion c = progNode->execute(&newExec);
+        Completion c = evalNode->execute(&newExec);
           
         if (switchGlobal)
             newExec.popScope();
index f47d304a212615b2eb66941dbd299b8dc43db44d..65bb5a04d54e68777b3c607e9f5185cd4133dc6e 100644 (file)
@@ -282,7 +282,7 @@ namespace KJS {
       return static_cast<JSObject* >(obj);
     }
     JSObject* newObject = new ClassCtor(exec);
-    globalObject->put(exec, propertyName, newObject, Internal | DontEnum);
+    globalObject->putDirect(propertyName, newObject, Internal | DontEnum);
     return newObject;
   }
 
index d9702ec5dbeb5487a4108d23556638bb1298aad0..99e3442c6e86f1e16960205e8f98887020f30130 100644 (file)
@@ -3488,6 +3488,7 @@ void VarDeclNode::handleSlowCase(ExecState* exec, const ScopeChain& chain, JSVal
 // ECMA 12.2
 inline void VarDeclNode::evaluateSingle(ExecState* exec)
 {
+    ASSERT(exec->variableObject()->hasOwnProperty(exec, ident) || exec->codeType() == EvalCode); // Guaranteed by processDeclarations.
     const ScopeChain& chain = exec->scopeChain();
     JSObject* variableObject = exec->variableObject();
 
@@ -3495,31 +3496,32 @@ inline void VarDeclNode::evaluateSingle(ExecState* exec)
 
     bool inGlobalScope = ++chain.begin() == chain.end();
 
-    if (inGlobalScope && (init || !variableObject->getDirect(ident))) {
-        JSValue* val = init ? init->evaluate(exec) : jsUndefined();
-        int flags = Internal;
-        if (exec->codeType() != EvalCode)
-            flags |= DontDelete;
-        if (varType == VarDeclNode::Constant)
-            flags |= ReadOnly;
-        variableObject->putDirect(ident, val, flags);
-    } else if (init) {
-        JSValue* val = init->evaluate(exec);
-        KJS_CHECKEXCEPTIONVOID
+    if (init) {
+        if (inGlobalScope) {
+            JSValue* val = init->evaluate(exec);
+            int flags = Internal;
+            if (exec->codeType() != EvalCode)
+                flags |= DontDelete;
+            if (varType == VarDeclNode::Constant)
+                flags |= ReadOnly;
+            variableObject->put(exec, ident, val, flags);
+        } else {
+            JSValue* val = init->evaluate(exec);
+            KJS_CHECKEXCEPTIONVOID
+
+            // if the variable object is the top of the scope chain, then that must
+            // be where this variable is declared, processVarDecls would have put 
+            // it there. Don't search the scope chain, to optimize this very common case.
+            if (chain.top() != variableObject)
+                return handleSlowCase(exec, chain, val);
+
+            unsigned flags = 0;
+            variableObject->getPropertyAttributes(ident, flags);
+            if (varType == VarDeclNode::Constant)
+                flags |= ReadOnly;
             
-        // if the variable object is the top of the scope chain, then that must
-        // be where this variable is declared, processVarDecls would have put 
-        // it there. Don't search the scope chain, to optimize this very common case.
-        if (chain.top() != variableObject)
-            return handleSlowCase(exec, chain, val);
-
-        unsigned flags = 0;
-        variableObject->getPropertyAttributes(ident, flags);
-        if (varType == VarDeclNode::Constant)
-            flags |= ReadOnly;
-        
-        ASSERT(variableObject->hasProperty(exec, ident));
-        variableObject->put(exec, ident, val, flags);
+            variableObject->put(exec, ident, val, flags);
+        }
     }
 }
 
@@ -4274,32 +4276,83 @@ FunctionBodyNode::FunctionBodyNode(SourceElements* children, DeclarationStacks::
 
 void FunctionBodyNode::initializeSymbolTable(ExecState* exec)
 {
-    size_t i, size;
-    size_t count = 0;
+    SymbolTable& symbolTable = exec->variableObject()->symbolTable();
+    ASSERT(!symbolTable.size());
 
-    // The order of additions here implicitly enforces the mutual exclusion described in ECMA 10.1.3.
-    for (i = 0, size = m_varStack.size(); i < size; ++i) {
-        if (m_varStack[i]->ident != exec->propertyNames().arguments)
-            m_symbolTable.set(m_varStack[i]->ident.ustring().rep(), count);
-        count++;
+    size_t localStorageIndex = 0;
+
+    for (size_t i = 0, size = m_parameters.size(); i < size; ++i, ++localStorageIndex) {
+        UString::Rep* rep = m_parameters[i].ustring().rep();
+        symbolTable.set(rep, localStorageIndex);
     }
 
-    for (i = 0, size = m_parameters.size(); i < size; ++i)
-        m_symbolTable.set(m_parameters[i].ustring().rep(), count++);
+    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i, ++localStorageIndex) {
+        UString::Rep* rep = m_functionStack[i]->ident.ustring().rep();
+        symbolTable.set(rep, localStorageIndex);
+    }
 
-    for (i = 0, size = m_functionStack.size(); i < size; ++i)
-        m_symbolTable.set(m_functionStack[i]->ident.ustring().rep(), count++);
+    for (size_t i = 0, size = m_varStack.size(); i < size; ++i, ++localStorageIndex) {
+        Identifier& ident = m_varStack[i]->ident;
+        if (ident == exec->propertyNames().arguments)
+            continue;
+        symbolTable.add(ident.ustring().rep(), localStorageIndex);
+    }
 }
 
-void FunctionBodyNode::optimizeVariableAccess()
+void ProgramNode::initializeSymbolTable(ExecState* exec)
+{
+    // If a previous script defined a symbol with the same name as one of our
+    // symbols, to avoid breaking previously optimized nodes, we need to reuse
+    // the symbol's existing storage index. So, we can't be as efficient as
+    // FunctionBodyNode::initializeSymbolTable, which knows that no bindings
+    // have yet been made.
+    
+    JSVariableObject* variableObject = exec->variableObject();
+    SymbolTable& symbolTable = variableObject->symbolTable();
+
+    size_t localStorageIndex = symbolTable.size();
+    size_t size;
+    
+    size = m_functionStack.size();
+    m_functionIndexes.resize(size);
+    for (size_t i = 0; i < size; ++i) {
+        UString::Rep* rep = m_functionStack[i]->ident.ustring().rep();
+        pair<SymbolTable::iterator, bool> result = symbolTable.add(rep, localStorageIndex);
+        m_functionIndexes[i] = result.first->second;
+        if (result.second)
+            ++localStorageIndex;
+    }
+
+    size = m_varStack.size();
+    m_varIndexes.resize(size);
+    for (size_t i = 0; i < size; ++i) {
+        const Identifier& ident = m_varStack[i]->ident;
+        if (variableObject->getDirect(ident)) {
+            m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration.
+            continue;
+        }
+
+        UString::Rep* rep = ident.ustring().rep();
+        pair<SymbolTable::iterator, bool> result = symbolTable.add(rep, localStorageIndex);
+        if (!result.second) {
+            m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration.
+            continue;
+        }
+        m_varIndexes[i] = result.first->second;
+        ++localStorageIndex;
+    }
+}
+
+void ScopeNode::optimizeVariableAccess(ExecState* exec)
 {
     DeclarationStacks::NodeStack nodeStack;
     Node* node = statementListInitializeVariableAccessStack(*m_children, nodeStack);
     if (!node)
         return;
     
+    SymbolTable& symbolTable = exec->variableObject()->symbolTable();
     while (true) {
-        node->optimizeVariableAccess(m_symbolTable, nodeStack);
+        node->optimizeVariableAccess(symbolTable, nodeStack);
         
         size_t size = nodeStack.size();
         if (!size)
@@ -4314,66 +4367,100 @@ void FunctionBodyNode::processDeclarations(ExecState* exec)
 {
     if (!m_initialized) {
         initializeSymbolTable(exec);
-        optimizeVariableAccess();
+        optimizeVariableAccess(exec);
         
         m_initialized = true;
     }
 
     LocalStorage& localStorage = exec->variableObject()->localStorage();
+    
+    // We can't just resize localStorage here because that would temporarily
+    // leave uninitialized entries, which would crash GC during the mark phase.
     localStorage.reserveCapacity(m_varStack.size() + m_parameters.size() + m_functionStack.size());
     
     int minAttributes = Internal | DontDelete;
     
-    size_t i, size;
+    // In order for our localStorage indexes to be correct, we must match the 
+    // order of addition in initializeSymbolTable().
 
-    // NOTE: Must match the order of addition in initializeSymbolTable().
+    const List& args = *exec->arguments();
+    for (size_t i = 0, size = m_parameters.size(); i < size; ++i)
+        localStorage.uncheckedAppend(LocalStorageEntry(args[i], DontDelete));
 
-    for (i = 0, size = m_varStack.size(); i < size; ++i) {
+    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) {
+        FuncDeclNode* node = m_functionStack[i];
+        localStorage.uncheckedAppend(LocalStorageEntry(node->makeFunction(exec), minAttributes));
+    }
+
+    for (size_t i = 0, size = m_varStack.size(); i < size; ++i) {
         VarDeclNode* node = m_varStack[i];
         int attributes = minAttributes;
         if (node->varType == VarDeclNode::Constant)
             attributes |= ReadOnly;
-        localStorage.append(LocalStorageEntry(jsUndefined(), attributes));
-    }
-
-    const List& args = *exec->arguments();
-    for (i = 0, size = m_parameters.size(); i < size; ++i)
-        localStorage.append(LocalStorageEntry(args[i], DontDelete));
-
-    for (i = 0, size = m_functionStack.size(); i < size; ++i) {
-        FuncDeclNode* node = m_functionStack[i];
-        localStorage.append(LocalStorageEntry(node->makeFunction(exec), minAttributes));
+        localStorage.uncheckedAppend(LocalStorageEntry(jsUndefined(), attributes));
     }
+}
 
-    exec->updateLocalStorage();
+static void gccIsCrazy() KJS_FAST_CALL;
+static void gccIsCrazy()
+{
 }
 
 void ProgramNode::processDeclarations(ExecState* exec)
 {
-    size_t i, size;
-
-    JSVariableObject* variableObject = exec->variableObject();
+    // If you remove this call, some SunSpider tests, including
+    // bitops-nsieve-bits.js, will regress substantially on Mac, due to a ~40%
+    // increase in L2 cache misses. FIXME: WTF?
+    gccIsCrazy();
     
+    initializeSymbolTable(exec);
+    optimizeVariableAccess(exec);
+
+    LocalStorage& localStorage = exec->variableObject()->localStorage();
+
+    // We can't just resize localStorage here because that would temporarily
+    // leave uninitialized entries, which would crash GC during the mark phase.
+    localStorage.reserveCapacity(localStorage.size() + m_varStack.size() + m_functionStack.size());
+
     int minAttributes = Internal | DontDelete;
 
-    for (i = 0, size = m_varStack.size(); i < size; ++i) {
-        VarDeclNode* node = m_varStack[i];
-        if (variableObject->hasProperty(exec, node->ident))
+    // In order for our localStorage indexes to be correct, we must match the
+    // order of addition in initializeSymbolTable().
+
+    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) {
+        FuncDeclNode* node = m_functionStack[i];
+        LocalStorageEntry entry = LocalStorageEntry(node->makeFunction(exec), minAttributes);
+        size_t index = m_functionIndexes[i];
+
+        if (index == localStorage.size())
+            localStorage.uncheckedAppend(entry);
+        else {
+            ASSERT(index < localStorage.size());
+            localStorage[index] = entry;
+        }
+    }
+
+    for (size_t i = 0, size = m_varStack.size(); i < size; ++i) {
+        size_t index = m_varIndexes[i];
+        if (index == missingSymbolMarker())
             continue;
+
+        VarDeclNode* node = m_varStack[i];
         int attributes = minAttributes;
         if (node->varType == VarDeclNode::Constant)
             attributes |= ReadOnly;
-        variableObject->put(exec, node->ident, jsUndefined(), attributes);
-    }
-
-    for (i = 0, size = m_functionStack.size(); i < size; ++i) {
-        FuncDeclNode* node = m_functionStack[i];
-        variableObject->put(exec, node->ident, node->makeFunction(exec), minAttributes);
+        LocalStorageEntry entry = LocalStorageEntry(jsUndefined(), attributes);
+            
+        ASSERT(index == localStorage.size());
+        localStorage.uncheckedAppend(entry);
     }
 }
 
 void EvalNode::processDeclarations(ExecState* exec)
 {
+    // We could optimize access to pre-existing symbols here, but SunSpider
+    // reports that to be a net loss.
+
     size_t i, size;
 
     JSVariableObject* variableObject = exec->variableObject();
@@ -4382,7 +4469,7 @@ void EvalNode::processDeclarations(ExecState* exec)
 
     for (i = 0, size = m_varStack.size(); i < size; ++i) {
         VarDeclNode* node = m_varStack[i];
-        if (variableObject->hasProperty(exec, node->ident))
+        if (variableObject->hasOwnProperty(exec, node->ident))
             continue;
         int attributes = minAttributes;
         if (node->varType == VarDeclNode::Constant)
index 6691494697498b012e0bb80b0aaa5c4e15f24e80..fc2e1df88e4ca5de79ba5d87f850ccff499b3c52 100644 (file)
@@ -1947,10 +1947,11 @@ namespace KJS {
   public:
     ScopeNode(SourceElements*, DeclarationStacks::VarStack*, DeclarationStacks::FunctionStack*) KJS_FAST_CALL;
 
-    int sourceId() KJS_FAST_CALL { return m_sourceId; }
-    const UString& sourceURL() KJS_FAST_CALL { return m_sourceURL; }
+    int sourceId() const KJS_FAST_CALL { return m_sourceId; }
+    const UString& sourceURL() const KJS_FAST_CALL { return m_sourceURL; }
 
   protected:
+    void optimizeVariableAccess(ExecState*) KJS_FAST_CALL;
 
     DeclarationStacks::VarStack m_varStack;
     DeclarationStacks::FunctionStack m_functionStack;
@@ -1966,7 +1967,11 @@ namespace KJS {
     virtual Completion execute(ExecState*) KJS_FAST_CALL;
     
   private:
+    void initializeSymbolTable(ExecState*) KJS_FAST_CALL;
     ALWAYS_INLINE void processDeclarations(ExecState*) KJS_FAST_CALL;
+
+    Vector<size_t> m_varIndexes; // Storage indexes belonging to the nodes in m_varStack. (Recorded to avoid double lookup.)
+    Vector<size_t> m_functionIndexes; // Storage indexes belonging to the nodes in m_functionStack. (Recorded to avoid double lookup.)
   };
 
   class EvalNode : public ScopeNode {
@@ -1991,7 +1996,6 @@ namespace KJS {
 
   private:
     void initializeSymbolTable(ExecState*) KJS_FAST_CALL;
-    void optimizeVariableAccess() KJS_FAST_CALL;
     ALWAYS_INLINE void processDeclarations(ExecState*) KJS_FAST_CALL;
 
     bool m_initialized;
index b373bb745b12133e0205edfbddc4120299ee6677..0894e42d8975b74252d90596430e8484f956d6a2 100644 (file)
@@ -334,6 +334,12 @@ bool JSObject::deleteProperty(ExecState* /*exec*/, const Identifier &propertyNam
   return true;
 }
 
+bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
+{
+    PropertySlot slot;
+    return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
+}
+
 bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
 {
   return deleteProperty(exec, Identifier::from(propertyName));
index ccf2e9b6cd8dd29baf1ec9efbf4f4a63206f468e..caa5a861d85f904f6c5745e22b3caf101129e10c 100644 (file)
@@ -111,7 +111,7 @@ namespace KJS {
      * (that is, the ECMAScript "null" value, not a null object pointer).
      */
     JSObject();
-
+    
     virtual void mark();
     virtual JSType type() const;
 
@@ -283,8 +283,9 @@ namespace KJS {
      * @param propertyName The name of the property to check for
      * @return true if the object has the property, otherwise false
      */
-    bool hasProperty(ExecState *exec, const Identifier &propertyName) const;
-    bool hasProperty(ExecState *exec, unsigned propertyName) const;
+    bool hasProperty(ExecState*, const Identifier&) const;
+    bool hasProperty(ExecState*, unsigned) const;
+    bool hasOwnProperty(ExecState*, const Identifier&) const;
 
     /**
      * Removes the specified property from the object.
@@ -443,20 +444,15 @@ namespace KJS {
     void defineGetter(ExecState *exec, const Identifier& propertyName, JSObject *getterFunc);
     void defineSetter(ExecState *exec, const Identifier& propertyName, JSObject *setterFunc);
 
-    /**
-     * Remove all properties from this object.
-     * This doesn't take DontDelete into account, and isn't in the ECMA spec.
-     * It's simply a quick way to remove everything stored in the property map.
-     */
-    void clearProperties() { _prop.clear(); }
-
     void saveProperties(SavedProperties &p) const { _prop.save(p); }
     void restoreProperties(const SavedProperties &p) { _prop.restore(p); }
 
     virtual bool isActivationObject() { return false; }
     virtual bool isGlobalObject() const { return false; }
+
   protected:
     PropertyMap _prop;
+
   private:
     const HashEntry* findPropertyHashEntry( const Identifier& propertyName ) const;
     JSValue *_proto;
index 31c935147a7162c08c95b69fcb349bc077fcb09e..d7e9bcc641097563b25c11be1f54f55d6ea34287 100644 (file)
@@ -74,10 +74,8 @@ JSValue *ObjectProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, con
     switch (id) {
         case ValueOf:
             return thisObj;
-        case HasOwnProperty: {
-            PropertySlot slot;
-            return jsBoolean(thisObj->getOwnPropertySlot(exec, Identifier(args[0]->toString(exec)), slot));
-        }
+        case HasOwnProperty:
+            return jsBoolean(thisObj->hasOwnProperty(exec, Identifier(args[0]->toString(exec))));
         case IsPrototypeOf: {
             if (!args[0]->isObject())
                 return jsBoolean(false);
index 45e391a6d4bca80662a78a01c1870348584703e3..284cdfff8dfe422cd17a8486b4ea91f225426e91 100644 (file)
@@ -122,12 +122,6 @@ struct PropertyMapHashTable {
     }
 };
 
-struct SavedProperty {
-    Identifier key;
-    ProtectedPtr<JSValue> value;
-    unsigned attributes;
-};
-
 static const unsigned emptyEntryIndex = 0;
 static const unsigned deletedSentinelIndex = 1;
 
index 82144efb55dc068cdab778472c6d4cf121f49999..c07ebf11cdbb5841b000e7136870115d15165f9e 100644 (file)
@@ -23,6 +23,7 @@
 #define KJS_PROPERTY_MAP_H_
 
 #include "identifier.h"
+#include "protect.h"
 #include <wtf/OwnArrayPtr.h>
 
 namespace KJS {
@@ -33,15 +34,17 @@ namespace KJS {
     
     struct PropertyMapEntry;
     struct PropertyMapHashTable;
-    struct SavedProperty;
     
-    class SavedProperties {
-        friend class PropertyMap;
-    public:
+    struct SavedProperty {
+        Identifier key;
+        ProtectedPtr<JSValue> value;
+        unsigned attributes;
+    };
+
+    struct SavedProperties {
         SavedProperties();
         ~SavedProperties();
         
-    private:
         unsigned m_count;
         OwnArrayPtr<SavedProperty> m_properties;
     };
@@ -50,7 +53,7 @@ namespace KJS {
     public:
         PropertyMap();
         ~PropertyMap();
-
+        
         void clear();
         
         void put(const Identifier&, JSValue*, unsigned attributes, bool checkReadOnly = false);
index f90dde3e8d2830250b74867495c70fbfa23fb1ef..22971c1ca03837c5ba5f7542d7b1560581090d4d 100644 (file)
@@ -451,6 +451,7 @@ namespace WTF {
         const T& last() const { return at(size() - 1); }
 
         void shrink(size_t size);
+        void grow(size_t size);
         void resize(size_t size);
         void reserveCapacity(size_t newCapacity);
 
@@ -634,6 +635,16 @@ namespace WTF {
         m_size = size;
     }
 
+    template<typename T, size_t inlineCapacity>
+    void Vector<T, inlineCapacity>::grow(size_t size)
+    {
+        ASSERT(size >= m_size);
+        if (size > capacity())
+            expandCapacity(size);
+        TypeOperations::initialize(end(), begin() + size);
+        m_size = size;
+    }
+
     template<typename T, size_t inlineCapacity>
     void Vector<T, inlineCapacity>::reserveCapacity(size_t newCapacity)
     {
index f2e93585ea156d193b664ac5e7d7edd64746fce8..31faad4752c5200e340ef074fab843f11940f0f1 100644 (file)
@@ -1,3 +1,9 @@
+2007-12-19  Geoffrey Garen  <ggaren@apple.com>
+
+        Build fix.
+
+        * ForwardingHeaders/wtf/VectorTraits.h: Added.
+
 2007-12-10  Timothy Hatcher  <timothy@apple.com>
 
         Reviewed by Mark Rowe.
diff --git a/JavaScriptGlue/ForwardingHeaders/wtf/VectorTraits.h b/JavaScriptGlue/ForwardingHeaders/wtf/VectorTraits.h
new file mode 100644 (file)
index 0000000..2fc1158
--- /dev/null
@@ -0,0 +1 @@
+#include <JavaScriptCore/VectorTraits.h>
index a188834ac096dbed257408ab1745ed478fa6a31b..b656d6ade2e1a9a038c48be4d9b7d2d47ef7911c 100644 (file)
@@ -1,3 +1,31 @@
+2007-12-19  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by Oliver Hunt.
+        
+        Added some tests to verify some of the changes I made while optimizing
+        global access to global variables.
+
+        * fast/dom/Window/resources/window-property-clearing-iframe0.html: Added.
+        * fast/dom/Window/resources/window-property-clearing-iframe1.html: Added.
+        * fast/dom/Window/window-property-clearing-expected.txt: Added.
+        * fast/dom/Window/window-property-clearing.html: Added.
+        * fast/dom/getter-on-window-object2-expected.txt: Added.
+        * fast/dom/getter-on-window-object2.html: Added.
+
+        Checked in failing results for these const tests. The symbol table
+        optimization broke const. (We didn't know this before because our only
+        tests used global variables.)
+
+        * fast/js/const-expected.txt:
+        * fast/js/kde/const-expected.txt:
+
+        * fast/js/resources/for-in-avoid-duplicates.js: Fixed a typo I noticed.
+        Not related to this patch.
+
+        * fast/dom/Window/window-property-shadowing.html: Changed this test to
+        use "this" instead of "window". The fact that "window" worked before,
+        despite an overriding / shadowing var declaration, was a bug.
+
 2007-12-19  Dan Bernstein  <mitz@apple.com>
 
         - remove two more tests that pass now due to changed font fallback
diff --git a/LayoutTests/fast/dom/Window/resources/window-property-clearing-iframe0.html b/LayoutTests/fast/dom/Window/resources/window-property-clearing-iframe0.html
new file mode 100644 (file)
index 0000000..3b83e99
--- /dev/null
@@ -0,0 +1,6 @@
+<script>
+var x = 1;
+function f() {}
+
+top.didFinishLoading(window);
+</script>
diff --git a/LayoutTests/fast/dom/Window/resources/window-property-clearing-iframe1.html b/LayoutTests/fast/dom/Window/resources/window-property-clearing-iframe1.html
new file mode 100644 (file)
index 0000000..f039f0c
--- /dev/null
@@ -0,0 +1,3 @@
+<script>
+top.didFinishLoading(window);
+</script>
diff --git a/LayoutTests/fast/dom/Window/window-property-clearing-expected.txt b/LayoutTests/fast/dom/Window/window-property-clearing-expected.txt
new file mode 100644 (file)
index 0000000..8a1d814
--- /dev/null
@@ -0,0 +1,15 @@
+This page tests whether global declarations are cleared after a navigation. If the test passes, you'll see a series of PASS messages below.
+
+\r
+Page 0:\r
+PASS: 'x' in childWindow should be true and is.\r
+PASS: childWindow.x should be 1 and is.\r
+PASS: 'f' in childWindow should be true and is.\r
+PASS: childWindow.f should be function and is.\r
+\r
+Page 1:\r
+PASS: 'x' in childWindow should be false and is.\r
+PASS: childWindow.x should be undefined and is.\r
+PASS: 'f' in childWindow should be false and is.\r
+PASS: typeof childWindow.f should be undefined and is.\r
+
diff --git a/LayoutTests/fast/dom/Window/window-property-clearing.html b/LayoutTests/fast/dom/Window/window-property-clearing.html
new file mode 100644 (file)
index 0000000..9f6c6f3
--- /dev/null
@@ -0,0 +1,57 @@
+<p>
+This page tests whether global declarations are cleared after a navigation.
+If the test passes, you'll see a series of PASS messages below.
+</p>
+<pre id="console"></pre>
+
+<script>
+function log(s)
+{
+    document.getElementById("console").appendChild(document.createTextNode(s + "\r\n"));
+}
+
+function shouldBe(evalA, a, b)
+{
+    if (evalA === b) {
+        log("PASS: " + a + " should be " + b + " and is.");
+    } else {
+        log("FAIL: " + a + " should be " + b + " but instead is " + evalA + ".");
+    }
+}
+
+var count = 0;
+function didFinishLoading(childWindow) // called by subframes
+{
+    log("\r\nPage " + count + ":");
+    if (!count) {
+        shouldBe('x' in childWindow, "'x' in childWindow", true);
+        shouldBe(childWindow.x, "childWindow.x", 1);
+        shouldBe('f' in childWindow, "'f' in childWindow", true);
+        shouldBe(typeof childWindow.f, "childWindow.f", "function");
+        
+        childWindow.location = "window-property-clearing-iframe1.html";
+        
+        ++count;
+    } else {
+        shouldBe('x' in childWindow, "'x' in childWindow", false);
+        shouldBe(childWindow.x, "childWindow.x", undefined);
+        shouldBe('f' in childWindow, "'f' in childWindow", false);
+        shouldBe(typeof childWindow.f, "typeof childWindow.f", "undefined");
+    
+        if (window.layoutTestController)
+            layoutTestController.notifyDone();
+    }
+}
+
+function test()
+{
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        layoutTestController.waitUntilDone();
+    }
+}
+
+test();
+</script>
+
+<iframe src="resources/window-property-clearing-iframe0.html"></iframe>
index d046426babf71f386e14b4efa0539cba1149db3b..5550d019d1804ea9945c8820736d10974efe4b95 100644 (file)
@@ -11,7 +11,7 @@
 <body>
     <pre id="console"></pre>
     <script>
-        if (window.layoutTestController)
+        if (this.layoutTestController)
             layoutTestController.dumpAsText();
 
         // Window Attributes
diff --git a/LayoutTests/fast/dom/getter-on-window-object2-expected.txt b/LayoutTests/fast/dom/getter-on-window-object2-expected.txt
new file mode 100644 (file)
index 0000000..72e110b
--- /dev/null
@@ -0,0 +1,13 @@
+This page tests what happens when a getter / setter on the window object conflicts with another property or declared variable.
+
+window.x is: 1
+typeof window.__lookupGetter__("x") is: function
+window.x is: 2
+typeof window.__lookupSetter__("x") is: function
+window.y is: window.y __getter__
+typeof window.__lookupGetter__("y") is: function
+window.y is: 2
+typeof window.__lookupSetter__("y") is: undefined
+window.z __setter__ called
+typeof window.__lookupSetter__("z") is: undefined
+
diff --git a/LayoutTests/fast/dom/getter-on-window-object2.html b/LayoutTests/fast/dom/getter-on-window-object2.html
new file mode 100644 (file)
index 0000000..3b5a25f
--- /dev/null
@@ -0,0 +1,52 @@
+<p>
+This page tests what happens when a getter / setter on the window object conflicts
+with another property or declared variable.
+</p>
+
+<pre id="console"></pre>
+
+<script>
+function log(s)
+{
+    document.getElementById("console").appendChild(document.createTextNode(s + "\n"));
+}
+
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+
+var x = 1;
+
+try {
+    window.__defineGetter__("x", function() { return "window.x __getter__"; });
+} catch(e) { log(e); }
+log("window.x is: " + window.x);
+log("typeof window.__lookupGetter__(\"x\") is: " + typeof window.__lookupGetter__("x"));
+
+try {
+window.__defineSetter__("x", function() { log("window.x __setter__ called"); });
+} catch(e) { log(e); }
+x = 2;
+log("window.x is: " + window.x);
+log("typeof window.__lookupSetter__(\"x\") is: " + typeof window.__lookupGetter__("x"));
+
+y = 1;
+
+try {
+    window.__defineGetter__("y", function() { return "window.y __getter__"; });
+} catch(e) { log(e); }
+log("window.y is: " + window.y);
+log("typeof window.__lookupGetter__(\"y\") is: " + typeof window.__lookupGetter__("y"));
+
+try {
+window.__defineSetter__("y", function() { log("window.y __setter__ called"); });
+} catch(e) { log(e); }
+y = 2;
+log("window.y is: " + window.y);
+log("typeof window.__lookupSetter__(\"y\") is: " + typeof window.__lookupGetter__("y"));
+
+try {
+window.__defineSetter__("z", function() { log("window.z __setter__ called"); });
+} catch(e) { log(e); }
+window.z = 1;
+log("typeof window.__lookupSetter__(\"z\") is: " + typeof window.__lookupGetter__("z"));
+</script>
index 4453a542ddcc167797e843fd9dd4db35a3ee8b43..e8f6aa67d69fbf46e37b2ba8dd42f36285d87e23 100644 (file)
@@ -3,8 +3,8 @@ This test checks that const declarations in JavaScript work and are readonly.
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS x is "RIGHT"
-PASS y is "RIGHT"
+FAIL x should be RIGHT. Was WRONG.
+FAIL y should be RIGHT. Was WRONG.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 61cd431b42fc382fd3c9667ab6ce797f93bd3442..10c046f8478a4100405995cb40c323377f72aceb 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS c is 11
-PASS c is 11
+FAIL c should be 11. Was 22.
 PASS v is 1
 PASS successfullyParsed is true
 
index 3358fa8f6def14cd22d6b2bb8cc5c3f59983f0b4..eb737deb9d05874bbf3e3916a05724b85f984f45 100644 (file)
@@ -8,7 +8,7 @@ function constr() {
     this.yyy = "bar";
 }
 
-constructor.prototype = { xxx: "baz", yyy: "quux" };
+constr.prototype = { xxx: "baz", yyy: "quux" };
 
 var obj = new constr();
 
index 08385658afda3169e890f09c9c9bafce1570767a..f246f4708d3bbf62147709f6d77a277e2fd1f622 100644 (file)
@@ -1,3 +1,32 @@
+2007-12-19  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by Oliver Hunt.
+
+        Build support:
+        * ForwardingHeaders/kjs/SymbolTable.h: Added.
+        * ForwardingHeaders/wtf/VectorTraits.h: Added.
+
+        * bindings/js/JSDOMWindowCustom.cpp:
+        (WebCore::JSDOMWindow::customGetOwnPropertySlot): Replaced use of
+        getDirectLocation with getOwnPropertySlot. getDirectLocation is no
+        longer valid, since global declarations are not stored in the property
+        map.
+
+        (WebCore::JSDOMWindow::customPut): Replaced use of JSObject::put with
+        JSGlobalObject::put. JSObject::put is no longer valid, since global
+        declarations are not stored in the property map.
+
+        * bindings/js/kjs_window.cpp: Replaced JSObject:: calls with Base::
+        calls, since JSObject is not our base class. This was always a bug, but
+        the bug is even more apparent after some of my changes.
+
+        (KJS::Window::clear): Removed call to clearProperties because
+        JSGlobalObject::reset takes care of that now.
+
+        * history/CachedPage.cpp:
+        * history/CachedPage.h: Added support for saving a symbol table and
+        localStorage to the page cache, and restoring it.
+
 2007-12-19  Dan Bernstein  <mitz@apple.com>
 
         Reviewed by Darin Adler and Dave Hyatt.
diff --git a/WebCore/ForwardingHeaders/kjs/SymbolTable.h b/WebCore/ForwardingHeaders/kjs/SymbolTable.h
new file mode 100644 (file)
index 0000000..0868c02
--- /dev/null
@@ -0,0 +1 @@
+#include <JavaScriptCore/SymbolTable.h>
diff --git a/WebCore/ForwardingHeaders/wtf/VectorTraits.h b/WebCore/ForwardingHeaders/wtf/VectorTraits.h
new file mode 100644 (file)
index 0000000..2fc1158
--- /dev/null
@@ -0,0 +1 @@
+#include <JavaScriptCore/VectorTraits.h>
index daeba628140bde105280797256637702f04c1358..50dd00f426869073b6793bd06d7b83824afa3fba 100644 (file)
@@ -54,18 +54,10 @@ bool JSDOMWindow::customGetOwnPropertySlot(KJS::ExecState* exec, const KJS::Iden
     }
 
     // Look for overrides first
-    KJS::JSValue** val = getDirectLocation(propertyName);
-    if (val) {
-        if (!allowsAccessFrom(exec)) {
+    if (JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot)) {
+        if (!allowsAccessFrom(exec))
             slot.setUndefined(this);
-            return true;
-        }
 
-        // FIXME: Come up with a way of having JavaScriptCore handle getters/setters in this case
-        if (_prop.hasGetterSetterProperties() && val[0]->type() == KJS::GetterSetterType)
-            fillGetterPropertySlot(slot, val);
-        else
-            slot.setValueSlot(this, val);
         return true;
     }
 
@@ -99,16 +91,17 @@ bool JSDOMWindow::customPut(KJS::ExecState* exec, const KJS::Identifier& propert
     if (!impl()->frame())
         return true;
 
-    // Called by an internal KJS, save time and jump directly to JSObject.
+    // Called by an internal KJS, save time and jump directly to JSGlobalObject.
     if (attr != KJS::None && attr != KJS::DontDelete) {
-        KJS::JSObject::put(exec, propertyName, value, attr);
+        KJS::JSGlobalObject::put(exec, propertyName, value, attr);
         return true;
     }
 
-    // We have a local override (e.g. "var location"), save time and jump directly to JSObject.
-    if (KJS::JSObject::getDirect(propertyName)) {
+    // We have a local override (e.g. "var location"), save time and jump directly to JSGlobalObject.
+    KJS::PropertySlot slot;
+    if (KJS::JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot)) {
         if (allowsAccessFrom(exec))
-            KJS::JSObject::put(exec, propertyName, value, attr);
+            KJS::JSGlobalObject::put(exec, propertyName, value, attr);
         return true;
     }
 
index 800e3c7f55b335edf141fad963d46ef39ac2d9f6..ecc0ec936d76aaf89e9e9a87080a1c2d154744bb 100644 (file)
@@ -274,7 +274,7 @@ WebCore::JSLocation* Window::location() const
 // reference our special objects during garbage collection
 void Window::mark()
 {
-    JSGlobalObject::mark();
+    Base::mark();
     if (d->loc && !d->loc->marked())
         d->loc->mark();
 }
@@ -658,7 +658,7 @@ bool Window::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName,
     return true;
   }
 
-  return JSObject::getOwnPropertySlot(exec, propertyName, slot);
+  return Base::getOwnPropertySlot(exec, propertyName, slot);
 }
 
 void Window::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
@@ -667,7 +667,7 @@ void Window::put(ExecState* exec, const Identifier& propertyName, JSValue* value
   if (entry) {
      if (entry->attr & Function) {
        if (allowsAccessFrom(exec))
-         JSObject::put(exec, propertyName, value, attr);
+         Base::put(exec, propertyName, value, attr);
        return;
     }
     if (entry->attr & ReadOnly)
@@ -793,7 +793,7 @@ void Window::put(ExecState* exec, const Identifier& propertyName, JSValue* value
     }
   }
   if (allowsAccessFrom(exec))
-    JSObject::put(exec, propertyName, value, attr);
+    Base::put(exec, propertyName, value, attr);
 }
 
 bool Window::allowsAccessFrom(const JSGlobalObject* other) const
@@ -846,7 +846,7 @@ ExecState* Window::globalExec()
     // frame does not destroy it
     ASSERT(impl()->frame());
     impl()->frame()->keepAlive();
-    return JSGlobalObject::globalExec();
+    return Base::globalExec();
 }
 
 bool Window::shouldInterruptScript() const
@@ -949,7 +949,6 @@ void Window::clear()
     *d->m_returnValueSlot = getDirect("returnValue");
 
   clearAllTimeouts();
-  clearProperties();
   clearHelperObjectProperties();
 
   // Now recreate a working global object for the next URL that will use us; but only if we haven't been
index ece8425f43eaa79e71735f2c3f5674d124e3eda4..5b38d3af3b519a19b9325d55b8bbdd3faa89f51d 100644 (file)
@@ -48,6 +48,8 @@ namespace KJS {
 
   // This is the only WebCore JS binding which does not inherit from DOMObject
   class Window : public JSGlobalObject {
+    typedef JSGlobalObject Base;
+    
     friend class WebCore::JSLocation;
     friend class WebCore::ScheduledAction;
   protected:
index 80c47d342800b514f06efdb9d089e91315dd43be..b245b2bea8bab1ae4177b54b7d55c7d0c417d703 100644 (file)
@@ -83,6 +83,7 @@ CachedPage::CachedPage(Page* page)
     , m_URL(page->mainFrame()->loader()->url())
     , m_windowProperties(new SavedProperties)
     , m_locationProperties(new SavedProperties)
+    , m_windowLocalStorage(new SavedProperties)
     , m_windowBuiltins(new SavedBuiltins)
 {
 #ifndef NDEBUG
@@ -92,16 +93,17 @@ CachedPage::CachedPage(Page* page)
     m_document->willSaveToCache(); 
     
     Frame* mainFrame = page->mainFrame();
-    KJSProxy* proxy = mainFrame->scriptProxy();
-    KJS::Window* window = KJS::Window::retrieveWindow(mainFrame);
+    Window* window = Window::retrieveWindow(mainFrame);
 
     mainFrame->clearTimers();
 
     JSLock lock;
 
-    if (proxy && window) {
-        proxy->globalObject()->saveBuiltins(*m_windowBuiltins.get());
+    if (window) {
+        window->saveBuiltins(*m_windowBuiltins.get());
         window->saveProperties(*m_windowProperties.get());
+        window->saveSymbolTable(m_windowSymbolTable);
+        window->saveLocalStorage(*m_windowLocalStorage.get());
         window->location()->saveProperties(*m_locationProperties.get());
         m_pausedTimeouts.set(window->pauseTimeouts());
     }
@@ -128,14 +130,15 @@ void CachedPage::restore(Page* page)
     ASSERT(m_document->view() == m_view);
 
     Frame* mainFrame = page->mainFrame();
-    KJSProxy* proxy = mainFrame->scriptProxy();
-    KJS::Window* window = KJS::Window::retrieveWindow(mainFrame);
+    Window* window = Window::retrieveWindow(mainFrame);
 
     JSLock lock;
 
-    if (proxy && window) {
-        proxy->globalObject()->restoreBuiltins(*m_windowBuiltins.get());
+    if (window) {
+        window->restoreBuiltins(*m_windowBuiltins.get());
         window->restoreProperties(*m_windowProperties.get());
+        window->restoreSymbolTable(m_windowSymbolTable);
+        window->restoreLocalStorage(*m_windowLocalStorage.get());
         window->location()->restoreProperties(*m_locationProperties.get());
         window->resumeTimeouts(m_pausedTimeouts.get());
     }
@@ -194,6 +197,8 @@ void CachedPage::clear()
     m_windowBuiltins.clear();
     m_pausedTimeouts.clear();
     m_cachedPagePlatformData.clear();
+    m_windowLocalStorage.clear();
+    m_windowSymbolTable.clear();
 
     gcController().garbageCollectSoon();
 }
index c41e869103da966b1d77b4e6799e7a61289a0337..2fe1b2e2feb6f2e054b8cecd2b56448c68817d9a 100644 (file)
@@ -27,6 +27,7 @@
 #define CachedPage_h
 
 #include "DocumentLoader.h"
+#include <kjs/SymbolTable.h>
 #include <wtf/RefCounted.h>
 #include <wtf/Forward.h>
 #include <wtf/RefPtr.h>
@@ -40,7 +41,7 @@ typedef struct objc_object* id;
 namespace KJS {
     
     class SavedBuiltins;
-    class SavedProperties;
+    struct SavedProperties;
 }
 
 namespace WebCore {
@@ -83,6 +84,8 @@ private:
     KURL m_URL;
     OwnPtr<KJS::SavedProperties> m_windowProperties;
     OwnPtr<KJS::SavedProperties> m_locationProperties;
+    OwnPtr<KJS::SavedProperties> m_windowLocalStorage;
+    KJS::SymbolTable m_windowSymbolTable;
     OwnPtr<KJS::SavedBuiltins> m_windowBuiltins;
     OwnPtr<PausedTimeouts> m_pausedTimeouts;
     OwnPtr<CachedPagePlatformData> m_cachedPagePlatformData;
index 7e875b3ce8f9f4124ac78b1825c827cb681da428..97b5cf992d27e40e2dc25cfdcee1e170d71d1786 100644 (file)
@@ -1,3 +1,12 @@
+2007-12-19  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by Oliver Hunt.
+
+        Build fix.
+
+        * ForwardingHeaders/kjs/SymbolTable.h: Added.
+        * ForwardingHeaders/wtf/VectorTraits.h: Added.
+
 2007-12-16  Mark Rowe  <mrowe@apple.com>
 
         Reviewed by Maciej Stachowiak.
diff --git a/WebKit/mac/ForwardingHeaders/kjs/SymbolTable.h b/WebKit/mac/ForwardingHeaders/kjs/SymbolTable.h
new file mode 100644 (file)
index 0000000..0868c02
--- /dev/null
@@ -0,0 +1 @@
+#include <JavaScriptCore/SymbolTable.h>
diff --git a/WebKit/mac/ForwardingHeaders/wtf/VectorTraits.h b/WebKit/mac/ForwardingHeaders/wtf/VectorTraits.h
new file mode 100644 (file)
index 0000000..2fc1158
--- /dev/null
@@ -0,0 +1 @@
+#include <JavaScriptCore/VectorTraits.h>