Browse Source

Script lifetime and update fixes (+1 squashed commit)
Squashed commits:
[8be2097] Experimental script lifetime control

Josh Engebretson 9 years ago
parent
commit
228eb3fed1

+ 3 - 0
Script/AtomicEditor/ui/frames/ResourceFrame.ts

@@ -161,6 +161,8 @@ class ResourceFrame extends ScriptWidget {
 
         editor.unsubscribeFromAllEvents();
 
+        (<EditorRootContentWidget> editor.rootContentWidget).editor = null;
+
         var editors = Object.keys(this.editors);
 
         var closedIndex = editors.indexOf(editor.fullPath);
@@ -216,6 +218,7 @@ class ResourceFrame extends ScriptWidget {
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
 
         if (ev.type == Atomic.UI_EVENT_TYPE_TAB_CHANGED && ev.target == this.tabcontainer) {
+
             var w = <EditorRootContentWidget> this.tabcontainer.currentPageWidget;
 
             if (w && w.editor) {

+ 2 - 0
Script/AtomicEditor/ui/frames/inspector/InspectorFrame.ts

@@ -94,6 +94,8 @@ class InspectorFrame extends ScriptWidget {
 
             }
 
+            // FIXME: Duktape is leaking selection object without nulling out
+            selection = null;
         }
 
     }

+ 49 - 0
Source/Atomic/Container/RefCounted.cpp

@@ -32,6 +32,7 @@ namespace Atomic
 RefCounted::RefCounted() :
     refCount_(new RefCount()),
 // ATOMIC BEGIN
+    instantiationType_(INSTANTIATION_NATIVE),
     jsHeapPtr_(0)
 // ATOMIC END
 {
@@ -58,12 +59,38 @@ void RefCounted::AddRef()
 {
     assert(refCount_->refs_ >= 0);
     (refCount_->refs_)++;
+
+    if (jsHeapPtr_ && refCount_->refs_ == 2)
+    {
+        for (unsigned i = 0; i < refCountChangedFunctions_.Size(); i++)
+        {
+            refCountChangedFunctions_[i](this, 2);
+        }
+    }
 }
 
 void RefCounted::ReleaseRef()
 {
     assert(refCount_->refs_ > 0);
     (refCount_->refs_)--;
+
+
+    if (jsHeapPtr_ && refCount_->refs_ == 1)
+    {
+        for (unsigned i = 0; i < refCountChangedFunctions_.Size(); i++)
+        {
+            (refCount_->refs_)++;
+            refCountChangedFunctions_[i](this, 1);
+            if (refCount_->refs_ == 1)
+            {
+                refCount_->refs_ = 0;
+                delete this;
+                return;
+            }
+            (refCount_->refs_)--;
+        }
+    }
+
     if (!refCount_->refs_)
         delete this;
 }
@@ -79,4 +106,26 @@ int RefCounted::WeakRefs() const
     return refCount_->weakRefs_ - 1;
 }
 
+// ATOMIC BEGIN
+
+PODVector<RefCountChangedFunction> RefCounted::refCountChangedFunctions_;
+
+void RefCounted::AddRefSilent()
+{
+    assert(refCount_->refs_ >= 0);
+    (refCount_->refs_)++;
+}
+
+void RefCounted::AddRefCountChangedFunction(RefCountChangedFunction function)
+{
+    refCountChangedFunctions_.Push(function);
+}
+
+void RefCounted::RemoveRefCountChangedFunction(RefCountChangedFunction function)
+{
+    refCountChangedFunctions_.Remove(function);
+}
+
+// ATOMIC END
+
 }

+ 27 - 2
Source/Atomic/Container/RefCounted.h

@@ -23,14 +23,26 @@
 #pragma once
 
 #include "Atomic/Atomic.h"
+#include "Vector.h"
 
 namespace Atomic
 {
 
 // ATOMIC BEGIN
 
+/// Instantation type, native code, JS, or C#
+enum InstantiationType
+{
+    INSTANTIATION_NATIVE = 0,
+    INSTANTIATION_JAVASCRIPT = 1,
+    INSTANTIATION_NET = 2
+};
+
 class RefCounted;
-typedef void (*RefCountedDeletedFunction)(RefCounted*);
+
+// function that is called when ref count goes to 1 or 2+, used for script object lifetime
+typedef void (*RefCountChangedFunction)(RefCounted*, int refCount);
+
 typedef const void* ClassID;
 
 /// Macro to be included in RefCounted derived classes for efficient RTTI
@@ -41,7 +53,6 @@ typedef const void* ClassID;
 
 // ATOMIC END
 
-
 /// Reference count structure.
 struct RefCount
 {
@@ -91,6 +102,9 @@ public:
 
     virtual bool IsObject() const { return false; }
 
+    /// Increment reference count. Do not call any lifetime book keeping
+    void AddRefSilent();
+
     virtual ClassID GetClassID() const  = 0;
     static ClassID GetClassIDStatic() { static const int typeID = 0; return (ClassID) &typeID; }
 
@@ -98,6 +112,12 @@ public:
     inline void* JSGetHeapPtr() const { return jsHeapPtr_; }
     inline void  JSSetHeapPtr(void* heapptr) { jsHeapPtr_ = heapptr; }
 
+    inline InstantiationType GetInstantiationType()  const { return instantiationType_; }
+    inline void SetInstantiationType(InstantiationType type) { instantiationType_ = type; }
+
+    static void AddRefCountChangedFunction(RefCountChangedFunction function);
+    static void RemoveRefCountChangedFunction(RefCountChangedFunction function);
+
 // ATOMIC END
 
 private:
@@ -110,7 +130,12 @@ private:
     RefCount* refCount_;
 
     // ATOMIC BEGIN
+
+    InstantiationType instantiationType_;
     void* jsHeapPtr_;
+
+    static PODVector<RefCountChangedFunction> refCountChangedFunctions_;
+
     // ATOMIC END
 
 };

+ 4 - 1
Source/Atomic/Graphics/AnimatedModel.cpp

@@ -193,11 +193,14 @@ void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQu
 {
     // If no bones or no bone-level testing, use the StaticModel test
     RayQueryLevel level = query.level_;
-    if (level < RAY_TRIANGLE || !skeleton_.GetNumBones())
+
+    // ATOMIC BEGIN
+    if ((level < RAY_TRIANGLE || !skeleton_.GetNumBones()) || context_->GetEditorContext())
     {
         StaticModel::ProcessRayQuery(query, results);
         return;
     }
+    // ATOMIC END
 
     // Check ray hit distance to AABB before proceeding with bone-level tests
     if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_)

+ 9 - 5
Source/Atomic/IO/FileSystem.cpp

@@ -437,11 +437,15 @@ bool FileSystem::SystemOpen(const String& fileName, const String& mode)
 {
     if (allowedPaths_.Empty())
     {
-        if (!FileExists(fileName) && !DirExists(fileName))
-        {
-            ATOMIC_LOGERROR("File or directory " + fileName + " not found");
-            return false;
-        }
+        // ATOMIC BEGIN
+        // allow opening of http and file urls
+        if (!fileName.StartsWith("http://") && !fileName.StartsWith("https://") && !fileName.StartsWith("file://"))        
+            if (!FileExists(fileName) && !DirExists(fileName))
+            {
+                ATOMIC_LOGERROR("File or directory " + fileName + " not found");
+                return false;
+            }
+        // ATOMIC END
 
 #ifdef _WIN32
         bool success = (size_t)ShellExecuteW(0, !mode.Empty() ? WString(mode).CString() : 0,

+ 34 - 34
Source/Atomic/UI/UI.cpp

@@ -631,7 +631,7 @@ void UI::WrapWidget(UIWidget* widget, tb::TBWidget* tbwidget)
     assert (!widgetWrap_.Contains(tbwidget));
 
     widgetWrap_[tbwidget] = widget;
-
+    widget->AddRef();
 }
 
 UIWidget* UI::WrapWidget(tb::TBWidget* widget)
@@ -650,7 +650,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIPopupWindow* popupWindow = new UIPopupWindow(context_, false);
         popupWindow->SetWidget(widget);
-        widgetWrap_[widget] = popupWindow;
+        WrapWidget(popupWindow, widget);
         return popupWindow;
     }
 
@@ -658,7 +658,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIDimmer* dimmer = new UIDimmer(context_, false);
         dimmer->SetWidget(widget);
-        widgetWrap_[widget] = dimmer;
+        WrapWidget(dimmer, widget);
         return dimmer;
     }
 
@@ -666,7 +666,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIScrollContainer* container = new UIScrollContainer(context_, false);
         container->SetWidget(widget);
-        widgetWrap_[widget] = container;
+        WrapWidget(container, widget);
         return container;
     }
 
@@ -674,39 +674,39 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIInlineSelect* select = new UIInlineSelect(context_, false);
         select->SetWidget(widget);
-        widgetWrap_[widget] = select;
+        WrapWidget(select, widget);
         return select;
     }
 
     if (widget->IsOfType<TBSlider>())
     {
-        UISlider* select = new UISlider(context_, false);
-        select->SetWidget(widget);
-        widgetWrap_[widget] = select;
-        return select;
+        UISlider* slider = new UISlider(context_, false);
+        slider->SetWidget(widget);
+        WrapWidget(slider, widget);
+        return slider;
     }
 
     if (widget->IsOfType<TBColorWidget>())
     {
-        UIColorWidget* select = new UIColorWidget(context_, false);
-        select->SetWidget(widget);
-        widgetWrap_[widget] = select;
-        return select;
+        UIColorWidget* colorWidget = new UIColorWidget(context_, false);
+        colorWidget->SetWidget(widget);
+        WrapWidget(colorWidget, widget);
+        return colorWidget;
     }
 
     if (widget->IsOfType<TBColorWheel>())
     {
-        UIColorWheel* select = new UIColorWheel(context_, false);
-        select->SetWidget(widget);
-        widgetWrap_[widget] = select;
-        return select;
+        UIColorWheel* colorWheel = new UIColorWheel(context_, false);
+        colorWheel->SetWidget(widget);
+        WrapWidget(colorWheel, widget);
+        return colorWheel;
     }
 
     if (widget->IsOfType<TBSection>())
     {
         UISection* section = new UISection(context_, false);
         section->SetWidget(widget);
-        widgetWrap_[widget] = section;
+        WrapWidget(section, widget);
         return section;
     }
 
