diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c6bb2931 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.meta diff --git a/Assets/Examples/01_JsCallCs/JsCallCs.cs b/Assets/Examples/01_JsCallCs/JsCallCs.cs new file mode 100644 index 00000000..88814d65 --- /dev/null +++ b/Assets/Examples/01_JsCallCs/JsCallCs.cs @@ -0,0 +1,26 @@ +using UnityEngine; +using Puerts; + +namespace PuertsTest +{ + public class JsCallCs : MonoBehaviour + { + JsEnv jsEnv; + + void Start() + { + jsEnv = new JsEnv(); + + jsEnv.Eval(@" + const CS = require('csharp'); + let gameObject = new CS.UnityEngine.GameObject('testObject'); + CS.UnityEngine.Debug.Log(gameObject.name); + "); + } + + void OnDestroy() + { + jsEnv.Dispose(); + } + } +} diff --git a/Assets/Examples/01_JsCallCs/JsCallCs.unity b/Assets/Examples/01_JsCallCs/JsCallCs.unity new file mode 100644 index 00000000..eada567a Binary files /dev/null and b/Assets/Examples/01_JsCallCs/JsCallCs.unity differ diff --git a/Assets/Examples/02_Require/Require.cs b/Assets/Examples/02_Require/Require.cs new file mode 100644 index 00000000..7807ef4b --- /dev/null +++ b/Assets/Examples/02_Require/Require.cs @@ -0,0 +1,30 @@ +using UnityEngine; +using Puerts; + +namespace PuertsTest +{ + public class Require : MonoBehaviour + { + JsEnv jsEnv; + + // Use this for initialization + void Start() + { + //JsEnv还有一个构造函数可以传loader, + //通过实现不同的loader,可以做到从诸如 + //AssetBundle,压缩包,网络等源加载代码 + //这个无参构造会用默认的loader,默认loader + //从Resources目录加载 + jsEnv = new JsEnv(); + + jsEnv.Eval(@" + require('main') + "); + } + + void OnDestroy() + { + jsEnv.Dispose(); + } + } +} \ No newline at end of file diff --git a/Assets/Examples/02_Require/Require.unity b/Assets/Examples/02_Require/Require.unity new file mode 100644 index 00000000..374a39ad Binary files /dev/null and b/Assets/Examples/02_Require/Require.unity differ diff --git a/Assets/Examples/02_Require/Resources/main.js.txt b/Assets/Examples/02_Require/Resources/main.js.txt new file mode 100644 index 00000000..2a849ac6 --- /dev/null +++ b/Assets/Examples/02_Require/Resources/main.js.txt @@ -0,0 +1,3 @@ +const module1 = require('module1'); + +module1.callMe('from john'); diff --git a/Assets/Examples/02_Require/Resources/module1.js.txt b/Assets/Examples/02_Require/Resources/module1.js.txt new file mode 100644 index 00000000..863dbdfc --- /dev/null +++ b/Assets/Examples/02_Require/Resources/module1.js.txt @@ -0,0 +1,7 @@ +console.log('module1 loading'); + +function callMe(msg) { + console.log('callMe called', msg); +} + +exports.callMe = callMe; \ No newline at end of file diff --git a/Assets/Examples/03_Callback/Callback.cs b/Assets/Examples/03_Callback/Callback.cs new file mode 100644 index 00000000..41ce97bb --- /dev/null +++ b/Assets/Examples/03_Callback/Callback.cs @@ -0,0 +1,36 @@ +using UnityEngine; +using Puerts; + +namespace PuertsTest +{ + public class Callback : MonoBehaviour + { + JsEnv jsEnv; + + // Use this for initialization + void Start() + { + jsEnv = new JsEnv(); + + //要使用值类型参数或者返回值的委托要声明,而Callback1因为是引用类型所以不用。 + jsEnv.UsingAction(); + + jsEnv.Eval(@" + const CS = require('csharp'); + let obj = new CS.PuertsTest.TestClass(); + //如果你后续要remove,需要这样构建一个Delegate,后续可以用该Delegate引用去remove + let delegate = new CS.PuertsTest.Callback1(o => o.Foo()); + obj.AddEventCallback1(delegate); + obj.AddEventCallback2(i => console.log(i)); //如果不需要remove,直接传函数即可 + obj.Trigger(); + obj.RemoveEventCallback1(delegate); + obj.Trigger(); + "); + } + + void OnDestroy() + { + jsEnv.Dispose(); + } + } +} diff --git a/Assets/Examples/03_Callback/Callback.unity b/Assets/Examples/03_Callback/Callback.unity new file mode 100644 index 00000000..8547d490 Binary files /dev/null and b/Assets/Examples/03_Callback/Callback.unity differ diff --git a/Assets/Examples/03_Callback/TestClass.cs b/Assets/Examples/03_Callback/TestClass.cs new file mode 100644 index 00000000..ff64c6ae --- /dev/null +++ b/Assets/Examples/03_Callback/TestClass.cs @@ -0,0 +1,50 @@ +using UnityEngine; +using Puerts; + +namespace PuertsTest +{ + public delegate void Callback1(TestClass obj); + + public delegate void Callback2(int str); + + public class TestClass + { + Callback1 callback1; + + Callback2 callback2; + + public void AddEventCallback1(Callback1 callback1) + { + this.callback1 += callback1; + } + + public void RemoveEventCallback1(Callback1 callback1) + { + this.callback1 -= callback1; + } + + public void AddEventCallback2(Callback2 callback2) + { + this.callback2 += callback2; + } + + public void Trigger() + { + Debug.Log("begin Trigger"); + if (callback1 != null) + { + callback1(this); + } + if (callback2 != null) + { + callback2(1024); + } + Debug.Log("end Trigger"); + } + + public void Foo() + { + Debug.Log("Foo"); + } + } +} diff --git a/Assets/Examples/04_JsBehaviour/JsBehaviour.cs b/Assets/Examples/04_JsBehaviour/JsBehaviour.cs new file mode 100644 index 00000000..3995a78c --- /dev/null +++ b/Assets/Examples/04_JsBehaviour/JsBehaviour.cs @@ -0,0 +1,48 @@ +using UnityEngine; +using Puerts; +using System; + +namespace PuertsTest +{ + public delegate void ModuleInit(JsBehaviour monoBehaviour); + + //只是演示纯用js实现MonoBehaviour逻辑的可能, + //但从性能角度这并不是最佳实践,会导致过多的跨语言调用 + public class JsBehaviour : MonoBehaviour + { + public string ModuleName;//可配置加载的js模块 + + public Action JsStart; + public Action JsUpdate; + public Action JsOnDestroy; + + static JsEnv jsEnv; + + void Awake() + { + if (jsEnv == null) jsEnv = new JsEnv(); + + var init = jsEnv.Eval("const m = require('" + ModuleName + "'); m.init;"); + + if (init != null) init(this); + } + + void Start() + { + if (JsStart != null) JsStart(); + } + + void Update() + { + if (JsUpdate != null) JsUpdate(); + } + + void OnDestroy() + { + if (JsOnDestroy != null) JsOnDestroy(); + JsStart = null; + JsUpdate = null; + JsOnDestroy = null; + } + } +} \ No newline at end of file diff --git a/Assets/Examples/04_JsBehaviour/Resources/rotate.js.txt b/Assets/Examples/04_JsBehaviour/Resources/rotate.js.txt new file mode 100644 index 00000000..c3708db7 --- /dev/null +++ b/Assets/Examples/04_JsBehaviour/Resources/rotate.js.txt @@ -0,0 +1,24 @@ +const speed = 10; +const CS = require('csharp'); + +class Rotate { + constructor(bindTo) { + this.bindTo = bindTo; + this.bindTo.JsUpdate = () => this.onUpdate(); + this.bindTo.JsOnDestroy = () => this.onDestroy(); + } + + onUpdate() { + //jsֲ֧Vector3ijô + let r = CS.UnityEngine.Vector3.op_Multiply(CS.UnityEngine.Vector3.up, CS.UnityEngine.Time.deltaTime * speed); + this.bindTo.transform.Rotate(r); + } + + onDestroy() { + console.log('onDestroy...'); + } +} + +exports.init = function(bindTo) { + new Rotate(bindTo); +} diff --git a/Assets/Examples/04_JsBehaviour/RotateCube.unity b/Assets/Examples/04_JsBehaviour/RotateCube.unity new file mode 100644 index 00000000..632b186b Binary files /dev/null and b/Assets/Examples/04_JsBehaviour/RotateCube.unity differ diff --git a/Assets/Examples/05_Typescript/Resources/QuickStart.js.map.txt b/Assets/Examples/05_Typescript/Resources/QuickStart.js.map.txt new file mode 100644 index 00000000..f42746e1 --- /dev/null +++ b/Assets/Examples/05_Typescript/Resources/QuickStart.js.map.txt @@ -0,0 +1 @@ +{"version":3,"file":"QuickStart.js","sourceRoot":"","sources":["../QuickStart.ts"],"names":[],"mappings":";AAAA,kBAAkB;;AAElB,mCAAsD;AACtD,mCAAuD;AAEvD,MAAM;AACN,oBAAW,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AAErC,MAAM;AACN,IAAI,GAAG,GAAG,IAAI,mBAAU,CAAC,YAAY,EAAE,CAAC;AAExC,QAAQ;AACR,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA,MAAM;AACnB,GAAG,CAAC,MAAM,CAAC,mBAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA,MAAM;AACvC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAC9B,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAA,MAAM;AACnB,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAA,MAAM;AACnB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAE9B,MAAM;AACN,OAAO,CAAC,GAAG,CAAC,mBAAU,CAAC,SAAS,CAAC,GAAG,EAAE,mBAAU,CAAC,YAAY,CAAC,GAAG,EAAE,mBAAU,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AAEhG,OAAO;AACP,gCAAgC;AAChC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,GAAG,GAAG,CAAC,CAAC;AACtE,+BAA+B;AAC/B,IAAI,QAAQ,GAAG,IAAI,mBAAU,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,GAAG,GAAG,CAAC,CAAC,CAAC;AAC3F,kEAAkE;AAClE,GAAG,CAAC,UAAU,GAAG,eAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAA0B,CAAC;AAC5F,GAAG,CAAC,OAAO,EAAE,CAAC;AACd,oDAAoD;AACpD,GAAG,CAAC,UAAU,GAAG,eAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAA0B,CAAC;AAC3F,GAAG,CAAC,OAAO,EAAE,CAAC;AACd,IAAI;AACJ,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC1B,GAAG,CAAC,OAAO,EAAE,CAAC;AACd,GAAG,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;AAC7B,GAAG,CAAC,OAAO,EAAE,CAAC;AACd,MAAM;AACN,mBAAU,CAAC,YAAY,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AACpD,GAAG,CAAC,OAAO,EAAE,CAAC;AACd,mBAAU,CAAC,YAAY,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AACvD,GAAG,CAAC,OAAO,EAAE,CAAC;AAEd,MAAM;AACN,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAE/C,WAAW;AACX,IAAI,EAAE,GAAG,aAAI,EAAE,CAAC;AAChB,IAAI,EAAE,GAAG,aAAI,CAAC,EAAE,CAAC,CAAC;AAClB,IAAI,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,GAAG,QAAQ,GAAG,eAAM,CAAC,EAAE,CAAC,GAAG,QAAQ,GAAE,eAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AAEzE,IAAI;AACJ,oBAAoB;AACpB,IAAI,IAAI,GAAG,iBAAQ,CAAC,eAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,eAAM,CAAC,KAAK,CAAC,CAAC;AACrE,IAAI,GAAG,GAAG,IAAI,IAAI,EAAU,CAAC;AAC7B,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACX,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACX,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACX,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACX,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAEnB,oEAAoE;AACpE;;;;;;;;sDAQsD"} \ No newline at end of file diff --git a/Assets/Examples/05_Typescript/Resources/QuickStart.js.txt b/Assets/Examples/05_Typescript/Resources/QuickStart.js.txt new file mode 100644 index 00000000..9a866155 --- /dev/null +++ b/Assets/Examples/05_Typescript/Resources/QuickStart.js.txt @@ -0,0 +1,66 @@ +"use strict"; +//部署:npm run build +Object.defineProperty(exports, "__esModule", { value: true }); +const csharp_1 = require("csharp"); +const puerts_1 = require("puerts"); +//静态函数 +csharp_1.UnityEngine.Debug.Log('hello world'); +//对象构造 +let obj = new csharp_1.PuertsTest.DerivedClass(); +//实例成员访问 +obj.BMFunc(); //父类方法 +obj.DMFunc(csharp_1.PuertsTest.MyEnum.E1); //子类方法 +console.log(obj.BMF, obj.DMF); +obj.BMF = 10; //父类属性 +obj.DMF = 30; //子类属性 +console.log(obj.BMF, obj.DMF); +//静态成员 +console.log(csharp_1.PuertsTest.BaseClass.BSF, csharp_1.PuertsTest.DerivedClass.DSF, csharp_1.PuertsTest.DerivedClass.BSF); +//委托,事件 +//如果你后续不需要-=,可以像这样直接传函数当delegate +obj.MyCallback = msg => console.log("do not need remove, msg=" + msg); +//通过new构建的delegate,后续可以拿这个引用去-= +let delegate = new csharp_1.PuertsTest.MyCallback(msg => console.log('can be removed, msg=' + msg)); +//由于ts不支持操作符重载,Delegate.Combine相当于C#里头的obj.myCallback += delegate; +obj.MyCallback = csharp_1.System.Delegate.Combine(obj.MyCallback, delegate); +obj.Trigger(); +//Delegate.Remove相当于C#里头的obj.myCallback += delegate; +obj.MyCallback = csharp_1.System.Delegate.Remove(obj.MyCallback, delegate); +obj.Trigger(); +//事件 +obj.add_MyEvent(delegate); +obj.Trigger(); +obj.remove_MyEvent(delegate); +obj.Trigger(); +//静态事件 +csharp_1.PuertsTest.DerivedClass.add_MyStaticEvent(delegate); +obj.Trigger(); +csharp_1.PuertsTest.DerivedClass.remove_MyStaticEvent(delegate); +obj.Trigger(); +//可变参数 +obj.ParamsFunc(1024, 'haha', 'hehe', 'heihei'); +//in out 参数 +let p1 = puerts_1.$ref(); +let p2 = puerts_1.$ref(10); +let ret = obj.InOutArgFunc(100, p1, p2); +console.log('ret=' + ret + ', out=' + puerts_1.$unref(p1) + ', ref=' + puerts_1.$unref(p2)); +//泛型 +//先通过$generic实例化泛型参数 +let List = puerts_1.$generic(csharp_1.System.Collections.Generic.List$1, csharp_1.System.Int32); +let lst = new List(); +lst.Add(1); +lst.Add(0); +lst.Add(2); +lst.Add(4); +obj.PrintList(lst); +//typescript和c#的async,await联动,为了不在低版本的Unity下报错,先注释,c#7.3以上版本可以打开这些注释 +/*async function asyncCall() { + let task = obj.GetFileLength("Assets/Examples/05_Typescript/TsQuickStart.cs"); + let result = await $promise(task); + console.log('file length is ' + result); + let task2 = obj.GetFileLength("notexistedfile");//这个会抛文件找不到异常,被catch + let result2 = await $promise(task2); + console.log('file length is ,' + result2); +} +asyncCall().catch(e => console.error("catch:" + e));*/ +//# sourceMappingURL=QuickStart.js.map \ No newline at end of file diff --git a/Assets/Examples/05_Typescript/SomeClasses.cs b/Assets/Examples/05_Typescript/SomeClasses.cs new file mode 100644 index 00000000..e939c97f --- /dev/null +++ b/Assets/Examples/05_Typescript/SomeClasses.cs @@ -0,0 +1,123 @@ +using UnityEngine; +using System.Collections.Generic; +#if CSHARP_7_3_OR_NEWER +using System.IO; +using System.Threading.Tasks; +#endif + +//typescript工程在TsProj,在该目录执行npm run build即可编译并把js文件拷贝进unity工程 + +namespace PuertsTest +{ + public enum MyEnum + { + E1, + E2 + } + + public delegate void MyCallback(string msg); + + public class BaseClass + { + public static void BSFunc() + { + Debug.Log("BaseClass Static Func, BSF = " + BSF); + } + + public static int BSF = 1; + + public void BMFunc() + { + Debug.Log("BaseClass Member Func, BMF = " + BMF); + } + + public int BMF { get; set; } + } + + public class DerivedClass : BaseClass + { + public static void DSFunc() + { + Debug.Log("DerivedClass Static Func, DSF = " + DSF); + } + + public static int DSF = 2; + + public void DMFunc() + { + Debug.Log("DerivedClass Member Func, DMF = " + DMF); + } + + public MyEnum DMFunc(MyEnum myEnum) + { + Debug.Log("DMFunc(MyEnum myEnum), myEnum = " + myEnum); + return MyEnum.E2; + } + + public int DMF { get; set; } + + public MyCallback MyCallback; + + public event MyCallback MyEvent; + + public static event MyCallback MyStaticEvent; + + public void Trigger() + { + Debug.Log("begin Trigger"); + if (MyCallback != null) + { + MyCallback("hello"); + } + if (MyEvent != null) + { + MyEvent("john"); + } + if (MyStaticEvent != null) + { + MyStaticEvent("static event"); + } + Debug.Log("end Trigger"); + } + + public int ParamsFunc(int a, params string[] b) + { + Debug.Log("ParamsFunc.a = " + a); + for (int i = 0; i < b.Length; i++) + { + Debug.Log("ParamsFunc.b[" + i + "] = " + b[i]); + } + return a + b.Length; + } + + public double InOutArgFunc(int a, out int b, ref int c) + { + Debug.Log("a=" + a + ",c=" + c); + b = 100; + c = c * 2; + return a + b; + } + + public void PrintList(List lst) + { + Debug.Log("lst.Count=" + lst.Count); + for (int i = 0; i < lst.Count; i++) + { + Debug.Log(string.Format("lst[{0}]={1}", i, lst[i])); + } + } + +#if CSHARP_7_3_OR_NEWER + public async Task GetFileLength(string path) + { + Debug.Log("start read " + path); + using (StreamReader reader = new StreamReader(path)) + { + string s = await reader.ReadToEndAsync(); + Debug.Log("read " + path + " completed"); + return s.Length; + } + } +#endif + } +} diff --git a/Assets/Examples/05_Typescript/TsQuickStart.cs b/Assets/Examples/05_Typescript/TsQuickStart.cs new file mode 100644 index 00000000..f1ff42cc --- /dev/null +++ b/Assets/Examples/05_Typescript/TsQuickStart.cs @@ -0,0 +1,23 @@ +using UnityEngine; +using Puerts; + +//typescript工程在TsProj,在该目录执行npm run build即可编译并把js文件拷贝进unity工程 + +namespace PuertsTest +{ + public class TsQuickStart : MonoBehaviour + { + JsEnv jsEnv; + + void Start() + { + jsEnv = new JsEnv(); + jsEnv.Eval("require('QuickStart')"); + } + + void OnDestroy() + { + jsEnv.Dispose(); + } + } +} diff --git a/Assets/Examples/05_Typescript/TsQuickStart.unity b/Assets/Examples/05_Typescript/TsQuickStart.unity new file mode 100644 index 00000000..309b520e Binary files /dev/null and b/Assets/Examples/05_Typescript/TsQuickStart.unity differ diff --git a/Assets/Examples/Editor/ExamplesCfg.cs b/Assets/Examples/Editor/ExamplesCfg.cs new file mode 100644 index 00000000..0aeb628b --- /dev/null +++ b/Assets/Examples/Editor/ExamplesCfg.cs @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making InjectFix available. + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +using System.Collections.Generic; +using Puerts; +using System; +using UnityEngine; + +//1、配置类必须打[Configure]标签 +//2、必须放Editor目录 +[Configure] +public class ExamplesCfg +{ + [Binding] + static IEnumerable Bindings + { + get + { + return new List() + { + typeof(Debug), + typeof(PuertsTest.TestClass), + typeof(Vector3), + typeof(List), + //typeof(Dictionary), + typeof(PuertsTest.BaseClass), + typeof(PuertsTest.DerivedClass), + typeof(PuertsTest.MyEnum), + typeof(Time), + typeof(Transform), + typeof(Component), + typeof(GameObject), + typeof(UnityEngine.Object), + typeof(Delegate), + }; + } + } + + [BlittableCopy] + static IEnumerable Blittables + { + get + { + return new List() + { + //打开这个可以优化Vector3的GC,但需要开启unsafe编译 + //typeof(Vector3), + }; + } + } +} diff --git a/Assets/Plugins/Android/libs/arm64-v8a/libpuerts.so b/Assets/Plugins/Android/libs/arm64-v8a/libpuerts.so new file mode 100644 index 00000000..6868bc55 Binary files /dev/null and b/Assets/Plugins/Android/libs/arm64-v8a/libpuerts.so differ diff --git a/Assets/Plugins/Android/libs/armeabi-v7a/libpuerts.so b/Assets/Plugins/Android/libs/armeabi-v7a/libpuerts.so new file mode 100644 index 00000000..8afe0a66 Binary files /dev/null and b/Assets/Plugins/Android/libs/armeabi-v7a/libpuerts.so differ diff --git a/Assets/Plugins/iOS/libbindings.a b/Assets/Plugins/iOS/libbindings.a new file mode 100644 index 00000000..06350ef3 Binary files /dev/null and b/Assets/Plugins/iOS/libbindings.a differ diff --git a/Assets/Plugins/iOS/libencoding.a b/Assets/Plugins/iOS/libencoding.a new file mode 100644 index 00000000..c869ffd5 Binary files /dev/null and b/Assets/Plugins/iOS/libencoding.a differ diff --git a/Assets/Plugins/iOS/libinspector.a b/Assets/Plugins/iOS/libinspector.a new file mode 100644 index 00000000..a24b4fe8 Binary files /dev/null and b/Assets/Plugins/iOS/libinspector.a differ diff --git a/Assets/Plugins/iOS/libinspector_string_conversions.a b/Assets/Plugins/iOS/libinspector_string_conversions.a new file mode 100644 index 00000000..44d74e55 Binary files /dev/null and b/Assets/Plugins/iOS/libinspector_string_conversions.a differ diff --git a/Assets/Plugins/iOS/libpuerts.a b/Assets/Plugins/iOS/libpuerts.a new file mode 100644 index 00000000..3d61e5f6 Binary files /dev/null and b/Assets/Plugins/iOS/libpuerts.a differ diff --git a/Assets/Plugins/iOS/libtorque_generated_definitions.a b/Assets/Plugins/iOS/libtorque_generated_definitions.a new file mode 100644 index 00000000..3253e110 Binary files /dev/null and b/Assets/Plugins/iOS/libtorque_generated_definitions.a differ diff --git a/Assets/Plugins/iOS/libv8_base_without_compiler.a b/Assets/Plugins/iOS/libv8_base_without_compiler.a new file mode 100644 index 00000000..69added5 Binary files /dev/null and b/Assets/Plugins/iOS/libv8_base_without_compiler.a differ diff --git a/Assets/Plugins/iOS/libv8_compiler.a b/Assets/Plugins/iOS/libv8_compiler.a new file mode 100644 index 00000000..5213a0a0 Binary files /dev/null and b/Assets/Plugins/iOS/libv8_compiler.a differ diff --git a/Assets/Plugins/iOS/libv8_external_snapshot.a b/Assets/Plugins/iOS/libv8_external_snapshot.a new file mode 100644 index 00000000..4aad067a Binary files /dev/null and b/Assets/Plugins/iOS/libv8_external_snapshot.a differ diff --git a/Assets/Plugins/iOS/libv8_libbase.a b/Assets/Plugins/iOS/libv8_libbase.a new file mode 100644 index 00000000..7a800eb0 Binary files /dev/null and b/Assets/Plugins/iOS/libv8_libbase.a differ diff --git a/Assets/Plugins/iOS/libv8_libplatform.a b/Assets/Plugins/iOS/libv8_libplatform.a new file mode 100644 index 00000000..b5422843 Binary files /dev/null and b/Assets/Plugins/iOS/libv8_libplatform.a differ diff --git a/Assets/Plugins/iOS/libv8_libsampler.a b/Assets/Plugins/iOS/libv8_libsampler.a new file mode 100644 index 00000000..6be1e522 Binary files /dev/null and b/Assets/Plugins/iOS/libv8_libsampler.a differ diff --git a/Assets/Plugins/puerts.bundle/Contents/Info.plist b/Assets/Plugins/puerts.bundle/Contents/Info.plist new file mode 100644 index 00000000..1eac23be --- /dev/null +++ b/Assets/Plugins/puerts.bundle/Contents/Info.plist @@ -0,0 +1,46 @@ + + + + + BuildMachineOSBuild + 18G84 + CFBundleDevelopmentRegion + English + CFBundleExecutable + puerts + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + + CSResourcesFileMapped + + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 11C505 + DTPlatformVersion + GM + DTSDKBuild + 19B90 + DTSDKName + macosx10.15 + DTXcode + 1131 + DTXcodeBuild + 11C505 + LSMinimumSystemVersion + 10.14 + NSHumanReadableCopyright + + + diff --git a/Assets/Plugins/puerts.bundle/Contents/MacOS/puerts b/Assets/Plugins/puerts.bundle/Contents/MacOS/puerts new file mode 100644 index 00000000..49c4196d Binary files /dev/null and b/Assets/Plugins/puerts.bundle/Contents/MacOS/puerts differ diff --git a/Assets/Plugins/x86_64/puerts.dll b/Assets/Plugins/x86_64/puerts.dll new file mode 100644 index 00000000..b528bb3f Binary files /dev/null and b/Assets/Plugins/x86_64/puerts.dll differ diff --git a/Assets/Puerts/Src/ArgumentHelper.cs b/Assets/Puerts/Src/ArgumentHelper.cs new file mode 100644 index 00000000..4dfcbf51 --- /dev/null +++ b/Assets/Puerts/Src/ArgumentHelper.cs @@ -0,0 +1,221 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; + +namespace Puerts +{ + public struct ArgumentHelper + { + private readonly int jsEnvIdx; + private readonly IntPtr isolate; + private readonly IntPtr value; + private readonly JsValueType valueType; + private Type csType; + + public ArgumentHelper(int jsEnvIdx, IntPtr isolate, IntPtr info, int index) + { + this.jsEnvIdx = jsEnvIdx; + this.isolate = isolate; + value = PuertsDLL.GetArgumentValue(info, index); + valueType = PuertsDLL.GetJsValueType(isolate, value, false); + csType = null; + } + + public bool IsMatch(JsValueType expectJsType, Type expectCsType, bool isByRef, bool isOut) + { + var jsType = this.valueType; + if (jsType == JsValueType.JsObject) + { + if (!isByRef) return false; + if (isOut) return true; + jsType = PuertsDLL.GetJsValueType(isolate, value, true); + } + if ((expectJsType & jsType) != jsType) + { + return false; + } + if (jsType == JsValueType.NativeObject) + { + if (csType == null) + { + var typeId = NativeValueApi.GetValueFromArgument.GetTypeId(isolate, value, isByRef); + if (typeId >= 0) + { + csType = JsEnv.jsEnvs[jsEnvIdx].TypeRegister.GetType(typeId); + } + } + return csType != null && expectCsType != null && expectCsType.IsAssignableFrom(csType); + } + return true; + } + + public char GetChar(bool isByRef) + { + return (char)PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(char val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public sbyte GetSByte(bool isByRef) + { + return (sbyte)PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(sbyte val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public byte GetByte(bool isByRef) + { + return (byte)PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(byte val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public short GetInt16(bool isByRef) + { + return (short)PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(short val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public ushort GetUInt16(bool isByRef) + { + return (ushort)PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(ushort val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public int GetInt32(bool isByRef) + { + return (int)PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(int val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public uint GetUInt32(bool isByRef) + { + return (uint)PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(uint val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public long GetInt64(bool isByRef) + { + return PuertsDLL.GetBigIntFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(long val) + { + PuertsDLL.SetBigIntToOutValue(isolate, value, val); + } + + public ulong GetUInt64(bool isByRef) + { + return (ulong)PuertsDLL.GetBigIntFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(ulong val) + { + PuertsDLL.SetBigIntToOutValue(isolate, value, (long)val); + } + + public double GetDouble(bool isByRef) + { + return PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(double val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public float GetFloat(bool isByRef) + { + return (float)PuertsDLL.GetNumberFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(float val) + { + PuertsDLL.SetNumberToOutValue(isolate, value, val); + } + + public bool GetBoolean(bool isByRef) + { + return PuertsDLL.GetBooleanFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(bool val) + { + PuertsDLL.SetBooleanToOutValue(isolate, value, val); + } + + public string GetString(bool isByRef) + { + return PuertsDLL.GetStringFromValue(isolate, value, isByRef); + } + + public void SetByRefValue(string val) + { + PuertsDLL.SetStringToOutValue(isolate, value, val); + } + + public DateTime GetDateTime(bool isByRef) + { + var ticks = PuertsDLL.GetDateFromValue(isolate, value, isByRef); + return (new DateTime(1970, 1, 1)).AddMilliseconds(ticks); + } + + public void SetByRefValue(DateTime val) + { + PuertsDLL.SetDateToOutValue(isolate, value, (val - new DateTime(1970, 1, 1)).TotalMilliseconds); + } + + public T Get(bool isByRef) + { + return StaticTranslate.Get(jsEnvIdx, isolate, NativeValueApi.GetValueFromArgument, value, isByRef); + } + + public T[] GetParams(IntPtr info, int start, int end) + { + T[] result = new T[end - start]; + + for(int i = start; i < end; i++) + { + var val = PuertsDLL.GetArgumentValue(info, i); + result[i - start] = StaticTranslate.Get(jsEnvIdx, isolate, NativeValueApi.GetValueFromArgument, val, false); + } + + return result; + } + + public void SetByRefValue(T val) + { + StaticTranslate.Set(jsEnvIdx, isolate, NativeValueApi.SetValueToByRefArgument, value, val); + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/DataTranslate.cs b/Assets/Puerts/Src/DataTranslate.cs new file mode 100644 index 00000000..a7aaaf8c --- /dev/null +++ b/Assets/Puerts/Src/DataTranslate.cs @@ -0,0 +1,525 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; +using System.Collections.Generic; + +namespace Puerts +{ + //targetjsĬתc#ʱ + public delegate object GeneralGetter(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef); + + public class GeneralGetterManager + { + private ObjectPool objectPool; + + private TypeRegister typeRegister; + + private Dictionary generalGetterMap = new Dictionary(); + + private Dictionary nullableTypeGeneralGetterMap = new Dictionary(); + + internal GenericDelegateFactory genericDelegateFactory; + + internal GeneralGetterManager(JsEnv jsEnv) + { + objectPool = jsEnv.objectPool; + typeRegister = jsEnv.TypeRegister; + genericDelegateFactory = new GenericDelegateFactory(jsEnv); + + generalGetterMap[typeof(char)] = CharTranslator; + generalGetterMap[typeof(sbyte)] = SbyteTranslator; + generalGetterMap[typeof(byte)] = ByteTranslator; + generalGetterMap[typeof(short)] = ShortTranslator; + generalGetterMap[typeof(ushort)] = UshortTranslator; + generalGetterMap[typeof(int)] = IntTranslator; + generalGetterMap[typeof(uint)] = UintTranslator; + generalGetterMap[typeof(long)] = LongTranslator; + generalGetterMap[typeof(ulong)] = UlongTranslator; + generalGetterMap[typeof(double)] = DoubleTranslator; + generalGetterMap[typeof(float)] = FloatTranslator; + //translatorMap[typeof(decimal)] = decimalTranslator; + generalGetterMap[typeof(bool)] = BooleanTranslator; + generalGetterMap[typeof(string)] = StringTranslator; + generalGetterMap[typeof(DateTime)] = DateTranslator; + generalGetterMap[typeof(object)] = AnyTranslator; + //special type + //translatorMap[typeof(LuaTable)] = getLuaTable; + //translatorMap[typeof(LuaFunction)] = getLuaFunction; + } + + + private static object CharTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (char)getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object SbyteTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (sbyte)getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object ByteTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (byte)getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object ShortTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (short)getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object UshortTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (ushort)getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object IntTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (int)getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object UintTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (uint)getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object LongTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return getValueApi.GetBigInt(isolate, value, isByRef); + } + + private static object UlongTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (ulong)getValueApi.GetBigInt(isolate, value, isByRef); + } + + private static object DoubleTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object FloatTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (float)getValueApi.GetNumber(isolate, value, isByRef); + } + + private static object BooleanTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return getValueApi.GetBoolean(isolate, value, isByRef); + } + + private static object StringTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return getValueApi.GetString(isolate, value, isByRef); + } + + private static object DateTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + var ticks = getValueApi.GetDate(isolate, value, isByRef); + return (new DateTime(1970, 1, 1)).AddMilliseconds(ticks); + } + + internal object AnyTranslator(IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + var type = getValueApi.GetJsValueType(isolate, value, isByRef); + switch (type) + { + case JsValueType.BigInt: + return getValueApi.GetBigInt(isolate, value, isByRef); + case JsValueType.Boolean: + return getValueApi.GetBoolean(isolate, value, isByRef); + case JsValueType.Date: + return DateTranslator(isolate, getValueApi, value, isByRef); + //case JsValueType.Function: + //case JsValueType.JsObject: + case JsValueType.NativeObject: + var typeId = getValueApi.GetTypeId(isolate, value, isByRef); + var objType = typeRegister.GetType(typeId); + if (objType != typeof(object) && generalGetterMap.ContainsKey(objType)) + { + return generalGetterMap[objType](isolate, getValueApi, value, isByRef); + } + var objPtr = getValueApi.GetObject(isolate, value, isByRef); + return objectPool.Get(objPtr.ToInt32()); + case JsValueType.Number: + return getValueApi.GetNumber(isolate, value, isByRef); + case JsValueType.String: + return getValueApi.GetString(isolate, value, isByRef); + default: + return null; + } + } + + GeneralGetter MakeNullableTranslateFunc(GeneralGetter jvt) + { + return (IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) => + { + if (getValueApi.GetJsValueType(isolate, value, isByRef) == JsValueType.NullOrUndefined) + { + return null; + } + else + { + return jvt(isolate, getValueApi, value, isByRef); + } + }; + } + + private GeneralGetter MakeTranslateFunc(Type type) + { + GeneralGetter fixTypeGetter = (IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) => + { + if (getValueApi.GetJsValueType(isolate, value, isByRef) == JsValueType.NativeObject) + { + var objPtr = getValueApi.GetObject(isolate, value, isByRef); + var obj = objectPool.Get(objPtr.ToInt32()); + return (obj != null && type.IsAssignableFrom(obj.GetType())) ? obj : null; + } + return null; + }; + + if (typeof(Delegate).IsAssignableFrom(type)) + { + return (IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) => + { + var jsValueType = getValueApi.GetJsValueType(isolate, value, isByRef); + if (jsValueType == JsValueType.Function) + { + var nativePtr = getValueApi.GetFunction(isolate, value, isByRef); + var result = genericDelegateFactory.Create(type, nativePtr); + if (result == null) + { + throw new Exception("can not find delegate bridge for " + type.GetFriendlyName()); + } + return result; + } + else + { + return fixTypeGetter(isolate, getValueApi, value, isByRef); + } + }; + } + + return fixTypeGetter; + } + + public GeneralGetter GetTranslateFunc(Type type) + { + if (type.IsByRef) return GetTranslateFunc(type.GetElementType()); + + Type underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType != null) + { + GeneralGetter jvt; + if (!nullableTypeGeneralGetterMap.TryGetValue(underlyingType, out jvt)) + { + jvt = MakeNullableTranslateFunc(GetTranslateFunc(underlyingType)); + nullableTypeGeneralGetterMap.Add(underlyingType, jvt); + } + return jvt; + } + else + { + if (type.IsEnum) + { + return GetTranslateFunc(Enum.GetUnderlyingType(type)); + } + GeneralGetter jvt; + if (!generalGetterMap.TryGetValue(type, out jvt)) + { + jvt = MakeTranslateFunc(type); + generalGetterMap.Add(type, jvt); + } + return jvt; + } + } + + public void RegisterGetter(Type type, GeneralGetter generalGetter) + { + Type underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType != null) + { + nullableTypeGeneralGetterMap.Add(underlyingType, generalGetter); + } + else + { + generalGetterMap.Add(type, generalGetter); + } + } + + public object GetSelf(IntPtr Self) + { + return objectPool.Get(Self.ToInt32()); + } + + static private Dictionary primitiveTypeMap = new Dictionary() + { + { typeof(sbyte), JsValueType.Number }, + { typeof(byte), JsValueType.Number }, + { typeof(short), JsValueType.Number }, + { typeof(ushort), JsValueType.Number }, + { typeof(int), JsValueType.Number }, + { typeof(uint), JsValueType.Number }, + { typeof(long), JsValueType.BigInt }, + { typeof(ulong), JsValueType.BigInt }, + { typeof(double), JsValueType.Number }, + { typeof(char), JsValueType.Number }, + { typeof(float), JsValueType.Number }, + //{ typeof(decimal), JsValueType.Number }, TODO: decimal by value ݵjs + { typeof(bool), JsValueType.Boolean }, + { typeof(string), JsValueType.String | JsValueType.NullOrUndefined }, + { typeof(object), JsValueType.Any} + }; + + public static JsValueType GetJsTypeMask(Type type) + { + if (type.IsByRef) + { + return GetJsTypeMask(type.GetElementType()); + } + + Type underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType != null) + { + return GetJsTypeMask(underlyingType) | JsValueType.NullOrUndefined; + } + + if (type.IsEnum) + { + return GetJsTypeMask(Enum.GetUnderlyingType(type)); + } + + JsValueType mash = 0; + if (primitiveTypeMap.ContainsKey(type)) + { + mash = primitiveTypeMap[type]; + } + else if (type.IsArray) + { + mash = JsValueType.Array; + } + else if (type == typeof(DateTime)) + { + mash = JsValueType.Date; + } + else if (!type.IsAbstract() && typeof(Delegate).IsAssignableFrom(type)) + { + mash = JsValueType.Function | JsValueType.NativeObject | JsValueType.NullOrUndefined; + } + else if (type.IsValueType()) + { + mash = JsValueType.NativeObject/* | JsValueType.JsObject*/; //TODO: ֧jsC#Ĭת + } + else + { + mash = JsValueType.NativeObject | JsValueType.NullOrUndefined; + /*if ((type.IsClass() && type.GetConstructor(System.Type.EmptyTypes) != null)) + { + mash = mash | JsValueType.JsObject; + }*/ + } + + return mash; + } + } + + public delegate void GeneralSetter(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj); + + public class GeneralSetterManager + { + private ObjectPool objectPool; + + private TypeRegister typeRegister; + + private Dictionary generalSetterMap = new Dictionary(); + + public GeneralSetterManager(JsEnv jsEnv) + { + objectPool = jsEnv.objectPool; + typeRegister = jsEnv.TypeRegister; + + generalSetterMap[typeof(char)] = CharTranslator; + generalSetterMap[typeof(sbyte)] = SbyteTranslator; + generalSetterMap[typeof(byte)] = ByteTranslator; + generalSetterMap[typeof(short)] = ShortTranslator; + generalSetterMap[typeof(ushort)] = UshortTranslator; + generalSetterMap[typeof(int)] = IntTranslator; + generalSetterMap[typeof(uint)] = UintTranslator; + generalSetterMap[typeof(long)] = LongTranslator; + generalSetterMap[typeof(ulong)] = UlongTranslator; + generalSetterMap[typeof(double)] = DoubleTranslator; + generalSetterMap[typeof(float)] = FloatTranslator; + //translatorMap[typeof(decimal)] = decimalTranslator; + generalSetterMap[typeof(bool)] = BooleanTranslator; + generalSetterMap[typeof(string)] = StringTranslator; + generalSetterMap[typeof(DateTime)] = DateTranslator; + generalSetterMap[typeof(void)] = VoidTranslator; + generalSetterMap[typeof(object)] = AnyTranslator; + } + + private static void VoidTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + } + + private static void CharTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (char)obj); + } + + private static void SbyteTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (sbyte)obj); + } + + private static void ByteTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (byte)obj); + } + + private static void ShortTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (short)obj); + } + + private static void UshortTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (ushort)obj); + } + + private static void IntTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (int)obj); + } + + private static void UintTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (uint)obj); + } + + private static void LongTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetBigInt(isolate, holder, (long)obj); + } + + private static void UlongTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetBigInt(isolate, holder, (long)(ulong)obj); + } + + private static void DoubleTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (double)obj); + } + + private static void FloatTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetNumber(isolate, holder, (float)obj); + } + + private static void BooleanTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + setValueApi.SetBoolean(isolate, holder, (bool)obj); + } + + private static void StringTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + if (obj == null) + { + setValueApi.SetNull(isolate, holder); + } + else + { + setValueApi.SetString(isolate, holder, obj as string); + } + } + + private static void DateTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + DateTime date = (DateTime)obj; + setValueApi.SetDate(isolate, holder, (date - new DateTime(1970, 1, 1)).TotalMilliseconds); + } + + internal void AnyTranslator(IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) + { + if (obj == null) + { + setValueApi.SetNull(isolate, holder); + } + else + { + Type realType = obj.GetType(); + if (realType == typeof(object)) + { + int typeId = typeRegister.GetTypeId(isolate, realType); + int objectId = objectPool.FindOrAddObject(obj); + setValueApi.SetObject(isolate, holder, typeId, new IntPtr(objectId)); + } + else + { + GetTranslateFunc(realType)(isolate, setValueApi, holder, obj); + } + } + } + + private GeneralSetter MakeTranslateFunc(Type type) + { + if (type.IsValueType) + { + return (IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) => + { + if (obj == null) + { + setValueApi.SetNull(isolate, holder); + } + else + { + int typeId = typeRegister.GetTypeId(isolate, type); + int objectId = objectPool.AddBoxedValueType(obj); + setValueApi.SetObject(isolate, holder, typeId, new IntPtr(objectId)); + } + }; + } + else + { + return (IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, object obj) => + { + if (obj == null) + { + setValueApi.SetNull(isolate, holder); + } + else + { + int typeId = typeRegister.GetTypeId(isolate, type); + int objectId = objectPool.FindOrAddObject(obj); + setValueApi.SetObject(isolate, holder, typeId, new IntPtr(objectId)); + } + }; + } + } + + public GeneralSetter GetTranslateFunc(Type type) + { + if (type.IsByRef) return GetTranslateFunc(type.GetElementType()); + + if (type.IsEnum) return GetTranslateFunc(Enum.GetUnderlyingType(type)); + + GeneralSetter jvt; + if (!generalSetterMap.TryGetValue(type, out jvt)) + { + jvt = MakeTranslateFunc(type); + generalSetterMap.Add(type, jvt); + } + return jvt; + } + + public void RegisterSetter(Type type, GeneralSetter generalSetter) + { + generalSetterMap.Add(type, generalSetter); + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/Editor/Configure.cs b/Assets/Puerts/Src/Editor/Configure.cs new file mode 100644 index 00000000..64f5338e --- /dev/null +++ b/Assets/Puerts/Src/Editor/Configure.cs @@ -0,0 +1,118 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Collections; +using System; + +/************************************************************************************************ + * 配置 + * 1、Binding、BlittableCopy、Filter须放到一个打了Configure标签的类里; + * 2、Binding、BlittableCopy、Filter均用打了相应标签的属性来表示; + * 3、Binding、BlittableCopy、Filter配置须放到Editor目录下; +*************************************************************************************************/ + +namespace Puerts +{ + //放置配置的类 + [AttributeUsage(AttributeTargets.Class)] + public class ConfigureAttribute : Attribute + { + + } + + //要在ts/js里头调用,必须放在标记了Configure的类里 + [AttributeUsage(AttributeTargets.Property)] + public class BindingAttribute : Attribute + { + } + + //相比Binding,这标签仅生成ts声明 + [AttributeUsage(AttributeTargets.Property)] + public class TypingAttribute : Attribute + { + } + + //对blittable值类型通过内存拷贝传递,需要开启unsafe编译选项 + [AttributeUsage(AttributeTargets.Property)] + public class BlittableCopyAttribute : Attribute + { + + } + + [AttributeUsage(AttributeTargets.Method)] + public class FilterAttribute : Attribute + { + } + + public static class Configure + { + public static Dictionary>> GetConfigureByTags(List tags) + { + var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + from type in assembly.GetTypes() + where type.IsDefined(typeof(ConfigureAttribute), false) + select type; + var tagsMap = tags.ToDictionary(t => t, t => new List>()); + + foreach(var type in types) + { + foreach (var prop in type.GetProperties(BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType)) + { + foreach (var ca in prop.GetCustomAttributes(false)) + { + int flag = 0; + var fp = ca.GetType().GetProperty("Flag"); + if (fp != null) + { + flag = (int)fp.GetValue(ca, null); + } + List> infos; + if (tagsMap.TryGetValue(ca.GetType().ToString(), out infos)) + { + foreach (var applyTo in prop.GetValue(null, null) as IEnumerable) + { + infos.Add(new KeyValuePair(applyTo, flag)); + } + } + } + } + } + } + return tagsMap; + } + + public static List GetFilters() + { + var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + from type in assembly.GetTypes() + where type.IsDefined(typeof(ConfigureAttribute), false) + select type; + + List filters = new List(); + foreach (var type in types) + { + foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + if(method.IsDefined(typeof(FilterAttribute), false)) + { + filters.Add(method); + } + } + } + return filters; + } + } +} diff --git a/Assets/Puerts/Src/Editor/Generator.cs b/Assets/Puerts/Src/Editor/Generator.cs new file mode 100644 index 00000000..a93ea874 --- /dev/null +++ b/Assets/Puerts/Src/Editor/Generator.cs @@ -0,0 +1,731 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using UnityEngine; +using UnityEditor; +using System; +using System.Linq; +using System.Reflection; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Puerts.Editor +{ + public class Generator + { + const BindingFlags Flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; + + public static string GetGenName(Type type) + { + if (type.IsGenericType) + { + var argNames = type.GetGenericArguments().Select(x => GetGenName(x)).ToArray(); + return type.FullName.Split('`')[0] + "<" + string.Join(", ", argNames) + ">"; + } + else + return type.FullName; + } + + static List filters; + + public class TypeGenInfo + { + public string Name; + public string WrapClassName; + public MethodGenInfo[] Methods; + public bool IsValueType; + public MethodGenInfo Constructor; + public PropertyGenInfo[] Properties; + public IndexGenInfo[] GetIndexs; + public IndexGenInfo[] SetIndexs; + public MethodGenInfo[] Operators; + public EventGenInfo[] Events; + public bool BlittableCopy; + } + + public class DataTypeInfo + { + public string TypeName; + public bool IsEnum; + public string UnderlyingTypeName; + } + + public class ParameterGenInfo : DataTypeInfo + { + public bool IsOut; + public bool IsByRef; + public string ExpectJsType; + public string ExpectCsType; + public bool IsParams; + } + + public class PropertyGenInfo : DataTypeInfo + { + public string Name; + public bool IsStatic; + public bool HasGetter; + public bool HasSetter; + } + + public class IndexGenInfo : DataTypeInfo + { + public ParameterGenInfo IndexParameter; + public bool HasGetter; + public bool HasSetter; + } + + public class EventGenInfo : DataTypeInfo + { + public string Name; + public bool IsStatic; + public bool HasAdd; + public bool HasRemove; + } + + public class OverloadGenInfo : DataTypeInfo + { + public ParameterGenInfo[] ParameterInfos; + public bool IsVoid; + public bool HasParams; + } + + public class MethodGenInfo + { + public string Name; + public bool IsStatic; + public OverloadGenInfo[][] OverloadGroups; + public bool HasOverloads; + } + + static string ToCode(JsValueType ExpectJsType) + { + return string.Join(" | ", ExpectJsType.ToString().Split(',').Select(s => "Puerts.JsValueType." + s.Trim()).ToArray()); + } + + static void FillEnumInfo(DataTypeInfo info, Type type) + { + if (type.IsEnum) + { + info.IsEnum = true; + info.UnderlyingTypeName = Enum.GetUnderlyingType(type).GetFriendlyName(); + } + } + + static ParameterGenInfo ToParameterGenInfo(ParameterInfo parameterInfo) + { + var ExpectJsType = GeneralGetterManager.GetJsTypeMask(parameterInfo.ParameterType); + var result = new ParameterGenInfo() + { + IsOut = !parameterInfo.IsIn && parameterInfo.IsOut, + IsByRef = parameterInfo.ParameterType.IsByRef, + TypeName = (parameterInfo.ParameterType.IsByRef ? parameterInfo.ParameterType.GetElementType() : parameterInfo.ParameterType).GetFriendlyName(), + ExpectJsType = ToCode(ExpectJsType), + IsParams = parameterInfo.IsDefined(typeof(ParamArrayAttribute), false), + }; + if (result.IsParams) + { + result.TypeName = parameterInfo.ParameterType.GetElementType().GetFriendlyName(); + } + result.ExpectCsType = ((ExpectJsType & JsValueType.NativeObject) == JsValueType.NativeObject) ? string.Format("typeof({0})", result.TypeName) : "null"; + FillEnumInfo(result, parameterInfo.ParameterType); + return result; + } + + static IndexGenInfo ToIndexGenInfo(PropertyInfo propertyInfo) + { + var getMethod = propertyInfo.GetGetMethod(); + var setMethod = propertyInfo.GetSetMethod(); + var result = new IndexGenInfo() + { + TypeName = propertyInfo.PropertyType.GetFriendlyName(), + IndexParameter = ToParameterGenInfo(propertyInfo.GetIndexParameters()[0]), + HasGetter = getMethod != null && getMethod.IsPublic, + HasSetter = setMethod != null && setMethod.IsPublic, + }; + FillEnumInfo(result, propertyInfo.PropertyType); + return result; + } + + static PropertyGenInfo ToPropertyGenInfo(PropertyInfo propertyInfo) + { + var getMethod = propertyInfo.GetGetMethod(); + var setMethod = propertyInfo.GetSetMethod(); + bool isStatic = getMethod == null ? setMethod.IsStatic : getMethod.IsStatic; + var result = new PropertyGenInfo() + { + Name = propertyInfo.Name, + TypeName = propertyInfo.PropertyType.GetFriendlyName(), + IsStatic = isStatic, + HasGetter = getMethod != null && getMethod.IsPublic, + HasSetter = setMethod != null && setMethod.IsPublic, + }; + FillEnumInfo(result, propertyInfo.PropertyType); + return result; + } + + static EventGenInfo ToEventGenInfo(EventInfo eventInfo) + { + var addMethod = eventInfo.GetAddMethod(); + var removeMethod = eventInfo.GetRemoveMethod(); + bool isStatic = addMethod == null ? removeMethod.IsStatic : addMethod.IsStatic; + return new EventGenInfo() + { + Name = eventInfo.Name, + TypeName = eventInfo.EventHandlerType.GetFriendlyName(), + IsStatic = isStatic, + HasAdd = addMethod != null && addMethod.IsPublic, + HasRemove = removeMethod != null && removeMethod.IsPublic, + }; + } + + static PropertyGenInfo ToPropertyGenInfo(FieldInfo fieldInfo) + { + var result = new PropertyGenInfo() + { + Name = fieldInfo.Name, + TypeName = fieldInfo.FieldType.GetFriendlyName(), + IsStatic = fieldInfo.IsStatic, + HasGetter = true, + HasSetter = !fieldInfo.IsInitOnly && !fieldInfo.IsLiteral, + }; + FillEnumInfo(result, fieldInfo.FieldType); + return result; + } + + static MethodGenInfo ToMethodGenInfo(List overloads) + { + var result = new MethodGenInfo() + { + Name = overloads[0].Name, + IsStatic = overloads[0].IsStatic, + HasOverloads = overloads.Count > 1, + OverloadGroups = overloads.Select(o => ToOverloadGenInfo(o)).GroupBy(m => m.ParameterInfos.Length).Select(lst => lst.ToArray()).ToArray() + }; + return result; + } + + static OverloadGenInfo ToOverloadGenInfo(MethodBase methodBase) + { + OverloadGenInfo result = null; + if (methodBase is MethodInfo) + { + var methodInfo = methodBase as MethodInfo; + result = new OverloadGenInfo() + { + ParameterInfos = methodInfo.GetParameters().Select(info => ToParameterGenInfo(info)).ToArray(), + TypeName = methodInfo.ReturnType.GetFriendlyName(), + IsVoid = methodInfo.ReturnType == typeof(void) + }; + FillEnumInfo(result, methodInfo.ReturnType); + } + else if (methodBase is ConstructorInfo) + { + var constructorInfo = methodBase as ConstructorInfo; + result = new OverloadGenInfo() + { + ParameterInfos = constructorInfo.GetParameters().Select(info => ToParameterGenInfo(info)).ToArray(), + TypeName = constructorInfo.DeclaringType.GetFriendlyName(), + IsVoid = false + }; + } + else + { + throw new NotSupportedException(); + } + + result.HasParams = result.ParameterInfos.Any(info => info.IsParams); + + return result; + } + + static TypeGenInfo ToTypeGenInfo(Type type) + { + var methodGroups = type.GetMethods(Flags).Where(m => !isFiltered(m)) + .Where(m => !m.IsSpecialName && !m.IsGenericMethodDefinition) + .GroupBy(m => new MethodKey { Name = m.Name, IsStatic = m.IsStatic }) + .Select(i => i.Cast().ToList()); + var indexs = type.GetProperties(Flags).Where(m => !isFiltered(m)) + .Where(p => p.GetIndexParameters().GetLength(0) == 1).Select(p => ToIndexGenInfo(p)).ToArray(); + var operatorGroups = type.GetMethods(Flags) + .Where(m => !isFiltered(m) && m.IsSpecialName && m.Name.StartsWith("op_") && m.IsStatic) + .GroupBy(m => new MethodKey { Name = m.Name, IsStatic = m.IsStatic }) + .Select(i => i.Cast().ToList()); + + var constructors = type.GetConstructors(Flags).Where(m => !isFiltered(m)).Cast().ToList(); + + return new TypeGenInfo + { + WrapClassName = GetWrapTypeName(type), + Name = type.GetFriendlyName(), + Methods = methodGroups.Select(m => ToMethodGenInfo(m)).ToArray(), + IsValueType = type.IsValueType, + Constructor = constructors.Count > 0 ? ToMethodGenInfo(constructors) : null, + Properties = type.GetProperties(Flags) + .Where(m => !isFiltered(m)) + .Where(p => !p.IsSpecialName && p.GetIndexParameters().GetLength(0) == 0) + .Select(p => ToPropertyGenInfo(p)).Concat( + type.GetFields(Flags).Where(m => !isFiltered(m)).Select(f => ToPropertyGenInfo(f))).ToArray(), + GetIndexs = indexs.Where(i => i.HasGetter).ToArray(), + SetIndexs = indexs.Where(i => i.HasSetter).ToArray(), + Operators = operatorGroups.Select(m => ToMethodGenInfo(m)).ToArray(), + Events = type.GetEvents(Flags).Where(m => !isFiltered(m)).Select(e => ToEventGenInfo(e)).ToArray(), + }; + } + + static string GetWrapTypeName(Type type) + { + return type.ToString().Replace("+", "_").Replace(".", "_").Replace("`", "_").Replace("&", "_").Replace("[", "_").Replace("]", "_").Replace(",", "_") + "_Wrap"; + } + + public class TsParameterGenInfo + { + public string Name; + public bool IsByRef; + public string TypeName; + public bool IsParams; + } + + // #lizard forgives + static string GetTsTypeName(Type type) + { + if (type == typeof(int)) + return "number"; + if (type == typeof(uint)) + return "number"; + else if (type == typeof(short)) + return "number"; + else if (type == typeof(byte)) + return "number"; + else if (type == typeof(sbyte)) + return "number"; + else if (type == typeof(ushort)) + return "number"; + else if (type == typeof(bool)) + return "boolean"; + else if (type == typeof(long)) + return "bigint"; + else if (type == typeof(ulong)) + return "bigint"; + else if (type == typeof(float)) + return "number"; + else if (type == typeof(double)) + return "number"; + else if (type == typeof(string)) + return "string"; + else if (type == typeof(void)) + return "void"; + else if (type == typeof(DateTime)) + return "Date"; + else if (type == typeof(object)) + return "any"; + else if (type == typeof(Delegate)) + return "Function"; + else if (type.IsByRef) + return "$Ref<" + GetTsTypeName(type.GetElementType()) + ">"; + else if (type.IsArray) + return GetTsTypeName(type.GetElementType()) + "[]"; + else if (type.IsGenericType) + { + var fullName = type.FullName == null ? type.ToString() : type.FullName; + var parts = fullName.Replace('+', '.').Split('`'); + var argTypenames = type.GetGenericArguments() + .Select(x => GetTsTypeName(x)).ToArray(); + return parts[0] + '$' + parts[1].Split('[')[0] + "<" + string.Join(", ", argTypenames) + ">"; + } + else if (type.FullName == null) + return type.ToString(); + else + return type.FullName.Replace('+', '.'); + } + + static TsParameterGenInfo ToTsParameterGenInfo(ParameterInfo parameterInfo) + { + return new TsParameterGenInfo() + { + Name = parameterInfo.Name, + IsByRef = parameterInfo.ParameterType.IsByRef, + TypeName = GetTsTypeName(parameterInfo.ParameterType), + IsParams = parameterInfo.IsDefined(typeof(ParamArrayAttribute), false), + }; + } + + public class TsMethodGenInfo + { + public string Name; + public TsParameterGenInfo[] ParameterInfos; + public string TypeName; + public bool IsConstructor; + public bool IsStatic; + } + + public class TsPropertyGenInfo + { + public string Name; + public string TypeName; + public bool IsStatic; + } + + public static TsMethodGenInfo ToTsMethodGenInfo(MethodBase methodBase) + { + return new TsMethodGenInfo() + { + Name = methodBase.IsConstructor ? "constructor" : methodBase.Name, + ParameterInfos = methodBase.GetParameters().Select(info => ToTsParameterGenInfo(info)).ToArray(), + TypeName = methodBase.IsConstructor ? "" : GetTsTypeName((methodBase as MethodInfo).ReturnType), + IsConstructor = methodBase.IsConstructor, + IsStatic = methodBase.IsStatic, + }; + } + + public class TsTypeGenInfo + { + public string Name; + public TsMethodGenInfo[] Methods; + public TsPropertyGenInfo[] Properties; + public bool IsGenericTypeDefinition; + public string[] GenericParameters; + public bool IsDelegate; + public string DelegateDef; + public bool IsInterface; + public string Namespace; + public TsTypeGenInfo BaseType; + public bool IsEnum; + public string EnumKeyValues; + } + + public static bool IsGetterOrSetter(MethodInfo method) + { + return (method.IsSpecialName && method.Name.StartsWith("get_") && method.GetParameters().Length != 1) + || (method.IsSpecialName && method.Name.StartsWith("set_") && method.GetParameters().Length != 2); + } + + public static bool IsDelegate(Type type) + { + if (type == null) + { + return false; + } + else if (type == typeof(Delegate)) + { + return true; + } + else + { + return IsDelegate(type.BaseType); + } + } + + static bool IsStatic(PropertyInfo propertyInfo) + { + var getMethod = propertyInfo.GetGetMethod(); + var setMethod = propertyInfo.GetSetMethod(); + return getMethod == null ? setMethod.IsStatic : getMethod.IsStatic; + } + + public static TsTypeGenInfo ToTsTypeGenInfo(Type type, HashSet genTypeSet) + { + var result = new TsTypeGenInfo() + { + Name = type.Name.Replace('`', '$'), + Methods = genTypeSet.Contains(type) ? type.GetConstructors(Flags).Where(m => !isFiltered(m)).Cast() + .Concat(type.GetMethods(Flags) + .Where(m => !isFiltered(m) && !IsGetterOrSetter(m) && !m.IsGenericMethodDefinition) + .Cast()) + .Select(m => ToTsMethodGenInfo(m)).ToArray() : new TsMethodGenInfo[] { }, + Properties = genTypeSet.Contains(type) ? type.GetFields(Flags).Where(m => !isFiltered(m)) + .Select(f => new TsPropertyGenInfo() { Name = f.Name, TypeName = GetTsTypeName(f.FieldType), IsStatic = f.IsStatic }) + .Concat( + type.GetProperties(Flags).Where(m => !isFiltered(m)) + .Select(p => new TsPropertyGenInfo() { Name = p.Name, TypeName = GetTsTypeName(p.PropertyType), IsStatic = IsStatic(p)})) + .ToArray() : new TsPropertyGenInfo[] { }, + IsGenericTypeDefinition = type.IsGenericTypeDefinition, + IsDelegate = (IsDelegate(type) && type != typeof(Delegate)), + IsInterface = type.IsInterface, + Namespace = type.Namespace, + }; + + if (result.IsGenericTypeDefinition) + { + result.GenericParameters = type.GetGenericArguments().Select(t => t.ToString()).ToArray(); + } + + if (result.IsDelegate) + { + if (type == typeof(Delegate)) + { + result.DelegateDef = "(...args:any[]) => any"; + } + else + { + var m = type.GetMethod("Invoke"); + var tsFuncDef = "(" + string.Join(", ", m.GetParameters().Select(p => p.Name + ": " + GetTsTypeName(p.ParameterType)).ToArray()) + ") => " + GetTsTypeName(m.ReturnType); + result.DelegateDef = tsFuncDef; + } + } + + if (type.IsNested) + { + List p = new List(); + Type temp = type; + while (temp.IsNested) + { + p.Add(temp.DeclaringType.Name.Replace('`', '$')); + temp = temp.DeclaringType; + } + p.Reverse(); + if (type.Namespace != null) + { + result.Namespace = type.Namespace + '.' + string.Join(".", p.ToArray()); + } + else + { + result.Namespace = string.Join(".", p.ToArray()); + } + } + + if (genTypeSet.Contains(type) && !type.IsEnum && type.BaseType != null && type != typeof(object) && !result.IsDelegate && !result.IsInterface) + { + result.BaseType = ToTsTypeGenInfo(type.BaseType, genTypeSet); + } + + if (type.IsEnum) + { + result.IsEnum = true; + var KeyValues = type.GetFields(BindingFlags.Static | BindingFlags.Public) + .Where(f => f.Name != "value__") + .Select(f => f.Name + " = " + Convert.ToInt32(f.GetValue(null))).ToArray(); + result.EnumKeyValues = string.Join(", ", KeyValues); + } + + return result; + } + + public class TsNamespaceGenInfo + { + public string Name; + public TsTypeGenInfo[] Types; + } + + public class TsTypingGenInfo + { + public TsNamespaceGenInfo[] Namespaces; + + public TsTypeGenInfo[] Types; + } + + static Type GetRawType(Type type) + { + if (type.IsByRef || type.IsArray) + { + return GetRawType(type.GetElementType()); + } + if (type.IsGenericType) return type.GetGenericTypeDefinition(); + return type; + } + + static bool isFiltered(MemberInfo mb) + { + if (mb == null) return false; + ObsoleteAttribute oa = mb.GetCustomAttributes(typeof(ObsoleteAttribute), false).FirstOrDefault() as ObsoleteAttribute; + if (oa != null && oa.IsError) + { + return true; + } + + if (filters != null && filters.Count > 0) + { + foreach (var filter in filters) + { + if ((bool)filter.Invoke(null, new object[] { mb })) + { + return true; + } + } + } + + return false; + } + + static void AddRefType(HashSet refTypes, Type type) + { + type = GetRawType(type); + if (type.IsGenericParameter) return; + refTypes.Add(type); + } + + public class TypingGenInfo + { + public TsNamespaceGenInfo[] NamespaceInfos; + + public string TaskDef; + } + + static TypingGenInfo ToTypingGenInfo(IEnumerable types) + { + HashSet genTypeSet = new HashSet(); + + HashSet refTypes = new HashSet(); + + foreach(var type in types) + { + AddRefType(refTypes, type); + var defType = type.IsGenericType ? type.GetGenericTypeDefinition() : type; + if (!genTypeSet.Contains(defType)) genTypeSet.Add(defType); + foreach (var field in type.GetFields(Flags)) + { + AddRefType(refTypes, field.FieldType); + } + + foreach(var method in type.GetMethods(Flags)) + { + AddRefType(refTypes, method.ReturnType); + foreach(var pinfo in method.GetParameters()) + { + AddRefType(refTypes, pinfo.ParameterType); + } + } + foreach(var constructor in type.GetConstructors()) + { + foreach (var pinfo in constructor.GetParameters()) + { + AddRefType(refTypes, pinfo.ParameterType); + } + } + + var baseType = type.BaseType; + while (baseType != null) + { + AddRefType(refTypes, baseType); + baseType = baseType.BaseType; + } + } + + return new TypingGenInfo() + { + NamespaceInfos = refTypes.Distinct().Select(t => ToTsTypeGenInfo(t, genTypeSet)).GroupBy(t => t.Namespace) + .Select(g => new TsNamespaceGenInfo() + { + Name = g.Key, + Types = g.ToArray() + }).ToArray(), +#if CSHARP_7_3_OR_NEWER + TaskDef = refTypes.Contains(typeof(System.Threading.Tasks.Task<>)) ? + "type $Task = System.Threading.Tasks.Task$1" + : "interface $Task {}", +#else + TaskDef = "interface $Task {}", +#endif + }; + } + + [MenuItem("tgame.js/Generate Code", false, 1)] + public static void GenerateCode() + { + var start = DateTime.Now; + var saveTo = Application.dataPath + "/Gen/"; + Directory.CreateDirectory(saveTo); + Directory.CreateDirectory(saveTo + "Typing/csharp"); + GenerateCode(saveTo); + Debug.Log("finished! use " + (DateTime.Now - start).TotalMilliseconds + " ms"); + AssetDatabase.Refresh(); + } + + [MenuItem("tgame.js/Generate index.d.ts", false, 1)] + public static void GenerateDTS() + { + var start = DateTime.Now; + var saveTo = Application.dataPath + "/Gen/"; + Directory.CreateDirectory(saveTo); + Directory.CreateDirectory(saveTo + "Typing/csharp"); + GenerateCode(saveTo, true); + Debug.Log("finished! use " + (DateTime.Now - start).TotalMilliseconds + " ms"); + AssetDatabase.Refresh(); + } + + [MenuItem("tgame.js/Clear Generated Code", false, 2)] + public static void ClearAll() + { + var saveTo = Application.dataPath + "/Gen/"; + if (Directory.Exists(saveTo)) + { + Directory.Delete(saveTo, true); + AssetDatabase.DeleteAsset(saveTo.Substring(saveTo.IndexOf("Assets") + "Assets".Length)); + AssetDatabase.Refresh(); + } + } + + public static void GenerateCode(string saveTo, bool tsOnly = false) + { + filters = Configure.GetFilters(); + var configure = Configure.GetConfigureByTags(new List() { + "Puerts.BindingAttribute", + "Puerts.BlittableCopyAttribute", + "Puerts.TypingAttribute", + }); + + var genTypes = configure["Puerts.BindingAttribute"].Select( kv => kv.Key) + .Where(o => o is Type) + .Cast() + .Where(t => !t.IsGenericTypeDefinition); + + var blittableCopyTypes = new HashSet(configure["Puerts.BlittableCopyAttribute"].Select(kv => kv.Key) + .Where(o => o is Type) + .Cast() + .Where(t => t.IsValueType && !t.IsPrimitive) + .Distinct()); + + var tsTypes = configure["Puerts.TypingAttribute"].Select(kv => kv.Key) + .Where(o => o is Type) + .Cast() + .Where(t => !t.IsGenericTypeDefinition) + .Concat(genTypes) + .Distinct(); + + using (var jsEnv = new JsEnv()) + { + var templateGetter = jsEnv.Eval>>("require('puerts/gencode/main.js')"); + var wrapRender = templateGetter("type.tpl"); + + if (!tsOnly) + { + var typeGenInfos = new List(); + foreach (var type in genTypes) + { + if (type.IsEnum || type.IsArray || (IsDelegate(type) && type != typeof(Delegate))) continue; + TypeGenInfo typeGenInfo = ToTypeGenInfo(type); + typeGenInfo.BlittableCopy = blittableCopyTypes.Contains(type); + typeGenInfos.Add(typeGenInfo); + string filePath = saveTo + typeGenInfo.WrapClassName + ".cs"; + string fileContext = wrapRender(typeGenInfo); + using (StreamWriter textWriter = new StreamWriter(filePath, false, Encoding.UTF8)) + { + textWriter.Write(fileContext); + textWriter.Flush(); + } + } + + var autoRegisterRender = templateGetter("autoreg.tpl"); + using (StreamWriter textWriter = new StreamWriter(saveTo + "AutoStaticCodeRegister.cs", false, Encoding.UTF8)) + { + string fileContext = autoRegisterRender(typeGenInfos.ToArray()); + textWriter.Write(fileContext); + textWriter.Flush(); + } + } + + var typingRender = templateGetter("typing.tpl"); + using (StreamWriter textWriter = new StreamWriter(saveTo + "Typing/csharp/index.d.ts", false, Encoding.UTF8)) + { + string fileContext = typingRender(ToTypingGenInfo(tsTypes)); + textWriter.Write(fileContext); + textWriter.Flush(); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/Editor/Resources/puerts/gencode/doT.js.txt b/Assets/Puerts/Src/Editor/Resources/puerts/gencode/doT.js.txt new file mode 100644 index 00000000..c61400af --- /dev/null +++ b/Assets/Puerts/Src/Editor/Resources/puerts/gencode/doT.js.txt @@ -0,0 +1,144 @@ +// doT.js +// 2011-2014, Laura Doktorova, https://github.com/olado/doT +// Licensed under the MIT license. + +(function () { + "use strict"; + + var doT = { + name: "doT", + version: "1.1.1", + templateSettings: { + evaluate: /\{\{([\s\S]+?(\}?)+)\}\}/g, + interpolate: /\{\{=([\s\S]+?)\}\}/g, + encode: /\{\{!([\s\S]+?)\}\}/g, + use: /\{\{#([\s\S]+?)\}\}/g, + useParams: /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g, + define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g, + defineParams:/^\s*([\w$]+):([\s\S]+)/, + conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g, + iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g, + varname: "it", + strip: true, + append: true, + selfcontained: false, + doNotSkipEncoded: false + }, + template: undefined, //fn, compile template + compile: undefined, //fn, for express + log: true + }, _globals; + + doT.encodeHTMLSource = function(doNotSkipEncoded) { + var encodeHTMLRules = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "/": "/" }, + matchHTML = doNotSkipEncoded ? /[&<>"'\/]/g : /&(?!#?\w+;)|<|>|"|'|\//g; + return function(code) { + return code ? code.toString().replace(matchHTML, function(m) {return encodeHTMLRules[m] || m;}) : ""; + }; + }; + + _globals = (function(){ return this || (0,eval)("this"); }()); + + /* istanbul ignore else */ + if (typeof module !== "undefined" && module.exports) { + module.exports = doT; + } else if (typeof define === "function" && define.amd) { + define(function(){return doT;}); + } else { + _globals.doT = doT; + } + + var startend = { + append: { start: "'+(", end: ")+'", startencode: "'+encodeHTML(" }, + split: { start: "';out+=(", end: ");out+='", startencode: "';out+=encodeHTML(" } + }, skip = /$^/; + + function resolveDefs(c, block, def) { + return ((typeof block === "string") ? block : block.toString()) + .replace(c.define || skip, function(m, code, assign, value) { + if (code.indexOf("def.") === 0) { + code = code.substring(4); + } + if (!(code in def)) { + if (assign === ":") { + if (c.defineParams) value.replace(c.defineParams, function(m, param, v) { + def[code] = {arg: param, text: v}; + }); + if (!(code in def)) def[code]= value; + } else { + new Function("def", "def['"+code+"']=" + value)(def); + } + } + return ""; + }) + .replace(c.use || skip, function(m, code) { + if (c.useParams) code = code.replace(c.useParams, function(m, s, d, param) { + if (def[d] && def[d].arg && param) { + var rw = (d+":"+param).replace(/'|\\/g, "_"); + def.__exp = def.__exp || {}; + def.__exp[rw] = def[d].text.replace(new RegExp("(^|[^\\w$])" + def[d].arg + "([^\\w$])", "g"), "$1" + param + "$2"); + return s + "def.__exp['"+rw+"']"; + } + }); + var v = new Function("def", "return " + code)(def); + return v ? resolveDefs(c, v, def) : v; + }); + } + + function unescape(code) { + return code.replace(/\\('|\\)/g, "$1").replace(/[\r\t\n]/g, " "); + } + + doT.template = function(tmpl, c, def) { + c = c || doT.templateSettings; + var cse = c.append ? startend.append : startend.split, needhtmlencode, sid = 0, indv, + str = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl; + + str = ("var out='" + (c.strip ? str.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ") + .replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""): str) + .replace(/'|\\/g, "\\$&") + .replace(c.interpolate || skip, function(m, code) { + return cse.start + unescape(code) + cse.end; + }) + .replace(c.encode || skip, function(m, code) { + needhtmlencode = true; + return cse.startencode + unescape(code) + cse.end; + }) + .replace(c.conditional || skip, function(m, elsecase, code) { + return elsecase ? + (code ? "';}else if(" + unescape(code) + "){out+='" : "';}else{out+='") : + (code ? "';if(" + unescape(code) + "){out+='" : "';}out+='"); + }) + .replace(c.iterate || skip, function(m, iterate, vname, iname) { + if (!iterate) return "';} } out+='"; + sid+=1; indv=iname || "i"+sid; iterate=unescape(iterate); + return "';var arr"+sid+"="+iterate+";if(arr"+sid+"){var "+vname+","+indv+"=-1,l"+sid+"=arr"+sid+".length-1;while("+indv+"', + op_LessThan: '<', + op_GreaterThanOrEqual: '>=', + op_LessThanOrEqual: '<=', + op_BitwiseAnd: '&', + op_BitwiseOr: '|', + op_Addition: '+', + op_Subtraction: '-', + op_Division: '/', + op_Modulus: '%', + op_Multiply: '*', + op_LeftShift: '<<', + op_RightShift: '>>', + op_ExclusiveOr: '^', + op_UnaryNegation: '-', + op_UnaryPlus: '+', + op_LogicalNot: '!', + op_OnesComplement: '~', + op_False: '', + op_True: '', + op_Increment: '++', + op_Decrement: '--', +}; +function getArgument(typeInfo, argHelper, idx) { + let typeName = typeInfo.TypeName; + let isByRef = typeInfo.IsByRef ? "true" : "false"; + if (typeInfo.IsParams) { + return `${argHelper}.GetParams<${typeName}>(info, ${idx}, paramLen)`; + } else if (typeInfo.IsEnum) { + return `(${typeName})${argHelper}.${fixGet[typeInfo.UnderlyingTypeName]}(${isByRef})`; + } else if (typeName in fixGet) { + return `${argHelper}.${fixGet[typeName]}(${isByRef})`; + } else { + return `${argHelper}.Get<${typeName}>(${isByRef})`; + } +} +function setReturn(typeInfo) { + let typeName = typeInfo.TypeName; + if (typeName in fixReturn) { + return fixReturn[typeName]; + } else if (typeInfo.IsEnum) { + return fixReturn[typeInfo.UnderlyingTypeName].replace('result', `(${typeInfo.UnderlyingTypeName})result`); + } else { + return `Puerts.StaticTranslate<${typeName}>.Set((int)data, isolate, Puerts.NativeValueApi.SetValueToResult, info, result)`; + } +} +function operatorCall(methodName, argCount, typeInfo) { + if (methodName == 'op_Implicit') { + return `(${typeInfo.TypeName})arg0`; + } + if (argCount == 1) { + return operatorMap[methodName] + 'arg0'; + } else if (argCount == 2) { + return 'arg0 ' + operatorMap[methodName] + ' arg1'; + } +} + +function getSelf(type) { + if (type.IsValueType) { + return `(${type.Name})Puerts.Utils.GetSelf((int)data, self)`; + } else if (it.BlittableCopy) { + return `*(${type.Name}*)self`; + } else { + return `Puerts.Utils.GetSelf((int)data, self) as ${type.Name}`; + } +} +}} +namespace PuertsStaticWrap +{ + public static class {{=it.WrapClassName}} + { + {{?it.BlittableCopy}}static {{=it.Name}} HeapValue; + {{?}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8ConstructorCallback))] + {{?it.BlittableCopy}}unsafe {{?}}private static IntPtr Constructor(IntPtr isolate, IntPtr info, int paramLen, long data) + { + try + { + {{?it.Constructor}}{{~it.Constructor.OverloadGroups :overloadGroup}} + {{?it.Constructor.HasOverloads}}if (paramLen == {{=overloadGroup[0].ParameterInfos.length}}){{?}} + { + {{for(var i=0; i < overloadGroup[0].ParameterInfos.length; i++) {}} + var argHelper{{=i}} = new Puerts.ArgumentHelper((int)data, isolate, info, {{=i}});{{}}} + + {{~overloadGroup :overload}} + {{?it.Constructor.HasOverloads && overload.ParameterInfos.length > 0}}if ({{~overload.ParameterInfos :paramInfo:idx}}{{?idx!=0}} + && {{?}}argHelper{{=idx}}.IsMatch({{=paramInfo.ExpectJsType}}, {{=paramInfo.ExpectCsType}}, {{=paramInfo.IsByRef}}, {{=paramInfo.IsOut}}){{~}}){{?}} + { + {{~overload.ParameterInfos :paramInfo:idx}} + var Arg{{=idx}} = {{=getArgument(paramInfo, 'argHelper' + idx, idx)}};{{~}} + {{=it.BlittableCopy?"HeapValue":"var result"}} = new {{=it.Name}}({{~overload.ParameterInfos :paramInfo:idx}}{{?idx!=0}},{{?}}{{=paramInfo.IsOut?"out ":(paramInfo.IsByRef?"ref ":"")}}Arg{{=idx}}{{~}}); + {{~overload.ParameterInfos :paramInfo:idx}}{{?paramInfo.IsByRef}} + argHelper{{=idx}}.SetByRefValue(Arg{{=idx}});{{?}}{{~}} + + {{if(it.BlittableCopy){}}fixed ({{=it.Name}}* result = &HeapValue) + { + return new IntPtr(result); + }{{}else{}}return Puerts.Utils.GetObjectPtr((int)data, typeof({{=it.Name}}), result);{{}}} + }{{~}} + } + {{~}}{{?}} + {{?it.Constructor && it.Constructor.HasOverloads}}Puerts.PuertsDLL.ThrowException(isolate, "invalid arguments to {{=it.Name}} constructor");{{?}} + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + return IntPtr.Zero; + } + {{~it.Methods :method}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8FunctionCallback))] + {{?it.BlittableCopy && !method.IsStatic}}unsafe {{?}}private static void {{=(method.IsStatic ? "F" : "M")}}_{{=method.Name}}(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + try + { + {{?!method.IsStatic}}var obj = {{=getSelf(it)}};{{?}} + {{~method.OverloadGroups :overloadGroup}} + {{?method.HasOverloads}}if (paramLen == {{=overloadGroup[0].ParameterInfos.length}}){{?}} + { + {{for(var i=0; i < overloadGroup[0].ParameterInfos.length; i++) {}} + var argHelper{{=i}} = new Puerts.ArgumentHelper((int)data, isolate, info, {{=i}});{{}}} + + {{~overloadGroup :overload}} + {{?method.HasOverloads && overload.ParameterInfos.length > 0}}if ({{~overload.ParameterInfos :paramInfo:idx}}{{?idx!=0}} + && {{?}}argHelper{{=idx}}.IsMatch({{=paramInfo.ExpectJsType}}, {{=paramInfo.ExpectCsType}}, {{=paramInfo.IsByRef}}, {{=paramInfo.IsOut}}){{~}}){{?}} + { + {{~overload.ParameterInfos :paramInfo:idx}} + var Arg{{=idx}} = {{=getArgument(paramInfo, 'argHelper' + idx, idx)}};{{~}} + {{=overload.IsVoid?"":"var result = "}}{{=method.IsStatic?it.Name:"obj"}}.{{=method.Name}}({{~overload.ParameterInfos :paramInfo:idx}}{{?idx!=0}},{{?}}{{=paramInfo.IsOut?"out ":(paramInfo.IsByRef?"ref ":"")}}Arg{{=idx}}{{~}}); + {{~overload.ParameterInfos :paramInfo:idx}}{{?paramInfo.IsByRef}} + argHelper{{=idx}}.SetByRefValue(Arg{{=idx}});{{?}}{{~}} + {{?!overload.IsVoid}}{{=setReturn(overload)}};{{?}} + {{?method.HasOverloads}}return;{{?}} + }{{~}} + } + {{~}} + {{?method.HasOverloads}}Puerts.PuertsDLL.ThrowException(isolate, "invalid arguments to {{=method.Name}}");{{?}} + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + {{~}} + {{~it.Properties :property}} + {{?property.HasGetter}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8FunctionCallback))] + {{?it.BlittableCopy && !property.IsStatic}}unsafe {{?}}private static void G_{{=property.Name}}(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + try + { + {{?!property.IsStatic}}{{=it.Name}} obj = {{=getSelf(it)}};{{?}} + var result = {{=property.IsStatic?it.Name:"obj"}}.{{=property.Name}}; + {{=setReturn(property)}}; + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + }{{?}} + {{?property.HasSetter}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8FunctionCallback))] + {{?it.BlittableCopy && !property.IsStatic}}unsafe {{?}}private static void S_{{=property.Name}}(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + try + { + {{?!property.IsStatic}}{{=it.Name}} obj = {{=getSelf(it)}};{{?}} + var argHelper = new Puerts.ArgumentHelper((int)data, isolate, info, 0); + {{=property.IsStatic?it.Name:"obj"}}.{{=property.Name}} = {{=getArgument(property, 'argHelper')}}; + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + }{{?}} + {{~}} + {{?it.GetIndexs.length > 0}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8FunctionCallback))] + {{?it.BlittableCopy}}unsafe {{?}}private static void GetItem(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + try + { + var obj = {{=getSelf(it)}}; + var keyHelper = new Puerts.ArgumentHelper((int)data, isolate, info, 0); + {{~it.GetIndexs :indexInfo}} + if (keyHelper.IsMatch({{=indexInfo.IndexParameter.ExpectJsType}}, {{=indexInfo.IndexParameter.ExpectCsType}}, {{=indexInfo.IndexParameter.IsByRef}}, {{=indexInfo.IndexParameter.IsOut}})) + { + var key = {{=getArgument(indexInfo.IndexParameter, 'keyHelper')}}; + var result = obj[key]; + {{=setReturn(indexInfo)}}; + return; + } + {{~}} + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + {{?}}{{?it.SetIndexs.length > 0}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8FunctionCallback))] + {{?it.BlittableCopy}}unsafe {{?}}private static void SetItem(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + try + { + var obj = {{=getSelf(it)}}; + var keyHelper = new Puerts.ArgumentHelper((int)data, isolate, info, 0); + var valueHelper = new Puerts.ArgumentHelper((int)data, isolate, info, 1); + {{~it.SetIndexs :indexInfo}} + if (keyHelper.IsMatch({{=indexInfo.IndexParameter.ExpectJsType}}, {{=indexInfo.IndexParameter.ExpectCsType}}, {{=indexInfo.IndexParameter.IsByRef}}, {{=indexInfo.IndexParameter.IsOut}})) + { + var key = {{=getArgument(indexInfo.IndexParameter, 'keyHelper')}}; + obj[key] = {{=getArgument(indexInfo, 'valueHelper')}}; + return; + } + {{~}} + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + }{{?}} + {{~it.Operators :operator}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8FunctionCallback))] + private static void O_{{=operator.Name}}(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + try + { + {{~operator.OverloadGroups :overloadGroup}} + {{?operator.HasOverloads}}if (paramLen == {{=overloadGroup[0].ParameterInfos.length}}){{?}} + { + {{for(var i=0; i < overloadGroup[0].ParameterInfos.length; i++) {}} + var argHelper{{=i}} = new Puerts.ArgumentHelper((int)data, isolate, info, {{=i}});{{}}} + + {{~overloadGroup :overload}} + {{?operator.HasOverloads && overload.ParameterInfos.length > 0}}if ({{~overload.ParameterInfos :paramInfo:idx}}{{?idx!=0}} + && {{?}}argHelper{{=idx}}.IsMatch({{=paramInfo.ExpectJsType}}, {{=paramInfo.ExpectCsType}}, {{=paramInfo.IsByRef}}, {{=paramInfo.IsOut}}){{~}}){{?}} + { + {{~overload.ParameterInfos :paramInfo:idx}} + var arg{{=idx}} = {{=getArgument(paramInfo, 'argHelper' + idx, idx)}};{{~}} + var result = {{=operatorCall(operator.Name, overload.ParameterInfos.length, overload)}}; + {{?!overload.IsVoid}}{{=setReturn(overload)}};{{?}} + {{?operator.HasOverloads}}return;{{?}} + }{{~}} + } + {{~}} + {{?operator.HasOverloads}}Puerts.PuertsDLL.ThrowException(isolate, "invalid arguments to {{=operator.Name}}");{{?}} + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + {{~}} + {{~it.Events :eventInfo}} + {{?eventInfo.HasAdd}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8FunctionCallback))] + {{?it.BlittableCopy && !eventInfo.IsStatic}}unsafe {{?}}private static void A_{{=eventInfo.Name}}(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + try + { + {{?!eventInfo.IsStatic}}var obj = {{=getSelf(it)}};{{?}} + var argHelper = new Puerts.ArgumentHelper((int)data, isolate, info, 0); + {{=eventInfo.IsStatic?it.Name:"obj"}}.{{=eventInfo.Name}} += {{=getArgument(eventInfo, 'argHelper')}}; + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + {{?}}{{?eventInfo.HasRemove}} + [Puerts.MonoPInvokeCallback(typeof(Puerts.V8FunctionCallback))] + {{?it.BlittableCopy && !eventInfo.IsStatic}}unsafe {{?}}private static void R_{{=eventInfo.Name}}(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + try + { + {{?!eventInfo.IsStatic}}var obj = {{=getSelf(it)}};{{?}} + var argHelper = new Puerts.ArgumentHelper((int)data, isolate, info, 0); + {{=eventInfo.IsStatic?it.Name:"obj"}}.{{=eventInfo.Name}} -= {{=getArgument(eventInfo, 'argHelper')}}; + } + catch (Exception e) + { + Puerts.PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + {{?}}{{~}} + public static Puerts.TypeRegisterInfo GetRegisterInfo() + { + return new Puerts.TypeRegisterInfo() + { + BlittableCopy = {{=it.BlittableCopy}}, + Constructor = Constructor, + Methods = new System.Collections.Generic.Dictionary() + { + {{~it.Methods :method}}{ new Puerts.MethodKey {Name = "{{=method.Name}}", IsStatic = {{=method.IsStatic}}}, {{=(method.IsStatic ? "F" : "M")}}_{{=method.Name}} }, + {{~}}{{?it.GetIndexs.length > 0}}{ new Puerts.MethodKey {Name = "get_Item", IsStatic = false}, GetItem }, + {{?}}{{?it.SetIndexs.length > 0}}{ new Puerts.MethodKey {Name = "set_Item", IsStatic = false}, SetItem}, + {{?}}{{~it.Operators :operator}}{ new Puerts.MethodKey {Name = "{{=operator.Name}}", IsStatic = true}, O_{{=operator.Name}}}, + {{~}}{{~it.Events :eventInfo}}{{?eventInfo.HasAdd}}{ new Puerts.MethodKey {Name = "add_{{=eventInfo.Name}}", IsStatic = {{=eventInfo.IsStatic}}}, A_{{=eventInfo.Name}}}, + {{?}}{{?eventInfo.HasRemove}}{ new Puerts.MethodKey {Name = "remove_{{=eventInfo.Name}}", IsStatic = {{=eventInfo.IsStatic}}}, R_{{=eventInfo.Name}}}, + {{?}}{{~}} + }, + Properties = new System.Collections.Generic.Dictionary() + { + {{~it.Properties :property}}{"{{=property.Name}}", new Puerts.PropertyRegisterInfo(){ IsStatic = {{=property.IsStatic}}, Getter = {{=property.HasGetter?"G_"+property.Name:"null"}}, Setter = {{=property.HasSetter?"S_"+property.Name:"null"}}} }, + {{~}} + } + }; + } + {{?it.BlittableCopy}} + unsafe private static {{=it.Name}} StaticGetter(int jsEnvIdx, IntPtr isolate, Puerts.IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + {{=it.Name}}* result = ({{=it.Name}}*)getValueApi.GetObject(isolate, value, isByRef); + return *result; + } + + unsafe private static void StaticSetter(int jsEnvIdx, IntPtr isolate, Puerts.ISetValueToJs setValueApi, IntPtr value, {{=it.Name}} val) + { + HeapValue = val; + fixed ({{=it.Name}}* result = &HeapValue) + { + var typeId = Puerts.JsEnv.jsEnvs[jsEnvIdx].GetTypeId(typeof({{=it.Name}})); + setValueApi.SetObject(isolate, value, typeId, new IntPtr(result)); + } + } + + public static void InitBlittableCopy(Puerts.JsEnv jsEnv) + { + Puerts.StaticTranslate<{{=it.Name}}>.ReplaceDefault(StaticSetter, StaticGetter); + int jsEnvIdx = jsEnv.Index; + jsEnv.RegisterGeneralGetSet(typeof({{=it.Name}}), (IntPtr isolate, Puerts.IGetValueFromJs getValueApi, IntPtr value, bool isByRef) => + { + return StaticGetter(jsEnvIdx, isolate, getValueApi, value, isByRef); + }, (IntPtr isolate, Puerts.ISetValueToJs setValueApi, IntPtr value, object obj) => + { + StaticSetter(jsEnvIdx, isolate, setValueApi, value, ({{=it.Name}})obj); + }); + } + {{?}} + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/Editor/Resources/puerts/templates/typing.tpl.txt b/Assets/Puerts/Src/Editor/Resources/puerts/templates/typing.tpl.txt new file mode 100644 index 00000000..da281a45 --- /dev/null +++ b/Assets/Puerts/Src/Editor/Resources/puerts/templates/typing.tpl.txt @@ -0,0 +1,46 @@ +{{ +function parameterDef(pinfo) { + return (pinfo.IsParams ? ("..." + pinfo.Name) : pinfo.Name) + ": " + pinfo.TypeName; +} +function typeKeyword(type) { + if (type.IsDelegate) { + return 'type'; + } else if (type.IsInterface) { + return 'interface'; + } else if (type.IsEnum) { + return 'enum'; + } else { + return 'class' + } +} + +function typeDeclaration(type, level1) { + var result = type.Name; + if (type.IsGenericTypeDefinition) { + result += "<" + Array.prototype.join.call(type.GenericParameters, ',') + ">"; + } + if (level1 && type.BaseType) { + result += " extends " + typeDeclaration(type.BaseType); + } + if(!level1 && type.Namespace) { + result = type.Namespace + "." + result; + } + return result; +} +}} +declare module 'csharp' { + interface $Ref {} + + {{=it.TaskDef}} + + {{~it.NamespaceInfos :ns}}{{?ns.Name}}namespace {{=ns.Name}} {{{?}} + {{~ns.Types :type}}{{=typeKeyword(type)}} {{=typeDeclaration(type, true)}} {{if(type.IsDelegate){}}= {{=type.DelegateDef}};{{?!type.IsGenericTypeDefinition}} + var {{=type.Name}}: {new (func: {{=type.DelegateDef}}): {{=type.Name}};}{{?}}{{ } else if(type.IsEnum) { }}{ {{=type.EnumKeyValues}} }{{ }else{ }}{ + {{~type.Properties :property}}public {{?property.IsStatic}}static {{?}}{{=property.Name}}: {{=property.TypeName}}; + {{~}}{{~type.Methods :method}}public {{?method.IsStatic}}static {{?}}{{=method.Name}}({{~method.ParameterInfos :pinfo:idx}}{{?idx>0}}, {{?}}{{=parameterDef(pinfo)}}{{~}}){{=method.IsConstructor?"":":" + method.TypeName}}; + {{~}} + }{{}}} + {{~}} + {{?ns.Name}}}{{?}} + {{~}} +} \ No newline at end of file diff --git a/Assets/Puerts/Src/Editor/U2018Compatible.cs b/Assets/Puerts/Src/Editor/U2018Compatible.cs new file mode 100644 index 00000000..4bb198f6 --- /dev/null +++ b/Assets/Puerts/Src/Editor/U2018Compatible.cs @@ -0,0 +1,47 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System.Collections.Generic; +using Puerts; +using System.Reflection; + +//1、配置类必须打[Configure]标签 +//2、必须放Editor目录 +[Configure] +public class U2018Compatible +{ +#if UNITY_2018_1_OR_NEWER + [Filter] + static bool Filter(MemberInfo memberInfo) + { + if (memberInfo.DeclaringType.IsGenericType && memberInfo.DeclaringType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + { + if (memberInfo.MemberType == MemberTypes.Constructor) + { + ConstructorInfo constructorInfo = memberInfo as ConstructorInfo; + var parameterInfos = constructorInfo.GetParameters(); + if (parameterInfos.Length > 0) + { + if (typeof(System.Collections.IEnumerable).IsAssignableFrom(parameterInfos[0].ParameterType)) + { + return true; + } + } + } + else if (memberInfo.MemberType == MemberTypes.Method) + { + var methodInfo = memberInfo as MethodInfo; + if (methodInfo.Name == "TryAdd" || methodInfo.Name == "Remove" && methodInfo.GetParameters().Length == 2) + { + return true; + } + } + } + return false; + } +#endif +} diff --git a/Assets/Puerts/Src/GenericDelegate.cs b/Assets/Puerts/Src/GenericDelegate.cs new file mode 100644 index 00000000..3c6b6e12 --- /dev/null +++ b/Assets/Puerts/Src/GenericDelegate.cs @@ -0,0 +1,429 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Puerts +{ + internal class DelegateCreatorTree + { + private class Node + { + public Func Creator; + public Dictionary Branchs = new Dictionary(); + } + + private readonly Node root = new Node(); + + private Node FindNode(Type[] types, bool createIfNotExisted) + { + Node cur = root; + Node next; + + for (int i = 0; i < types.Length; i++) + { + if (!cur.Branchs.TryGetValue(types[i], out next)) + { + if (createIfNotExisted) + { + next = new Node(); + cur.Branchs.Add(types[i], next); + } + else + { + return null; + } + } + cur = next; + } + return cur; + } + + public void Add(Func creator, params Type[] types) + { + Node node = FindNode(types, true); + node.Creator = creator; + } + + public Func Find(Type[] types) + { + Node node = FindNode(types, false); + return node == null ? null : node.Creator; + } + } + + internal class GenericDelegateFactory + { + readonly JsEnv jsEnv; + + //无返回值泛型方法 + MethodInfo[] genericAction = null; + //有返回值泛型方法 + MethodInfo[] genericFunc = null; + + //泛型delegate适配器构造器的缓存 + Dictionary> genericDelegateCreatorCache + = new Dictionary>(); + + internal GenericDelegateFactory(JsEnv jsEnv) + { + this.jsEnv = jsEnv; + } + + //Prevent unity il2cpp code stripping + static void PreventStripping(object obj) + { + if (obj != null) + { + var gd = new GenericDelegate(IntPtr.Zero, null); + gd.Action(); + gd.Action(obj); + gd.Action(obj, obj); + gd.Action(obj, obj, obj); + gd.Action(obj, obj, obj, obj); + + gd.Func(); + gd.Func(obj); + gd.Func(obj, obj); + gd.Func(obj, obj, obj); + gd.Func(obj, obj, obj, obj); + } + } + + static GenericDelegateFactory() + { + PrimitiveTypeTranslate.Init(); + } + + internal Delegate Create(Type delegateType, IntPtr nativeJsFuncPtr) + { + Func genericDelegateCreator; + if (!genericDelegateCreatorCache.TryGetValue(delegateType, out genericDelegateCreator)) + { + //如果泛型方法数组未初始化 + if (genericAction == null) + { + PreventStripping(null); + var methods = typeof(GenericDelegate).GetMethods(BindingFlags.Instance | BindingFlags.Public + | BindingFlags.DeclaredOnly); + genericAction = methods.Where(m => m.Name == "Action").OrderBy(m => m.GetParameters().Length) + .ToArray(); + genericFunc = methods.Where(m => m.Name == "Func").OrderBy(m => m.GetParameters().Length).ToArray(); + } + + MethodInfo delegateMethod = delegateType.GetMethod("Invoke"); + var parameters = delegateMethod.GetParameters(); + var typeArgs = parameters.Select(pinfo => pinfo.ParameterType).ToArray(); + + if (delegateMethod.ReturnType == typeof(void)) + { + if (parameters.Length == 0) + { + //对无参无返回值特殊处理 + var methodInfo = genericAction[0]; + genericDelegateCreator = (dt, ptr) => Delegate.CreateDelegate(dt, new GenericDelegate(ptr, jsEnv), methodInfo); + } + else + { + genericDelegateCreator = ActionCreatorTree.Find(typeArgs); + } + } + else + { + //如果是有返回值,需要加上返回值作为泛型实参 + typeArgs = typeArgs.Concat(new Type[] { delegateMethod.ReturnType }).ToArray(); + genericDelegateCreator = FuncCreatorTree.Find(typeArgs); + } + + if (genericDelegateCreator == null) + { + if ((delegateMethod.ReturnType.IsValueType && delegateMethod.ReturnType != typeof(void)) + || parameters.Length > 4 + || typeArgs.Any(paramType => paramType.IsValueType || paramType.IsByRef) + ) + { + //如果不在支持的范围,则生成一个永远返回空的构造器 + genericDelegateCreator = (dt, x) => null; + } + else + { + //根据参数个数,返回值找到泛型实现 + MethodInfo genericMethodInfo = null; + if (delegateMethod.ReturnType == typeof(void)) + { + genericMethodInfo = genericAction[parameters.Length]; + } + else + { + genericMethodInfo = genericFunc[parameters.Length]; + } + //实例化泛型方法 + var methodInfo = genericMethodInfo.MakeGenericMethod(typeArgs); + //构造器 + genericDelegateCreator = (dt, ptr) => Delegate.CreateDelegate(dt, new GenericDelegate(ptr, jsEnv), methodInfo); + } + } + //缓存构造器,下次调用直接返回 + genericDelegateCreatorCache[delegateType] = genericDelegateCreator; + } + //创建delegate + return genericDelegateCreator(delegateType, nativeJsFuncPtr); + } + + DelegateCreatorTree ActionCreatorTree = new DelegateCreatorTree(); + + DelegateCreatorTree FuncCreatorTree = new DelegateCreatorTree(); + + public void RegisterAction() + { + ActionCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Action(genericDelegate.Action).Method); + }, typeof(T1)); + } + + public void RegisterAction() + { + ActionCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Action(genericDelegate.Action).Method); + }, typeof(T1), typeof(T2)); + } + + public void RegisterAction() + { + ActionCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Action(genericDelegate.Action).Method); + }, typeof(T1), typeof(T2), typeof(T3)); + } + + public void RegisterAction() + { + ActionCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Action(genericDelegate.Action).Method); + }, typeof(T1), typeof(T2), typeof(T3), typeof(T4)); + } + + public void RegisterFunc() + { + FuncCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Func(genericDelegate.Func).Method); + }, typeof(TResult)); + } + + public void RegisterFunc() + { + FuncCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Func(genericDelegate.Func).Method); + }, typeof(T1), typeof(TResult)); + } + + public void RegisterFunc() + { + FuncCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Func(genericDelegate.Func).Method); + }, typeof(T1), typeof(T2), typeof(TResult)); + } + + public void RegisterFunc() + { + FuncCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Func(genericDelegate.Func).Method); + }, typeof(T1), typeof(T2), typeof(T3), typeof(TResult)); + } + + public void RegisterFunc() + { + FuncCreatorTree.Add((type, ptr) => + { + GenericDelegate genericDelegate = new GenericDelegate(ptr, jsEnv); + return Delegate.CreateDelegate(type, genericDelegate, new Func(genericDelegate.Func).Method); + }, typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(TResult)); + } + } + + //泛型适配器 + internal class GenericDelegate + { + private readonly JsEnv jsEnv; + private IntPtr nativeJsFuncPtr; + private IntPtr isolate; + + internal GenericDelegate(IntPtr nativeJsFuncPtr, JsEnv jsEnv) + { + this.nativeJsFuncPtr = nativeJsFuncPtr; + isolate = jsEnv != null ? jsEnv.isolate : IntPtr.Zero; + this.jsEnv = jsEnv; + } + + //TODO: 虚拟机析构时应调用该函数 + internal void Close() + { + nativeJsFuncPtr = IntPtr.Zero; + } + + ~GenericDelegate() + { + if (jsEnv.isolate != IntPtr.Zero) + { + PuertsDLL.ReleaseJSFunction(jsEnv.isolate, nativeJsFuncPtr); + } + } + + public void Action() + { + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, false); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + } + + public void Action(T1 p1) + { + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p1); + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, false); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + } + + public void Action(T1 p1, T2 p2) + { + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p1); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p2); + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, false); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + } + + public void Action(T1 p1, T2 p2, T3 p3) + { + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p1); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p2); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p3); + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, false); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + } + + public void Action(T1 p1, T2 p2, T3 p3, T4 p4) + { + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p1); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p2); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p3); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p4); + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, false); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + } + + public TResult Func() + { + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, true); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + TResult result = StaticTranslate.Get(jsEnv.Idx, isolate, NativeValueApi.GetValueFromResult, resultInfo, false); + PuertsDLL.ResetResult(resultInfo); + return result; + } + + public TResult Func(T1 p1) + { + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p1); + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, true); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + TResult result = StaticTranslate.Get(jsEnv.Idx, isolate, NativeValueApi.GetValueFromResult, resultInfo, false); + PuertsDLL.ResetResult(resultInfo); + return result; + } + + public TResult Func(T1 p1, T2 p2) + { + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p1); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p2); + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, true); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + TResult result = StaticTranslate.Get(jsEnv.Idx, isolate, NativeValueApi.GetValueFromResult, resultInfo, false); + PuertsDLL.ResetResult(resultInfo); + return result; + } + + public TResult Func(T1 p1, T2 p2, T3 p3) + { + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p1); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p2); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p3); + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, true); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + TResult result = StaticTranslate.Get(jsEnv.Idx, isolate, NativeValueApi.GetValueFromResult, resultInfo, false); + PuertsDLL.ResetResult(resultInfo); + return result; + } + + public TResult Func(T1 p1, T2 p2, T3 p3, T4 p4) + { + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p1); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p2); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p3); + StaticTranslate.Set(jsEnv.Idx, isolate, NativeValueApi.SetValueToArgument, nativeJsFuncPtr, p4); + IntPtr resultInfo = PuertsDLL.InvokeJSFunction(nativeJsFuncPtr, true); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetFunctionLastExceptionInfo(nativeJsFuncPtr); + throw new Exception(exceptionInfo); + } + TResult result = StaticTranslate.Get(jsEnv.Idx, isolate, NativeValueApi.GetValueFromResult, resultInfo, false); + PuertsDLL.ResetResult(resultInfo); + return result; + } + } +} diff --git a/Assets/Puerts/Src/JsEnv.cs b/Assets/Puerts/Src/JsEnv.cs new file mode 100644 index 00000000..0084446a --- /dev/null +++ b/Assets/Puerts/Src/JsEnv.cs @@ -0,0 +1,335 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; +using System.Collections.Generic; + +namespace Puerts +{ + public delegate void FunctionCallback(IntPtr isolate, IntPtr info, IntPtr self, int argumentsLen); + public delegate object ConstructorCallback(IntPtr isolate, IntPtr info, int argumentsLen); + + public class JsEnv : IDisposable + { + internal readonly int Idx; + + internal readonly GeneralGetterManager GeneralGetterManager; + + internal readonly GeneralSetterManager GeneralSetterManager; + + internal readonly TypeRegister TypeRegister = null; + + private readonly ILoader loader; + + internal static List jsEnvs = new List(); + + internal IntPtr isolate; + + internal ObjectPool objectPool; + + public JsEnv() : this(new DefaultLoader(), -1) + { + } + + public JsEnv(ILoader loader, int port = -1) + { + //PuertsDLL.SetLogCallback(LogCallback, LogWarningCallback, LogErrorCallback); + this.loader = loader; + isolate = PuertsDLL.CreateJSEngine(); + lock (jsEnvs) + { + Idx = -1; + for (int i = 0; i < jsEnvs.Count; i++) + { + if (jsEnvs[i] == null) + { + Idx = i; + jsEnvs[Idx] = this; + break; + } + } + if (Idx == -1) + { + Idx = jsEnvs.Count; + jsEnvs.Add(this); + } + } + + objectPool = new ObjectPool(); + TypeRegister = new TypeRegister(this); + + GeneralGetterManager = new GeneralGetterManager(this); + GeneralSetterManager = new GeneralSetterManager(this); + + PuertsDLL.SetGeneralDestructor(isolate, StatiCallbacks.GeneralDestructor); + + TypeRegister.InitArrayTypeId(isolate); + + PuertsDLL.SetGlobalFunction(isolate, "__tgjsLoadType", StatiCallbacks.JsEnvCallbackWrap, AddCallback(LoadType)); + PuertsDLL.SetGlobalFunction(isolate, "__tgjsGetLoader", StatiCallbacks.JsEnvCallbackWrap, AddCallback(GetLoader)); + + var autoRegister = Type.GetType("PuertsStaticWrap.AutoStaticCodeRegister", false); + if (autoRegister != null) + { + var methodInfoOfRegister = autoRegister.GetMethod("Register"); + methodInfoOfRegister.Invoke(null, new object[] { this }); + } + + if (port != -1) + { + PuertsDLL.CreateInspector(isolate, port); + } + + ExecuteFile("puerts/init.js"); + ExecuteFile("puerts/log.js"); + ExecuteFile("puerts/cjsload.js"); + ExecuteFile("puerts/modular.js"); + ExecuteFile("puerts/csharp.js"); + } + + void ExecuteFile(string filename) + { + if (loader.FileExists(filename)) + { + string debugPath; + var context = loader.ReadFile(filename, out debugPath); + Eval(context, debugPath); + } + } + + public void Eval(string chunk, string chunkName = "chunk") + { + IntPtr resultInfo = PuertsDLL.Eval(isolate, chunk, chunkName); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetLastExceptionInfo(isolate); + throw new Exception(exceptionInfo); + } + PuertsDLL.ResetResult(resultInfo); + } + + public TResult Eval(string chunk, string chunkName = "chunk") + { + IntPtr resultInfo = PuertsDLL.Eval(isolate, chunk, chunkName); + if (resultInfo == IntPtr.Zero) + { + string exceptionInfo = PuertsDLL.GetLastExceptionInfo(isolate); + throw new Exception(exceptionInfo); + } + TResult result = StaticTranslate.Get(Idx, isolate, NativeValueApi.GetValueFromResult, resultInfo, false); + PuertsDLL.ResetResult(resultInfo); + return result; + } + + public void AddLazyStaticWrapLoader(Type type, Func lazyStaticWrapLoader) + { + TypeRegister.AddLazyStaticWrapLoader(type, lazyStaticWrapLoader); + } + + private readonly List callbacks = new List(); + + internal void InvokeCallback(IntPtr isolate, int callbackIdx, IntPtr info, IntPtr self, int paramLen) + { + callbacks[callbackIdx](isolate, info, self, paramLen); + } + + internal long AddCallback(FunctionCallback callback) + { + int callbackIdx = callbacks.Count; + callbacks.Add(callback); + return Utils.TwoIntToLong(Idx, callbackIdx); + } + + private readonly List constructorCallbacks = new List(); + + internal IntPtr InvokeConstructor(IntPtr isolate, int callbackIdx, IntPtr info, int paramLen) + { + object obj = constructorCallbacks[callbackIdx](isolate, info, paramLen); + if (obj == null) return IntPtr.Zero; + int objectId = objectPool.FindOrAddObject(obj); + return new IntPtr(objectId); + } + + internal long AddConstructor(ConstructorCallback callback) + { + int callbackIdx = constructorCallbacks.Count; + constructorCallbacks.Add(callback); + return Utils.TwoIntToLong(Idx, callbackIdx); + } + + internal void JsReleaseObject(int objectID) + { + objectPool.Remove(objectID); + //if (obj != null) UnityEngine.Debug.Log("release " + obj + "(" + obj.GetHashCode() + ")"); + } + + void GetLoader(IntPtr isolate, IntPtr info, IntPtr self, int paramLen) + { + GeneralSetterManager.AnyTranslator(isolate, NativeValueApi.SetValueToResult, info, loader); + } + + public void RegisterGeneralGetSet(Type type, GeneralGetter getter, GeneralSetter setter) + { + GeneralGetterManager.RegisterGetter(type, getter); + GeneralSetterManager.RegisterSetter(type, setter); + } + + public int GetTypeId(Type type) + { + return TypeRegister.GetTypeId(isolate, type); + } + + public int Index + { + get + { + return Idx; + } + } + + void LoadType(IntPtr isolate, IntPtr info, IntPtr self, int paramLen) + { + try + { + Type type = null; + var value = PuertsDLL.GetArgumentValue(info, 0); + if (PuertsDLL.GetJsValueType(isolate, value, false) == JsValueType.String) + { + string classFullName = PuertsDLL.GetStringFromValue(isolate, value, false); + var maybeType = TypeRegister.GetType(classFullName); + if (paramLen == 1) + { + type = maybeType; + } + else if (maybeType != null + && maybeType.IsGenericTypeDefinition + && maybeType.GetGenericArguments().Length == (paramLen - 1)) //泛型 + { + var genericArguments = new Type[paramLen - 1]; + for (int i = 1; i < paramLen; i++) + { + value = PuertsDLL.GetArgumentValue(info, i); + if (PuertsDLL.GetJsValueType(isolate, value, false) != JsValueType.Function) return; + var argTypeId = PuertsDLL.GetTypeIdFromValue(isolate, value, false); + if (argTypeId == -1) return; + genericArguments[i - 1] = TypeRegister.GetType(argTypeId); + } + type = maybeType.MakeGenericType(genericArguments); + //UnityEngine.Debug.Log(type); + } + } + if (type == null) + { + return; + } + int typeId = TypeRegister.GetTypeId(isolate, type); + PuertsDLL.ReturnClass(isolate, info, typeId); + } + catch(Exception e) + { + PuertsDLL.ThrowException(isolate, "loadClass throw c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + + public void UsingAction() + { + GeneralGetterManager.genericDelegateFactory.RegisterAction(); + } + + public void UsingAction() + { + GeneralGetterManager.genericDelegateFactory.RegisterAction(); + } + + public void UsingAction() + { + GeneralGetterManager.genericDelegateFactory.RegisterAction(); + } + + public void UsingAction() + { + GeneralGetterManager.genericDelegateFactory.RegisterAction(); + } + + public void UsingFunc() + { + GeneralGetterManager.genericDelegateFactory.RegisterFunc(); + } + + public void UsingFunc() + { + GeneralGetterManager.genericDelegateFactory.RegisterFunc(); + } + + public void UsingFunc() + { + GeneralGetterManager.genericDelegateFactory.RegisterFunc(); + } + + public void UsingFunc() + { + GeneralGetterManager.genericDelegateFactory.RegisterFunc(); + } + + public void UsingFunc() + { + GeneralGetterManager.genericDelegateFactory.RegisterFunc(); + } + + public void LowMemoryNotification() + { + PuertsDLL.LowMemoryNotification(isolate); + } + + public void Tick() + { + PuertsDLL.InspectorTick(isolate); + } + + /*[MonoPInvokeCallback(typeof(LogCallback))] + private static void LogCallback(string msg) + { + UnityEngine.Debug.Log(msg); + } + + [MonoPInvokeCallback(typeof(LogCallback))] + private static void LogWarningCallback(string msg) + { + UnityEngine.Debug.LogWarning(msg); + } + + [MonoPInvokeCallback(typeof(LogCallback))] + private static void LogErrorCallback(string msg) + { + UnityEngine.Debug.LogError(msg); + }*/ + + ~JsEnv() + { + Dispose(true); + } + + public void Dispose() + { + Dispose(true); + } + + private bool disposed = false; + + protected virtual void Dispose(bool dispose) + { + lock (jsEnvs) + { + if (disposed) return; + jsEnvs[Idx] = null; + PuertsDLL.DestroyJSEngine(isolate); + isolate = IntPtr.Zero; + disposed = true; + } + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/Loader.cs b/Assets/Puerts/Src/Loader.cs new file mode 100644 index 00000000..5bf2ad6a --- /dev/null +++ b/Assets/Puerts/Src/Loader.cs @@ -0,0 +1,44 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +namespace Puerts +{ + public interface ILoader + { + bool FileExists(string filepath); + string ReadFile(string filepath, out string debugpath); + } + + public class DefaultLoader : ILoader + { + private string root = ""; + + public DefaultLoader() + { + } + + public DefaultLoader(string root) + { + this.root = root; + } + + public bool FileExists(string filepath) + { + return UnityEngine.Resources.Load(filepath) != null; + } + + public string ReadFile(string filepath, out string debugpath) + { + UnityEngine.TextAsset file = (UnityEngine.TextAsset)UnityEngine.Resources.Load(filepath); + debugpath = System.IO.Path.Combine(root, filepath); +#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + debugpath = debugpath.Replace("/", "\\"); +#endif + return file == null ? null : file.text; + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/MethodReflection.cs b/Assets/Puerts/Src/MethodReflection.cs new file mode 100644 index 00000000..67b397f9 --- /dev/null +++ b/Assets/Puerts/Src/MethodReflection.cs @@ -0,0 +1,365 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Puerts +{ + public class CallInfo + { + public IntPtr Isolate; + public IntPtr Info; + public IntPtr Self; + public int Length = 0; + + public JsValueType[] JsTypes = null; + + public object[] Values = null; + + public IntPtr[] NativePtrs = null; + + public void Init(IntPtr isolate, IntPtr info, IntPtr self, int len) + { + Isolate = isolate; + Info = info; + Self = self; + Length = len; + if (JsTypes == null || JsTypes.Length < Length) + { + JsTypes = new JsValueType[Length]; + Values = new object[Length]; + NativePtrs = new IntPtr[Length]; + } + + for(int i = 0; i < Length; i++) + { + var nativeValuePtr = PuertsDLL.GetArgumentValue(info, i); + NativePtrs[i] = nativeValuePtr; + var type = PuertsDLL.GetJsValueType(isolate, nativeValuePtr, false); + JsTypes[i] = type; + Values[i] = null; + } + } + } + + public class Parameters + { + private readonly int length; + + private readonly GeneralGetterManager generalGetterManager; + + private bool hasParamArray = false; + + private JsValueType[] typeMasks = null; + + private Type[] types = null; + + private object[] args = null; + + private bool[] byRef = null; + + private bool[] isOut = null; + + private GeneralGetter[] argsTranslateFuncs = null; + + private GeneralSetter[] byRefValueSetFuncs = null; + + public Parameters(ParameterInfo[] parameterInfos, GeneralGetterManager generalGetterManager, GeneralSetterManager generalSetterManager) + { + this.generalGetterManager = generalGetterManager; + length = parameterInfos.Length; + typeMasks = new JsValueType[parameterInfos.Length]; + types = new Type[parameterInfos.Length]; + args = new object[parameterInfos.Length]; + argsTranslateFuncs = new GeneralGetter[parameterInfos.Length]; + byRefValueSetFuncs = new GeneralSetter[parameterInfos.Length]; + byRef = new bool[parameterInfos.Length]; + isOut = new bool[parameterInfos.Length]; + + for (int i = 0; i < parameterInfos.Length; i++) + { + var parameterInfo = parameterInfos[i]; + var parameterType = parameterInfo.ParameterType; + if (parameterInfo.IsDefined(typeof(ParamArrayAttribute), false)) + { + parameterType = parameterType.GetElementType(); + hasParamArray = true; + } + types[i] = parameterType; + typeMasks[i] = GeneralGetterManager.GetJsTypeMask(parameterType); + argsTranslateFuncs[i] = generalGetterManager.GetTranslateFunc(parameterType); + byRef[i] = parameterType.IsByRef; + if (parameterType.IsByRef) + { + byRefValueSetFuncs[i] = generalSetterManager.GetTranslateFunc(parameterType.GetElementType()); + } + isOut[i] = parameterType.IsByRef && parameterInfo.IsOut && !parameterInfo.IsIn; + } + } + + public bool IsMatch(CallInfo callInfo)//TODO: 先不支持默认值 + { + if (hasParamArray) + { + if (callInfo.Length < length - 1) + { + return false; + } + } + else if (callInfo.Length != length) + { + return false; + } + + for (int i = 0; i < callInfo.Length; i++) + { + if (i < length) + { + var argJsType = callInfo.JsTypes[i]; + if (argJsType == JsValueType.JsObject) + { + if (!byRef[i]) return false; + if (!isOut[i]) + { + argJsType = PuertsDLL.GetJsValueType(callInfo.Isolate, callInfo.NativePtrs[i], true); + } + else + { + continue; + } + } + if ((typeMasks[i] & argJsType) != argJsType) + { + //UnityEngine.Debug.Log("arg " + i + " not match, expected " + typeMasks[i] + ", but got " + argJsType); + return false; + } + if (argJsType == JsValueType.NativeObject) + { + if (callInfo.Values[i] == null) + { + callInfo.Values[i] = generalGetterManager.AnyTranslator(callInfo.Isolate, NativeValueApi.GetValueFromArgument, callInfo.NativePtrs[i], false); + } + if (!types[i].IsAssignableFrom(callInfo.Values[i].GetType())) + { + return false; + } + } + } + } + + return true; + } + + public object[] GetArguments(CallInfo callInfo) + { + for (int i = 0; i < length; i++) + { + if(hasParamArray && i == length - 1) + { + Array paramArray = Array.CreateInstance(types[length - 1], callInfo.Length + 1 - length); + var translateFunc = argsTranslateFuncs[i]; + for (int j = i; j < callInfo.Length; j++) + { + paramArray.SetValue(translateFunc(callInfo.Isolate, NativeValueApi.GetValueFromArgument, callInfo.NativePtrs[j], false), j - i); + } + args[i] = paramArray; + } + else + { + if (!isOut[i]) + { + if (callInfo.Values[i] != null) + { + args[i] = callInfo.Values[i]; + } + else + { + args[i] = argsTranslateFuncs[i](callInfo.Isolate, NativeValueApi.GetValueFromArgument, callInfo.NativePtrs[i], byRef[i]); + } + } + } + } + return args; + } + + public void FillByRefParameters(CallInfo callInfo) + { + for (int i = 0; i < length; i++) + { + if (byRef[i]) + { + byRefValueSetFuncs[i](callInfo.Isolate, NativeValueApi.SetValueToByRefArgument, callInfo.NativePtrs[i], args[i]); + } + } + } + + public void ClearArguments() + { + for (int i = 0; i < args.Length; i++) + { + args[i] = null; + } + } + } + + public class OverloadReflectionWrap + { + Parameters parameters = null; + + MethodInfo methodInfo = null; + + ConstructorInfo constructorInfo = null; + + GeneralGetterManager generalGetterManager = null; + + GeneralSetter resultSetter = null; + + public OverloadReflectionWrap(MethodBase methodBase, GeneralGetterManager generalGetterManager, GeneralSetterManager generalSetterManager) + { + parameters = new Parameters(methodBase.GetParameters(), generalGetterManager, generalSetterManager); + + this.generalGetterManager = generalGetterManager; + + if (methodBase.IsConstructor) + { + constructorInfo = methodBase as ConstructorInfo; + } + else + { + methodInfo = methodBase as MethodInfo; + resultSetter = generalSetterManager.GetTranslateFunc(methodInfo.ReturnType); + } + } + + public bool IsMatch(CallInfo callInfo) + { + return parameters.IsMatch(callInfo); + } + + public void Invoke(CallInfo callInfo) + { + try + { + object target = methodInfo.IsStatic ? null : generalGetterManager.GetSelf(callInfo.Self); + object ret = methodInfo.Invoke(target, parameters.GetArguments(callInfo)); + parameters.FillByRefParameters(callInfo); + resultSetter(callInfo.Isolate, NativeValueApi.SetValueToResult, callInfo.Info, ret); + } + finally + { + parameters.ClearArguments(); + } + } + + public object Construct(CallInfo callInfo) + { + return constructorInfo.Invoke(parameters.GetArguments(callInfo)); + } + } + + public class DelegateConstructWrap + { + private Type delegateType; + + private GeneralGetter translateFunc; + + public DelegateConstructWrap(Type delegateType, GeneralGetterManager generalGetterManager) + { + this.delegateType = delegateType; + translateFunc = generalGetterManager.GetTranslateFunc(delegateType); + } + + public object Construct(IntPtr isolate, IntPtr info, int argumentsLen) + { + if (argumentsLen == 1) + { + var arg0 = PuertsDLL.GetArgumentValue(info, 0); + var arg0type = NativeValueApi.GetValueFromArgument.GetJsValueType(isolate, arg0, false); + if (arg0type == JsValueType.Function || arg0type == JsValueType.NativeObject ) + { + object obj = translateFunc(isolate, NativeValueApi.GetValueFromArgument, arg0, false); + if (obj != null) + { + return obj; + } + } + } + PuertsDLL.ThrowException(isolate, "invalid arguments to constructor of " + delegateType.GetFriendlyName()); + return null; + } + } + + public class MethodReflectionWrap + { + private CallInfo callInfo = new CallInfo(); + + private string name; + + private List overloads; + + public MethodReflectionWrap(string name, List overloads) + { + this.name = name; + this.overloads = overloads; + } + + public void Invoke(IntPtr isolate, IntPtr info, IntPtr self, int argumentsLen) + { + try + { + callInfo.Init(isolate, info, self, argumentsLen); + for (int i = 0; i < overloads.Count; ++i) + { + var overload = overloads[i]; + if (overload.IsMatch(callInfo)) + { + overload.Invoke(callInfo); + return; + } + } + PuertsDLL.ThrowException(isolate, "invalid arguments to " + name); + } + catch (TargetInvocationException e) + { + PuertsDLL.ThrowException(isolate, "c# exception:" + e.InnerException.Message + ",stack:" + e.InnerException.StackTrace); + } + catch (Exception e) + { + PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + + public object Construct(IntPtr isolate, IntPtr info, int argumentsLen) + { + callInfo.Init(isolate, info, IntPtr.Zero, argumentsLen); + + try + { + for (int i = 0; i < overloads.Count; ++i) + { + var overload = overloads[i]; + if (overload.IsMatch(callInfo)) + { + return overload.Construct(callInfo); + } + } + PuertsDLL.ThrowException(isolate, "invalid arguments to " + name); + } + catch (TargetInvocationException e) + { + PuertsDLL.ThrowException(isolate, "c# exception:" + e.InnerException.Message + ",stack:" + e.InnerException.StackTrace); + } + catch (Exception e) + { + PuertsDLL.ThrowException(isolate, "c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + return null; + } + } +} diff --git a/Assets/Puerts/Src/NativeValueApiGeneric.cs b/Assets/Puerts/Src/NativeValueApiGeneric.cs new file mode 100644 index 00000000..8b294048 --- /dev/null +++ b/Assets/Puerts/Src/NativeValueApiGeneric.cs @@ -0,0 +1,314 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; + +namespace Puerts +{ + public static class NativeValueApi + { + public static IGetValueFromJs GetValueFromArgument = new GetValueFromArgumentImpl(); + + public static IGetValueFromJs GetValueFromResult = new GetValueFromResultImpl(); + + public static ISetValueToJs SetValueToIndexResult = new SetValueToIndexResultImpl(); + + public static ISetValueToJs SetValueToResult = new SetValueToResultImpl(); + + public static ISetValueToJs SetValueToByRefArgument = new SetValueToByRefArgumentImpl(); + + public static ISetValueToJs SetValueToArgument = new SetValueToArgumentImpl(); + } + + public interface ISetValueToJs + { + void SetObject(IntPtr isolate, IntPtr holder, int classID, IntPtr self); + + void SetNumber(IntPtr isolate, IntPtr holder, double number); + + void SetString(IntPtr isolate, IntPtr holder, string str); + + void SetBigInt(IntPtr isolate, IntPtr holder, long number); + + void SetBoolean(IntPtr isolate, IntPtr holder, bool b); + + void SetDate(IntPtr isolate, IntPtr holder, double date); + + void SetNull(IntPtr isolate, IntPtr holder); + } + + public interface IGetValueFromJs + { + JsValueType GetJsValueType(IntPtr isolate, IntPtr holder, bool isByRef); + + double GetNumber(IntPtr isolate, IntPtr holder, bool isByRef); + + double GetDate(IntPtr isolate, IntPtr holder, bool isByRef); + + string GetString(IntPtr isolate, IntPtr holder, bool isByRef); + + bool GetBoolean(IntPtr isolate, IntPtr holder, bool isByRef); + + long GetBigInt(IntPtr isolate, IntPtr holder, bool isByRef); + + IntPtr GetObject(IntPtr isolate, IntPtr holder, bool isByRef); + + int GetTypeId(IntPtr isolate, IntPtr holder, bool isByRef); + + IntPtr GetFunction(IntPtr isolate, IntPtr holder, bool isByRef); + } + + public class GetValueFromResultImpl : IGetValueFromJs + { + public long GetBigInt(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetBigIntFromResult(holder); + } + + public bool GetBoolean(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetBooleanFromResult(holder); + } + + public double GetDate(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetDateFromResult(holder); + } + + public IntPtr GetFunction(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetFunctionFromResult(holder); + } + + public JsValueType GetJsValueType(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetResultType(holder); + } + + public double GetNumber(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetNumberFromResult(holder); + } + + public IntPtr GetObject(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetObjectFromResult(holder); + } + + public int GetTypeId(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetTypeIdFromResult(holder); + } + + public string GetString(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetStringFromResult(holder); + } + } + + public class GetValueFromArgumentImpl : IGetValueFromJs + { + public long GetBigInt(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetBigIntFromValue(isolate, holder, isByRef); + } + + public bool GetBoolean(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetBooleanFromValue(isolate, holder, isByRef); + } + + public double GetDate(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetDateFromValue(isolate, holder, isByRef); + } + + public IntPtr GetFunction(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetFunctionFromValue(isolate, holder, isByRef); + } + + public JsValueType GetJsValueType(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetJsValueType(isolate, holder, isByRef); + } + + public double GetNumber(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetNumberFromValue(isolate, holder, isByRef); + } + + public IntPtr GetObject(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetObjectFromValue(isolate, holder, isByRef); + } + + public int GetTypeId(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetTypeIdFromValue(isolate, holder, isByRef); + } + + public string GetString(IntPtr isolate, IntPtr holder, bool isByRef) + { + return PuertsDLL.GetStringFromValue(isolate, holder, isByRef); + } + } + + public class SetValueToIndexResultImpl : ISetValueToJs + { + public void SetBigInt(IntPtr isolate, IntPtr holder, long number) + { + PuertsDLL.PropertyReturnBigInt(isolate, holder, number); + } + + public void SetBoolean(IntPtr isolate, IntPtr holder, bool b) + { + PuertsDLL.PropertyReturnBoolean(isolate, holder, b); + } + + public void SetDate(IntPtr isolate, IntPtr holder, double date) + { + PuertsDLL.PropertyReturnDate(isolate, holder, date); + } + + public void SetNull(IntPtr isolate, IntPtr holder) + { + PuertsDLL.PropertyReturnNull(isolate, holder); + } + + public void SetNumber(IntPtr isolate, IntPtr holder, double number) + { + PuertsDLL.PropertyReturnNumber(isolate, holder, number); + } + + public void SetObject(IntPtr isolate, IntPtr holder, int classID, IntPtr self) + { + PuertsDLL.PropertyReturnObject(isolate, holder, classID, self); + } + + public void SetString(IntPtr isolate, IntPtr holder, string str) + { + PuertsDLL.PropertyReturnString(isolate, holder, str); + } + } + + public class SetValueToResultImpl : ISetValueToJs + { + public void SetBigInt(IntPtr isolate, IntPtr holder, long number) + { + PuertsDLL.ReturnBigInt(isolate, holder, number); + } + + public void SetBoolean(IntPtr isolate, IntPtr holder, bool b) + { + PuertsDLL.ReturnBoolean(isolate, holder, b); + } + + public void SetDate(IntPtr isolate, IntPtr holder, double date) + { + PuertsDLL.ReturnDate(isolate, holder, date); + } + + public void SetNull(IntPtr isolate, IntPtr holder) + { + PuertsDLL.ReturnNull(isolate, holder); + } + + public void SetNumber(IntPtr isolate, IntPtr holder, double number) + { + PuertsDLL.ReturnNumber(isolate, holder, number); + } + + public void SetObject(IntPtr isolate, IntPtr holder, int classID, IntPtr self) + { + PuertsDLL.ReturnObject(isolate, holder, classID, self); + } + + public void SetString(IntPtr isolate, IntPtr holder, string str) + { + PuertsDLL.ReturnString(isolate, holder, str); + } + } + + public class SetValueToByRefArgumentImpl : ISetValueToJs + { + public void SetBigInt(IntPtr isolate, IntPtr holder, long number) + { + PuertsDLL.SetBigIntToOutValue(isolate, holder, number); + } + + public void SetBoolean(IntPtr isolate, IntPtr holder, bool b) + { + PuertsDLL.SetBooleanToOutValue(isolate, holder, b); + } + + public void SetDate(IntPtr isolate, IntPtr holder, double date) + { + PuertsDLL.SetDateToOutValue(isolate, holder, date); + } + + public void SetNull(IntPtr isolate, IntPtr holder) + { + PuertsDLL.SetNullToOutValue(isolate, holder); + } + + public void SetNumber(IntPtr isolate, IntPtr holder, double number) + { + PuertsDLL.SetNumberToOutValue(isolate, holder, number); + } + + public void SetObject(IntPtr isolate, IntPtr holder, int classID, IntPtr self) + { + PuertsDLL.SetObjectToOutValue(isolate, holder, classID, self); + } + + public void SetString(IntPtr isolate, IntPtr holder, string str) + { + PuertsDLL.SetStringToOutValue(isolate, holder, str); + } + } + + + public class SetValueToArgumentImpl : ISetValueToJs + { + public void SetBigInt(IntPtr isolate, IntPtr holder, long number) + { + PuertsDLL.PushBigIntForJSFunction(holder, number); + } + + public void SetBoolean(IntPtr isolate, IntPtr holder, bool b) + { + PuertsDLL.PushBooleanForJSFunction(holder, b); + } + + public void SetDate(IntPtr isolate, IntPtr holder, double date) + { + PuertsDLL.PushDateForJSFunction(holder, date); + } + + public void SetNull(IntPtr isolate, IntPtr holder) + { + PuertsDLL.PushNullForJSFunction(holder); + } + + public void SetNumber(IntPtr isolate, IntPtr holder, double number) + { + PuertsDLL.PushNumberForJSFunction(holder, number); + } + + public void SetObject(IntPtr isolate, IntPtr holder, int classID, IntPtr self) + { + PuertsDLL.PushObjectForJSFunction(holder, classID, self); + } + + public void SetString(IntPtr isolate, IntPtr holder, string str) + { + PuertsDLL.PushStringForJSFunction(holder, str); + } + } + +} \ No newline at end of file diff --git a/Assets/Puerts/Src/ObjectPool.cs b/Assets/Puerts/Src/ObjectPool.cs new file mode 100644 index 00000000..868c7351 --- /dev/null +++ b/Assets/Puerts/Src/ObjectPool.cs @@ -0,0 +1,215 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; +using System.Collections.Generic; + +namespace Puerts +{ + class ReferenceEqualsComparer : IEqualityComparer + { + public new bool Equals(object o1, object o2) + { + return object.ReferenceEquals(o1, o2); + } + public int GetHashCode(object obj) + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); + } + } + + public class ObjectPool + { + //TODO: V8 SetAlignedPointerInInternalField 要求第一位必须是0,先左移一位解决问题,这种潜规则有可能会有变动,后续应该换通过更换接口来解决 + const int SHIFT_BIT = 1; + const int LIST_END = -1; + const int ALLOCED = -2; + struct Slot + { + public int next; + public object obj; + + public Slot(int next, object obj) + { + this.next = next; + this.obj = obj; + } + } + + private Slot[] list = new Slot[512]; + private int freelist = LIST_END; + private int count = 0; + private Dictionary reverseMap = new Dictionary(new ReferenceEqualsComparer()); + + public ObjectPool() + { + AddToFreeList(null); //0号位为null + } + + public void Clear() + { + freelist = LIST_END; + count = 0; + list = new Slot[512]; + reverseMap = new Dictionary(); + AddToFreeList(null); //0号位为null + } + + private void ExtendCapacity() + { + Slot[] new_list = new Slot[list.Length * 2]; + for (int i = 0; i < list.Length; i++) + { + new_list[i] = list[i]; + } + list = new_list; + } + + public int FindOrAddObject(object obj) + { + if (obj == null) return 0; + int id; + if (!reverseMap.TryGetValue(obj, out id)) + { + id = Add(obj); + } + return id << SHIFT_BIT; + } + + public int AddBoxedValueType(object obj) //不做检查,靠调用者保证 + { + return AddToFreeList(obj) << SHIFT_BIT; + } + + private int Add(object obj) + { + int id = AddToFreeList(obj); + reverseMap[obj] = id; + return id; + } + + private int AddToFreeList(object obj) + { + int index = LIST_END; + + if (freelist != LIST_END) + { + index = freelist; + list[index].obj = obj; + freelist = list[index].next; + list[index].next = ALLOCED; + } + else + { + if (count == list.Length) + { + ExtendCapacity(); + } + index = count; + list[index] = new Slot(ALLOCED, obj); + count = index + 1; + } + + return index; + } + + public bool TryGetValue(int index, out object obj) + { + index = index >> SHIFT_BIT; + if (index >= 0 && index < count && list[index].next == ALLOCED) + { + obj = list[index].obj; + return true; + } + + obj = null; + return false; + } + + public object Get(int index) + { + index = index >> SHIFT_BIT; + if (index >= 0 && index < count) + { + return list[index].obj; + } + return null; + } + + public object Remove(int index) + { + index = index >> SHIFT_BIT; + if (index >= 0 && index < count && list[index].next == ALLOCED) + { + object o = list[index].obj; + list[index].obj = null; + list[index].next = freelist; + freelist = index; + + int reverseId; + if (reverseMap.TryGetValue(o, out reverseId) && reverseId == index) + { + reverseMap.Remove(o); + } + return o; + } + + return null; + } + + private object ReplaceFreeList(int index, object o) + { + if (index >= 0 && index < count) + { + object obj = list[index].obj; + list[index].obj = o; + return obj; + } + + return null; + } + + public object ReplaceValueType(int index, object o) + { + index = index >> SHIFT_BIT; + return ReplaceFreeList(index, o); + } + + private void ReleaseObjectRefInner(int index) + { + object obj = ReplaceFreeList(index, null); + if (obj == null) return; + int objIndex; + if (reverseMap.TryGetValue(obj, out objIndex) && objIndex == index) + { + reverseMap.Remove(obj); + } + } + + public int Check(int checkPos, int maxCheck, Func checker, Dictionary reverseMap) + { + if (count == 0) + { + return 0; + } + for (int i = 0; i < Math.Min(maxCheck, count); ++i) + { + checkPos %= count; + if (list[checkPos].next == ALLOCED && !Object.ReferenceEquals(list[checkPos].obj, null)) + { + if (!checker(list[checkPos].obj)) + { + ReleaseObjectRefInner(checkPos); + } + } + ++checkPos; + } + + return checkPos %= count; + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/PuertsDLL.cs b/Assets/Puerts/Src/PuertsDLL.cs new file mode 100644 index 00000000..e673d285 --- /dev/null +++ b/Assets/Puerts/Src/PuertsDLL.cs @@ -0,0 +1,338 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Puerts +{ +#pragma warning disable 414 + public class MonoPInvokeCallbackAttribute : System.Attribute + { + private Type type; + public MonoPInvokeCallbackAttribute(Type t) + { + type = t; + } + } +#pragma warning restore 414 + + //[UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void V8FunctionCallback(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data); + + //[UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr V8ConstructorCallback(IntPtr isolate, IntPtr info, int paramLen, long data); + + //[UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void V8DestructorCallback(IntPtr self, long data); + + public delegate void V8IndexedGetterCallback(IntPtr isolate, IntPtr info, IntPtr self, uint index, long data); + + public delegate void V8IndexedSetterCallback(IntPtr isolate, IntPtr info, IntPtr self, uint index, IntPtr value, long data); + + public delegate void LogCallback(string content); + + [Flags] + public enum JsValueType + { + NullOrUndefined = 1, + BigInt = 2, + Number = 4, + String = 8, + Boolean = 16, + NativeObject = 32, + JsObject = 64, + Array = 128, + Function = 256, + Date = 512, + Unknow = 1024, + Any = NullOrUndefined | BigInt | Number | String | Boolean | NativeObject | Array | Function | Date, + }; + + public class PuertsDLL + { +#if (UNITY_IPHONE || UNITY_TVOS || UNITY_WEBGL || UNITY_SWITCH) && !UNITY_EDITOR + const string DLLNAME = "__Internal"; +#else + const string DLLNAME = "puerts"; +#endif + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr CreateJSEngine(); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void DestroyJSEngine(IntPtr isolate); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetGlobalFunction(IntPtr isolate, string name, V8FunctionCallback v8FunctionCallback, long data); + + private static string GetStringFromNative(IntPtr str, int strlen) + { + if (str != IntPtr.Zero) + { +#if XLUA_GENERAL || (UNITY_WSA && !UNITY_EDITOR) + int len = strlen.ToInt32(); + byte[] buffer = new byte[len]; + Marshal.Copy(str, buffer, 0, len); + return Encoding.UTF8.GetString(buffer); +#else + string ret = Marshal.PtrToStringAnsi(str, strlen); + if (ret == null) + { + int len = strlen; + byte[] buffer = new byte[len]; + Marshal.Copy(str, buffer, 0, len); + return Encoding.UTF8.GetString(buffer); + } + return ret; +#endif + } + else + { + return null; + } + } + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetLastExceptionInfo(IntPtr isolate, out int strlen); + + public static string GetLastExceptionInfo(IntPtr isolate) + { + int strlen; + IntPtr str = GetLastExceptionInfo(isolate, out strlen); + return GetStringFromNative(str, strlen); + } + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void LowMemoryNotification(IntPtr isolate); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetGeneralDestructor(IntPtr isolate, V8DestructorCallback generalDestructor); + + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Eval(IntPtr isolate, string code, string path); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int RegisterClass(IntPtr isolate, int BaseTypeId, string fullName, V8ConstructorCallback constructor, V8DestructorCallback destructor, long data); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int RegisterStruct(IntPtr isolate, int BaseTypeId, string fullName, V8ConstructorCallback constructor, V8DestructorCallback destructor, long data, int size); + + //切记注册的回调不能抛C#异常,必须先catch,然后转js异常 + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern bool RegisterFunction(IntPtr isolate, int classID, string name, bool isStatic, V8FunctionCallback callback, long data); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern bool RegisterProperty(IntPtr isolate, int classID, string name, bool isStatic, V8FunctionCallback getter, long getterData, V8FunctionCallback setter, long setterData); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern bool RegisterIndexedProperty(IntPtr isolate, int classID, V8IndexedGetterCallback getter, V8IndexedSetterCallback setter, long data); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReturnClass(IntPtr isolate, IntPtr info, int classID); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReturnObject(IntPtr isolate, IntPtr info, int classID, IntPtr self); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReturnNumber(IntPtr isolate, IntPtr info, double number); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReturnString(IntPtr isolate, IntPtr info, string str); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReturnBigInt(IntPtr isolate, IntPtr info, long number); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReturnBoolean(IntPtr isolate, IntPtr info, bool b); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReturnDate(IntPtr isolate, IntPtr info, double date); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReturnNull(IntPtr isolate, IntPtr info); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetArgumentValue(IntPtr info, int index); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern JsValueType GetJsValueType(IntPtr isolate, IntPtr value, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern JsValueType GetArgumentType(IntPtr isolate, IntPtr info, int index, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern double GetNumberFromValue(IntPtr isolate, IntPtr value, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern double GetDateFromValue(IntPtr isolate, IntPtr value, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetStringFromValue(IntPtr isolate, IntPtr value, out int len, bool isByRef); + + public static string GetStringFromValue(IntPtr isolate, IntPtr value, bool isByRef) + { + int strlen; + IntPtr str = GetStringFromValue(isolate, value, out strlen, isByRef); + return GetStringFromNative(str, strlen); + } + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern bool GetBooleanFromValue(IntPtr isolate, IntPtr value, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern long GetBigIntFromValue(IntPtr isolate, IntPtr value, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetObjectFromValue(IntPtr isolate, IntPtr value, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int GetTypeIdFromValue(IntPtr isolate, IntPtr value, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetFunctionFromValue(IntPtr isolate, IntPtr value, bool isByRef); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetNumberToOutValue(IntPtr isolate, IntPtr value, double number); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetDateToOutValue(IntPtr isolate, IntPtr value, double date); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetStringToOutValue(IntPtr isolate, IntPtr value, string str); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetBooleanToOutValue(IntPtr isolate, IntPtr value, bool b); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetBigIntToOutValue(IntPtr isolate, IntPtr value, long bigInt); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetObjectToOutValue(IntPtr isolate, IntPtr value, int classId, IntPtr ptr); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetNullToOutValue(IntPtr isolate, IntPtr value); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ThrowException(IntPtr isolate, string message); + + //begin cs call js + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PushNullForJSFunction(IntPtr function); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PushDateForJSFunction(IntPtr function, double dateValue); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PushBooleanForJSFunction(IntPtr function, bool b); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PushBigIntForJSFunction(IntPtr function, long l); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PushStringForJSFunction(IntPtr function, string str); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PushNumberForJSFunction(IntPtr function, double d); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PushObjectForJSFunction(IntPtr function, int classId, IntPtr objectId); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr InvokeJSFunction(IntPtr function, bool hasResult); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetFunctionLastExceptionInfo(IntPtr function, out int len); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ReleaseJSFunction(IntPtr isolate, IntPtr function); + + public static string GetFunctionLastExceptionInfo(IntPtr function) + { + int strlen; + IntPtr str = GetFunctionLastExceptionInfo(function, out strlen); + return GetStringFromNative(str, strlen); + } + + //保守方案 + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern JsValueType GetResultType(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern double GetNumberFromResult(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern double GetDateFromResult(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetStringFromResult(IntPtr resultInfo, out int len); + + public static string GetStringFromResult(IntPtr resultInfo) + { + int strlen; + IntPtr str = GetStringFromResult(resultInfo, out strlen); + return GetStringFromNative(str, strlen); + } + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern bool GetBooleanFromResult(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern long GetBigIntFromResult(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetObjectFromResult(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern int GetTypeIdFromResult(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetFunctionFromResult(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void ResetResult(IntPtr resultInfo); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PropertyReturnObject(IntPtr isolate, IntPtr info, int classID, IntPtr self); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PropertyReturnNumber(IntPtr isolate, IntPtr info, double number); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PropertyReturnString(IntPtr isolate, IntPtr info, string str); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PropertyReturnBigInt(IntPtr isolate, IntPtr info, long number); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PropertyReturnBoolean(IntPtr isolate, IntPtr info, bool b); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PropertyReturnDate(IntPtr isolate, IntPtr info, double date); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void PropertyReturnNull(IntPtr isolate, IntPtr info); + + //end cs call js + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void CreateInspector(IntPtr isolate, int port); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void DestroyInspector(IntPtr isolate); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void InspectorTick(IntPtr isolate); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public static extern void SetLogCallback(LogCallback log, LogCallback logWarning, LogCallback logError); + } +} + + diff --git a/Assets/Puerts/Src/Resources/puerts/cjsload.js.txt b/Assets/Puerts/Src/Resources/puerts/cjsload.js.txt new file mode 100644 index 00000000..4b0a9bf1 --- /dev/null +++ b/Assets/Puerts/Src/Resources/puerts/cjsload.js.txt @@ -0,0 +1,86 @@ +/* + * Tencent is pleased to support the open source community by making Puerts available. + * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. + * Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +var global = global || (function () { return this; }()); +(function (global) { + "use strict"; + + let loader = global.__tgjsGetLoader(); + global.__tgjsGetLoader = undefined; + + function pathNormalize(path) { + let reversePathFrags = path.split('/').reverse(); + let newPathFrags = []; + while (reversePathFrags.length > 0) { + let el = reversePathFrags.pop(); + if (el != "" && el != ".") { + if (el == ".." && newPathFrags.length > 0 && newPathFrags[newPathFrags.length - 1] != "..") { + newPathFrags.pop(); + } else { + newPathFrags.push(el); + } + } + } + return newPathFrags.join("/"); + } + + function searchModuleInDirWithExt(dir, requiredModule) { + var searchPath = pathNormalize(dir + '/' + requiredModule); + if (loader.FileExists(searchPath)) { + return searchPath; + } + if (dir.startsWith("node_modules")) { + searchPath = pathNormalize(dir + '/node_modules/' + requiredModule); + if (loader.FileExists(searchPath)) { + return searchPath; + } + } + } + + function getFileExtension(filepath) { + let last = filepath.split('/').pop(); + let frags = last.split('.'); + if (frags.length > 1) { + return frags.pop(); + } + } + + function searchModuleInDir(dir, requiredModule) { + if (getFileExtension(requiredModule)) { + return searchModuleInDirWithExt(dir, requiredModule); + } else { + return searchModuleInDirWithExt(dir, requiredModule + ".js") + || searchModuleInDirWithExt(dir, requiredModule + "/index.js") + || searchModuleInDirWithExt(dir, requiredModule + "/package.json"); + } + } + + function searchModule(dir, requiredModule) { + var result = searchModuleInDir(dir, requiredModule); + if (result) return result; + if (dir != "" && requiredModule.indexOf('/') == -1 && !requiredModule.endsWith(".js")) { + let pathFrags = dir.split('/'); + pathFrags.pop(); + while (pathFrags.length > 0) { + if (pathFrags[pathFrags.length - 1] != "node_modules") { + result = searchModuleInDir(pathFrags.join("/"), requiredModule); + if (result) return result; + } + pathFrags.pop(); + } + } + } + + function loadFile(path) { + let debugPath = {}; + var context = loader.ReadFile(path, debugPath); + return {context:context, debugPath:debugPath.value}; + } + + puerts.searchModule = searchModule; + puerts.loadFile = loadFile; +}(global)); diff --git a/Assets/Puerts/Src/Resources/puerts/csharp.js.txt b/Assets/Puerts/Src/Resources/puerts/csharp.js.txt new file mode 100644 index 00000000..321496e6 --- /dev/null +++ b/Assets/Puerts/Src/Resources/puerts/csharp.js.txt @@ -0,0 +1,101 @@ +/* + * Tencent is pleased to support the open source community by making Puerts available. + * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. + * Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +var global = global || (function () { return this; }()); +(function (global) { + "use strict"; + + function createTypeProxy(namespace) { + return new Proxy(Object.create(null), { + get: function(cache, name) { + if (!(name in cache)) { + let fullName = namespace ? (namespace + '.' + name) : name; + if (/\$\d+$/.test(name)) { + let genericTypeInfo = new Map(); + genericTypeInfo.set('$name', fullName.replace('$', '`')); + cache[name] = genericTypeInfo; + } else { + let cls = puerts.loadType(fullName); + if (cls) { + cache[name] = cls + let parentPrototype = Object.getPrototypeOf(cls.prototype); + if (parentPrototype) { + Object.setPrototypeOf(cls, parentPrototype.constructor);//v8 api的inherit并不能把静态属性也继承,通过这种方式修复下 + } + //console.log(fullName + ' is a class'); + } else { + cache[name] = createTypeProxy(fullName); + //console.log(fullName + ' is a namespace'); + } + } + } + return cache[name]; + } + }); + } + + let csharpModule = createTypeProxy(undefined); + puerts.registerBuildinModule('csharp', csharpModule); + + csharpModule.System.Object.prototype.toString = csharpModule.System.Object.prototype.ToString; + + function ref(x) { + return {value:x}; + } + + function unref(r) { + return r.value; + } + + function setref(x, val) { + x.value = val; + } + + function taskToPromise(task) { + return new Promise((resolve, reject) => { + task.GetAwaiter().OnCompleted(() => { + let t = task; + task = undefined; + if (t.IsFaulted) { + if (t.Exception) { + if (t.Exception.InnerException) { + reject(t.Exception.InnerException.Message); + } else { + reject(t.Exception.Message); + } + } else { + reject("unknow exception!"); + } + } else { + resolve(t.Result); + } + }); + }); + } + + function makeGeneric(genericTypeInfo, ...genericArgs) { + let p = genericTypeInfo; + for (var i = 0; i < genericArgs.length; i++) { + let genericArg = genericArgs[i]; + if (!p.get(genericArg)) { + p.set(genericArg, new Map()); + } + p = p.get(genericArg); + } + if (!p.get('$type')) { + p.set('$type', puerts.loadType(genericTypeInfo.get('$name'), ...genericArgs)); + } + return p.get('$type'); + } + + puerts.$ref = ref; + puerts.$unref = unref; + puerts.$set = setref; + puerts.$promise = taskToPromise; + puerts.$generic = makeGeneric; + +}(global)); \ No newline at end of file diff --git a/Assets/Puerts/Src/Resources/puerts/init.js.txt b/Assets/Puerts/Src/Resources/puerts/init.js.txt new file mode 100644 index 00000000..9b1b6715 --- /dev/null +++ b/Assets/Puerts/Src/Resources/puerts/init.js.txt @@ -0,0 +1,21 @@ +/* + * Tencent is pleased to support the open source community by making Puerts available. + * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. + * Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +var global = global || (function () { return this; }()); +(function (global) { + "use strict"; + + let puerts = global.puerts = global.puerts || {}; + + puerts.loadType = global.__tgjsLoadType; + global.__tgjsLoadType = undefined; + + puerts.evalScript = global.__tgjsEvalScript || function(script, debugPath) { + return eval(script); + } + global.__tgjsEvalScript = undefined; +}(global)); diff --git a/Assets/Puerts/Src/Resources/puerts/log.js.txt b/Assets/Puerts/Src/Resources/puerts/log.js.txt new file mode 100644 index 00000000..50e0539b --- /dev/null +++ b/Assets/Puerts/Src/Resources/puerts/log.js.txt @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making Puerts available. + * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. + * Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +var global = global || (function () { return this; }()); +(function (global) { + "use strict"; + + let UnityEngine_Debug = puerts.loadType('UnityEngine.Debug'); + + var console = {} + + function toString(args) { + return Array.prototype.map.call(args, x => x === null? "null": x === undefined ? 'undefined' : x.toString()).join(','); + } + + console.log = function(msg) { + UnityEngine_Debug.Log(toString(arguments)); + } + + console.info = function(msg) { + UnityEngine_Debug.Log(toString(arguments)); + } + + console.warn = function(msg) { + UnityEngine_Debug.LogWarning(toString(arguments)); + } + + console.error = function(msg) { + UnityEngine_Debug.LogError(toString(arguments)); + } + + global.console = console; + puerts.console = console; +}(global)); diff --git a/Assets/Puerts/Src/Resources/puerts/modular.js.txt b/Assets/Puerts/Src/Resources/puerts/modular.js.txt new file mode 100644 index 00000000..917c6da9 --- /dev/null +++ b/Assets/Puerts/Src/Resources/puerts/modular.js.txt @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Puerts available. + * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. + * Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +var global = global || (function () { return this; }()); +(function (global) { + "use strict"; + + let loadModule = function(moduleName, requiringDir) { + let path = puerts.searchModule(requiringDir, moduleName); + if (!path) throw new Error("can not find " + moduleName); + let {context, debugPath} = puerts.loadFile(path); + return {fullPath: path, debugPath: debugPath, script: context} + } + + let moduleCache = Object.create(null); + let buildinModule = Object.create(null); + function executeModule(fullPath, script, debugPath) { + let fullPathInJs = fullPath.replace(/\\/g, '\\\\'); + let fullDirInJs = (fullPath.indexOf('/') != -1) ? fullPath.substring(0, fullPath.lastIndexOf("/")) : fullPath.substring(0, fullPath.lastIndexOf("\\")).replace(/\\/g, '\\\\'); + let executeScript = "(function() { var __filename = '" + + fullPathInJs + "', __dirname = '" + + fullDirInJs + "', exports ={}, module = { exports : exports, filename : __filename }; (function (exports, require, console, prompt) { " + + script + "\n})(exports, puerts.genRequire('" + + fullDirInJs + "'), puerts.console); return module.exports})()"; + return puerts.evalScript(executeScript, debugPath); + } + + function genRequire(requiringDir) { + let localModuleCache = Object.create(null); + function require(moduleName) { + moduleName = moduleName.startsWith('./') ? moduleName.substr(2) : moduleName; + if (moduleName in localModuleCache) return localModuleCache[moduleName]; + if (moduleName in buildinModule) return buildinModule[moduleName]; + let {fullPath, debugPath, script} = loadModule(moduleName, requiringDir); + let key = fullPath; + if (key in moduleCache) { + localModuleCache[moduleName] = moduleCache[key]; + return localModuleCache[moduleName]; + } + let m + if (fullPath.endsWith("package.json")) { + let packageConfigure = JSON.parse(script); + let fullDirInJs = (fullPath.indexOf('/') != -1) ? fullPath.substring(0, fullPath.lastIndexOf("/")) : fullPath.substring(0, fullPath.lastIndexOf("\\")).replace(/\\/g, '\\\\'); + let tmpRequire = genRequire(fullDirInJs); + m = tmpRequire(packageConfigure.main); + } else { + m = executeModule(fullPath, script, debugPath); + } + localModuleCache[moduleName] = m; + moduleCache[key] = m; + return m; + } + + return require; + } + + function registerBuildinModule(name, module) { + buildinModule[name] = module; + } + + registerBuildinModule("puerts", puerts) + + puerts.genRequire = genRequire; + + puerts.registerBuildinModule = registerBuildinModule; + + global.require = genRequire(""); +}(global)); \ No newline at end of file diff --git a/Assets/Puerts/Src/StatiCallbacks.cs b/Assets/Puerts/Src/StatiCallbacks.cs new file mode 100644 index 00000000..807f4d0f --- /dev/null +++ b/Assets/Puerts/Src/StatiCallbacks.cs @@ -0,0 +1,54 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; + +namespace Puerts +{ + internal class StatiCallbacks + { + [MonoPInvokeCallback(typeof(V8FunctionCallback))] + internal static void JsEnvCallbackWrap(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data) + { + int jsEnvIdx, callbackIdx; + Utils.LongToTwoInt(data, out jsEnvIdx, out callbackIdx); + JsEnv.jsEnvs[jsEnvIdx].InvokeCallback(isolate, callbackIdx, info, self, paramLen); + } + + [MonoPInvokeCallback(typeof(V8DestructorCallback))] + internal static void GeneralDestructor(IntPtr self, long data) + { + int jsEnvIdx, callbackIdx; + Utils.LongToTwoInt(data, out jsEnvIdx, out callbackIdx); + JsEnv.jsEnvs[jsEnvIdx].JsReleaseObject(self.ToInt32()); + } + + [MonoPInvokeCallback(typeof(V8DestructorCallback))] + internal static IntPtr ConstructorWrap(IntPtr isolate, IntPtr info, int paramLen, long data) + { + int jsEnvIdx, callbackIdx; + Utils.LongToTwoInt(data, out jsEnvIdx, out callbackIdx); + return JsEnv.jsEnvs[jsEnvIdx].InvokeConstructor(isolate, callbackIdx, info, paramLen); + } + + [MonoPInvokeCallback(typeof(V8IndexedGetterCallback))] + internal static void IndexedGetterWrap(IntPtr isolate, IntPtr info, IntPtr self, uint index, long data) + { + int jsEnvIdx, callbackIdx; + Utils.LongToTwoInt(data, out jsEnvIdx, out callbackIdx); + JsEnv.jsEnvs[jsEnvIdx].TypeRegister.ArrayGet(isolate, info, self, index); + } + + [MonoPInvokeCallback(typeof(V8IndexedSetterCallback))] + internal static void IndexedSetterWrap(IntPtr isolate, IntPtr info, IntPtr self, uint index, IntPtr value, long data) + { + int jsEnvIdx, callbackIdx; + Utils.LongToTwoInt(data, out jsEnvIdx, out callbackIdx); + JsEnv.jsEnvs[jsEnvIdx].TypeRegister.ArraySet(isolate, info, self, index, value); + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/StaticTranslate.cs b/Assets/Puerts/Src/StaticTranslate.cs new file mode 100644 index 00000000..128f0a9c --- /dev/null +++ b/Assets/Puerts/Src/StaticTranslate.cs @@ -0,0 +1,199 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; + +namespace Puerts +{ + public static class StaticTranslate + { + public delegate void PushFunc(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr function, T o); + + public delegate T GetFunc(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef); + + public static PushFunc Set = DefaultPush; + + public static GetFunc Get = DefaultGetResult; + + private static void DefaultPush(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr function, T o) + { + JsEnv.jsEnvs[jsEnvIdx].GeneralSetterManager.GetTranslateFunc(typeof(T))(isolate, setValueApi, function, o); + } + + private static T DefaultGetResult(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr value, bool isByRef) + { + return (T)JsEnv.jsEnvs[jsEnvIdx].GeneralGetterManager.GetTranslateFunc(typeof(T))(isolate, getValueApi, value, isByRef); + } + + public static void ReplaceDefault(PushFunc pushFunc, GetFunc getFunc) + { + Set = pushFunc; + Get = getFunc; + } + } + + public static class PrimitiveTypeTranslate + { + public static void PushChar(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, char i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static char GetChar(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (char)getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushSByte(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, sbyte i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static sbyte GetSByte(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (sbyte)getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushByte(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, byte i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static byte GetByte(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (byte)getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushInt16(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, short i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static short GetInt16(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (short)getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushUInt16(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, ushort i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static ushort GetUInt16(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (ushort)getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushInt32(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, int i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static int GetInt32(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (int)getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushUInt32(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, uint i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static uint GetUInt32(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (uint)getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushInt64(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, long i) + { + setValueApi.SetBigInt(isolate, holder, i); + } + + public static long GetInt64(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return getValueApi.GetBigInt(isolate, holder, isByRef); + } + + public static void PushUInt64(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, ulong i) + { + setValueApi.SetBigInt(isolate, holder, (long)i); + } + + public static ulong GetUInt64(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (ulong)getValueApi.GetBigInt(isolate, holder, isByRef); + } + + public static void PushDouble(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, double i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static double GetDouble(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushFloat(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, float i) + { + setValueApi.SetNumber(isolate, holder, i); + } + + public static float GetFloat(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return (float)getValueApi.GetNumber(isolate, holder, isByRef); + } + + public static void PushBoolean(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, bool i) + { + setValueApi.SetBoolean(isolate, holder, i); + } + + public static bool GetBoolean(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return getValueApi.GetBoolean(isolate, holder, isByRef); + } + + public static void PushString(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, string i) + { + setValueApi.SetString(isolate, holder, i); + } + + public static string GetString(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + return getValueApi.GetString(isolate, holder, isByRef); + } + + public static void PushDateTime(int jsEnvIdx, IntPtr isolate, ISetValueToJs setValueApi, IntPtr holder, DateTime date) + { + setValueApi.SetDate(isolate, holder, (date - new DateTime(1970, 1, 1)).TotalMilliseconds); + } + + public static DateTime GetDateTime(int jsEnvIdx, IntPtr isolate, IGetValueFromJs getValueApi, IntPtr holder, bool isByRef) + { + var ticks = getValueApi.GetDate(isolate, holder, isByRef); + return (new DateTime(1970, 1, 1)).AddMilliseconds(ticks); + } + + internal static void Init() + { + StaticTranslate.ReplaceDefault(PushChar, GetChar); + StaticTranslate.ReplaceDefault(PushSByte, GetSByte); + StaticTranslate.ReplaceDefault(PushByte, GetByte); + StaticTranslate.ReplaceDefault(PushInt16, GetInt16); + StaticTranslate.ReplaceDefault(PushUInt16, GetUInt16); + StaticTranslate.ReplaceDefault(PushInt32, GetInt32); + StaticTranslate.ReplaceDefault(PushUInt32, GetUInt32); + StaticTranslate.ReplaceDefault(PushInt64, GetInt64); + StaticTranslate.ReplaceDefault(PushUInt64, GetUInt64); + StaticTranslate.ReplaceDefault(PushDouble, GetDouble); + StaticTranslate.ReplaceDefault(PushFloat, GetFloat); + StaticTranslate.ReplaceDefault(PushString, GetString); + StaticTranslate.ReplaceDefault(PushDateTime, GetDateTime); + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/StaticWrapRegister.cs b/Assets/Puerts/Src/StaticWrapRegister.cs new file mode 100644 index 00000000..4e61bcc1 --- /dev/null +++ b/Assets/Puerts/Src/StaticWrapRegister.cs @@ -0,0 +1,35 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System.Collections.Generic; + +namespace Puerts +{ + public struct PropertyRegisterInfo + { + public bool IsStatic; + public V8FunctionCallback Getter; + public V8FunctionCallback Setter; + } + + public struct MethodKey + { + public string Name; + public bool IsStatic; + } + + public class TypeRegisterInfo + { + public bool BlittableCopy; + + public V8ConstructorCallback Constructor; + + public Dictionary Methods; + + public Dictionary Properties; + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/TypeExtensions.cs b/Assets/Puerts/Src/TypeExtensions.cs new file mode 100644 index 00000000..7257ac2e --- /dev/null +++ b/Assets/Puerts/Src/TypeExtensions.cs @@ -0,0 +1,198 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; +using System.Linq; + +namespace Puerts +{ + public static class TypeExtensions + { + internal static bool IsValueType(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsValueType; +#else + return type.GetTypeInfo().IsValueType; +#endif + } + + internal static bool IsEnum(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsEnum; +#else + return type.GetTypeInfo().IsEnum; +#endif + } + + internal static bool IsPrimitive(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsPrimitive; +#else + return type.GetTypeInfo().IsPrimitive; +#endif + } + + internal static bool IsAbstract(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsAbstract; +#else + return type.GetTypeInfo().IsAbstract; +#endif + } + + internal static bool IsSealed(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsSealed; +#else + return type.GetTypeInfo().IsSealed; +#endif + } + + internal static bool IsInterface(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsInterface; +#else + return type.GetTypeInfo().IsInterface; +#endif + } + + internal static bool IsClass(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsClass; +#else + return type.GetTypeInfo().IsClass; +#endif + } + + internal static Type BaseType(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.BaseType; +#else + return type.GetTypeInfo().BaseType; +#endif + } + + internal static bool IsGenericType(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsGenericType; +#else + return type.GetTypeInfo().IsGenericType; +#endif + } + + internal static bool IsGenericTypeDefinition(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsGenericTypeDefinition; +#else + return type.GetTypeInfo().IsGenericTypeDefinition; +#endif + } + +#if UNITY_WSA && !UNITY_EDITOR + internal static bool IsSubclassOf(this Type type, Type c) + { + return type.GetTypeInfo().IsSubclassOf(c); + } + + internal static bool IsDefined(this Type type, Type attributeType, bool inherit) + { + return type.GetTypeInfo().IsDefined(attributeType, inherit); + } + + internal static Type[] GetGenericParameterConstraints(this Type type) + { + return type.GetTypeInfo().GetGenericParameterConstraints(); + } +#endif + + internal static bool IsNestedPublic(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsNestedPublic; +#else + return type.GetTypeInfo().IsNestedPublic; +#endif + } + + internal static bool IsPublic(this Type type) + { +#if !UNITY_WSA || UNITY_EDITOR + return type.IsPublic; +#else + return type.GetTypeInfo().IsPublic; +#endif + } + + internal static bool IsStruct(this Type type) + { + return type.IsValueType() && !type.IsEnum() && !type.IsPrimitive(); + } + + public static string GetFriendlyName(this Type type) + { + if (type == typeof(int)) + return "int"; + if (type == typeof(uint)) + return "uint"; + else if (type == typeof(short)) + return "short"; + else if (type == typeof(byte)) + return "byte"; + else if (type == typeof(sbyte)) + return "sbyte"; + else if (type == typeof(ushort)) + return "ushort"; + else if (type == typeof(bool)) + return "bool"; + else if (type == typeof(long)) + return "long"; + else if (type == typeof(ulong)) + return "ulong"; + else if (type == typeof(float)) + return "float"; + else if (type == typeof(double)) + return "double"; + else if (type == typeof(decimal)) + return "decimal"; + else if (type == typeof(string)) + return "string"; + else if (type == typeof(void)) + return "void"; + else if (type.IsNested) + { + if (type.DeclaringType.IsGenericTypeDefinition) + { + var genericArgumentNames = type.GetGenericArguments() + .Select(x => GetFriendlyName(x)).ToArray(); + return type.DeclaringType.FullName.Split('`')[0] + "<" + string.Join(", ", genericArgumentNames) + ">" + '.' + type.Name; + } + else + { + return GetFriendlyName(type.DeclaringType) + '.' + type.Name; + } + } + else if (type.IsGenericType) + { + var genericArgumentNames = type.GetGenericArguments() + .Select(x => GetFriendlyName(x)).ToArray(); + return type.FullName.Split('`')[0] + "<" + string.Join(", ", genericArgumentNames) + ">"; + } + else + return type.FullName; + } + } +} diff --git a/Assets/Puerts/Src/TypeRegister.cs b/Assets/Puerts/Src/TypeRegister.cs new file mode 100644 index 00000000..fead2f3c --- /dev/null +++ b/Assets/Puerts/Src/TypeRegister.cs @@ -0,0 +1,492 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Linq; + +namespace Puerts +{ + internal class TypeRegister + { + public TypeRegister(JsEnv jsEnv) + { + this.jsEnv = jsEnv; + +#if (UNITY_WSA && !ENABLE_IL2CPP) && !UNITY_EDITOR + var assembliesUsorted = Utils.GetAssemblies(); +#else + assemblies.Add(Assembly.GetExecutingAssembly()); + var assembliesUsorted = AppDomain.CurrentDomain.GetAssemblies(); +#endif + AddAssemblieByName(assembliesUsorted, "mscorlib,"); //Ϊ⼸ǰ + AddAssemblieByName(assembliesUsorted, "System,"); + AddAssemblieByName(assembliesUsorted, "System.Core,"); + foreach (Assembly assembly in assembliesUsorted) + { + if (!assemblies.Contains(assembly)) + { + assemblies.Add(assembly); + } + } + } + + void ArrayLength(IntPtr isolate, IntPtr info, IntPtr self, int argumentsLen) + { + try + { + Array array = jsEnv.GeneralGetterManager.GetSelf(self) as Array; + PuertsDLL.ReturnNumber(isolate, info, array.Length); + } + catch(Exception e) + { + PuertsDLL.ThrowException(isolate, "array.length throw c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + + internal void ArrayGet(IntPtr isolate, IntPtr info, IntPtr self, uint index) + { + try + { + Array array = jsEnv.GeneralGetterManager.GetSelf(self) as Array; + var transalteFunc = jsEnv.GeneralSetterManager.GetTranslateFunc(array.GetType().GetElementType()); + transalteFunc(isolate, NativeValueApi.SetValueToIndexResult, info, array.GetValue((int)index)); + } + catch (Exception e) + { + PuertsDLL.ThrowException(isolate, "array.get throw c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + + internal void ArraySet(IntPtr isolate, IntPtr info, IntPtr self, uint index, IntPtr value) + { + try + { + Array array = jsEnv.GeneralGetterManager.GetSelf(self) as Array; + var transalteFunc = jsEnv.GeneralGetterManager.GetTranslateFunc(array.GetType().GetElementType()); + var val = transalteFunc(isolate, NativeValueApi.GetValueFromArgument, value, false); + array.SetValue(val, (int)index); + } + catch (Exception e) + { + PuertsDLL.ThrowException(isolate, "array.get throw c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + + private int arrayTypeId = -1; + internal void InitArrayTypeId(IntPtr isolate) + { + arrayTypeId = PuertsDLL.RegisterClass(jsEnv.isolate, GetTypeId(isolate, typeof(Array)), "__puerts.Array", null, null, 0); + PuertsDLL.RegisterProperty(jsEnv.isolate, arrayTypeId, "length", false, callbackWrap, jsEnv.AddCallback(ArrayLength), null, 0); + PuertsDLL.RegisterIndexedProperty(jsEnv.isolate, arrayTypeId, StatiCallbacks.IndexedGetterWrap, StatiCallbacks.IndexedSetterWrap, Utils.TwoIntToLong(jsEnv.Idx, 0)); + } + + void AddAssemblieByName(IEnumerable assembliesUsorted, string name) + { + foreach (var assemblie in assembliesUsorted) + { + if (assemblie.FullName.StartsWith(name) && !assemblies.Contains(assemblie)) + { + assemblies.Add(assemblie); + break; + } + } + } + +#if (UNITY_WSA && !ENABLE_IL2CPP) && !UNITY_EDITOR + public static List GetAssemblies() + { + List assembliesCache = null; + System.Threading.Tasks.Task t = new System.Threading.Tasks.Task(() => + { + assembliesCache = GetAssemblyList().Result; + }); + t.Start(); + t.Wait(); + return assembliesCache; + + } + + public static async System.Threading.Tasks.Task> GetAssemblyList() + { + List assemblies = new List(); + //return assemblies; + var files = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFilesAsync(); + if (files == null) + return assemblies; + + foreach (var file in files.Where(file => file.FileType == ".dll" || file.FileType == ".exe")) + { + try + { + assemblies.Add(Assembly.Load(new AssemblyName(file.DisplayName))); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } + + } + return assemblies; + } +#endif + internal Type GetType(string className, bool isQualifiedName = false) + { + Type type = Type.GetType(className, false); + if (type != null) + { + return type; + } + foreach (Assembly assembly in assemblies) + { + type = assembly.GetType(className); + + if (type != null) + { + return type; + } + } + int p1 = className.IndexOf('['); + if (p1 > 0 && !isQualifiedName) + { + string qualified_name = className.Substring(0, p1 + 1); + string[] generic_params = className.Substring(p1 + 1, className.Length - qualified_name.Length - 1).Split(','); + for (int i = 0; i < generic_params.Length; i++) + { + Type generic_param = GetType(generic_params[i].Trim()); + if (generic_param == null) + { + return null; + } + if (i != 0) + { + qualified_name += ", "; + } + qualified_name = qualified_name + "[" + generic_param.AssemblyQualifiedName + "]"; + } + qualified_name += "]"; + return GetType(qualified_name, true); + } + return null; + } + + private readonly V8FunctionCallback callbackWrap = new V8FunctionCallback(StatiCallbacks.JsEnvCallbackWrap); + + private readonly V8ConstructorCallback constructorWrap = new V8ConstructorCallback(StatiCallbacks.ConstructorWrap); + + private readonly Dictionary typeIdMap = new Dictionary(); + + private readonly Dictionary typeMap = new Dictionary(); + + private readonly JsEnv jsEnv; + + private readonly List assemblies = new List(); + + class ProperyMethods + { + public MethodInfo Getter; + public MethodInfo Setter; + } + + private FunctionCallback GenFieldGetter(Type type, FieldInfo field) + { + var translateFunc = jsEnv.GeneralSetterManager.GetTranslateFunc(field.FieldType); + if (field.IsStatic) + { + return (IntPtr isolate, IntPtr info, IntPtr self, int argumentsLen) => + { + translateFunc(isolate, NativeValueApi.SetValueToResult, info, field.GetValue(null)); + }; + } + else + { + return (IntPtr isolate, IntPtr info, IntPtr self, int argumentsLen) => + { + var me = jsEnv.GeneralGetterManager.GetSelf(self); + translateFunc(isolate, NativeValueApi.SetValueToResult, info, field.GetValue(me)); + }; + } + } + + private FunctionCallback GenFieldSetter(Type type, FieldInfo field) + { + var translateFunc = jsEnv.GeneralGetterManager.GetTranslateFunc(field.FieldType); + var typeMask = GeneralGetterManager.GetJsTypeMask(field.FieldType); + if (field.IsStatic) + { + return (IntPtr isolate, IntPtr info, IntPtr self, int argumentsLen) => + { + var valuePtr = PuertsDLL.GetArgumentValue(info, 0); + var valueType = PuertsDLL.GetJsValueType(isolate, valuePtr, false); + if ((typeMask & valueType) != valueType) + { + PuertsDLL.ThrowException(isolate, "expect " + typeMask + " but got " + valueType); + } + else + { + field.SetValue(null, translateFunc(isolate, NativeValueApi.GetValueFromArgument, valuePtr, false)); + } + }; + } + else + { + return (IntPtr isolate, IntPtr info, IntPtr self, int argumentsLen) => + { + var valuePtr = PuertsDLL.GetArgumentValue(info, 0); + var valueType = PuertsDLL.GetJsValueType(isolate, valuePtr, false); + if ((typeMask & valueType) != valueType) + { + PuertsDLL.ThrowException(isolate, "expect " + typeMask + " but got " + valueType); + } + else + { + var me = jsEnv.GeneralGetterManager.GetSelf(self); + field.SetValue(me, translateFunc(isolate, NativeValueApi.GetValueFromArgument, valuePtr, false)); + } + }; + } + } + + Dictionary> lazyStaticWrapLoaders = new Dictionary>(); + + internal void AddLazyStaticWrapLoader(Type type, Func lazyStaticWrapLoader) + { + lazyStaticWrapLoaders.Add(type, lazyStaticWrapLoader); + } + + // #lizard forgives + private int RegisterType(IntPtr isolate, Type type, bool includeNoPublic) + { + TypeRegisterInfo registerInfo = null; + + if (lazyStaticWrapLoaders.ContainsKey(type)) + { + registerInfo = lazyStaticWrapLoaders[type](); + lazyStaticWrapLoaders.Remove(type); + } + + BindingFlags flag = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + if (includeNoPublic) + { + flag = flag | BindingFlags.NonPublic; + } + + MethodInfo[] methods = type.GetMethods(flag); + Dictionary> methodGroup = new Dictionary>(); + Dictionary propertyGroup = new Dictionary(); + + for (int i = 0; i < methods.Length; ++i) + { + MethodInfo method = methods[i]; + if (method.IsConstructor || method.IsGenericMethodDefinition) + { + continue; + } + + MethodKey methodKey = new MethodKey { Name = method.Name, IsStatic = method.IsStatic }; + + if (registerInfo != null && registerInfo.Methods.ContainsKey(methodKey)) + { + continue; + } + + if (method.IsSpecialName && method.Name.StartsWith("get_") && method.GetParameters().Length != 1) // getter of property + { + string propName = method.Name.Substring(4); + if (registerInfo != null && registerInfo.Properties.ContainsKey(propName)) + { + continue; + } + ProperyMethods properyMethods; + if (!propertyGroup.TryGetValue(propName, out properyMethods)) + { + properyMethods = new ProperyMethods(); + propertyGroup.Add(propName, properyMethods); + } + properyMethods.Getter = method; + continue; + } + else if (method.IsSpecialName && method.Name.StartsWith("set_") && method.GetParameters().Length != 2) // setter of property + { + string propName = method.Name.Substring(4); + if (registerInfo != null && registerInfo.Properties.ContainsKey(propName)) + { + continue; + } + ProperyMethods properyMethods; + if (!propertyGroup.TryGetValue(propName, out properyMethods)) + { + properyMethods = new ProperyMethods(); + propertyGroup.Add(propName, properyMethods); + } + properyMethods.Setter = method; + continue; + } + + List overloads; + if (!methodGroup.TryGetValue(methodKey, out overloads)) + { + overloads = new List(); + methodGroup.Add(methodKey, overloads); + } + overloads.Add(method); + } + + ConstructorCallback constructorCallback = null; + + if (typeof(Delegate).IsAssignableFrom(type)) + { + DelegateConstructWrap delegateConstructWrap = new DelegateConstructWrap(type, jsEnv.GeneralGetterManager); + constructorCallback = delegateConstructWrap.Construct; + } + else + { + var constructorWraps = type.GetConstructors(flag) + .Select(m => new OverloadReflectionWrap(m, jsEnv.GeneralGetterManager, jsEnv.GeneralSetterManager)).ToList(); + MethodReflectionWrap constructorReflectionWrap = new MethodReflectionWrap(".ctor", constructorWraps); + constructorCallback = constructorReflectionWrap.Construct; + } + + int baseTypeId = -1; + if (type.BaseType != null) + { + baseTypeId = GetTypeId(isolate, type.BaseType); + } + + int typeId = -1; + if (registerInfo == null) + { + typeId = PuertsDLL.RegisterClass(jsEnv.isolate, baseTypeId, type.AssemblyQualifiedName, constructorWrap, null, jsEnv.AddConstructor(constructorCallback)); + } + else + { + if (registerInfo.BlittableCopy) + { + typeId = PuertsDLL.RegisterStruct(jsEnv.isolate, -1, type.AssemblyQualifiedName, registerInfo.Constructor, + null, jsEnv.Idx, System.Runtime.InteropServices.Marshal.SizeOf(type)); + } + else + { + typeId = PuertsDLL.RegisterClass(jsEnv.isolate, baseTypeId, type.AssemblyQualifiedName, registerInfo.Constructor, null, jsEnv.Idx); + } + + foreach (var kv in registerInfo.Methods) + { + PuertsDLL.RegisterFunction(jsEnv.isolate, typeId, kv.Key.Name, kv.Key.IsStatic, kv.Value, jsEnv.Idx); + } + + foreach (var kv in registerInfo.Properties) + { + PuertsDLL.RegisterProperty(jsEnv.isolate, typeId, kv.Key, kv.Value.IsStatic, kv.Value.Getter, jsEnv.Idx, kv.Value.Setter, jsEnv.Idx); + } + } + + //int typeId = PuertsDLL.RegisterClass(jsEngine, type.AssemblyQualifiedName, null, null, Utils.TwoIntToLong(idx, 0)); + + foreach (var kv in methodGroup) + { + MethodReflectionWrap methodReflectionWrap = new MethodReflectionWrap(kv.Key.Name, kv.Value.Select(m => new OverloadReflectionWrap(m, jsEnv.GeneralGetterManager, jsEnv.GeneralSetterManager)).ToList()); + PuertsDLL.RegisterFunction(jsEnv.isolate, typeId, kv.Key.Name, kv.Key.IsStatic, callbackWrap, jsEnv.AddCallback(methodReflectionWrap.Invoke)); + } + + foreach(var kv in propertyGroup) + { + V8FunctionCallback getter = null; + long getterData = 0; + bool isStatic = false; + if (kv.Value.Getter != null) + { + getter = callbackWrap; + MethodReflectionWrap methodReflectionWrap = new MethodReflectionWrap(kv.Value.Getter.Name, new List() { + new OverloadReflectionWrap(kv.Value.Getter, jsEnv.GeneralGetterManager, jsEnv.GeneralSetterManager) + }); + getterData = jsEnv.AddCallback(methodReflectionWrap.Invoke); + isStatic = kv.Value.Getter.IsStatic; + } + V8FunctionCallback setter = null; + long setterData = 0; + if (kv.Value.Setter != null) + { + setter = callbackWrap; + MethodReflectionWrap methodReflectionWrap = new MethodReflectionWrap(kv.Value.Setter.Name, new List() { + new OverloadReflectionWrap(kv.Value.Setter, jsEnv.GeneralGetterManager, jsEnv.GeneralSetterManager) + }); + setterData = jsEnv.AddCallback(methodReflectionWrap.Invoke); + isStatic = kv.Value.Setter.IsStatic; + } + PuertsDLL.RegisterProperty(jsEnv.isolate, typeId, kv.Key, isStatic, getter, getterData, setter, setterData); + } + + foreach(var field in type.GetFields(flag)) + { + if (registerInfo != null && registerInfo.Properties.ContainsKey(field.Name)) + { + continue; + } + var getterData = jsEnv.AddCallback(GenFieldGetter(type, field)); + + V8FunctionCallback setter = null; + long setterData = 0; + + if (!field.IsInitOnly && !field.IsLiteral) + { + setter = callbackWrap; + setterData = jsEnv.AddCallback(GenFieldSetter(type, field)); + } + + PuertsDLL.RegisterProperty(jsEnv.isolate, typeId, field.Name, field.IsStatic, callbackWrap, getterData, setter, setterData); + } + + return typeId; + } + /* + unsafe private int RegisterTestStruct(IntPtr isolate, Type type) + { + int typeId = PuertsDLL.RegisterStruct(jsEnv.isolate, -1, type.AssemblyQualifiedName, TestStructWrap.Constructor, null, 0, System.Runtime.InteropServices.Marshal.SizeOf(type)); + PuertsDLL.RegisterProperty(jsEnv.isolate, typeId, "X", false, TestStructWrap.GetX, 0, TestStructWrap.SetX, 0); + PuertsDLL.RegisterProperty(jsEnv.isolate, typeId, "Y", false, TestStructWrap.GetY, 0, TestStructWrap.SetY, 0); + jsEnv.generalGetterManager.RegisterTranslateFunc(type, (IntPtr isolate1, IJsValueApi nativeTranslateApi, IntPtr value, bool isByRef) => + { + TestStruct* testStruct = (TestStruct*)nativeTranslateApi.GetObject(isolate1, value, isByRef); + return *testStruct; + }); + + return typeId; + } + */ + public int GetTypeId(IntPtr isolate, Type type, out bool isFirst) + { + if (type.IsArray) + { + isFirst = false; + return arrayTypeId; + } + int typeId; + isFirst = false; + if (!typeIdMap.TryGetValue(type, out typeId)) + { + isFirst = true; + typeId = /*typeof(TestStruct) == type ? RegisterTestStruct(isolate, type) : */RegisterType(isolate, type, false); + typeIdMap[type] = typeId; + typeMap[typeId] = type; + } + return typeId; + } + + public int GetTypeId(IntPtr isolate, Type type) + { + bool isFirst; + return GetTypeId(isolate, type, out isFirst); + } + + public Type GetType(int typeId) + { + return typeMap[typeId]; + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Src/Utils.cs b/Assets/Puerts/Src/Utils.cs new file mode 100644 index 00000000..6bd7e9ee --- /dev/null +++ b/Assets/Puerts/Src/Utils.cs @@ -0,0 +1,36 @@ +/* +* Tencent is pleased to support the open source community by making Puerts available. +* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. +* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. +*/ + +using System; + +namespace Puerts +{ + static class Utils + { + public static long TwoIntToLong(int a, int b) + { + return (long)a << 32 | b & 0xFFFFFFFFL; + } + + public static void LongToTwoInt(long c, out int a, out int b) + { + a = (int)(c >> 32); + b = (int)c; + } + + public static IntPtr GetObjectPtr(int jsEnvIdx, Type type, object obj) + { + var jsEnv = JsEnv.jsEnvs[jsEnvIdx]; + return new IntPtr(type.IsValueType() ? jsEnv.objectPool.AddBoxedValueType(obj) : jsEnv.objectPool.FindOrAddObject(obj)); + } + + public static object GetSelf(int jsEnvIdx, IntPtr self) + { + return JsEnv.jsEnvs[jsEnvIdx].objectPool.Get(self.ToInt32()); + } + } +} \ No newline at end of file diff --git a/Assets/Puerts/Typing/puerts/index.d.ts b/Assets/Puerts/Typing/puerts/index.d.ts new file mode 100644 index 00000000..53906839 --- /dev/null +++ b/Assets/Puerts/Typing/puerts/index.d.ts @@ -0,0 +1,15 @@ +declare module "puerts" { + import {$Ref, $Task} from "csharp" + + function $ref(x? : T) : $Ref; + + function $unref(x: $Ref) : T; + + function $set(x: $Ref, val:T) : void; + + function $promise(x: $Task) : Promise + + function $generic any> (genericType :T, ...genericArguments: (new (...args:any[]) => any)[]) : T +} + +declare function require(name: string): any; \ No newline at end of file diff --git a/TsProj/.vscode/launch.json b/TsProj/.vscode/launch.json new file mode 100644 index 00000000..e0ffc2f0 --- /dev/null +++ b/TsProj/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Launch", + "protocol": "inspector", + "port": 8080 + } + ] +} \ No newline at end of file diff --git a/TsProj/QuickStart.ts b/TsProj/QuickStart.ts new file mode 100644 index 00000000..21837f4c --- /dev/null +++ b/TsProj/QuickStart.ts @@ -0,0 +1,73 @@ +//部署:npm run build + +import {UnityEngine, PuertsTest, System} from 'csharp' +import {$ref, $unref, $generic, $promise} from 'puerts' + +//静态函数 +UnityEngine.Debug.Log('hello world'); + +//对象构造 +let obj = new PuertsTest.DerivedClass(); + +//实例成员访问 +obj.BMFunc();//父类方法 +obj.DMFunc(PuertsTest.MyEnum.E1);//子类方法 +console.log(obj.BMF, obj.DMF); +obj.BMF = 10;//父类属性 +obj.DMF = 30;//子类属性 +console.log(obj.BMF, obj.DMF); + +//静态成员 +console.log(PuertsTest.BaseClass.BSF, PuertsTest.DerivedClass.DSF, PuertsTest.DerivedClass.BSF); + +//委托,事件 +//如果你后续不需要-=,可以像这样直接传函数当delegate +obj.MyCallback = msg => console.log("do not need remove, msg=" + msg); +//通过new构建的delegate,后续可以拿这个引用去-= +let delegate = new PuertsTest.MyCallback(msg => console.log('can be removed, msg=' + msg)); +//由于ts不支持操作符重载,Delegate.Combine相当于C#里头的obj.myCallback += delegate; +obj.MyCallback = System.Delegate.Combine(obj.MyCallback, delegate) as PuertsTest.MyCallback; +obj.Trigger(); +//Delegate.Remove相当于C#里头的obj.myCallback += delegate; +obj.MyCallback = System.Delegate.Remove(obj.MyCallback, delegate) as PuertsTest.MyCallback; +obj.Trigger(); +//事件 +obj.add_MyEvent(delegate); +obj.Trigger(); +obj.remove_MyEvent(delegate); +obj.Trigger(); +//静态事件 +PuertsTest.DerivedClass.add_MyStaticEvent(delegate); +obj.Trigger(); +PuertsTest.DerivedClass.remove_MyStaticEvent(delegate); +obj.Trigger(); + +//可变参数 +obj.ParamsFunc(1024, 'haha', 'hehe', 'heihei'); + +//in out 参数 +let p1 = $ref(); +let p2 = $ref(10); +let ret = obj.InOutArgFunc(100, p1, p2); +console.log('ret=' + ret + ', out=' + $unref(p1) + ', ref='+ $unref(p2)); + +//泛型 +//先通过$generic实例化泛型参数 +let List = $generic(System.Collections.Generic.List$1, System.Int32); +let lst = new List(); +lst.Add(1); +lst.Add(0); +lst.Add(2); +lst.Add(4); +obj.PrintList(lst); + +//typescript和c#的async,await联动,为了不在低版本的Unity下报错,先注释,c#7.3以上版本可以打开这些注释 +/*async function asyncCall() { + let task = obj.GetFileLength("Assets/Examples/05_Typescript/TsQuickStart.cs"); + let result = await $promise(task); + console.log('file length is ' + result); + let task2 = obj.GetFileLength("notexistedfile");//这个会抛文件找不到异常,被catch + let result2 = await $promise(task2); + console.log('file length is ,' + result2); +} +asyncCall().catch(e => console.error("catch:" + e));*/ diff --git a/TsProj/copyJsFile.js b/TsProj/copyJsFile.js new file mode 100644 index 00000000..d6d30216 --- /dev/null +++ b/TsProj/copyJsFile.js @@ -0,0 +1,43 @@ +var fs = require('fs'); +var path = require('path'); + +function copyFileSync( source, target ) { + + var targetFile = target; + + //if target is a directory a new file with the same name will be created + if ( fs.existsSync( target ) ) { + if ( fs.lstatSync( target ).isDirectory() ) { + targetFile = path.join( target, path.basename( source ) + '.txt' ); + } + } + + fs.writeFileSync(targetFile, fs.readFileSync(source)); +} + +function copyFolderRecursiveSync( source, targetFolder ) { + var files = []; + + if ( !fs.existsSync( targetFolder ) ) { + fs.mkdirSync( targetFolder ); + } + + //copy + if ( fs.lstatSync( source ).isDirectory() ) { + files = fs.readdirSync( source ); + files.forEach( function ( file ) { + var curSource = path.join( source, file ); + if ( fs.lstatSync( curSource ).isDirectory() ) { + copyFolderRecursiveSync( curSource, targetFolder ); + } else { + copyFileSync( curSource, targetFolder ); + } + } ); + } +} + +if (process.argv.length == 4) { + copyFolderRecursiveSync(process.argv[2], process.argv[3]); +} else { + console.error('invalid arguments'); +} \ No newline at end of file diff --git a/TsProj/package.json b/TsProj/package.json new file mode 100644 index 00000000..6a821c74 --- /dev/null +++ b/TsProj/package.json @@ -0,0 +1,9 @@ +{ + "name": "tsproj", + "version": "1.0.0", + "description": "ts project", + "scripts": { + "build": "tsc -p tsconfig.json", + "postbuild": "node copyJsFile.js output ../UnityProj/Assets/Examples/05_Typescript/Resources" + } +} diff --git a/TsProj/tsconfig.json b/TsProj/tsconfig.json new file mode 100644 index 00000000..f1fa1dc5 --- /dev/null +++ b/TsProj/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "jsx": "react", + "sourceMap": true, + "typeRoots": [ + "../UnityProj/Assets/Puerts/Typing", + "../UnityProj/Assets/Gen/Typing", + "./node_modules/@types", + ], + "outDir": "output", + } +} \ No newline at end of file