Browse Source

Updated to AngelScript 2.23.1 WIP.
Fixed crash at exit when a script contains an array of Node handles.
Fixed missing sound type enum in the editor. Added Ambient sound type.
Fixed incorrect occlusion when a drawable is set for no occlusion, but the parent octant is occluded.
Removed unnecessary node lookup in network replication.
Added GetScene() to Component for convenience.

Lasse Öörni 13 years ago
parent
commit
1a9dced05b
49 changed files with 1688 additions and 1338 deletions
  1. 0 3
      Bin/Data/Scripts/Editor/EditorNodeWindow.as
  2. 1 1
      Docs/Urho3D.dox
  3. 2 1
      Engine/Audio/AudioDefs.h
  4. 2 2
      Engine/Audio/SoundSource.cpp
  5. 2 1
      Engine/Engine/AudioAPI.cpp
  6. 1 1
      Engine/Graphics/AnimatedModel.cpp
  7. 1 1
      Engine/Graphics/BillboardSet.cpp
  8. 5 1
      Engine/Graphics/Drawable.cpp
  9. 1 1
      Engine/Graphics/Light.cpp
  10. 7 10
      Engine/Graphics/Octree.cpp
  11. 0 6
      Engine/Graphics/Octree.h
  12. 2 2
      Engine/Graphics/ParticleEmitter.cpp
  13. 1 1
      Engine/Graphics/StaticModel.cpp
  14. 96 75
      Engine/Network/Connection.cpp
  15. 1 1
      Engine/Network/Connection.h
  16. 1 1
      Engine/Physics/CollisionShape.cpp
  17. 2 2
      Engine/Physics/Joint.cpp
  18. 1 1
      Engine/Physics/PhysicsWorld.cpp
  19. 3 3
      Engine/Physics/RigidBody.cpp
  20. 5 0
      Engine/Scene/Component.cpp
  21. 4 3
      Engine/Scene/Component.h
  22. 1 1
      Engine/Scene/SmoothedTransform.cpp
  23. 14 22
      Engine/Script/ScriptInstance.cpp
  24. 1 1
      Readme.txt
  25. 28 25
      ThirdParty/AngelScript/include/angelscript.h
  26. 64 0
      ThirdParty/AngelScript/source/as_array.h
  27. 8 8
      ThirdParty/AngelScript/source/as_builder.cpp
  28. 126 121
      ThirdParty/AngelScript/source/as_bytecode.cpp
  29. 0 3
      ThirdParty/AngelScript/source/as_bytecode.h
  30. 235 235
      ThirdParty/AngelScript/source/as_callfunc_arm_gcc.S
  31. 242 242
      ThirdParty/AngelScript/source/as_callfunc_arm_msvc.asm
  32. 223 152
      ThirdParty/AngelScript/source/as_callfunc_x86.cpp
  33. 222 133
      ThirdParty/AngelScript/source/as_compiler.cpp
  34. 1 1
      ThirdParty/AngelScript/source/as_compiler.h
  35. 1 7
      ThirdParty/AngelScript/source/as_config.h
  36. 175 85
      ThirdParty/AngelScript/source/as_context.cpp
  37. 1 0
      ThirdParty/AngelScript/source/as_context.h
  38. 3 1
      ThirdParty/AngelScript/source/as_criticalsection.h
  39. 2 2
      ThirdParty/AngelScript/source/as_generic.cpp
  40. 2 4
      ThirdParty/AngelScript/source/as_module.cpp
  41. 1 2
      ThirdParty/AngelScript/source/as_objecttype.cpp
  42. 85 97
      ThirdParty/AngelScript/source/as_restore.cpp
  43. 6 13
      ThirdParty/AngelScript/source/as_scriptengine.cpp
  44. 15 9
      ThirdParty/AngelScript/source/as_scriptfunction.cpp
  45. 9 5
      ThirdParty/AngelScript/source/as_scriptfunction.h
  46. 2 0
      ThirdParty/AngelScript/source/as_texts.h
  47. 72 40
      ThirdParty/AngelScript/source/as_thread.cpp
  48. 10 12
      ThirdParty/AngelScript/source/as_thread.h
  49. 1 0
      Urho3D/Urho3D.cpp

+ 0 - 3
Bin/Data/Scripts/Editor/EditorNodeWindow.as