@@ -714,7 +714,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UISeparator* sep = new UISeparator(context_, false);
         sep->SetWidget(widget);
-        widgetWrap_[widget] = sep;
+        WrapWidget(sep, widget);
         return sep;
     }
 
@@ -722,7 +722,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIContainer* container = new UIContainer(context_, false);
         container->SetWidget(widget);
-        widgetWrap_[widget] = container;
+        WrapWidget(container, widget);
         return container;
     }
 
@@ -730,7 +730,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UISelectDropdown* select = new UISelectDropdown(context_, false);
         select->SetWidget(widget);
-        widgetWrap_[widget] = select;
+        WrapWidget(select, widget);
         return select;
     }
 
@@ -742,7 +742,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
 
         UIButton* button = new UIButton(context_, false);
         button->SetWidget(widget);
-        widgetWrap_[widget] = button;
+        WrapWidget(button, widget);
         return button;
     }
 
@@ -750,7 +750,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UITextField* textfield = new UITextField(context_, false);
         textfield->SetWidget(widget);
-        widgetWrap_[widget] = textfield;
+        WrapWidget(textfield, widget);
         return textfield;
     }
 
@@ -758,7 +758,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIEditField* editfield = new UIEditField(context_, false);
         editfield->SetWidget(widget);
-        widgetWrap_[widget] = editfield;
+        WrapWidget(editfield, widget);
         return editfield;
     }
 
@@ -766,7 +766,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UISkinImage* skinimage = new UISkinImage(context_, "", false);
         skinimage->SetWidget(widget);
-        widgetWrap_[widget] = skinimage;
+        WrapWidget(skinimage, widget);
         return skinimage;
     }
 
