-- Tencent is pleased to support the open source community by making xLua available. -- Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. -- Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -- http://opensource.org/licenses/MIT -- Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. local friendlyNameMap = { ["System.Object"] = "object", ["System.String"] = "string", ["System.Boolean"] = "bool", ["System.Byte"] = "byte", ["System.Char"] = "char", ["System.Decimal"] = "decimal", ["System.Double"] = "double", ["System.Int16"] = "short", ["System.Int32"] = "int", ["System.Int64"] = "long", ["System.SByte"] = "sbyte", ["System.Single"] = "float", ["System.UInt16"] = "ushort", ["System.UInt32"] = "uint", ["System.UInt64"] = "ulong", ["System.Void"] = "void", } local csKeywords = { "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while" } for _, kw in ipairs(csKeywords) do csKeywords[kw] = '@'..kw end for i = 1, #csKeywords do csKeywords[i] = nil end function UnK(symbol) return csKeywords[symbol] or symbol end local fixChecker = { --["System.String"] = "LuaAPI.lua_isstring", ["System.Boolean"] = "LuaTypes.LUA_TBOOLEAN == LuaAPI.lua_type", ["System.Byte"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.Char"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", --["System.Decimal"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.Double"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.Int16"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.Int32"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", --["System.Int64"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.SByte"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.Single"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.UInt16"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.UInt32"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", --["System.UInt64"] = "LuaTypes.LUA_TNUMBER == LuaAPI.lua_type", ["System.IntPtr"] = "LuaTypes.LUA_TLIGHTUSERDATA == LuaAPI.lua_type", } local typedCaster = { ["System.Byte"] = "LuaAPI.xlua_tointeger", ["System.Char"] = "LuaAPI.xlua_tointeger", ["System.Int16"] = "LuaAPI.xlua_tointeger", ["System.SByte"] = "LuaAPI.xlua_tointeger", ["System.Single"] = "LuaAPI.lua_tonumber", ["System.UInt16"] = "LuaAPI.xlua_tointeger", } local fixCaster = { ["System.Double"] = "LuaAPI.lua_tonumber", ["System.String"] = "LuaAPI.lua_tostring", ["System.Boolean"] = "LuaAPI.lua_toboolean", ["System.Byte[]"] = "LuaAPI.lua_tobytes", ["System.IntPtr"] = "LuaAPI.lua_touserdata", ["System.UInt32"] = "LuaAPI.xlua_touint", ["System.UInt64"] = "LuaAPI.lua_touint64", ["System.Int32"] = "LuaAPI.xlua_tointeger", ["System.Int64"] = "LuaAPI.lua_toint64", } local fixPush = { ["System.Byte"] = "LuaAPI.xlua_pushinteger", ["System.Char"] = "LuaAPI.xlua_pushinteger", ["System.Int16"] = "LuaAPI.xlua_pushinteger", ["System.Int32"] = "LuaAPI.xlua_pushinteger", ["System.Int64"] = "LuaAPI.lua_pushint64", ["System.SByte"] = "LuaAPI.xlua_pushinteger", ["System.Single"] = "LuaAPI.lua_pushnumber", ["System.UInt16"] = "LuaAPI.xlua_pushinteger", ["System.UInt32"] = "LuaAPI.xlua_pushuint", ["System.UInt64"] = "LuaAPI.lua_pushuint64", ["System.Single"] = "LuaAPI.lua_pushnumber", ["System.Double"] = "LuaAPI.lua_pushnumber", ["System.String"] = "LuaAPI.lua_pushstring", ["System.Byte[]"] = "LuaAPI.lua_pushstring", ["System.Boolean"] = "LuaAPI.lua_pushboolean", ["System.IntPtr"] = "LuaAPI.lua_pushlightuserdata", ["System.Decimal"] = "translator.PushDecimal", ["System.Object"] = "translator.PushAny", } local notranslator = { ["System.Byte"] = true, ["System.Char"] = true, ["System.Int16"] = true, ["System.Int32"] = true, ["System.Int64"] = true, ["System.SByte"] = true, ["System.Single"] = true, ["System.UInt16"] = true, ["System.UInt32"] = true, ["System.UInt64"] = true, ["System.Double"] = true, ["System.String"] = true, ["System.Boolean"] = true, ["System.Void"] = true, ["System.IntPtr"] = true, ["System.Byte[]"] = true, } function ForEachCsList(...) local list_count = select('#', ...) - 1 local callback = select(list_count + 1, ...) for i = 1, list_count do local list = select(i, ...) for i = 0, (list.Count or list.Length) - 1 do callback(list[i], i) end end end function CalcCsList(list, predicate) local count = 0 for i = 0, (list.Count or list.Length) - 1 do if predicate(list[i], i) then count = count + 1 end end return count end function IfAny(list, predicate) for i = 0, (list.Count or list.Length) - 1 do if predicate(list[i], i) then return true end end return false end local genPushAndUpdateTypes function SetGenPushAndUpdateTypes(list) genPushAndUpdateTypes = {} ForEachCsList(list, function(t) genPushAndUpdateTypes[t] = true end) end local xLuaClasses function SetXLuaClasses(list) xLuaClasses = {} ForEachCsList(list, function(t) xLuaClasses[t.Name] = true end) end local objType = typeof(CS.System.Object) local valueType = typeof(CS.System.ValueType) local function _CsFullTypeName(t) if t.IsArray then local element_name, element_is_array = _CsFullTypeName(t:GetElementType()) if element_is_array then local bracket_pos = element_name:find('%[') return element_name:sub(1, bracket_pos - 1) .. '[' .. string.rep(',', t:GetArrayRank() - 1) .. ']' .. element_name:sub(bracket_pos, -1), true else return element_name .. '[' .. string.rep(',', t:GetArrayRank() - 1) .. ']', true end elseif t.IsByRef then return _CsFullTypeName(t:GetElementType()) elseif t.IsGenericParameter then return (t.BaseType == objType or t.BaseType == valueType) and t.Name or _CsFullTypeName(t.BaseType) --TODO:应该判断是否类型约束 end local name = t.FullName:gsub("&", ""):gsub("%+", ".") if not t.IsGenericType then return friendlyNameMap[name] or name end local genericParameter = "" ForEachCsList(t:GetGenericArguments(), function(at, ati) if ati ~= 0 then genericParameter = genericParameter .. ', ' end genericParameter = genericParameter .. _CsFullTypeName(at) end) return name:gsub("`%d+", '<' .. genericParameter .. '>'):gsub("%[[^,%]].*", ""), false end function CsFullTypeName(t) if t.DeclaringType then local name = _CsFullTypeName(t) local declaringTypeName = _CsFullTypeName(t.DeclaringType); return xLuaClasses[declaringTypeName] and ("global::" .. name) or name else local name = _CsFullTypeName(t) return xLuaClasses[name] and ("global::" .. name) or name end end function CSVariableName(t) if t.IsArray then return CSVariableName(t:GetElementType()) .. '_'.. t:GetArrayRank() ..'_' end return t:ToString():gsub("&", ""):gsub("%+", ""):gsub("`", "_"):gsub("%.", ""):gsub("%[", "_"):gsub("%]", "_"):gsub(",", "") end local function getSafeFullName(t) if t == nil then return "" end if t.IsGenericParameter then return t.BaseType == objType and t.Name or getSafeFullName(t.BaseType) end if not t.FullName then return "" end return t.FullName:gsub("&", "") end function GetCheckStatement(t, idx, is_v_params) local cond_start = is_v_params and "(LuaTypes.LUA_TNONE == LuaAPI.lua_type(L, ".. idx ..") || " or "" local cond_end = is_v_params and ")" or "" local testname = getSafeFullName(t) if testname == "System.String" or testname == "System.Byte[]" then return cond_start .. "(LuaAPI.lua_isnil(L, " .. idx .. ") || LuaAPI.lua_type(L, ".. idx ..") == LuaTypes.LUA_TSTRING)" .. cond_end elseif testname == "System.Int64" then return cond_start .. "(LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, ".. idx ..") || LuaAPI.lua_isint64(L, ".. idx .."))" .. cond_end elseif testname == "System.UInt64" then return cond_start .. "(LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, ".. idx ..") || LuaAPI.lua_isuint64(L, ".. idx .."))" .. cond_end elseif testname == "System.Decimal" then return cond_start .. "(LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, ".. idx ..") || translator.IsDecimal(L, ".. idx .."))" .. cond_end elseif testname == "XLua.LuaTable" then return cond_start .. "(LuaAPI.lua_isnil(L, " .. idx .. ") || LuaAPI.lua_type(L, ".. idx ..") == LuaTypes.LUA_TTABLE)" .. cond_end elseif testname == "XLua.LuaFunction" then return cond_start .. "(LuaAPI.lua_isnil(L, " .. idx .. ") || LuaAPI.lua_type(L, ".. idx ..") == LuaTypes.LUA_TFUNCTION)" .. cond_end end return cond_start .. (fixChecker[testname] or ("translator.Assignable<" .. CsFullTypeName(t).. ">")) .. "(L, ".. idx ..")" .. cond_end end local delegateType = typeof(CS.System.Delegate) local ExtensionAttribute = typeof(CS.System.Runtime.CompilerServices.ExtensionAttribute) function IsExtensionMethod(method) return method:IsDefined(ExtensionAttribute, false) end function IsDelegate(t) return delegateType:IsAssignableFrom(t) end function MethodParameters(method) if not IsExtensionMethod(method) then return method:GetParameters() else local parameters = method:GetParameters() if parameters[0].ParameterType.IsInterface then return parameters end local ret = {} for i = 1, parameters.Length - 1 do ret[i - 1] = parameters[i] end ret.Length = parameters.Length - 1 return ret end end function IsStruct(t) if t.IsByRef then t = t:GetElementType() end return t.IsValueType and not t.IsPrimitive end function NeedUpdate(t) if t.IsByRef then t = t:GetElementType() end return t.IsValueType and not t.IsPrimitive and not t.IsEnum and t ~= typeof(CS.System.Decimal) end function GetCasterStatement(t, idx, var_name, need_declare, is_v_params) local testname = getSafeFullName(t) local statement = "" local is_struct = IsStruct(t) if need_declare then statement = CsFullTypeName(t) .. " " .. var_name if is_struct and not typedCaster[testname] and not fixCaster[testname] then statement = statement .. ";" else statement = statement .. " = " end elseif not is_struct then statement = var_name .. " = " end if is_v_params then return statement .. "translator.GetParams<" .. CsFullTypeName(t:GetElementType()).. ">" .. "(L, ".. idx ..")" elseif typedCaster[testname] then return statement .. "(" .. CsFullTypeName(t) .. ")" ..typedCaster[testname] .. "(L, ".. idx ..")" elseif IsDelegate(t) then return statement .. "translator.GetDelegate<" .. CsFullTypeName(t).. ">" .. "(L, ".. idx ..")" elseif fixCaster[testname] then return statement .. fixCaster[testname] .. "(L, ".. idx ..")" elseif testname == "System.Object" then return statement .. "translator.GetObject(L, ".. idx ..", typeof(" .. CsFullTypeName(t) .."))" elseif is_struct then return statement .. "translator.Get(L, ".. idx ..", out " .. var_name .. ")" elseif t.IsGenericParameter and not t.DeclaringMethod then return statement .. "translator.GetByType<"..t.Name..">(L, ".. idx ..")" else return statement .. "("..CsFullTypeName(t)..")translator.GetObject(L, ".. idx ..", typeof(" .. CsFullTypeName(t) .."))" end end local paramsAttriType = typeof(CS.System.ParamArrayAttribute) function IsParams(pi) if (not pi.IsDefined) then return pi.IsParamArray end return pi:IsDefined(paramsAttriType, false) end local obsoluteAttriType = typeof(CS.System.ObsoleteAttribute) function IsObsolute(f) return f:IsDefined(obsoluteAttriType, false) end local objectType = typeof(CS.System.Object) function GetSelfStatement(t) local fulltypename = CsFullTypeName(t) local is_struct = IsStruct(t) if is_struct then return fulltypename .. " gen_to_be_invoked;translator.Get(L, 1, out gen_to_be_invoked)" else if t == objectType then return "object gen_to_be_invoked = translator.FastGetCSObj(L, 1)" else return fulltypename .. " gen_to_be_invoked = (" .. fulltypename .. ")translator.FastGetCSObj(L, 1)" end end end local GetNullableUnderlyingType = CS.System.Nullable.GetUnderlyingType function GetPushStatement(t, variable, is_v_params) if is_v_params then local item_push = GetPushStatement(t:GetElementType(), variable..'[__gen_i]') return 'if ('.. variable ..' != null) { for (int __gen_i = 0; __gen_i < ' .. variable .. '.Length; ++__gen_i) ' .. item_push .. '; }' end if t.IsByRef then t = t:GetElementType() end local testname = getSafeFullName(t) if fixPush[testname] then return fixPush[testname] .. "(L, ".. variable ..")" elseif genPushAndUpdateTypes[t] then return "translator.Push".. CSVariableName(t) .."(L, "..variable..")" elseif t.IsGenericParameter and not t.DeclaringMethod then return "translator.PushByType(L, "..variable..")" elseif t.IsInterface or GetNullableUnderlyingType(t) then return "translator.PushAny(L, "..variable..")" else return "translator.Push(L, "..variable..")" end end function GetUpdateStatement(t, idx, variable) if t.IsByRef then t = t:GetElementType() end if typeof(CS.System.Decimal) == t then error('Decimal not update!') end if genPushAndUpdateTypes[t] then return "translator.Update".. CSVariableName(t) .."(L, ".. idx ..", "..variable..")" else return "translator.Update(L, ".. idx ..", "..variable..")" end end function JustLuaType(t) return notranslator[getSafeFullName(t)] end function CallNeedTranslator(overload, isdelegate) if not overload.IsStatic and not isdelegate then return true end local ret_type_name = getSafeFullName(overload.ReturnType) if not notranslator[ret_type_name] then return true end local parameters = overload:GetParameters() return IfAny(overload:GetParameters(), function(parameter) return IsParams(parameter) or (not notranslator[getSafeFullName(parameter.ParameterType)]) end) end function MethodCallNeedTranslator(method) return IfAny(method.Overloads, function(overload) return CallNeedTranslator(overload) end) end function AccessorNeedTranslator(accessor) return not accessor.IsStatic or not JustLuaType(accessor.Type) end function PushObjectNeedTranslator(type_info) return IfAny(type_info.FieldInfos, function(field_info) return not JustLuaType(field_info.Type) end) end local GenericParameterAttributes = CS.System.Reflection.GenericParameterAttributes local enum_and_op = debug.getmetatable(CS.System.Reflection.BindingFlags.Public).__band local has_generic_flag = function(f1, f2) return (f1 ~= GenericParameterAttributes.None) and (enum_and_op(f1, f2) == f2) end function GenericArgumentList(type) local generic_arg_list = "" local type_constraints = "" if type.IsGenericTypeDefinition then generic_arg_list = "<" local constraints = {} ForEachCsList(type:GetGenericArguments(), function(generic_arg, gai) local constraint = {} if gai ~= 0 then generic_arg_list = generic_arg_list .. ", " end generic_arg_list = generic_arg_list .. generic_arg.Name if has_generic_flag(generic_arg.GenericParameterAttributes, GenericParameterAttributes.ReferenceTypeConstraint) then table.insert(constraint, 'class') end if has_generic_flag(generic_arg.GenericParameterAttributes, GenericParameterAttributes.NotNullableValueTypeConstraint) then table.insert(constraint, 'struct') end ForEachCsList(generic_arg:GetGenericParameterConstraints(), function(gpc) if gpc ~= typeof(CS.System.ValueType) or not has_generic_flag(generic_arg.GenericParameterAttributes, GenericParameterAttributes.NotNullableValueTypeConstraint) then table.insert(constraint, CsFullTypeName(gpc)) end end) if not has_generic_flag(generic_arg.GenericParameterAttributes, GenericParameterAttributes.NotNullableValueTypeConstraint) and has_generic_flag(generic_arg.GenericParameterAttributes, GenericParameterAttributes.DefaultConstructorConstraint) then table.insert(constraint, 'new()') end if #constraint > 0 then table.insert(constraints, 'where ' .. generic_arg.Name .. ' : ' .. table.concat(constraint, ',')) end end) generic_arg_list = generic_arg_list .. ">" if #constraints > 0 then type_constraints = table.concat(constraints, ',') end end return generic_arg_list, type_constraints end function LocalName(name) return "_" .. name end