@@ -463,9 +463,6 @@ UIElement@ CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializa
 
             for (uint i = 0; i < enumNames.length; ++i)
             {
-                // Hack: check for certain internal enums and break
-                if (enumNames[i] == "Master")
-                    break;
                 Text@ choice = Text();
                 choice.SetStyle(uiStyle, "EditorEnumAttributeText");
                 choice.text = enumNames[i];

+ 1 - 1
Docs/Urho3D.dox

@@ -61,7 +61,7 @@ Urho3D is greatly inspired by OGRE (http://www.ogre3d.org/) and Horde3D (http://
 
 Urho3D uses the following third-party libraries:
 
-- AngelScript 2.23.0 (http://www.angelcode.com/angelscript/)
+- AngelScript 2.23.1 WIP (http://www.angelcode.com/angelscript/)
 - Bullet 2.80 (http://www.bulletphysics.org/)
 - FreeType 2.3.12 (http://www.freetype.org/)
 - GLee 5.4 (http://elf-stone.com/)

+ 2 - 1
Engine/Audio/AudioDefs.h

@@ -27,8 +27,9 @@
 enum SoundType
 {
     SOUND_EFFECT = 0,
-    SOUND_MUSIC,
+    SOUND_AMBIENT,
     SOUND_VOICE,
+    SOUND_MUSIC,
     SOUND_MASTER,
     MAX_SOUND_TYPES
 };

+ 2 - 2
Engine/Audio/SoundSource.cpp

@@ -90,10 +90,10 @@
 
 static const String typeNames[] =
 {
-    "Master",
     "Effect",
-    "Music",
+    "Ambient",
     "Voice",
+    "Music",
     ""
 };
 

+ 2 - 1
Engine/Engine/AudioAPI.cpp

@@ -44,8 +44,9 @@ void RegisterSoundSources(asIScriptEngine* engine)
 {
     engine->RegisterEnum("SoundType");
     engine->RegisterEnumValue("SoundType", "SOUND_EFFECT", SOUND_EFFECT);
-    engine->RegisterEnumValue("SoundType", "SOUND_MUSIC", SOUND_MUSIC);
+    engine->RegisterEnumValue("SoundType", "SOUND_AMBIENT", SOUND_AMBIENT);
     engine->RegisterEnumValue("SoundType", "SOUND_VOICE", SOUND_VOICE);
+    engine->RegisterEnumValue("SoundType", "SOUND_MUSIC", SOUND_MUSIC);
     engine->RegisterEnumValue("SoundType", "SOUND_MASTER", SOUND_MASTER);
     
     RegisterSoundSource<SoundSource>(engine, "SoundSource");

+ 1 - 1
Engine/Graphics/AnimatedModel.cpp

@@ -83,7 +83,7 @@ void AnimatedModel::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_RESOURCEREFLIST, "Material", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()), AM_DEFAULT);
     ATTRIBUTE(AnimatedModel, VAR_BOOL, "Is Visible", visible_, true, AM_DEFAULT);
     ATTRIBUTE(AnimatedModel, VAR_BOOL, "Is Occluder", occluder_, false, AM_DEFAULT);
-    ATTRIBUTE(AnimatedModel, VAR_BOOL, "Can Be Occluded", occludee_, true, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_BOOL, "Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ATTRIBUTE(AnimatedModel, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_FLOAT, "Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_FLOAT, "Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);

+ 1 - 1
Engine/Graphics/BillboardSet.cpp

@@ -77,7 +77,7 @@ void BillboardSet::RegisterObject(Context* context)
     
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_RESOURCEREF, "Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
     ATTRIBUTE(BillboardSet, VAR_BOOL, "Is Visible", visible_, true, AM_DEFAULT);
-    ATTRIBUTE(BillboardSet, VAR_BOOL, "Can Be Occluded", occludee_, true, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ATTRIBUTE(BillboardSet, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Relative Position", IsRelative, SetRelative, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Relative Scale", IsScaled, SetScaled, bool, true, AM_DEFAULT);

+ 5 - 1
Engine/Graphics/Drawable.cpp

@@ -172,6 +172,10 @@ void Drawable::SetOccluder(bool enable)
 void Drawable::SetOccludee(bool enable)
 {
     occludee_ = enable;
+    // If occludee mode is disabled, reinsert to octree root to make sure octant occlusion does not erroneously hide
+    // this drawable
+    if (octant_ && !reinsertionQueued_)
+        octant_->GetRoot()->QueueReinsertion(this);
 }
 
 void Drawable::MarkForUpdate()
@@ -304,7 +308,7 @@ void Drawable::OnMarkedDirty(Node* node)
 
 void Drawable::AddToOctree()
 {
-    Scene* scene = node_->GetScene();
+    Scene* scene = GetScene();
     if (scene)
     {
         Octree* octree = scene->GetComponent<Octree>();

+ 1 - 1
Engine/Graphics/Light.cpp

@@ -117,7 +117,7 @@ void Light::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(Light, VAR_RESOURCEREF, "Attenuation Texture", GetRampTextureAttr, SetRampTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Light, VAR_RESOURCEREF, "Light Shape Texture", GetShapeTextureAttr, SetShapeTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_DEFAULT);
     ATTRIBUTE(Light, VAR_BOOL, "Is Visible", visible_, true, AM_DEFAULT);
-    ATTRIBUTE(Light, VAR_BOOL, "Can Be Occluded", occludee_, true, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Light, VAR_BOOL, "Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
     ATTRIBUTE(Light, VAR_BOOL, "Per Vertex", perVertex_, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Light, VAR_FLOAT, "Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);

+ 7 - 10
Engine/Graphics/Octree.cpp

@@ -148,8 +148,10 @@ void Octant::DeleteChild(Octant* octant)
 
 void Octant::InsertDrawable(Drawable* drawable, const Vector3& boxCenter, const Vector3& boxSize)
 {
-    // If size OK or outside, stop recursion & insert here
-    if (CheckDrawableSize(boxSize) || cullingBox_.IsInside(drawable->GetWorldBoundingBox()) != INSIDE)
+    // If size OK or outside, stop recursion & insert here. Also, if drawable is not occluded, must stay in the root octant
+    // so that hierarchic octant occlusion does not erroneously hide the drawable
+    if ((!drawable->IsOccludee() && this == root_) || CheckDrawableSize(boxSize) ||
+        cullingBox_.IsInside(drawable->GetWorldBoundingBox()) != INSIDE)
     {
         Octant* oldOctant = drawable->octant_;
         if (oldOctant != this)
@@ -319,7 +321,6 @@ OBJECTTYPESTATIC(Octree);
 Octree::Octree(Context* context) :
     Component(context),
     Octant(BoundingBox(-DEFAULT_OCTREE_SIZE, DEFAULT_OCTREE_SIZE), 0, 0, this),
-    scene_(0),
     numLevels_(DEFAULT_OCTREE_LEVELS)
 {
     // Resize threaded ray query intermediate result vector according to number of worker threads
@@ -503,7 +504,8 @@ void Octree::QueueUpdate(Drawable* drawable)
 
 void Octree::QueueReinsertion(Drawable* drawable)
 {
-    if (scene_ && scene_->IsThreadedUpdate())
+    Scene* scene = GetScene();
+    if (scene && scene->IsThreadedUpdate())
     {
         MutexLock lock(octreeMutex_);
         drawableReinsertions_.Push(WeakPtr<Drawable>(drawable));
@@ -523,11 +525,6 @@ void Octree::DrawDebugGeometry(bool depthTest)
         Octant::DrawDebugGeometry(debug, depthTest);
 }
 
-void Octree::OnNodeSet(Node* node)
-{
-    scene_ = node ? node->GetScene() : 0;
-}
-
 void Octree::UpdateDrawables(const FrameInfo& frame)
 {
     // Let drawables update themselves before reinsertion
@@ -536,7 +533,7 @@ void Octree::UpdateDrawables(const FrameInfo& frame)
     
     PROFILE(UpdateDrawables);
     
-    Scene* scene = node_->GetScene();
+    Scene* scene = GetScene();
     WorkQueue* queue = GetSubsystem<WorkQueue>();
     scene->BeginThreadedUpdate();
     

+ 0 - 6
Engine/Graphics/Octree.h

@@ -192,18 +192,12 @@ public:
     /// Add debug geometry to the debug renderer.
     void DrawDebugGeometry(bool depthTest);
     
-protected:
-    /// Handle node being assigned.
-    virtual void OnNodeSet(Node* node);
-    
 private:
     /// Update drawable objects marked for update. Updates are executed in worker threads.
     void UpdateDrawables(const FrameInfo& frame);
     /// Reinsert moved drawable objects into the octree.
     void ReinsertDrawables(const FrameInfo& frame);
     
-    /// Scene.
-    Scene* scene_;
     /// Drawable objects that require update.
     Vector<WeakPtr<Drawable> > drawableUpdates_;
     /// Drawable objects that require reinsertion.

+ 2 - 2
Engine/Graphics/ParticleEmitter.cpp

@@ -83,7 +83,7 @@ void ParticleEmitter::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_RESOURCEREF, "Parameter Source", GetParameterSourceAttr, SetParameterSourceAttr, ResourceRef, ResourceRef(XMLFile::GetTypeStatic()), AM_DEFAULT);
     ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Active", active_, true, AM_DEFAULT);
     ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Visible", visible_, true, AM_DEFAULT);
-    ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Can Be Occluded", occludee_, true, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
@@ -409,7 +409,7 @@ void ParticleEmitter::OnNodeSet(Node* node)
 {
     if (node)
     {
-        Scene* scene = node->GetScene();
+        Scene* scene = GetScene();
         if (scene)
             SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
     }

+ 1 - 1
Engine/Graphics/StaticModel.cpp

@@ -62,7 +62,7 @@ void StaticModel::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(StaticModel, VAR_RESOURCEREFLIST, "Material", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()), AM_DEFAULT);
     ATTRIBUTE(StaticModel, VAR_BOOL, "Is Visible", visible_, true, AM_DEFAULT);
     ATTRIBUTE(StaticModel, VAR_BOOL, "Is Occluder", occluder_, false, AM_DEFAULT);
-    ATTRIBUTE(StaticModel, VAR_BOOL, "Can Be Occluded", occludee_, true, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(StaticModel, VAR_BOOL, "Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ATTRIBUTE(StaticModel, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(StaticModel, VAR_FLOAT, "Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(StaticModel, VAR_FLOAT, "Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);

+ 96 - 75
Engine/Network/Connection.cpp

@@ -223,36 +223,18 @@ void Connection::SendServerUpdate()
     
     // Always check the root node (scene) first so that the scene-wide components get sent first,
     // and all other replicated nodes get added to the dirty set for sending the initial state
-    nodesToProcess_.Insert(scene_->GetID());
-    ProcessNode(scene_);
+    unsigned sceneID = scene_->GetID();
+    nodesToProcess_.Insert(sceneID);
+    ProcessNode(sceneID);
     
     // Then go through all dirtied nodes
     nodesToProcess_.Insert(sceneState_.dirtyNodes_);
-    nodesToProcess_.Erase(scene_->GetID()); // Do not process the root node twice
+    nodesToProcess_.Erase(sceneID); // Do not process the root node twice
     
     while (nodesToProcess_.Size())
     {
-        unsigned id = nodesToProcess_.Front();
-        Node* node = scene_->GetNode(id);
-        if (node)
-            ProcessNode(node);
-        else
-        {
-            nodesToProcess_.Erase(id);
-            
-            // If node is dirty, but is no longer found, it has been removed
-            if (sceneState_.nodeStates_.Contains(id))
-            {
-                msg_.Clear();
-                msg_.WriteNetID(id);
-                
-                // Note: we will send MSG_REMOVENODE redundantly for each node in the hierarchy, even if removing the root node
-                // would be enough. However, this may be better due to the client not possibly having updated parenting
-                // information at the time of receiving this message
-                SendMessage(MSG_REMOVENODE, true, true, msg_);
-                sceneState_.nodeStates_.Erase(id);
-            }
-        }
+        unsigned nodeID = nodesToProcess_.Front();
+        ProcessNode(nodeID);
     }
 }
 
@@ -1009,31 +991,57 @@ void Connection::HandleAsyncLoadFinished(StringHash eventType, VariantMap& event
     SendMessage(MSG_SCENELOADED, true, true, msg_);
 }
 
-void Connection::ProcessNode(Node* node)
+void Connection::ProcessNode(unsigned nodeID)
 {
-    if (!node || !nodesToProcess_.Contains(node->GetID()))
+    // Check that we have not already processed this due to dependency recursion
+    if (!nodesToProcess_.Erase(nodeID))
         return;
     
-    nodesToProcess_.Erase(node->GetID());
-    
-    // Process depended upon nodes first, if they are dirty
-    const PODVector<Node*>& dependencyNodes = node->GetDependencyNodes();
-    for (PODVector<Node*>::ConstIterator i = dependencyNodes.Begin(); i != dependencyNodes.End(); ++i)
+    // Find replication state for the node
+    HashMap<unsigned, NodeReplicationState>::Iterator i = sceneState_.nodeStates_.Find(nodeID);
+    if (i != sceneState_.nodeStates_.End())
     {
-        if (sceneState_.dirtyNodes_.Contains((*i)->GetID()))
-            ProcessNode(*i);
+        // Replication state found: the node is either be existing or removed
+        Node* node = i->second_.node_;
+        if (!node)
+        {
+            msg_.Clear();
+            msg_.WriteNetID(nodeID);
+            
+            // Note: we will send MSG_REMOVENODE redundantly for each node in the hierarchy, even if removing the root node
+            // would be enough. However, this may be better due to the client not possibly having updated parenting
+            // information at the time of receiving this message
+            SendMessage(MSG_REMOVENODE, true, true, msg_);
+            sceneState_.nodeStates_.Erase(nodeID);
+        }
+        else
+            ProcessExistingNode(node, i->second_);
     }
-    
-    // Check if the client's replication state already has this node
-    HashMap<unsigned, NodeReplicationState>::Iterator i = sceneState_.nodeStates_.Find(node->GetID());
-    if (i != sceneState_.nodeStates_.End())
-        ProcessExistingNode(node, i->second_);
     else
-        ProcessNewNode(node);
+    {
+        // Replication state not found: this is a new node
+        Node* node = scene_->GetNode(nodeID);
+        if (node)
+            ProcessNewNode(node);
+        else
+        {
+            // Did not find the new node (may have been created, then removed immediately): erase from dirty set.
+            sceneState_.dirtyNodes_.Erase(nodeID);
+        }
+    }
 }
 
 void Connection::ProcessNewNode(Node* node)
 {
+    // Process depended upon nodes first, if they are dirty
+    const PODVector<Node*>& dependencyNodes = node->GetDependencyNodes();
+    for (PODVector<Node*>::ConstIterator i = dependencyNodes.Begin(); i != dependencyNodes.End(); ++i)
+    {
+        unsigned nodeID = (*i)->GetID();
+        if (sceneState_.dirtyNodes_.Contains(nodeID))
+            ProcessNode(nodeID);
+    }
+    
     msg_.Clear();
     msg_.WriteNetID(node->GetID());
     
@@ -1084,7 +1092,17 @@ void Connection::ProcessNewNode(Node* node)
 
 void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState)
 {
+    // Process depended upon nodes first, if they are dirty
+    const PODVector<Node*>& dependencyNodes = node->GetDependencyNodes();
+    for (PODVector<Node*>::ConstIterator i = dependencyNodes.Begin(); i != dependencyNodes.End(); ++i)
+    {
+        unsigned nodeID = (*i)->GetID();
+        if (sceneState_.dirtyNodes_.Contains(nodeID))
+            ProcessNode(nodeID);
+    }
+    
     // Check from the interest management component, if exists, whether should update
+    /// \todo Searching for the component is a potential CPU hotspot. It should be cached
     NetworkPriority* priority = node->GetComponent<NetworkPriority>();
     if (priority && (!priority->GetAlwaysUpdateOwner() || node->GetOwner() != this))
     {
@@ -1153,39 +1171,25 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
         }
     }
     
-    // Check for new or changed components
-    const Vector<SharedPtr<Component> >& components = node->GetComponents();
-    for (unsigned i = 0; i < components.Size(); ++i)
+    // Check for removed or changed components
+    for (HashMap<unsigned, ComponentReplicationState>::Iterator i = nodeState.componentStates_.Begin();
+        i != nodeState.componentStates_.End(); )
     {
-        Component* component = components[i];
-        // Check if component is not to be replicated
-        if (component->GetID() >= FIRST_LOCAL_ID)
-            continue;
-        
-        HashMap<unsigned, ComponentReplicationState>::Iterator j = nodeState.componentStates_.Find(component->GetID());
-        if (j == nodeState.componentStates_.End())
+        HashMap<unsigned, ComponentReplicationState>::Iterator current = i++;
+        ComponentReplicationState& componentState = current->second_;
+        Component* component = componentState.component_;
+        if (!component)
         {
-            // New component
-            ComponentReplicationState& componentState = nodeState.componentStates_[component->GetID()];
-            componentState.connection_ = this;
-            componentState.nodeState_ = &nodeState;
-            componentState.component_ = component;
-            component->AddReplicationState(&componentState);
-            
+            // Removed component
             msg_.Clear();
-            msg_.WriteNetID(node->GetID());
-            msg_.WriteShortStringHash(component->GetType());
-            msg_.WriteNetID(component->GetID());
-            component->WriteInitialDeltaUpdate(msg_);
+            msg_.WriteNetID(current->first_);
             
-            SendMessage(MSG_CREATECOMPONENT, true, true, msg_);
+            SendMessage(MSG_REMOVECOMPONENT, true, true, msg_);
+            nodeState.componentStates_.Erase(current);
         }
         else
         {
-            // Existing component
-            ComponentReplicationState& componentState = j->second_;
-            
-            // Check if attributes have changed
+            // Existing component. Check if attributes have changed
             if (componentState.dirtyAttributes_.Count())
             {
                 const Vector<AttributeInfo>* attributes = component->GetNetworkAttributes();
@@ -1211,7 +1215,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
                     SendMessage(MSG_COMPONENTLATESTDATA, true, false, msg_, component->GetID());
                 }
                 
-                // Send deltaupdate if remaining dirty bits, or vars have changed
+                // Send deltaupdate if remaining dirty bits
                 if (componentState.dirtyAttributes_.Count())
                 {
                     msg_.Clear();
@@ -1226,18 +1230,35 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
         }
     }
     
-    // Check for removed components
-    for (HashMap<unsigned, ComponentReplicationState>::Iterator i = nodeState.componentStates_.Begin();
-        i != nodeState.componentStates_.End();)
+    // Check for new components
+    if (nodeState.componentStates_.Size() != node->GetNumNetworkComponents())
     {
-        HashMap<unsigned, ComponentReplicationState>::Iterator current = i++;
-        if (!current->second_.component_)
+        const Vector<SharedPtr<Component> >& components = node->GetComponents();
+        for (unsigned i = 0; i < components.Size(); ++i)
         {
-            msg_.Clear();
-            msg_.WriteNetID(current->first_);
+            Component* component = components[i];
+            // Check if component is not to be replicated
+            if (component->GetID() >= FIRST_LOCAL_ID)
+                continue;
             
-            SendMessage(MSG_REMOVECOMPONENT, true, true, msg_);
-            nodeState.componentStates_.Erase(current);
+            HashMap<unsigned, ComponentReplicationState>::Iterator j = nodeState.componentStates_.Find(component->GetID());
+            if (j == nodeState.componentStates_.End())
+            {
+                // New component
+                ComponentReplicationState& componentState = nodeState.componentStates_[component->GetID()];
+                componentState.connection_ = this;
+                componentState.nodeState_ = &nodeState;
+                componentState.component_ = component;
+                component->AddReplicationState(&componentState);
+                
+                msg_.Clear();
+                msg_.WriteNetID(node->GetID());
+                msg_.WriteShortStringHash(component->GetType());
+                msg_.WriteNetID(component->GetID());
+                component->WriteInitialDeltaUpdate(msg_);
+                
+                SendMessage(MSG_CREATECOMPONENT, true, true, msg_);
+            }
         }
     }
     

+ 1 - 1
Engine/Network/Connection.h

@@ -194,7 +194,7 @@ private:
     /// Handle scene loaded event.
     void HandleAsyncLoadFinished(StringHash eventType, VariantMap& eventData);
     /// Process a node for sending a network update. Recurses to process depended on node(s) first.
-    void ProcessNode(Node* node);
+    void ProcessNode(unsigned nodeID);
     /// Process a node that the client has not yet received.
     void ProcessNewNode(Node* node);
     /// Process a node that the client has already received.

+ 1 - 1
Engine/Physics/CollisionShape.cpp

@@ -494,7 +494,7 @@ void CollisionShape::OnNodeSet(Node* node)
 {
     if (node)
     {
-        Scene* scene = node->GetScene();
+        Scene* scene = GetScene();
         if (scene)
         {
             physicsWorld_ = scene->GetComponent<PhysicsWorld>();

+ 2 - 2
Engine/Physics/Joint.cpp

@@ -85,7 +85,7 @@ void Joint::ApplyAttributes()
     {
         otherBody_.Reset();
         
-        Scene* scene = node_ ? node_->GetScene() : 0;
+        Scene* scene = GetScene();
         if (scene && otherBodyNodeID_)
         {
             Node* otherNode = scene->GetNode(otherBodyNodeID_);
@@ -153,7 +153,7 @@ void Joint::OnNodeSet(Node* node)
 {
     if (node)
     {
-        Scene* scene = node->GetScene();
+        Scene* scene = GetScene();
         if (scene)
         {
             physicsWorld_ = scene->GetComponent<PhysicsWorld>();

+ 1 - 1
Engine/Physics/PhysicsWorld.cpp

@@ -323,7 +323,7 @@ void PhysicsWorld::OnNodeSet(Node* node)
     // Subscribe to the scene subsystem update, which will trigger the physics simulation step
     if (node)
     {
-        scene_ = node->GetScene();
+        scene_ = GetScene();
         SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(PhysicsWorld, HandleSceneSubsystemUpdate));
     }
 }

+ 3 - 3
Engine/Physics/RigidBody.cpp

@@ -149,7 +149,7 @@ void RigidBody::setWorldTransform(const btTransform &worldTrans)
     // If the rigid body is parented to another rigid body, can not set the transform immediately.
     // In that case store it to PhysicsWorld for delayed assignment
     Node* parent = node_->GetParent();
-    if (parent && parent != node_->GetScene())
+    if (parent && parent != GetScene())
         parentRigidBody = parent->GetComponent<RigidBody>();
     
     if (!parentRigidBody)
@@ -643,7 +643,7 @@ void RigidBody::OnMarkedDirty(Node* node)
     if (!inSetTransform_ && !hasSmoothedTransform_)
     {
         // Physics operations are not safe from worker threads
-        Scene* scene = node->GetScene();
+        Scene* scene = GetScene();
         if (scene && scene->IsThreadedUpdate())
         {
             scene->DelayedMarkedDirty(this);
@@ -671,7 +671,7 @@ void RigidBody::OnNodeSet(Node* node)
 {
     if (node)
     {
-        Scene* scene = node->GetScene();
+        Scene* scene = GetScene();
         if (scene)
         {
             physicsWorld_ = scene->GetComponent<PhysicsWorld>();

+ 5 - 0
Engine/Scene/Component.cpp

@@ -73,6 +73,11 @@ void Component::Remove()
         node_->RemoveComponent(this);
 }
 
+Scene* Component::GetScene() const
+{
+    return node_ ? node_->GetScene() : 0;
+}
+
 const Matrix3x4& Component::GetWorldTransform() const
 {
     return node_ ? node_->GetWorldTransform() : Matrix3x4::IDENTITY;

+ 4 - 3
Engine/Scene/Component.h

@@ -27,6 +27,7 @@
 #include "Serializable.h"
 
 class Node;
+class Scene;
 
 struct ComponentReplicationState;
 
@@ -56,9 +57,10 @@ public:
     
     /// Return ID.
     unsigned GetID() const { return id_; }
-    /// Get scene node.
+    /// Return scene node.
     Node* GetNode() const { return node_; }
-    
+    /// Return the scene the node belongs to.
+    Scene* GetScene() const;
     /// Return parent node's transform matrix in world space.
     const Matrix3x4& GetWorldTransform() const;
     /// Return parent node's position in world space.
@@ -67,7 +69,6 @@ public:
     Quaternion GetWorldRotation() const { return GetWorldTransform().Rotation(); }
     /// Return parent node's scale in world space.
     Vector3 GetWorldScale() const { return GetWorldTransform().Scale(); }
-    
     /// Return components in the same scene node by type.
     void GetComponents(PODVector<Component*>& dest, ShortStringHash type) const;
     /// Return component in the same scene node by type. If there are several, returns the first.

+ 1 - 1
Engine/Scene/SmoothedTransform.cpp

@@ -149,7 +149,7 @@ void SmoothedTransform::OnNodeSet(Node* node)
         targetRotation_ = node->GetRotation();
         
         // Subscribe to smoothing update
-        Scene* scene = node_->GetScene();
+        Scene* scene = GetScene();
         if (scene)
             SubscribeToEvent(scene, E_UPDATESMOOTHING, HANDLER(SmoothedTransform, HandleUpdateSmoothing));
     }

+ 14 - 22
Engine/Script/ScriptInstance.cpp

@@ -175,11 +175,7 @@ void ScriptInstance::DelayedExecute(float delay, const String& declaration, cons
     
     // Make sure we are registered to the scene update event, because delayed calls are executed there
     if (!methods_[METHOD_UPDATE] && !HasSubscribedToEvent(E_SCENEUPDATE))
-    {
-        Node* node = GetNode();
-        if (node)
-            SubscribeToEvent(node->GetScene(), E_SCENEUPDATE, HANDLER(ScriptInstance, HandleSceneUpdate));
-    }
+        SubscribeToEvent(GetScene(), E_SCENEUPDATE, HANDLER(ScriptInstance, HandleSceneUpdate));
 }
 
 void ScriptInstance::ClearDelayedExecute()
@@ -357,25 +353,21 @@ void ScriptInstance::GetSupportedMethods()
         methods_[i] = scriptFile_->GetMethod(scriptObject_, methodDeclarations[i]);
     
     // Subscribe to the update events as supported
-    Node* node = GetNode();
-    if (node)
+    Scene* scene = GetScene();
+    if (scene)
     {
-        Scene* scene = node->GetScene();
-        if (scene)
+        if (methods_[METHOD_UPDATE])
+            SubscribeToEvent(scene, E_SCENEUPDATE, HANDLER(ScriptInstance, HandleSceneUpdate));
+        if (methods_[METHOD_POSTUPDATE])
+            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ScriptInstance, HandleScenePostUpdate));
+        
+        PhysicsWorld* world = scene->GetComponent<PhysicsWorld>();
+        if (world)
         {
-            if (methods_[METHOD_UPDATE])
-                SubscribeToEvent(scene, E_SCENEUPDATE, HANDLER(ScriptInstance, HandleSceneUpdate));
-            if (methods_[METHOD_POSTUPDATE])
-                SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ScriptInstance, HandleScenePostUpdate));
-            
-            PhysicsWorld* world = scene->GetComponent<PhysicsWorld>();
-            if (world)
-            {
-                if (methods_[METHOD_FIXEDUPDATE])
-                    SubscribeToEvent(world, E_PHYSICSPRESTEP, HANDLER(ScriptInstance, HandlePhysicsPreStep));
-                if (methods_[METHOD_FIXEDPOSTUPDATE])
-                    SubscribeToEvent(world, E_PHYSICSPOSTSTEP, HANDLER(ScriptInstance, HandlePhysicsPostStep));
-            }
+            if (methods_[METHOD_FIXEDUPDATE])
+                SubscribeToEvent(world, E_PHYSICSPRESTEP, HANDLER(ScriptInstance, HandlePhysicsPreStep));
+            if (methods_[METHOD_FIXEDPOSTUPDATE])
+                SubscribeToEvent(world, E_PHYSICSPOSTSTEP, HANDLER(ScriptInstance, HandlePhysicsPostStep));
         }
     }
 }

+ 1 - 1
Readme.txt

@@ -36,7 +36,7 @@ Urho3D is greatly inspired by OGRE (http://www.ogre3d.org) and Horde3D
   http://timothylottes.blogspot.com/2011/04/nvidia-fxaa-ii-for-console.html
 
 Urho3D uses the following third-party libraries:
-- AngelScript 2.23.0 (http://www.angelcode.com/angelscript/)
+- AngelScript 2.23.1 WIP (http://www.angelcode.com/angelscript/)
 - Bullet 2.80 (http://www.bulletphysics.org/)
 - FreeType 2.3.12 (http://www.freetype.org/)
 - GLee 5.4 (http://elf-stone.com/)

+ 28 - 25
ThirdParty/AngelScript/include/angelscript.h

@@ -59,8 +59,8 @@ BEGIN_AS_NAMESPACE
 
 // AngelScript version
 
-#define ANGELSCRIPT_VERSION        22300
-#define ANGELSCRIPT_VERSION_STRING "2.23.0"
+#define ANGELSCRIPT_VERSION        22301
+#define ANGELSCRIPT_VERSION_STRING "2.23.1 WIP"
 
 // Data types
 
@@ -1147,8 +1147,8 @@ public:
 // Byte code instructions
 enum asEBCInstr
 {
-	asBC_POP			= 0,
-	asBC_PUSH			= 1,
+	asBC_PopPtr			= 0,
+	asBC_PshGPtr		= 1,
 	asBC_PshC4			= 2,
 	asBC_PshV4			= 3,
 	asBC_PSF			= 4,
@@ -1323,22 +1323,25 @@ enum asEBCInstr
 	asBC_ChkNullS		= 173,
 	asBC_ClrHi			= 174,
 	asBC_JitEntry		= 175,
-	asBC_CallPtr        = 176,
-	asBC_FuncPtr        = 177,
-	asBC_LoadThisR      = 178,
-	asBC_PshV8          = 179,
+	asBC_CallPtr		= 176,
+	asBC_FuncPtr		= 177,
+	asBC_LoadThisR		= 178,
+	asBC_PshV8			= 179,
 	asBC_DIVu			= 180,
 	asBC_MODu			= 181,
 	asBC_DIVu64			= 182,
 	asBC_MODu64			= 183,
-	asBC_LoadRObjR      = 184,
-	asBC_LoadVObjR      = 185,
+	asBC_LoadRObjR		= 184,
+	asBC_LoadVObjR		= 185,
+	asBC_RefCpyV		= 186,
+	asBC_JLowZ			= 187,
+	asBC_JLowNZ			= 188,
 
-	asBC_MAXBYTECODE	= 186,
+	asBC_MAXBYTECODE	= 189,
 
 	// Temporary tokens. Can't be output to the final program
-	asBC_VarDecl        = 251,
-	asBC_Block          = 252,
+	asBC_VarDecl		= 251,
+	asBC_Block			= 252,
 	asBC_ObjInfo		= 253,
 	asBC_LINE			= 254,
 	asBC_LABEL			= 255
@@ -1426,8 +1429,8 @@ struct asSBCInfo
 
 const asSBCInfo asBCInfo[256] =
 {
-	asBCINFO(POP,		W_ARG,			0xFFFF),
-	asBCINFO(PUSH,		W_ARG,			0xFFFF),
+	asBCINFO(PopPtr,	NO_ARG,			-AS_PTR_SIZE),
+	asBCINFO(PshGPtr,	PTR_ARG,		AS_PTR_SIZE),
 	asBCINFO(PshC4,		DW_ARG,			1),
 	asBCINFO(PshV4,		rW_ARG,			1),
 	asBCINFO(PSF,		rW_ARG,			AS_PTR_SIZE),
@@ -1499,8 +1502,8 @@ const asSBCInfo asBCInfo[256] =
 	asBCINFO(CHKREF,	NO_ARG,			0),
 	asBCINFO(GETOBJREF,	W_ARG,			0),
 	asBCINFO(GETREF,	W_ARG,			0),
-	asBCINFO(PshNull,   NO_ARG,			AS_PTR_SIZE),
-	asBCINFO(ClrVPtr,   rW_ARG,			0),
+	asBCINFO(PshNull,	NO_ARG,			AS_PTR_SIZE),
+	asBCINFO(ClrVPtr,	rW_ARG,			0),
 	asBCINFO(OBJTYPE,	PTR_ARG,		AS_PTR_SIZE),
 	asBCINFO(TYPEID,	DW_ARG,			1),
 	asBCINFO(SetV4,		wW_DW_ARG,		0),
@@ -1602,20 +1605,20 @@ const asSBCInfo asBCInfo[256] =
 	asBCINFO(ChkNullS,	W_ARG,			0),
 	asBCINFO(ClrHi,		NO_ARG,			0),
 	asBCINFO(JitEntry,	PTR_ARG,		0),
-	asBCINFO(CallPtr,   rW_ARG,         0xFFFF),
-	asBCINFO(FuncPtr,   PTR_ARG,        AS_PTR_SIZE),
-	asBCINFO(LoadThisR, W_DW_ARG,       0),
+	asBCINFO(CallPtr,	rW_ARG,			0xFFFF),
+	asBCINFO(FuncPtr,	PTR_ARG,		AS_PTR_SIZE),
+	asBCINFO(LoadThisR,	W_DW_ARG,		0),
 	asBCINFO(PshV8,		rW_ARG,			2),
 	asBCINFO(DIVu,		wW_rW_rW_ARG,	0),
 	asBCINFO(MODu,		wW_rW_rW_ARG,	0),
 	asBCINFO(DIVu64,	wW_rW_rW_ARG,	0),
 	asBCINFO(MODu64,	wW_rW_rW_ARG,	0),
-	asBCINFO(LoadRObjR, rW_W_DW_ARG,    0),
-	asBCINFO(LoadVObjR, rW_W_DW_ARG,    0),
+	asBCINFO(LoadRObjR,	rW_W_DW_ARG,	0),
+	asBCINFO(LoadVObjR,	rW_W_DW_ARG,	0),
+	asBCINFO(RefCpyV,	wW_PTR_ARG,		0),
+	asBCINFO(JLowZ,		DW_ARG,			0),
+	asBCINFO(JLowNZ,	DW_ARG,			0),
 
-	asBCINFO_DUMMY(186),
-	asBCINFO_DUMMY(187),
-	asBCINFO_DUMMY(188),
 	asBCINFO_DUMMY(189),
 	asBCINFO_DUMMY(190),
 	asBCINFO_DUMMY(191),

+ 64 - 0
ThirdParty/AngelScript/source/as_array.h

@@ -51,12 +51,14 @@ public:
 	~asCArray();
 
 	void   Allocate(size_t numElements, bool keepData);
+	void   AllocateNoConstruct(size_t numElements, bool keepData);
 	size_t GetCapacity() const;
 
 	void PushLast(const T &element);
 	T    PopLast();
 
 	void   SetLength(size_t numElements);
+	void   SetLengthNoConstruct(size_t numElements);
 	size_t GetLength() const;
 
 	void Copy(const T*, size_t count);
@@ -248,6 +250,59 @@ void asCArray<T>::Allocate(size_t numElements, bool keepData)
 	maxLength = numElements;
 }
 
+template <class T>
+void asCArray<T>::AllocateNoConstruct(size_t numElements, bool keepData)
+{
+	// We have 4 situations
+	// 1. The previous array is 8 bytes or smaller and the new array is also 8 bytes or smaller
+	// 2. The previous array is 8 bytes or smaller and the new array is larger than 8 bytes
+	// 3. The previous array is larger than 8 bytes and the new array is 8 bytes or smaller
+	// 4. The previous array is larger than 8 bytes and the new array is also larger than 8 bytes
+
+	T *tmp = 0;
+	if( numElements )
+	{
+		if( sizeof(T)*numElements <= 8 )
+			// Use the internal buffer
+			tmp = (T*)buf;
+		else
+			// Allocate the array and construct each of the elements
+			tmp = asNEWARRAY(T,numElements);
+	}
+
+	if( array )
+	{
+		if( array == tmp )
+		{
+			if( keepData )
+			{
+				if( length > numElements )
+					length = numElements;
+			}
+			else
+				length = 0;
+		}
+		else
+		{
+			if( keepData )
+			{
+				if( length > numElements )
+					length = numElements;
+
+				memcpy(tmp, array, sizeof(T)*length);
+			}
+			else
+				length = 0;
+
+			if( array != (T*)buf )
+				asDELETEARRAY(array);
+		}
+	}
+
+	array = tmp;
+	maxLength = numElements;
+}
+
 template <class T>
 size_t asCArray<T>::GetCapacity() const
 {
@@ -263,6 +318,15 @@ void asCArray<T>::SetLength(size_t numElements)
 	length = numElements;
 }
 
+template <class T>
+void asCArray<T>::SetLengthNoConstruct(size_t numElements)
+{
+	if( numElements > maxLength )
+		AllocateNoConstruct(numElements, true);
+
+	length = numElements;
+}
+
 template <class T>
 void asCArray<T>::Copy(const T *data, size_t count)
 {

+ 8 - 8
ThirdParty/AngelScript/source/as_builder.cpp

@@ -1409,7 +1409,7 @@ int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file, const as
 	decl->objType = st;
 
 	// Add script classes to the GC
-	// TODO: optimize: Only add the class to the GC when the module won't use the type anymore
+	// TODO: runtime optimize: Only add the class to the GC when the module won't use the type anymore
 	engine->gc.AddScriptObjectToGC(st, &engine->objectTypeBehaviours);
 
 	// Use the default script class behaviours
@@ -2298,19 +2298,19 @@ void asCBuilder::CompileClasses()
 			{
 				if( dt.IsObjectHandle() )
 				{
-					// TODO: optimize: If it is known that the handle can't be involved in a circular reference
-					//                 then this object doesn't need to be marked as garbage collected. 
-					//                 - The application could set a flag when registering the object.
-					//                 - The script classes can be marked as final, then the compiler will 
-					//                   be able to determine whether the class is garbage collected or not.
+					// TODO: runtime optimize: If it is known that the handle can't be involved in a circular reference
+					//                         then this object doesn't need to be marked as garbage collected. 
+					//                         - The application could set a flag when registering the object.
+					//                         - The script classes can be marked as final, then the compiler will 
+					//                           be able to determine whether the class is garbage collected or not.
 
 					ot->flags |= asOBJ_GC;
 					break;
 				}
 				else if( dt.GetObjectType()->flags & asOBJ_GC )
 				{
-					// TODO: optimize: Just because the member type is a potential circle doesn't mean that this one is
-					//                 Only if the object is of a type that can reference this type, either directly or indirectly
+					// TODO: runtime optimize: Just because the member type is a potential circle doesn't mean that this one is
+					//                         Only if the object is of a type that can reference this type, either directly or indirectly
 
 					ot->flags |= asOBJ_GC;
 					break;

+ 126 - 121
ThirdParty/AngelScript/source/as_bytecode.cpp

@@ -390,7 +390,7 @@ bool asCByteCode::PostponeInitOfTemp(cByteInstruction *curr, cByteInstruction **
 
 bool asCByteCode::RemoveUnusedValue(cByteInstruction *curr, cByteInstruction **next)
 {
-	// TODO: optimize: Should work for 64bit types as well
+	// TODO: runtime optimize: Should work for 64bit types as well
 
 	// The value isn't used for anything
 	if( (asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG ||
@@ -414,7 +414,7 @@ bool asCByteCode::RemoveUnusedValue(cByteInstruction *curr, cByteInstruction **n
 		return true;
 	}
 
-	// TODO: optimize: There should be one for doubles as well
+	// TODO: runtime optimize: There should be one for doubles as well
 	// The value is immediately used and then never again
 	if( curr->op == asBC_SetV4 &&
 		curr->next && 
@@ -590,28 +590,17 @@ bool asCByteCode::IsTemporary(short offset)
 
 int asCByteCode::Optimize()
 {
-	// TODO: optimize: The optimizer should be able to inline function calls.
-	//                 If the called function has only a few instructions, the function call should be inlined.
-	//                 This is especially useful with the factory stubs used for template types and script classes.
+	// TODO: runtime optimize: The optimizer should be able to inline function calls.
+	//                         If the called function has only a few instructions, the function call should be inlined.
+	//                         This is especially useful with the factory stubs used for template types and script classes.
 
-	// TODO: optimize: Need a bytecode BC_AddRef so that BC_CALLSYS doesn't have to be used for this trivial call
+	// TODO: runtime optimize: Need a bytecode BC_AddRef so that BC_CALLSYS doesn't have to be used for this trivial call
 	
-	// TODO: optimize: A bytecode BC_RefCpyV that copies a handle from a local variable to another local variable
-	//                 can easily substitute the frequently appearing pattern BC_PshV4, BC_PSF, BC_REFCPY, BC_POP
+	// TODO: runtime optimize: A single bytecode for incrementing a variable, comparing, and jumping can probably improve 
+	//                         loops a lot. How often do these loops really occur?
 
-	// TODO: optimize: A single bytecode for incrementing a variable, comparing, and jumping can probably improve 
-	//                 loops a lot. How often do these loops really occur?
-
-	// TODO: optimize: Script class methods are currently implemented to increase the ref count of the object upon
-	//                 entry, and then release it upon exit. When the method isn't doing anything at all, this is
-	//                 not necessary, as the function could simply do a RET immediately. This optimization is only
-	//                 possible if the code has been built without the line cues, as if the SUSPEND is within the 
-	//                 function, then we can't do this optimization. Of course, this optimization may not be all
-	//                 that useful, since in a real world app, it is probably not very common that empty class 
-	//                 methods are called.
-
-	// TODO: optimize: VAR + GET... should be optimized if the only instructions between them are trivial, i.e. no 
-	//                 function calls that can suspend the execution.
+	// TODO: runtime optimize: VAR + GET... should be optimized if the only instructions between them are trivial, i.e. no 
+	//                         function calls that can suspend the execution.
 
 	cByteInstruction *instr = first;
 	while( instr )
@@ -638,17 +627,63 @@ int asCByteCode::Optimize()
 			instr = GoBack(instr);
 		}
 		// T??, ClrHi -> T??
-		else if( IsCombination(curr, asBC_TZ, asBC_ClrHi) ||
+		else if( IsCombination(curr, asBC_TZ , asBC_ClrHi) ||
 				 IsCombination(curr, asBC_TNZ, asBC_ClrHi) ||
-				 IsCombination(curr, asBC_TS, asBC_ClrHi) ||
+				 IsCombination(curr, asBC_TS , asBC_ClrHi) ||
 				 IsCombination(curr, asBC_TNS, asBC_ClrHi) ||
-				 IsCombination(curr, asBC_TP, asBC_ClrHi) ||
+				 IsCombination(curr, asBC_TP , asBC_ClrHi) ||
 				 IsCombination(curr, asBC_TNP, asBC_ClrHi) )
 		{
 			// Remove the ClrHi instruction, since the test instructions always clear the top bytes anyway
 			DeleteInstruction(instr);
 			instr = GoBack(curr);
 		}
+		// ClrHi, JZ -> JLowZ
+		else if( IsCombination(curr, asBC_ClrHi, asBC_JZ) )
+		{
+			DeleteInstruction(curr);
+			instr->op = asBC_JLowZ;
+			instr = GoBack(instr);
+		}
+		// ClrHi, JNZ -> JLowNZ
+		else if( IsCombination(curr, asBC_ClrHi, asBC_JNZ) )
+		{
+			DeleteInstruction(curr);
+			instr->op = asBC_JLowNZ;
+			instr = GoBack(instr);
+		}
+		// PGA, RDSPtr -> PshGPtr
+		else if( IsCombination(curr, asBC_PGA, asBC_RDSPtr) )
+		{
+			curr->op = asBC_PshGPtr;
+			DeleteInstruction(instr);
+			instr = GoBack(curr);
+		}
+		// ChkRefS, RDSPtr -> RDSPtr, CHKREF
+		else if( IsCombination(curr, asBC_ChkRefS, asBC_RDSPtr) )
+		{
+			// This exchange removes one pointer dereference, and also 
+			// makes it easier to completely remove the CHKREF instruction
+			curr->op = asBC_RDSPtr;
+			instr->op = asBC_CHKREF;
+			instr = GoBack(curr);
+		}
+		// CHKREF, ADDSi -> ADDSi
+		// CHKREF, RDSPtr -> RDSPtr
+		else if( IsCombination(curr, asBC_CHKREF, asBC_ADDSi) ||
+			     IsCombination(curr, asBC_CHKREF, asBC_RDSPtr) )
+		{
+			// As ADDSi & RDSPtr already checks the pointer, the CHKREF instruction is unnecessary
+			DeleteInstruction(curr);
+			instr = GoBack(instr);
+		}
+		// ADDSi, CHKREF -> ADDSi
+		else if( IsCombination(curr, asBC_ADDSi, asBC_CHKREF) )
+		{
+			// As ADDSi is guaranteed to work on valid pointers, then CHKREF is not necessary
+			DeleteInstruction(instr);
+			instr = GoBack(curr);
+		}
 		// PshVPtr 0, ADDSi, PopRPtr -> LoadThisR
 		else if( IsCombination(curr, asBC_PshVPtr, asBC_ADDSi) &&
 		         IsCombination(instr, asBC_ADDSi, asBC_PopRPtr) &&
@@ -657,7 +692,7 @@ int asCByteCode::Optimize()
 			DeleteInstruction(curr);
 			instr = GoBack(ChangeFirstDeleteNext(instr, asBC_LoadThisR));
 		}
-		// TODO: Optimize: PshVPtr x, PopRPtr -> LoadRObjR x, 0
+		// TODO: runtime optimize: PshVPtr x, PopRPtr -> LoadRObjR x, 0
 		// PshVPtr x, ADDSi, PopRPtr -> LoadRObjR
 		else if( IsCombination(curr, asBC_PshVPtr, asBC_ADDSi) &&
 		         IsCombination(instr, asBC_ADDSi, asBC_PopRPtr) &&
@@ -672,6 +707,24 @@ int asCByteCode::Optimize()
 			DeleteInstruction(instr);
 			instr = GoBack(curr);
 		}
+		// PSF x, REFCPY -> RefCpyV x
+		else if( IsCombination(curr, asBC_PSF, asBC_REFCPY) )
+		{
+			instr->op = asBC_RefCpyV;
+			instr->wArg[0] = curr->wArg[0];
+			instr->stackInc = asBCInfo[asBC_LoadVObjR].stackInc;
+			DeleteInstruction(curr);
+			instr = GoBack(instr);
+		}
+		// PshNull, RefCpyV, PopPtr -> FREE
+		else if( IsCombination(curr, asBC_PshNull, asBC_RefCpyV) &&
+			     IsCombination(instr, asBC_RefCpyV, asBC_PopPtr) )
+		{
+			DeleteInstruction(curr);
+			instr->op = asBC_FREE;
+			DeleteInstruction(instr->next);
+			instr = GoBack(instr);
+		}
 		// PSF x, ADDSi, PopRPtr -> LoadVObjR
 		else if( IsCombination(curr, asBC_PSF, asBC_ADDSi) &&
 		         IsCombination(instr, asBC_ADDSi, asBC_PopRPtr) )
@@ -724,13 +777,13 @@ int asCByteCode::Optimize()
 			DeleteInstruction(instr);
 			instr = GoBack(curr);
 		}
-		// POP a, RET b -> RET b
-		else if( IsCombination(curr, asBC_POP, asBC_RET) )
+		// PopPtr, RET b -> RET b
+		else if( IsCombination(curr, asBC_PopPtr, asBC_RET) )
 		{
-			// We don't combine the POP+RET because RET first restores
+			// We don't combine the PopPtr+RET because RET first restores
 			// the previous stack pointer and then pops the arguments
 
-			// Delete POP
+			// Delete PopPtr
 			instr = GoBack(DeleteInstruction(curr));
 		}
 		// Delete JitEntry if the JIT instructions are not supposed to be included
@@ -762,16 +815,6 @@ int asCByteCode::Optimize()
 			// Delete the first instruction
 			instr = GoBack(DeleteInstruction(curr));
 		}
-		// PUSH a, PUSH b -> PUSH a+b
-		else if( IsCombination(curr, asBC_PUSH, asBC_PUSH) )
-		{
-			// Combine the two PUSH
-			instr->wArg[0] = curr->wArg[0] + instr->wArg[0];
-			// Delete current
-			DeleteInstruction(curr);
-			// Continue with the instruction before the one removed
-			instr = GoBack(instr);
-		}
 		// VAR a, GETREF 0 -> PSF a
 		else if( IsCombination(curr, asBC_VAR, asBC_GETREF) && instr->wArg[0] == 0 )
 		{
@@ -805,30 +848,24 @@ int asCByteCode::Optimize()
 			InsertBefore(curr, instr);
 			instr = GoBack(instr);
 		}
-		// PshVPtr y, POP x -> POP x-AS_PTR_SIZE
-		// PSF y    , POP x -> POP x-AS_PTR_SIZE
-		// VAR y    , POP x -> POP x-AS_PTR_SIZE
-		// PshNull  , POP x -> POP x-AS_PTR_SIZE
-		// PshRPtr  , POP x -> POP x-AS_PTR_SIZE
-		else if( (IsCombination(curr, asBC_PshRPtr, asBC_POP) ||
-			      IsCombination(curr, asBC_PSF    , asBC_POP) ||
-				  IsCombination(curr, asBC_VAR    , asBC_POP) || 
-				  IsCombination(curr, asBC_PshVPtr, asBC_POP) ||
-			      IsCombination(curr, asBC_PshNull, asBC_POP)) && 
-				 instr->wArg[0] >= AS_PTR_SIZE )
+		// PshVPtr y, PopPtr -> nothing
+		// PSF y    , PopPtr -> nothing
+		// VAR y    , PopPtr -> nothing
+		// PshNull  , PopPtr -> nothing
+		// PshRPtr  , PopPtr -> nothing
+		else if( IsCombination(curr, asBC_PshRPtr, asBC_PopPtr) ||
+			     IsCombination(curr, asBC_PSF    , asBC_PopPtr) ||
+				 IsCombination(curr, asBC_VAR    , asBC_PopPtr) || 
+				 IsCombination(curr, asBC_PshVPtr, asBC_PopPtr) ||
+			     IsCombination(curr, asBC_PshNull, asBC_PopPtr) )
 		{
-			// The pop instruction will always only pop a single pointer
-			asASSERT( instr->wArg[0] == AS_PTR_SIZE );
-
 			// A pointer is pushed on the stack then immediately removed
+			// Remove both instructions as they cancel each other
+			cByteInstruction *instr2 = instr->next;
 			DeleteInstruction(curr);
-			instr->wArg[0] -= AS_PTR_SIZE;
-			instr = GoBack(instr);
+			DeleteInstruction(instr);
+			instr = GoBack(instr2);
 		}
-		// POP 0 -> remove
-		// PUSH 0 -> remove
-		else if( (curr->op == asBC_POP || curr->op == asBC_PUSH ) && curr->wArg[0] == 0 )  
-			instr = GoBack(DeleteInstruction(curr));
 // Begin PATTERN
 		// T**; J** +x -> J** +x
 		else if( IsCombination(curr, asBC_TZ , asBC_JZ ) || 
@@ -869,23 +906,21 @@ int asCByteCode::Optimize()
 			DeleteInstruction(instr->next);
 			instr = GoBack(curr);
 		}
-		// PSF, ChkRefS, POP -> ChkNullV
-		else if( (IsCombination(curr, asBC_PSF, asBC_ChkRefS) &&
-		          IsCombination(instr, asBC_ChkRefS, asBC_POP) &&
-		          instr->next->wArg[0] >= AS_PTR_SIZE) )
+		// PSF, ChkRefS, PopPtr -> ChkNullV
+		else if( IsCombination(curr, asBC_PSF, asBC_ChkRefS) &&
+		         IsCombination(instr, asBC_ChkRefS, asBC_PopPtr) )
 		{
 			curr->op = asBC_ChkNullV;
 			curr->stackInc = 0;
-			// Decrease the number of DWORDs popped
-			instr->next->wArg[0] -= AS_PTR_SIZE;
+			// Delete the PopPtr instruction
+			DeleteInstruction(instr->next);
 			// Delete the ChkRefS instruction
 			DeleteInstruction(instr);
 			instr = GoBack(curr);
 		}
-		// PshVPtr, CHKREF, POP -> ChkNullV
-		else if( (IsCombination(curr, asBC_PshVPtr, asBC_CHKREF) &&
-		          IsCombination(instr, asBC_CHKREF, asBC_POP) &&
-		          instr->next->wArg[0] == AS_PTR_SIZE) )
+		// PshVPtr, CHKREF, PopPtr -> ChkNullV
+		else if( IsCombination(curr, asBC_PshVPtr, asBC_CHKREF) &&
+		         IsCombination(instr, asBC_CHKREF, asBC_PopPtr) )
 		{
 			curr->op = asBC_ChkNullV;
 			curr->stackInc = 0;
@@ -934,6 +969,8 @@ bool asCByteCode::IsInstrJmpOrLabel(cByteInstruction *curr)
 		curr->op == asBC_JMP     ||
 		curr->op == asBC_JZ      ||
 		curr->op == asBC_JNZ     ||
+		curr->op == asBC_JLowZ   ||
+		curr->op == asBC_JLowNZ  ||
 		curr->op == asBC_LABEL   )
 		return true;
 
@@ -992,9 +1029,10 @@ bool asCByteCode::IsTempVarRead(cByteInstruction *curr, int offset)
 
 				break;
 			}
-			else if( curr->op == asBC_JZ || curr->op == asBC_JNZ ||
-				     curr->op == asBC_JS || curr->op == asBC_JNS ||
-					 curr->op == asBC_JP || curr->op == asBC_JNP )
+			else if( curr->op == asBC_JZ    || curr->op == asBC_JNZ    ||
+				     curr->op == asBC_JS    || curr->op == asBC_JNS    ||
+					 curr->op == asBC_JP    || curr->op == asBC_JNP    ||
+					 curr->op == asBC_JLowZ || curr->op == asBC_JLowNZ )
 			{
 				cByteInstruction *dest = 0;
 				int label = *((int*)ARG_DW(curr->arg));
@@ -1073,6 +1111,8 @@ bool asCByteCode::IsTempRegUsed(cByteInstruction *curr)
 			curr->op == asBC_TNP      ||
 			curr->op == asBC_JZ       ||
 			curr->op == asBC_JNZ      ||
+			curr->op == asBC_JLowZ    ||
+			curr->op == asBC_JLowNZ   ||
 			curr->op == asBC_JS       ||
 			curr->op == asBC_JNS      ||
 			curr->op == asBC_JP       ||
@@ -1104,6 +1144,8 @@ bool asCByteCode::IsTempRegUsed(cByteInstruction *curr)
 			curr->op == asBC_JMP       ||
 			curr->op == asBC_JZ        ||
 			curr->op == asBC_JNZ       ||
+			curr->op == asBC_JLowZ     ||
+			curr->op == asBC_JLowNZ    ||
 			curr->op == asBC_CMPi      ||
 			curr->op == asBC_CMPu      ||
 			curr->op == asBC_CMPf      ||
@@ -1483,17 +1525,14 @@ int asCByteCode::FindLabel(int label, cByteInstruction *from, cByteInstruction *
 
 int asCByteCode::ResolveJumpAddresses()
 {
-	int pos = 0;
 	cByteInstruction *instr = first;
 	while( instr )
 	{
-		// The program pointer is updated as the instruction is read
-		pos += instr->GetSize();
-
-		if( instr->op == asBC_JMP || 
-			instr->op == asBC_JZ || instr->op == asBC_JNZ ||
-			instr->op == asBC_JS || instr->op == asBC_JNS || 
-			instr->op == asBC_JP || instr->op == asBC_JNP )
+		if( instr->op == asBC_JMP   || 
+			instr->op == asBC_JZ    || instr->op == asBC_JNZ    ||
+			instr->op == asBC_JLowZ || instr->op == asBC_JLowNZ ||
+			instr->op == asBC_JS    || instr->op == asBC_JNS    || 
+			instr->op == asBC_JP    || instr->op == asBC_JNP    )
 		{
 			int label = *((int*) ARG_DW(instr->arg));
 			int labelPosOffset;			
@@ -1641,9 +1680,10 @@ void asCByteCode::PostProcess()
 				AddPath(paths, dest, stackSize);
 				break;
 			}
-			else if( instr->op == asBC_JZ || instr->op == asBC_JNZ ||
-					 instr->op == asBC_JS || instr->op == asBC_JNS ||
-					 instr->op == asBC_JP || instr->op == asBC_JNP )
+			else if( instr->op == asBC_JZ    || instr->op == asBC_JNZ ||
+					 instr->op == asBC_JLowZ || instr->op == asBC_JLowNZ ||
+					 instr->op == asBC_JS    || instr->op == asBC_JNS ||
+					 instr->op == asBC_JP    || instr->op == asBC_JNP )
 			{
 				// Find the label that is being jumped to
 				int label = *((int*) ARG_DW(instr->arg));
@@ -1792,7 +1832,7 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri
 		fprintf(file, "%5d ", pos);
 		pos += instr->GetSize();
 
-		fprintf(file, "%3d %c ", instr->stackSize, instr->marked ? '*' : ' ');
+		fprintf(file, "%3d %c ", instr->stackSize + func->variableSpace, instr->marked ? '*' : ' ');
 
 		switch( asBCInfo[instr->op].type )
 		{
@@ -1870,9 +1910,11 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri
 
 			case asBC_JMP:
 			case asBC_JZ:
+			case asBC_JLowZ:
 			case asBC_JS:
 			case asBC_JP:
 			case asBC_JNZ:
+			case asBC_JLowNZ:
 			case asBC_JNS:
 			case asBC_JNP:
 				fprintf(file, "   %-8s %+d              (d:%d)\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg)), pos+*((int*) ARG_DW(instr->arg)));
@@ -1990,43 +2032,6 @@ void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine, asCScri
 
 //=============================================================================
 
-// Decrease stack with "numDwords"
-int asCByteCode::Pop(int numDwords)
-{
-	// Only single pointers are popped
-	// TODO: optimize: Change the instruction to be PopPtr and remove the argument
-	asASSERT(numDwords == AS_PTR_SIZE);
-
-	asASSERT(asBCInfo[asBC_POP].type == asBCTYPE_W_ARG);
-
-	if( AddInstruction() < 0 )
-		return 0;
-
-	last->op = asBC_POP;
-	last->wArg[0] = (short)numDwords;
-	last->size = asBCTypeSize[asBCInfo[asBC_POP].type];
-	last->stackInc = -numDwords;
-
-	return last->stackInc;
-}
-
-// Increase stack with "numDwords"
-int asCByteCode::Push(int numDwords)
-{
-	asASSERT(asBCInfo[asBC_PUSH].type == asBCTYPE_W_ARG);
-
-	if( AddInstruction() < 0 )
-		return 0;
-
-	last->op = asBC_PUSH;
-	last->wArg[0] = (short)numDwords;
-	last->size = asBCTypeSize[asBCInfo[asBC_PUSH].type];
-	last->stackInc = numDwords;
-
-	return last->stackInc;
-}
-
-
 int asCByteCode::InsertFirstInstrDWORD(asEBCInstr bc, asDWORD param)
 {
 	asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG);

+ 0 - 3
ThirdParty/AngelScript/source/as_bytecode.h

@@ -128,9 +128,6 @@ public:
 	int InstrW_FLOAT(asEBCInstr bc, asWORD a, float b);
 	int InstrW_W(asEBCInstr bc, int w, int b);
 
-	int Pop (int numDwords);
-	int Push(int numDwords);
-
 	asCArray<int> lineNumbers;
 	int largestStackUsed;
 

+ 235 - 235
ThirdParty/AngelScript/source/as_callfunc_arm_gcc.S

@@ -1,235 +1,235 @@
-/*
-  AngelCode Scripting Library
-  Copyright (c) 2003-2009 Andreas Jonsson
-
-  This software is provided 'as-is', without any express or implied
-  warranty. In no event will the authors be held liable for any
-  damages arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any
-  purpose, including commercial applications, and to alter it and
-  redistribute it freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you
-     must not claim that you wrote the original software. If you use
-     this software in a product, an acknowledgment in the product
-     documentation would be appreciated but is not required.
-
-  2. Altered source versions must be plainly marked as such, and
-     must not be misrepresented as being the original software.
-
-  3. This notice may not be removed or altered from any source
-     distribution.
-
-  The original version of this library can be located at:
-  http://www.angelcode.com/angelscript/
-
-  Andreas Jonsson
-  [email protected]
-*/
-
-
-// Assembly routines for the ARM call convention
-// Written by Fredrik Ehnbom in June 2009
-
-// Adapted to GNUC by darktemplar216 in September 2009
-
-#if defined(__arm__) || defined(__ARM__)
-
-.global armFunc
-.global armFuncR0
-.global armFuncR0R1
-.global armFuncObjLast
-.global armFuncR0ObjLast
-    
-armFunc:
-    stmdb   sp!, {r4-r8, lr}
-    mov     r6, r0  // arg table
-    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  // function address
-    mov     r8, #0
-
-    beq     nomoreargs
-
-    // Load the first 4 arguments into r0-r3
-    cmp     r7, #4
-    ldrge   r0, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r1, [r6],#4
-    cmp     r7, #3*4
-    ldrge   r2, [r6],#4
-    cmp     r7, #4*4
-    ldrge   r3, [r6],#4
-    ble     nomoreargs
-
-    // Load the rest of the arguments onto the stack
-    sub     r7, r7, #4*4    // skip the 4 registers already loaded into r0-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-stackargsloop:
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     stackargsloop
-nomoreargs:
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    ldmia   sp!, {r4-r8, pc}
-
-armFuncObjLast:
-    stmdb   sp!, {r4-r8, lr}
-    mov     r6, r0  // arg table
-    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  // function address
-    mov     r8, #0
-
-    mov     r0, r3          // objlast. might get overwritten
-    str     r3, [sp, #-4]!  // objlast again.
-
-    beq     nomoreargsarmFuncObjLast
-
-    // Load the first 4 arguments into r0-r3
-    cmp     r7, #4
-    ldrge   r0, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r1, [r6],#4
-    ldrlt   r1, [sp]    
-    cmp     r7, #3*4
-    ldrge   r2, [r6],#4
-    ldrlt   r2, [sp]
-    cmp     r7, #4*4
-    ldrge   r3, [r6],#4
-    ldrlt   r3, [sp]
-    ble     nomoreargsarmFuncObjLast
-
-    // Load the rest of the arguments onto the stack
-    sub     r7, r7, #4*4    // skip the 4 registers already loaded into r0-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-stackargslooparmFuncObjLast:
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     stackargslooparmFuncObjLast
-nomoreargsarmFuncObjLast:
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    add     sp, sp, #4
-    ldmia   sp!, {r4-r8, pc}
-
-armFuncR0ObjLast:
-    stmdb   sp!, {r4-r8, lr}
-    ldr     r7, [sp,#6*4]
-    str     r7, [sp,#-4]!
-
-    mov     r6, r0  // arg table
-    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  // function address
-    mov     r8, #0
-
-    mov     r0, r3      // r0 explicitly set
-    ldr     r1, [sp]    // objlast.  might get overwritten
-
-    beq     nomoreargsarmFuncR0ObjLast
-
-    // Load the first 3 arguments into r1-r3
-    cmp     r7, #1*4
-    ldrge   r1, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r2, [r6],#4
-    ldrlt   r2, [sp]
-    cmp     r7, #3*4
-    ldrge   r3, [r6],#4
-    ldrlt   r3, [sp]
-    ble     nomoreargsarmFuncR0ObjLast
-
-    // Load the rest of the arguments onto the stack
-    sub     r7, r7, #3*4    // skip the 3 registers already loaded into r1-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-stackargslooparmFuncR0ObjLast:
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     stackargslooparmFuncR0ObjLast
-nomoreargsarmFuncR0ObjLast:
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    add     sp, sp, #4
-    ldmia   sp!, {r4-r8, pc}
-
-
-armFuncR0:
-    stmdb   sp!, {r4-r8, lr}
-    mov     r6, r0  // arg table
-    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  // function address
-    mov     r8, #0
-
-    mov     r0, r3  // r0 explicitly set
-
-    beq     nomoreargsarmFuncR0
-
-    // Load the first 3 arguments into r1-r3
-    cmp     r7, #1*4
-    ldrge   r1, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r2, [r6],#4
-    cmp     r7, #3*4
-    ldrge   r3, [r6],#4
-    ble     nomoreargsarmFuncR0
-
-    // Load the rest of the arguments onto the stack
-    sub     r7, r7, #3*4    // skip the 3 registers already loaded into r1-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-stackargslooparmFuncR0:
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     stackargslooparmFuncR0
-nomoreargsarmFuncR0:
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    ldmia   sp!, {r4-r8, pc}
-
-
-armFuncR0R1:
-    stmdb   sp!, {r4-r8, lr}
-    mov     r6, r0  // arg table
-    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  // function address
-    mov     r8, #0
-
-    mov     r0, r3          // r0 explicitly set
-    ldr     r1, [sp, #6*4]  // r1 explicitly set too
-
-    beq     nomoreargsarmFuncR0R1
-
-    // Load the first 2 arguments into r2-r3
-    cmp     r7, #1*4
-    ldrge   r2, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r3, [r6],#4
-    ble     nomoreargsarmFuncR0R1
-
-    // Load the rest of the arguments onto the stack
-    sub     r7, r7, #2*4    // skip the 2 registers already loaded into r2-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-stackargslooparmFuncR0R1:
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     stackargslooparmFuncR0R1
-nomoreargsarmFuncR0R1:
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    ldmia   sp!, {r4-r8, pc}
-
-#endif
+/*
+  AngelCode Scripting Library
+  Copyright (c) 2003-2009 Andreas Jonsson
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any
+  damages arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any
+  purpose, including commercial applications, and to alter it and
+  redistribute it freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you
+     must not claim that you wrote the original software. If you use
+     this software in a product, an acknowledgment in the product
+     documentation would be appreciated but is not required.
+
+  2. Altered source versions must be plainly marked as such, and
+     must not be misrepresented as being the original software.
+
+  3. This notice may not be removed or altered from any source
+     distribution.
+
+  The original version of this library can be located at:
+  http://www.angelcode.com/angelscript/
+
+  Andreas Jonsson
+  [email protected]
+*/
+
+
+// Assembly routines for the ARM call convention
+// Written by Fredrik Ehnbom in June 2009
+
+// Adapted to GNUC by darktemplar216 in September 2009
+
+#if defined(__arm__) || defined(__ARM__)
+
+.global armFunc
+.global armFuncR0
+.global armFuncR0R1
+.global armFuncObjLast
+.global armFuncR0ObjLast
+    
+armFunc:
+    stmdb   sp!, {r4-r8, lr}
+    mov     r6, r0  // arg table
+    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  // function address
+    mov     r8, #0
+
+    beq     nomoreargs
+
+    // Load the first 4 arguments into r0-r3
+    cmp     r7, #4
+    ldrge   r0, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r1, [r6],#4
+    cmp     r7, #3*4
+    ldrge   r2, [r6],#4
+    cmp     r7, #4*4
+    ldrge   r3, [r6],#4
+    ble     nomoreargs
+
+    // Load the rest of the arguments onto the stack
+    sub     r7, r7, #4*4    // skip the 4 registers already loaded into r0-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+stackargsloop:
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     stackargsloop
+nomoreargs:
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    ldmia   sp!, {r4-r8, pc}
+
+armFuncObjLast:
+    stmdb   sp!, {r4-r8, lr}
+    mov     r6, r0  // arg table
+    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  // function address
+    mov     r8, #0
+
+    mov     r0, r3          // objlast. might get overwritten
+    str     r3, [sp, #-4]!  // objlast again.
+
+    beq     nomoreargsarmFuncObjLast
+
+    // Load the first 4 arguments into r0-r3
+    cmp     r7, #4
+    ldrge   r0, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r1, [r6],#4
+    ldrlt   r1, [sp]    
+    cmp     r7, #3*4
+    ldrge   r2, [r6],#4
+    ldrlt   r2, [sp]
+    cmp     r7, #4*4
+    ldrge   r3, [r6],#4
+    ldrlt   r3, [sp]
+    ble     nomoreargsarmFuncObjLast
+
+    // Load the rest of the arguments onto the stack
+    sub     r7, r7, #4*4    // skip the 4 registers already loaded into r0-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+stackargslooparmFuncObjLast:
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     stackargslooparmFuncObjLast
+nomoreargsarmFuncObjLast:
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    add     sp, sp, #4
+    ldmia   sp!, {r4-r8, pc}
+
+armFuncR0ObjLast:
+    stmdb   sp!, {r4-r8, lr}
+    ldr     r7, [sp,#6*4]
+    str     r7, [sp,#-4]!
+
+    mov     r6, r0  // arg table
+    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  // function address
+    mov     r8, #0
+
+    mov     r0, r3      // r0 explicitly set
+    ldr     r1, [sp]    // objlast.  might get overwritten
+
+    beq     nomoreargsarmFuncR0ObjLast
+
+    // Load the first 3 arguments into r1-r3
+    cmp     r7, #1*4
+    ldrge   r1, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r2, [r6],#4
+    ldrlt   r2, [sp]
+    cmp     r7, #3*4
+    ldrge   r3, [r6],#4
+    ldrlt   r3, [sp]
+    ble     nomoreargsarmFuncR0ObjLast
+
+    // Load the rest of the arguments onto the stack
+    sub     r7, r7, #3*4    // skip the 3 registers already loaded into r1-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+stackargslooparmFuncR0ObjLast:
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     stackargslooparmFuncR0ObjLast
+nomoreargsarmFuncR0ObjLast:
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    add     sp, sp, #4
+    ldmia   sp!, {r4-r8, pc}
+
+
+armFuncR0:
+    stmdb   sp!, {r4-r8, lr}
+    mov     r6, r0  // arg table
+    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  // function address
+    mov     r8, #0
+
+    mov     r0, r3  // r0 explicitly set
+
+    beq     nomoreargsarmFuncR0
+
+    // Load the first 3 arguments into r1-r3
+    cmp     r7, #1*4
+    ldrge   r1, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r2, [r6],#4
+    cmp     r7, #3*4
+    ldrge   r3, [r6],#4
+    ble     nomoreargsarmFuncR0
+
+    // Load the rest of the arguments onto the stack
+    sub     r7, r7, #3*4    // skip the 3 registers already loaded into r1-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+stackargslooparmFuncR0:
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     stackargslooparmFuncR0
+nomoreargsarmFuncR0:
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    ldmia   sp!, {r4-r8, pc}
+
+
+armFuncR0R1:
+    stmdb   sp!, {r4-r8, lr}
+    mov     r6, r0  // arg table
+    movs    r7, r1  // arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  // function address
+    mov     r8, #0
+
+    mov     r0, r3          // r0 explicitly set
+    ldr     r1, [sp, #6*4]  // r1 explicitly set too
+
+    beq     nomoreargsarmFuncR0R1
+
+    // Load the first 2 arguments into r2-r3
+    cmp     r7, #1*4
+    ldrge   r2, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r3, [r6],#4
+    ble     nomoreargsarmFuncR0R1
+
+    // Load the rest of the arguments onto the stack
+    sub     r7, r7, #2*4    // skip the 2 registers already loaded into r2-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+stackargslooparmFuncR0R1:
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     stackargslooparmFuncR0R1
+nomoreargsarmFuncR0R1:
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    ldmia   sp!, {r4-r8, pc}
+
+#endif

+ 242 - 242
ThirdParty/AngelScript/source/as_callfunc_arm_msvc.asm

@@ -1,242 +1,242 @@
-;
-;  AngelCode Scripting Library
-;  Copyright (c) 2003-2009 Andreas Jonsson
-;
-;  This software is provided 'as-is', without any express or implied
-;  warranty. In no event will the authors be held liable for any
-;  damages arising from the use of this software.
-;
-;  Permission is granted to anyone to use this software for any
-;  purpose, including commercial applications, and to alter it and
-;  redistribute it freely, subject to the following restrictions:
-;
-;  1. The origin of this software must not be misrepresented; you
-;     must not claim that you wrote the original software. If you use
-;     this software in a product, an acknowledgment in the product
-;     documentation would be appreciated but is not required.
-;
-;  2. Altered source versions must be plainly marked as such, and
-;     must not be misrepresented as being the original software.
-;
-;  3. This notice may not be removed or altered from any source
-;     distribution.
-;
-;  The original version of this library can be located at:
-;  http://www.angelcode.com/angelscript/
-;
-;  Andreas Jonsson
-;  [email protected]
-;
-
-
-; Assembly routines for the ARM call convention
-; Written by Fredrik Ehnbom in June 2009
-
-; MSVC currently doesn't support inline assembly for the ARM platform
-; so this separate file is needed.
-
-
-    AREA	|.rdata|, DATA, READONLY
-    EXPORT |armFunc|
-    EXPORT armFuncR0
-    EXPORT armFuncR0R1
-    EXPORT armFuncObjLast
-    EXPORT armFuncR0ObjLast
-    
-
-    AREA	|.text|, CODE, ARM
-
-|armFunc| PROC
-    stmdb   sp!, {r4-r8, lr}
-    mov     r6, r0  ; arg table
-    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  ; function address
-    mov     r8, #0
-
-    beq     |nomoreargs|
-
-    ; Load the first 4 arguments into r0-r3
-    cmp     r7, #4
-    ldrge   r0, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r1, [r6],#4
-    cmp     r7, #3*4
-    ldrge   r2, [r6],#4
-    cmp     r7, #4*4
-    ldrge   r3, [r6],#4
-    ble     |nomoreargs|
-
-    ; Load the rest of the arguments onto the stack
-    sub     r7, r7, #4*4    ; skip the 4 registers already loaded into r0-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-|stackargsloop|
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     |stackargsloop|
-|nomoreargs|
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    ldmia   sp!, {r4-r8, pc}
-    ENDP
-
-armFuncObjLast PROC
-    stmdb   sp!, {r4-r8, lr}
-    mov     r6, r0  ; arg table
-    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  ; function address
-    mov     r8, #0
-
-    mov     r0, r3          ; objlast. might get overwritten
-    str     r3, [sp, #-4]!  ; objlast again.
-
-    beq     |nomoreargs@armFuncObjLast|
-
-    ; Load the first 4 arguments into r0-r3
-    cmp     r7, #4
-    ldrge   r0, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r1, [r6],#4
-    ldrlt   r1, [sp]    
-    cmp     r7, #3*4
-    ldrge   r2, [r6],#4
-    ldrlt   r2, [sp]
-    cmp     r7, #4*4
-    ldrge   r3, [r6],#4
-    ldrlt   r3, [sp]
-    ble     |nomoreargs@armFuncObjLast|
-
-    ; Load the rest of the arguments onto the stack
-    sub     r7, r7, #4*4    ; skip the 4 registers already loaded into r0-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-|stackargsloop@armFuncObjLast|
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     |stackargsloop@armFuncObjLast|
-|nomoreargs@armFuncObjLast|
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    add     sp, sp, #4
-    ldmia   sp!, {r4-r8, pc}
-    ENDP
-
-armFuncR0ObjLast PROC
-    stmdb   sp!, {r4-r8, lr}
-    ldr     r7, [sp,#6*4]
-    str     r7, [sp,#-4]!
-
-    mov     r6, r0  ; arg table
-    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  ; function address
-    mov     r8, #0
-
-    mov     r0, r3      ; r0 explicitly set
-    ldr     r1, [sp]    ; objlast.  might get overwritten
-
-    beq     |nomoreargs@armFuncR0ObjLast|
-
-    ; Load the first 3 arguments into r1-r3
-    cmp     r7, #1*4
-    ldrge   r1, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r2, [r6],#4
-    ldrlt   r2, [sp]
-    cmp     r7, #3*4
-    ldrge   r3, [r6],#4
-    ldrlt   r3, [sp]
-    ble     |nomoreargs@armFuncR0ObjLast|
-
-    ; Load the rest of the arguments onto the stack
-    sub     r7, r7, #3*4    ; skip the 3 registers already loaded into r1-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-|stackargsloop@armFuncR0ObjLast|
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     |stackargsloop@armFuncR0ObjLast|
-|nomoreargs@armFuncR0ObjLast|
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    add     sp, sp, #4
-    ldmia   sp!, {r4-r8, pc}
-    ENDP
-
-armFuncR0 PROC
-    stmdb   sp!, {r4-r8, lr}
-    mov     r6, r0  ; arg table
-    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  ; function address
-    mov     r8, #0
-
-    mov     r0, r3  ; r0 explicitly set
-
-    beq     |nomoreargs@armFuncR0|
-
-    ; Load the first 3 arguments into r1-r3
-    cmp     r7, #1*4
-    ldrge   r1, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r2, [r6],#4
-    cmp     r7, #3*4
-    ldrge   r3, [r6],#4
-    ble     |nomoreargs@armFuncR0|
-
-    ; Load the rest of the arguments onto the stack
-    sub     r7, r7, #3*4    ; skip the 3 registers already loaded into r1-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-|stackargsloop@armFuncR0|
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     |stackargsloop@armFuncR0|
-|nomoreargs@armFuncR0|
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    ldmia   sp!, {r4-r8, pc}
-    ENDP
-
-armFuncR0R1 PROC
-    stmdb   sp!, {r4-r8, lr}
-    mov     r6, r0  ; arg table
-    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
-    mov     r4, r2  ; function address
-    mov     r8, #0
-
-    mov     r0, r3          ; r0 explicitly set
-    ldr     r1, [sp, #6*4]  ; r1 explicitly set too
-
-    beq     |nomoreargs@armFuncR0R1|
-
-    ; Load the first 2 arguments into r2-r3
-    cmp     r7, #1*4
-    ldrge   r2, [r6],#4
-    cmp     r7, #2*4
-    ldrge   r3, [r6],#4
-    ble     |nomoreargs@armFuncR0R1|
-
-    ; Load the rest of the arguments onto the stack
-    sub     r7, r7, #2*4    ; skip the 2 registers already loaded into r2-r3
-    sub     sp, sp, r7
-    mov     r8, r7
-|stackargsloop@armFuncR0R1|
-    ldr     r5, [r6], #4
-    str     r5, [sp], #4
-    subs    r7, r7, #4
-    bne     |stackargsloop@armFuncR0R1|
-|nomoreargs@armFuncR0R1|
-    sub     sp, sp, r8
-    blx     r4
-    add     sp, sp, r8
-    ldmia   sp!, {r4-r8, pc}
-    ENDP
-
-    END
+;
+;  AngelCode Scripting Library
+;  Copyright (c) 2003-2009 Andreas Jonsson
+;
+;  This software is provided 'as-is', without any express or implied
+;  warranty. In no event will the authors be held liable for any
+;  damages arising from the use of this software.
+;
+;  Permission is granted to anyone to use this software for any
+;  purpose, including commercial applications, and to alter it and
+;  redistribute it freely, subject to the following restrictions:
+;
+;  1. The origin of this software must not be misrepresented; you
+;     must not claim that you wrote the original software. If you use
+;     this software in a product, an acknowledgment in the product
+;     documentation would be appreciated but is not required.
+;
+;  2. Altered source versions must be plainly marked as such, and
+;     must not be misrepresented as being the original software.
+;
+;  3. This notice may not be removed or altered from any source
+;     distribution.
+;
+;  The original version of this library can be located at:
+;  http://www.angelcode.com/angelscript/
+;
+;  Andreas Jonsson
+;  [email protected]
+;
+
+
+; Assembly routines for the ARM call convention
+; Written by Fredrik Ehnbom in June 2009
+
+; MSVC currently doesn't support inline assembly for the ARM platform
+; so this separate file is needed.
+
+
+    AREA	|.rdata|, DATA, READONLY
+    EXPORT |armFunc|
+    EXPORT armFuncR0
+    EXPORT armFuncR0R1
+    EXPORT armFuncObjLast
+    EXPORT armFuncR0ObjLast
+    
+
+    AREA	|.text|, CODE, ARM
+
+|armFunc| PROC
+    stmdb   sp!, {r4-r8, lr}
+    mov     r6, r0  ; arg table
+    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  ; function address
+    mov     r8, #0
+
+    beq     |nomoreargs|
+
+    ; Load the first 4 arguments into r0-r3
+    cmp     r7, #4
+    ldrge   r0, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r1, [r6],#4
+    cmp     r7, #3*4
+    ldrge   r2, [r6],#4
+    cmp     r7, #4*4
+    ldrge   r3, [r6],#4
+    ble     |nomoreargs|
+
+    ; Load the rest of the arguments onto the stack
+    sub     r7, r7, #4*4    ; skip the 4 registers already loaded into r0-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+|stackargsloop|
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     |stackargsloop|
+|nomoreargs|
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    ldmia   sp!, {r4-r8, pc}
+    ENDP
+
+armFuncObjLast PROC
+    stmdb   sp!, {r4-r8, lr}
+    mov     r6, r0  ; arg table
+    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  ; function address
+    mov     r8, #0
+
+    mov     r0, r3          ; objlast. might get overwritten
+    str     r3, [sp, #-4]!  ; objlast again.
+
+    beq     |nomoreargs@armFuncObjLast|
+
+    ; Load the first 4 arguments into r0-r3
+    cmp     r7, #4
+    ldrge   r0, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r1, [r6],#4
+    ldrlt   r1, [sp]    
+    cmp     r7, #3*4
+    ldrge   r2, [r6],#4
+    ldrlt   r2, [sp]
+    cmp     r7, #4*4
+    ldrge   r3, [r6],#4
+    ldrlt   r3, [sp]
+    ble     |nomoreargs@armFuncObjLast|
+
+    ; Load the rest of the arguments onto the stack
+    sub     r7, r7, #4*4    ; skip the 4 registers already loaded into r0-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+|stackargsloop@armFuncObjLast|
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     |stackargsloop@armFuncObjLast|
+|nomoreargs@armFuncObjLast|
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    add     sp, sp, #4
+    ldmia   sp!, {r4-r8, pc}
+    ENDP
+
+armFuncR0ObjLast PROC
+    stmdb   sp!, {r4-r8, lr}
+    ldr     r7, [sp,#6*4]
+    str     r7, [sp,#-4]!
+
+    mov     r6, r0  ; arg table
+    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  ; function address
+    mov     r8, #0
+
+    mov     r0, r3      ; r0 explicitly set
+    ldr     r1, [sp]    ; objlast.  might get overwritten
+
+    beq     |nomoreargs@armFuncR0ObjLast|
+
+    ; Load the first 3 arguments into r1-r3
+    cmp     r7, #1*4
+    ldrge   r1, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r2, [r6],#4
+    ldrlt   r2, [sp]
+    cmp     r7, #3*4
+    ldrge   r3, [r6],#4
+    ldrlt   r3, [sp]
+    ble     |nomoreargs@armFuncR0ObjLast|
+
+    ; Load the rest of the arguments onto the stack
+    sub     r7, r7, #3*4    ; skip the 3 registers already loaded into r1-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+|stackargsloop@armFuncR0ObjLast|
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     |stackargsloop@armFuncR0ObjLast|
+|nomoreargs@armFuncR0ObjLast|
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    add     sp, sp, #4
+    ldmia   sp!, {r4-r8, pc}
+    ENDP
+
+armFuncR0 PROC
+    stmdb   sp!, {r4-r8, lr}
+    mov     r6, r0  ; arg table
+    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  ; function address
+    mov     r8, #0
+
+    mov     r0, r3  ; r0 explicitly set
+
+    beq     |nomoreargs@armFuncR0|
+
+    ; Load the first 3 arguments into r1-r3
+    cmp     r7, #1*4
+    ldrge   r1, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r2, [r6],#4
+    cmp     r7, #3*4
+    ldrge   r3, [r6],#4
+    ble     |nomoreargs@armFuncR0|
+
+    ; Load the rest of the arguments onto the stack
+    sub     r7, r7, #3*4    ; skip the 3 registers already loaded into r1-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+|stackargsloop@armFuncR0|
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     |stackargsloop@armFuncR0|
+|nomoreargs@armFuncR0|
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    ldmia   sp!, {r4-r8, pc}
+    ENDP
+
+armFuncR0R1 PROC
+    stmdb   sp!, {r4-r8, lr}
+    mov     r6, r0  ; arg table
+    movs    r7, r1  ; arg size (also set the condition code flags so that we detect if there are no arguments)
+    mov     r4, r2  ; function address
+    mov     r8, #0
+
+    mov     r0, r3          ; r0 explicitly set
+    ldr     r1, [sp, #6*4]  ; r1 explicitly set too
+
+    beq     |nomoreargs@armFuncR0R1|
+
+    ; Load the first 2 arguments into r2-r3
+    cmp     r7, #1*4
+    ldrge   r2, [r6],#4
+    cmp     r7, #2*4
+    ldrge   r3, [r6],#4
+    ble     |nomoreargs@armFuncR0R1|
+
+    ; Load the rest of the arguments onto the stack
+    sub     r7, r7, #2*4    ; skip the 2 registers already loaded into r2-r3
+    sub     sp, sp, r7
+    mov     r8, r7
+|stackargsloop@armFuncR0R1|
+    ldr     r5, [r6], #4
+    str     r5, [sp], #4
+    subs    r7, r7, #4
+    bne     |stackargsloop@armFuncR0R1|
+|nomoreargs@armFuncR0R1|
+    sub     sp, sp, r8
+    blx     r4
+    add     sp, sp, r8
+    ldmia   sp!, {r4-r8, pc}
+    ENDP
+
+    END

+ 223 - 152
ThirdParty/AngelScript/source/as_callfunc_x86.cpp

@@ -28,7 +28,6 @@
    [email protected]
 */
 
-// Modified by Lasse Öörni for Urho3D
 
 //
 // as_callfunc_x86.cpp
@@ -73,35 +72,16 @@ BEGIN_AS_NAMESPACE
 #define _S(x) _TOSTRING(x)
 #define _TOSTRING(x) #x
 
-typedef asQWORD (*t_CallCDeclQW)(const asDWORD *, int, asFUNCTION_t);
-typedef asQWORD (*t_CallCDeclQWObj)(void *obj, const asDWORD *, int, asFUNCTION_t);
-typedef asDWORD (*t_CallCDeclRetByRef)(const asDWORD *, int, asFUNCTION_t, void *);
-typedef asDWORD (*t_CallCDeclObjRetByRef)(void *obj, const asDWORD *, int, asFUNCTION_t, void *);
-typedef asQWORD (*t_CallSTDCallQW)(const asDWORD *, int, asFUNCTION_t);
-typedef asQWORD (*t_CallThisCallQW)(const void *, const asDWORD *, int, asFUNCTION_t);
-typedef asDWORD (*t_CallThisCallRetByRef)(const void *, const asDWORD *, int, asFUNCTION_t, void *);
-
 // Prototypes
-void CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
-void CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
-void CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
-void CallCDeclFunctionRetByRef_impl(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
-void CallCDeclFunctionRetByRefObjLast_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
-void CallCDeclFunctionRetByRefObjFirst_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
-void CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
-void CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
-void CallThisCallFunctionRetByRef_impl(const void *, const asDWORD *, int, asFUNCTION_t, void *retPtr);
-
-// Initialize function pointers
-const t_CallCDeclQW CallCDeclFunctionQWord = (t_CallCDeclQW)CallCDeclFunction;
-const t_CallCDeclQWObj CallCDeclFunctionQWordObjLast = (t_CallCDeclQWObj)CallCDeclFunctionObjLast;
-const t_CallCDeclQWObj CallCDeclFunctionQWordObjFirst = (t_CallCDeclQWObj)CallCDeclFunctionObjFirst;
-const t_CallCDeclRetByRef CallCDeclFunctionRetByRef = (t_CallCDeclRetByRef)CallCDeclFunctionRetByRef_impl;
-const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjLast = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjLast_impl;
-const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjFirst = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjFirst_impl;
-const t_CallSTDCallQW CallSTDCallFunctionQWord = (t_CallSTDCallQW)CallSTDCallFunction;
-const t_CallThisCallQW CallThisCallFunctionQWord = (t_CallThisCallQW)CallThisCallFunction;
-const t_CallThisCallRetByRef CallThisCallFunctionRetByRef = (t_CallThisCallRetByRef)CallThisCallFunctionRetByRef_impl;
+asQWORD CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
+asQWORD CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
+asQWORD CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
+asQWORD CallCDeclFunctionRetByRef(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
+asQWORD CallCDeclFunctionRetByRefObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
+asQWORD CallCDeclFunctionRetByRefObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
+asQWORD CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
+asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
+asQWORD CallThisCallFunctionRetByRef(const void *, const asDWORD *, int, asFUNCTION_t, void *retPtr);
 
 asDWORD GetReturnedFloat();
 asQWORD GetReturnedDouble();
@@ -111,16 +91,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
 	asCScriptEngine            *engine    = context->engine;
 	asSSystemFunctionInterface *sysFunc   = descr->sysFuncIntf;
 
-	// This needs to be volatile, as GCC 4.2 is optimizing 
-	// away the assignment of the return code on Mac OS X. 
-	// ref: http://www.gamedev.net/topic/621357-porting-dustforce-to-os-x/
-
-	// Urho3D: do not apply this fix on Linux, as it causes problems in release builds
-#ifndef __linux__
-	volatile asQWORD retQW = 0;
-#else
 	asQWORD retQW = 0;
-#endif
 
 	// Prepare the parameters
 	int paramSize = sysFunc->paramSize;
@@ -175,7 +146,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
 	switch( callConv )
 	{
 	case ICC_CDECL:
-		retQW = CallCDeclFunctionQWord(args, paramSize<<2, func);
+		retQW = CallCDeclFunction(args, paramSize<<2, func);
 		break;
 
 	case ICC_CDECL_RETURNINMEM:
@@ -183,7 +154,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
 		break;
 
 	case ICC_STDCALL:
-		retQW = CallSTDCallFunctionQWord(args, paramSize<<2, func);
+		retQW = CallSTDCallFunction(args, paramSize<<2, func);
 		break;
 
 	case ICC_STDCALL_RETURNINMEM:
@@ -192,11 +163,11 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
 		args--;
 		*(asPWORD*)args = (size_t)retPointer;
 
-		retQW = CallSTDCallFunctionQWord(args, paramSize<<2, func);
+		retQW = CallSTDCallFunction(args, paramSize<<2, func);
 		break;
 
 	case ICC_THISCALL:
-		retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, func);
+		retQW = CallThisCallFunction(obj, args, paramSize<<2, func);
 		break;
 
 	case ICC_THISCALL_RETURNINMEM:
@@ -207,7 +178,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
 		{
 			// Get virtual function table from the object pointer
 			asFUNCTION_t *vftable = *(asFUNCTION_t**)obj;
-			retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2]);
+			retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2]);
 		}
 		break;
 
@@ -220,7 +191,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
 		break;
 
 	case ICC_CDECL_OBJLAST:
-		retQW = CallCDeclFunctionQWordObjLast(obj, args, paramSize<<2, func);
+		retQW = CallCDeclFunctionObjLast(obj, args, paramSize<<2, func);
 		break;
 
 	case ICC_CDECL_OBJLAST_RETURNINMEM:
@@ -230,7 +201,7 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
 
 	case ICC_CDECL_OBJFIRST:
 		// Call the system object method as a cdecl with the obj ref as the first parameter
-		retQW = CallCDeclFunctionQWordObjFirst(obj, args, paramSize<<2, func);
+		retQW = CallCDeclFunctionObjFirst(obj, args, paramSize<<2, func);
 		break;
 
 	case ICC_CDECL_OBJFIRST_RETURNINMEM:
@@ -264,8 +235,10 @@ asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr,
 #endif
 
 
-void NOINLINE CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
+asQWORD NOINLINE CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -298,26 +271,32 @@ endcopy:
 		// Pop arguments from stack
 		add  esp, paramSize
 
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
-
-		// return value in EAX or EAX:EDX
 	}
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
+	// It is not possible to rely on ESP or BSP to refer to variables or arguments on the stack
+	// depending on compiler settings BSP may not even be used, and the ESP is not always on the 
+	// same offset from the local variables. Because the code adjusts the ESP register it is not
+	// possible to inform the arguments through symbolic names below.
+
+	// It's not also not possible to rely on the memory layout of the function arguments, because
+	// on some compiler versions and settings the arguments may be copied to local variables with a 
+	// different ordering before they are accessed by the rest of the code. 
 
-	// GNUC 4.6.1 seems to have a bug when compiling with -O2. This wasn't a problem in earlier versions.
-	// Even though the clobber list is specifically listing the esp register, it still doesn't understand
-	// that it cannot rely on esp for getting the function arguments. So in order to work around this
-	// I'm passing the address of the first arg in edx to the inline assembly, and then copy it to ebx
-	// where it is guaranteed to be maintained over the function call.
+	// I'm copying the arguments into this array where I know the exact memory layout. The address
+	// of this array will then be passed to the inline asm in the EDX register.
+	volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func)};
 
 	asm __volatile__(
-		_S(CLEAR_FPU_STACK)  "\n"
+		_S(CLEAR_FPU_STACK)    "\n"
 		"pushl %%ebx            \n"
 		"movl  %%edx, %%ebx     \n"	
 
@@ -333,6 +312,7 @@ endcopy:
 		"subl  %%ecx, %%esp     \n"
 		"pushl %%eax            \n" // Store the original stack pointer
 
+		// Copy all arguments to the stack and call the function
 		"movl  4(%%ebx), %%ecx  \n" // paramSize
 		"movl  0(%%ebx), %%eax  \n" // args
 		"addl  %%ecx, %%eax     \n" // push arguments on the stack
@@ -346,20 +326,30 @@ endcopy:
 		"endcopy:               \n"
 		"call  *8(%%ebx)        \n"
 		"addl  4(%%ebx), %%esp  \n" // pop arguments
-		
+
 		// Pop the alignment bytes
 		"popl  %%esp            \n"
 		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&args)               // input - pass pointer of args in edx
-		: "%eax", "%ecx", "%esp"   // clobber
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 
 #endif
+
+	return retQW;
 }
 
-void NOINLINE CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
+asQWORD NOINLINE CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -396,21 +386,21 @@ endcopy:
 		add  esp, paramSize
 		add  esp, 4
 
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
-
-		// return value in EAX or EAX:EDX
 	}
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(obj);
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
+	volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)};
 
 	asm __volatile__ (
-		_S(CLEAR_FPU_STACK)  "\n"
+		_S(CLEAR_FPU_STACK)    "\n"
 		"pushl %%ebx            \n"
 		"movl  %%edx, %%ebx     \n"	
 
@@ -445,16 +435,26 @@ endcopy:
 		// Pop the alignment bytes
 		"popl  %%esp            \n"
 		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&obj)                // input - pass pointer of obj in edx
-		: "%eax", "%ecx", "%esp"   // clobber 
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 
 #endif
+
+	return retQW;
 }
 
-void NOINLINE CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
+asQWORD NOINLINE CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -491,21 +491,21 @@ endcopy:
 		add  esp, paramSize
 		add  esp, 4
 
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
-
-		// return value in EAX or EAX:EDX
 	}
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(obj);
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
+	volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)};
 
 	asm __volatile__ (
-		_S(CLEAR_FPU_STACK)  "\n"
+		_S(CLEAR_FPU_STACK)    "\n"
 		"pushl %%ebx            \n"
 		"movl  %%edx, %%ebx     \n"	
 
@@ -540,16 +540,26 @@ endcopy:
 		// Pop the alignment bytes
 		"popl  %%esp            \n"
 		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&obj)                // input - pass pointer of obj in edx
-		: "%eax", "%ecx", "%esp"   // clobber 
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 
 #endif
+
+	return retQW;
 }
 
-void NOINLINE CallCDeclFunctionRetByRefObjFirst_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
+asQWORD NOINLINE CallCDeclFunctionRetByRefObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -594,22 +604,22 @@ endcopy:
 #else
 		add  esp, 4
 #endif
+
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
-
-		// return value in EAX or EAX:EDX
 	}
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(obj);
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
-	UNUSED_VAR(retPtr);
+	volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)};
 
 	asm __volatile__ (
-		_S(CLEAR_FPU_STACK)  "\n"
+		_S(CLEAR_FPU_STACK)    "\n"
 		"pushl %%ebx            \n"
 		"movl  %%edx, %%ebx     \n"	
 
@@ -648,15 +658,25 @@ endcopy:
 		// Pop the alignment bytes
 		"popl  %%esp            \n"
 		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&obj)                // input - pass pointer of obj in edx
-		: "%eax", "%ecx", "%esp"   // clobber 
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 #endif
+
+	return retQW;
 }
 
-void NOINLINE CallCDeclFunctionRetByRef_impl(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
+asQWORD NOINLINE CallCDeclFunctionRetByRef(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -696,6 +716,12 @@ endcopy:
 		// Pop the return pointer
 		add  esp, 4
 #endif
+
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
 
@@ -704,13 +730,10 @@ endcopy:
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
-	UNUSED_VAR(retPtr);
+	volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)};
 
 	asm __volatile__ (
-		_S(CLEAR_FPU_STACK)  "\n"
+		_S(CLEAR_FPU_STACK)    "\n"
 		"pushl %%ebx            \n"
 		"movl  %%edx, %%ebx     \n"	
 
@@ -746,16 +769,26 @@ endcopy:
 		// Pop the alignment bytes
 		"popl  %%esp            \n"
 		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&args)               // input - pass pointer of args in edx
-		: "%eax", "%ecx", "%esp"   // clobber 
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 
 #endif
+
+	return retQW;
 }
 
-void NOINLINE CallCDeclFunctionRetByRefObjLast_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
+asQWORD NOINLINE CallCDeclFunctionRetByRefObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -798,22 +831,22 @@ endcopy:
 		// Pop the return pointer
 		add  esp, 4
 #endif
+
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
-
-		// return value in EAX or EAX:EDX
 	}
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(obj);
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
-	UNUSED_VAR(retPtr);
+	volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)};
 
 	asm __volatile__ (
-		_S(CLEAR_FPU_STACK)  "\n"
+		_S(CLEAR_FPU_STACK)    "\n"
 		"pushl %%ebx            \n"
 		"movl  %%edx, %%ebx     \n"	
 
@@ -852,16 +885,26 @@ endcopy:
 		// Pop the alignment bytes
 		"popl  %%esp            \n"
 		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&obj)                // input - pass pointer of obj in edx
-		: "%eax", "%ecx", "%esp"   // clobber 
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 
 #endif
+
+	return retQW;
 }
 
-void NOINLINE CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
+asQWORD NOINLINE CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -893,20 +936,21 @@ endcopy:
 
 		// The callee already removed parameters from the stack
 
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
-
-		// return value in EAX or EAX:EDX
 	}
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
+	volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func)};
 
 	asm __volatile__ (
-		_S(CLEAR_FPU_STACK)  "\n"
+		_S(CLEAR_FPU_STACK)    "\n"
 		"pushl %%ebx            \n"
 		"movl  %%edx, %%ebx     \n"	
 
@@ -938,17 +982,27 @@ endcopy:
 		// Pop the alignment bytes
 		"popl  %%esp            \n"
 		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&args)               // input - pass pointer of args in edx
-		: "%eax", "%ecx", "%esp"   // clobber 
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 
 #endif
+
+	return retQW;
 }
 
 
-void NOINLINE CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
+asQWORD NOINLINE CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -995,21 +1049,21 @@ endcopy:
 #endif
 #endif
 
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
-
-		// Return value in EAX or EAX:EDX
 	}
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(obj);
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
+	volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func)};
 
 	asm __volatile__ (
-		_S(CLEAR_FPU_STACK)  "\n"
+		_S(CLEAR_FPU_STACK)    "\n"
 		"pushl %%ebx            \n"
 		"movl  %%edx, %%ebx     \n"	
 
@@ -1045,16 +1099,26 @@ endcopy:
 		// Pop the alignment bytes
 		"popl  %%esp            \n"
 		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&obj)                // input - pass pointer of obj in edx
-		: "%eax", "%ecx", "%esp"   // clobber 
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 
 #endif
+
+	return retQW;
 }
 
-void NOINLINE CallThisCallFunctionRetByRef_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
+asQWORD NOINLINE CallThisCallFunctionRetByRef(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
 {
+	volatile asQWORD retQW;
+
 #if defined ASM_INTEL
 
 	// Copy the data to the real stack. If we fail to do
@@ -1109,24 +1173,23 @@ endcopy:
 #endif
 #endif
 
+		// Copy return value from EAX:EDX
+		lea  ecx, retQW
+		mov  [ecx], eax
+		mov  4[ecx], edx
+
 		// Restore registers
 		pop  ecx
-
-		// Return value in EAX or EAX:EDX
 	}
 
 #elif defined ASM_AT_N_T
 
-	UNUSED_VAR(obj);
-	UNUSED_VAR(args);
-	UNUSED_VAR(paramSize);
-	UNUSED_VAR(func);
-	UNUSED_VAR(retPtr);
+	volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(retPtr)};
 
 	asm __volatile__ (
-		_S(CLEAR_FPU_STACK)  "\n"
-		"pushl %%ebx            \n"
-		"movl  %%edx, %%ebx     \n"	
+		_S(CLEAR_FPU_STACK)   "\n"
+		"pushl %%ebx           \n"
+		"movl  %%edx, %%ebx    \n"	
 
 		// Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
 		// It is assumed that when entering this function, the stack pointer is already aligned, so we need
@@ -1163,13 +1226,21 @@ endcopy:
 		                           // the return pointer was popped by the callee
 		// Pop the alignment bytes
 		"popl  %%esp           \n"
-		"popl  %%ebx            \n" 
-		:                          // output
-		: "d"(&obj)                // input - pass pointer of obj in edx
-		: "%eax", "%ecx", "%esp"   // clobber 
+		"popl  %%ebx           \n" 
+
+		// Copy EAX:EDX to retQW. As the stack pointer has been 
+		// restored it is now safe to access the local variable
+		"leal  %1, %%ecx        \n"
+		"movl  %%eax, 0(%%ecx)  \n"
+		"movl  %%edx, 4(%%ecx)  \n"
+		:                           // output
+		: "d"(a), "m"(retQW)        // input - pass pointer of args in edx, pass pointer of retQW in memory argument
+		: "%eax", "%ecx"            // clobber
 		);
 
 #endif
+
+	return retQW;
 }
 
 asDWORD GetReturnedFloat()

+ 222 - 133
ThirdParty/AngelScript/source/as_compiler.cpp

@@ -51,6 +51,18 @@
 
 BEGIN_AS_NAMESPACE
 
+//
+// The calling convention rules for script functions:
+// - If a class method returns a reference, the caller must guarantee the object pointer stays alive until the function returns, and the reference is no longer going to be used
+// - If a class method doesn't return a reference, it must guarantee by itself that the this pointer stays alive during the function call. If no outside access is made, then the function is guaranteed to stay alive and nothing needs to be done
+// - The object pointer is always passed as the first argument, position 0
+// - If the function returns a value type the caller must reserve the memory for this and pass the pointer as the first argument after the object pointer
+//
+
+
+
+
+
 // TODO: I must correct the interpretation of a references to objects in the compiler.
 //       A reference should mean that a pointer to the object is on the stack.
 //       No expression should end up as non-references to objects, as the actual object is
@@ -168,11 +180,11 @@ int asCCompiler::CompileFactory(asCBuilder *builder, asCScriptCode *script, asCS
 	// Allocate the class and instanciate it with the constructor
 	int varOffset = AllocateVariable(dt, true);
 
-	byteCode.Push(AS_PTR_SIZE);
+	outFunc->variableSpace = AS_PTR_SIZE;
 	byteCode.InstrSHORT(asBC_PSF, (short)varOffset);
 
 	// Copy all arguments to the top of the stack
-	// TODO: optimize: Might be interesting to have a specific instruction for copying all arguments
+	// TODO: runtime optimize: Might be interesting to have a specific instruction for copying all arguments
 	int offset = (int)outFunc->GetSpaceNeededForArguments();
 	for( int a = int(outFunc->parameterTypes.GetLength()) - 1; a >= 0; a-- )
 	{
@@ -238,14 +250,30 @@ void asCCompiler::FinalizeFunction()
 	byteCode.ExtractObjectVariableInfo(outFunc);
 
 	// Compile the list of object variables for the exception handler
+	// Start with the variables allocated on the heap, and then the ones allocated on the stack
 	for( n = 0; n < variableAllocations.GetLength(); n++ )
 	{
 		if( variableAllocations[n].IsObject() && !variableAllocations[n].IsReference() )
 		{
-			outFunc->objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
-			outFunc->funcVariableTypes.PushLast(variableAllocations[n].GetFuncDef());
-			outFunc->objVariablePos.PushLast(GetVariableOffset(n));
-			outFunc->objVariableIsOnHeap.PushLast(variableIsOnHeap[n]);
+			if( variableIsOnHeap[n] )
+			{
+				outFunc->objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
+				outFunc->funcVariableTypes.PushLast(variableAllocations[n].GetFuncDef());
+				outFunc->objVariablePos.PushLast(GetVariableOffset(n));
+			}
+		}
+	}
+	outFunc->objVariablesOnHeap = outFunc->objVariablePos.GetLength();
+	for( n = 0; n < variableAllocations.GetLength(); n++ )
+	{
+		if( variableAllocations[n].IsObject() && !variableAllocations[n].IsReference() )
+		{
+			if( !variableIsOnHeap[n] )
+			{
+				outFunc->objVariableTypes.PushLast(variableAllocations[n].GetObjectType());
+				outFunc->funcVariableTypes.PushLast(variableAllocations[n].GetFuncDef());
+				outFunc->objVariablePos.PushLast(GetVariableOffset(n));
+			}
 		}
 	}
 
@@ -253,7 +281,7 @@ void asCCompiler::FinalizeFunction()
 	outFunc->byteCode.SetLength(byteCode.GetSize());
 	byteCode.Output(outFunc->byteCode.AddressOf());
 	outFunc->AddReferences();
-	outFunc->stackNeeded = byteCode.largestStackUsed;
+	outFunc->stackNeeded = byteCode.largestStackUsed + outFunc->variableSpace;
 	outFunc->lineNumbers = byteCode.lineNumbers;
 }
 
@@ -286,10 +314,19 @@ int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, sEx
 
 	if( !signature )
 	{
-		// Skip the private keyword if it is there
 		node = func->firstChild;
-		if( node->nodeType == snUndefined && node->tokenType == ttPrivate )
+		if( outFunc->objectType )
+		{
+			// Skip the private keyword if it is there
+			if( node->nodeType == snUndefined && node->tokenType == ttPrivate )
+				node = node->next;
+		}
+		else if( outFunc->IsShared() )
+		{
+			// Skip the shared keyword
+			asASSERT( node->nodeType == snIdentifier );
 			node = node->next;
+		}
 
 		//----------------------------------------------
 		// Examine return type
@@ -487,23 +524,37 @@ int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, sEx
 
 	// Count total variable size
 	int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1;
-	byteCode.Push(varSize);
+	outFunc->variableSpace = varSize;
 
 	if( outFunc->objectType )
 	{
 		// Call the base class' default constructor unless called manually in the code
 		if( m_isConstructor && !m_isConstructorCalled && outFunc->objectType->derivedFrom )
 		{
-			byteCode.InstrSHORT(asBC_PSF, 0);
-			byteCode.Instr(asBC_RDSPtr);
-			byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
+			if( outFunc->objectType->derivedFrom->beh.construct )
+			{
+				byteCode.InstrSHORT(asBC_PSF, 0);
+				byteCode.Instr(asBC_RDSPtr);
+				byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE);
+			}
+			else
+			{
+				Error(TEXT_BASE_DOESNT_HAVE_DEF_CONSTR, blockBegin);
+			}
 		}
 
 		// Increase the reference for the object pointer, so that it is guaranteed to live during the entire call
-		// TODO: optimize: This is probably not necessary for constructors as no outside reference to the object is created yet
-		byteCode.InstrSHORT(asBC_PSF, 0);
-		byteCode.Instr(asBC_RDSPtr);
-		byteCode.Call(asBC_CALLSYS, outFunc->objectType->beh.addref, AS_PTR_SIZE);
+		if( !m_isConstructor && !outFunc->returnType.IsReference() )
+		{
+			// TODO: runtime optimize: If the function is trivial, i.e. doesn't access any outside functions, 
+			//                         then this is not necessary. If I implement this, then the function needs 
+			//                         to set a flag so the exception handler doesn't try to release the handle.
+			// It is not necessary to do this for constructors, as they have no outside references that can be released anyway
+			// It is not necessary to do this for methods that return references, as the caller is guaranteed to hold a reference to the object
+			byteCode.InstrSHORT(asBC_PSF, 0);
+			byteCode.Instr(asBC_RDSPtr);
+			byteCode.Call(asBC_CALLSYS, outFunc->objectType->beh.addref, AS_PTR_SIZE);
+		}
 	}
 
 	// Add the code for the statement block
@@ -542,7 +593,7 @@ int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, sEx
 	}
 
 	// Release the object pointer again
-	if( outFunc->objectType )
+	if( outFunc->objectType && !m_isConstructor && !outFunc->returnType.IsReference() )
 	{
 		byteCode.InstrW_PTR(asBC_FREE, 0, outFunc->objectType);
 	}
@@ -613,7 +664,7 @@ int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, bool isObjec
 				PerformFunctionCall(func, &ctx, false, &args, type.GetObjectType(), true, offset);
 
 				// Pop the reference left by the function call
-				ctx.bc.Pop(AS_PTR_SIZE);
+				ctx.bc.Instr(asBC_PopPtr);
 			}
 			else
 			{
@@ -624,7 +675,7 @@ int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, bool isObjec
 				ctx.bc.Instr(asBC_RDSPtr);
 				ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
 				ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType());
-				ctx.bc.Pop(AS_PTR_SIZE);
+				ctx.bc.Instr(asBC_PopPtr);
 				ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
 			}
 
@@ -709,7 +760,7 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb
 				PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType(), true, offset);
 
 				// Pop the reference left by the function call
-				ctx.bc.Pop(AS_PTR_SIZE);
+				ctx.bc.Instr(asBC_PopPtr);
 			}
 			else
 			{
@@ -720,7 +771,7 @@ int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, bool isOb
 				ctx.bc.Instr(asBC_RDSPtr);
 				ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue());
 				ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType());
-				ctx.bc.Pop(AS_PTR_SIZE);
+				ctx.bc.Instr(asBC_PopPtr);
 				ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
 			}
 
@@ -961,7 +1012,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
 							ctx.bc.Instr(asBC_RDSPtr);
 							ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[gvar->index]->GetAddressOfValue());
 							ctx.bc.InstrPTR(asBC_REFCPY, gvar->datatype.GetObjectType());
-							ctx.bc.Pop(AS_PTR_SIZE);
+							ctx.bc.Instr(asBC_PopPtr);
 							ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
 						}
 						else
@@ -1036,9 +1087,9 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
 		}
 		else
 		{
-			// TODO: optimize: Here we should look for the best matching constructor, instead of
-			//                 just the copy constructor. Only if no appropriate constructor is
-			//                 available should the assignment operator be used.
+			// TODO: runtime optimize: Here we should look for the best matching constructor, instead of
+			//                         just the copy constructor. Only if no appropriate constructor is
+			//                         available should the assignment operator be used.
 
 			if( !gvar->datatype.IsObjectHandle() )
 			{
@@ -1071,7 +1122,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
 				if( assigned )
 				{
 					// Pop the resulting value
-					ctx.bc.Pop(AS_PTR_SIZE);
+					ctx.bc.Instr(asBC_PopPtr);
 
 					// Release the argument
 					ProcessDeferredParams(&ctx);
@@ -1102,7 +1153,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
 				// Release temporary variables used by expression
 				ReleaseTemporaryVariable(expr.type, &ctx.bc);
 
-				ctx.bc.Pop(AS_PTR_SIZE);
+				ctx.bc.Instr(asBC_PopPtr);
 			}
 		}
 	}
@@ -1124,7 +1175,7 @@ int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *scrip
 	LineInstr(&byteCode, pos);
 
 	// Reserve space for all local variables
-	byteCode.Push(varSize);
+	outFunc->variableSpace = varSize;
 
 	byteCode.AddCode(&ctx.bc);
 
@@ -1269,7 +1320,7 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a
 
 					PerformAssignment(&type, &ctx->type, &ctx->bc, node);
 
-					ctx->bc.Pop(AS_PTR_SIZE);
+					ctx->bc.Instr(asBC_PopPtr);
 
 					ReleaseTemporaryVariable(ctx->type, &ctx->bc);
 
@@ -1363,7 +1414,7 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a
 					ctx->bc.Instr(asBC_RDSPtr);
 				ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
 				ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType());
-				ctx->bc.Pop(AS_PTR_SIZE);
+				ctx->bc.Instr(asBC_PopPtr);
 				ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset);
 
 				dt.MakeHandle(false);
@@ -1468,7 +1519,7 @@ void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, a
 
 			if( ctx->type.isVariable || ctx->type.isTemporary )
 			{
-				ctx->bc.Pop(AS_PTR_SIZE);
+				ctx->bc.Instr(asBC_PopPtr);
 				ctx->bc.InstrSHORT(asBC_VAR, ctx->type.stackOffset);
 
 				ProcessDeferredParams(ctx);
@@ -1547,8 +1598,8 @@ void asCCompiler::MoveArgsToStack(int funcId, asCByteCode *bc, asCArray<asSExprC
 					if( (args[n]->type.isVariable || args[n]->type.isTemporary) )
 					{
 						if( !IsVariableOnHeap(args[n]->type.stackOffset) )
-							// TODO: optimize: Actually the reference can be pushed on the stack directly
-							//                 as the value allocated on the stack is guaranteed to be safe
+							// TODO: runtime optimize: Actually the reference can be pushed on the stack directly
+							//                         as the value allocated on the stack is guaranteed to be safe
 							bc->InstrWORD(asBC_GETREF, (asWORD)offset);
 						else 
 							bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
@@ -1565,8 +1616,8 @@ void asCCompiler::MoveArgsToStack(int funcId, asCByteCode *bc, asCArray<asSExprC
 					// Send the object as a reference to the object,
 					// and not to the variable holding the object
 					if( !IsVariableOnHeap(args[n]->type.stackOffset) )
-						// TODO: optimize: Actually the reference can be pushed on the stack directly
-						//                 as the value allocated on the stack is guaranteed to be safe
+						// TODO: runtime optimize: Actually the reference can be pushed on the stack directly
+						//                         as the value allocated on the stack is guaranteed to be safe
 						bc->InstrWORD(asBC_GETREF, (asWORD)offset);
 					else
 						bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset);
@@ -1953,7 +2004,7 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
 								MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, v->stackOffset);
 
 								// Pop the reference left by the function call
-								ctx.bc.Pop(AS_PTR_SIZE);
+								ctx.bc.Instr(asBC_PopPtr);
 							}
 							else
 							{
@@ -2045,7 +2096,7 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
 				}
 				else
 				{
-					// TODO: optimize: We can use a copy constructor here
+					// TODO: runtime optimize: We can use a copy constructor here
 
 					sVariable *v = variables->GetVariable(name.AddressOf());
 
@@ -2077,7 +2128,7 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
 						if( assigned )
 						{
 							// Pop the resulting value
-							ctx.bc.Pop(AS_PTR_SIZE);
+							ctx.bc.Instr(asBC_PopPtr);
 
 							// Release the argument
 							ProcessDeferredParams(&ctx);
@@ -2112,7 +2163,7 @@ void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc)
 						// Release temporary variables used by expression
 						ReleaseTemporaryVariable(expr.type, &ctx.bc);
 
-						ctx.bc.Pop(AS_PTR_SIZE);
+						ctx.bc.Instr(asBC_PopPtr);
 
 						ProcessDeferredParams(&ctx);
 					}
@@ -2176,7 +2227,7 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte
 	{
 		// Call factory and store the handle in the given variable
 		PerformFunctionCall(funcId, &ctx, false, &args, 0, true, var->stackOffset);
-		ctx.bc.Pop(AS_PTR_SIZE);
+		ctx.bc.Instr(asBC_PopPtr);
 	}
 	else
 	{
@@ -2186,7 +2237,7 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte
 		ctx.bc.Instr(asBC_RDSPtr);
 		ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue());
 		ctx.bc.InstrPTR(asBC_REFCPY, var->dataType.GetObjectType());
-		ctx.bc.Pop(AS_PTR_SIZE);
+		ctx.bc.Instr(asBC_PopPtr);
 		ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc);
 	}
 
@@ -2284,7 +2335,7 @@ void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByte
 			DoAssignment(&ctx, &lctx, &rctx, el, el, ttAssignment, el);
 
 			if( !lctx.type.dataType.IsPrimitive() )
-				ctx.bc.Pop(AS_PTR_SIZE);
+				ctx.bc.Instr(asBC_PopPtr);
 
 			// Release temporary variables used by expression
 			ReleaseTemporaryVariable(ctx.type, &ctx.bc);
@@ -2496,8 +2547,8 @@ void asCCompiler::CompileSwitchStatement(asCScriptNode *snode, bool *, asCByteCo
 	expr.bc.InstrDWORD(asBC_JP, defaultLabel);
 	ReleaseTemporaryVariable(tmpOffset, &expr.bc);
 
-	// TODO: optimize: We could possibly optimize this even more by doing a
-	//                 binary search instead of a linear search through the ranges
+	// TODO: runtime optimize: We could possibly optimize this even more by doing a
+	//                         binary search instead of a linear search through the ranges
 
 	// For each range
 	int range;
@@ -2778,27 +2829,14 @@ void asCCompiler::CompileIfStatement(asCScriptNode *inode, bool *hasReturn, asCB
 
 void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc)
 {
-	// TODO: optimize: We should be able to remove the static JMP to the beginning of the loop by rearranging the
-	//                 byte code a bit.
-	//
-	//                 init
-	//                 jump to before
-	//                 begin:
-	//                 statements
-	//                 continue:
-	//                 next
-	//                 before:
-	//                 condition
-	//                 if loop jump to begin
-	//                 break:
-
 	// Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables
 	AddVariableScope(true, true);
 
 	// We will use three labels for the for loop
-	int beforeLabel = nextLabel++;
+	int conditionLabel = nextLabel++;
 	int afterLabel = nextLabel++;
 	int continueLabel = nextLabel++;
+	int insideLabel = nextLabel++;
 
 	continueLabels.PushLast(continueLabel);
 	breakLabels.PushLast(afterLabel);
@@ -2833,7 +2871,7 @@ void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc)
 				ConvertToVariable(&expr);
 				expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset);
 				expr.bc.Instr(asBC_ClrHi);
-				expr.bc.InstrDWORD(asBC_JZ, afterLabel);
+				expr.bc.InstrDWORD(asBC_JNZ, insideLabel);
 				ReleaseTemporaryVariable(expr.type, &expr.bc);
 			}
 		}
@@ -2855,20 +2893,28 @@ void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc)
 	//-------------------------------
 	// Join the code pieces
 	bc->AddCode(&initBC);
-	bc->Label((short)beforeLabel);
+	bc->InstrDWORD(asBC_JMP, conditionLabel);
+
+	bc->Label((short)insideLabel);
 
 	// Add a suspend bytecode inside the loop to guarantee
 	// that the application can suspend the execution
 	bc->Instr(asBC_SUSPEND);
 	bc->InstrPTR(asBC_JitEntry, 0);
 
-
-	bc->AddCode(&expr.bc);
 	LineInstr(bc, fnode->lastChild->tokenPos);
 	bc->AddCode(&forBC);
+
 	bc->Label((short)continueLabel);
 	bc->AddCode(&nextBC);
-	bc->InstrINT(asBC_JMP, beforeLabel);
+
+	bc->Label((short)conditionLabel);
+	if( expr.bc.GetLastInstr() == -1 )
+		// There is no condition, so we just always jump
+		bc->InstrDWORD(asBC_JMP, insideLabel);
+	else
+		bc->AddCode(&expr.bc);
+
 	bc->Label((short)afterLabel);
 
 	continueLabels.PopLast();
@@ -3078,9 +3124,15 @@ void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode *
 		asSExprContext expr(engine);
 		CompileAssignment(enode->firstChild, &expr);
 
+		// If we get here and there is still an unprocessed property
+		// accessor, then process it as a get access. Don't call if there is 
+		// already a compile error, or we might report an error that is not valid
+		if( !hasCompileErrors )
+			ProcessPropertyGetAccessor(&expr, enode);
+
 		// Pop the value from the stack
 		if( !expr.type.dataType.IsPrimitive() )
-			expr.bc.Pop(AS_PTR_SIZE);
+			expr.bc.Instr(asBC_PopPtr);
 
 		// Release temporary variables used by expression
 		ReleaseTemporaryVariable(expr.type, &expr.bc);
@@ -3103,7 +3155,7 @@ void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ct
 		// the expression needs to be reevaluated to a reference
 		if( !ctx->type.dataType.IsReference() )
 		{
-			ctx->bc.Pop(AS_PTR_SIZE);
+			ctx->bc.Instr(asBC_PopPtr);
 			ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset);
 			ctx->type.dataType.MakeReference(true);
 		}
@@ -3157,7 +3209,7 @@ void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ct
 			}
 
 			// Pop the original reference
-			ctx->bc.Pop(AS_PTR_SIZE);
+			ctx->bc.Instr(asBC_PopPtr);
 		}
 	}
 
@@ -3344,9 +3396,9 @@ void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
 				// that has been reserved by the calling function. 
 				if( outFunc->DoesReturnOnStack() )
 				{
-					// TODO: optimize: If the return type has a constructor that takes the type of the expression,
-					//                 it should be called directly instead of first converting the expression and 
-					//                 then copy the value.
+					// TODO: runtime optimize: If the return type has a constructor that takes the type of the expression,
+					//                         it should be called directly instead of first converting the expression and 
+					//                         then copy the value.
 					if( !v->type.IsEqualExceptRefAndConst(expr.type.dataType) ) 
 					{
 						ImplicitConversion(&expr, v->type, rnode->firstChild, asIC_IMPLICIT_CONV);
@@ -3377,7 +3429,7 @@ void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
 						lexpr.type.Set(v->type);
 						lexpr.type.isLValue = true;
 						PerformAssignment(&lexpr.type, &expr.type, &expr.bc, rnode->firstChild);
-						expr.bc.Pop(AS_PTR_SIZE);
+						expr.bc.Instr(asBC_PopPtr);
 
 						// Release any temporary variable
 						ReleaseTemporaryVariable(expr.type, &expr.bc);
@@ -3396,7 +3448,7 @@ void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc)
 					PrepareArgument(&v->type, &expr, rnode->firstChild, false, 0);
 
 					// Pop the reference to the temporary variable
-					expr.bc.Pop(AS_PTR_SIZE);
+					expr.bc.Instr(asBC_PopPtr);
 
 					// Clean up the local variables and process deferred parameters
 					DestroyVariables(&expr.bc);
@@ -3709,10 +3761,7 @@ void asCCompiler::Dereference(asSExprContext *ctx, bool generateCode)
 		{
 			ctx->type.dataType.MakeReference(false);
 			if( generateCode )
-			{
-				ctx->bc.Instr(asBC_CHKREF);
 				ctx->bc.Instr(asBC_RDSPtr);
-			}
 		}
 		else
 		{
@@ -3920,7 +3969,7 @@ int asCCompiler::PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asC
 			}
 
 			// Copy larger data types from a reference
-			// TODO: optimize: COPY should pop both arguments and store the reference in the register. 
+			// TODO: runtime optimize: COPY should pop both arguments and store the reference in the register. 
 			bc->InstrSHORT_DW(asBC_COPY, (short)lvalue->dataType.GetSizeInMemoryDWords(), engine->GetTypeIdFromDataType(lvalue->dataType));
 		}
 	}
@@ -3933,7 +3982,6 @@ int asCCompiler::PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asC
 			return -1;
 		}
 
-		// TODO: optimize: Convert to register based
 		bc->InstrPTR(asBC_REFCPY, lvalue->dataType.GetObjectType());
 
 		// Mark variable as initialized
@@ -4030,30 +4078,30 @@ bool asCCompiler::CompileRefCast(asSExprContext *ctx, const asCDataType &to, boo
 		{
 			if( generateCode )
 			{
-				// TODO: optimize: Instead of producing bytecode for checking if the handle is
-				//                 null, we can create a special CALLSYS instruction that checks
-				//                 if the object pointer is null and if so sets the object register
-				//                 to null directly without executing the function.
+				// TODO: runtime optimize: Instead of producing bytecode for checking if the handle is
+				//                         null, we can create a special CALLSYS instruction that checks
+				//                         if the object pointer is null and if so sets the object register
+				//                         to null directly without executing the function.
 				//
-				//                 Alternatively I could force the ref cast behaviours be global
-				//                 functions with 1 parameter, even though they should still be
-				//                 registered with RegisterObjectBehaviour()
+				//                         Alternatively I could force the ref cast behaviours be global
+				//                         functions with 1 parameter, even though they should still be
+				//                         registered with RegisterObjectBehaviour()
 
 				// Add code to avoid calling the cast behaviour if the handle is already null,
 				// because that will raise a null pointer exception due to the cast behaviour
 				// being a class method, and the this pointer cannot be null.
 
 				if( ctx->type.isVariable )
-					ctx->bc.Pop(AS_PTR_SIZE);
+					ctx->bc.Instr(asBC_PopPtr);
 				else
 				{
 					Dereference(ctx, true);
 					ConvertToVariable(ctx);
 				}
 
-				// TODO: optimize: should have immediate comparison for null pointer
+				// TODO: runtime optimize: should have immediate comparison for null pointer
 				int offset = AllocateVariable(asCDataType::CreateNullHandle(), true);
-				// TODO: optimize: ClrVPtr is not necessary, because the VM will initialize the variable to null anyway
+				// TODO: runtime optimize: ClrVPtr is not necessary, because the VM should initialize the variable to null anyway (it is currently not done for null pointers though)
 				ctx->bc.InstrSHORT(asBC_ClrVPtr, (asWORD)offset);
 				ctx->bc.InstrW_W(asBC_CmpPtr, ctx->type.stackOffset, offset);
 				DeallocateVariable(offset);
@@ -4070,7 +4118,7 @@ bool asCCompiler::CompileRefCast(asSExprContext *ctx, const asCDataType &to, boo
 				asCArray<asSExprContext *> args;
 				MakeFunctionCall(ctx, ops[0], objType.dataType.GetObjectType(), args, node);
 
-				ctx->bc.Pop(AS_PTR_SIZE);
+				ctx->bc.Instr(asBC_PopPtr);
 
 				int endLabel = nextLabel++;
 
@@ -5949,8 +5997,14 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr
 				return -1;
 			}
 		}
-		else if( rctx->type.dataType.IsReference() && (!(rctx->type.isVariable || rctx->type.isTemporary) || IsVariableOnHeap(rctx->type.stackOffset)) )
-			rctx->bc.Instr(asBC_RDSPtr);
+		else 
+		{
+			// Process any property accessor first, before placing the final reference on the stack
+			ProcessPropertyGetAccessor(rctx, rexpr);
+
+			if( rctx->type.dataType.IsReference() && (!(rctx->type.isVariable || rctx->type.isTemporary) || IsVariableOnHeap(rctx->type.stackOffset)) )
+				rctx->bc.Instr(asBC_RDSPtr);
+		}
 
 		MergeExprBytecode(ctx, rctx);
 		MergeExprBytecode(ctx, lctx);
@@ -5958,9 +6012,9 @@ int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExpr
 		if( !simpleExpr )
 		{
 			if( (rctx->type.isVariable || rctx->type.isTemporary) && !IsVariableOnHeap(rctx->type.stackOffset) )
-				// TODO: optimize: Actually the reference can be pushed on the stack directly
-				//                 as the value allocated on the stack is guaranteed to be safe.
-				//                 The bytecode optimizer should be able to determine this and optimize away the VAR + GETREF
+				// TODO: runtime optimize: Actually the reference can be pushed on the stack directly
+				//                         as the value allocated on the stack is guaranteed to be safe.
+				//                         The bytecode optimizer should be able to determine this and optimize away the VAR + GETREF
 				ctx->bc.InstrWORD(asBC_GETREF, AS_PTR_SIZE);
 			else
 				ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE);
@@ -6132,7 +6186,7 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx)
 				}
 				PerformAssignment(&rtemp, &le.type, &ctx->bc, cexpr->next);
 				if( !rtemp.dataType.IsPrimitive() )
-					ctx->bc.Pop(AS_PTR_SIZE); // Pop the original value (always a pointer)
+					ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer)
 
 				// Release the old temporary variable
 				ReleaseTemporaryVariable(le.type, &ctx->bc);
@@ -6153,7 +6207,7 @@ int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx)
 				}
 				PerformAssignment(&rtemp, &re.type, &ctx->bc, cexpr->next);
 				if( !rtemp.dataType.IsPrimitive() )
-					ctx->bc.Pop(AS_PTR_SIZE); // Pop the original value (always a pointer)
+					ctx->bc.Instr(asBC_PopPtr); // Pop the original value (always a pointer)
 
 				// Release the old temporary variable
 				ReleaseTemporaryVariable(re.type, &ctx->bc);
@@ -6450,7 +6504,7 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s
 				// Reference to primitive must be stored in the temp register
 				if( prop->type.IsPrimitive() )
 				{
-					// TODO: optimize: The ADD offset command should store the reference in the register directly
+					// TODO: runtime optimize: The ADD offset command should store the reference in the register directly
 					ctx->bc.Instr(asBC_PopRPtr);
 				}
 
@@ -6573,7 +6627,7 @@ int asCCompiler::CompileVariableAccess(const asCString &name, const asCString &s
 							if( ctx->type.dataType.GetObjectType()->flags & asOBJ_VALUE ||
 								!ctx->type.dataType.IsObjectHandle() )
 							{
-								// TODO: optimize: This is not necessary for application registered properties
+								// TODO: runtime optimize: This is not necessary for application registered properties
 								ctx->bc.Instr(asBC_ChkRefS);
 							}
 						}
@@ -7427,7 +7481,7 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx)
 				asSExprContext o(engine);
 				DoAssignment(&o, expr, &rctx, outParam.argNode, outParam.argNode, ttAssignment, outParam.argNode);
 
-				if( !o.type.dataType.IsPrimitive() ) o.bc.Pop(AS_PTR_SIZE);
+				if( !o.type.dataType.IsPrimitive() ) o.bc.Instr(asBC_PopPtr);
 
 				MergeExprBytecode(ctx, &o);
 			}
@@ -7436,7 +7490,7 @@ void asCCompiler::ProcessDeferredParams(asSExprContext *ctx)
 				// We must still evaluate the expression
 				MergeExprBytecode(ctx, expr);
 				if( !expr->type.isConstant || expr->type.IsNullConstant() )
-					ctx->bc.Pop(AS_PTR_SIZE);
+					ctx->bc.Instr(asBC_PopPtr);
 
 				// Give a warning, except if the argument is null or 0 which indicate the argument is really to be ignored
 				if( !expr->type.IsNullConstant() && !(expr->type.isConstant && expr->type.qwordValue == 0) )
@@ -7714,9 +7768,6 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a
 			ctx->type.SetVariable(dt, 0, false);
 			ctx->type.dataType.MakeReference(true);
 
-			// TODO: optimize: This adds a CHKREF. Is that really necessary? It isn't as the 
-			//                 VM will check for null pointer anyway before calling the method.
-			//                 The bytecode optimizer should know this and remove the unnecessary CHKREF
 			Dereference(ctx, true);
 		}			
 	}