@@ -774,14 +774,14 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIImageWidget* imagewidget = new UIImageWidget(context_, false);
         imagewidget->SetWidget(widget);
-        widgetWrap_[widget] = imagewidget;
+        WrapWidget(imagewidget, widget);
         return imagewidget;
     }
     if (widget->IsOfType<TBClickLabel>())
     {
         UIClickLabel* nwidget = new UIClickLabel(context_, false);
         nwidget->SetWidget(widget);
-        widgetWrap_[widget] = nwidget;
+        WrapWidget(nwidget, widget);
         return nwidget;
     }
 
@@ -789,7 +789,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UICheckBox* nwidget = new UICheckBox(context_, false);
         nwidget->SetWidget(widget);
-        widgetWrap_[widget] = nwidget;
+        WrapWidget(nwidget, widget);
         return nwidget;
     }
 
@@ -797,7 +797,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UISelectList* nwidget = new UISelectList(context_, false);
         nwidget->SetWidget(widget);
-        widgetWrap_[widget] = nwidget;
+        WrapWidget(nwidget, widget);
         return nwidget;
     }
 
@@ -805,7 +805,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIMessageWindow* nwidget = new UIMessageWindow(context_, NULL, "", false);
         nwidget->SetWidget(widget);
-        widgetWrap_[widget] = nwidget;
+        WrapWidget(nwidget, widget);
         return nwidget;
     }
 
@@ -813,7 +813,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UITabContainer* nwidget = new UITabContainer(context_, false);
         nwidget->SetWidget(widget);
-        widgetWrap_[widget] = nwidget;
+        WrapWidget(nwidget, widget);
         return nwidget;
     }
 
@@ -821,7 +821,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UISceneView* nwidget = new UISceneView(context_, false);
         nwidget->SetWidget(widget);
-        widgetWrap_[widget] = nwidget;
+        WrapWidget(nwidget, widget);
         return nwidget;
     }
 
@@ -830,7 +830,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UILayout* layout = new UILayout(context_, (UI_AXIS) widget->GetAxis(), false);
         layout->SetWidget(widget);
-        widgetWrap_[widget] = layout;
+        WrapWidget(layout, widget);
         return layout;
     }
 
@@ -838,7 +838,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
     {
         UIWidget* nwidget = new UIWidget(context_, false);
         nwidget->SetWidget(widget);
-        widgetWrap_[widget] = nwidget;
+        WrapWidget(nwidget, widget);
         return nwidget;
     }
 

+ 3 - 2
Source/Atomic/UI/UIEditField.cpp

@@ -210,7 +210,8 @@ void UIEditField::OnFocusChanged(bool focused)
     {
         if (focused)
         {
-            styleEdit->selection.SelectAll();
+            if (!w->GetMultiline())
+                styleEdit->selection.SelectAll();
             firstFocusFlag_ = true;
         }
         else
@@ -241,7 +242,7 @@ bool UIEditField::OnEvent(const tb::TBWidgetEvent &ev)
             TBEditField* w = (TBEditField*) widget_;
 
             TBStyleEdit* styleEdit = w->GetStyleEdit();
-            if (styleEdit != NULL)
+            if (styleEdit != NULL && !w->GetMultiline())
             {
                 styleEdit->selection.SelectAll();
             }

+ 16 - 11
Source/Atomic/UI/UIWidget.cpp

@@ -40,14 +40,12 @@ UIWidget::UIWidget(Context* context, bool createWidget) : Object(context),
     preferredSize_(new UIPreferredSize()),
     multiTouch_(false)
 {
-    AddRef();
-
     if (createWidget)
     {
         widget_ = new TBWidget();
         widget_->SetDelegate(this);
         GetSubsystem<UI>()->WrapWidget(this, widget_);
-    }
+    }    
 
 }
 
