JavaScriptCore:
authormjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 12 Jan 2008 02:08:50 +0000 (02:08 +0000)
committermjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 12 Jan 2008 02:08:50 +0000 (02:08 +0000)
        Reviewed by Maciej.

        Optimized ActivationImp allocation, so that activation records are now
        first allocated on an explicitly managed stack and only heap allocated
        when necessary. Roughly a 5% improvement on SunSpider, and a larger
        improvement on benchmarks that use more function calls.

        * JavaScriptCore.xcodeproj/project.pbxproj:
        * kjs/Activation.h: Added.
        (KJS::ActivationImp::ActivationData::ActivationData):
        (KJS::ActivationImp::ActivationImp):
        (KJS::ActivationImp::classInfo):
        (KJS::ActivationImp::isActivationObject):
        (KJS::ActivationImp::isOnStack):
        (KJS::ActivationImp::d):
        (KJS::StackActivation::StackActivation):
        * kjs/ExecState.cpp:
        (KJS::ExecState::ExecState):
        (KJS::ExecState::~ExecState):
        * kjs/ExecState.h:
        (KJS::ExecState::replaceScopeChainTop):
        (KJS::ExecState::setActivationObject):
        (KJS::ExecState::setLocalStorage):
        * kjs/JSGlobalObject.cpp:
        (KJS::JSGlobalObject::reset):
        (KJS::JSGlobalObject::pushActivation):
        (KJS::JSGlobalObject::checkActivationCount):
        (KJS::JSGlobalObject::popActivationHelper):
        (KJS::JSGlobalObject::popActivation):
        (KJS::JSGlobalObject::tearOffActivation):
        * kjs/JSGlobalObject.h:
        * kjs/JSVariableObject.h:
        (KJS::JSVariableObject::JSVariableObjectData::JSVariableObjectData):
        (KJS::JSVariableObject::JSVariableObject):
        * kjs/function.cpp:
        (KJS::FunctionImp::argumentsGetter):
        (KJS::ActivationImp::ActivationImp):
        (KJS::ActivationImp::~ActivationImp):
        (KJS::ActivationImp::init):
        (KJS::ActivationImp::getOwnPropertySlot):
        (KJS::ActivationImp::markHelper):
        (KJS::ActivationImp::mark):
        (KJS::ActivationImp::ActivationData::ActivationData):
        (KJS::GlobalFuncImp::callAsFunction):
        * kjs/function.h:
        * kjs/nodes.cpp:
        (KJS::PostIncResolveNode::evaluate):
        (KJS::PostDecResolveNode::evaluate):
        (KJS::PreIncResolveNode::evaluate):
        (KJS::PreDecResolveNode::evaluate):
        (KJS::ReadModifyResolveNode::evaluate):
        (KJS::AssignResolveNode::evaluate):
        (KJS::WithNode::execute):
        (KJS::TryNode::execute):
        (KJS::FunctionBodyNode::processDeclarations):
        (KJS::FuncExprNode::evaluate):
        * kjs/object.h:
        * kjs/scope_chain.h:
        (KJS::ScopeChain::replace):
        * kjs/scope_chain_mark.h: Added.
        (KJS::ScopeChain::mark):

WebCore:

        Reviewed by Maciej.

        Added a new forwarding header, because Activation.h has been separated
        from function.h

        * ForwardingHeaders/kjs/Activation.h: Added.

LayoutTests:

        Reviewed by Maciej.

        Added a test case that came up when developing the ActivationImp tear-off.

        * fast/js/resources/vardecl-preserve-arguments.js:
        * fast/js/vardecl-preserve-arguments-expected.txt:

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