@@ -7853,7 +7904,7 @@ int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, a
 					ConvertToVariable(&funcPtr);
 					ctx->bc.AddCode(&funcPtr.bc);
 					if( !funcPtr.type.isTemporary )
-						ctx->bc.Pop(AS_PTR_SIZE);
+						ctx->bc.Instr(asBC_PopPtr);
 				}
 
 				MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, funcPtr.type.stackOffset);
@@ -8353,27 +8404,55 @@ int asCCompiler::FindPropertyAccessor(const asCString &name, asSExprContext *ctx
 		}
 	}
 
+	bool isConst = false;
+	if( ctx->type.dataType.IsObjectHandle() )
+		isConst = ctx->type.dataType.IsHandleToConst();
+	else
+		isConst = ctx->type.dataType.IsReadOnly();
+
 	// Check for multiple matches
 	if( multipleGetFuncs.GetLength() > 0 )
 	{
-		asCString str;
-		str.Format(TXT_MULTIPLE_PROP_GET_ACCESSOR_FOR_s, name.AddressOf());
-		Error(str.AddressOf(), node);
+		// Filter the list by constness
+		FilterConst(multipleGetFuncs, !isConst);
 
-		PrintMatchingFuncs(multipleGetFuncs, node);
+		if( multipleGetFuncs.GetLength() > 1 )
+		{
+			asCString str;
+			str.Format(TXT_MULTIPLE_PROP_GET_ACCESSOR_FOR_s, name.AddressOf());
+			Error(str.AddressOf(), node);
 
-		return -1;
+			PrintMatchingFuncs(multipleGetFuncs, node);
+
+			return -1;
+		}
+		else
+		{
+			// The id may have changed
+			getId = multipleGetFuncs[0];
+		}
 	}
 
 	if( multipleSetFuncs.GetLength() > 0 )
 	{
-		asCString str;
-		str.Format(TXT_MULTIPLE_PROP_SET_ACCESSOR_FOR_s, name.AddressOf());
-		Error(str.AddressOf(), node);
+		// Filter the list by constness
+		FilterConst(multipleSetFuncs, !isConst);
 
-		PrintMatchingFuncs(multipleSetFuncs, node);
+		if( multipleSetFuncs.GetLength() > 1 )
+		{
+			asCString str;
+			str.Format(TXT_MULTIPLE_PROP_SET_ACCESSOR_FOR_s, name.AddressOf());
+			Error(str.AddressOf(), node);
 
-		return -1;
+			PrintMatchingFuncs(multipleSetFuncs, node);
+
+			return -1;
+		}
+		else
+		{
+			// The id may have changed
+			setId = multipleSetFuncs[0];
+		}
 	}
 
 	// Check for type compatibility between get and set accessor