@@ -191,23 +189,30 @@ void UIWidget::ConvertEvent(UIWidget *handler, UIWidget* target, const tb::TBWid
 
 void UIWidget::OnDelete()
 {
+    VariantMap eventData;
+    eventData[WidgetDeleted::P_WIDGET] = this;
+    SendEvent(E_WIDGETDELETED, eventData);
+
+    UnsubscribeFromAllEvents();   
+
     if (widget_)
     {
         // if we don't have a UI subsystem, we are exiting
         UI* ui = GetSubsystem<UI>();
+
         if (ui)
-            ui->UnwrapWidget(widget_);
+        { 
+            if (ui->UnwrapWidget(widget_))
+            {
+                widget_ = 0;
+                ReleaseRef();
+                return;
+            }
+        }
     }
 
     widget_ = 0;
 
-    VariantMap eventData;
-    eventData[WidgetDeleted::P_WIDGET] = this;
-    SendEvent(E_WIDGETDELETED, eventData);
-
-    UnsubscribeFromAllEvents();
-
-    ReleaseRef();
 }
 
 void UIWidget::AddChildAfter(UIWidget* child, UIWidget* otherChild)

+ 1 - 1
Source/AtomicEditor/Editors/SceneEditor3D/SceneEditHistory.h

@@ -66,7 +66,7 @@ private:
 
     void AddUndoOp(SelectionEditOp* op);
 
-    SharedPtr<SceneEditor3D> sceneEditor_;
+    WeakPtr<SceneEditor3D> sceneEditor_;
 
     SelectionEditOp* curSelEditOp_;
 

+ 3 - 0
Source/AtomicEditor/Editors/SceneEditor3D/SceneEditor3D.cpp

@@ -248,6 +248,9 @@ void SceneEditor3D::Close(bool navigateToAvailableResource)
     data["Scene"] = scene_;
     SendEvent("EditorSceneClosed", data);
 
+    selection_ = 0;
+    editHistory_ = 0;
+
     if (sceneImporter_.NotNull())
     {
         sceneImporter_->SetSceneCamPosition(sceneView_->GetCameraNode()->GetWorldPosition());

+ 9 - 9
Source/AtomicEditor/Utils/FileUtils.cpp

@@ -49,7 +49,7 @@ String FileUtils::OpenProjectFileDialog()
     String upath = GetSubsystem<FileSystem>()->GetUserDocumentsDir();
 
     nfdresult_t result = NFD_OpenDialog( "atomic",
-                                upath.CString(),
+                                GetNativePath(upath).CString(),
                                 &outPath);
 
     String fullpath;
@@ -89,7 +89,7 @@ String FileUtils::NewProjectFileDialog()
     String upath = GetSubsystem<FileSystem>()->GetUserDocumentsDir();
 
     nfdresult_t result = NFD_ChooseDirectory( "Please choose the root folder for your project",
-                                upath.CString(),
+                                GetNativePath(upath).CString(),
                                 &outPath);
 
 
@@ -117,7 +117,7 @@ String FileUtils::GetBuildPath(const String& defaultPath)
     if ( defaultPath.Length() > 0) ppath = defaultPath;
 
     nfdresult_t result = NFD_ChooseDirectory( "Please choose the build folder",
-                               ppath.CString(),
+                                GetNativePath(ppath).CString(),
                                 &outPath);
 
     if (outPath && result == NFD_OKAY)
@@ -150,8 +150,8 @@ String FileUtils::GetAntPath(const String& defaultPath)
     if ( defaultPath.Length() > 0) ppath = defaultPath;
 
     nfdresult_t result = NFD_ChooseDirectory(msg.CString(),
-        ppath.CString(),
-        &outPath);
+                        GetNativePath(ppath).CString(),
+                        &outPath);
 
     if (outPath && result == NFD_OKAY)
     {
@@ -173,7 +173,7 @@ String FileUtils::GetMobileProvisionPath()
     String upath = GetSubsystem<FileSystem>()->GetUserDocumentsDir();
 
     nfdresult_t result = NFD_OpenDialog( "mobileprovision",
-                                upath.CString(),
+                                GetNativePath(upath).CString(),
                                 &outPath);
 
     String fullpath;
@@ -212,8 +212,8 @@ String FileUtils::FindPath(const String& title, const String& defaultPath)
     if ( defaultPath.Length() > 0) upath = defaultPath;
 
     nfdresult_t result = NFD_ChooseDirectory(title.CString(),
-        upath.CString(),
-        &outPath);
+                        GetNativePath(upath).CString(),
+                        &outPath);
 
     if (outPath && result == NFD_OKAY)
     {
@@ -237,7 +237,7 @@ String FileUtils::FindFile (const String& filterlist, const String& defaultPath)
     if ( defaultPath.Length() > 0) upath = defaultPath;
 
     nfdresult_t result = NFD_OpenDialog( filterlist.CString(),
-        upath.CString(),
+        GetNativePath(upath).CString(),
         &outPath);
 
     if (outPath && result == NFD_OKAY)

+ 2 - 3
Source/AtomicJS/Javascript/JSAPI.h

@@ -28,9 +28,8 @@ typedef void* JS_HEAP_PTR;
 
 #include "../Javascript/JSVM.h"
 
-#define JS_GLOBALSTASH_INDEX_COMPONENTS 0
-#define JS_GLOBALSTASH_INDEX_NODE_REGISTRY 1
-#define JS_GLOBALSTASH_VARIANTMAP_CACHE 2
+#define JS_GLOBALSTASH_INDEX_REFCOUNTED_REGISTRY 0
+#define JS_GLOBALSTASH_VARIANTMAP_CACHE 1
 
 // indexers for instance objects
 #define JS_INSTANCE_INDEX_FINALIZED 0

+ 2 - 2
Source/AtomicJS/Javascript/JSAtomic.cpp

@@ -361,9 +361,9 @@ void jsapi_init_atomic(JSVM* vm)
     // Node registry
     duk_push_global_stash(ctx);
     duk_push_object(ctx);
-    duk_put_prop_index(ctx, -2, JS_GLOBALSTASH_INDEX_NODE_REGISTRY);
-    duk_push_object(ctx);
     duk_put_prop_index(ctx, -2, JS_GLOBALSTASH_VARIANTMAP_CACHE);
+    duk_push_object(ctx);
+    duk_put_prop_index(ctx, -2, JS_GLOBALSTASH_INDEX_REFCOUNTED_REGISTRY);
     duk_pop(ctx);
 
     duk_push_c_function(ctx, js_openConsoleWindow, 0);

+ 0 - 46
Source/AtomicJS/Javascript/JSComponent.cpp

@@ -157,46 +157,6 @@ void JSComponent::SetUpdateEventMask(unsigned char mask)
     }
 }
 
-void JSComponent::UpdateReferences(bool remove)
-{
-    if (context_->GetEditorContext())
-        return;
-
-    duk_context* ctx = vm_->GetJSContext();
-
-    int top = duk_get_top(ctx);
-
-    duk_push_global_stash(ctx);
-    duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_INDEX_NODE_REGISTRY);
-
-    // can't use instance as key, as this coerces to [Object] for
-    // string property, pointer will be string representation of
-    // address, so, unique key
-
-    if (node_)
-    {
-        duk_push_pointer(ctx, (void*) node_);
-        if (remove)
-            duk_push_undefined(ctx);
-        else
-            js_push_class_object_instance(ctx, node_);
-
-        duk_put_prop(ctx, -3);
-    }
-
-    duk_push_pointer(ctx, (void*) this);
-    if (remove)
-        duk_push_undefined(ctx);
-    else
-        js_push_class_object_instance(ctx, this);
-
-    duk_put_prop(ctx, -3);
-
-    duk_pop_2(ctx);
-
-    assert(duk_get_top(ctx) == top);
-}
-
 void JSComponent::ApplyAttributes()
 {
 }
@@ -210,9 +170,6 @@ void JSComponent::InitInstance(bool hasArgs, int argIdx)
 
     duk_idx_t top = duk_get_top(ctx);
 
-    // store, so pop doesn't clear
-    UpdateReferences();
-
     // apply fields
 
     const FieldMap& fields = componentFile_->GetFields();
@@ -407,12 +364,9 @@ void JSComponent::OnNodeSet(Node* node)
 {
     if (node)
     {
-        UpdateReferences();
     }
     else
     {
-        // We are being detached from a node: execute user-defined stop function and prepare for destruction
-        UpdateReferences(true);
         Stop();
     }
 }