19 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
JavaScriptCore/kjs/Activation.h [new file with mode: 0644]
JavaScriptCore/kjs/ExecState.cpp
JavaScriptCore/kjs/ExecState.h
JavaScriptCore/kjs/JSGlobalObject.cpp
JavaScriptCore/kjs/JSGlobalObject.h
JavaScriptCore/kjs/JSVariableObject.h
JavaScriptCore/kjs/function.cpp
JavaScriptCore/kjs/function.h
JavaScriptCore/kjs/nodes.cpp
JavaScriptCore/kjs/object.h
JavaScriptCore/kjs/scope_chain.h
JavaScriptCore/kjs/scope_chain_mark.h [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/fast/js/resources/vardecl-preserve-arguments.js
LayoutTests/fast/js/vardecl-preserve-arguments-expected.txt
WebCore/ChangeLog
WebCore/ForwardingHeaders/kjs/Activation.h [new file with mode: 0644]

index 519c424..09dbc0a 100644 (file)
@@ -1,3 +1,67 @@
+2008-01-11  Cameron Zwarich  <cwzwarich@uwaterloo.ca>
+
+        Reviewed by Maciej.
+
+        Optimized ActivationImp allocation, so that activation records are now
+        first allocated on an explicitly managed stack and only heap allocated
+        when necessary. Roughly a 5% improvement on SunSpider, and a larger
+        improvement on benchmarks that use more function calls.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * kjs/Activation.h: Added.
+        (KJS::ActivationImp::ActivationData::ActivationData):
+        (KJS::ActivationImp::ActivationImp):
+        (KJS::ActivationImp::classInfo):
+        (KJS::ActivationImp::isActivationObject):
+        (KJS::ActivationImp::isOnStack):
+        (KJS::ActivationImp::d):
+        (KJS::StackActivation::StackActivation):
+        * kjs/ExecState.cpp:
+        (KJS::ExecState::ExecState):
+        (KJS::ExecState::~ExecState):
+        * kjs/ExecState.h:
+        (KJS::ExecState::replaceScopeChainTop):
+        (KJS::ExecState::setActivationObject):
+        (KJS::ExecState::setLocalStorage):
+        * kjs/JSGlobalObject.cpp:
+        (KJS::JSGlobalObject::reset):
+        (KJS::JSGlobalObject::pushActivation):
+        (KJS::JSGlobalObject::checkActivationCount):
+        (KJS::JSGlobalObject::popActivationHelper):
+        (KJS::JSGlobalObject::popActivation):
+        (KJS::JSGlobalObject::tearOffActivation):
+        * kjs/JSGlobalObject.h:
+        * kjs/JSVariableObject.h:
+        (KJS::JSVariableObject::JSVariableObjectData::JSVariableObjectData):
+        (KJS::JSVariableObject::JSVariableObject):
+        * kjs/function.cpp:
+        (KJS::FunctionImp::argumentsGetter):
+        (KJS::ActivationImp::ActivationImp):
+        (KJS::ActivationImp::~ActivationImp):
+        (KJS::ActivationImp::init):
+        (KJS::ActivationImp::getOwnPropertySlot):
+        (KJS::ActivationImp::markHelper):
+        (KJS::ActivationImp::mark):
+        (KJS::ActivationImp::ActivationData::ActivationData):
+        (KJS::GlobalFuncImp::callAsFunction):
+        * kjs/function.h:
+        * kjs/nodes.cpp:
+        (KJS::PostIncResolveNode::evaluate):
+        (KJS::PostDecResolveNode::evaluate):
+        (KJS::PreIncResolveNode::evaluate):
+        (KJS::PreDecResolveNode::evaluate):
+        (KJS::ReadModifyResolveNode::evaluate):
+        (KJS::AssignResolveNode::evaluate):
+        (KJS::WithNode::execute):
+        (KJS::TryNode::execute):
+        (KJS::FunctionBodyNode::processDeclarations):
+        (KJS::FuncExprNode::evaluate):
+        * kjs/object.h:
+        * kjs/scope_chain.h:
+        (KJS::ScopeChain::replace):
+        * kjs/scope_chain_mark.h: Added.
+        (KJS::ScopeChain::mark):
+
 2008-01-11  Simon Hausmann  <hausmann@webkit.org>
 
         Reviewed by Mark Rowe.
index 655d60c..044e589 100644 (file)
                7073BE3D0581291E005EE2C9 /* runtime_array.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = runtime_array.h; path = bindings/runtime_array.h; sourceTree = "<group>"; tabWidth = 8; };
                70B16A260569A10900DB756D /* runtime_object.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = runtime_object.cpp; path = bindings/runtime_object.cpp; sourceTree = "<group>"; tabWidth = 8; };
                70B16A270569A10900DB756D /* runtime_object.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = runtime_object.h; path = bindings/runtime_object.h; sourceTree = "<group>"; tabWidth = 8; };
+               7E2C6C950D31C6AB002D44E2 /* Activation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Activation.h; path = kjs/Activation.h; sourceTree = "<group>"; };
+               7E2C6C980D31C6B6002D44E2 /* scope_chain_mark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scope_chain_mark.h; path = kjs/scope_chain_mark.h; sourceTree = "<group>"; };
                84ABF1DE070B628C00A3AC05 /* npruntime_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = npruntime_impl.h; path = bindings/npruntime_impl.h; sourceTree = "<group>"; tabWidth = 8; };
                9303F567099118FA00AD71B8 /* OwnPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OwnPtr.h; sourceTree = "<group>"; };
                9303F5690991190000AD71B8 /* Noncopyable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Noncopyable.h; sourceTree = "<group>"; };
                0867D691FE84028FC02AAC07 /* JavaScriptCore */ = {
                        isa = PBXGroup;
                        children = (
+                               7E2C6C980D31C6B6002D44E2 /* scope_chain_mark.h */,
+                               7E2C6C950D31C6AB002D44E2 /* Activation.h */,
                                937B63CC09E766D200A671DD /* DerivedSources.make */,
                                14B8ECA60A5653980062BE54 /* JavaScriptCore.exp */,
                                F5C290E60284F98E018635CA /* JavaScriptCorePrefix.h */,
diff --git a/JavaScriptCore/kjs/Activation.h b/JavaScriptCore/kjs/Activation.h
new file mode 100644 (file)
index 0000000..65bb944
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ *  Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
+ *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
+ *  Copyright (C) 2007 Maks Orlovich
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this library; see the file COPYING.LIB.  If not, write to
+ *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef Activation_h
+#define Activation_h
+
+#include "ExecState.h"
+#include "JSVariableObject.h"
+#include "object.h"
+
+namespace KJS {
+
+    class Arguments;
+    class FunctionImp;
+    class StackActivation;
+
+    class ActivationImp : public JSVariableObject {
+        friend class JSGlobalObject;
+        friend class StackActivation;
+    private:
+        struct ActivationData : public JSVariableObjectData {
+            ActivationData() : isOnStack(true), leftRelic(false) { }
+            ActivationData(const ActivationData&);
+
+            ExecState* exec;
+            FunctionImp* function;
+            Arguments* argumentsObject;
+
+            bool isOnStack : 1;
+            bool leftRelic : 1;
+        };
+
+    public:
+        ActivationImp() { }
+        ActivationImp(const ActivationData&, bool);
+
+        virtual ~ActivationImp();
+
+        void init(ExecState*);
+
+        virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
+        virtual void put(ExecState*, const Identifier&, JSValue*, int attr = None);
+        virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
+
+        virtual const ClassInfo* classInfo() const { return &info; }
+        static const ClassInfo info;
+
+        virtual void mark();
+        void markChildren();
+
+        virtual bool isActivationObject() { return true; }
+    
+        bool isOnStack() const { return d()->isOnStack; }
+        bool needsPop() const { return d()->isOnStack || d()->leftRelic; }
+
+    private:
+        static PropertySlot::GetValueFunc getArgumentsGetter();
+        static JSValue* argumentsGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&);
+        void createArgumentsObject(ExecState*);
+        ActivationData* d() const { return static_cast<ActivationData*>(JSVariableObject::d); }
+    };
+
+    const size_t activationStackNodeSize = 32;
+
+    struct StackActivation {
+        StackActivation() { activationStorage.JSVariableObject::d = &activationDataStorage; }
+        StackActivation(const StackActivation&);
+      
+        ActivationImp activationStorage;
+        ActivationImp::ActivationData activationDataStorage;
+    };
+
+    struct ActivationStackNode {
+        ActivationStackNode* prev;
+        StackActivation data[activationStackNodeSize];
+    };
+
+} // namespace
+
+#endif
index 120c468..df6d9fa 100644 (file)
 #include "config.h"
 #include "ExecState.h"
 
+#include "Activation.h"
 #include "JSGlobalObject.h"
 #include "function.h"
 #include "internal.h"
+#include "scope_chain_mark.h"
 
 namespace KJS {
 
@@ -105,7 +107,7 @@ ExecState::ExecState(JSGlobalObject* globalObject, JSObject* thisObject,
     , m_switchDepth(0) 
     , m_codeType(FunctionCode)
 {
-    ActivationImp* activation = new ActivationImp(this);
+    ActivationImp* activation = globalObject->pushActivation(this);
     m_activation = activation;
     m_localStorage = &activation->localStorage();
     m_variableObject = activation;
@@ -115,6 +117,9 @@ ExecState::ExecState(JSGlobalObject* globalObject, JSObject* thisObject,
 
 ExecState::~ExecState()
 {
+    if (m_codeType == FunctionCode && m_activation->needsPop())
+        m_globalObject->popActivation();
+    
     m_globalObject->setCurrentExec(m_savedExec);
 }
 
index e383e9b..911d7f2 100644 (file)
@@ -77,6 +77,7 @@ namespace KJS  {
         bool hadException() const { return !!m_exception; }
         
         const ScopeChain& scopeChain() const { return m_scopeChain; }
+        void replaceScopeChainTop(JSObject* o) { m_scopeChain.replaceTop(o); }
         
         JSVariableObject* variableObject() const { return m_variableObject; }
         void setVariableObject(JSVariableObject* v) { m_variableObject = v; }
@@ -87,6 +88,7 @@ namespace KJS  {
         ExecState* savedExec() { return m_savedExec; }
         
         ActivationImp* activationObject() { return m_activation; }
+        void setActivationObject(ActivationImp* a) { m_activation = a; }
         CodeType codeType() { return m_codeType; }
         ScopeNode* scopeNode() { return m_scopeNode; }
         FunctionImp* function() const { return m_function; }
@@ -112,6 +114,7 @@ namespace KJS  {
         const List& emptyList() const { return *m_emptyList; }
 
         LocalStorage& localStorage() { return *m_localStorage; }
+        void setLocalStorage(LocalStorage* s) { m_localStorage = s; }
 
         // These are only valid right after calling execute().
         ComplType completionType() const { return m_completionType; }
index 9679c14..d06735c 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Cameron Zwarich (cwzwarich@uwaterloo.ca)
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,7 +30,7 @@
 #include "config.h"
 #include "JSGlobalObject.h"
 
-#include "SavedBuiltins.h"
+#include "Activation.h"
 #include "array_object.h"
 #include "bool_object.h"
 #include "date_object.h"
@@ -40,6 +41,7 @@
 #include "number_object.h"
 #include "object_object.h"
 #include "regexp_object.h"
+#include "SavedBuiltins.h"
 #include "string_object.h"
 
 #if HAVE(SYS_TIME_H)
@@ -200,6 +202,9 @@ void JSGlobalObject::reset(JSValue* prototype)
 
     ExecState* exec = &d()->globalExec;
 
+    d()->activations = new ActivationStackNode;
+    d()->activationCount = 0;
+
     // Prototypes
     d()->functionPrototype = new FunctionPrototype(exec);
     d()->objectPrototype = new ObjectPrototype(exec, d()->functionPrototype);
@@ -494,4 +499,55 @@ ExecState* JSGlobalObject::globalExec()
     return &d()->globalExec;
 }
 
+ActivationImp* JSGlobalObject::pushActivation(ExecState* exec)
+{
+    if (d()->activationCount == activationStackNodeSize) {
+        ActivationStackNode* newNode = new ActivationStackNode;
+        newNode->prev = d()->activations;
+        d()->activations = newNode;
+        d()->activationCount = 0;
+    }
+    
+    StackActivation* stackEntry = &d()->activations->data[d()->activationCount++];
+    stackEntry->activationStorage.init(exec);
+    
+    return &(stackEntry->activationStorage);
+}
+
+inline void JSGlobalObject::checkActivationCount()
+{
+    if (!d()->activationCount) {
+        ActivationStackNode* prev = d()->activations->prev;
+        delete d()->activations;
+        d()->activations = prev;
+        d()->activationCount = activationStackNodeSize;
+    }
+}
+
+void JSGlobalObject::popActivation()
+{
+    checkActivationCount();
+    d()->activations->data[--d()->activationCount].activationDataStorage.localStorage.shrink(0);    
+}
+
+void JSGlobalObject::tearOffActivation(ExecState* exec, bool leaveRelic)
+{
+    if (exec->codeType() == FunctionCode && static_cast<ActivationImp*>(exec->activationObject())->isOnStack()) {
+        ActivationImp* oldActivation = static_cast<ActivationImp*>(exec->activationObject());
+        ActivationImp* newActivation = new ActivationImp(*oldActivation->d(), leaveRelic);
+        
+        if (!leaveRelic) {
+            checkActivationCount();
+            d()->activationCount--;
+        }
+        
+        oldActivation->d()->localStorage.shrink(0);
+        
+        exec->setActivationObject(newActivation);
+        exec->setVariableObject(newActivation);
+        exec->setLocalStorage(&(newActivation->localStorage()));
+        exec->replaceScopeChainTop(newActivation);
+    }
+}
+
 } // namespace KJS
index 34dfe28..d371deb 100644 (file)
@@ -27,6 +27,8 @@
 
 namespace KJS {
 
+    class ActivationImp;
+    class ActivationStackNode;
     class ArrayObjectImp;
     class ArrayPrototype;
     class BooleanObjectImp;
@@ -128,6 +130,9 @@ namespace KJS {
             NativeErrorPrototype* URIErrorPrototype;
 
             SymbolTable inlineSymbolTable;
+
+            ActivationStackNode* activations;
+            size_t activationCount;
         };
 
     public:
@@ -227,6 +232,10 @@ namespace KJS {
 
         virtual bool allowsAccessFrom(const JSGlobalObject*) const { return true; }
 
+        ActivationImp* pushActivation(ExecState*);
+        void popActivation();
+        void tearOffActivation(ExecState*, bool markAsRelic = false);
+
     private:
         void init();
         
@@ -235,6 +244,8 @@ namespace KJS {
         bool checkTimeout();
         void resetTimeoutCheck();
 
+        void checkActivationCount();
+
         static JSGlobalObject* s_head;
     };
 
index 7834359..75cd58a 100644 (file)
@@ -56,6 +56,8 @@ namespace KJS {
         // without increasing their own size (since there's a hard limit on the
         // size of a JSCell).
         struct JSVariableObjectData {
+            JSVariableObjectData() { }
+
             JSVariableObjectData(SymbolTable* s)
                 : symbolTable(s) // Subclass owns this pointer.
             {
@@ -66,6 +68,8 @@ namespace KJS {
 
         };
 
+        JSVariableObject() { }
+
         JSVariableObject(JSVariableObjectData* data)
             : d(data) // Subclass owns this pointer.
         {
index dcce892..4212e10 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "function.h"
 
+#include "Activation.h"
 #include "ExecState.h"
 #include "JSGlobalObject.h"
 #include "Parser.h"
@@ -84,12 +85,13 @@ JSValue* FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const L
 JSValue* FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
 {
   FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
-  ExecState* e = exec;
-  while (e) {
-    if (e->function() == thisObj)
+  
+  for (ExecState* e = exec; e; e = e->callingExecState())
+    if (e->function() == thisObj) {
+      e->dynamicGlobalObject()->tearOffActivation(e, e == exec);
       return e->activationObject()->get(exec, propertyName);
-    e = e->callingExecState();
-  }
+    }
+  
   return jsNull();
 }
 
@@ -340,6 +342,26 @@ bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName)
 
 const ClassInfo ActivationImp::info = { "Activation", 0, 0 };
 
+ActivationImp::ActivationImp(const ActivationData& oldData, bool leaveRelic)
+{
+    JSVariableObject::d = new ActivationData(oldData);
+    d()->leftRelic = leaveRelic;
+}
+
+ActivationImp::~ActivationImp()
+{
+    if (!d()->isOnStack)
+        delete d();
+}
+
+void ActivationImp::init(ExecState* exec)
+{
+    d()->symbolTable = &exec->function()->body->symbolTable();
+    d()->exec = exec;
+    d()->function = exec->function();
+    d()->argumentsObject = 0;
+}
+
 JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
 {
   ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase());
@@ -367,6 +389,14 @@ bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& proper
 
     // Only return the built-in arguments object if it wasn't overridden above.
     if (propertyName == exec->propertyNames().arguments) {
+        for (ExecState* e = exec; e; e = e->callingExecState())
+            if (e->function() == d()->function) {
+                e->dynamicGlobalObject()->tearOffActivation(e, e == exec);
+                ActivationImp* newActivation = e->activationObject();
+                slot.setCustom(newActivation, newActivation->getArgumentsGetter());
+                return true;
+            }
+        
         slot.setCustom(this, getArgumentsGetter());
         return true;
     }
@@ -398,15 +428,29 @@ void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* val
     _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
 }
 
-void ActivationImp::mark()
+void ActivationImp::markChildren()
 {
-    JSVariableObject::mark();
+    LocalStorage& localStorage = d()->localStorage;
+    size_t size = localStorage.size();
+    
+    for (size_t i = 0; i < size; ++i) {
+        JSValue* value = localStorage[i].value;
+        
+        if (!value->marked())
+            value->mark();
+    }
     
     if (!d()->function->marked())
         d()->function->mark();
-
+    
     if (d()->argumentsObject && !d()->argumentsObject->marked())
-        d()->argumentsObject->mark();
+        d()->argumentsObject->mark();    
+}
+
+void ActivationImp::mark()
+{
+    JSObject::mark();
+    markChildren();
 }
 
 void ActivationImp::createArgumentsObject(ExecState* exec)
@@ -417,6 +461,15 @@ void ActivationImp::createArgumentsObject(ExecState* exec)
     d()->argumentsObject = new Arguments(exec, d()->exec->function(), *d()->exec->arguments(), this);
 }
 
+ActivationImp::ActivationData::ActivationData(const ActivationData& old)
+    : JSVariableObjectData(old)
+    , exec(old.exec)
+    , function(old.function)
+    , argumentsObject(old.argumentsObject)
+    , isOnStack(false)
+{
+}
+
 // ------------------------------ GlobalFunc -----------------------------------
 
 
@@ -696,6 +749,9 @@ JSValue* GlobalFuncImp::callAsFunction(ExecState* exec, JSObject* thisObj, const
         bool switchGlobal = thisObj && thisObj != exec->dynamicGlobalObject() && thisObj->isGlobalObject();
 
         // enter a new execution context
+        if (!switchGlobal)
+            exec->dynamicGlobalObject()->tearOffActivation(exec);
+        
         JSGlobalObject* globalObject = switchGlobal ? static_cast<JSGlobalObject*>(thisObj) : exec->dynamicGlobalObject();
         ExecState newExec(globalObject, evalNode.get(), exec);
           
index cc8e716..cc2c58b 100644 (file)
@@ -36,6 +36,7 @@ namespace KJS {
   class ActivationImp;
   class FunctionBodyNode;
   class FunctionPrototype;
+  class JSGlobalObject;
 
   class InternalFunctionImp : public JSObject {
   public:
@@ -137,53 +138,6 @@ namespace KJS {
     mutable IndexToNameMap indexToNameMap;
   };
 
-  class ActivationImp : public JSVariableObject {
-  private:
-    using JSVariableObject::JSVariableObjectData;
-
-    struct ActivationImpData : public JSVariableObjectData {
-        ActivationImpData(ExecState* e)
-            : JSVariableObjectData(&e->function()->body->symbolTable())
-            , exec(e)
-            , function(e->function()) // Store this pointer for marking, to keep our symbol table / scope alive after exec has gone out of scope.
-            , argumentsObject(0)
-        {
-        }
-
-        ExecState* exec;
-        FunctionImp* function;
-        Arguments* argumentsObject;
-    };
-
-  public:
-    ActivationImp(ExecState* exec)
-        : JSVariableObject(new ActivationImpData(exec))
-    {
-    }
-
-    virtual ~ActivationImp()
-    {
-        delete d();
-    }
-
-    virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
-    virtual void put(ExecState*, const Identifier& propertyName, JSValue* value, int attr = None);
-    virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
-
-    virtual const ClassInfo* classInfo() const { return &info; }
-    static const ClassInfo info;
-
-    virtual void mark();
-
-    virtual bool isActivationObject() { return true; }
-
-  private:
-    static PropertySlot::GetValueFunc getArgumentsGetter();
-    static JSValue* argumentsGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot);
-    void createArgumentsObject(ExecState*);
-    ActivationImpData* d() { return static_cast<ActivationImpData*>(JSVariableObject::d); }
-  };
-
   class GlobalFuncImp : public InternalFunctionImp {
   public:
     GlobalFuncImp(ExecState*, FunctionPrototype*, int i, int len, const Identifier&);
index 45b6d7f..4d93ce3 100644 (file)
@@ -1281,10 +1281,11 @@ JSValue *PostIncResolveNode::evaluate(ExecState *exec)
   ASSERT(iter != end);
 
   PropertySlot slot;
-  JSObject *base;
   do { 
-    base = *iter;
-    if (base->getPropertySlot(exec, m_ident, slot)) {
+    if ((*iter)->getPropertySlot(exec, m_ident, slot)) {
+        // See the comment in PostIncResolveNode::evaluate().
+        
+        JSObject* base = *iter;
         JSValue* v = slot.getValue(exec, base, m_ident)->toJSNumber(exec);
         base->put(exec, m_ident, jsNumber(v->toNumber(exec) + 1));
         return v;
@@ -1338,10 +1339,11 @@ JSValue *PostDecResolveNode::evaluate(ExecState *exec)
   ASSERT(iter != end);
 
   PropertySlot slot;
-  JSObject *base;
   do { 
-    base = *iter;
-    if (base->getPropertySlot(exec, m_ident, slot)) {
+    if ((*iter)->getPropertySlot(exec, m_ident, slot)) {
+        // See the comment in PostIncResolveNode::evaluate().
+        
+        JSObject* base = *iter;
         JSValue* v = slot.getValue(exec, base, m_ident)->toJSNumber(exec);
         base->put(exec, m_ident, jsNumber(v->toNumber(exec) - 1));
         return v;
@@ -1751,10 +1753,11 @@ JSValue *PreIncResolveNode::evaluate(ExecState *exec)
   ASSERT(iter != end);
 
   PropertySlot slot;
-  JSObject *base;
   do { 
-    base = *iter;
-    if (base->getPropertySlot(exec, m_ident, slot)) {
+    if ((*iter)->getPropertySlot(exec, m_ident, slot)) {
+        // See the comment in PostIncResolveNode::evaluate().
+        
+        JSObject* base = *iter;
         JSValue *v = slot.getValue(exec, base, m_ident);
 
         double n = v->toNumber(exec);
@@ -1798,10 +1801,11 @@ JSValue *PreDecResolveNode::evaluate(ExecState *exec)
   ASSERT(iter != end);
 
   PropertySlot slot;
-  JSObject *base;
   do { 
-    base = *iter;
-    if (base->getPropertySlot(exec, m_ident, slot)) {
+    if ((*iter)->getPropertySlot(exec, m_ident, slot)) {
+        // See the comment in PostIncResolveNode::evaluate().
+        
+        JSObject* base = *iter;
         JSValue *v = slot.getValue(exec, base, m_ident);
 
         double n = v->toNumber(exec);
@@ -3228,8 +3232,12 @@ JSValue *ReadModifyResolveNode::evaluate(ExecState *exec)
   JSObject *base;
   do { 
     base = *iter;
-    if (base->getPropertySlot(exec, m_ident, slot))
+    if (base->getPropertySlot(exec, m_ident, slot)) {
+      // See the comment in PostIncResolveNode::evaluate().
+      
+      base = *iter;
       goto found;
+    }
 
     ++iter;
   } while (iter != end);
@@ -3265,8 +3273,12 @@ JSValue *AssignResolveNode::evaluate(ExecState *exec)
   JSObject *base;
   do { 
     base = *iter;
-    if (base->getPropertySlot(exec, m_ident, slot))
+    if (base->getPropertySlot(exec, m_ident, slot)) {
+      // See the comment in PostIncResolveNode::evaluate().
+      
+      base = *iter;
       goto found;
+    }
 
     ++iter;
   } while (iter != end);
@@ -3997,6 +4009,7 @@ JSValue* WithNode::execute(ExecState *exec)
   KJS_CHECKEXCEPTION
   JSObject *o = v->toObject(exec);
   KJS_CHECKEXCEPTION
+  exec->dynamicGlobalObject()->tearOffActivation(exec);
   exec->pushScope(o);
   JSValue* value = statement->execute(exec);
   exec->popScope();
@@ -4195,6 +4208,7 @@ JSValue* TryNode::execute(ExecState *exec)
   if (catchBlock && exec->completionType() == Throw) {
     JSObject* obj = new JSObject;
     obj->put(exec, exceptionIdent, result, DontDelete);
+    exec->dynamicGlobalObject()->tearOffActivation(exec);
     exec->pushScope(obj);
     result = catchBlock->execute(exec);
     exec->popScope();
@@ -4357,6 +4371,9 @@ void FunctionBodyNode::processDeclarations(ExecState* exec)
         m_initialized = true;
     }
 
+    if (m_functionStack.size() != 0)
+      exec->dynamicGlobalObject()->tearOffActivation(exec);
+
     LocalStorage& localStorage = exec->variableObject()->localStorage();
     
     // We can't just resize localStorage here because that would temporarily
@@ -4569,6 +4586,8 @@ void FuncExprNode::addParams()
 
 JSValue *FuncExprNode::evaluate(ExecState *exec)
 {
+  exec->dynamicGlobalObject()->tearOffActivation(exec);
+  
   bool named = !ident.isNull();
   JSObject *functionScopeObject = 0;
 
index caa5a86..8a914ff 100644 (file)
@@ -580,18 +580,6 @@ ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifie
     return false;
 }
 
-
-// FIXME: Put this function in a separate file named something like scope_chain_mark.h -- can't put it in scope_chain.h since it depends on JSObject.
-
-inline void ScopeChain::mark()
-{
-    for (ScopeChainNode *n = _node; n; n = n->next) {
-        JSObject *o = n->object;
-        if (!o->marked())
-            o->mark();
-    }
-}
-
 inline void ScopeChain::release()
 {
     // This function is only called by deref(),
index a10abcd..7441cb8 100644 (file)
@@ -80,6 +80,7 @@ namespace KJS {
         void clear() { deref(); _node = 0; }
         void push(JSObject *);
         void push(const ScopeChain &);
+        void replaceTop(JSObject*);
         void pop();
         
         void mark();
@@ -129,6 +130,12 @@ inline void ScopeChain::push(JSObject *o)
     _node = new ScopeChainNode(_node, o);
 }
 
+inline void ScopeChain::replaceTop(JSObject* o)
+{
+    ASSERT(o);
+    _node->object = o;
+}
+
 inline void ScopeChain::pop()
 {
     ScopeChainNode *oldNode = _node;
diff --git a/JavaScriptCore/kjs/scope_chain_mark.h b/JavaScriptCore/kjs/scope_chain_mark.h
new file mode 100644 (file)
index 0000000..d756121
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (C) 2003, 2006 Apple Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this library; see the file COPYING.LIB.  If not, write to
+ *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scope_chain.h"
+#include "Activation.h"
+#include "object.h"
+
+namespace KJS {
+
+    inline void ScopeChain::mark()
+    {
+        for (ScopeChainNode* n = _node; n; n = n->next) {
+            JSObject* o = n->object;
+            
+            // An ActivationImp that is on the activation stack can't have the
+            // JSObject::marked() method called on it, because it doesn't have an
+            // entry in a GC mark bitmap, so we check here whether it is on the
+            // stack and directly call the portion of the marking code that is
+            // still relevant.
+            
+            if (o->isActivationObject() && static_cast<ActivationImp*>(o)->isOnStack())
+                static_cast<ActivationImp*>(o)->markChildren();
+            else if (!o->marked())
+                o->mark();
+        }
+    }
+
+} // namespace KJS
index 8297051..c4d2f86 100644 (file)
@@ -1,3 +1,12 @@
+2008-01-11  Cameron Zwarich  <cwzwarich@uwaterloo.ca>
+
+        Reviewed by Maciej.
+
+        Added a test case that came up when developing the ActivationImp tear-off.
+
+        * fast/js/resources/vardecl-preserve-arguments.js:
+        * fast/js/vardecl-preserve-arguments-expected.txt:
+
 2008-01-11  Justin Garcia  <justin.garcia@apple.com>
 
         <rdar://problem/5541054> editing/pasteboard/paste-RTFD.html failing on Tiger
index d36a6a8..a520973 100644 (file)
@@ -111,6 +111,24 @@ function argumentsLengthOverrideInnerBlock3() {
   return argslen;
 }
 
+function argumentsTearOff1()
+{
+    return argumentsTearOff2(2);
+}
+
+function argumentsTearOff2(b)
+{
+    var v = b;
+    var w = argumentsTearOff1.arguments;
+    argumentsTearOff3(3);
+    return v;
+}
+
+function argumentsTearOff3(c)
+{
+    var v = c;
+}
+
 shouldBe("argumentsLength()", "0");
 shouldBe("argumentsLength(1)", "1");
 shouldBe("argumentsLength('a','b')", "2");
@@ -142,6 +160,8 @@ shouldBe("argumentsLengthOverrideInnerBlock3()", "0");
 shouldBe("argumentsLengthOverrideInnerBlock3(1)", "1");
 shouldBe("argumentsLengthOverrideInnerBlock3('a','b')", "2");
 
+shouldBe("argumentsTearOff1()", "2");
+
 // this tests that behaviour should persists for
 // the program source elements also
 shouldBe("typeof undefined", "'undefined'");
index 622622d..d0918a6 100644 (file)
@@ -59,6 +59,7 @@ PASS argumentsLengthOverrideInnerBlock3(1) is 1
 PASS 'object' is 'object'
 PASS 7 is 7
 PASS argumentsLengthOverrideInnerBlock3('a','b') is 2
+PASS argumentsTearOff1() is 2
 PASS typeof undefined is 'undefined'
 PASS 'undefined' is 'undefined'
 PASS typeof arguments is 'object'
index 83f4da1..cb37c00 100644 (file)
@@ -1,3 +1,12 @@
+2008-01-11  Cameron Zwarich  <cwzwarich@uwaterloo.ca>
+
+        Reviewed by Maciej.
+
+        Added a new forwarding header, because Activation.h has been separated
+        from function.h
+
+        * ForwardingHeaders/kjs/Activation.h: Added.
+
 2008-01-11  Luca Bruno  <lethalman88@gmail.com>
 
         Reviewed by Alp Toker.
diff --git a/WebCore/ForwardingHeaders/kjs/Activation.h b/WebCore/ForwardingHeaders/kjs/Activation.h
new file mode 100644 (file)
index 0000000..f726f07
--- /dev/null
@@ -0,0 +1 @@
+#include <JavaScriptCore/Activation.h>