@@ -8970,6 +9049,11 @@ int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ct
 				if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true);
 				if( ctx->property_ref )	ctx->type.dataType.MakeReference(true);
 			}
+			else
+			{
+				ctx->type.SetDummy();
+			}
+
 			ctx->property_get = ctx->property_set = 0;
 			if( ctx->property_arg )
 			{
@@ -9646,7 +9730,7 @@ void asCCompiler::ConvertToVariable(asSExprContext *ctx)
 		if( ctx->type.IsNullConstant() )
 		{
 			if( ctx->bc.GetLastInstr() == asBC_PshNull )
-				ctx->bc.Pop(AS_PTR_SIZE); // Pop the null constant pushed onto the stack
+				ctx->bc.Instr(asBC_PopPtr); // Pop the null constant pushed onto the stack
 			ctx->bc.InstrSHORT(asBC_ClrVPtr, (short)offset);
 		}
 		else
@@ -9654,8 +9738,7 @@ void asCCompiler::ConvertToVariable(asSExprContext *ctx)
 			// Copy the object handle to a variable
 			ctx->bc.InstrSHORT(asBC_PSF, (short)offset);
 			ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType());
-			// TODO: optimize: REFCPY should pop both arguments, and store the return in the register, just like a normal function
-			ctx->bc.Pop(AS_PTR_SIZE);
+			ctx->bc.Instr(asBC_PopPtr);
 		}
 
 		ReleaseTemporaryVariable(ctx->type, &ctx->bc);