+ 0 - 2
Source/AtomicJS/Javascript/JSComponent.h

@@ -129,8 +129,6 @@ private:
     /// Called on physics post-update, fixed timestep.
     virtual void FixedPostUpdate(float timeStep);
 
-    void UpdateReferences(bool remove = false);
-
     /// Requested event subscription mask.
     unsigned char updateEventMask_;
     /// Current event subscription mask.

+ 0 - 2
Source/AtomicJS/Javascript/JSComponentFile.cpp

@@ -112,8 +112,6 @@ JSComponent* JSComponentFile::CreateJSComponent()
         {
             component = js_to_class_instance<JSComponent>(ctx, -1, 0);
             component->scriptClassInstance_ = true;
-            // store reference below so pop doesn't gc the component
-            component->UpdateReferences();
         }
 
         duk_pop(ctx);

+ 3 - 1
Source/AtomicJS/Javascript/JSCore.cpp

@@ -73,7 +73,9 @@ static int Object_SubscribeToEvent(duk_context* ctx)
         duk_pop(ctx); // pop null or undefined
 
         // construct a new event helper
-        js_push_class_object_instance(ctx, new JSEventHelper(object->GetContext(), object));
+        JSEventHelper* eventHelper = new JSEventHelper(object->GetContext(), object);
+        eventHelper->SetInstantiationType(INSTANTIATION_JAVASCRIPT);
+        js_push_class_object_instance(ctx, eventHelper);
 
         duk_push_object(ctx);
         duk_put_prop_string(ctx, -2, "__eventHelperFunctions");

+ 1 - 1
Source/AtomicJS/Javascript/JSEventHelper.cpp

@@ -145,7 +145,7 @@ void JSEventHelper::AddEventHandler(Object* sender, StringHash eventType)
 
 void JSEventHelper::HandleEvent(StringHash eventType, VariantMap& eventData)
 {
-    if (object_.Null())
+    if (object_.Expired())
         return;
 
     JSVM* vm = JSVM::GetJSVM(0);

+ 0 - 92
Source/AtomicJS/Javascript/JSUI.cpp

@@ -42,51 +42,10 @@ JSUI::JSUI(Context* context) : Object(context),
 
     SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(JSUI, HandleUpdate));
 
-    SubscribeToEvent(E_JSOBJECTADDED, ATOMIC_HANDLER(JSUI, HandleObjectAdded));
-
-    // for debugging only, commented out otherwise
-    //SubscribeToEvent(E_JSOBJECTREMOVED, HANDLER(JSUI, HandleObjectRemoved));
-
-    SubscribeToEvent(E_WIDGETDELETED, ATOMIC_HANDLER(JSUI, HandleWidgetDeleted));
     SubscribeToEvent(E_WIDGETEVENT, ATOMIC_HANDLER(JSUI, HandleWidgetEvent));
     SubscribeToEvent(E_WIDGETLOADED, ATOMIC_HANDLER(JSUI, HandleWidgetLoaded));
     SubscribeToEvent(E_POPUPMENUSELECT, ATOMIC_HANDLER(JSUI, HandlePopupMenuSelect));
 
-    duk_push_global_stash(ctx_);
-    duk_push_object(ctx_);
-    duk_put_prop_string(ctx_, -2, "__jsui_widgetkeepalive");
-    duk_pop(ctx_);
-
-    uiTypes_["UIWidget"] = true;
-    uiTypes_["UIButton"] = true;
-    uiTypes_["UIView"] = true;
-    uiTypes_["UIEditField"] = true;
-    uiTypes_["UITextField"] = true;
-    uiTypes_["UIImageWidget"] = true;
-    uiTypes_["UILayout"] = true;
-    uiTypes_["UIMenuWindow"] = true;
-    uiTypes_["UIWindow"] = true;
-    uiTypes_["UIClickLabel"] = true;
-    uiTypes_["UICheckBox"] = true;
-    uiTypes_["UISelectLost"] = true;
-    uiTypes_["UIListView"] = true;
-    uiTypes_["UIMessageWindow"] = true;
-    uiTypes_["UISkinImage"] = true;
-    uiTypes_["UITabContainer"] = true;
-    uiTypes_["UISceneView"] = true;
-    uiTypes_["UIContainer"] = true;
-    uiTypes_["UISection"] = true;
-    uiTypes_["UIInlineSelect"] = true;
-    uiTypes_["UITextureWidget"] = true;
-    uiTypes_["UIScrollContainer"] = true;
-    uiTypes_["UISeparator"] = true;
-    uiTypes_["UIDimmer"] = true;
-    uiTypes_["UISelectDropdown"] = true;
-    uiTypes_["UIPopupWindow"] = true;
-    uiTypes_["UISlider"] = true;
-    uiTypes_["UIColorWidget"] = true;
-    uiTypes_["UIColorWheel"] = true;
-
 }
 
 JSUI::~JSUI()
@@ -106,58 +65,7 @@ void JSUI::GatherWidgets(tb::TBWidget* widget, PODVector<tb::TBWidget*>& widgets
 
 }
 
