From 9d9205d067bfb88bc9969f6a242d7d7eb8b904a7 Mon Sep 17 00:00:00 2001 From: chcurran <82187351+carlitosan@users.noreply.github.com> Date: Tue, 29 Jun 2021 18:42:32 -0700 Subject: [PATCH] SC user nodeables now use __index method does not report error on (allowable) nil table entries LYN-3664 Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com> --- .../AzCore/AzCore/Script/ScriptContext.cpp | 116 ++++++++++++++---- .../AzCore/Script/ScriptContextAttributes.h | 1 + .../Include/ScriptCanvas/Core/Nodeable.cpp | 1 + 3 files changed, 91 insertions(+), 27 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp index 0dd24261b8..d3d95b3078 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp +++ b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp @@ -689,6 +689,81 @@ namespace AZ return 1; } + int Class__IndexAllowNil(lua_State* l) + { + LSV_BEGIN(l, 1); + + // calling format __index(table,key) + lua_getmetatable(l, -2); // load the userdata metatable + int metaTableIndex = lua_gettop(l); + + // Check if the key is string, if so we expect it to be a function or property name + // otherwise we allow users to provide custom index handlers + // Technically we can allow strings too, but it will clash with function/property names and it be hard to figure + // out what is going on from with in the system. + if (lua_type(l, -2) == LUA_TSTRING) + { + lua_pushvalue(l, -2); // duplicate the key + lua_rawget(l, -2); // load the value at this index + } + else + { + lua_pushliteral(l, "__AZ_Index"); + lua_rawget(l, -2); // check if the user provided custom Index method in the class metatable + if (lua_isnil(l, -1)) // if not report an error + { + lua_rawgeti(l, -2, AZ_LUA_CLASS_METATABLE_NAME_INDEX); // load the class name for a better error + if (!lua_isstring(l, -1)) // if we failed it means we are the base metatable + { + lua_pop(l, 1); + lua_rawgeti(l, 1, AZ_LUA_CLASS_METATABLE_NAME_INDEX); + } + ScriptContext::FromNativeContext(l)->Error(ScriptContext::ErrorType::Warning, true, "Invalid index type [], should be string! '%s:%s'!", lua_tostring(l, -1), lua_tostring(l, -4)); + } + else + { + // if we have custom index handler + lua_pushvalue(l, -4); // duplicate the table (class pointer) + lua_pushvalue(l, -4); // duplicate the index value for the call + lua_call(l, 2, 1); // call the function + } + + lua_remove(l, metaTableIndex); // remove the metatable + return 1; + } + + if (!lua_isnil(l, -1)) + { + if (lua_tocfunction(l, -1) == &Internal::LuaPropertyTagHelper) // if it's a property + { + lua_getupvalue(l, -1, 1); // push on the stack the getter function + lua_remove(l, -2); // remove property object + + if (lua_isnil(l, -1)) + { + lua_rawgeti(l, -2, AZ_LUA_CLASS_METATABLE_NAME_INDEX); // load the class name for a better error + if (!lua_isstring(l, -1)) // if we failed it means we are the base metatable + { + lua_pop(l, 1); + lua_rawgeti(l, 1, AZ_LUA_CLASS_METATABLE_NAME_INDEX); + } + + ScriptContext::FromNativeContext(l)->Error(ScriptContext::ErrorType::Warning, true, "Property '%s:%s' is write only", lua_tostring(l, -1), lua_tostring(l, -4)); + lua_pop(l, 1); // pop class name + } + else + { + lua_pushvalue(l, -4); // copy the user data to be passed as a this pointer. + lua_call(l, 1, 1); // call a function with one argument (this pointer) and 1 result + } + } + } + + lua_remove(l, metaTableIndex); // remove the metatable + return 1; + } + + //========================================================================= // Class__NewIndex // [3/22/2012] @@ -826,30 +901,6 @@ namespace AZ return 1; } - //========================================================================= - // ClassMetatable__Index - // [3/24/2012] - //========================================================================= - int ClassMetatable__Index(lua_State* l) - { - // since the Class__Index is generic function (ask for the class metatable) - // we can reuse the code for the base metatable (which is a metatable - // of the class metatable) - return Class__Index(l); - } - - //========================================================================= - // ClassMetatable__NewIndex - // [3/30/2012] - //========================================================================= - int ClassMetatable__NewIndex(lua_State* l) - { - // since the Class__NewIndex is generic function (ask for the class metatable) - // we can reuse the code for the base metatable (which is a metatable - // of the class metatable) - return Class__NewIndex(l); - } - inline size_t BufferStringCopy(const char* source, char* destination, size_t destinationSize) { size_t srcLen = strlen(source); @@ -5053,9 +5104,20 @@ LUA_API const Node* lua_getDummyNode() lua_pushcclosure(m_lua, &DefaultBehaviorCaller::Destroy, 0); lua_rawset(m_lua, -3); - lua_pushliteral(m_lua, "__index"); - lua_pushcclosure(m_lua, &Internal::Class__Index, 0); - lua_rawset(m_lua, -3); + { + lua_pushliteral(m_lua, "__index"); + + if (FindAttribute(Script::Attributes::UseClassIndexAllowNil, behaviorClass->m_attributes)) + { + lua_pushcclosure(m_lua, &Internal::Class__IndexAllowNil, 0); + } + else + { + lua_pushcclosure(m_lua, &Internal::Class__Index, 0); + } + + lua_rawset(m_lua, -3); + } lua_pushliteral(m_lua, "__newindex"); lua_pushcclosure(m_lua, &Internal::Class__NewIndex, 0); diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptContextAttributes.h b/Code/Framework/AzCore/AzCore/Script/ScriptContextAttributes.h index 2970ee2e67..76018f3ed0 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptContextAttributes.h +++ b/Code/Framework/AzCore/AzCore/Script/ScriptContextAttributes.h @@ -29,6 +29,7 @@ namespace AZ const static AZ::Crc32 Deprecated = AZ_CRC("Deprecated", 0xfe49a138); ///< Marks a reflected class, method, EBus or property as deprecated. const static AZ::Crc32 DisallowBroadcast = AZ_CRC("DisallowBroadcast", 0x389b0ac7); ///< Marks a reflected EBus as not allowing Broadcasts, only Events. const static AZ::Crc32 ClassConstantValue = AZ_CRC_CE("ClassConstantValue"); ///< Indicates the property is backed by a constant value + const static AZ::Crc32 UseClassIndexAllowNil = AZ_CRC_CE("UseClassIndexAllowNil"); ///< Use the Class__IndexAllowNil method, which will not report an error on accessing undeclared values (allows for nil) //! Attribute which stores BehaviorAzEventDescription structure which contains //! the script name of an AZ::Event and the name of it's parameter arguments diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Nodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Nodeable.cpp index bdb8576d8f..868751d2a4 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Nodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Nodeable.cpp @@ -79,6 +79,7 @@ namespace ScriptCanvas behaviorContext->Class() ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List) ->Attribute(AZ::ScriptCanvasAttributes::VariableCreationForbidden, AZ::AttributeIsValid::IfPresent) + ->Attribute(AZ::Script::Attributes::UseClassIndexAllowNil, AZ::AttributeIsValid::IfPresent) ->Constructor() ->Method("Deactivate", &Nodeable::Deactivate) ->Method("InitializeExecutionState", &Nodeable::InitializeExecutionState)