@@ -10869,9 +10952,9 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *
 
 	// Need to pop the value if it is a null constant
 	if( lctx->type.IsNullConstant() )
-		lctx->bc.Pop(AS_PTR_SIZE);
+		lctx->bc.Instr(asBC_PopPtr);
 	if( rctx->type.IsNullConstant() )
-		rctx->bc.Pop(AS_PTR_SIZE);
+		rctx->bc.Instr(asBC_PopPtr);
 
 	// Convert both sides to explicit handles
 	to.MakeHandle(true);
@@ -10898,6 +10981,12 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *
 		Error(str.AddressOf(), node);
 	}
 
+	// Make sure it really is handles that are being compared
+	if( !lctx->type.dataType.IsObjectHandle() )
+	{
+		Error(TXT_OPERANDS_MUST_BE_HANDLES, node);
+	}
+
 	ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true));
 
 	int op = node->tokenType;
@@ -10905,11 +10994,11 @@ void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *
 	{
 		// If the object handle already is in a variable we must manually pop it from the stack
 		if( lctx->type.isVariable )
-			lctx->bc.Pop(AS_PTR_SIZE);
+			lctx->bc.Instr(asBC_PopPtr);
 		if( rctx->type.isVariable )
-			rctx->bc.Pop(AS_PTR_SIZE);
+			rctx->bc.Instr(asBC_PopPtr);
 
-		// TODO: optimize: Treat the object handles as two integers, i.e. don't do REFCPY
+		// TODO: runtime optimize: don't do REFCPY
 		ConvertToVariableNotIn(lctx, rctx);
 		ConvertToVariable(rctx);
 
@@ -10972,7 +11061,7 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo
 	{
 		// The class method we're calling is returning a reference, which may be to a member of the object.
 		// In order to guarantee the lifetime of the reference, we must hold a local reference to the object.
-		// TODO: optimize: This can be avoided for local variables (non-handles) as they have a well defined life time
+		// TODO: runtime optimize: This can be avoided for local variables (non-handles) as they have a well defined life time
 		int tempRef = AllocateVariable(ctx->type.dataType, true);
 		ctx->bc.InstrSHORT(asBC_PSF, (short)tempRef);
 		ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType());
@@ -11023,13 +11112,13 @@ void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isCo
 			argSize += AS_PTR_SIZE;
 		}
 