-void JSUI::HandleObjectAdded(StringHash eventType, VariantMap& eventData)
-{
-    RefCounted* ref = static_cast<RefCounted*>(eventData[ObjectAdded::P_OBJECT].GetPtr());
-
-    assert(ref->IsObject());
-
-    Object* o = (Object*) ref;
-
-    // for any UI type, we make sure it is kept alive
-    if (uiTypes_.Contains(o->GetType()))
-    {
-        assert(o->JSGetHeapPtr());
-
-        duk_push_global_stash(ctx_);
-        duk_get_prop_string(ctx_, -1, "__jsui_widgetkeepalive");
-        // can't use instance as key, as this coerces to [Object] for
-        // string property, pointer will be string representation of
-        // address, so, unique key
-        duk_push_pointer(ctx_, o);
-        duk_push_heapptr(ctx_, o->JSGetHeapPtr());
-        duk_put_prop(ctx_, -3);
-        duk_pop_2(ctx_);
-
-    }
-
-}
-
-void JSUI::HandleObjectRemoved(StringHash eventType, VariantMap& eventData)
-{
-    Object* o = static_cast<Object*>(eventData[ObjectAdded::P_OBJECT].GetPtr());
-
-    ATOMIC_LOGINFOF("Removing %s", o->GetTypeName().CString());
-
-}
-
-void JSUI::HandleWidgetDeleted(StringHash eventType, VariantMap& eventData)
-{
-    UIWidget* widget = static_cast<UIWidget*>(eventData[WidgetDeleted::P_WIDGET].GetPtr());
-
-    if (!widget->JSGetHeapPtr())
-        return;
-
-    duk_push_global_stash(ctx_);
-    duk_get_prop_string(ctx_, -1, "__jsui_widgetkeepalive");
-    // can't use instance as key, as this coerces to [Object] for
-    // string property, pointer will be string representation of
-    // address, so, unique key
-    duk_push_pointer(ctx_, widget);
-    duk_del_prop(ctx_, -2);
-    duk_pop_2(ctx_);
 
-}
 
 void JSUI::HandleUpdate(StringHash eventType, VariantMap& eventData)
 {

+ 0 - 6
Source/AtomicJS/Javascript/JSUI.h

@@ -49,19 +49,13 @@ private:
 
     void PushWidgetEventObject(VariantMap& eventData);
 
-    void HandleObjectAdded(StringHash eventType, VariantMap& eventData);
-    void HandleObjectRemoved(StringHash eventType, VariantMap& eventData);
-
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
     void HandleWidgetEvent(StringHash eventType, VariantMap& eventData);
-    void HandleWidgetDeleted(StringHash eventType, VariantMap& eventData);
     void HandleWidgetLoaded(StringHash eventType, VariantMap& eventData);
     void HandlePopupMenuSelect(StringHash eventType, VariantMap& eventData);
 
     void GatherWidgets(tb::TBWidget* widget, PODVector<tb::TBWidget*>& widgets);
 
-    HashMap<StringHash, bool> uiTypes_;
-
 };
 
 }

+ 0 - 25
Source/AtomicJS/Javascript/JSUIAPI.cpp

@@ -118,28 +118,6 @@ int UIWindow_GetResizeToFitContentRect(duk_context* ctx)
 
 }
 
-int UI_DebugGetUIKeepAliveCount(duk_context* ctx)
-{
-    duk_push_global_stash(ctx);
-    duk_get_prop_string(ctx, -1, "__jsui_widgetkeepalive");
-
-    duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY);
-
-    double count = 0;
-
-    while (duk_next(ctx, -1 , 0)) {
-
-        duk_pop(ctx);  /* pop_key */
-        count++;
-    }
-
-    duk_pop_n(ctx, 3);  /* pop enum object, keep alive object, and stash */
-
-    duk_push_number(ctx, count);
-
-    return 1;
-}
-
 int UI_DebugGetWrappedWidgetCount(duk_context* ctx)
 {
     JSVM* vm = JSVM::GetJSVM(ctx);
@@ -160,9 +138,6 @@ void jsapi_init_ui(JSVM* vm)
     duk_push_c_function(ctx, UI_DebugGetWrappedWidgetCount, 0);
     duk_put_prop_string(ctx, -2, "debugGetWrappedWidgetCount");
 
-    duk_push_c_function(ctx, UI_DebugGetUIKeepAliveCount, 0);
-    duk_put_prop_string(ctx, -2, "debugGetUIKeepAliveCount");
-
     duk_pop_2(ctx);
 
     js_class_get_prototype(ctx, "Atomic", "UIButton");

+ 170 - 6
Source/AtomicJS/Javascript/JSVM.cpp

@@ -50,7 +50,10 @@ Vector<JSVM::JSAPIPackageRegistration*> JSVM::packageRegistrations_;
 JSVM::JSVM(Context* context) :
     Object(context),
     ctx_(0),
-    gcTime_(0.0f)
+    gcTime_(0.0f),
+    stashCount_(0),
+    totalStashCount_(0),
+    totalUnstashCount_(0)
 {
     assert(!instance_);
 
@@ -61,6 +64,7 @@ JSVM::JSVM(Context* context) :
     SharedPtr<JSEventDispatcher> dispatcher(new JSEventDispatcher(context_));
     context_->RegisterSubsystem(dispatcher);
     context_->AddGlobalEventListener(dispatcher);
+    RefCounted::AddRefCountChangedFunction(OnRefCountChanged);
 
 }
 
@@ -70,6 +74,11 @@ JSVM::~JSVM()
     context_->RemoveSubsystem(JSEventDispatcher::GetTypeStatic());
 
     duk_destroy_heap(ctx_);
+
+    RefCounted::RemoveRefCountChangedFunction(OnRefCountChanged);
+
+    // assert(stashCount_ == 0);
+
     instance_ = NULL;
 }
 
@@ -85,11 +94,6 @@ void JSVM::InitJSContext()
     duk_put_prop_string(ctx_, -2, "editor");
     duk_pop(ctx_);
 
-    duk_push_global_stash(ctx_);
-    duk_push_object(ctx_);
-    duk_put_prop_index(ctx_, -2, JS_GLOBALSTASH_INDEX_COMPONENTS);
-    duk_pop(ctx_);
-
     js_init_require(this);
     js_init_jsplugin(this);
 
@@ -139,6 +143,96 @@ void JSVM::SubscribeToEvents()
     SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(JSVM, HandleUpdate));
 }
 
