forked from Tencent/xLua
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0aeb337
commit 3aaf258
Showing
16 changed files
with
1,860 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
## What & Why | ||
|
||
XLua's currently built-in extension libraries: | ||
|
||
* LuaJIT support for 64-bit integers; | ||
* Positioning tool for function call times and memory leaks; | ||
* LuaSocket library for supporting ZeroBraneStudio; | ||
* tdr 4 lua; | ||
|
||
With the increasing extensiveness and intensiveness of project use, the current extension libraries have been unable to meet the project team needs. Since various projects require very different extension libraries, and since mobile phone platforms are sensitive to the size of the installation package, xLua is unable to meet these needs through pre-integration. That is why we are offering this tutorial. | ||
|
||
In this tutorial, we will use lua-rapidjson as an example to explain step by step how to add C/C++ extensions to xLua. Once you know how to add them, you will also know how to delete them naturally. The project team can delete those pre-integrated extensions if they are not used any more. | ||
|
||
## How it is done | ||
|
||
There are three steps: | ||
|
||
1. Modify the build file and project settings. Compile the extensions you want to integrate into the XLua Plugin directory. | ||
2. Call the C# APIs on xLua so that the extensions can be loaded as needed (when required in the Lua code). | ||
3. (Optional) If you need to use 64-bit integers in your extensions, you can use xLua's 64-bit extension library to work with C#. | ||
|
||
### First, add extensions & compile. | ||
|
||
Preparations | ||
|
||
1. Extract the xLua’s C source code package to the same level directory as the Assets of your Unity project. | ||
|
||
Download the lua-rapidjson code and place it anywhere you like. In this tutorial, we place the rapidjson header file in the $UnityProj\build\lua-rapidjson\include directory, and place the extended source code rapidjson.cpp in the $UnityProj\build\lua-rapidjson\source directory (Note: $UnityProj refers to your project directory). | ||
|
||
2. Add extensions to CMakeLists.txt | ||
|
||
xLua’s platform Plugins are compiled using CMake. The advantage of this is that the compilations of all platforms are written in a makefile, and most compilation processing logic is cross-platform. | ||
|
||
XLua's CMakeLists.txt provides extension points (all lists) for third-party extensions: | ||
1. THIRDPART_INC: Third-party extension header search path. | ||
2. THIRDPART_SRC: Third-party extended source code. | ||
3. THIRDPART_LIB: The library on which third-party extensions rely. | ||
|
||
The following is added with RapidJSON: | ||
|
||
#begin lua-rapidjson | ||
set (RAPIDJSON_SRC lua-rapidjson/source/rapidjson.cpp) | ||
set_property( | ||
SOURCE ${RAPIDJSON_SRC} | ||
APPEND | ||
PROPERTY COMPILE_DEFINITIONS | ||
LUA_LIB | ||
) | ||
list(APPEND THIRDPART_INC lua-rapidjson/include) | ||
set (THIRDPART_SRC ${THIRDPART_SRC} ${RAPIDJSON_SRC}) | ||
#end lua-rapidjson | ||
|
||
See the attachment for the complete code. | ||
|
||
3. Compile platforms | ||
|
||
All compiled scripts are named with this format: make_platform_lua version.extension name. | ||
|
||
For example, the name of the Windows 64-bit Lua 5.3 version is make_win64_lua53.bat, and the name of the Android LuaJIT version is make_android_luajit.sh. you can execute the corresponding script to compile the target version. | ||
|
||
The compiled scripts are automatically copied to the plugin_lua53 or plugin_luajit directory. The former is for Lua 5.3 and the latter for LuaJIT. | ||
|
||
The supporting Android script is used on Linux. The NDK path at the beginning of the script must be modified accordingly. | ||
|
||
### Second, C# side integration: | ||
|
||
Each C extension library on Lua will provide a function, luaopen_xxx, where xxx is the name of the dynamic library. For example, the function for the Lua-RapidJSON library is luaopen_rapidjson. Such functions are automatically called by the Lua virtual machine when loading the dynamic library. In the mobile platform, we cannot load the dynamic library due to iOS restrictions. They are compiled directly into the process instead. | ||
|
||
For this purpose, xLua provides an API to replace this feature (LuaEnv's member methods): | ||
|
||
public void AddBuildin(string name, LuaCSFunction initer) | ||
|
||
Parameters: | ||
|
||
Name: name of the buildin module, a parameter entered during require; | ||
initer: the initialization function; Its prototype is public delegate int lua_CSFunction(IntPtr L); This must be a static function and be modified with the property MonoPInvokeCallbackProperty; This API will check these two conditions. | ||
|
||
We use calling luaopen_rapidjson to show how to use it. | ||
|
||
Extend the LuaDLL.Lua type, export luaopen_rapidjson to C# via pinvoke, and then write a static function that satisfies the definition of lua_CSFunction. You can write initialization work in it, such as calling luaopen_rapidjson. Here is the complete code: | ||
|
||
namespace LuaDLL | ||
{ | ||
public partial class Lua | ||
{ | ||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)] | ||
public static extern int luaopen_rapidjson(System.IntPtr L); | ||
|
||
[MonoPInvokeCallback(typeof(LuaDLL.lua_CSFunction))] | ||
public static int LoadRapidJson(System.IntPtr L) | ||
{ | ||
return luaopen_rapidjson(L); | ||
} | ||
} | ||
} | ||
|
||
Then call AddBuildin: | ||
|
||
luaenv.AddBuildin("rapidjson", LuaDLL.Lua.LoadRapidJson); | ||
|
||
After this, it should work properly. Try the extension in the Lua code: | ||
|
||
local rapidjson = require('rapidjson') | ||
local t = rapidjson.decode('{"a":123}') | ||
print(t.a) | ||
t.a = 456 | ||
local s = rapidjson.encode(t) | ||
print('json', s) | ||
|
||
### Third, 64-bit transformation | ||
|
||
Include the i64lib.h file in a file that requires a 64-bit transformation. | ||
The header file include these APIs: | ||
|
||
//Place an int64 on stack/uint64 | ||
void lua_pushint64(lua_State* L, int64_t n); | ||
void lua_pushuint64(lua_State* L, uint64_t n); | ||
//Judge whether int64 is at the pos position on stack/uint64 | ||
int lua_isint64(lua_State* L, int pos); | ||
int lua_isuint64(lua_State* L, int pos); | ||
//Get an int64 from the pos position on stack/uint64 | ||
int64_t lua_toint64(lua_State* L, int pos); | ||
uint64_t lua_touint64(lua_State* L, int pos); | ||
|
||
The usage of these APIs varies depending on the actual situation. See the attached file (rapidjson.cpp file). | ||
|
||
Compile project related modifications | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
# XLua configuration | ||
|
||
All xLua configurations support three methods: tagging, static lists, and dynamic lists. | ||
|
||
There are two requirements and two recommended items for configuration: | ||
|
||
* List mode must use static fields/properties. | ||
* List mode must be placed in a static type. | ||
* Using tagging is not recommended. | ||
* Placing the list mode configuration in the Editor directory is recommended. | ||
|
||
**Tagging** | ||
|
||
xLua uses a whitelist to indicate which code is to be generated, and the whitelist is configured via attributes. For example, if you want to call a C# type from Lua or you want to generate the adaptation code, you can add a LuaCallCSharp tag for this type: | ||
|
||
~~~csharp | ||
[LuaCallCSharp] | ||
publicclassA | ||
{ | ||
|
||
} | ||
~~~ | ||
|
||
This mode is convenient, but it will increase the code on the il2cpp and therefore is not recommended. | ||
|
||
**Static list** | ||
|
||
Sometimes we cannot directly tag a type, such as a system API, a library without source code, or an instantiated generic type. In this case, you can declare a static field in a static type. This field can be any type except for BlackList and AdditionalProperties, as long as IEnumerable<Type> is implemented (these two exceptions will be specifically described later). Then add a tag to this field: | ||
|
||
~~~csharp | ||
[LuaCallCSharp] | ||
public static List<Type> mymodule_lua_call_cs_list = new List<Type>() | ||
{ | ||
typeof(GameObject), | ||
typeof(Dictionary<string, int>), | ||
}; | ||
~~~ | ||
|
||
This field needs to be placed in a **static type** and placing it in the **Editor directory** is recommended. | ||
|
||
**Dynamic list** | ||
|
||
Declare a static property and tag it accordingly. | ||
|
||
~~~csharp | ||
[Hotfix] | ||
public static List<Type> by_property | ||
{ | ||
get | ||
{ | ||
return (from type in Assembly.Load("Assembly-CSharp").GetTypes() | ||
where type.Namespace == "XXXX" | ||
select type).ToList(); | ||
} | ||
} | ||
~~~ | ||
|
||
Getter is code. You can use it to implement a lot of results, such as configuration by namespace, configuration by assembly, and so on. | ||
|
||
This property needs to be placed in a **static type** and placing it in the **Editor directory** is recommended. | ||
|
||
### XLua.LuaCallCSharp | ||
|
||
When adding this configuration for a C# type, xLua will generate the adapter code for this type (including constructing an instance for the type, and accessing its member properties & methods and static properties & methods). Otherwise, it will try to gain access using the reflection mode with lower performance. | ||
|
||
Adding this configuration to the Extension Methods of a type will also generate the adaptation code and append it to the member methods of the extended type. | ||
|
||
XLua will only generate the type loaded with this configuration. It will not automatically generate the adaptation code of its parent type. When accessing the parent type method of the child type object, if the parent type has the LuaCallCSharp configuration, the parent type's adaptation code will be executed. Otherwise it will try to gain access using the reflection mode. | ||
|
||
The reflection mode access not only has poor performance, but also may cause failed access on the il2cpp due to code stripping. This problem can be avoided through the ReflectionUse tag, which is described below. | ||
|
||
### XLua.ReflectionUse | ||
|
||
When adding this configuration to a C# type, xLua generates a link.xml to block code stripping on the il2cpp. | ||
|
||
For extension methods, you must add LuaCallCSharp or ReflectionUse to make them accessible. | ||
|
||
It is recommended that all types to be accessed in Lua have the LuaCallCSharp or ReflectionUse tag, to insure their proper operation on all platforms. | ||
|
||
### XLua.DoNotGen | ||
|
||
This indicates that some of the functions, fields, and properties in a type do not generate code and are accessed through the reflection mode. | ||
|
||
Only the fields or properties in the standard Dictionary<Type, List<string>> can be used. The key indicates the effective type. Value is a list. The name of the functions, fields, and properties with no code generated are configured. | ||
|
||
The differences from ReflectionUse are: 1. ReflectionUse specifies the entire type; 2. Upon the first access to a function (field, property), ReflectionUse will wrap the entire type, while DoNotGen will only wrap the function (field, property). In other words, DoNotGen is lazier. | ||
|
||
The differences from BlackList are: 1. BlackList cannot be used when it is configured. 2. BlackList can specify an overloaded function, while DoNotGen cannot. | ||
|
||
### XLua.CSharpCallLua | ||
|
||
This allows you to adapt a Lua function to a C# delegate (one scenario is various callbacks at the C# side: UI events, delegate parameters, such as List<T>:ForEach; another scenario is to use the Get function of LuaTable to indicate that a Lua function is bound to a delegate), or to adapt a Lua table to a C# interface. The delegate or interface needs this configuration. | ||
|
||
### XLua.GCOptimize | ||
|
||
A C# pure value type (Note: It refers to a struct that contains only the value type, and it can nest other structs that contain only the value type) or a C# enumerated value has this configuration. xLua generates gc-optimized code for this type. The result is that the value type is passed between Lua and C# with no (C#)gc alloc generated, and that no gc is generated during array access to this type. For various GC-free scenarios, refer to the 05\_NoGc example. | ||
|
||
Any type except enumeration (including the complex types that contain parameterless constructors) will generate Lua tables for that type, as well as the conversion code of a one-dimensional array with modified type. This will optimize the performance of this conversion, including fewer gc allocs. | ||
|
||
### XLua.AdditionalProperties | ||
|
||
This is GCOptimize's extended configuration. Sometimes, some structs want to make the field private and access the field through the property. In this case, you need to use this configuration (by default, GCOptimize only packetizes/depacketizes the public field). | ||
|
||
The tagging mode is relatively simple and the configuration mode is complicated. The requirements are that Dictionary<Type, List<string>> type, and the Key of the Dictionary are effective types; and value is the list of property names. See xLua's configuration of several UnityEngine value types and the SysGCOptimize type. | ||
|
||
### XLua.BlackList | ||
|
||
If you do not want to generate an adaption code for a member of a type, you can implement it with this configuration. | ||
|
||
The tagging method is relatively simple, and the corresponding member can be added. | ||
|
||
Considering that it may be necessary to add one of the overloaded functions to the blacklist, the configuration is more complicated. The type is List<List<string>>. For each member, the first-level list has only one entry and the second-level list is a string list. The first string is the full path name of the type, the second string is the member name. If the member is a method, you also need to list the full path of the type of its parameters starting from the third string. | ||
|
||
For example, the following adds a property of GameObject and a method of FileInfo to the blacklist: | ||
|
||
~~~csharp | ||
[BlackList] | ||
public static List<List<string>> BlackList = new List<List<string>>() { | ||
new List<string>(){"UnityEngine.GameObject", "networkView"}, | ||
new List<string>(){"System.IO.FileInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"}, | ||
}; | ||
~~~ | ||
|
||
### The following is the generator configuration, which must be placed in the Editor directory. | ||
|
||
### CSObjectWrapEditor.GenPath | ||
|
||
Configures the path of the generated code, with the type being a string. By default, it is plated in "Assets/XLua/Gen/". | ||
|
||
### CSObjectWrapEditor.GenCodeMenu | ||
|
||
This configuration is used for secondary development of the build engine. When adding this tag to a parameterless function, it will trigger calling the function when executing the "XLua/Generate Code" menu. | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
## Secondary development of the build engine | ||
|
||
xLua's build engine supports secondary development, and you can use it to generate some text files (for example, code and configuration files). The xLua's link.xml file is generated by the build engine plugin. Other application scenarios (such as generating a Lua IDE auto configuration file) can also be accomplished using this feature. | ||
|
||
## Overview | ||
|
||
The plugin needs to provide two things: 1. a template for generated files, 2. a callback function that accepts the user configuration and returns the data that needs to be injected into the template and the output stream of the files. | ||
|
||
## Template syntax | ||
|
||
The template syntax is simple, with only three elements: | ||
|
||
* eval: The syntax is <%=exp%>. Exp is an arbitrary expression that will calculate and output the value of exp as a string. | ||
* Code: The syntax is <% if true then end%>. The blue part is any Lua code that will be executed. | ||
* Literal: This contains the parts other than eval and code. Literal means output as it is. | ||
|
||
Example: | ||
|
||
~~~xml | ||
<% | ||
require "TemplateCommon" | ||
%> | ||
|
||
<linker> | ||
<%ForEachCsList(assembly_infos, function(assembly_info)%> | ||
<assembly fullname="<%=assembly_info.FullName%>"> | ||
<%ForEachCsList(assembly_info.Types, function(type) | ||
%><type fullname="<%=type:ToString()%>" preserve="all"/> | ||
<%end)%> | ||
</assembly> | ||
<%end)%> | ||
</linker> | ||
~~~ | ||
|
||
TemplateCommon has some predefined functions that can be used (for example ForEachCsList). You can search in TemplateCommon.lua.txt of the project to see which functions are available. For ordinary Lua, you can write one. | ||
|
||
## API | ||
|
||
~~~csharp | ||
public static void CSObjectWrapEditor.Generator.CustomGen(string template_src, GetTasks get_tasks) | ||
~~~ | ||
|
||
* template_src: template source code | ||
* get_tasks: This is a callback function. The type is GetTasks. This function is used to accept the user configuration and return the data that needs to be injected into the template and the output stream of the files. | ||
|
||
~~~csharp | ||
public delegate IEnumerable<CustomGenTask> GetTasks(LuaEnv lua_env, UserConfig user_cfg); | ||
~~~ | ||
|
||
* lua_env: This is a LuaEnv object. Because the returned template data needs to be placed in LuaTable, LuaEnv.NewTable is required. | ||
* user_cfg: user configuration | ||
* return: Among the returned values, CustomGenTask represents a generated file, and IEnumerable type indicates that the same template can generate multiple files. | ||
|
||
~~~csharp | ||
public struct UserConfig | ||
{ | ||
public IEnumerable<Type> LuaCallCSharp; | ||
public IEnumerable<Type> CSharpCallLua; | ||
public IEnumerable<Type> ReflectionUse; | ||
} | ||
~~~ | ||
|
||
~~~csharp | ||
public struct CustomGenTask | ||
{ | ||
public LuaTable Data; | ||
public TextWriter Output; | ||
} | ||
~~~ | ||
|
||
Example: | ||
|
||
~~~csharp | ||
public static IEnumerable<CustomGenTask> GetTasks(LuaEnv lua_env, UserConfig user_cfg) | ||
{ | ||
LuaTable data = lua_env.NewTable(); | ||
var assembly_infos = (from type in user_cfg.ReflectionUse | ||
group type by type.Assembly.GetName().Name into assembly_info | ||
select new { FullName = assembly_info.Key, Types = assembly_info.ToList()}).ToList(); | ||
data.Set("assembly_infos", assembly_infos); | ||
|
||
yield return new CustomGenTask | ||
{ | ||
Data = data, | ||
Output = new StreamWriter(GeneratorConfig.common_path + "/link.xml", | ||
false, Encoding.UTF8) | ||
}; | ||
} | ||
~~~ | ||
|
||
* Only one file is generated here, so only one CustomGenTask is returned. | ||
* data is the data to be used in the template. There is an assembly_infos field included. See the template section for how to use this field. | ||
|
||
## Tag | ||
|
||
Generally speaking, you can use MenuItem to create a menu to trigger a custom generate operation. However, sometimes you may want the generate operation to be triggered directly by the xLua "Generate Code" menu. In this situation, you will need to use CSObjectWrapEditor.GenCodeMenu | ||
|
||
Example: | ||
|
||
~~~csharp | ||
[GenCodeMenu]//加到Generate Code菜单里头 | ||
public static void GenLinkXml() | ||
{ | ||
Generator.CustomGen(ScriptableObject.CreateInstance<LinkXmlGen>().Template.text, GetTasks); | ||
} | ||
~~~ | ||
|
||
PS: All the code related to the content above is in the XLua\Src\Editor\LinkXmlGen directory, which is also the implementation of the link.xml generation function that was explained at the beginning. | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.