-		// TODO: optimize: If it is known that a class method cannot be overridden the call
-		//                 should be made with asBC_CALL as it is faster. Examples where this
-		//                 is known is for example finalled methods where the class doesn't derive 
-		//                 from any other, or even non-finalled methods but where it is known
-		//                 at compile time the true type of the object. The first should be
-		//                 quite easy to determine, but the latter will be quite complex and possibly
-		//                 not worth it.
+		// TODO: runtime optimize: If it is known that a class method cannot be overridden the call
+		//                         should be made with asBC_CALL as it is faster. Examples where this
+		//                         is known is for example finalled methods where the class doesn't derive 
+		//                         from any other, or even non-finalled methods but where it is known
+		//                         at compile time the true type of the object. The first should be
+		//                         quite easy to determine, but the latter will be quite complex and possibly
+		//                         not worth it.
 		if( descr->funcType == asFUNC_IMPORTED )
 			ctx->bc.Call(asBC_CALLBND , descr->id, argSize);
 		// TODO: Maybe we need two different byte codes
@@ -11203,7 +11292,7 @@ void asCCompiler::MergeExprBytecodeAndType(asSExprContext *before, asSExprContex
 	// Do not copy the origExpr member
 }
 
-void asCCompiler::FilterConst(asCArray<int> &funcs)
+void asCCompiler::FilterConst(asCArray<int> &funcs, bool removeConst)
 {
 	if( funcs.GetLength() == 0 ) return;
 
@@ -11217,7 +11306,7 @@ void asCCompiler::FilterConst(asCArray<int> &funcs)
 	for( n = 0; n < funcs.GetLength(); n++ )
 	{
 		desc = builder->GetFunctionDescription(funcs[n]);
-		if( !desc->isReadOnly )
+		if( desc->isReadOnly != removeConst )
 		{
 			foundNonConst = true;
 			break;
@@ -11230,7 +11319,7 @@ void asCCompiler::FilterConst(asCArray<int> &funcs)
 		for( n = 0; n < funcs.GetLength(); n++ )
 		{
 			desc = builder->GetFunctionDescription(funcs[n]);
-			if( desc->isReadOnly )
+			if( desc->isReadOnly == removeConst )
 			{
 				if( n == funcs.GetLength() - 1 )
 					funcs.PopLast();

+ 1 - 1
ThirdParty/AngelScript/source/as_compiler.h

@@ -206,7 +206,7 @@ protected:
 	int  DoAssignment(asSExprContext *out, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, int op, asCScriptNode *opNode);
 	void MergeExprBytecode(asSExprContext *before, asSExprContext *after);
 	void MergeExprBytecodeAndType(asSExprContext *before, asSExprContext *after);
-	void FilterConst(asCArray<int> &funcs);
+	void FilterConst(asCArray<int> &funcs, bool removeConst = true);
 	void ConvertToVariable(asSExprContext *ctx);
 	void ConvertToVariableNotIn(asSExprContext *ctx, asSExprContext *exclude);
 	void ConvertToTempVariable(asSExprContext *ctx);

+ 1 - 7
ThirdParty/AngelScript/source/as_config.h

@@ -61,12 +61,7 @@
 
 // AS_DEBUG
 // This flag can be defined to make the library write some extra output when
-// compiling and executing scripts.
-
-// AS_DEBUG_STATS
-// This flag, combined with AS_DEBUG, make the library write out statistics
-// from the VM, for example how many times each bytecode instruction is executed,
-// etc. It is used to help identify potential code for optimizations.
+// compiling and executing scripts. 
 
 // AS_DEPRECATED
 // If this flag is defined then some backwards compatibility is maintained.
@@ -582,7 +577,6 @@
 			#define COMPLEX_RETURN_MASK (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR)
 		#elif defined(__LP64__) && !defined(__ppc__) && !defined(__PPC__)
 			// http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/140-x86-64_Function_Calling_Conventions/x86_64.html#//apple_ref/doc/uid/TP40005035-SW1
-			#define AS_NO_THREADS
 			#define AS_X64_GCC
 			#define HAS_128_BIT_PRIMITIVES
 			#define SPLIT_OBJS_BY_MEMBER_TYPES

+ 175 - 85
ThirdParty/AngelScript/source/as_context.cpp

@@ -62,7 +62,7 @@ const int RESERVE_STACK = 2*AS_PTR_SIZE;
 const int CALLSTACK_FRAME_SIZE = 5;
 
 
-#if defined(AS_DEBUG) && defined(AS_DEBUG_STATS)
+#if defined(AS_DEBUG)
 
 class asCDebugStats
 {
@@ -137,8 +137,7 @@ public:
 
 AS_API asIScriptContext *asGetActiveContext()
 {
-	asASSERT(threadManager);
-	asCThreadLocalData *tld = threadManager->GetLocalData();
+	asCThreadLocalData *tld = asCThreadManager::GetLocalData();
 	if( tld->activeContexts.GetLength() == 0 )
 		return 0;
 	return tld->activeContexts[tld->activeContexts.GetLength()-1];
@@ -146,15 +145,13 @@ AS_API asIScriptContext *asGetActiveContext()
 
 void asPushActiveContext(asIScriptContext *ctx)
 {
-	asASSERT(threadManager);
-	asCThreadLocalData *tld = threadManager->GetLocalData();
+	asCThreadLocalData *tld = asCThreadManager::GetLocalData();
 	tld->activeContexts.PushLast(ctx);
 }
 
 void asPopActiveContext(asIScriptContext *ctx)
 {
-	asASSERT(threadManager);
-	asCThreadLocalData *tld = threadManager->GetLocalData();
+	asCThreadLocalData *tld = asCThreadManager::GetLocalData();
 
 	asASSERT(tld->activeContexts.GetLength() > 0);
 	asASSERT(tld->activeContexts[tld->activeContexts.GetLength()-1] == ctx);
@@ -288,10 +285,20 @@ asIScriptFunction *asCContext::GetSystemFunction()
 int asCContext::Prepare(asIScriptFunction *func)
 {
 	if( func == 0 ) 
+	{
+		asCString str;
+		str.Format(TXT_FAILED_IN_FUNC_s_WITH_s, "Prepare", "null");
+		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
 		return asNO_FUNCTION;
+	}
 
 	if( status == asEXECUTION_ACTIVE || status == asEXECUTION_SUSPENDED )
+	{
+		asCString str;
+		str.Format(TXT_FAILED_IN_FUNC_s, "Prepare");
+		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
 		return asCONTEXT_ACTIVE;
+	}
 
 	// Clean the stack if not done before
 	if( status != asEXECUTION_FINISHED && status != asEXECUTION_UNINITIALIZED )
@@ -318,7 +325,7 @@ int asCContext::Prepare(asIScriptFunction *func)
 		initialFunction->AddRef();
 		currentFunction = initialFunction;
 
-		// TODO: optimize: GetSpaceNeededForArguments() should be precomputed
+		// TODO: runtime optimize: GetSpaceNeededForArguments() should be precomputed
 		argumentsSize = currentFunction->GetSpaceNeededForArguments() + (currentFunction->objectType ? AS_PTR_SIZE : 0);
 
 		// Reserve space for the arguments and return value
@@ -365,6 +372,7 @@ int asCContext::Prepare(asIScriptFunction *func)
 		stackIndex              = 0;
 	}
 	status = asEXECUTION_PREPARED;
+	regs.programPointer = 0;
 
 	// Reserve space for the arguments and return value
 	regs.stackFramePointer = stackBlocks[0] + stackBlockSize - argumentsSize - returnValueSize;
@@ -383,22 +391,6 @@ int asCContext::Prepare(asIScriptFunction *func)
 		*(void**)ptr = (void*)(stackBlocks[0] + stackBlockSize - returnValueSize);
 	}
 
-	if( currentFunction->funcType == asFUNC_SCRIPT )
-	{
-		regs.programPointer = currentFunction->byteCode.AddressOf();
-
-		// Set all object variables to 0
-		for( asUINT n = 0; n < currentFunction->objVariablePos.GetLength(); n++ )
-		{
-			if( !currentFunction->objVariableIsOnHeap[n] ) continue;
-
-			int pos = currentFunction->objVariablePos[n];
-			*(asPWORD*)&regs.stackFramePointer[-pos] = 0;
-		}
-	}
-	else
-		regs.programPointer = 0;
-
 	return asSUCCESS;
 }
 
@@ -999,7 +991,12 @@ int asCContext::Execute()
 	asASSERT( engine != 0 );
 
 	if( status != asEXECUTION_SUSPENDED && status != asEXECUTION_PREPARED )
+	{
+		asCString str;
+		str.Format(TXT_FAILED_IN_FUNC_s, "Execute");
+		engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
 		return asERROR;
+	}
 
 	status = asEXECUTION_ACTIVE;
 
@@ -1050,24 +1047,20 @@ int asCContext::Execute()
 				if( realFunc )
 				{
 					if( realFunc->signatureId != currentFunction->signatureId )
-					{
 						SetInternalException(TXT_NULL_POINTER_ACCESS);
-					}
 					else
-					{
 						currentFunction = realFunc;
-						regs.programPointer = currentFunction->byteCode.AddressOf();
-
-						// Set the local objects to 0
-						for( asUINT n = 0; n < currentFunction->objVariablePos.GetLength(); n++ )
-						{
-							int pos = currentFunction->objVariablePos[n];
-							*(asPWORD*)&regs.stackFramePointer[-pos] = 0;
-						}
-					}
 				}
 			}
 		}
+
+		if( currentFunction->funcType == asFUNC_SCRIPT )
+		{
+			regs.programPointer = currentFunction->byteCode.AddressOf();
+
+			// Set up the internal registers for executing the script function
+			PrepareScriptFunction();
+		}
 		else if( currentFunction->funcType == asFUNC_SYSTEM )
 		{
 			// The current function is an application registered function
@@ -1121,7 +1114,12 @@ int asCContext::Execute()
 
 void asCContext::PushCallState()
 {
-	callStack.SetLength(callStack.GetLength() + CALLSTACK_FRAME_SIZE);
+	if( callStack.GetLength() == callStack.GetCapacity() )
+	{
+		// Allocate space for 10 call states at a time to save time
+		callStack.AllocateNoConstruct(callStack.GetLength() + 10*CALLSTACK_FRAME_SIZE, true);
+	}
+	callStack.SetLengthNoConstruct(callStack.GetLength() + CALLSTACK_FRAME_SIZE);
 
     // Separating the loads and stores limits data cache trash, and with a smart compiler
     // could turn into SIMD style loading/storing if available.
@@ -1216,16 +1214,12 @@ int asCContext::GetLineNumber(asUINT stackLevel, int *column, const char **secti
 
 void asCContext::CallScriptFunction(asCScriptFunction *func)
 {
-	// TODO: Should perhaps call CallLineCallback() if set. This will allow the 
-	//       application that compiles without line cues to interrupt scripts
-	//       with endless recursive calls. Without this there is no guarantee
-	//       that the SUSPEND instruction is guaranteed to be executed.
-
 	// Push the framepointer, function id and programCounter on the stack
 	PushCallState();
 
+	// Update the current function and program position before increasing the stack 
+	// so the exception handler will know what to do if there is a stack overflow
 	currentFunction = func;
-
 	regs.programPointer = currentFunction->byteCode.AddressOf();
 
 	// Verify if there is enough room in the stack block. Allocate new block if not
@@ -1270,18 +1264,35 @@ void asCContext::CallScriptFunction(asCScriptFunction *func)
 		memcpy(regs.stackPointer, oldStackPointer, sizeof(asDWORD)*numDwords);
 	}
 
-	// Update framepointer and programCounter
+	PrepareScriptFunction();
+}
+
+void asCContext::PrepareScriptFunction()
+{
+	// Update framepointer
 	regs.stackFramePointer = regs.stackPointer;
 
-	// TODO: optimize: This can be avoided handling this as is done for value types in the exception handler
-	// Set all object variables to 0
-	for( asUINT n = 0; n < currentFunction->objVariablePos.GetLength(); n++ )
+	// Set all object variables to 0 to guarantee that they are null before they are used
+	// Only variables on the heap should be cleared. The rest will be cleared by calling the constructor
+	asUINT n = currentFunction->objVariablesOnHeap;
+	while( n-- > 0 )
 	{
-		if( !currentFunction->objVariableIsOnHeap[n] ) continue;
-
 		int pos = currentFunction->objVariablePos[n];
 		*(asPWORD*)&regs.stackFramePointer[-pos] = 0;
 	}
+
+	// Initialize the stack pointer with the space needed for local variables
+	regs.stackPointer -= currentFunction->variableSpace;
+
+	// Call the line callback for each script function, to guarantee that infinitely recursive scripts can
+	// be interrupted, even if the scripts have been compiled with asEP_BUILD_WITHOUT_LINE_CUES
+	if( regs.doProcessSuspend )
+	{
+		if( lineCallback )
+			CallLineCallback();
+		if( doSuspend )
+			status = asEXECUTION_SUSPENDED;
+	}
 }
 
 void asCContext::CallInterfaceMethod(asCScriptFunction *func)
@@ -1296,14 +1307,14 @@ void asCContext::CallInterfaceMethod(asCScriptFunction *func)
 
 	asCObjectType *objType = obj->objType;
 
-	// TODO: optimize: The object type should have a list of only those methods that 
-	//                 implement interface methods. This list should be ordered by
-	//                 the signatureId so that a binary search can be made, instead
-	//                 of a linear search.
+	// TODO: runtime optimize: The object type should have a list of only those methods that 
+	//                         implement interface methods. This list should be ordered by
+	//                         the signatureId so that a binary search can be made, instead
+	//                         of a linear search.
 	//
-	//                 When this is done, we must also make sure the signatureId of a 
-	//                 function never changes, e.g. when if the signature functions are
-	//                 released.
+	//                         When this is done, we must also make sure the signatureId of a 
+	//                         function never changes, e.g. when if the signature functions are
+	//                         released.
 
 	// Search the object type for a function that matches the interface function
 	asCScriptFunction *realFunc = 0;
@@ -1347,9 +1358,9 @@ void asCContext::ExecuteNext()
 	{
 
 #ifdef AS_DEBUG
-#ifdef AS_DEBUG_STATS
+	// Gather statistics on executed bytecode 
 	stats.Instr(*(asBYTE*)l_bc);
-#endif
+
 	// Used to verify that the size of the instructions are correct
 	asDWORD *old = l_bc;
 #endif
@@ -1365,18 +1376,17 @@ void asCContext::ExecuteNext()
 //--------------
 // memory access functions
 
-	// Decrease the stack pointer with n dwords (stack grows downward)
-	case asBC_POP:
-		// TODO: optimize: This instruction always pop a single pointer, so there is not really any need for it to take an argument
-		asASSERT( asBC_WORDARG0(l_bc) == AS_PTR_SIZE );
-		l_sp += asBC_WORDARG0(l_bc);
+	case asBC_PopPtr:
+		// Pop a pointer from the stack
+		l_sp += AS_PTR_SIZE;
 		l_bc++;
 		break;
 
-	// Increase the stack pointer with n dwords
-	case asBC_PUSH:
-		l_sp -= asBC_WORDARG0(l_bc);
-		l_bc++;
+	case asBC_PshGPtr:
+		// Replaces PGA + RDSPtr
+		l_sp -= AS_PTR_SIZE;
+		*(asPWORD*)l_sp = *(asPWORD*)asBC_PTRARG(l_bc);
+		l_bc += 1 + AS_PTR_SIZE;
 		break;
 
 	// Push a dword value on the stack
@@ -1898,8 +1908,21 @@ void asCContext::ExecuteNext()
 		break;
 
 	case asBC_RDSPtr:
-		// Pop an address from the stack, read a pointer from that address and push it on the stack
-		*(asPWORD*)l_sp = *(asPWORD*)*(asPWORD*)l_sp;
+		{
+			// The pointer must not be null
+			asPWORD a = *(asPWORD*)l_sp;
+			if( a == 0 )
+			{
+				regs.programPointer = l_bc;
+				regs.stackPointer = l_sp;
+				regs.stackFramePointer = l_fp;
+
+				SetInternalException(TXT_NULL_POINTER_ACCESS);
+				return;
+			}
+			// Pop an address from the stack, read a pointer from that address and push it on the stack
+			*(asPWORD*)l_sp = *(asPWORD*)a;
+		}
 		l_bc++;
 		break;
 
@@ -2360,6 +2383,9 @@ void asCContext::ExecuteNext()
 		break;
 
 	case asBC_ClrVPtr:
+		// TODO: optimize: Is this instruction really necessary? 
+		//                 CallScriptFunction() can clear the null handles upon entry, just as is done for 
+		//                 all other object variables
 		// Clear pointer variable
 		*(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = 0;
 		l_bc++;
@@ -2390,7 +2416,21 @@ void asCContext::ExecuteNext()
 		break;
 
 	case asBC_ADDSi:
-		*(asPWORD*)l_sp = asPWORD(*(asPWORD*)l_sp) + asBC_SWORDARG0(l_bc);
+		{
+			// The pointer must not be null
+			asPWORD a = *(asPWORD*)l_sp;
+			if( a == 0 )
+			{
+				regs.programPointer = l_bc;
+				regs.stackPointer = l_sp;
+				regs.stackFramePointer = l_fp;
+
+				SetInternalException(TXT_NULL_POINTER_ACCESS);
+				return;
+			}
+			// Add an offset to the pointer
+			*(asPWORD*)l_sp = a + asBC_SWORDARG0(l_bc);
+		}
 		l_bc += 2;
 		break;
 
@@ -2506,8 +2546,8 @@ void asCContext::ExecuteNext()
 
 	case asBC_CmpPtr:
 		{
-			// TODO: optimize: This instruction should really just be an equals, and return true or false.
-			//                 The instruction is only used for is and !is tests anyway.
+			// TODO: runtime optimize: This instruction should really just be an equals, and return true or false.
+			//                         The instruction is only used for is and !is tests anyway.
 			asPWORD p1 = *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc));
 			asPWORD p2 = *(asPWORD*)(l_fp - asBC_SWORDARG1(l_bc));
 			if( p1 == p2 )     *(int*)&regs.valueRegister =  0;
@@ -2806,7 +2846,7 @@ void asCContext::ExecuteNext()
 		{
 			// Verify if the pointer on the stack refers to a non-null value
 			// This is used to validate a reference to a handle
-			asDWORD *a = (asDWORD*)*(asPWORD*)l_sp;
+			asPWORD *a = (asPWORD*)*(asPWORD*)l_sp;
 			if( *a == 0 )
 			{
 				regs.programPointer = l_bc;
@@ -3397,11 +3437,56 @@ void asCContext::ExecuteNext()
 		}
 		break;
 
+	case asBC_RefCpyV:
+		// Same as PSF v, REFCPY
+		{
+			asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc);
+			asSTypeBehaviour *beh = &objType->beh;
+
+			// Determine destination from argument
+			void **d = (void**)asPWORD(l_fp - asBC_SWORDARG0(l_bc));
+			
+			// Read wanted pointer from the stack
+			void *s = (void*)*(asPWORD*)l_sp;
+
+			// Need to move the values back to the context as the called functions
+			// may use the debug interface to inspect the registers
+			regs.programPointer = l_bc;
+			regs.stackPointer = l_sp;
+			regs.stackFramePointer = l_fp;
+
+			if( !(objType->flags & asOBJ_NOCOUNT) )
+			{
+				// Release previous object held by destination pointer
+				if( *d != 0 )
+					engine->CallObjectMethod(*d, beh->release);
+				// Increase ref counter of wanted object
+				if( s != 0 )
+					engine->CallObjectMethod(s, beh->addref);
+			}
+
+			// Set the new object in the destination
+			*d = s;
+		}
+		l_bc += 1+AS_PTR_SIZE;
+		break;
+
+	case asBC_JLowZ:
+		if( *(asBYTE*)&regs.valueRegister == 0 )
+			l_bc += asBC_INTARG(l_bc) + 2;
+		else
+			l_bc += 2;
+		break;
+
+	case asBC_JLowNZ:
+		if( *(asBYTE*)&regs.valueRegister != 0 )
+			l_bc += asBC_INTARG(l_bc) + 2;
+		else
+			l_bc += 2;
+		break;
+
 	// Don't let the optimizer optimize for size,
 	// since it requires extra conditions and jumps
-	case 186: l_bc = (asDWORD*)186; break;
-	case 187: l_bc = (asDWORD*)187; break;
-	case 188: l_bc = (asDWORD*)188; break;
 	case 189: l_bc = (asDWORD*)189; break;
 	case 190: l_bc = (asDWORD*)190; break;
 	case 191: l_bc = (asDWORD*)191; break;
@@ -3475,18 +3560,19 @@ void asCContext::ExecuteNext()
 		asASSERT(false);
 		SetInternalException(TXT_UNRECOGNIZED_BYTE_CODE);
 #endif
-/*
+#if defined(_MSC_VER) && !defined(AS_DEBUG)
 	default:
 		// This Microsoft specific code allows the
 		// compiler to optimize the switch case as
 		// it will know that the code will never
 		// reach this point
 		__assume(0);
-*/	}
+#endif
+	}
 
 #ifdef AS_DEBUG
 		asDWORD instr = *(asBYTE*)old;
-		if( instr != asBC_JMP && instr != asBC_JMPP && (instr < asBC_JZ || instr > asBC_JNP) &&
+		if( instr != asBC_JMP && instr != asBC_JMPP && (instr < asBC_JZ || instr > asBC_JNP) && instr != asBC_JLowZ && instr != asBC_JLowNZ &&
 			instr != asBC_CALL && instr != asBC_CALLBND && instr != asBC_CALLINTF && instr != asBC_RET && instr != asBC_ALLOC && instr != asBC_CallPtr && 
 			instr != asBC_JitEntry )
 		{
@@ -3749,7 +3835,7 @@ void asCContext::CleanStackFrame()
 		for( asUINT n = 0; n < currentFunction->objVariablePos.GetLength(); n++ )
 		{
 			int pos = currentFunction->objVariablePos[n];
-			if( currentFunction->objVariableIsOnHeap[n] )
+			if( n < currentFunction->objVariablesOnHeap )
 			{
 				// Check if the pointer is initialized
 				if( *(asPWORD*)&regs.stackFramePointer[-pos] )
@@ -3793,11 +3879,15 @@ void asCContext::CleanStackFrame()
 		// the function has actually been entered
 		if( currentFunction->objectType && regs.programPointer != currentFunction->byteCode.AddressOf() )
 		{
-			asSTypeBehaviour *beh = &currentFunction->objectType->beh;
-			if( beh->release && *(asPWORD*)&regs.stackFramePointer[0] != 0 )
+			// Methods returning a reference or constructors don't add a reference
+			if( !currentFunction->returnType.IsReference() && currentFunction->name != currentFunction->objectType->name )
 			{
-				engine->CallObjectMethod((void*)*(asPWORD*)&regs.stackFramePointer[0], beh->release);
-				*(asPWORD*)&regs.stackFramePointer[0] = 0;
+				asSTypeBehaviour *beh = &currentFunction->objectType->beh;
+				if( beh->release && *(asPWORD*)&regs.stackFramePointer[0] != 0 )
+				{
+					engine->CallObjectMethod((void*)*(asPWORD*)&regs.stackFramePointer[0], beh->release);
+					*(asPWORD*)&regs.stackFramePointer[0] = 0;
+				}
 			}
 		}
 	}
@@ -4131,7 +4221,7 @@ void *asCContext::GetAddressOfVar(asUINT varIndex, asUINT stackLevel)
 			{
 				if( func->objVariablePos[n] == pos )
 				{
-					onHeap = func->objVariableIsOnHeap[n];
+					onHeap = n < func->objVariablesOnHeap;
 
 					if( !onHeap )
 					{

+ 1 - 0
ThirdParty/AngelScript/source/as_context.h

@@ -144,6 +144,7 @@ public:
 	void PopCallState();
 	void CallScriptFunction(asCScriptFunction *func);
 	void CallInterfaceMethod(asCScriptFunction *func);
+	void PrepareScriptFunction();
 
 	void SetInternalException(const char *descr);
 

+ 3 - 1
ThirdParty/AngelScript/source/as_criticalsection.h

@@ -48,7 +48,9 @@ BEGIN_AS_NAMESPACE
 #define DECLARECRITICALSECTION(x) 
 #define ENTERCRITICALSECTION(x) 
 #define LEAVECRITICALSECTION(x) 
-#define TRYENTERCRITICALSECTION(x) true
+
+inline bool tryEnter() { return true; }
+#define TRYENTERCRITICALSECTION(x) tryEnter()
 
 #else
 

+ 2 - 2
ThirdParty/AngelScript/source/as_generic.cpp

@@ -42,8 +42,8 @@
 
 BEGIN_AS_NAMESPACE
 
-// TODO: optimize: The access to the arguments should be optimized so that code 
-//                 doesn't have to count the position of the argument with every call
+// TODO: runtime optimize: The access to the arguments should be optimized so that code 
+//                         doesn't have to count the position of the argument with every call
 
 // internal
 asCGeneric::asCGeneric(asCScriptEngine *engine, asCScriptFunction *sysFunction, void *currentObject, asDWORD *stackPointer)

+ 2 - 4
ThirdParty/AngelScript/source/as_module.cpp

@@ -671,8 +671,7 @@ const char *asCModule::GetGlobalVarDeclaration(asUINT index, bool includeNamespa
 
 	asCGlobalProperty *prop = scriptGlobals[index];
 
-	asASSERT(threadManager);
-	asCString *tempString = &threadManager->GetLocalData()->string;
+	asCString *tempString = &asCThreadManager::GetLocalData()->string;
 	*tempString = prop->type.Format();
 	*tempString += " ";
 	if( includeNamespace )
@@ -979,8 +978,7 @@ const char *asCModule::GetImportedFunctionDeclaration(asUINT index) const
 	asCScriptFunction *func = GetImportedFunction(index);
 	if( func == 0 ) return 0;
 
-	asASSERT(threadManager);
-	asCString *tempString = &threadManager->GetLocalData()->string;
+	asCString *tempString = &asCThreadManager::GetLocalData()->string;
 	*tempString = func->GetDeclarationStr();
 
 	return tempString->AddressOf();

+ 1 - 2
ThirdParty/AngelScript/source/as_objecttype.cpp

@@ -521,8 +521,7 @@ const char *asCObjectType::GetPropertyDeclaration(asUINT index) const
 	if( index >= properties.GetLength() )
 		return 0;
 
-	asASSERT(threadManager);
-	asCString *tempString = &threadManager->GetLocalData()->string;
+	asCString *tempString = &asCThreadManager::GetLocalData()->string;
 	if( properties[index]->isPrivate )
 		*tempString = "private ";
 	else

+ 85 - 97
ThirdParty/AngelScript/source/as_restore.cpp

@@ -536,6 +536,8 @@ asCScriptFunction *asCReader::ReadFunction(bool addToModule, bool addToEngine, b
 		
 		ReadByteCode(func);
 
+		func->variableSpace = ReadEncodedUInt();
+
 		count = ReadEncodedUInt();
 		func->objVariablePos.Allocate(count, 0);
 		func->objVariableTypes.Allocate(count, 0);
@@ -547,9 +549,11 @@ asCScriptFunction *asCReader::ReadFunction(bool addToModule, bool addToEngine, b
 			func->funcVariableTypes.PushLast((asCScriptFunction*)(asPWORD)idx);
 			num = ReadEncodedUInt();
 			func->objVariablePos.PushLast(num);
-			bool b; ReadData(&b, 1);
-			func->objVariableIsOnHeap.PushLast(b);
 		}
+		if( count > 0 )
+			func->objVariablesOnHeap = ReadEncodedUInt();
+		else
+			func->objVariablesOnHeap = 0;
 
 		int length = ReadEncodedUInt();
 		func->objVariableInfo.SetLength(length);
@@ -871,10 +875,15 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase)
 							// TODO: Write message
 							error = true;
 						}
-						// Destroy the function without releasing any references
-						func->id = 0;
-						func->byteCode.SetLength(0);
-						func->Release();
+						// If the function received wasn't an already existing 
+						// function we must now destroy it
+						if( !savedFunctions.Exists(func) )
+						{
+							// Destroy the function without releasing any references
+							func->id = 0;
+							func->byteCode.SetLength(0);
+							func->Release();
+						}
 					}
 					else
 					{
@@ -920,10 +929,15 @@ void asCReader::ReadObjectTypeDeclaration(asCObjectType *ot, int phase)
 							// TODO: Write message
 							error = true;
 						}
-						// Destroy the function without releasing any references
-						func->id = 0;
-						func->byteCode.SetLength(0);
-						func->Release();
+						// If the function received wasn't an already existing 
+						// function we must now destroy it
+						if( !savedFunctions.Exists(func) )
+						{
+							// Destroy the function without releasing any references
+							func->id = 0;
+							func->byteCode.SetLength(0);
+							func->Release();
+						}
 					}
 					else
 					{
@@ -1693,7 +1707,9 @@ void asCReader::TranslateFunction(asCScriptFunction *func)
 	{
 		int c = *(asBYTE*)&bc[n];
 		if( c == asBC_FREE ||
-			c == asBC_REFCPY || c == asBC_OBJTYPE )
+			c == asBC_REFCPY || 
+			c == asBC_RefCpyV ||
+			c == asBC_OBJTYPE )
 		{
 			// Translate the index to the true object type
 			asPWORD *ot = (asPWORD*)&bc[n+1];
@@ -1751,10 +1767,6 @@ void asCReader::TranslateFunction(asCScriptFunction *func)
 			if( func->objectType ) dw += AS_PTR_SIZE;
 			asBC_WORDARG0(&bc[n]) = dw;
 		}
-		else if( c == asBC_POP )
-		{
-			asBC_WORDARG0(&bc[n]) = AS_PTR_SIZE;
-		}
 		else if( c == asBC_CALL ||
 				 c == asBC_CALLINTF ||
 				 c == asBC_CALLSYS )
@@ -1836,13 +1848,14 @@ void asCReader::TranslateFunction(asCScriptFunction *func)
 				return;
 			}
 		}
-		else if( c == asBC_PGA ||
-			     c == asBC_LDG ||
-				 c == asBC_PshG4 ||
-				 c == asBC_LdGRdR4 ||
+		else if( c == asBC_PGA      ||
+			     c == asBC_PshGPtr  ||
+			     c == asBC_LDG      ||
+				 c == asBC_PshG4    ||
+				 c == asBC_LdGRdR4  ||
 				 c == asBC_CpyGtoV4 ||
 				 c == asBC_CpyVtoG4 ||
-				 c == asBC_SetG4 )
+				 c == asBC_SetG4    )
 		{
 			// Translate the global var index to pointer
 			asPWORD *index = (asPWORD*)&bc[n+1];
@@ -1855,13 +1868,15 @@ void asCReader::TranslateFunction(asCScriptFunction *func)
 				return;
 			}
 		}
-		else if( c == asBC_JMP ||
-			     c == asBC_JZ ||
-				 c == asBC_JNZ ||
-				 c == asBC_JS ||
-				 c == asBC_JNS ||
-				 c == asBC_JP ||
-				 c == asBC_JNP ) // The JMPP instruction doesn't need modification
+		else if( c == asBC_JMP    ||
+			     c == asBC_JZ     ||
+				 c == asBC_JNZ    ||
+			     c == asBC_JLowZ  ||
+				 c == asBC_JLowNZ ||
+				 c == asBC_JS     ||
+				 c == asBC_JNS    ||
+				 c == asBC_JP     ||
+				 c == asBC_JNP    ) // The JMPP instruction doesn't need modification
 		{
 			// Get the offset 
 			int offset = int(bc[n+1]);
@@ -1929,19 +1944,12 @@ void asCReader::TranslateFunction(asCScriptFunction *func)
 			break;
 		}
 
-		if( c == asBC_PUSH )
-		{
-			// TODO: Maybe the push instruction should be removed, and be kept in 
-			//       the asCScriptFunction as a property instead. CallScriptFunction 
-			//       can immediately reserve the space
-
-			// PUSH is only used to reserve stack space for variables
-			asBC_SWORDARG0(&bc[n]) = (short)AdjustStackPosition(asBC_SWORDARG0(&bc[n]));
-		}
-
 		n += asBCTypeSize[asBCInfo[c].type];
 	}
 
+	// Adjust the space needed for local variables
+	func->variableSpace = AdjustStackPosition(func->variableSpace);
+
 	// Adjust the variable information. This will be used during the adjustment below
 	for( n = 0; n < func->variables.GetLength(); n++ )
 	{
@@ -2006,10 +2014,10 @@ void asCReader::CalculateStackNeeded(asCScriptFunction *func)
 	memset(&stackSize[0], -1, stackSize.GetLength()*4);
 
 	// Add the first instruction to the list of unchecked code 
-	// paths and set the stack size at that instruction to 0
+	// paths and set the stack size at that instruction to variableSpace
 	asCArray<asUINT> paths;
 	paths.PushLast(0);
-	stackSize[0] = 0;
+	stackSize[0] = func->variableSpace;
 
 	// Go through each of the code paths
 	for( asUINT p = 0; p < paths.GetLength(); ++p )
@@ -2026,27 +2034,21 @@ void asCReader::CalculateStackNeeded(asCScriptFunction *func)
 		if( stackInc == 0xFFFF )
 		{
 			// Determine the true delta from the instruction arguments
-			if( bc == asBC_POP )
-			{
-				stackInc = -(int)asBC_WORDARG0(&func->byteCode[pos]);
-			}
-			else if( bc == asBC_PUSH )
-			{
-				stackInc = asBC_WORDARG0(&func->byteCode[pos]);
-			}
-			else if( bc == asBC_CALL ||
-			         bc == asBC_CALLSYS ||
-					 bc == asBC_CALLBND ||
-					 bc == asBC_ALLOC ||
-					 bc == asBC_CALLINTF )
+			if( bc == asBC_CALL ||
+			    bc == asBC_CALLSYS ||
+				bc == asBC_CALLBND ||
+				bc == asBC_ALLOC ||
+				bc == asBC_CALLINTF ||
+				bc == asBC_CallPtr )
 			{
 				asCScriptFunction *called = GetCalledFunction(func, pos);
 				if( called )
 				{
 					stackInc = -called->GetSpaceNeededForArguments();
-
 					if( called->objectType )
 						stackInc -= AS_PTR_SIZE;
+					if( called->DoesReturnOnStack() )
+						stackInc -= AS_PTR_SIZE;
 				}
 				else
 				{
@@ -2055,11 +2057,6 @@ void asCReader::CalculateStackNeeded(asCScriptFunction *func)
 					stackInc = -AS_PTR_SIZE;
 				}
 			}
-			else if ( bc == asBC_CallPtr )
-			{
-				// TODO: bytecode: Determine the function signature from the variable
-				break;
-			}
 		}
 		
 		currStackSize += stackInc;
@@ -2084,9 +2081,10 @@ void asCReader::CalculateStackNeeded(asCScriptFunction *func)
 				asASSERT(stackSize[pos] == currStackSize);
 			continue;
 		}
-		else if( bc == asBC_JZ || bc == asBC_JNZ ||
-				 bc == asBC_JS || bc == asBC_JNS ||
-				 bc == asBC_JP || bc == asBC_JNP )
+		else if( bc == asBC_JZ    || bc == asBC_JNZ    ||
+				 bc == asBC_JLowZ || bc == asBC_JLowNZ ||
+				 bc == asBC_JS    || bc == asBC_JNS    ||
+				 bc == asBC_JP    || bc == asBC_JNP )
 		{
 			// Find the label that is being jumped to
 			int offset = asBC_INTARG(&func->byteCode[pos]);
@@ -2207,7 +2205,7 @@ void asCReader::CalculateAdjustmentByPos(asCScriptFunction *func)
 			// Determine the size the variable currently occupies on the stack
 			int size = AS_PTR_SIZE;
 			if( (func->objVariableTypes[n]->GetFlags() & asOBJ_VALUE) &&
-				!func->objVariableIsOnHeap[n] )
+				n >= func->objVariablesOnHeap )
 			{
 				size = func->objVariableTypes[n]->GetSize();
 				if( size < 4 ) 
@@ -2663,6 +2661,9 @@ void asCWriter::WriteFunction(asCScriptFunction* func)
 
 		WriteByteCode(func);
 
+		asDWORD varSpace = AdjustStackPosition(func->variableSpace);
+		WriteEncodedInt64(varSpace);
+
 		count = (asUINT)func->objVariablePos.GetLength();
 		WriteEncodedInt64(count);
 		for( i = 0; i < count; ++i )
@@ -2671,8 +2672,9 @@ void asCWriter::WriteFunction(asCScriptFunction* func)
 			// TODO: Only write this if the object type is the builtin function type
 			WriteEncodedInt64(FindFunctionIndex(func->funcVariableTypes[i]));
 			WriteEncodedInt64(AdjustStackPosition(func->objVariablePos[i]));
-			WriteData(&func->objVariableIsOnHeap[i], 1);
 		}
+		if( count > 0 )
+			WriteEncodedInt64(func->objVariablesOnHeap);
 
 		WriteEncodedInt64((asUINT)func->objVariableInfo.GetLength());
 		for( i = 0; i < func->objVariableInfo.GetLength(); ++i )
@@ -3110,7 +3112,7 @@ void asCWriter::CalculateAdjustmentByPos(asCScriptFunction *func)
 			// Determine the size the variable currently occupies on the stack
 			int size = AS_PTR_SIZE;
 			if( (func->objVariableTypes[n]->GetFlags() & asOBJ_VALUE) &&
-				!func->objVariableIsOnHeap[n] )
+				n >= func->objVariablesOnHeap )
 			{
 				size = func->objVariableTypes[n]->GetSize();
 				if( size < 4 ) 
@@ -3303,9 +3305,10 @@ void asCWriter::WriteByteCode(asCScriptFunction *func)
 			if( ot->flags & asOBJ_SCRIPT_OBJECT )
 				*(int*)&tmp[1+AS_PTR_SIZE] = FindFunctionIndex(engine->scriptFunctions[*(int*)&tmp[1+AS_PTR_SIZE]]);
 		}
-		else if( c == asBC_FREE   || // wW_PTR_ARG
-			     c == asBC_REFCPY || // PTR_ARG
-				 c == asBC_OBJTYPE ) // PTR_ARG
+		else if( c == asBC_FREE    || // wW_PTR_ARG
+			     c == asBC_REFCPY  || // PTR_ARG
+				 c == asBC_RefCpyV || // wW_PTR_ARG
+				 c == asBC_OBJTYPE )  // PTR_ARG
 		{
 			// Translate object type pointers into indices
 			*(int*)(tmp+1) = FindObjectTypeIdx(*(asCObjectType**)(tmp+1));
@@ -3352,14 +3355,6 @@ void asCWriter::WriteByteCode(asCScriptFunction *func)
 			// Save with arg 0, as this will be recalculated on the target platform
 			asBC_WORDARG0(tmp) = 0;
 		}
-		else if( c == asBC_POP ) // W_ARG
-		{
-			// The pop instruction always pop a single pointer
-			asASSERT(asBC_WORDARG0(tmp) == AS_PTR_SIZE);
-			
-			// Save with arg 0, as this will be recalculated on the target platform
-			asBC_WORDARG0(tmp) = 0;
-		}
 		else if( c == asBC_CALL ||     // DW_ARG
 				 c == asBC_CALLINTF || // DW_ARG
 				 c == asBC_CALLSYS )   // DW_ARG
@@ -3391,24 +3386,27 @@ void asCWriter::WriteByteCode(asCScriptFunction *func)
 
 			tmp[1] = funcId;
 		}
-		else if( c == asBC_PGA ||      // PTR_ARG
-			     c == asBC_LDG ||      // PTR_ARG
-				 c == asBC_PshG4 ||    // PTR_ARG
-				 c == asBC_LdGRdR4 ||  // wW_PTR_ARG
+		else if( c == asBC_PGA      || // PTR_ARG
+			     c == asBC_PshGPtr  || // PTR_ARG 
+			     c == asBC_LDG      || // PTR_ARG
+				 c == asBC_PshG4    || // PTR_ARG
+				 c == asBC_LdGRdR4  || // wW_PTR_ARG
 				 c == asBC_CpyGtoV4 || // wW_PTR_ARG
 				 c == asBC_CpyVtoG4 || // rW_PTR_ARG
-				 c == asBC_SetG4 )     // PTR_DW_ARG
+				 c == asBC_SetG4    )  // PTR_DW_ARG
 		{
 			// Translate global variable pointers into indices
 			*(int*)(tmp+1) = FindGlobalPropPtrIndex(*(void**)(tmp+1));
 		}
-		else if( c == asBC_JMP ||	// DW_ARG
-			     c == asBC_JZ ||
-				 c == asBC_JNZ ||
-				 c == asBC_JS ||
-				 c == asBC_JNS ||
-				 c == asBC_JP ||
-				 c == asBC_JNP ) // The JMPP instruction doesn't need modification
+		else if( c == asBC_JMP    ||	// DW_ARG
+			     c == asBC_JZ     ||
+				 c == asBC_JNZ    ||
+				 c == asBC_JLowZ  ||
+				 c == asBC_JLowNZ ||
+				 c == asBC_JS     ||
+				 c == asBC_JNS    ||
+				 c == asBC_JP     ||
+				 c == asBC_JNP    ) // The JMPP instruction doesn't need modification
 		{
 			// Get the DWORD offset from arg
 			int offset = *(int*)(tmp+1);
@@ -3467,16 +3465,6 @@ void asCWriter::WriteByteCode(asCScriptFunction *func)
 			break;
 		}
 
-		if( c == asBC_PUSH )
-		{
-			// TODO: Maybe the push instruction should be removed, and be kept in 
-			//       the asCScriptFunction as a property instead. CallScriptFunction 
-			//       can immediately reserve the space
-
-			// PUSH is only used to reserve stack space for variables
-			asBC_WORDARG0(tmp) = (asWORD)AdjustStackPosition(asBC_WORDARG0(tmp));
-		}
-				 
 		// TODO: bytecode: Must make sure that floats and doubles are always stored the same way regardless of platform. 
 		//                 Some platforms may not use the IEEE 754 standard, in which case it is necessary to encode the values
 		

+ 6 - 13
ThirdParty/AngelScript/source/as_scriptengine.cpp

@@ -384,13 +384,7 @@ asPWORD asCScriptEngine::GetEngineProperty(asEEngineProp property) const
 
 asCScriptEngine::asCScriptEngine() 
 {
-	// Instanciate the thread manager
-	ENTERCRITICALSECTION(engineCritical);
-	if( threadManager == 0 )
-		threadManager = asNEW(asCThreadManager);
-	else
-		threadManager->AddRef();
-	LEAVECRITICALSECTION(engineCritical);
+	asCThreadManager::AddRef();
 
 	// Engine properties
 	{
@@ -672,8 +666,7 @@ asCScriptEngine::~asCScriptEngine()
 	if( userData && cleanEngineFunc )
 		cleanEngineFunc(this);
 
-	// Release the thread manager
-	threadManager->Release();
+	asCThreadManager::Release();
 }
 
 // interface
@@ -2968,6 +2961,7 @@ asCScriptFunction *asCScriptEngine::GenerateTemplateFactoryStub(asCObjectType *t
 		}
 		func->inOutFlags[p-1] = factory->inOutFlags[p];
 	}
+	func->objVariablesOnHeap = 0;
 
 	SetScriptFunction(func);
 
@@ -3493,7 +3487,7 @@ void asCScriptEngine::GCEnumCallback(void *reference)
 
 
 // TODO: multithread: The mapTypeIdToDataType must be protected with critical sections in all functions that access it
-/// Urho3D: modified for type id caching
+// Urho3D: modified for type id caching
 int asCScriptEngine::GetTypeIdFromDataType(const asCDataType &dtIn) const
 {
 	if( dtIn.IsNullHandle() ) return 0;
@@ -3617,8 +3611,7 @@ const char *asCScriptEngine::GetTypeDeclaration(int typeId, bool includeNamespac
 {
 	asCDataType dt = GetDataTypeFromTypeId(typeId);
 
-	asASSERT(threadManager);
-	asCString *tempString = &threadManager->GetLocalData()->string;
+	asCString *tempString = &asCThreadManager::GetLocalData()->string;
 	*tempString = dt.Format(includeNamespace);
 
 	return tempString->AddressOf();
@@ -3685,7 +3678,7 @@ void asCScriptEngine::ConstructScriptObjectCopy(void *mem, void *obj, asCObjectT
 	// This function is only meant to be used for value types
 	asASSERT( type->flags & asOBJ_VALUE );
 
-	// TODO: optimize: Should use the copy constructor when available
+	// TODO: runtime optimize: Should use the copy constructor when available
 	int funcIndex = type->beh.construct;
 	if( funcIndex )
 		CallObjectMethod(mem, funcIndex);

+ 15 - 9
ThirdParty/AngelScript/source/as_scriptfunction.cpp

@@ -148,11 +148,12 @@ asCScriptFunction::asCScriptFunction(asCScriptEngine *engine, asCModule *mod, as
 	id                     = 0;
 	accessMask             = 0xFFFFFFFF;
 	isShared               = false;
+	variableSpace          = 0;
 
-	// TODO: optimize: The engine could notify the GC just before it wants to
-	//                 discard the function. That way the GC won't waste time
-	//                 trying to determine if the functions are garbage before
-	//                 they can actually be considered garbage.
+	// TODO: runtime optimize: The engine could notify the GC just before it wants to
+	//                         discard the function. That way the GC won't waste time
+	//                         trying to determine if the functions are garbage before
+	//                         they can actually be considered garbage.
 	// Notify the GC of script functions
 	if( funcType == asFUNC_SCRIPT )
 		engine->gc.AddScriptObjectToGC(this, &engine->functionBehaviours);
@@ -194,7 +195,7 @@ void asCScriptFunction::DestroyInternal()
 	// Release all references the function holds to other objects
 	ReleaseReferences();
 	parameterTypes.SetLength(0);
-	returnType == asCDataType::CreatePrimitive(ttVoid, false);
+	returnType = asCDataType::CreatePrimitive(ttVoid, false);
 	byteCode.SetLength(0);
 
 	for( asUINT n = 0; n < variables.GetLength(); n++ )
@@ -488,8 +489,7 @@ const char *asCScriptFunction::GetVarDecl(asUINT index) const
 	if( index >= variables.GetLength() )
 		return 0;
 
-	asASSERT(threadManager);
-	asCString *tempString = &threadManager->GetLocalData()->string;
+	asCString *tempString = &asCThreadManager::GetLocalData()->string;
 	*tempString = variables[index]->type.Format();
 	*tempString += " " + variables[index]->name;
 
@@ -597,6 +597,7 @@ void asCScriptFunction::AddReferences()
 		case asBC_OBJTYPE:
 		case asBC_FREE:
 		case asBC_REFCPY:
+		case asBC_RefCpyV:
 			{
                 asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&byteCode[n]);
 				objType->AddRef();
@@ -617,6 +618,7 @@ void asCScriptFunction::AddReferences()
 
 		// Global variables
 		case asBC_PGA:
+		case asBC_PshGPtr:
 		case asBC_LDG:
 		case asBC_PshG4:
 		case asBC_LdGRdR4:
@@ -704,6 +706,7 @@ void asCScriptFunction::ReleaseReferences()
 		case asBC_OBJTYPE:
 		case asBC_FREE:
 		case asBC_REFCPY:
+		case asBC_RefCpyV:
 			{
 				asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&byteCode[n]);
 				if( objType ) 
@@ -736,6 +739,7 @@ void asCScriptFunction::ReleaseReferences()
 
 		// Global variables
 		case asBC_PGA:
+		case asBC_PshGPtr:
 		case asBC_LDG:
 		case asBC_PshG4:
 		case asBC_LdGRdR4:
@@ -843,8 +847,7 @@ asIScriptEngine *asCScriptFunction::GetEngine() const
 // interface
 const char *asCScriptFunction::GetDeclaration(bool includeObjectName, bool includeNamespace) const
 {
-	asASSERT(threadManager);
-	asCString *tempString = &threadManager->GetLocalData()->string;
+	asCString *tempString = &asCThreadManager::GetLocalData()->string;
 	*tempString = GetDeclarationStr(includeObjectName, includeNamespace);
 	return tempString->AddressOf();
 }
@@ -979,6 +982,7 @@ void asCScriptFunction::EnumReferences(asIScriptEngine *)
 		case asBC_OBJTYPE:
 		case asBC_FREE:
 		case asBC_REFCPY:
+		case asBC_RefCpyV:
 			{
                 asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&byteCode[n]);
 				engine->GCEnumCallback(objType);
@@ -1016,6 +1020,7 @@ void asCScriptFunction::EnumReferences(asIScriptEngine *)
 
 		// Global variables
 		case asBC_PGA:
+		case asBC_PshGPtr:
 		case asBC_LDG:
 		case asBC_PshG4:
 		case asBC_LdGRdR4:
@@ -1068,6 +1073,7 @@ void asCScriptFunction::ReleaseAllHandles(asIScriptEngine *)
 		case asBC_OBJTYPE:
 		case asBC_FREE:
 		case asBC_REFCPY:
+		case asBC_RefCpyV:
 			{
 				asCObjectType *objType = (asCObjectType*)asBC_PTRARG(&byteCode[n]);
 				if( objType )

+ 9 - 5
ThirdParty/AngelScript/source/as_scriptfunction.h

@@ -86,10 +86,10 @@ struct asSSystemFunctionInterface;
 //       also functions/methods that are being called. This could be used to build a 
 //       code database with call graphs, etc.
 
-// TODO: optimize: The GC should only be notified of the script function when the last module
-//                 removes it from the scope. Must make sure it is only added to the GC once
-//                 in case the function is added to another module after the GC already knows 
-//                 about the function.
+// TODO: runtime optimize: The GC should only be notified of the script function when the last module
+//                         removes it from the scope. Must make sure it is only added to the GC once
+//                         in case the function is added to another module after the GC already knows 
+//                         about the function.
 
 void RegisterScriptFunction(asCScriptEngine *engine);
 
@@ -211,13 +211,17 @@ public:
 
 	// Used by asFUNC_SCRIPT
 	asCArray<asDWORD>               byteCode;
+	// The stack space needed for the local variables
+	asDWORD                         variableSpace;
 
 	// These hold information objects and function pointers, including temporary
 	// variables used by exception handler and when saving bytecode
 	asCArray<asCObjectType*>        objVariableTypes;
 	asCArray<asCScriptFunction*>    funcVariableTypes;
 	asCArray<int>	                objVariablePos;
-	asCArray<bool>                  objVariableIsOnHeap;
+	// The first variables in above array are allocated on the heap, the rest on the stack.
+	// This variable shows how many are on the heap.
+	asUINT                          objVariablesOnHeap;
 
 	// Holds information on scope for object variables on the stack
 	asCArray<asSObjectVariableInfo> objVariableInfo;

+ 2 - 0
ThirdParty/AngelScript/source/as_texts.h

@@ -46,6 +46,7 @@
 
 #define TXT_BOTH_MUST_BE_SAME                     "Both expressions must have the same type"
 #define TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR "Both conditions must call constructor"
+#define TEXT_BASE_DOESNT_HAVE_DEF_CONSTR          "Base class doesn't have default constructor. Make explicit call to base constructor"
 
 #define TXT_CANDIDATES_ARE                       "Candidates are:"
 #define TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS     "Can't call a constructor in loops"
@@ -168,6 +169,7 @@
 #define TXT_ONLY_ONE_ARGUMENT_IN_CAST      "A cast operator has one argument"
 #define TXT_ONLY_ONE_FUNCTION_ALLOWED      "The code must contain one and only one function"
 #define TXT_ONLY_ONE_VARIABLE_ALLOWED      "The code must contain one and only one global variable"
+#define TXT_OPERANDS_MUST_BE_HANDLES       "Both operands must be handles when comparing identity"
 
 #define TXT_PARAMETER_ALREADY_DECLARED            "Parameter already declared"
 #define TXT_PARAMETER_CANT_BE_s                   "Parameter type can't be '%s', because the type cannot be instanciated."

+ 72 - 40
ThirdParty/AngelScript/source/as_thread.cpp

@@ -1,6 +1,6 @@
 /*
    AngelCode Scripting Library
-   Copyright (c) 2003-2011 Andreas Jonsson
+   Copyright (c) 2003-2012 Andreas Jonsson
 
    This software is provided 'as-is', without any express or implied 
    warranty. In no event will the authors be held liable for any 
@@ -42,57 +42,74 @@
 
 BEGIN_AS_NAMESPACE
 
-// Singleton
-asCThreadManager *threadManager = 0;
-
 //======================================================================
 
 extern "C"
 {
 
+// Global API function
 AS_API int asThreadCleanup()
 {
-	// As this function can be called globally, 
-	// we can't assume the threadManager exists
-	if( threadManager )
-		return threadManager->CleanupLocalData();
-
-	return 0;
+	return asCThreadManager::CleanupLocalData();
 }
 
 }
 
+//=======================================================================
+
+// Singleton
+static asCThreadManager *threadManager = 0;
+
+#ifndef AS_NO_THREADS
+static DECLARECRITICALSECTION(criticalSection)
+#endif
+
 //======================================================================
 
 asCThreadManager::asCThreadManager()
 {
+	// We're already in the critical section when this function is called
+
 #ifdef AS_NO_THREADS
 	tld = 0;
 #endif
-	refCount.set(1);
+	refCount = 1;
 }
 
 void asCThreadManager::AddRef()
 {
-	refCount.atomicInc();
+	// It's necessary to protect this section to 
+	// avoid two threads attempting to create thread
+	// managers at the same time.
+	ENTERCRITICALSECTION(criticalSection);
+
+	if( threadManager == 0 )
+		threadManager = asNEW(asCThreadManager);
+	else
+		threadManager->refCount++;
+
+	LEAVECRITICALSECTION(criticalSection);
 }
 
 void asCThreadManager::Release()
 {
-	if( refCount.atomicDec() == 0 )
+	// It's necessary to protect this section so no
+	// other thread attempts to call AddRef or Release
+	// while clean up is in progress.
+	ENTERCRITICALSECTION(criticalSection);
+	if( --threadManager->refCount == 0 )
 	{
 		// The last engine has been destroyed, so we 
 		// need to delete the thread manager as well
-		asDELETE(this,asCThreadManager);
+		asDELETE(threadManager,asCThreadManager);
 		threadManager = 0;
 	}
+	LEAVECRITICALSECTION(criticalSection);
 }
 
 asCThreadManager::~asCThreadManager()
 {
 #ifndef AS_NO_THREADS
-	ENTERCRITICALSECTION(criticalSection);
-
 	// Delete all thread local datas
 	asSMapNode<asPWORD,asCThreadLocalData*> *cursor = 0;
 	if( tldMap.MoveFirst(&cursor) )
@@ -105,8 +122,6 @@ asCThreadManager::~asCThreadManager()
 			}
 		} while( tldMap.MoveNext(&cursor, cursor) );
 	}
-
-	LEAVECRITICALSECTION(criticalSection);
 #else
 	if( tld ) 
 	{
@@ -128,16 +143,22 @@ int asCThreadManager::CleanupLocalData()
 
 	ENTERCRITICALSECTION(criticalSection);
 
+	if( threadManager == 0 )
+	{
+		LEAVECRITICALSECTION(criticalSection);
+		return 0;
+	}
+
 	asSMapNode<asPWORD,asCThreadLocalData*> *cursor = 0;
-	if( tldMap.MoveTo(&cursor, id) )
+	if( threadManager->tldMap.MoveTo(&cursor, id) )
 	{
-		asCThreadLocalData *tld = tldMap.GetValue(cursor);
+		asCThreadLocalData *tld = threadManager->tldMap.GetValue(cursor);
 		
 		// Can we really remove it at this time?
 		if( tld->activeContexts.GetLength() == 0 )
 		{
 			asDELETE(tld,asCThreadLocalData);
-			tldMap.Erase(cursor);
+			threadManager->tldMap.Erase(cursor);
 			r = 0;
 		}
 		else
@@ -148,12 +169,12 @@ int asCThreadManager::CleanupLocalData()
 
 	return r;
 #else
-	if( tld )
+	if( threadManager && threadManager->tld )
 	{
-		if( tld->activeContexts.GetLength() == 0 )
+		if( threadManager->tld->activeContexts.GetLength() == 0 )
 		{
-			asDELETE(tld,asCThreadLocalData);
-			tld = 0;
+			asDELETE(threadManager->tld,asCThreadLocalData);
+			threadManager->tld = 0;
 		}
 		else
 			return asCONTEXT_ACTIVE;
@@ -165,26 +186,22 @@ int asCThreadManager::CleanupLocalData()
 #ifndef AS_NO_THREADS
 asCThreadLocalData *asCThreadManager::GetLocalData(asPWORD threadId)
 {
-	asCThreadLocalData *tld = 0;
+	// We're already in the critical section when this function is called
 
-	ENTERCRITICALSECTION(criticalSection);
+	asCThreadLocalData *tld = 0;
 
 	asSMapNode<asPWORD,asCThreadLocalData*> *cursor = 0;
-	if( tldMap.MoveTo(&cursor, threadId) )
-		tld = tldMap.GetValue(cursor);
-
-	LEAVECRITICALSECTION(criticalSection);
+	if( threadManager->tldMap.MoveTo(&cursor, threadId) )
+		tld = threadManager->tldMap.GetValue(cursor);
 
 	return tld;
 }
 
 void asCThreadManager::SetLocalData(asPWORD threadId, asCThreadLocalData *tld)
 {
-	ENTERCRITICALSECTION(criticalSection);
+	// We're already in the critical section when this function is called
 
 	tldMap.Insert(threadId, tld);
-
-	LEAVECRITICALSECTION(criticalSection);
 }
 #endif
 
@@ -196,21 +213,36 @@ asCThreadLocalData *asCThreadManager::GetLocalData()
 #elif defined AS_WINDOWS_THREADS
 	asPWORD id = (asPWORD)GetCurrentThreadId();
 #endif
-		
-	asCThreadLocalData *tld = GetLocalData(id);
+
+	ENTERCRITICALSECTION(criticalSection);
+
+	asASSERT(threadManager);
+
+	if( threadManager == 0 )
+	{
+		LEAVECRITICALSECTION(criticalSection);
+		return 0;
+	}
+
+	asCThreadLocalData *tld = threadManager->GetLocalData(id);
 	if( tld == 0 )
 	{
 		// Create a new tld
 		tld = asNEW(asCThreadLocalData)();
-		SetLocalData(id, tld);
+		threadManager->SetLocalData(id, tld);
 	}
 
+	LEAVECRITICALSECTION(criticalSection);
+
 	return tld;
 #else
-	if( tld == 0 )
-		tld = asNEW(asCThreadLocalData)();
+	if( threadManager == 0 )
+		return 0;
 
-	return tld;
+	if( threadManager->tld == 0 )
+		threadManager->tld = asNEW(asCThreadLocalData)();
+
+	return threadManager->tld;
 #endif
 }
 

+ 10 - 12
ThirdParty/AngelScript/source/as_thread.h

@@ -1,6 +1,6 @@
 /*
    AngelCode Scripting Library
-   Copyright (c) 2003-2008 Andreas Jonsson
+   Copyright (c) 2003-2012 Andreas Jonsson
 
    This software is provided 'as-is', without any express or implied 
    warranty. In no event will the authors be held liable for any 
@@ -43,7 +43,6 @@
 #include "as_string.h"
 #include "as_array.h"
 #include "as_map.h"
-#include "as_atomic.h"
 #include "as_criticalsection.h"
 
 BEGIN_AS_NAMESPACE
@@ -53,31 +52,30 @@ class asCThreadLocalData;
 class asCThreadManager
 {
 public:
-	asCThreadManager();
-
-	asCThreadLocalData *GetLocalData();
-	int CleanupLocalData();
+	static asCThreadLocalData *GetLocalData();
+	static int CleanupLocalData();
 
-	void AddRef();
-	void Release();
+	static void AddRef();
+	static void Release();
 
 protected:
+	asCThreadManager();
 	~asCThreadManager();
-	asCAtomic refCount;
+
+	// No need to use the atomic int here, as it will only be
+	// updated within the thread manager's critical section
+	int refCount;
 
 #ifndef AS_NO_THREADS
 	asCThreadLocalData *GetLocalData(asPWORD threadId);
 	void SetLocalData(asPWORD threadId, asCThreadLocalData *tld);
 
 	asCMap<asPWORD,asCThreadLocalData*> tldMap;
-	DECLARECRITICALSECTION(criticalSection)
 #else
 	asCThreadLocalData *tld;
 #endif
 };
 
-extern asCThreadManager *threadManager;
-
 //======================================================================
 
 class asIScriptContext;

+ 1 - 0
Urho3D/Urho3D.cpp

@@ -125,6 +125,7 @@ void Run()
             engine->Exit(); // Close the rendering window
             ErrorDialog("Urho3D", context->GetSubsystem<Log>()->GetLastMessage());
         }
+        scriptFile.Reset();
     }
     catch (std::bad_alloc&)
     {