+void JSVM::OnRefCountChanged(RefCounted* refCounted, int refCount)
+{
+    assert(instance_);
+    assert(refCounted->JSGetHeapPtr());
+
+    if (refCount == 1)
+    {
+        // only script reference is left, so unstash
+        instance_->Unstash(refCounted);
+    }
+    else if (refCount == 2)
+    {
+        // We are going from solely having a script reference to having another reference
+        instance_->Stash(refCounted);
+    }
+
+}
+
+void JSVM::Stash(RefCounted* refCounted)
+{
+    assert(refCounted);
+    assert(refCounted->JSGetHeapPtr());
+
+    totalStashCount_++;
+    stashCount_++;
+
+    duk_push_global_stash(ctx_);
+    duk_get_prop_index(ctx_, -1, JS_GLOBALSTASH_INDEX_REFCOUNTED_REGISTRY);
+    // can't use instance as key, as this coerces to [Object] for
+    // string property, pointer will be string representation of
+    // address, so, unique key
+
+/*
+    duk_push_pointer(ctx_, refCounted);
+    duk_get_prop(ctx_, -2);
+    assert(duk_is_undefined(ctx_, -1));
+    duk_pop(ctx_);
+*/
+
+    duk_push_pointer(ctx_, refCounted);
+    duk_push_heapptr(ctx_, refCounted->JSGetHeapPtr());
+    
+    duk_put_prop(ctx_, -3);
+    duk_pop_2(ctx_);
+
+}
+void JSVM::Unstash(RefCounted* refCounted)
+{
+    assert(refCounted);
+    assert(refCounted->JSGetHeapPtr());
+
+    assert(stashCount_ > 0);
+
+    stashCount_--;
+    totalUnstashCount_++;
+
+    duk_push_global_stash(ctx_);
+    duk_get_prop_index(ctx_, -1, JS_GLOBALSTASH_INDEX_REFCOUNTED_REGISTRY);
+    // can't use instance as key, as this coerces to [Object] for
+    // string property, pointer will be string representation of
+    // address, so, unique key
+
+/*
+    duk_push_pointer(ctx_, refCounted);
+    duk_get_prop(ctx_, -2);
+    assert(!duk_is_undefined(ctx_, -1));
+    duk_pop(ctx_);
+*/
+
+    duk_push_pointer(ctx_, refCounted);
+    duk_del_prop(ctx_, -2);   
+    duk_pop_2(ctx_);
+}
+
+// Returns if the given object is stashed
+bool JSVM::GetStashed(RefCounted* refcounted) const
+{
+    duk_push_global_stash(ctx_);
+    duk_get_prop_index(ctx_, -1, JS_GLOBALSTASH_INDEX_REFCOUNTED_REGISTRY);
+
+    duk_push_pointer(ctx_, refcounted);
+    duk_get_prop(ctx_, -2);
+
+    bool result = !duk_is_undefined(ctx_, -1);
+
+    duk_pop_3(ctx_);
+    return result;
+}
+
+
 void JSVM::HandleUpdate(StringHash eventType, VariantMap& eventData)
 {
 
@@ -163,6 +257,8 @@ void JSVM::HandleUpdate(StringHash eventType, VariantMap& eventData)
 
         gcTime_ = 0;
 
+        // DumpJavascriptObjects();
+
     }
 
     duk_get_global_string(ctx_, "__js_atomic_main_update");
@@ -478,4 +574,72 @@ bool JSVM::ExecuteMain()
 
 }
 
+void JSVM::DumpJavascriptObjects()
+{
+    ATOMIC_LOGINFOF("--- JS Objects ---");
+    ATOMIC_LOGINFOF("Stash Count: %u, Total Stash: %u, Total Unstash: %u", stashCount_, totalStashCount_, totalUnstashCount_);
+
+    HashMap<StringHash, String> strLookup;
+    HashMap<StringHash, unsigned> totalClassCount;
+    HashMap<StringHash, unsigned> stashedClassCount;
+    HashMap<StringHash, int> maxRefCount;
+
+    StringHash refCountedTypeHash("RefCounted");
+    strLookup[refCountedTypeHash] = "RefCounted";
+
+    duk_push_global_stash(ctx_);
+    duk_get_prop_index(ctx_, -1, JS_GLOBALSTASH_INDEX_REFCOUNTED_REGISTRY);
+
+    HashMap<void*, RefCounted*>::ConstIterator itr = heapToObject_.Begin();
+    while (itr != heapToObject_.End())
+    {
+        void* heapPtr = itr->first_;
+        RefCounted* refCounted = itr->second_;
+
+        // TODO: need a lookup for refcounted classid to typename
+        const String& className = refCounted->IsObject() ? ((Object*)refCounted)->GetTypeName() : "RefCounted";
+        StringHash typeHash = refCounted->IsObject() ? ((Object*)refCounted)->GetType() : refCountedTypeHash;
+
+        strLookup.InsertNew(typeHash, className);
+
+        totalClassCount.InsertNew(typeHash, 0);
+        totalClassCount[typeHash]++;
+
+        maxRefCount.InsertNew(typeHash, 0);
+        if (refCounted->Refs() > maxRefCount[typeHash])
+            maxRefCount[typeHash] = refCounted->Refs();
+
+        duk_push_pointer(ctx_, refCounted);
+        duk_get_prop(ctx_, -2);
+
+        if (!duk_is_undefined(ctx_, -1))
+        {
+            stashedClassCount.InsertNew(typeHash, 0);
+            stashedClassCount[typeHash]++;
+        }
+
+        duk_pop(ctx_);
+
+        itr++;
+
+    }
+
+    HashMap<StringHash, String>::ConstIterator itr2 = strLookup.Begin();
+    while (itr2 != strLookup.End())
+    {
+        StringHash typeHash = itr2->first_;
+        const String& className = itr2->second_;
+        unsigned totalCount = totalClassCount[typeHash];
+        unsigned stashedCount = stashedClassCount[typeHash];
+        int _maxRefCount = maxRefCount[typeHash];
+
+        ATOMIC_LOGINFOF("Classname: %s, Total: %u, Stashed: %u, Max Refs: %i", className.CString(), totalCount, stashedCount, _maxRefCount);
+
+        itr2++;
+    }
+
+    duk_pop_2(ctx_);
+
+}
+
 }

+ 41 - 3
Source/AtomicJS/Javascript/JSVM.h

@@ -91,7 +91,7 @@ public:
     void GC();
     JSMetrics* GetMetrics() { return metrics_; }
 
-    void DumpJavascriptObjects() {}
+    void DumpJavascriptObjects();
 
 #ifdef JSVM_DEBUG
 
@@ -103,18 +103,20 @@ public:
     }
 #endif
 
+    // Returns if the given object is stashed
+    bool GetStashed(RefCounted* refcounted) const;
 
-    inline void AddObject(void* heapptr, RefCounted* object)
+    inline void AddObject(void* heapptr, RefCounted* object, InstantiationType instantiationType)
     {
         assert(!object->JSGetHeapPtr());
 
         object->JSSetHeapPtr(heapptr);
+        object->SetInstantiationType(instantiationType);
 
 #ifdef JSVM_DEBUG
         assert(heapToObject_.Find(heapptr) == heapToObject_.End());
 #endif
 
-
         heapToObject_[heapptr] = object;
 
 #ifdef JSVM_DEBUG
@@ -122,6 +124,29 @@ public:
         if (itr != removedHeapPtr_.End())
             removedHeapPtr_.Erase(itr);
 #endif
+        
+        if (instantiationType != INSTANTIATION_JAVASCRIPT)
+        {
+            if (!object->Refs())
+            {
+                ATOMIC_LOGWARNING("JSVM::AddObject, native or C# instantiated object added with 0 refs");
+            }
+            else
+            {
+                // stash
+                Stash(object);
+            }
+        }
+        else
+        {
+            if (object->Refs() != 0)
+            {
+                // we already have a native reference, so need to stash
+                Stash(object);
+            }
+        }
+
+        object->AddRefSilent();
 
         if (object->IsObject())
         {
@@ -153,6 +178,8 @@ public:
 
     }
 
+    void Stash(RefCounted* refCounted);
+
     inline RefCounted* GetObjectPtr(void* heapptr, bool allowNull = false)
     {
 #ifdef JSVM_DEBUG
@@ -197,8 +224,14 @@ public:
 
     int GetRealLineNumber(const String& fileName, const int lineNumber);
 
+    unsigned GetStashCount() const { return stashCount_; }
+    unsigned GetTotalStashCount() const { return totalStashCount_;  }
+    unsigned GetTotalUnstashCount() const { return totalUnstashCount_; }
+
 private:
 
+    void Unstash(RefCounted* refCounted);
+
     struct JSAPIPackageRegistration
     {
         JSAPIPackageRegistration()
@@ -226,6 +259,8 @@ private:
         VariantMap settings;
     };
 
+    static void OnRefCountChanged(RefCounted* refCounted, int refCount);
+
     void SubscribeToEvents();
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
 
@@ -255,6 +290,9 @@ private:
 
     static Vector<JSAPIPackageRegistration*> packageRegistrations_;
 
+    unsigned stashCount_;
+    unsigned totalStashCount_;
+    unsigned totalUnstashCount_;
 
     static JSVM* instance_;
 

+ 10 - 14
Source/ToolCore/JSBind/JavaScript/JSFunctionWriter.cpp

@@ -311,15 +311,16 @@ void JSFunctionWriter::WriteConstructor(String& source)
         }
 
         source.AppendWithFormat("if (!duk_get_top(ctx) || !duk_is_pointer(ctx, 0))\n"\
-                                "{\n"\
-                                "%s\n"\
-                                "%s* native = new %s(%s);\n" \
-                                "vm->AddObject(ptr, native);\n"\
-                                "}\n" \
-                                "else if (duk_is_pointer(ctx, 0))\n" \
-                                "{\n" \
-                                "vm->AddObject(ptr, (RefCounted*) duk_get_pointer(ctx, 0));\n" \
-                                "}\n", marshal.CString(), klass->GetNativeName().CString(), klass->GetNativeName().CString(), sparams.CString());
+            "{\n"\
+            "%s\n"\
+            "%s* native = new %s(%s);\n" \
+            "vm->AddObject(ptr, native, INSTANTIATION_JAVASCRIPT);\n"\
+            "}\n" \
+            "else if (duk_is_pointer(ctx, 0))\n" \
+            "{\n" \
+            "RefCounted* rc = (RefCounted*) duk_get_pointer(ctx, 0);\n" \
+            "vm->AddObject(ptr, rc, rc->GetInstantiationType());\n" \
+            "}\n", marshal.CString(), klass->GetNativeName().CString(), klass->GetNativeName().CString(), sparams.CString());
     }
     else
     {
@@ -343,12 +344,7 @@ void JSFunctionWriter::WriteConstructor(String& source)
         source.Append("duk_push_this(ctx);\n "\
                       "duk_push_c_function(ctx, jsb_finalizer_RefCounted, 1);\n "\
                       "duk_set_finalizer(ctx, -2);\n "\
-                      \
-                      "RefCounted* ref = JSVM::GetJSVM(ctx)->GetObjectPtr(duk_get_heapptr(ctx, -1));\n "\
-                      "ref->AddRef();\n "\
-                      \
                       "duk_pop(ctx);\n");
-
     }
 
     source += "   return 0;";

+ 11 - 1
Source/ToolCore/NETTools/NETProjectSystem.cpp

@@ -250,10 +250,20 @@ namespace ToolCore
             projectPath = GetParentPath(projectPath);
 
         solutionPath_ = AddTrailingSlash(projectPath) + "AtomicNET/Solution/AtomicProject.sln";
-        projectAssemblyPath_ = AddTrailingSlash(projectPath) + "Resource/AtomicProject.dll";
+        projectAssemblyPath_ = AddTrailingSlash(projectPath) + "Resources/AtomicProject.dll";
 
         FileSystem* fileSystem = GetSubsystem<FileSystem>();
 
+        // TODO: We need a better way of marking C# projects
+        StringVector results;
+        fileSystem->ScanDir(results, AddTrailingSlash(projectPath) + "Resources", "*.cs", SCAN_FILES, true);
+        if (!results.Size())
+        {
+            fileSystem->ScanDir(results, AddTrailingSlash(projectPath) + "Resources", "*.dll", SCAN_FILES, true);
+            if (!results.Size())
+                return;
+        }            
+
         // if the solution or project assemblies don't exist mark as dirty
 
         if (!fileSystem->FileExists(solutionPath_))