diff --git a/unity/Assets/Duktape/Editor/AbstractBindingProcess.cs b/unity/Assets/Duktape/Editor/AbstractBindingProcess.cs index 9ab3abc..26647ca 100644 --- a/unity/Assets/Duktape/Editor/AbstractBindingProcess.cs +++ b/unity/Assets/Duktape/Editor/AbstractBindingProcess.cs @@ -1,62 +1,62 @@ -using System; -using System.IO; -using System.Collections.Generic; -using System.Reflection; - -namespace Duktape -{ - using UnityEngine; - using UnityEditor; - - public abstract class AbstractBindingProcess : IBindingProcess - { - public virtual void OnInitialize(BindingManager bindingManager) - { - } - - public virtual void OnPreCollectAssemblies(BindingManager bindingManager) - { - } - - public virtual void OnPostCollectAssemblies(BindingManager bindingManager) - { - } - - public virtual void OnPostExporting(BindingManager bindingManager) - { - } - - public virtual void OnPreCollectTypes(BindingManager bindingManager) - { - } - - public virtual void OnPostCollectTypes(BindingManager bindingManager) - { - } - - public virtual bool OnExportingType(BindingManager bindingManager, Type type) - { - return false; - } - - public virtual void OnPreGenerateType(BindingManager bindingManager, TypeBindingInfo bindingInfo) - { - } - - public virtual void OnPostGenerateType(BindingManager bindingManager, TypeBindingInfo bindingInfo) - { - } - - public virtual void OnPreGenerateDelegate(BindingManager bindingManager, DelegateBindingInfo bindingInfo) - { - } - - public virtual void OnPostGenerateDelegate(BindingManager bindingManager, DelegateBindingInfo bindingInfo) - { - } - - public virtual void OnCleanup(BindingManager bindingManager) - { - } - } -} +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; + +namespace Duktape +{ + using UnityEngine; + using UnityEditor; + + public abstract class AbstractBindingProcess : IBindingProcess + { + public virtual void OnInitialize(BindingManager bindingManager) + { + } + + public virtual void OnPreCollectAssemblies(BindingManager bindingManager) + { + } + + public virtual void OnPostCollectAssemblies(BindingManager bindingManager) + { + } + + public virtual void OnPostExporting(BindingManager bindingManager) + { + } + + public virtual void OnPreCollectTypes(BindingManager bindingManager) + { + } + + public virtual void OnPostCollectTypes(BindingManager bindingManager) + { + } + + public virtual bool OnExportingType(BindingManager bindingManager, Type type) + { + return false; + } + + public virtual void OnPreGenerateType(BindingManager bindingManager, TypeBindingInfo bindingInfo) + { + } + + public virtual void OnPostGenerateType(BindingManager bindingManager, TypeBindingInfo bindingInfo) + { + } + + public virtual void OnPreGenerateDelegate(BindingManager bindingManager, DelegateBindingInfo bindingInfo) + { + } + + public virtual void OnPostGenerateDelegate(BindingManager bindingManager, DelegateBindingInfo bindingInfo) + { + } + + public virtual void OnCleanup(BindingManager bindingManager) + { + } + } +} diff --git a/unity/Assets/Duktape/Editor/BindingManager.cs b/unity/Assets/Duktape/Editor/BindingManager.cs index a44bf17..3ac5c11 100644 --- a/unity/Assets/Duktape/Editor/BindingManager.cs +++ b/unity/Assets/Duktape/Editor/BindingManager.cs @@ -1,1265 +1,1269 @@ -using System; -using System.IO; -using System.Collections.Generic; -using System.Reflection; -using System.Text.RegularExpressions; - -namespace Duktape -{ - using UnityEngine; - using UnityEditor; - - public partial class BindingManager - { - public DateTime dateTime; - public TextGenerator log; - public Prefs prefs; - - private List _implicitAssemblies = new List(); // 默认导出所有类型 - private List _explicitAssemblies = new List(); // 仅导出指定需要导出的类型 - - private HashSet blacklist; - private HashSet whitelist; - private List typePrefixBlacklist; - private Dictionary exportedTypes = new Dictionary(); - private Dictionary exportedDelegates = new Dictionary(); - private Dictionary redirectDelegates = new Dictionary(); - // 类型修改 - private Dictionary typesTarnsform = new Dictionary(); - private List outputFiles = new List(); - private List removedFiles = new List(); - - private Dictionary> _tsTypeNameMap = new Dictionary>(); - private Dictionary _csTypeNameMap = new Dictionary(); - private Dictionary _csTypeNameMapS = new Dictionary(); - private static HashSet _tsKeywords = new HashSet(); - - // 自定义的处理流程 - private List _bindingProcess = new List(); - - static BindingManager() - { - AddTSKeywords( - "function", - "interface", - "class", - "let", - "break", - "as", - "any", - "switch", - "case", - "if", - "throw", - "else", - "var", - "number", - "string", - "get", - "module", - // "type", - "instanceof", - "typeof", - "public", - "private", - "enum", - "export", - "finally", - "for", - "while", - "void", - "null", - "super", - "this", - "new", - "in", - "extends", - "static", - "package", - "implements", - "interface", - "continue", - "yield", - "const" - ); - } - - public BindingManager(Prefs prefs) - { - this.prefs = prefs; - this.dateTime = DateTime.Now; - var tab = prefs.tab; - var newline = prefs.newline; - typePrefixBlacklist = prefs.typePrefixBlacklist; - log = new TextGenerator(newline, tab); - blacklist = new HashSet(new Type[] - { - typeof(AOT.MonoPInvokeCallbackAttribute), - }); - whitelist = new HashSet(new Type[] - { - }); - - TransformType(typeof(GameObject)) - // .AddRedirectMethod("AddComponent", "_AddComponent") - // .AddRedirectMethod("GetComponent", "_GetComponent") - // .AddRedirectMethod("GetComponentInChildren", "_GetComponentInChildren") - // .AddRedirectMethod("GetComponentInParent", "_GetComponentInParent") - // .AddRedirectMethod("GetComponents", "_GetComponents") - // .AddRedirectMethod("GetComponentsInChildren", "_GetComponentsInChildren") - // .AddRedirectMethod("GetComponentsInParent", "_GetComponentsInParent") - .AddTSMethodDeclaration("AddComponent(type: { new(): T }): T", - "AddComponent", typeof(Type)) - .AddTSMethodDeclaration("GetComponent(type: { new(): T }): T", - "GetComponent", typeof(Type)) - .AddTSMethodDeclaration("GetComponentInChildren(type: { new(): T }, includeInactive: boolean): T", - "GetComponentInChildren", typeof(Type), typeof(bool)) - .AddTSMethodDeclaration("GetComponentInChildren(type: { new(): T }): T", - "GetComponentInChildren", typeof(Type)) - .AddTSMethodDeclaration("GetComponentInParent(type: { new(): T }): T", - "GetComponentInParent", typeof(Type)) - // .AddTSMethodDeclaration("GetComponents(type: { new(): T }, results: any): void", - // "GetComponents", typeof(Type)) - .AddTSMethodDeclaration("GetComponents(type: { new(): T }): T[]", - "GetComponents", typeof(Type)) - .AddTSMethodDeclaration("GetComponentsInChildren(type: { new(): T }, includeInactive: boolean): T[]", - "GetComponentsInChildren", typeof(Type), typeof(bool)) - .AddTSMethodDeclaration("GetComponentsInChildren(type: { new(): T }): T[]", - "GetComponentsInChildren", typeof(Type)) - .AddTSMethodDeclaration("GetComponentsInParent(type: { new(): T }, includeInactive: boolean): T[]", - "GetComponentsInParent", typeof(Type), typeof(bool)) - .AddTSMethodDeclaration("GetComponentsInParent(type: { new(): T }): T[]", - "GetComponentsInParent", typeof(Type)) - ; - - // fix d.ts, some C# classes use explicit implemented interface method - SetTypeBlocked(typeof(UnityEngine.ILogHandler)); - SetTypeBlocked(typeof(UnityEngine.ISerializationCallbackReceiver)); - - TransformType(typeof(object)) - .RenameTSMethod("$Equals", "Equals", typeof(object)) - .RenameTSMethod("$Equals", "Equals", typeof(object), typeof(object)) - ; - - TransformType(typeof(Vector3)) - .SetMethodBlocked("SqrMagnitude", typeof(Vector3)) - .SetMethodBlocked("Magnitude", typeof(Vector3)) - .AddTSMethodDeclaration("static Add(a: Vector3, b: Vector3): Vector3") - .AddTSMethodDeclaration("static Sub(a: Vector3, b: Vector3): Vector3") - .AddTSMethodDeclaration("static Mul(a: Vector3, b: Vector3): Vector3") - .AddTSMethodDeclaration("static Div(a: Vector3, b: Vector3): Vector3") - .AddTSMethodDeclaration("static Equals(a: Vector3, b: Vector3): boolean") - .AddTSMethodDeclaration("Equals(b: Vector3): boolean") - .AddTSMethodDeclaration("Inverse(): Vector3") - .AddTSMethodDeclaration("Clone(): Vector3") - ; - - TransformType(typeof(Vector2)) - .SetMethodBlocked("SqrMagnitude") - .SetMethodBlocked("SqrMagnitude", typeof(Vector2)) - .AddTSMethodDeclaration("static Add(a: Vector2, b: Vector2): Vector2") - .AddTSMethodDeclaration("static Sub(a: Vector2, b: Vector2): Vector2") - .AddTSMethodDeclaration("static Mul(a: Vector2, b: Vector2): Vector2") - .AddTSMethodDeclaration("static Div(a: Vector2, b: Vector2): Vector2") - .AddTSMethodDeclaration("static Equals(a: Vector2, b: Vector2): boolean") - .AddTSMethodDeclaration("Equals(b: Vector2): boolean") - .AddTSMethodDeclaration("Inverse(): Vector2") - .AddTSMethodDeclaration("Clone(): Vector2") - ; - - TransformType(typeof(Quaternion)) - .AddTSMethodDeclaration("Clone(): Quaternion") - ; - // SetTypeBlocked(typeof(RendererExtensions)); - SetTypeBlocked(typeof(UnityEngine.UI.ILayoutGroup)); - SetTypeBlocked(typeof(UnityEngine.UI.ILayoutSelfController)); - TransformType(typeof(UnityEngine.UI.PositionAsUV1)) - .SetMemberBlocked("ModifyMesh"); - TransformType(typeof(UnityEngine.UI.Shadow)) - .SetMemberBlocked("ModifyMesh"); - TransformType(typeof(UnityEngine.UI.Outline)) - .SetMemberBlocked("ModifyMesh"); - - // editor 使用的 .net 与 player 所用存在差异, 这里屏蔽不存在的成员 - TransformType(typeof(double)) - .SetMemberBlocked("IsFinite") - ; - TransformType(typeof(float)) - .SetMemberBlocked("IsFinite") - ; - TransformType(typeof(string)) - .SetMemberBlocked("Chars") - ; - - AddTSTypeNameMap(typeof(sbyte), "number"); - AddTSTypeNameMap(typeof(byte), "number"); - AddTSTypeNameMap(typeof(int), "number"); - AddTSTypeNameMap(typeof(uint), "number"); - AddTSTypeNameMap(typeof(short), "number"); - AddTSTypeNameMap(typeof(ushort), "number"); - AddTSTypeNameMap(typeof(long), "number"); - AddTSTypeNameMap(typeof(ulong), "number"); - AddTSTypeNameMap(typeof(float), "number"); - AddTSTypeNameMap(typeof(double), "number"); - AddTSTypeNameMap(typeof(bool), "boolean"); - AddTSTypeNameMap(typeof(string), "string"); - AddTSTypeNameMap(typeof(char), "string"); - AddTSTypeNameMap(typeof(void), "void"); - AddTSTypeNameMap(typeof(LayerMask), "UnityEngine.LayerMask", "number"); - AddTSTypeNameMap(typeof(Color), "UnityEngine.Color"); - AddTSTypeNameMap(typeof(Color32), "UnityEngine.Color32"); - AddTSTypeNameMap(typeof(Vector2), "UnityEngine.Vector2"); - AddTSTypeNameMap(typeof(Vector2Int), "UnityEngine.Vector2Int"); - AddTSTypeNameMap(typeof(Vector3), "UnityEngine.Vector3"); // 已优化, 在 VM 初始化时替换为 DuktapeJS.Vector3 - AddTSTypeNameMap(typeof(Vector3Int), "UnityEngine.Vector3Int"); - AddTSTypeNameMap(typeof(Vector4), "UnityEngine.Vector4"); - AddTSTypeNameMap(typeof(Quaternion), "UnityEngine.Quaternion"); - AddTSTypeNameMap(typeof(DuktapeArray), "any[]"); - - AddCSTypeNameMap(typeof(sbyte), "sbyte"); - AddCSTypeNameMap(typeof(byte), "byte"); - AddCSTypeNameMap(typeof(int), "int"); - AddCSTypeNameMap(typeof(uint), "uint"); - AddCSTypeNameMap(typeof(short), "short"); - AddCSTypeNameMap(typeof(ushort), "ushort"); - AddCSTypeNameMap(typeof(long), "long"); - AddCSTypeNameMap(typeof(ulong), "ulong"); - AddCSTypeNameMap(typeof(float), "float"); - AddCSTypeNameMap(typeof(double), "double"); - AddCSTypeNameMap(typeof(bool), "bool"); - AddCSTypeNameMap(typeof(string), "string"); - AddCSTypeNameMap(typeof(char), "char"); - AddCSTypeNameMap(typeof(System.Object), "object"); - AddCSTypeNameMap(typeof(void), "void"); - - Initialize(); - } - - public void SetTypeBlocked(Type type) - { - blacklist.Add(type); - } - - public bool GetTSMethodDeclaration(MethodBase method, out string code) - { - var transform = GetTypeTransform(method.DeclaringType); - if (transform != null) - { - return transform.GetTSMethodDeclaration(method, out code); - } - code = null; - return false; - } - - public bool GetTSMethodRename(MethodBase method, out string code) - { - var transform = GetTypeTransform(method.DeclaringType); - if (transform != null) - { - return transform.GetTSMethodRename(method, out code); - } - code = null; - return false; - } - - public TypeTransform GetTypeTransform(Type type) - { - TypeTransform transform; - return typesTarnsform.TryGetValue(type, out transform) ? transform : null; - } - - public TypeTransform TransformType(Type type) - { - TypeTransform transform; - if (!typesTarnsform.TryGetValue(type, out transform)) - { - typesTarnsform[type] = transform = new TypeTransform(type); - } - return transform; - } - - private static bool _FindFilterBindingProcess(Type type, object l) - { - return type == typeof(IBindingProcess); - } - - private void Initialize() - { - var assembly = Assembly.Load("Assembly-CSharp-Editor"); - var types = assembly.GetExportedTypes(); - for (int i = 0, size = types.Length; i < size; i++) - { - var type = types[i]; - if (type.IsAbstract) - { - continue; - } - try - { - var interfaces = type.FindInterfaces(_FindFilterBindingProcess, null); - if (interfaces != null && interfaces.Length > 0) - { - var ctor = type.GetConstructor(Type.EmptyTypes); - var inst = ctor.Invoke(null) as IBindingProcess; - inst.OnInitialize(this); - _bindingProcess.Add(inst); - Debug.Log($"add binding process: {type}"); - // _bindingProcess.Add - } - } - catch (Exception exception) - { - Debug.LogWarning($"failed to add binding process: {type}\n{exception}"); - } - } - } - - // TS: 添加保留字, CS中相关变量名等会自动重命名注册到js中 - public static void AddTSKeywords(params string[] keywords) - { - foreach (var keyword in keywords) - { - _tsKeywords.Add(keyword); - } - } - - // 指定类型在 ts 声明中的映射名 (可以指定多项) - public void AddTSTypeNameMap(Type type, params string[] names) - { - List list; - if (!_tsTypeNameMap.TryGetValue(type, out list)) - { - _tsTypeNameMap[type] = list = new List(); - } - list.AddRange(names); - } - - // CS, 添加类型名称映射, 用于简化导出时的常用类型名 - public void AddCSTypeNameMap(Type type, string name) - { - _csTypeNameMap[type] = name; - _csTypeNameMapS[type.FullName] = name; - _csTypeNameMapS[GetCSNamespace(type) + type.Name] = name; - } - - // 增加导出类型 (需要在 Collect 阶段进行) - //NOTE: editor mscorlib 与 runtime 存在差异, 需要手工 block 差异 - public TypeTransform AddExportedType(Type type) - { - if (type.IsGenericTypeDefinition) - { - whitelist.Add(type); - return null; - } - if (!exportedTypes.ContainsKey(type)) - { - var typeBindingInfo = new TypeBindingInfo(this, type); - exportedTypes.Add(type, typeBindingInfo); - log.AppendLine($"AddExportedType: {type} Assembly: {type.Assembly}"); - - // 检查具体化泛型基类 (如果基类泛型定义在显式导出清单中, 那么导出此具体化类) - var baseType = type.BaseType; - if (baseType != null && baseType.IsConstructedGenericType) - { - if (!IsExportingBlocked(baseType) && IsExportingExplicit(baseType.GetGenericTypeDefinition())) - { - AddExportedType(baseType); - } - } - } - return TransformType(type); - } - - public bool RemoveExportedType(Type type) - { - return exportedTypes.Remove(type); - } - - public DelegateBindingInfo GetDelegateBindingInfo(Type type) - { - Type target; - if (redirectDelegates.TryGetValue(type, out target)) - { - type = target; - } - DelegateBindingInfo delegateBindingInfo; - if (exportedDelegates.TryGetValue(type, out delegateBindingInfo)) - { - return delegateBindingInfo; - } - return null; - } - - public static bool ContainsPointer(MethodBase method) - { - var parameters = method.GetParameters(); - for (int i = 0, size = parameters.Length; i < size; i++) - { - var parameterType = parameters[i].ParameterType; - if (parameterType.IsPointer) - { - return true; - } - } - return false; - } - - public static bool IsGenericMethod(MethodBase method) - { - return method.GetGenericArguments().Length > 0; - } - - public static bool IsUnsupported(MethodBase method) - { - return ContainsPointer(method) || IsGenericMethod(method); - } - - // 收集所有 delegate 类型 - public void CollectDelegate(Type type) - { - if (type == null || type.BaseType != typeof(MulticastDelegate)) - { - return; - } - if (!exportedDelegates.ContainsKey(type)) - { - var invoke = type.GetMethod("Invoke"); - var returnType = invoke.ReturnType; - var parameters = invoke.GetParameters(); - if (ContainsPointer(invoke)) - { - log.AppendLine("skip unsafe (pointer) delegate: [{0}] {1}", type, invoke); - return; - } - // 是否存在等价 delegate - foreach (var kv in exportedDelegates) - { - if (kv.Value.Equals(returnType, parameters)) - { - log.AppendLine("skip delegate: {0} && {1}", kv.Value, type); - kv.Value.types.Add(type); - redirectDelegates[type] = kv.Key; - return; - } - } - var delegateBindingInfo = new DelegateBindingInfo(returnType, parameters); - delegateBindingInfo.types.Add(type); - exportedDelegates.Add(type, delegateBindingInfo); - log.AppendLine("add delegate: {0}", type); - for (var i = 0; i < parameters.Length; i++) - { - CollectDelegate(parameters[i].ParameterType); - } - } - } - - public bool IsExported(Type type) - { - return exportedTypes.ContainsKey(type); - } - - public string GetTSRefWrap(string name) - { - return $"DuktapeJS.Ref<{name}>"; - } - - public string GetTSTypeFullName(ParameterInfo parameter) - { - var parameterType = parameter.ParameterType; - return GetTSTypeFullName(parameterType, parameter.IsOut); - } - - // 获取 type 在 typescript 中对应类型名 - public string GetTSTypeFullName(Type type) - { - return GetTSTypeFullName(type, false); - } - - public string GetTSTypeFullName(Type type, bool isOut) - { - if (type == null || type == typeof(void)) - { - return "void"; - } - if (type.IsByRef) - { - if (isOut) - { - return $"DuktapeJS.Out<{GetTSTypeFullName(type.GetElementType())}>"; - } - return $"DuktapeJS.Ref<{GetTSTypeFullName(type.GetElementType())}>"; - } - List names; - if (_tsTypeNameMap.TryGetValue(type, out names)) - { - return names.Count > 1 ? $"({String.Join(" | ", names)})" : names[0]; - } - if (type.IsArray) - { - if (type.GetElementType() == typeof(byte)) - { - return "Buffer"; - } - var elementType = type.GetElementType(); - return GetTSTypeFullName(elementType) + "[]"; - } - var info = GetExportedType(type); - if (info != null) - { - return info.jsFullName; - } - if (type.BaseType == typeof(MulticastDelegate)) - { - var delegateBindingInfo = GetDelegateBindingInfo(type); - if (delegateBindingInfo != null) - { - var nargs = delegateBindingInfo.parameters.Length; - var ret = GetTSTypeFullName(delegateBindingInfo.returnType); - var t_arglist = (nargs > 0 ? ", " : "") + GetTSArglistTypes(delegateBindingInfo.parameters, false); - var v_arglist = GetTSArglistTypes(delegateBindingInfo.parameters, true); - return $"DuktapeJS.Delegate{nargs}<{ret}{t_arglist}> | (({v_arglist}) => {ret})"; - } - } - return "any"; - } - - public string GetCSNamespace(Type type) - { - return GetCSNamespace(type.Namespace); - } - - public string GetCSNamespace(string ns) - { - return string.IsNullOrEmpty(ns) ? "" : (ns + "."); - } - - // 生成参数对应的字符串形式参数列表定义 (typescript) - public string GetTSArglistTypes(ParameterInfo[] parameters, bool withVarName) - { - var size = parameters.Length; - var arglist = ""; - if (size == 0) - { - return arglist; - } - for (var i = 0; i < size; i++) - { - var parameter = parameters[i]; - var typename = GetTSTypeFullName(parameter.ParameterType); - // if (parameter.IsOut && parameter.ParameterType.IsByRef) - // { - // arglist += "out "; - // } - // else if (parameter.ParameterType.IsByRef) - // { - // arglist += "ref "; - // } - if (withVarName) - { - arglist += GetTSVariable(parameter.Name) + ": "; - } - arglist += typename; - // arglist += " "; - // arglist += parameter.Name; - if (i != size - 1) - { - arglist += ", "; - } - } - return arglist; - } - - public string GetDuktapeGenericError(string err) - { - return $"DuktapeDLL.duk_generic_error(ctx, \"{err}\");"; - } - - public string GetDuktapeGetter(Type type) - { - if (type.IsByRef) - { - return GetDuktapeGetter(type.GetElementType()); - } - if (type.IsArray) - { - var elementType = type.GetElementType(); - return GetDuktapeGetter(elementType) + "_array"; //TODO: 嵌套数组的问题 - } - if (type.IsValueType) - { - if (type.IsPrimitive) - { - return "duk_get_primitive"; - } - if (type.IsEnum) - { - return "duk_get_enumvalue"; - } - return "duk_get_structvalue"; - } - if (type == typeof(string)) - { - return "duk_get_primitive"; - } - if (type.BaseType == typeof(MulticastDelegate)) - { - return "duk_get_delegate"; - } - if (type == typeof(Type)) - { - return "duk_get_type"; - } - return "duk_get_classvalue"; - } - - public string GetDuktapePusher(Type type) - { - if (type.BaseType == typeof(MulticastDelegate)) - { - return "duk_push_delegate"; - } - if (type.IsValueType) - { - if (type.IsPrimitive) - { - return "duk_push_primitive"; - } - if (type.IsEnum) - { - return "duk_push_enumvalue"; - } - return "duk_push_structvalue"; - } - if (type == typeof(string)) - { - return "duk_push_primitive"; - } - return "duk_push_classvalue"; - } - - public static string GetTSVariable(string name) - { - if (_tsKeywords.Contains(name)) - { - return name + "_"; - } - return name; - } - - // 保证生成一个以 prefix 为前缀, 与参数列表中所有参数名不同的名字 - public string GetUniqueName(ParameterInfo[] parameters, string prefix) - { - return GetUniqueName(parameters, prefix, 0); - } - - public string GetUniqueName(ParameterInfo[] parameters, string prefix, int index) - { - var size = parameters.Length; - var name = prefix + index; - for (var i = 0; i < size; i++) - { - var parameter = parameters[i]; - if (parameter.Name == prefix) - { - return GetUniqueName(parameters, prefix, index + 1); - } - } - return name; - } - - // 获取父类的ts声明 (沿继承链上溯直到存在导出) - public string GetTSSuperName(TypeBindingInfo typeBindingInfo) - { - var super = typeBindingInfo.super; - while (super != null) - { - var superBindingInfo = GetExportedType(super); - if (superBindingInfo != null) - { - return GetTSTypeFullName(superBindingInfo.type); - } - super = super.BaseType; - } - return ""; - } - - // 获取实现的接口的ts声明 - public string GetTSInterfacesName(TypeBindingInfo typeBindingInfo) - { - var interfaces = typeBindingInfo.type.GetInterfaces(); - var str = ""; - foreach (var @interface in interfaces) - { - var interfaceBindingInfo = GetExportedType(@interface); - if (interfaceBindingInfo != null) - { - // Debug.Log($"{typeBindingInfo.type.Name} implements {@interface.Name}"); - str += GetTSTypeFullName(interfaceBindingInfo.type) + ", "; - } - } - if (str.Length > 0) - { - str = str.Substring(0, str.Length - 2); - } - return str; - } - - // 生成参数对应的字符串形式参数列表 (csharp) - public string GetCSArglistDecl(ParameterInfo[] parameters) - { - var size = parameters.Length; - var arglist = ""; - if (size == 0) - { - return arglist; - } - for (var i = 0; i < size; i++) - { - var parameter = parameters[i]; - var typename = GetCSTypeFullName(parameter.ParameterType); - if (parameter.IsOut && parameter.ParameterType.IsByRef) - { - arglist += "out "; - } - else if (parameter.ParameterType.IsByRef) - { - arglist += "ref "; - } - arglist += typename; - arglist += " "; - arglist += parameter.Name; - if (i != size - 1) - { - arglist += ", "; - } - } - return arglist; - } - - // 获取 type 在 绑定代码 中对应类型名 - public string GetCSTypeFullName(Type type) - { - return GetCSTypeFullName(type, true); - } - - public string GetCSTypeFullName(Type type, bool shortName) - { - // Debug.LogFormat("{0} Array {1} ByRef {2} GetElementType {3}", type, type.IsArray, type.IsByRef, type.GetElementType()); - if (type.IsGenericType) - { - var @namespace = string.Empty; - var classname = type.Name.Substring(0, type.Name.Length - 2); - if (type.IsNested) - { - var indexOf = type.FullName.IndexOf("+"); - @namespace = type.FullName.Substring(0, indexOf) + "."; - } - else - { - @namespace = GetCSNamespace(type); - } - var purename = @namespace + classname; - var gargs = type.GetGenericArguments(); - purename += "<"; - for (var i = 0; i < gargs.Length; i++) - { - var garg = gargs[i]; - purename += GetCSTypeFullName(garg, shortName); - if (i != gargs.Length - 1) - { - purename += ", "; - } - } - purename += ">"; - return purename; - } - if (type.IsArray) - { - return GetCSTypeFullName(type.GetElementType(), shortName) + "[]"; - } - if (type.IsByRef) - { - return GetCSTypeFullName(type.GetElementType(), shortName); - } - string name; - if (shortName) - { - if (_csTypeNameMap.TryGetValue(type, out name)) - { - return name; - } - } - var fullname = type.FullName.Replace('+', '.'); - if (fullname.Contains("`")) - { - fullname = new Regex(@"`\d", RegexOptions.None).Replace(fullname, ""); - fullname = fullname.Replace("[", "<"); - fullname = fullname.Replace("]", ">"); - } - if (_csTypeNameMapS.TryGetValue(fullname, out name)) - { - return name; - } - return fullname; - } - - public TypeBindingInfo GetExportedType(Type type) - { - if (type == null) - { - return null; - } - TypeBindingInfo typeBindingInfo; - return exportedTypes.TryGetValue(type, out typeBindingInfo) ? typeBindingInfo : null; - } - - // 是否在黑名单中屏蔽, 或者已知无需导出的类型 - public bool IsExportingBlocked(Type type) - { - if (blacklist.Contains(type)) - { - return true; - } - if (type.IsGenericType && !type.IsConstructedGenericType) - { - return true; - } - if (type.Name.Contains("<")) - { - return true; - } - if (type.IsDefined(typeof(JSBindingAttribute), false)) - { - return true; - } - if (type.BaseType == typeof(Attribute)) - { - return true; - } - if (type.BaseType == typeof(MulticastDelegate)) - { - return true; - } - if (type.IsPointer) - { - return true; - } - var encloser = type; - while (encloser != null) - { - if (encloser.IsDefined(typeof(ObsoleteAttribute), false)) - { - return true; - } - encloser = encloser.DeclaringType; - } - for (int i = 0, size = typePrefixBlacklist.Count; i < size; i++) - { - if (type.FullName.StartsWith(typePrefixBlacklist[i])) - { - return true; - } - } - return false; - } - - // 是否显式要求导出 - public bool IsExportingExplicit(Type type) - { - if (whitelist.Contains(type)) - { - return true; - } - if (type.IsDefined(typeof(JSTypeAttribute), false)) - { - return true; - } - return false; - } - - private void OnPreCollectAssemblies() - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPreCollectAssemblies(this); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPreCollect]: {exception}"); - } - } - } - - private void OnPostCollectAssemblies() - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPostCollectAssemblies(this); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPostCollect]: {exception}"); - } - } - } - - private void OnPostExporting() - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPostExporting(this); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPostExporting]: {exception}"); - } - } - } - - private void OnPreCollectTypes() - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPreCollectTypes(this); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPreCollect]: {exception}"); - } - } - } - - private void OnPostCollectTypes() - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPostCollectTypes(this); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPostCollect]: {exception}"); - } - } - } - - private void OnPreGenerateType(TypeBindingInfo bindingInfo) - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPreGenerateType(this, bindingInfo); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPreGenerateType]: {exception}"); - } - } - } - - private void OnPostGenerateType(TypeBindingInfo bindingInfo) - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPostGenerateType(this, bindingInfo); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPostGenerateType]: {exception}"); - } - } - } - - public void OnPreGenerateDelegate(DelegateBindingInfo bindingInfo) - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPreGenerateDelegate(this, bindingInfo); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPreGenerateDelegate]: {exception}"); - } - } - } - - public void OnPostGenerateDelegate(DelegateBindingInfo bindingInfo) - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnPostGenerateDelegate(this, bindingInfo); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnPostGenerateDelegate]: {exception}"); - } - } - } - - private void OnCleanup() - { - for (int i = 0, size = _bindingProcess.Count; i < size; i++) - { - var bp = _bindingProcess[i]; - try - { - bp.OnCleanup(this); - } - catch (Exception exception) - { - this.Error($"process failed [{bp}][OnCleanup]: {exception}"); - } - } - } - - - - public void Collect() - { - // 收集直接类型, 加入 exportedTypes - OnPreCollectAssemblies(); - AddAssemblies(false, prefs.explicitAssemblies.ToArray()); - AddAssemblies(true, prefs.implicitAssemblies.ToArray()); - OnPostCollectAssemblies(); - - ExportAssemblies(_explicitAssemblies, false); - ExportAssemblies(_implicitAssemblies, true); - ExportBuiltins(); - OnPostExporting(); - - log.AppendLine("collecting members"); - log.AddTabLevel(); - OnPreCollectTypes(); - foreach (var typeBindingInfoKV in exportedTypes) - { - var typeBindingInfo = typeBindingInfoKV.Value; - log.AppendLine("type: {0}", typeBindingInfo.type); - log.AddTabLevel(); - typeBindingInfo.Collect(); - log.DecTabLevel(); - } - OnPostCollectTypes(); - log.DecTabLevel(); - } - - public void AddAssemblies(bool implicitExport, params string[] assemblyNames) - { - if (implicitExport) - { - _implicitAssemblies.AddRange(assemblyNames); - } - else - { - _explicitAssemblies.AddRange(assemblyNames); - } - } - - public void RemoveAssemblies(params string[] assemblyNames) - { - foreach (var name in assemblyNames) - { - _implicitAssemblies.Remove(name); - _explicitAssemblies.Remove(name); - } - } - - // 导出一些必要的基本类型 (预实现的辅助功能需要用到, DuktapeJS) - private void ExportBuiltins() - { - AddExportedType(typeof(byte)); - AddExportedType(typeof(sbyte)); - AddExportedType(typeof(float)); - AddExportedType(typeof(double)); - AddExportedType(typeof(string)); - AddExportedType(typeof(int)); - AddExportedType(typeof(uint)); - AddExportedType(typeof(short)); - AddExportedType(typeof(ushort)); - AddExportedType(typeof(object)); - AddExportedType(typeof(Array)); - AddExportedType(typeof(Object)); - AddExportedType(typeof(Vector3)); - } - - // implicitExport: 默认进行导出(黑名单例外), 否则根据导出标记或手工添加 - private void ExportAssemblies(List assemblyNames, bool implicitExport) - { - foreach (var assemblyName in assemblyNames) - { - log.AppendLine("assembly: {0}", assemblyName); - log.AddTabLevel(); - try - { - var assembly = Assembly.Load(assemblyName); - var types = assembly.GetExportedTypes(); - - log.AppendLine("types {0}", types.Length); - foreach (var type in types) - { - if (IsExportingBlocked(type)) - { - log.AppendLine("blocked: {0}", type.FullName); - continue; - } - if (implicitExport || IsExportingExplicit(type)) - { - log.AppendLine("export: {0}", type.FullName); - this.AddExportedType(type); - continue; - } - log.AppendLine("skip: {0}", type.FullName); - } - } - catch (Exception exception) - { - log.AppendLine(exception.ToString()); - } - log.DecTabLevel(); - } - } - - // 清理多余文件 - public void Cleanup() - { - log.AppendLine("cleanup"); - log.AddTabLevel(); - Cleanup(prefs.outDir, outputFiles, file => - { - removedFiles.Add(file); - log.AppendLine("remove unused file {0}", file); - }); - OnCleanup(); - log.DecTabLevel(); - } - - public static void Cleanup(string outDir, List excludedFiles, Action ondelete) - { - foreach (var file in Directory.GetFiles(outDir)) - { - var nfile = file; - if (file.EndsWith(".meta")) - { - nfile = file.Substring(0, file.Length - 5); - } - // Debug.LogFormat("checking file {0}", nfile); - if (excludedFiles == null || !excludedFiles.Contains(nfile)) - { - File.Delete(file); - if (ondelete != null) - { - ondelete(file); - } - } - } - } - - public void AddOutputFile(string filename) - { - outputFiles.Add(filename); - } - - public void Generate() - { - var cg = new CodeGenerator(this); - var outDir = prefs.outDir; - var tx = prefs.extraExt; - // var tx = ""; - if (!Directory.Exists(outDir)) - { - Directory.CreateDirectory(outDir); - } - var cancel = false; - var current = 0; - var total = exportedTypes.Count; - foreach (var typeKV in exportedTypes) - { - var typeBindingInfo = typeKV.Value; - try - { - current++; - cancel = EditorUtility.DisplayCancelableProgressBar( - "Generating", - $"{current}/{total}: {typeBindingInfo.FullName}", - (float)current / total); - if (cancel) - { - Warn("operation canceled"); - break; - } - if (!typeBindingInfo.omit) - { - cg.Clear(); - OnPreGenerateType(typeBindingInfo); - cg.Generate(typeBindingInfo); - OnPostGenerateType(typeBindingInfo); - cg.WriteTo(outDir, typeBindingInfo.GetFileName(), tx); - } - } - catch (Exception exception) - { - Error($"generate failed {typeBindingInfo.type.FullName}: {exception.Message}"); - Debug.LogError(exception.StackTrace); - } - } - - if (!cancel) - { - try - { - var exportedDelegatesArray = new DelegateBindingInfo[this.exportedDelegates.Count]; - this.exportedDelegates.Values.CopyTo(exportedDelegatesArray, 0); - - cg.Clear(); - cg.Generate(exportedDelegatesArray); - // cg.tsSource.enabled = false; - cg.WriteTo(outDir, DuktapeVM._DuktapeDelegates, tx); - } - catch (Exception exception) - { - Error($"generate delegates failed: {exception.Message}"); - Debug.LogError(exception.StackTrace); - } - } - - var logPath = prefs.logPath; - File.WriteAllText(logPath, log.ToString()); - EditorUtility.ClearProgressBar(); - } - - public void Report() - { - var now = DateTime.Now; - var ts = now.Subtract(dateTime); - Debug.LogFormat("generated {0} type(s), {1} delegate(s), {2} deletion(s) in {3:0.##} seconds.", - exportedTypes.Count, - exportedDelegates.Count, - removedFiles.Count, - ts.TotalSeconds); - } - } +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace Duktape +{ + using UnityEngine; + using UnityEditor; + + public partial class BindingManager + { + public DateTime dateTime; + public TextGenerator log; + public Prefs prefs; + + private List _implicitAssemblies = new List(); // 默认导出所有类型 + private List _explicitAssemblies = new List(); // 仅导出指定需要导出的类型 + + private HashSet blacklist; + private HashSet whitelist; + private List typePrefixBlacklist; + private Dictionary exportedTypes = new Dictionary(); + private Dictionary exportedDelegates = new Dictionary(); + private Dictionary redirectDelegates = new Dictionary(); + // 类型修改 + private Dictionary typesTarnsform = new Dictionary(); + private List outputFiles = new List(); + private List removedFiles = new List(); + + private Dictionary> _tsTypeNameMap = new Dictionary>(); + private Dictionary _csTypeNameMap = new Dictionary(); + private Dictionary _csTypeNameMapS = new Dictionary(); + private static HashSet _tsKeywords = new HashSet(); + + // 自定义的处理流程 + private List _bindingProcess = new List(); + + static BindingManager() + { + AddTSKeywords( + "function", + "interface", + "class", + "let", + "break", + "as", + "any", + "switch", + "case", + "if", + "throw", + "else", + "var", + "number", + "string", + "get", + "module", + // "type", + "instanceof", + "typeof", + "public", + "private", + "enum", + "export", + "finally", + "for", + "while", + "void", + "null", + "super", + "this", + "new", + "in", + "extends", + "static", + "package", + "implements", + "interface", + "continue", + "yield", + "const" + ); + } + + public BindingManager(Prefs prefs) + { + this.prefs = prefs; + this.dateTime = DateTime.Now; + var tab = prefs.tab; + var newline = prefs.newline; + typePrefixBlacklist = prefs.typePrefixBlacklist; + log = new TextGenerator(newline, tab); + blacklist = new HashSet(new Type[] + { + typeof(AOT.MonoPInvokeCallbackAttribute), + }); + whitelist = new HashSet(new Type[] + { + }); + + TransformType(typeof(GameObject)) + // .AddRedirectMethod("AddComponent", "_AddComponent") + // .AddRedirectMethod("GetComponent", "_GetComponent") + // .AddRedirectMethod("GetComponentInChildren", "_GetComponentInChildren") + // .AddRedirectMethod("GetComponentInParent", "_GetComponentInParent") + // .AddRedirectMethod("GetComponents", "_GetComponents") + // .AddRedirectMethod("GetComponentsInChildren", "_GetComponentsInChildren") + // .AddRedirectMethod("GetComponentsInParent", "_GetComponentsInParent") + .AddTSMethodDeclaration("AddComponent(type: { new(): T }): T", + "AddComponent", typeof(Type)) + .AddTSMethodDeclaration("GetComponent(type: { new(): T }): T", + "GetComponent", typeof(Type)) + .AddTSMethodDeclaration("GetComponentInChildren(type: { new(): T }, includeInactive: boolean): T", + "GetComponentInChildren", typeof(Type), typeof(bool)) + .AddTSMethodDeclaration("GetComponentInChildren(type: { new(): T }): T", + "GetComponentInChildren", typeof(Type)) + .AddTSMethodDeclaration("GetComponentInParent(type: { new(): T }): T", + "GetComponentInParent", typeof(Type)) + // .AddTSMethodDeclaration("GetComponents(type: { new(): T }, results: any): void", + // "GetComponents", typeof(Type)) + .AddTSMethodDeclaration("GetComponents(type: { new(): T }): T[]", + "GetComponents", typeof(Type)) + .AddTSMethodDeclaration("GetComponentsInChildren(type: { new(): T }, includeInactive: boolean): T[]", + "GetComponentsInChildren", typeof(Type), typeof(bool)) + .AddTSMethodDeclaration("GetComponentsInChildren(type: { new(): T }): T[]", + "GetComponentsInChildren", typeof(Type)) + .AddTSMethodDeclaration("GetComponentsInParent(type: { new(): T }, includeInactive: boolean): T[]", + "GetComponentsInParent", typeof(Type), typeof(bool)) + .AddTSMethodDeclaration("GetComponentsInParent(type: { new(): T }): T[]", + "GetComponentsInParent", typeof(Type)) + ; + + // fix d.ts, some C# classes use explicit implemented interface method + SetTypeBlocked(typeof(UnityEngine.ILogHandler)); + SetTypeBlocked(typeof(UnityEngine.ISerializationCallbackReceiver)); + + TransformType(typeof(object)) + .RenameTSMethod("$Equals", "Equals", typeof(object)) + .RenameTSMethod("$Equals", "Equals", typeof(object), typeof(object)) + ; + + TransformType(typeof(Vector3)) + .SetMethodBlocked("SqrMagnitude", typeof(Vector3)) + .SetMethodBlocked("Magnitude", typeof(Vector3)) + .AddTSMethodDeclaration("static Add(a: Vector3, b: Vector3): Vector3") + .AddTSMethodDeclaration("static Sub(a: Vector3, b: Vector3): Vector3") + .AddTSMethodDeclaration("static Mul(a: Vector3, b: Vector3): Vector3") + .AddTSMethodDeclaration("static Div(a: Vector3, b: Vector3): Vector3") + .AddTSMethodDeclaration("static Equals(a: Vector3, b: Vector3): boolean") + .AddTSMethodDeclaration("Equals(b: Vector3): boolean") + .AddTSMethodDeclaration("Inverse(): Vector3") + .AddTSMethodDeclaration("Clone(): Vector3") + ; + + TransformType(typeof(Vector2)) + .SetMethodBlocked("SqrMagnitude") + .SetMethodBlocked("SqrMagnitude", typeof(Vector2)) + .AddTSMethodDeclaration("static Add(a: Vector2, b: Vector2): Vector2") + .AddTSMethodDeclaration("static Sub(a: Vector2, b: Vector2): Vector2") + .AddTSMethodDeclaration("static Mul(a: Vector2, b: Vector2): Vector2") + .AddTSMethodDeclaration("static Div(a: Vector2, b: Vector2): Vector2") + .AddTSMethodDeclaration("static Equals(a: Vector2, b: Vector2): boolean") + .AddTSMethodDeclaration("Equals(b: Vector2): boolean") + .AddTSMethodDeclaration("Inverse(): Vector2") + .AddTSMethodDeclaration("Clone(): Vector2") + ; + + TransformType(typeof(Quaternion)) + .AddTSMethodDeclaration("Clone(): Quaternion") + ; + // SetTypeBlocked(typeof(RendererExtensions)); + SetTypeBlocked(typeof(UnityEngine.UI.ILayoutGroup)); + SetTypeBlocked(typeof(UnityEngine.UI.ILayoutSelfController)); + TransformType(typeof(UnityEngine.UI.PositionAsUV1)) + .SetMemberBlocked("ModifyMesh"); + TransformType(typeof(UnityEngine.UI.Shadow)) + .SetMemberBlocked("ModifyMesh"); + TransformType(typeof(UnityEngine.UI.Outline)) + .SetMemberBlocked("ModifyMesh"); + + // editor 使用的 .net 与 player 所用存在差异, 这里屏蔽不存在的成员 + TransformType(typeof(double)) + .SetMemberBlocked("IsFinite") + ; + TransformType(typeof(float)) + .SetMemberBlocked("IsFinite") + ; + TransformType(typeof(string)) + .SetMemberBlocked("Chars") + ; + + AddTSTypeNameMap(typeof(sbyte), "number"); + AddTSTypeNameMap(typeof(byte), "number"); + AddTSTypeNameMap(typeof(int), "number"); + AddTSTypeNameMap(typeof(uint), "number"); + AddTSTypeNameMap(typeof(short), "number"); + AddTSTypeNameMap(typeof(ushort), "number"); + AddTSTypeNameMap(typeof(long), "number"); + AddTSTypeNameMap(typeof(ulong), "number"); + AddTSTypeNameMap(typeof(float), "number"); + AddTSTypeNameMap(typeof(double), "number"); + AddTSTypeNameMap(typeof(bool), "boolean"); + AddTSTypeNameMap(typeof(string), "string"); + AddTSTypeNameMap(typeof(char), "string"); + AddTSTypeNameMap(typeof(void), "void"); + AddTSTypeNameMap(typeof(LayerMask), "UnityEngine.LayerMask", "number"); + AddTSTypeNameMap(typeof(Color), "UnityEngine.Color"); + AddTSTypeNameMap(typeof(Color32), "UnityEngine.Color32"); + AddTSTypeNameMap(typeof(Vector2), "UnityEngine.Vector2"); + AddTSTypeNameMap(typeof(Vector2Int), "UnityEngine.Vector2Int"); + AddTSTypeNameMap(typeof(Vector3), "UnityEngine.Vector3"); // 已优化, 在 VM 初始化时替换为 DuktapeJS.Vector3 + AddTSTypeNameMap(typeof(Vector3Int), "UnityEngine.Vector3Int"); + AddTSTypeNameMap(typeof(Vector4), "UnityEngine.Vector4"); + AddTSTypeNameMap(typeof(Quaternion), "UnityEngine.Quaternion"); + AddTSTypeNameMap(typeof(DuktapeArray), "any[]"); + + AddCSTypeNameMap(typeof(sbyte), "sbyte"); + AddCSTypeNameMap(typeof(byte), "byte"); + AddCSTypeNameMap(typeof(int), "int"); + AddCSTypeNameMap(typeof(uint), "uint"); + AddCSTypeNameMap(typeof(short), "short"); + AddCSTypeNameMap(typeof(ushort), "ushort"); + AddCSTypeNameMap(typeof(long), "long"); + AddCSTypeNameMap(typeof(ulong), "ulong"); + AddCSTypeNameMap(typeof(float), "float"); + AddCSTypeNameMap(typeof(double), "double"); + AddCSTypeNameMap(typeof(bool), "bool"); + AddCSTypeNameMap(typeof(string), "string"); + AddCSTypeNameMap(typeof(char), "char"); + AddCSTypeNameMap(typeof(System.Object), "object"); + AddCSTypeNameMap(typeof(void), "void"); + + Initialize(); + } + + public void SetTypeBlocked(Type type) + { + blacklist.Add(type); + } + + public bool GetTSMethodDeclaration(MethodBase method, out string code) + { + var transform = GetTypeTransform(method.DeclaringType); + if (transform != null) + { + return transform.GetTSMethodDeclaration(method, out code); + } + code = null; + return false; + } + + public bool GetTSMethodRename(MethodBase method, out string code) + { + var transform = GetTypeTransform(method.DeclaringType); + if (transform != null) + { + return transform.GetTSMethodRename(method, out code); + } + code = null; + return false; + } + + public TypeTransform GetTypeTransform(Type type) + { + TypeTransform transform; + return typesTarnsform.TryGetValue(type, out transform) ? transform : null; + } + + public TypeTransform TransformType(Type type) + { + TypeTransform transform; + if (!typesTarnsform.TryGetValue(type, out transform)) + { + typesTarnsform[type] = transform = new TypeTransform(type); + } + return transform; + } + + private static bool _FindFilterBindingProcess(Type type, object l) + { + return type == typeof(IBindingProcess); + } + + private void Initialize() + { + var assembly = Assembly.Load("Assembly-CSharp-Editor"); + var types = assembly.GetExportedTypes(); + for (int i = 0, size = types.Length; i < size; i++) + { + var type = types[i]; + if (type.IsAbstract) + { + continue; + } + try + { + var interfaces = type.FindInterfaces(_FindFilterBindingProcess, null); + if (interfaces != null && interfaces.Length > 0) + { + var ctor = type.GetConstructor(Type.EmptyTypes); + var inst = ctor.Invoke(null) as IBindingProcess; + inst.OnInitialize(this); + _bindingProcess.Add(inst); + Debug.Log($"add binding process: {type}"); + // _bindingProcess.Add + } + } + catch (Exception exception) + { + Debug.LogWarning($"failed to add binding process: {type}\n{exception}"); + } + } + } + + // TS: 添加保留字, CS中相关变量名等会自动重命名注册到js中 + public static void AddTSKeywords(params string[] keywords) + { + foreach (var keyword in keywords) + { + _tsKeywords.Add(keyword); + } + } + + // 指定类型在 ts 声明中的映射名 (可以指定多项) + public void AddTSTypeNameMap(Type type, params string[] names) + { + List list; + if (!_tsTypeNameMap.TryGetValue(type, out list)) + { + _tsTypeNameMap[type] = list = new List(); + } + list.AddRange(names); + } + + // CS, 添加类型名称映射, 用于简化导出时的常用类型名 + public void AddCSTypeNameMap(Type type, string name) + { + _csTypeNameMap[type] = name; + _csTypeNameMapS[type.FullName] = name; + _csTypeNameMapS[GetCSNamespace(type) + type.Name] = name; + } + + // 增加导出类型 (需要在 Collect 阶段进行) + //NOTE: editor mscorlib 与 runtime 存在差异, 需要手工 block 差异 + public TypeTransform AddExportedType(Type type) + { + if (type.IsGenericTypeDefinition) + { + whitelist.Add(type); + return null; + } + if (!exportedTypes.ContainsKey(type)) + { + var typeBindingInfo = new TypeBindingInfo(this, type); + exportedTypes.Add(type, typeBindingInfo); + log.AppendLine($"AddExportedType: {type} Assembly: {type.Assembly}"); + + // 检查具体化泛型基类 (如果基类泛型定义在显式导出清单中, 那么导出此具体化类) + var baseType = type.BaseType; + if (baseType != null && baseType.IsConstructedGenericType) + { + if (!IsExportingBlocked(baseType) && IsExportingExplicit(baseType.GetGenericTypeDefinition())) + { + AddExportedType(baseType); + } + } + } + return TransformType(type); + } + + public bool RemoveExportedType(Type type) + { + return exportedTypes.Remove(type); + } + + public DelegateBindingInfo GetDelegateBindingInfo(Type type) + { + Type target; + if (redirectDelegates.TryGetValue(type, out target)) + { + type = target; + } + DelegateBindingInfo delegateBindingInfo; + if (exportedDelegates.TryGetValue(type, out delegateBindingInfo)) + { + return delegateBindingInfo; + } + return null; + } + + public static bool ContainsPointer(MethodBase method) + { + var parameters = method.GetParameters(); + for (int i = 0, size = parameters.Length; i < size; i++) + { + var parameterType = parameters[i].ParameterType; + if (parameterType.IsPointer) + { + return true; + } + } + return false; + } + + public static bool IsGenericMethod(MethodBase method) + { + return method.GetGenericArguments().Length > 0; + } + + public static bool IsUnsupported(MethodBase method) + { + return ContainsPointer(method) || IsGenericMethod(method); + } + + // 收集所有 delegate 类型 + public void CollectDelegate(Type type) + { + if (type == null || type.BaseType != typeof(MulticastDelegate)) + { + return; + } + if (!exportedDelegates.ContainsKey(type)) + { + var invoke = type.GetMethod("Invoke"); + var returnType = invoke.ReturnType; + var parameters = invoke.GetParameters(); + if (ContainsPointer(invoke)) + { + log.AppendLine("skip unsafe (pointer) delegate: [{0}] {1}", type, invoke); + return; + } + // 是否存在等价 delegate + foreach (var kv in exportedDelegates) + { + if (kv.Value.Equals(returnType, parameters)) + { + log.AppendLine("skip delegate: {0} && {1}", kv.Value, type); + kv.Value.types.Add(type); + redirectDelegates[type] = kv.Key; + return; + } + } + var delegateBindingInfo = new DelegateBindingInfo(returnType, parameters); + delegateBindingInfo.types.Add(type); + exportedDelegates.Add(type, delegateBindingInfo); + log.AppendLine("add delegate: {0}", type); + for (var i = 0; i < parameters.Length; i++) + { + CollectDelegate(parameters[i].ParameterType); + } + } + } + + public bool IsExported(Type type) + { + return exportedTypes.ContainsKey(type); + } + + public string GetTSRefWrap(string name) + { + return $"DuktapeJS.Ref<{name}>"; + } + + public string GetTSTypeFullName(ParameterInfo parameter) + { + var parameterType = parameter.ParameterType; + return GetTSTypeFullName(parameterType, parameter.IsOut); + } + + // 获取 type 在 typescript 中对应类型名 + public string GetTSTypeFullName(Type type) + { + return GetTSTypeFullName(type, false); + } + + public string GetTSTypeFullName(Type type, bool isOut) + { + if (type == null || type == typeof(void)) + { + return "void"; + } + if (type.IsByRef) + { + if (isOut) + { + return $"DuktapeJS.Out<{GetTSTypeFullName(type.GetElementType())}>"; + } + return $"DuktapeJS.Ref<{GetTSTypeFullName(type.GetElementType())}>"; + } + List names; + if (_tsTypeNameMap.TryGetValue(type, out names)) + { + return names.Count > 1 ? $"({String.Join(" | ", names)})" : names[0]; + } + if (type.IsArray) + { + if (type.GetElementType() == typeof(byte)) + { + return "Buffer"; + } + var elementType = type.GetElementType(); + return GetTSTypeFullName(elementType) + "[]"; + } + var info = GetExportedType(type); + if (info != null) + { + return info.jsFullName; + } + if (type.BaseType == typeof(MulticastDelegate)) + { + var delegateBindingInfo = GetDelegateBindingInfo(type); + if (delegateBindingInfo != null) + { + var nargs = delegateBindingInfo.parameters.Length; + var ret = GetTSTypeFullName(delegateBindingInfo.returnType); + var t_arglist = (nargs > 0 ? ", " : "") + GetTSArglistTypes(delegateBindingInfo.parameters, false); + var v_arglist = GetTSArglistTypes(delegateBindingInfo.parameters, true); + return $"DuktapeJS.Delegate{nargs}<{ret}{t_arglist}> | (({v_arglist}) => {ret})"; + } + } + return "any"; + } + + public string GetCSNamespace(Type type) + { + return GetCSNamespace(type.Namespace); + } + + public string GetCSNamespace(string ns) + { + return string.IsNullOrEmpty(ns) ? "" : (ns + "."); + } + + // 生成参数对应的字符串形式参数列表定义 (typescript) + public string GetTSArglistTypes(ParameterInfo[] parameters, bool withVarName) + { + var size = parameters.Length; + var arglist = ""; + if (size == 0) + { + return arglist; + } + for (var i = 0; i < size; i++) + { + var parameter = parameters[i]; + var typename = GetTSTypeFullName(parameter.ParameterType); + // if (parameter.IsOut && parameter.ParameterType.IsByRef) + // { + // arglist += "out "; + // } + // else if (parameter.ParameterType.IsByRef) + // { + // arglist += "ref "; + // } + if (withVarName) + { + arglist += GetTSVariable(parameter.Name) + ": "; + } + arglist += typename; + // arglist += " "; + // arglist += parameter.Name; + if (i != size - 1) + { + arglist += ", "; + } + } + return arglist; + } + + public string GetDuktapeGenericError(string err) + { + return $"DuktapeDLL.duk_generic_error(ctx, \"{err}\");"; + } + + public string GetDuktapeGetter(Type type) + { + if (type.IsByRef) + { + return GetDuktapeGetter(type.GetElementType()); + } + if (type.IsArray) + { + var elementType = type.GetElementType(); + return GetDuktapeGetter(elementType) + "_array"; //TODO: 嵌套数组的问题 + } + if (type.IsValueType) + { + if (type.IsPrimitive) + { + return "duk_get_primitive"; + } + if (type.IsEnum) + { + return "duk_get_enumvalue"; + } + return "duk_get_structvalue"; + } + if (type == typeof(string)) + { + return "duk_get_primitive"; + } + if (type.BaseType == typeof(MulticastDelegate)) + { + return "duk_get_delegate"; + } + if (type == typeof(Type)) + { + return "duk_get_type"; + } + return "duk_get_classvalue"; + } + + public string GetDuktapePusher(Type type) + { + if (type.IsByRef) + { + return GetDuktapePusher(type.GetElementType()); + } + if (type.BaseType == typeof(MulticastDelegate)) + { + return "duk_push_delegate"; + } + if (type.IsValueType) + { + if (type.IsPrimitive) + { + return "duk_push_primitive"; + } + if (type.IsEnum) + { + return "duk_push_enumvalue"; + } + return "duk_push_structvalue"; + } + if (type == typeof(string)) + { + return "duk_push_primitive"; + } + return "duk_push_classvalue"; + } + + public static string GetTSVariable(string name) + { + if (_tsKeywords.Contains(name)) + { + return name + "_"; + } + return name; + } + + // 保证生成一个以 prefix 为前缀, 与参数列表中所有参数名不同的名字 + public string GetUniqueName(ParameterInfo[] parameters, string prefix) + { + return GetUniqueName(parameters, prefix, 0); + } + + public string GetUniqueName(ParameterInfo[] parameters, string prefix, int index) + { + var size = parameters.Length; + var name = prefix + index; + for (var i = 0; i < size; i++) + { + var parameter = parameters[i]; + if (parameter.Name == prefix) + { + return GetUniqueName(parameters, prefix, index + 1); + } + } + return name; + } + + // 获取父类的ts声明 (沿继承链上溯直到存在导出) + public string GetTSSuperName(TypeBindingInfo typeBindingInfo) + { + var super = typeBindingInfo.super; + while (super != null) + { + var superBindingInfo = GetExportedType(super); + if (superBindingInfo != null) + { + return GetTSTypeFullName(superBindingInfo.type); + } + super = super.BaseType; + } + return ""; + } + + // 获取实现的接口的ts声明 + public string GetTSInterfacesName(TypeBindingInfo typeBindingInfo) + { + var interfaces = typeBindingInfo.type.GetInterfaces(); + var str = ""; + foreach (var @interface in interfaces) + { + var interfaceBindingInfo = GetExportedType(@interface); + if (interfaceBindingInfo != null) + { + // Debug.Log($"{typeBindingInfo.type.Name} implements {@interface.Name}"); + str += GetTSTypeFullName(interfaceBindingInfo.type) + ", "; + } + } + if (str.Length > 0) + { + str = str.Substring(0, str.Length - 2); + } + return str; + } + + // 生成参数对应的字符串形式参数列表 (csharp) + public string GetCSArglistDecl(ParameterInfo[] parameters) + { + var size = parameters.Length; + var arglist = ""; + if (size == 0) + { + return arglist; + } + for (var i = 0; i < size; i++) + { + var parameter = parameters[i]; + var typename = GetCSTypeFullName(parameter.ParameterType); + if (parameter.IsOut && parameter.ParameterType.IsByRef) + { + arglist += "out "; + } + else if (parameter.ParameterType.IsByRef) + { + arglist += "ref "; + } + arglist += typename; + arglist += " "; + arglist += parameter.Name; + if (i != size - 1) + { + arglist += ", "; + } + } + return arglist; + } + + // 获取 type 在 绑定代码 中对应类型名 + public string GetCSTypeFullName(Type type) + { + return GetCSTypeFullName(type, true); + } + + public string GetCSTypeFullName(Type type, bool shortName) + { + // Debug.LogFormat("{0} Array {1} ByRef {2} GetElementType {3}", type, type.IsArray, type.IsByRef, type.GetElementType()); + if (type.IsGenericType) + { + var @namespace = string.Empty; + var classname = type.Name.Substring(0, type.Name.Length - 2); + if (type.IsNested) + { + var indexOf = type.FullName.IndexOf("+"); + @namespace = type.FullName.Substring(0, indexOf) + "."; + } + else + { + @namespace = GetCSNamespace(type); + } + var purename = @namespace + classname; + var gargs = type.GetGenericArguments(); + purename += "<"; + for (var i = 0; i < gargs.Length; i++) + { + var garg = gargs[i]; + purename += GetCSTypeFullName(garg, shortName); + if (i != gargs.Length - 1) + { + purename += ", "; + } + } + purename += ">"; + return purename; + } + if (type.IsArray) + { + return GetCSTypeFullName(type.GetElementType(), shortName) + "[]"; + } + if (type.IsByRef) + { + return GetCSTypeFullName(type.GetElementType(), shortName); + } + string name; + if (shortName) + { + if (_csTypeNameMap.TryGetValue(type, out name)) + { + return name; + } + } + var fullname = type.FullName.Replace('+', '.'); + if (fullname.Contains("`")) + { + fullname = new Regex(@"`\d", RegexOptions.None).Replace(fullname, ""); + fullname = fullname.Replace("[", "<"); + fullname = fullname.Replace("]", ">"); + } + if (_csTypeNameMapS.TryGetValue(fullname, out name)) + { + return name; + } + return fullname; + } + + public TypeBindingInfo GetExportedType(Type type) + { + if (type == null) + { + return null; + } + TypeBindingInfo typeBindingInfo; + return exportedTypes.TryGetValue(type, out typeBindingInfo) ? typeBindingInfo : null; + } + + // 是否在黑名单中屏蔽, 或者已知无需导出的类型 + public bool IsExportingBlocked(Type type) + { + if (blacklist.Contains(type)) + { + return true; + } + if (type.IsGenericType && !type.IsConstructedGenericType) + { + return true; + } + if (type.Name.Contains("<")) + { + return true; + } + if (type.IsDefined(typeof(JSBindingAttribute), false)) + { + return true; + } + if (type.BaseType == typeof(Attribute)) + { + return true; + } + if (type.BaseType == typeof(MulticastDelegate)) + { + return true; + } + if (type.IsPointer) + { + return true; + } + var encloser = type; + while (encloser != null) + { + if (encloser.IsDefined(typeof(ObsoleteAttribute), false)) + { + return true; + } + encloser = encloser.DeclaringType; + } + for (int i = 0, size = typePrefixBlacklist.Count; i < size; i++) + { + if (type.FullName.StartsWith(typePrefixBlacklist[i])) + { + return true; + } + } + return false; + } + + // 是否显式要求导出 + public bool IsExportingExplicit(Type type) + { + if (whitelist.Contains(type)) + { + return true; + } + if (type.IsDefined(typeof(JSTypeAttribute), false)) + { + return true; + } + return false; + } + + private void OnPreCollectAssemblies() + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPreCollectAssemblies(this); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPreCollect]: {exception}"); + } + } + } + + private void OnPostCollectAssemblies() + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPostCollectAssemblies(this); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPostCollect]: {exception}"); + } + } + } + + private void OnPostExporting() + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPostExporting(this); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPostExporting]: {exception}"); + } + } + } + + private void OnPreCollectTypes() + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPreCollectTypes(this); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPreCollect]: {exception}"); + } + } + } + + private void OnPostCollectTypes() + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPostCollectTypes(this); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPostCollect]: {exception}"); + } + } + } + + private void OnPreGenerateType(TypeBindingInfo bindingInfo) + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPreGenerateType(this, bindingInfo); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPreGenerateType]: {exception}"); + } + } + } + + private void OnPostGenerateType(TypeBindingInfo bindingInfo) + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPostGenerateType(this, bindingInfo); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPostGenerateType]: {exception}"); + } + } + } + + public void OnPreGenerateDelegate(DelegateBindingInfo bindingInfo) + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPreGenerateDelegate(this, bindingInfo); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPreGenerateDelegate]: {exception}"); + } + } + } + + public void OnPostGenerateDelegate(DelegateBindingInfo bindingInfo) + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnPostGenerateDelegate(this, bindingInfo); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnPostGenerateDelegate]: {exception}"); + } + } + } + + private void OnCleanup() + { + for (int i = 0, size = _bindingProcess.Count; i < size; i++) + { + var bp = _bindingProcess[i]; + try + { + bp.OnCleanup(this); + } + catch (Exception exception) + { + this.Error($"process failed [{bp}][OnCleanup]: {exception}"); + } + } + } + + + + public void Collect() + { + // 收集直接类型, 加入 exportedTypes + OnPreCollectAssemblies(); + AddAssemblies(false, prefs.explicitAssemblies.ToArray()); + AddAssemblies(true, prefs.implicitAssemblies.ToArray()); + OnPostCollectAssemblies(); + + ExportAssemblies(_explicitAssemblies, false); + ExportAssemblies(_implicitAssemblies, true); + ExportBuiltins(); + OnPostExporting(); + + log.AppendLine("collecting members"); + log.AddTabLevel(); + OnPreCollectTypes(); + foreach (var typeBindingInfoKV in exportedTypes) + { + var typeBindingInfo = typeBindingInfoKV.Value; + log.AppendLine("type: {0}", typeBindingInfo.type); + log.AddTabLevel(); + typeBindingInfo.Collect(); + log.DecTabLevel(); + } + OnPostCollectTypes(); + log.DecTabLevel(); + } + + public void AddAssemblies(bool implicitExport, params string[] assemblyNames) + { + if (implicitExport) + { + _implicitAssemblies.AddRange(assemblyNames); + } + else + { + _explicitAssemblies.AddRange(assemblyNames); + } + } + + public void RemoveAssemblies(params string[] assemblyNames) + { + foreach (var name in assemblyNames) + { + _implicitAssemblies.Remove(name); + _explicitAssemblies.Remove(name); + } + } + + // 导出一些必要的基本类型 (预实现的辅助功能需要用到, DuktapeJS) + private void ExportBuiltins() + { + AddExportedType(typeof(byte)); + AddExportedType(typeof(sbyte)); + AddExportedType(typeof(float)); + AddExportedType(typeof(double)); + AddExportedType(typeof(string)); + AddExportedType(typeof(int)); + AddExportedType(typeof(uint)); + AddExportedType(typeof(short)); + AddExportedType(typeof(ushort)); + AddExportedType(typeof(object)); + AddExportedType(typeof(Array)); + AddExportedType(typeof(Object)); + AddExportedType(typeof(Vector3)); + } + + // implicitExport: 默认进行导出(黑名单例外), 否则根据导出标记或手工添加 + private void ExportAssemblies(List assemblyNames, bool implicitExport) + { + foreach (var assemblyName in assemblyNames) + { + log.AppendLine("assembly: {0}", assemblyName); + log.AddTabLevel(); + try + { + var assembly = Assembly.Load(assemblyName); + var types = assembly.GetExportedTypes(); + + log.AppendLine("types {0}", types.Length); + foreach (var type in types) + { + if (IsExportingBlocked(type)) + { + log.AppendLine("blocked: {0}", type.FullName); + continue; + } + if (implicitExport || IsExportingExplicit(type)) + { + log.AppendLine("export: {0}", type.FullName); + this.AddExportedType(type); + continue; + } + log.AppendLine("skip: {0}", type.FullName); + } + } + catch (Exception exception) + { + log.AppendLine(exception.ToString()); + } + log.DecTabLevel(); + } + } + + // 清理多余文件 + public void Cleanup() + { + log.AppendLine("cleanup"); + log.AddTabLevel(); + Cleanup(prefs.outDir, outputFiles, file => + { + removedFiles.Add(file); + log.AppendLine("remove unused file {0}", file); + }); + OnCleanup(); + log.DecTabLevel(); + } + + public static void Cleanup(string outDir, List excludedFiles, Action ondelete) + { + foreach (var file in Directory.GetFiles(outDir)) + { + var nfile = file; + if (file.EndsWith(".meta")) + { + nfile = file.Substring(0, file.Length - 5); + } + // Debug.LogFormat("checking file {0}", nfile); + if (excludedFiles == null || !excludedFiles.Contains(nfile)) + { + File.Delete(file); + if (ondelete != null) + { + ondelete(file); + } + } + } + } + + public void AddOutputFile(string filename) + { + outputFiles.Add(filename); + } + + public void Generate() + { + var cg = new CodeGenerator(this); + var outDir = prefs.outDir; + var tx = prefs.extraExt; + // var tx = ""; + if (!Directory.Exists(outDir)) + { + Directory.CreateDirectory(outDir); + } + var cancel = false; + var current = 0; + var total = exportedTypes.Count; + foreach (var typeKV in exportedTypes) + { + var typeBindingInfo = typeKV.Value; + try + { + current++; + cancel = EditorUtility.DisplayCancelableProgressBar( + "Generating", + $"{current}/{total}: {typeBindingInfo.FullName}", + (float)current / total); + if (cancel) + { + Warn("operation canceled"); + break; + } + if (!typeBindingInfo.omit) + { + cg.Clear(); + OnPreGenerateType(typeBindingInfo); + cg.Generate(typeBindingInfo); + OnPostGenerateType(typeBindingInfo); + cg.WriteTo(outDir, typeBindingInfo.GetFileName(), tx); + } + } + catch (Exception exception) + { + Error($"generate failed {typeBindingInfo.type.FullName}: {exception.Message}"); + Debug.LogError(exception.StackTrace); + } + } + + if (!cancel) + { + try + { + var exportedDelegatesArray = new DelegateBindingInfo[this.exportedDelegates.Count]; + this.exportedDelegates.Values.CopyTo(exportedDelegatesArray, 0); + + cg.Clear(); + cg.Generate(exportedDelegatesArray); + // cg.tsSource.enabled = false; + cg.WriteTo(outDir, DuktapeVM._DuktapeDelegates, tx); + } + catch (Exception exception) + { + Error($"generate delegates failed: {exception.Message}"); + Debug.LogError(exception.StackTrace); + } + } + + var logPath = prefs.logPath; + File.WriteAllText(logPath, log.ToString()); + EditorUtility.ClearProgressBar(); + } + + public void Report() + { + var now = DateTime.Now; + var ts = now.Subtract(dateTime); + Debug.LogFormat("generated {0} type(s), {1} delegate(s), {2} deletion(s) in {3:0.##} seconds.", + exportedTypes.Count, + exportedDelegates.Count, + removedFiles.Count, + ts.TotalSeconds); + } + } } \ No newline at end of file diff --git a/unity/Assets/Duktape/Editor/CodeGenHelper_Method.cs b/unity/Assets/Duktape/Editor/CodeGenHelper_Method.cs index b9186f1..f95daaa 100644 --- a/unity/Assets/Duktape/Editor/CodeGenHelper_Method.cs +++ b/unity/Assets/Duktape/Editor/CodeGenHelper_Method.cs @@ -1,608 +1,608 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; -using System.Reflection; - -namespace Duktape -{ - using UnityEngine; - using UnityEditor; - - public abstract class MethodBaseCodeGen : IDisposable - where T : MethodBase - { - protected CodeGenerator cg; - - // 方法参数数比较, 用于列表排序 - public static int MethodComparer(T a, T b) - { - var va = a.GetParameters().Length; - var vb = b.GetParameters().Length; - return va > vb ? 1 : ((va == vb) ? 0 : -1); - } - - public MethodBaseCodeGen(CodeGenerator cg) - { - this.cg = cg; - } - - protected virtual void OnBegin() - { - } - - public virtual void Dispose() - { - } - - public string GetParamArrayMatchType(T method) - { - var parameters = method.GetParameters(); - var parameter = parameters[parameters.Length - 1]; - var typename = this.cg.bindingManager.GetCSTypeFullName(parameter.ParameterType.GetElementType()); - return $"typeof({typename})"; - } - - // 生成定参部分 type 列表 (首参前也会补",") - public string GetFixedMatchTypes(T method) - { - var snippet = ""; - var parameters = method.GetParameters(); - for (int i = 0, length = parameters.Length; i < length; i++) - { - var parameter = parameters[i]; - if (parameter.IsDefined(typeof(ParamArrayAttribute), false)) - { - break; - } - snippet += ", "; - if (parameter.ParameterType.IsByRef) - { - //TODO: 检查 ref/out 参数有效请 (null undefined 或者 符合 Ref/Out 约定) - snippet += "null"; - } - else - { - var typename = this.cg.bindingManager.GetCSTypeFullName(parameter.ParameterType); - snippet += $"typeof({typename})"; - } - } - return snippet; - } - - // parametersByRef: 可修改参数将被加入此列表 - // hasParams: 是否包含变参 (最后一个参数将按数组处理) - public string AppendGetParameters(bool hasParams, string nargs, ParameterInfo[] parameters, List parametersByRef) - { - var arglist = ""; - var argBase = 0; - for (var i = argBase; i < parameters.Length; i++) - { - var parameter = parameters[i]; - if (parameter.IsOut && parameter.ParameterType.IsByRef) - { - arglist += "out "; - if (parametersByRef != null) - { - parametersByRef.Add(parameter); - } - } - else if (parameter.ParameterType.IsByRef) - { - arglist += "ref "; - if (parametersByRef != null) - { - parametersByRef.Add(parameter); - } - } - arglist += "arg" + i; - if (i != parameters.Length - 1) - { - arglist += ", "; - } - if (hasParams && i == parameters.Length - 1) - { - // 处理数组 - var argType = this.cg.bindingManager.GetCSTypeFullName(parameter.ParameterType); - var argElementType = this.cg.bindingManager.GetCSTypeFullName(parameter.ParameterType.GetElementType()); - var argElementIndex = i == 0 ? nargs : nargs + " - " + i; - this.cg.cs.AppendLine($"{argType} arg{i} = null;"); - this.cg.cs.AppendLine($"if ({argElementIndex} > 0)"); - this.cg.cs.AppendLine("{"); - this.cg.cs.AddTabLevel(); - { - this.cg.cs.AppendLine($"arg{i} = new {argElementType}[{argElementIndex}];"); - this.cg.cs.AppendLine($"for (var i = {i}; i < {nargs}; i++)"); - this.cg.cs.AppendLine("{"); - this.cg.cs.AddTabLevel(); - { - var argElementGetterOp = this.cg.bindingManager.GetDuktapeGetter(parameter.ParameterType.GetElementType()); - var argElementOffset = i == 0 ? "" : " - " + i; - this.cg.cs.AppendLine($"{argElementGetterOp}(ctx, i, out arg{i}[i{argElementOffset}]);"); - } - this.cg.cs.DecTabLevel(); - this.cg.cs.AppendLine("}"); - } - this.cg.cs.DecTabLevel(); - this.cg.cs.AppendLine("}"); - } - else - { - WriteParameterGetter(parameter, i, $"arg{i}"); - } - } - return arglist; - } - - protected void WriteParameterGetter(ParameterInfo parameter, int index, string argname) - { - var ptype = parameter.ParameterType; - var argType = this.cg.bindingManager.GetCSTypeFullName(ptype); - this.cg.cs.AppendLine($"{argType} {argname};"); - // 非 out 参数才需要取值 - if (!parameter.IsOut || !parameter.ParameterType.IsByRef) - { - var argGetterOp = this.cg.bindingManager.GetDuktapeGetter(ptype); - this.cg.cs.AppendLine($"{argGetterOp}(ctx, {index}, out {argname});"); - } - } - - // 输出所有变体绑定 - // hasOverrides: 是否需要处理重载 - protected void WriteAllVariants(MethodBaseBindingInfo bindingInfo) // SortedDictionary> variants) - { - var variants = bindingInfo.variants; - var hasOverrides = bindingInfo.count > 1; - if (hasOverrides) - { - // 需要处理重载 - GenMethodVariants(variants); - } - else - { - // 没有重载的情况 (variants.Count == 1) - foreach (var variantKV in variants) - { - var args = variantKV.Key; - var variant = variantKV.Value; - var argc = cg.AppendGetArgCount(variant.isVararg); - - if (variant.isVararg) - { - var method = variant.varargMethods[0]; - // Debug.Log($"varargMethods {method}"); - WriteCSMethodBinding(method, argc, true); - } - else - { - var method = variant.plainMethods[0]; - // Debug.Log($"plainMethods {method}"); - WriteCSMethodBinding(method, argc, false); - } - } - } - } - - protected void WriteTSAllVariants(MethodBaseBindingInfo bindingInfo) - { - var variants = bindingInfo.variants; - //TODO: 如果产生了无法在 typescript 中声明的方法, 则作标记, 并输出一条万能声明 - // [key: string]: any - foreach (var variantKV in variants) - { - foreach (var method in variantKV.Value.plainMethods) - { - WriteTSDeclaration(method, bindingInfo); - } - foreach (var method in variantKV.Value.varargMethods) - { - WriteTSDeclaration(method, bindingInfo); - } - } - } - - // 写入返回类型声明 - protected virtual void WriteTSReturn(T method, List returnParameters) - { - var returnType = GetReturnType(method); - if (returnType != null) - { - var returnTypeTS = this.cg.bindingManager.GetTSTypeFullName(returnType); - this.cg.tsDeclare.AppendL($": {returnTypeTS}"); - this.cg.tsDeclare.AppendLine(); - } - else - { - this.cg.tsDeclare.AppendLine(); - } - } - - protected void GenMethodVariants(SortedDictionary> variants) - { - var argc = cg.AppendGetArgCount(true); - cg.cs.AppendLine("do"); - cg.cs.AppendLine("{"); - cg.cs.AddTabLevel(); - { - foreach (var variantKV in variants) - { - var args = variantKV.Key; - var variant = variantKV.Value; - //variant.count > 1 - var gecheck = args > 0 && variant.isVararg; // 最后一组分支且存在变参时才需要判断 >= - if (gecheck) - { - cg.cs.AppendLine("if (argc >= {0})", args); - cg.cs.AppendLine("{"); - cg.cs.AddTabLevel(); - } - // 处理定参 - if (variant.plainMethods.Count > 0) - { - cg.cs.AppendLine("if (argc == {0})", args); - cg.cs.AppendLine("{"); - cg.cs.AddTabLevel(); - if (variant.plainMethods.Count > 1) - { - foreach (var method in variant.plainMethods) - { - cg.cs.AppendLine($"if (duk_match_types(ctx, argc{GetFixedMatchTypes(method)}))"); - cg.cs.AppendLine("{"); - cg.cs.AddTabLevel(); - this.WriteCSMethodBinding(method, argc, false); - cg.cs.DecTabLevel(); - cg.cs.AppendLine("}"); - } - cg.cs.AppendLine("break;"); - } - else - { - // 只有一个定参方法时, 不再判定类型匹配 - var method = variant.plainMethods[0]; - this.WriteCSMethodBinding(method, argc, false); - } - cg.cs.DecTabLevel(); - cg.cs.AppendLine("}"); - } - // 处理变参 - if (variant.varargMethods.Count > 0) - { - foreach (var method in variant.varargMethods) - { - cg.cs.AppendLine($"if (duk_match_types(ctx, argc{GetFixedMatchTypes(method)})"); - cg.cs.AppendLine($" && duk_match_param_types(ctx, {args}, argc, {GetParamArrayMatchType(method)}))"); - cg.cs.AppendLine("{"); - cg.cs.AddTabLevel(); - this.WriteCSMethodBinding(method, argc, true); - cg.cs.DecTabLevel(); - cg.cs.AppendLine("}"); - } - } - if (gecheck) - { - cg.cs.DecTabLevel(); - cg.cs.AppendLine("}"); - } - } - } - cg.cs.DecTabLevel(); - cg.cs.AppendLine("} while(false);"); - var error = this.cg.bindingManager.GetDuktapeGenericError("no matched method variant"); - cg.cs.AppendLine($"return {error}"); - } - - protected List WriteTSDeclaration(T method, MethodBaseBindingInfo bindingInfo) - { - var isExtension = method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute)); - var refParameters = new List(); - string tsMethodDeclaration; - if (this.cg.bindingManager.GetTSMethodDeclaration(method, out tsMethodDeclaration)) - { - this.cg.tsDeclare.AppendLine(tsMethodDeclaration); - return refParameters; - } - //TODO: 需要处理参数类型归并问题, 因为如果类型没有导入 ts 中, 可能会在声明中出现相同参数列表的定义 - // 在 MethodVariant 中创建每个方法对应的TS类型名参数列表, 完全相同的不再输出 - this.cg.AppendJSDoc(method); - var prefix = ""; - if (method.IsStatic && !isExtension) - { - prefix = "static "; - } - string tsMethodRename; - if (this.cg.bindingManager.GetTSMethodRename(method, out tsMethodRename)) - { - this.cg.tsDeclare.Append($"{prefix}{tsMethodRename}("); - } - else - { - this.cg.tsDeclare.Append($"{prefix}{bindingInfo.regName}("); - } - var parameters = method.GetParameters(); - if (isExtension) - { - ArrayUtility.RemoveAt(ref parameters, 0); - } - for (var i = 0; i < parameters.Length; i++) - { - var parameter = parameters[i]; - var parameter_prefix = ""; - var parameterType = parameter.ParameterType; - if (parameter.IsOut && parameterType.IsByRef) - { - // parameter_prefix = "/*out*/ "; - refParameters.Add(parameter); - } - else if (parameterType.IsByRef) - { - // parameter_prefix = "/*ref*/ "; - refParameters.Add(parameter); - } - if (parameter.IsDefined(typeof(ParamArrayAttribute), false) && i == parameters.Length - 1) - { - var elementType = parameterType.GetElementType(); - var elementTS = this.cg.bindingManager.GetTSTypeFullName(elementType); - var parameterVarName = BindingManager.GetTSVariable(parameter.Name); - this.cg.tsDeclare.AppendL($"{parameter_prefix}...{parameterVarName}: {elementTS}[]"); - } - else - { - var parameterTS = this.cg.bindingManager.GetTSTypeFullName(parameterType, parameter.IsOut); - var parameterVarName = BindingManager.GetTSVariable(parameter.Name); - this.cg.tsDeclare.AppendL($"{parameter_prefix}{parameterVarName}: {parameterTS}"); - } - if (i != parameters.Length - 1) - { - this.cg.tsDeclare.AppendL(", "); - } - } - this.cg.tsDeclare.AppendL($")"); - WriteTSReturn(method, refParameters); - return refParameters; - } - - // 获取返回值类型 - protected abstract Type GetReturnType(T method); - - // 获取方法调用 - protected abstract string GetInvokeBinding(string caller, T method, bool hasParams, bool isExtension, string nargs, ParameterInfo[] parameters, List parametersByRef); - - protected virtual void BeginInvokeBinding() { } - - protected virtual void EndInvokeBinding() { } - - // 写入绑定代码 - protected void WriteCSMethodBinding(T method, string argc, bool isVararg) - { - var parameters = method.GetParameters(); - var isExtension = method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute)); - if (isExtension) - { - ArrayUtility.RemoveAt(ref parameters, 0); - } - var parametersByRef = new List(); - var caller = this.cg.AppendGetThisCS(method); - var returnType = GetReturnType(method); - - if (returnType == null || returnType == typeof(void)) - { - // 方法本身没有返回值 - this.BeginInvokeBinding(); - cg.cs.AppendLine($"{this.GetInvokeBinding(caller, method, isVararg, isExtension, argc, parameters, parametersByRef)};"); - this.EndInvokeBinding(); - if (parametersByRef.Count > 0) - { - _WriteBackParametersByRef(isExtension, parametersByRef); - - } - if (!method.IsStatic && method.DeclaringType.IsValueType) // struct 非静态方法 检查 Mutable 属性 - { - if (!string.IsNullOrEmpty(caller)) - { - cg.cs.AppendLine($"duk_rebind_this(ctx, {caller});"); - } - } - cg.cs.AppendLine("return 0;"); - } - else - { - // 方法本身有返回值 - this.BeginInvokeBinding(); - cg.cs.AppendLine($"var ret = {this.GetInvokeBinding(caller, method, isVararg, isExtension, argc, parameters, parametersByRef)};"); - this.EndInvokeBinding(); - if (parametersByRef.Count > 0) - { - _WriteBackParametersByRef(isExtension, parametersByRef); - } - cg.AppendPushValue(returnType, "ret"); - cg.cs.AppendLine("return 1;"); - } - } - - // 回填 ref/out 参数 - //TODO: !!! 扩展方法参数索引需要偏移 - protected void _WriteBackParametersByRef(bool isExtension, List parametersByRef) - { - for (var i = 0; i < parametersByRef.Count; i++) - { - var parameter = parametersByRef[i]; - var position = isExtension ? parameter.Position - 1 : parameter.Position; - cg.cs.AppendLine($"if (!DuktapeDLL.duk_is_null_or_undefined(ctx, {position}))"); - cg.cs.AppendLine("{"); - cg.cs.AddTabLevel(); - var argname = $"arg{position}"; - cg.AppendPushValue(parameter.ParameterType, argname); - cg.cs.AppendLine($"DuktapeDLL.duk_unity_put_target_i(ctx, {position});"); - cg.cs.DecTabLevel(); - cg.cs.AppendLine("}"); - } - } - } - - public class ConstructorCodeGen : MethodBaseCodeGen - { - private ConstructorBindingInfo bindingInfo; - - protected override Type GetReturnType(ConstructorInfo method) - { - return null; - } - - protected override string GetInvokeBinding(string caller, ConstructorInfo method, bool hasParams, bool isExtension, string nargs, ParameterInfo[] parameters, List parametersByRef) - { - var arglist = this.AppendGetParameters(hasParams, nargs, parameters, parametersByRef); - var decalringTypeName = this.cg.bindingManager.GetCSTypeFullName(this.bindingInfo.decalringType); - return $"var o = new {decalringTypeName}({arglist})"; - } - - protected override void EndInvokeBinding() - { - this.cg.cs.AppendLine("duk_bind_native(ctx, o);"); - } - - public ConstructorCodeGen(CodeGenerator cg, TypeBindingInfo bindingInfo) - : base(cg) - { - // WriteInstanceEvents(bindingInfo); - this.bindingInfo = bindingInfo.constructors; - if (this.bindingInfo.count > 0) - { - WriteAllVariants(this.bindingInfo); - WriteTSAllVariants(this.bindingInfo); - } - else - { - WriteDefaultConstructorBinding(); - } - } - - // private void WriteInstanceEvents(TypeBindingInfo bindingInfo) - // { - // var eventBindingInfos = new List(); - // foreach (var kv in bindingInfo.events) - // { - // var eventBindingInfo = kv.Value; - // var bStatic = eventBindingInfo.isStatic; - // if (!bStatic) - // { - // eventBindingInfos.Add(eventBindingInfo); - // } - // } - // if (eventBindingInfos.Count > 0) - // { - // // Debug.Log($"Writing instance events... {bindingInfo.type}"); - // this.cg.cs.AppendLine("DuktapeDLL.duk_push_this(ctx);"); - // foreach (var eventBindingInfo in eventBindingInfos) - // { - // var tsFieldVar = BindingManager.GetTSVariable(eventBindingInfo.regName); - // cg.cs.AppendLine($"duk_add_event(ctx, \"{tsFieldVar}\", {eventBindingInfo.adderName}, {eventBindingInfo.removerName}, -1);"); - // } - // this.cg.cs.AppendLine("DuktapeDLL.duk_pop(ctx);"); - // } - // } - - // 写入默认构造函数 (struct 无参构造) - private void WriteDefaultConstructorBinding() - { - var decalringTypeName = this.cg.bindingManager.GetCSTypeFullName(this.bindingInfo.decalringType); - this.cg.cs.AppendLine($"var o = new {decalringTypeName}();"); - this.cg.cs.AppendLine("duk_bind_native(ctx, o);"); - this.cg.cs.AppendLine("return 0;"); - - this.cg.tsDeclare.AppendLine($"{this.bindingInfo.regName}()"); - } - } - - // 生成成员方法绑定代码 - public class MethodCodeGen : MethodBaseCodeGen - { - protected MethodBindingInfo bindingInfo; - - protected override Type GetReturnType(MethodInfo method) - { - return method.ReturnType; - } - - protected override string GetInvokeBinding(string caller, MethodInfo method, bool hasParams, bool isExtension, string nargs, ParameterInfo[] parameters, List parametersByRef) - { - if (bindingInfo.isIndexer) - { - if (method.ReturnType == typeof(void)) - { - var last = parameters.Length - 1; - var arglist_t = ""; - for (var i = 0; i < last; i++) - { - var argname = $"arg{i}"; - this.WriteParameterGetter(parameters[i], i, argname); - arglist_t += argname; - if (i != last - 1) - { - arglist_t += ", "; - } - } - var argname_last = $"arg{last}"; - this.WriteParameterGetter(parameters[last], last, argname_last); - return $"{caller}[{arglist_t}] = {argname_last}"; // setter - } - else - { - var last = parameters.Length; - var arglist_t = ""; - for (var i = 0; i < last; i++) - { - var argname = $"arg{i}"; - this.WriteParameterGetter(parameters[i], i, argname); - arglist_t += argname; - if (i != last - 1) - { - arglist_t += ", "; - } - } - return $"{caller}[{arglist_t}]"; // getter - } - } - var arglist = this.AppendGetParameters(hasParams, nargs, parameters, parametersByRef); - if (isExtension) - { - var methodDeclType = this.cg.bindingManager.GetCSTypeFullName(method.DeclaringType); - if (arglist.Length > 0) - { - arglist = ", " + arglist; - } - return $"{methodDeclType}.{method.Name}({caller}{arglist})"; - } - return $"{caller}.{method.Name}({arglist})"; - } - - public MethodCodeGen(CodeGenerator cg, MethodBindingInfo bindingInfo) - : base(cg) - { - this.bindingInfo = bindingInfo; - WriteAllVariants(this.bindingInfo); - // WriteTSAllVariants(this.bindingInfo); - } - } - - public class TSMethodCodeGen : MethodBaseCodeGen - { - protected MethodBindingInfo bindingInfo; - - protected override Type GetReturnType(MethodInfo method) - { - return method.ReturnType; - } - - protected override string GetInvokeBinding(string caller, MethodInfo method, bool hasParams, bool isExtension, string nargs, ParameterInfo[] parameters, List parametersByRef) - { - return null; - } - - public TSMethodCodeGen(CodeGenerator cg, MethodBindingInfo bindingInfo) - : base(cg) - { - this.bindingInfo = bindingInfo; - WriteTSAllVariants(this.bindingInfo); - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Reflection; + +namespace Duktape +{ + using UnityEngine; + using UnityEditor; + + public abstract class MethodBaseCodeGen : IDisposable + where T : MethodBase + { + protected CodeGenerator cg; + + // 方法参数数比较, 用于列表排序 + public static int MethodComparer(T a, T b) + { + var va = a.GetParameters().Length; + var vb = b.GetParameters().Length; + return va > vb ? 1 : ((va == vb) ? 0 : -1); + } + + public MethodBaseCodeGen(CodeGenerator cg) + { + this.cg = cg; + } + + protected virtual void OnBegin() + { + } + + public virtual void Dispose() + { + } + + public string GetParamArrayMatchType(T method) + { + var parameters = method.GetParameters(); + var parameter = parameters[parameters.Length - 1]; + var typename = this.cg.bindingManager.GetCSTypeFullName(parameter.ParameterType.GetElementType()); + return $"typeof({typename})"; + } + + // 生成定参部分 type 列表 (首参前也会补",") + public string GetFixedMatchTypes(T method) + { + var snippet = ""; + var parameters = method.GetParameters(); + for (int i = 0, length = parameters.Length; i < length; i++) + { + var parameter = parameters[i]; + if (parameter.IsDefined(typeof(ParamArrayAttribute), false)) + { + break; + } + snippet += ", "; + if (parameter.ParameterType.IsByRef) + { + //TODO: 检查 ref/out 参数有效请 (null undefined 或者 符合 Ref/Out 约定) + snippet += "null"; + } + else + { + var typename = this.cg.bindingManager.GetCSTypeFullName(parameter.ParameterType); + snippet += $"typeof({typename})"; + } + } + return snippet; + } + + // parametersByRef: 可修改参数将被加入此列表 + // hasParams: 是否包含变参 (最后一个参数将按数组处理) + public string AppendGetParameters(bool hasParams, string nargs, ParameterInfo[] parameters, List parametersByRef) + { + var arglist = ""; + var argBase = 0; + for (var i = argBase; i < parameters.Length; i++) + { + var parameter = parameters[i]; + if (parameter.IsOut && parameter.ParameterType.IsByRef) + { + arglist += "out "; + if (parametersByRef != null) + { + parametersByRef.Add(parameter); + } + } + else if (parameter.ParameterType.IsByRef) + { + arglist += "ref "; + if (parametersByRef != null) + { + parametersByRef.Add(parameter); + } + } + arglist += "arg" + i; + if (i != parameters.Length - 1) + { + arglist += ", "; + } + if (hasParams && i == parameters.Length - 1) + { + // 处理数组 + var argType = this.cg.bindingManager.GetCSTypeFullName(parameter.ParameterType); + var argElementType = this.cg.bindingManager.GetCSTypeFullName(parameter.ParameterType.GetElementType()); + var argElementIndex = i == 0 ? nargs : nargs + " - " + i; + this.cg.cs.AppendLine($"{argType} arg{i} = null;"); + this.cg.cs.AppendLine($"if ({argElementIndex} > 0)"); + this.cg.cs.AppendLine("{"); + this.cg.cs.AddTabLevel(); + { + this.cg.cs.AppendLine($"arg{i} = new {argElementType}[{argElementIndex}];"); + this.cg.cs.AppendLine($"for (var i = {i}; i < {nargs}; i++)"); + this.cg.cs.AppendLine("{"); + this.cg.cs.AddTabLevel(); + { + var argElementGetterOp = this.cg.bindingManager.GetDuktapeGetter(parameter.ParameterType.GetElementType()); + var argElementOffset = i == 0 ? "" : " - " + i; + this.cg.cs.AppendLine($"{argElementGetterOp}(ctx, i, out arg{i}[i{argElementOffset}]);"); + } + this.cg.cs.DecTabLevel(); + this.cg.cs.AppendLine("}"); + } + this.cg.cs.DecTabLevel(); + this.cg.cs.AppendLine("}"); + } + else + { + WriteParameterGetter(parameter, i, $"arg{i}"); + } + } + return arglist; + } + + protected void WriteParameterGetter(ParameterInfo parameter, int index, string argname) + { + var ptype = parameter.ParameterType; + var argType = this.cg.bindingManager.GetCSTypeFullName(ptype); + this.cg.cs.AppendLine($"{argType} {argname};"); + // 非 out 参数才需要取值 + if (!parameter.IsOut || !parameter.ParameterType.IsByRef) + { + var argGetterOp = this.cg.bindingManager.GetDuktapeGetter(ptype); + this.cg.cs.AppendLine($"{argGetterOp}(ctx, {index}, out {argname});"); + } + } + + // 输出所有变体绑定 + // hasOverrides: 是否需要处理重载 + protected void WriteAllVariants(MethodBaseBindingInfo bindingInfo) // SortedDictionary> variants) + { + var variants = bindingInfo.variants; + var hasOverrides = bindingInfo.count > 1; + if (hasOverrides) + { + // 需要处理重载 + GenMethodVariants(variants); + } + else + { + // 没有重载的情况 (variants.Count == 1) + foreach (var variantKV in variants) + { + var args = variantKV.Key; + var variant = variantKV.Value; + var argc = cg.AppendGetArgCount(variant.isVararg); + + if (variant.isVararg) + { + var method = variant.varargMethods[0]; + // Debug.Log($"varargMethods {method}"); + WriteCSMethodBinding(method, argc, true); + } + else + { + var method = variant.plainMethods[0]; + // Debug.Log($"plainMethods {method}"); + WriteCSMethodBinding(method, argc, false); + } + } + } + } + + protected void WriteTSAllVariants(MethodBaseBindingInfo bindingInfo) + { + var variants = bindingInfo.variants; + //TODO: 如果产生了无法在 typescript 中声明的方法, 则作标记, 并输出一条万能声明 + // [key: string]: any + foreach (var variantKV in variants) + { + foreach (var method in variantKV.Value.plainMethods) + { + WriteTSDeclaration(method, bindingInfo); + } + foreach (var method in variantKV.Value.varargMethods) + { + WriteTSDeclaration(method, bindingInfo); + } + } + } + + // 写入返回类型声明 + protected virtual void WriteTSReturn(T method, List returnParameters) + { + var returnType = GetReturnType(method); + if (returnType != null) + { + var returnTypeTS = this.cg.bindingManager.GetTSTypeFullName(returnType); + this.cg.tsDeclare.AppendL($": {returnTypeTS}"); + this.cg.tsDeclare.AppendLine(); + } + else + { + this.cg.tsDeclare.AppendLine(); + } + } + + protected void GenMethodVariants(SortedDictionary> variants) + { + var argc = cg.AppendGetArgCount(true); + cg.cs.AppendLine("do"); + cg.cs.AppendLine("{"); + cg.cs.AddTabLevel(); + { + foreach (var variantKV in variants) + { + var args = variantKV.Key; + var variant = variantKV.Value; + //variant.count > 1 + var gecheck = args > 0 && variant.isVararg; // 最后一组分支且存在变参时才需要判断 >= + if (gecheck) + { + cg.cs.AppendLine("if (argc >= {0})", args); + cg.cs.AppendLine("{"); + cg.cs.AddTabLevel(); + } + // 处理定参 + if (variant.plainMethods.Count > 0) + { + cg.cs.AppendLine("if (argc == {0})", args); + cg.cs.AppendLine("{"); + cg.cs.AddTabLevel(); + if (variant.plainMethods.Count > 1) + { + foreach (var method in variant.plainMethods) + { + cg.cs.AppendLine($"if (duk_match_types(ctx, argc{GetFixedMatchTypes(method)}))"); + cg.cs.AppendLine("{"); + cg.cs.AddTabLevel(); + this.WriteCSMethodBinding(method, argc, false); + cg.cs.DecTabLevel(); + cg.cs.AppendLine("}"); + } + cg.cs.AppendLine("break;"); + } + else + { + // 只有一个定参方法时, 不再判定类型匹配 + var method = variant.plainMethods[0]; + this.WriteCSMethodBinding(method, argc, false); + } + cg.cs.DecTabLevel(); + cg.cs.AppendLine("}"); + } + // 处理变参 + if (variant.varargMethods.Count > 0) + { + foreach (var method in variant.varargMethods) + { + cg.cs.AppendLine($"if (duk_match_types(ctx, argc{GetFixedMatchTypes(method)})"); + cg.cs.AppendLine($" && duk_match_param_types(ctx, {args}, argc, {GetParamArrayMatchType(method)}))"); + cg.cs.AppendLine("{"); + cg.cs.AddTabLevel(); + this.WriteCSMethodBinding(method, argc, true); + cg.cs.DecTabLevel(); + cg.cs.AppendLine("}"); + } + } + if (gecheck) + { + cg.cs.DecTabLevel(); + cg.cs.AppendLine("}"); + } + } + } + cg.cs.DecTabLevel(); + cg.cs.AppendLine("} while(false);"); + var error = this.cg.bindingManager.GetDuktapeGenericError("no matched method variant"); + cg.cs.AppendLine($"return {error}"); + } + + protected List WriteTSDeclaration(T method, MethodBaseBindingInfo bindingInfo) + { + var isExtension = method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute)); + var refParameters = new List(); + string tsMethodDeclaration; + if (this.cg.bindingManager.GetTSMethodDeclaration(method, out tsMethodDeclaration)) + { + this.cg.tsDeclare.AppendLine(tsMethodDeclaration); + return refParameters; + } + //TODO: 需要处理参数类型归并问题, 因为如果类型没有导入 ts 中, 可能会在声明中出现相同参数列表的定义 + // 在 MethodVariant 中创建每个方法对应的TS类型名参数列表, 完全相同的不再输出 + this.cg.AppendJSDoc(method); + var prefix = ""; + if (method.IsStatic && !isExtension) + { + prefix = "static "; + } + string tsMethodRename; + if (this.cg.bindingManager.GetTSMethodRename(method, out tsMethodRename)) + { + this.cg.tsDeclare.Append($"{prefix}{tsMethodRename}("); + } + else + { + this.cg.tsDeclare.Append($"{prefix}{bindingInfo.regName}("); + } + var parameters = method.GetParameters(); + if (isExtension) + { + ArrayUtility.RemoveAt(ref parameters, 0); + } + for (var i = 0; i < parameters.Length; i++) + { + var parameter = parameters[i]; + var parameter_prefix = ""; + var parameterType = parameter.ParameterType; + if (parameter.IsOut && parameterType.IsByRef) + { + // parameter_prefix = "/*out*/ "; + refParameters.Add(parameter); + } + else if (parameterType.IsByRef) + { + // parameter_prefix = "/*ref*/ "; + refParameters.Add(parameter); + } + if (parameter.IsDefined(typeof(ParamArrayAttribute), false) && i == parameters.Length - 1) + { + var elementType = parameterType.GetElementType(); + var elementTS = this.cg.bindingManager.GetTSTypeFullName(elementType); + var parameterVarName = BindingManager.GetTSVariable(parameter.Name); + this.cg.tsDeclare.AppendL($"{parameter_prefix}...{parameterVarName}: {elementTS}[]"); + } + else + { + var parameterTS = this.cg.bindingManager.GetTSTypeFullName(parameterType, parameter.IsOut); + var parameterVarName = BindingManager.GetTSVariable(parameter.Name); + this.cg.tsDeclare.AppendL($"{parameter_prefix}{parameterVarName}: {parameterTS}"); + } + if (i != parameters.Length - 1) + { + this.cg.tsDeclare.AppendL(", "); + } + } + this.cg.tsDeclare.AppendL($")"); + WriteTSReturn(method, refParameters); + return refParameters; + } + + // 获取返回值类型 + protected abstract Type GetReturnType(T method); + + // 获取方法调用 + protected abstract string GetInvokeBinding(string caller, T method, bool hasParams, bool isExtension, string nargs, ParameterInfo[] parameters, List parametersByRef); + + protected virtual void BeginInvokeBinding() { } + + protected virtual void EndInvokeBinding() { } + + // 写入绑定代码 + protected void WriteCSMethodBinding(T method, string argc, bool isVararg) + { + var parameters = method.GetParameters(); + var isExtension = method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute)); + if (isExtension) + { + ArrayUtility.RemoveAt(ref parameters, 0); + } + var parametersByRef = new List(); + var caller = this.cg.AppendGetThisCS(method); + var returnType = GetReturnType(method); + + if (returnType == null || returnType == typeof(void)) + { + // 方法本身没有返回值 + this.BeginInvokeBinding(); + cg.cs.AppendLine($"{this.GetInvokeBinding(caller, method, isVararg, isExtension, argc, parameters, parametersByRef)};"); + this.EndInvokeBinding(); + if (parametersByRef.Count > 0) + { + _WriteBackParametersByRef(isExtension, parametersByRef); + + } + if (!method.IsStatic && method.DeclaringType.IsValueType) // struct 非静态方法 检查 Mutable 属性 + { + if (!string.IsNullOrEmpty(caller)) + { + cg.cs.AppendLine($"duk_rebind_this(ctx, {caller});"); + } + } + cg.cs.AppendLine("return 0;"); + } + else + { + // 方法本身有返回值 + this.BeginInvokeBinding(); + cg.cs.AppendLine($"var ret = {this.GetInvokeBinding(caller, method, isVararg, isExtension, argc, parameters, parametersByRef)};"); + this.EndInvokeBinding(); + if (parametersByRef.Count > 0) + { + _WriteBackParametersByRef(isExtension, parametersByRef); + } + cg.AppendPushValue(returnType, "ret"); + cg.cs.AppendLine("return 1;"); + } + } + + // 回填 ref/out 参数 + //TODO: !!! 扩展方法参数索引需要偏移 + protected void _WriteBackParametersByRef(bool isExtension, List parametersByRef) + { + for (var i = 0; i < parametersByRef.Count; i++) + { + var parameter = parametersByRef[i]; + var position = isExtension ? parameter.Position - 1 : parameter.Position; + cg.cs.AppendLine($"if (!DuktapeDLL.duk_is_null_or_undefined(ctx, {position}))"); + cg.cs.AppendLine("{"); + cg.cs.AddTabLevel(); + var argname = $"arg{position}"; + cg.AppendPushValue(parameter.ParameterType, argname); + cg.cs.AppendLine($"DuktapeDLL.duk_unity_put_target_i(ctx, {position});"); + cg.cs.DecTabLevel(); + cg.cs.AppendLine("}"); + } + } + } + + public class ConstructorCodeGen : MethodBaseCodeGen + { + private ConstructorBindingInfo bindingInfo; + + protected override Type GetReturnType(ConstructorInfo method) + { + return null; + } + + protected override string GetInvokeBinding(string caller, ConstructorInfo method, bool hasParams, bool isExtension, string nargs, ParameterInfo[] parameters, List parametersByRef) + { + var arglist = this.AppendGetParameters(hasParams, nargs, parameters, parametersByRef); + var decalringTypeName = this.cg.bindingManager.GetCSTypeFullName(this.bindingInfo.decalringType); + return $"var o = new {decalringTypeName}({arglist})"; + } + + protected override void EndInvokeBinding() + { + this.cg.cs.AppendLine("duk_bind_native(ctx, o);"); + } + + public ConstructorCodeGen(CodeGenerator cg, TypeBindingInfo bindingInfo) + : base(cg) + { + // WriteInstanceEvents(bindingInfo); + this.bindingInfo = bindingInfo.constructors; + if (this.bindingInfo.count > 0) + { + WriteAllVariants(this.bindingInfo); + WriteTSAllVariants(this.bindingInfo); + } + else + { + WriteDefaultConstructorBinding(); + } + } + + // private void WriteInstanceEvents(TypeBindingInfo bindingInfo) + // { + // var eventBindingInfos = new List(); + // foreach (var kv in bindingInfo.events) + // { + // var eventBindingInfo = kv.Value; + // var bStatic = eventBindingInfo.isStatic; + // if (!bStatic) + // { + // eventBindingInfos.Add(eventBindingInfo); + // } + // } + // if (eventBindingInfos.Count > 0) + // { + // // Debug.Log($"Writing instance events... {bindingInfo.type}"); + // this.cg.cs.AppendLine("DuktapeDLL.duk_push_this(ctx);"); + // foreach (var eventBindingInfo in eventBindingInfos) + // { + // var tsFieldVar = BindingManager.GetTSVariable(eventBindingInfo.regName); + // cg.cs.AppendLine($"duk_add_event(ctx, \"{tsFieldVar}\", {eventBindingInfo.adderName}, {eventBindingInfo.removerName}, -1);"); + // } + // this.cg.cs.AppendLine("DuktapeDLL.duk_pop(ctx);"); + // } + // } + + // 写入默认构造函数 (struct 无参构造) + private void WriteDefaultConstructorBinding() + { + var decalringTypeName = this.cg.bindingManager.GetCSTypeFullName(this.bindingInfo.decalringType); + this.cg.cs.AppendLine($"var o = new {decalringTypeName}();"); + this.cg.cs.AppendLine("duk_bind_native(ctx, o);"); + this.cg.cs.AppendLine("return 0;"); + + this.cg.tsDeclare.AppendLine($"{this.bindingInfo.regName}()"); + } + } + + // 生成成员方法绑定代码 + public class MethodCodeGen : MethodBaseCodeGen + { + protected MethodBindingInfo bindingInfo; + + protected override Type GetReturnType(MethodInfo method) + { + return method.ReturnType; + } + + protected override string GetInvokeBinding(string caller, MethodInfo method, bool hasParams, bool isExtension, string nargs, ParameterInfo[] parameters, List parametersByRef) + { + if (bindingInfo.isIndexer) + { + if (method.ReturnType == typeof(void)) + { + var last = parameters.Length - 1; + var arglist_t = ""; + for (var i = 0; i < last; i++) + { + var argname = $"arg{i}"; + this.WriteParameterGetter(parameters[i], i, argname); + arglist_t += argname; + if (i != last - 1) + { + arglist_t += ", "; + } + } + var argname_last = $"arg{last}"; + this.WriteParameterGetter(parameters[last], last, argname_last); + return $"{caller}[{arglist_t}] = {argname_last}"; // setter + } + else + { + var last = parameters.Length; + var arglist_t = ""; + for (var i = 0; i < last; i++) + { + var argname = $"arg{i}"; + this.WriteParameterGetter(parameters[i], i, argname); + arglist_t += argname; + if (i != last - 1) + { + arglist_t += ", "; + } + } + return $"{caller}[{arglist_t}]"; // getter + } + } + var arglist = this.AppendGetParameters(hasParams, nargs, parameters, parametersByRef); + if (isExtension) + { + var methodDeclType = this.cg.bindingManager.GetCSTypeFullName(method.DeclaringType); + if (arglist.Length > 0) + { + arglist = ", " + arglist; + } + return $"{methodDeclType}.{method.Name}({caller}{arglist})"; + } + return $"{caller}.{method.Name}({arglist})"; + } + + public MethodCodeGen(CodeGenerator cg, MethodBindingInfo bindingInfo) + : base(cg) + { + this.bindingInfo = bindingInfo; + WriteAllVariants(this.bindingInfo); + // WriteTSAllVariants(this.bindingInfo); + } + } + + public class TSMethodCodeGen : MethodBaseCodeGen + { + protected MethodBindingInfo bindingInfo; + + protected override Type GetReturnType(MethodInfo method) + { + return method.ReturnType; + } + + protected override string GetInvokeBinding(string caller, MethodInfo method, bool hasParams, bool isExtension, string nargs, ParameterInfo[] parameters, List parametersByRef) + { + return null; + } + + public TSMethodCodeGen(CodeGenerator cg, MethodBindingInfo bindingInfo) + : base(cg) + { + this.bindingInfo = bindingInfo; + WriteTSAllVariants(this.bindingInfo); + } + } +} diff --git a/unity/Assets/Duktape/Editor/IBindingProcess.cs b/unity/Assets/Duktape/Editor/IBindingProcess.cs index 94eb7d5..f120027 100644 --- a/unity/Assets/Duktape/Editor/IBindingProcess.cs +++ b/unity/Assets/Duktape/Editor/IBindingProcess.cs @@ -1,51 +1,51 @@ -using System; -using System.IO; -using System.Collections.Generic; -using System.Reflection; - -namespace Duktape -{ - using UnityEngine; - using UnityEditor; - - // 继承该接口(或 AbstractBindingProcess)的类将在导出过程中执行指定方法, 可以在特定阶段进行特定操作 - // NOTE: AddAssemblies 等基本配置操作可以通过 ProjectSettings/duktape.json 进行配置, 不需要使用此接口方式 - // 可配置项参考 Prefs.cs - public interface IBindingProcess - { - // 初始化阶段回调, 可以调用 AddTSMethodDeclaration, AddTSKeywords 等进行定制 - void OnInitialize(BindingManager bindingManager); - - // 收集 Assembly 阶段, 可在该阶段 AddAssemblies/RemoveAssemblies - void OnPreCollectAssemblies(BindingManager bindingManager); - - // - void OnPostCollectAssemblies(BindingManager bindingManager); - - void OnPostExporting(BindingManager bindingManager); - - // 收集类型阶段开始, 可在该阶段 AddExportedType 增加导出类型 - void OnPreCollectTypes(BindingManager bindingManager); - - // - void OnPostCollectTypes(BindingManager bindingManager); - - // 是否要导出指定类型 - bool OnExportingType(BindingManager bindingManager, Type type); - - // 生成指定类型绑定代码前 - void OnPreGenerateType(BindingManager bindingManager, TypeBindingInfo bindingInfo); - - // 生成指定类型绑定代码后 - void OnPostGenerateType(BindingManager bindingManager, TypeBindingInfo bindingInfo); - - // 生成指定Delegate类型的绑定代码前 - void OnPreGenerateDelegate(BindingManager bindingManager, DelegateBindingInfo bindingInfo); - - // 生成指定Delegate类型的绑定代码后 - void OnPostGenerateDelegate(BindingManager bindingManager, DelegateBindingInfo bindingInfo); - - // 完成默认清理行为后 - void OnCleanup(BindingManager bindingManager); - } -} +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; + +namespace Duktape +{ + using UnityEngine; + using UnityEditor; + + // 继承该接口(或 AbstractBindingProcess)的类将在导出过程中执行指定方法, 可以在特定阶段进行特定操作 + // NOTE: AddAssemblies 等基本配置操作可以通过 ProjectSettings/duktape.json 进行配置, 不需要使用此接口方式 + // 可配置项参考 Prefs.cs + public interface IBindingProcess + { + // 初始化阶段回调, 可以调用 AddTSMethodDeclaration, AddTSKeywords 等进行定制 + void OnInitialize(BindingManager bindingManager); + + // 收集 Assembly 阶段, 可在该阶段 AddAssemblies/RemoveAssemblies + void OnPreCollectAssemblies(BindingManager bindingManager); + + // + void OnPostCollectAssemblies(BindingManager bindingManager); + + void OnPostExporting(BindingManager bindingManager); + + // 收集类型阶段开始, 可在该阶段 AddExportedType 增加导出类型 + void OnPreCollectTypes(BindingManager bindingManager); + + // + void OnPostCollectTypes(BindingManager bindingManager); + + // 是否要导出指定类型 + bool OnExportingType(BindingManager bindingManager, Type type); + + // 生成指定类型绑定代码前 + void OnPreGenerateType(BindingManager bindingManager, TypeBindingInfo bindingInfo); + + // 生成指定类型绑定代码后 + void OnPostGenerateType(BindingManager bindingManager, TypeBindingInfo bindingInfo); + + // 生成指定Delegate类型的绑定代码前 + void OnPreGenerateDelegate(BindingManager bindingManager, DelegateBindingInfo bindingInfo); + + // 生成指定Delegate类型的绑定代码后 + void OnPostGenerateDelegate(BindingManager bindingManager, DelegateBindingInfo bindingInfo); + + // 完成默认清理行为后 + void OnCleanup(BindingManager bindingManager); + } +} diff --git a/unity/Assets/Duktape/Editor/PrefsEditor.cs b/unity/Assets/Duktape/Editor/PrefsEditor.cs index f0ad039..900fb81 100644 --- a/unity/Assets/Duktape/Editor/PrefsEditor.cs +++ b/unity/Assets/Duktape/Editor/PrefsEditor.cs @@ -1,28 +1,28 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace Duktape -{ - using UnityEngine; - using UnityEditor; - - // 配置编辑器 - public class PrefsEditor : EditorWindow - { - void OnGUI() - { - if (GUILayout.Button("Generate")) - { - UnityHelper.GenerateBindings(); - } - if(GUILayout.Button("Clear")) - { - UnityHelper.ClearBindings(); - } - } - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Duktape +{ + using UnityEngine; + using UnityEditor; + + // 配置编辑器 + public class PrefsEditor : EditorWindow + { + void OnGUI() + { + if (GUILayout.Button("Generate")) + { + UnityHelper.GenerateBindings(); + } + if(GUILayout.Button("Clear")) + { + UnityHelper.ClearBindings(); + } + } + } } \ No newline at end of file diff --git a/unity/Assets/Duktape/Source/DuktapeArray.cs b/unity/Assets/Duktape/Source/DuktapeArray.cs index 51dbdd4..7ce0b70 100644 --- a/unity/Assets/Duktape/Source/DuktapeArray.cs +++ b/unity/Assets/Duktape/Source/DuktapeArray.cs @@ -1,124 +1,124 @@ -using System; - -namespace Duktape -{ - using UnityEngine; - - public class DuktapeArray : DuktapeValue - { - public DuktapeArray(DuktapeContext context, uint refid) - : base(context, refid) - { - } - - public DuktapeArray(IntPtr ctx, uint refid) - : base(ctx, refid) - { - } - - public int length - { - get - { - var ctx = _context.rawValue; - this.Push(ctx); - var length_ = DuktapeDLL.duk_unity_get_length(ctx, -1); - DuktapeDLL.duk_pop(ctx); - return (int)length_; - } - } - - public void SetIntValue(int index, int value) - { - var ctx = _context.rawValue; - this.Push(ctx); - DuktapeDLL.duk_push_int(ctx, value); - DuktapeDLL.duk_put_prop_index(ctx, -2, (uint)index); - DuktapeDLL.duk_pop(ctx); - } - - public void SetFloatValue(int index, float value) - { - var ctx = _context.rawValue; - this.Push(ctx); - DuktapeDLL.duk_push_number(ctx, value); - DuktapeDLL.duk_put_prop_index(ctx, -2, (uint)index); - DuktapeDLL.duk_pop(ctx); - } - - public void SetDoubleValue(int index, double value) - { - var ctx = _context.rawValue; - this.Push(ctx); - DuktapeDLL.duk_push_number(ctx, value); - DuktapeDLL.duk_put_prop_index(ctx, -2, (uint)index); - DuktapeDLL.duk_pop(ctx); - } - - public void SetStringValue(int index, string value) - { - var ctx = _context.rawValue; - this.Push(ctx); - DuktapeDLL.duk_push_string(ctx, value); - DuktapeDLL.duk_put_prop_index(ctx, -2, (uint)index); - DuktapeDLL.duk_pop(ctx); - } - - // - public int GetIntValue(int index) - { - var ctx = _context.rawValue; - this.Push(ctx); - DuktapeDLL.duk_get_prop_index(ctx, -1, (uint)index); - var res = 0; - if (DuktapeDLL.duk_is_number(ctx, -1)) - { - res = DuktapeDLL.duk_get_int(ctx, -1); - } - DuktapeDLL.duk_pop_2(ctx); - return res; - } - - public float GetFloatValue(int index) - { - var ctx = _context.rawValue; - this.Push(ctx); - DuktapeDLL.duk_get_prop_index(ctx, -1, (uint)index); - var res = 0f; - if (DuktapeDLL.duk_is_number(ctx, -1)) - { - res = (float)DuktapeDLL.duk_get_number(ctx, -1); - } - DuktapeDLL.duk_pop_2(ctx); - return res; - } - - public double GetDoubleValue(int index) - { - var ctx = _context.rawValue; - this.Push(ctx); - DuktapeDLL.duk_get_prop_index(ctx, -1, (uint)index); - var res = 0.0; - if (DuktapeDLL.duk_is_number(ctx, -1)) - { - res = DuktapeDLL.duk_get_number(ctx, -1); - } - DuktapeDLL.duk_pop_2(ctx); - return res; - } - - public string GetStringValue(int index) - { - var ctx = _context.rawValue; - this.Push(ctx); - DuktapeDLL.duk_get_prop_index(ctx, -1, (uint)index); - string res = null; - if (DuktapeDLL.duk_is_string(ctx, -1)) - { - res = DuktapeDLL.duk_get_string(ctx, -1); - } - DuktapeDLL.duk_pop_2(ctx); - return res; - } - } -} +using System; + +namespace Duktape +{ + using UnityEngine; + + public class DuktapeArray : DuktapeValue + { + public DuktapeArray(DuktapeContext context, uint refid) + : base(context, refid) + { + } + + public DuktapeArray(IntPtr ctx, uint refid) + : base(ctx, refid) + { + } + + public int length + { + get + { + var ctx = _context.rawValue; + this.Push(ctx); + var length_ = DuktapeDLL.duk_unity_get_length(ctx, -1); + DuktapeDLL.duk_pop(ctx); + return (int)length_; + } + } + + public void SetIntValue(int index, int value) + { + var ctx = _context.rawValue; + this.Push(ctx); + DuktapeDLL.duk_push_int(ctx, value); + DuktapeDLL.duk_put_prop_index(ctx, -2, (uint)index); + DuktapeDLL.duk_pop(ctx); + } + + public void SetFloatValue(int index, float value) + { + var ctx = _context.rawValue; + this.Push(ctx); + DuktapeDLL.duk_push_number(ctx, value); + DuktapeDLL.duk_put_prop_index(ctx, -2, (uint)index); + DuktapeDLL.duk_pop(ctx); + } + + public void SetDoubleValue(int index, double value) + { + var ctx = _context.rawValue; + this.Push(ctx); + DuktapeDLL.duk_push_number(ctx, value); + DuktapeDLL.duk_put_prop_index(ctx, -2, (uint)index); + DuktapeDLL.duk_pop(ctx); + } + + public void SetStringValue(int index, string value) + { + var ctx = _context.rawValue; + this.Push(ctx); + DuktapeDLL.duk_push_string(ctx, value); + DuktapeDLL.duk_put_prop_index(ctx, -2, (uint)index); + DuktapeDLL.duk_pop(ctx); + } + + // + public int GetIntValue(int index) + { + var ctx = _context.rawValue; + this.Push(ctx); + DuktapeDLL.duk_get_prop_index(ctx, -1, (uint)index); + var res = 0; + if (DuktapeDLL.duk_is_number(ctx, -1)) + { + res = DuktapeDLL.duk_get_int(ctx, -1); + } + DuktapeDLL.duk_pop_2(ctx); + return res; + } + + public float GetFloatValue(int index) + { + var ctx = _context.rawValue; + this.Push(ctx); + DuktapeDLL.duk_get_prop_index(ctx, -1, (uint)index); + var res = 0f; + if (DuktapeDLL.duk_is_number(ctx, -1)) + { + res = (float)DuktapeDLL.duk_get_number(ctx, -1); + } + DuktapeDLL.duk_pop_2(ctx); + return res; + } + + public double GetDoubleValue(int index) + { + var ctx = _context.rawValue; + this.Push(ctx); + DuktapeDLL.duk_get_prop_index(ctx, -1, (uint)index); + var res = 0.0; + if (DuktapeDLL.duk_is_number(ctx, -1)) + { + res = DuktapeDLL.duk_get_number(ctx, -1); + } + DuktapeDLL.duk_pop_2(ctx); + return res; + } + + public string GetStringValue(int index) + { + var ctx = _context.rawValue; + this.Push(ctx); + DuktapeDLL.duk_get_prop_index(ctx, -1, (uint)index); + string res = null; + if (DuktapeDLL.duk_is_string(ctx, -1)) + { + res = DuktapeDLL.duk_get_string(ctx, -1); + } + DuktapeDLL.duk_pop_2(ctx); + return res; + } + } +} diff --git a/unity/Assets/Duktape/Source/DuktapeBinding_delegate.cs b/unity/Assets/Duktape/Source/DuktapeBinding_delegate.cs index 7b96179..a0248de 100644 --- a/unity/Assets/Duktape/Source/DuktapeBinding_delegate.cs +++ b/unity/Assets/Duktape/Source/DuktapeBinding_delegate.cs @@ -1,76 +1,76 @@ -using System; -using System.Collections.Generic; -using AOT; - -namespace Duktape -{ - using UnityEngine; - - // 处理委托的绑定 - public partial class DuktapeBinding - { - //TODO: 尝试还原 js function/dispatcher - public static void duk_push_delegate(IntPtr ctx, Delegate o) - { - //TODO: delegate push - duk_push_object(ctx, (object)o); - } - - public static bool duk_get_delegate_array(IntPtr ctx, int idx, out T[] o) - where T : class - { - if (DuktapeDLL.duk_is_array(ctx, idx)) - { - var length = DuktapeDLL.duk_unity_get_length(ctx, idx); - var nidx = DuktapeDLL.duk_normalize_index(ctx, idx); - o = new T[length]; - for (var i = 0U; i < length; i++) - { - DuktapeDLL.duk_get_prop_index(ctx, idx, i); - T e; - duk_get_delegate(ctx, -1, out e); - o[i] = e; - DuktapeDLL.duk_pop(ctx); - } - return true; - } - duk_get_classvalue(ctx, idx, out o); - return true; - } - - public static bool duk_get_delegate(IntPtr ctx, int idx, out T o) - where T : class - { - if (DuktapeDLL.duk_is_object(ctx, idx)/* && check if js delegate type (hidden property) */ - || DuktapeDLL.duk_is_function(ctx, idx)) - { - DuktapeDLL.duk_get_prop_string(ctx, idx, DuktapeVM.OBJ_PROP_NATIVE_WEAK); - var refid = DuktapeDLL.duk_get_int(ctx, -1); - DuktapeDLL.duk_pop(ctx); - - var cache = DuktapeVM.GetObjectCache(ctx); - DuktapeDelegate fn; - if (cache.TryGetTypedWeakObject(refid, out fn) && fn != null) - { - o = fn.target as T; - return true; - } - // 默认赋值操作 - DuktapeDLL.duk_dup(ctx, idx); - var heapptr = DuktapeDLL.duk_get_heapptr(ctx, idx); - fn = new DuktapeDelegate(ctx, DuktapeDLL.duk_unity_ref(ctx)); - var vm = DuktapeVM.GetVM(ctx); - o = vm.CreateDelegate(typeof(T), fn) as T; - - // DuktapeDelegate 拥有 js 对象的强引用, 此 js 对象无法释放 cache 中的 object, 所以这里用弱引用注册 - // 会出现的问题是, 如果 c# 没有对 DuktapeDelegate 的强引用, 那么反复 get_delegate 会重复创建 DuktapeDelegate - refid = cache.AddWeakObject(fn); - DuktapeDLL.duk_unity_set_prop_i(ctx, idx, DuktapeVM.OBJ_PROP_NATIVE_WEAK, refid); - cache.AddJSValue(o, heapptr); - return true; - } - o = null; - return false; - } - } -} +using System; +using System.Collections.Generic; +using AOT; + +namespace Duktape +{ + using UnityEngine; + + // 处理委托的绑定 + public partial class DuktapeBinding + { + //TODO: 尝试还原 js function/dispatcher + public static void duk_push_delegate(IntPtr ctx, Delegate o) + { + //TODO: delegate push + duk_push_object(ctx, (object)o); + } + + public static bool duk_get_delegate_array(IntPtr ctx, int idx, out T[] o) + where T : class + { + if (DuktapeDLL.duk_is_array(ctx, idx)) + { + var length = DuktapeDLL.duk_unity_get_length(ctx, idx); + var nidx = DuktapeDLL.duk_normalize_index(ctx, idx); + o = new T[length]; + for (var i = 0U; i < length; i++) + { + DuktapeDLL.duk_get_prop_index(ctx, idx, i); + T e; + duk_get_delegate(ctx, -1, out e); + o[i] = e; + DuktapeDLL.duk_pop(ctx); + } + return true; + } + duk_get_classvalue(ctx, idx, out o); + return true; + } + + public static bool duk_get_delegate(IntPtr ctx, int idx, out T o) + where T : class + { + if (DuktapeDLL.duk_is_object(ctx, idx)/* && check if js delegate type (hidden property) */ + || DuktapeDLL.duk_is_function(ctx, idx)) + { + DuktapeDLL.duk_get_prop_string(ctx, idx, DuktapeVM.OBJ_PROP_NATIVE_WEAK); + var refid = DuktapeDLL.duk_get_int(ctx, -1); + DuktapeDLL.duk_pop(ctx); + + var cache = DuktapeVM.GetObjectCache(ctx); + DuktapeDelegate fn; + if (cache.TryGetTypedWeakObject(refid, out fn) && fn != null) + { + o = fn.target as T; + return true; + } + // 默认赋值操作 + DuktapeDLL.duk_dup(ctx, idx); + var heapptr = DuktapeDLL.duk_get_heapptr(ctx, idx); + fn = new DuktapeDelegate(ctx, DuktapeDLL.duk_unity_ref(ctx)); + var vm = DuktapeVM.GetVM(ctx); + o = vm.CreateDelegate(typeof(T), fn) as T; + + // DuktapeDelegate 拥有 js 对象的强引用, 此 js 对象无法释放 cache 中的 object, 所以这里用弱引用注册 + // 会出现的问题是, 如果 c# 没有对 DuktapeDelegate 的强引用, 那么反复 get_delegate 会重复创建 DuktapeDelegate + refid = cache.AddWeakObject(fn); + DuktapeDLL.duk_unity_set_prop_i(ctx, idx, DuktapeVM.OBJ_PROP_NATIVE_WEAK, refid); + cache.AddJSValue(o, heapptr); + return true; + } + o = null; + return false; + } + } +} diff --git a/unity/Assets/Duktape/Source/DuktapeBinding_match.cs b/unity/Assets/Duktape/Source/DuktapeBinding_match.cs index 7232bec..c90078b 100644 --- a/unity/Assets/Duktape/Source/DuktapeBinding_match.cs +++ b/unity/Assets/Duktape/Source/DuktapeBinding_match.cs @@ -1,143 +1,143 @@ -using System; -using System.Collections.Generic; -using AOT; - -namespace Duktape -{ - using UnityEngine; - - // 处理类型匹配 - public partial class DuktapeBinding - { - protected static HashSet _assignableFromArray = new HashSet(); - - static DuktapeBinding() - { - _assignableFromArray.Add(typeof(LayerMask)); - _assignableFromArray.Add(typeof(Color)); - _assignableFromArray.Add(typeof(Color32)); - _assignableFromArray.Add(typeof(Vector2)); - _assignableFromArray.Add(typeof(Vector2Int)); - _assignableFromArray.Add(typeof(Vector3)); - _assignableFromArray.Add(typeof(Vector3Int)); - _assignableFromArray.Add(typeof(Vector4)); - _assignableFromArray.Add(typeof(Quaternion)); - // _assignableFromArray.Add(typeof(Matrix4x4)); - } - - protected static bool duk_match_type(IntPtr ctx, int idx, Type type) - { - if (type == null) - { - return true; - } - if (type == typeof(object)) - { - return true; - } - if (type == typeof(Type)) - { - Type otype; - return duk_get_type(ctx, idx, out otype); // 只要求匹配 Type 本身, 不比较具体 Type - // return otype == type; - } - var jstype = DuktapeDLL.duk_get_type(ctx, idx); - switch (jstype) - { - // case duk_type_t.DUK_TYPE_NONE: - case duk_type_t.DUK_TYPE_OBJECT: // objects, arrays, functions, threads - if (DuktapeDLL.duk_is_array(ctx, idx)) - { - if (!type.IsArray && !_assignableFromArray.Contains(type)) - { - return false; - } - } - else if (DuktapeDLL.duk_is_function(ctx, idx)) - { - //TODO: 完善处理 delegate - return type == typeof(DuktapeFunction) || type.BaseType == typeof(MulticastDelegate); - } - else if (DuktapeDLL.duk_is_thread(ctx, idx)) - { - return false; - } - int refid; - //TODO: 根据记录在jsobject 上的 分类标记 做进一步分支 (类型, 实例, 或特定优化类型) - if (duk_get_native_refid(ctx, idx, out refid)) - { - var cache = DuktapeVM.GetObjectCache(ctx); - return cache.MatchObjectType(refid, type); - } - return true; - case duk_type_t.DUK_TYPE_NUMBER: - return type.IsPrimitive || type.IsEnum; - case duk_type_t.DUK_TYPE_STRING: - return type == typeof(string); - case duk_type_t.DUK_TYPE_UNDEFINED: - case duk_type_t.DUK_TYPE_NULL: - return !type.IsValueType && !type.IsPrimitive; - case duk_type_t.DUK_TYPE_BOOLEAN: - return type == typeof(bool); - case duk_type_t.DUK_TYPE_BUFFER: - return type == typeof(byte[]) || type == typeof(sbyte[]) /* || type == typeof(DuktapeBuffer) */; - case duk_type_t.DUK_TYPE_POINTER: - // return type == typeof(DuktapePointer); - case duk_type_t.DUK_TYPE_LIGHTFUNC: - default: - return false; - } - } - - // 检查变参参数 - // offset: 从偏移处开始为变参 - protected static bool duk_match_param_types(IntPtr ctx, int offset, int nargs, Type type) - { - for (var i = offset; i < nargs; i++) - { - if (!duk_match_type(ctx, i, type)) - { - return false; - } - } - return true; - } - - protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0) - { - return duk_match_type(ctx, 0, t0); - } - - protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0, Type t1) - { - return duk_match_type(ctx, 0, t0) && duk_match_type(ctx, 1, t1); - } - - protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0, Type t1, Type t2) - { - return duk_match_type(ctx, 0, t0) && duk_match_type(ctx, 1, t1) && duk_match_type(ctx, 2, t2); - } - - protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0, Type t1, Type t2, Type t3) - { - return duk_match_type(ctx, 0, t0) && duk_match_type(ctx, 1, t1) && duk_match_type(ctx, 2, t2) && duk_match_type(ctx, 3, t3); - } - - protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0, Type t1, Type t2, Type t3, Type t4) - { - return duk_match_type(ctx, 0, t0) && duk_match_type(ctx, 1, t1) && duk_match_type(ctx, 2, t2) && duk_match_type(ctx, 3, t3) && duk_match_type(ctx, 4, t4); - } - - protected static bool duk_match_types(IntPtr ctx, int nargs, params Type[] types) - { - for (int i = 0, size = types.Length; i < size; i++) - { - if (!duk_match_type(ctx, i, types[i])) - { - return false; - } - } - return true; - } - } -} +using System; +using System.Collections.Generic; +using AOT; + +namespace Duktape +{ + using UnityEngine; + + // 处理类型匹配 + public partial class DuktapeBinding + { + protected static HashSet _assignableFromArray = new HashSet(); + + static DuktapeBinding() + { + _assignableFromArray.Add(typeof(LayerMask)); + _assignableFromArray.Add(typeof(Color)); + _assignableFromArray.Add(typeof(Color32)); + _assignableFromArray.Add(typeof(Vector2)); + _assignableFromArray.Add(typeof(Vector2Int)); + _assignableFromArray.Add(typeof(Vector3)); + _assignableFromArray.Add(typeof(Vector3Int)); + _assignableFromArray.Add(typeof(Vector4)); + _assignableFromArray.Add(typeof(Quaternion)); + // _assignableFromArray.Add(typeof(Matrix4x4)); + } + + protected static bool duk_match_type(IntPtr ctx, int idx, Type type) + { + if (type == null) + { + return true; + } + if (type == typeof(object)) + { + return true; + } + if (type == typeof(Type)) + { + Type otype; + return duk_get_type(ctx, idx, out otype); // 只要求匹配 Type 本身, 不比较具体 Type + // return otype == type; + } + var jstype = DuktapeDLL.duk_get_type(ctx, idx); + switch (jstype) + { + // case duk_type_t.DUK_TYPE_NONE: + case duk_type_t.DUK_TYPE_OBJECT: // objects, arrays, functions, threads + if (DuktapeDLL.duk_is_array(ctx, idx)) + { + if (!type.IsArray && !_assignableFromArray.Contains(type)) + { + return false; + } + } + else if (DuktapeDLL.duk_is_function(ctx, idx)) + { + //TODO: 完善处理 delegate + return type == typeof(DuktapeFunction) || type.BaseType == typeof(MulticastDelegate); + } + else if (DuktapeDLL.duk_is_thread(ctx, idx)) + { + return false; + } + int refid; + //TODO: 根据记录在jsobject 上的 分类标记 做进一步分支 (类型, 实例, 或特定优化类型) + if (duk_get_native_refid(ctx, idx, out refid)) + { + var cache = DuktapeVM.GetObjectCache(ctx); + return cache.MatchObjectType(refid, type); + } + return true; + case duk_type_t.DUK_TYPE_NUMBER: + return type.IsPrimitive || type.IsEnum; + case duk_type_t.DUK_TYPE_STRING: + return type == typeof(string); + case duk_type_t.DUK_TYPE_UNDEFINED: + case duk_type_t.DUK_TYPE_NULL: + return !type.IsValueType && !type.IsPrimitive; + case duk_type_t.DUK_TYPE_BOOLEAN: + return type == typeof(bool); + case duk_type_t.DUK_TYPE_BUFFER: + return type == typeof(byte[]) || type == typeof(sbyte[]) /* || type == typeof(DuktapeBuffer) */; + case duk_type_t.DUK_TYPE_POINTER: + // return type == typeof(DuktapePointer); + case duk_type_t.DUK_TYPE_LIGHTFUNC: + default: + return false; + } + } + + // 检查变参参数 + // offset: 从偏移处开始为变参 + protected static bool duk_match_param_types(IntPtr ctx, int offset, int nargs, Type type) + { + for (var i = offset; i < nargs; i++) + { + if (!duk_match_type(ctx, i, type)) + { + return false; + } + } + return true; + } + + protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0) + { + return duk_match_type(ctx, 0, t0); + } + + protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0, Type t1) + { + return duk_match_type(ctx, 0, t0) && duk_match_type(ctx, 1, t1); + } + + protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0, Type t1, Type t2) + { + return duk_match_type(ctx, 0, t0) && duk_match_type(ctx, 1, t1) && duk_match_type(ctx, 2, t2); + } + + protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0, Type t1, Type t2, Type t3) + { + return duk_match_type(ctx, 0, t0) && duk_match_type(ctx, 1, t1) && duk_match_type(ctx, 2, t2) && duk_match_type(ctx, 3, t3); + } + + protected static bool duk_match_types(IntPtr ctx, int nargs, Type t0, Type t1, Type t2, Type t3, Type t4) + { + return duk_match_type(ctx, 0, t0) && duk_match_type(ctx, 1, t1) && duk_match_type(ctx, 2, t2) && duk_match_type(ctx, 3, t3) && duk_match_type(ctx, 4, t4); + } + + protected static bool duk_match_types(IntPtr ctx, int nargs, params Type[] types) + { + for (int i = 0, size = types.Length; i < size; i++) + { + if (!duk_match_type(ctx, i, types[i])) + { + return false; + } + } + return true; + } + } +} diff --git a/unity/Assets/Duktape/Source/DuktapeDebugger.cs b/unity/Assets/Duktape/Source/DuktapeDebugger.cs index 1a68ce9..ffd0027 100644 --- a/unity/Assets/Duktape/Source/DuktapeDebugger.cs +++ b/unity/Assets/Duktape/Source/DuktapeDebugger.cs @@ -1,291 +1,291 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using AOT; - -namespace Duktape -{ - using UnityEngine; - - public class DuktapeDebugger - { - private static DuktapeDebugger _instance; - private static byte[] _buffer; - private IntPtr _ctx; - private IntPtr _debugger; - private int _loop; - private Socket _server; - private Socket _client; - private List _pending = new List(); - - private DuktapeDebugger() { } - - public static DuktapeDebugger CreateDebugger(IntPtr ctx) - { - return CreateDebugger(ctx, 9091, 1024); - } - - public static DuktapeDebugger CreateDebugger(IntPtr ctx, int port) - { - return CreateDebugger(ctx, port, 1024); - } - - public static DuktapeDebugger CreateDebugger(IntPtr ctx, int port, int bufferSize) - { - if (_instance != null) - { - throw new Exception("debugger already exists"); - } - _buffer = new byte[bufferSize]; - _instance = new DuktapeDebugger(); - _instance._ctx = ctx; - _instance._debugger = IntPtr.Zero; - _instance.Start(port); - return _instance; - } - - public static void Shutdown() - { - if (_instance != null) - { - _instance.Stop(); - _instance = null; - } - } - - private void Start(int port) - { - Stop(); - _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - _server.Bind(new IPEndPoint(IPAddress.Any, port)); - _server.Listen(1); - _server.BeginAccept(_Accept, _server); - _loop = DuktapeRunner.SetLoop(OnUpdate); - } - - private void _Accept(IAsyncResult ar) - { - try - { - var server = ar.AsyncState as Socket; - var socket = server.EndAccept(ar); - Debug.LogWarningFormat("accept: {0}", socket.RemoteEndPoint); - lock (_pending) - { - if (_pending.Count == 0) - { - socket.NoDelay = true; - _pending.Add(socket); - } - else - { - socket.Close(); - } - } - server.BeginAccept(_Accept, server); - } - catch (Exception) - { - // Debug.LogWarningFormat("debugger server closed: {0}", exception); - } - } - - private void OnUpdate() - { - if (_client == null) - { - lock (_pending) - { - if (_pending.Count > 0) - { - var newClient = _pending[0]; - _pending.RemoveAt(0); - if (newClient.Connected) - { - DetachCurrent(); - Debug.LogFormat("debugger connected: {0}", newClient.RemoteEndPoint); - _client = newClient; - _debugger = DuktapeDLL.duk_unity_attach_debugger(_ctx, - duk_unity_debug_read_function, - duk_unity_debug_write_function, - duk_unity_debug_peek_function, - duk_unity_debug_read_flush_function, - duk_unity_debug_write_flush_function, - duk_unity_debug_request_function, - duk_unity_debug_detached_function, - 0); - } - else - { - Debug.LogWarningFormat("dead connection removed"); - } - } - } - } - else - { - if (!_client.Connected || (_client.Poll(1000, SelectMode.SelectRead) && _client.Available == 0)) - { - Debug.LogError("dead"); - _client.Close(); - _client = null; - } - } - } - - private void DetachCurrent() - { - DuktapeDLL.duk_unity_detach_debugger(_ctx, _debugger); - _debugger = IntPtr.Zero; - } - - private void Stop() - { - lock (_pending) - { - _pending.Clear(); - } - DetachCurrent(); - if (_client != null) - { - _client.Close(); - _client = null; - } - if (_server != null) - { - _server.Close(); - _server = null; - } - DuktapeRunner.Clear(_loop); - _loop = 0; - } - - [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_read_function))] - private static uint duk_unity_debug_read_function(int udata, IntPtr buffer, uint length) - { - // Debug.LogWarning("duk_unity_debug_read_function"); - try - { - if (_instance != null && _instance._client != null) - { - var bufferSize = _buffer.Length; - var size = bufferSize > length ? (int)length : bufferSize; - // Debug.LogWarningFormat("debugger read: {0} {1}", size, length); - var n = _instance._client.Receive(_buffer, size, SocketFlags.None); - // Debug.LogWarningFormat("debugger recv: {0}", n); - if (n > 0) - { - Marshal.Copy(_buffer, 0, buffer, n); - return (uint)n; - } - _instance._client.Close(); - _instance._client = null; - } - } - catch (Exception exception) - { - Debug.LogWarningFormat("debugger connection lost: {0}", exception); - _instance._client = null; - } - return 0; - } - - [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_write_function))] - private static uint duk_unity_debug_write_function(int udata, IntPtr buffer, uint length) - { - // Debug.LogWarning("duk_unity_debug_write_function"); - try - { - if (_instance != null && _instance._client != null) - { - var bufferSize = _buffer.Length; - var size = bufferSize > length ? (int)length : bufferSize; - Marshal.Copy(buffer, _buffer, 0, size); - // Debug.LogWarningFormat("debugger write: {0} {1}", size, length); - var n = _instance._client.Send(_buffer, size, SocketFlags.None); - // Debug.LogWarningFormat("debugger sent: {0}", n); - if (n > 0) - { - return (uint)n; - } - _instance._client.Close(); - _instance._client = null; - } - } - catch (Exception exception) - { - Debug.LogWarningFormat("debugger connection lost: {0}", exception); - _instance._client = null; - } - return 0; - } - - [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_peek_function))] - private static uint duk_unity_debug_peek_function(int udata) - { - Debug.LogWarning("duk_unity_debug_peek_function"); - try - { - if (_instance != null && _instance._client != null) - { - if (_instance._client.Connected) - { - if (_instance._client.Poll(1000, SelectMode.SelectRead)) - { - var n = _instance._client.Available; - Debug.LogWarningFormat("peek available {0}", n); - if (n > 0) - { - return (uint)n; - } - Debug.LogWarningFormat("remote closed"); - } - else if (_instance._client.Poll(1000, SelectMode.SelectError)) - { - Debug.LogWarningFormat("peek error"); - } - else - { - Debug.LogWarningFormat("no data"); - return 0; - } - } - Debug.LogWarningFormat("closing"); - _instance._client.Close(); - _instance._client = null; - } - } - catch (Exception exception) - { - Debug.LogWarningFormat("debugger connection lost: {0}", exception); - _instance._client = null; - } - return 0; - } - - [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_read_flush_function))] - private static void duk_unity_debug_read_flush_function(int udata) - { - } - - [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_write_flush_function))] - private static void duk_unity_debug_write_flush_function(int udata) - { - } - - [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_request_function))] - private static int duk_unity_debug_request_function(IntPtr ctx, int udata, int nvalues) - { - Debug.LogWarningFormat("duk_unity_debug_request_function: {0}", nvalues); - return 0; - } - - [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_detached_function))] - private static void duk_unity_debug_detached_function(IntPtr ctx, int udata) - { - Debug.LogWarningFormat("duk_unity_debug_detached_function"); - } - } +using System; +using System.Net; +using System.Net.Sockets; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using AOT; + +namespace Duktape +{ + using UnityEngine; + + public class DuktapeDebugger + { + private static DuktapeDebugger _instance; + private static byte[] _buffer; + private IntPtr _ctx; + private IntPtr _debugger; + private int _loop; + private Socket _server; + private Socket _client; + private List _pending = new List(); + + private DuktapeDebugger() { } + + public static DuktapeDebugger CreateDebugger(IntPtr ctx) + { + return CreateDebugger(ctx, 9091, 1024); + } + + public static DuktapeDebugger CreateDebugger(IntPtr ctx, int port) + { + return CreateDebugger(ctx, port, 1024); + } + + public static DuktapeDebugger CreateDebugger(IntPtr ctx, int port, int bufferSize) + { + if (_instance != null) + { + throw new Exception("debugger already exists"); + } + _buffer = new byte[bufferSize]; + _instance = new DuktapeDebugger(); + _instance._ctx = ctx; + _instance._debugger = IntPtr.Zero; + _instance.Start(port); + return _instance; + } + + public static void Shutdown() + { + if (_instance != null) + { + _instance.Stop(); + _instance = null; + } + } + + private void Start(int port) + { + Stop(); + _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _server.Bind(new IPEndPoint(IPAddress.Any, port)); + _server.Listen(1); + _server.BeginAccept(_Accept, _server); + _loop = DuktapeRunner.SetLoop(OnUpdate); + } + + private void _Accept(IAsyncResult ar) + { + try + { + var server = ar.AsyncState as Socket; + var socket = server.EndAccept(ar); + Debug.LogWarningFormat("accept: {0}", socket.RemoteEndPoint); + lock (_pending) + { + if (_pending.Count == 0) + { + socket.NoDelay = true; + _pending.Add(socket); + } + else + { + socket.Close(); + } + } + server.BeginAccept(_Accept, server); + } + catch (Exception) + { + // Debug.LogWarningFormat("debugger server closed: {0}", exception); + } + } + + private void OnUpdate() + { + if (_client == null) + { + lock (_pending) + { + if (_pending.Count > 0) + { + var newClient = _pending[0]; + _pending.RemoveAt(0); + if (newClient.Connected) + { + DetachCurrent(); + Debug.LogFormat("debugger connected: {0}", newClient.RemoteEndPoint); + _client = newClient; + _debugger = DuktapeDLL.duk_unity_attach_debugger(_ctx, + duk_unity_debug_read_function, + duk_unity_debug_write_function, + duk_unity_debug_peek_function, + duk_unity_debug_read_flush_function, + duk_unity_debug_write_flush_function, + duk_unity_debug_request_function, + duk_unity_debug_detached_function, + 0); + } + else + { + Debug.LogWarningFormat("dead connection removed"); + } + } + } + } + else + { + if (!_client.Connected || (_client.Poll(1000, SelectMode.SelectRead) && _client.Available == 0)) + { + Debug.LogError("dead"); + _client.Close(); + _client = null; + } + } + } + + private void DetachCurrent() + { + DuktapeDLL.duk_unity_detach_debugger(_ctx, _debugger); + _debugger = IntPtr.Zero; + } + + private void Stop() + { + lock (_pending) + { + _pending.Clear(); + } + DetachCurrent(); + if (_client != null) + { + _client.Close(); + _client = null; + } + if (_server != null) + { + _server.Close(); + _server = null; + } + DuktapeRunner.Clear(_loop); + _loop = 0; + } + + [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_read_function))] + private static uint duk_unity_debug_read_function(int udata, IntPtr buffer, uint length) + { + // Debug.LogWarning("duk_unity_debug_read_function"); + try + { + if (_instance != null && _instance._client != null) + { + var bufferSize = _buffer.Length; + var size = bufferSize > length ? (int)length : bufferSize; + // Debug.LogWarningFormat("debugger read: {0} {1}", size, length); + var n = _instance._client.Receive(_buffer, size, SocketFlags.None); + // Debug.LogWarningFormat("debugger recv: {0}", n); + if (n > 0) + { + Marshal.Copy(_buffer, 0, buffer, n); + return (uint)n; + } + _instance._client.Close(); + _instance._client = null; + } + } + catch (Exception exception) + { + Debug.LogWarningFormat("debugger connection lost: {0}", exception); + _instance._client = null; + } + return 0; + } + + [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_write_function))] + private static uint duk_unity_debug_write_function(int udata, IntPtr buffer, uint length) + { + // Debug.LogWarning("duk_unity_debug_write_function"); + try + { + if (_instance != null && _instance._client != null) + { + var bufferSize = _buffer.Length; + var size = bufferSize > length ? (int)length : bufferSize; + Marshal.Copy(buffer, _buffer, 0, size); + // Debug.LogWarningFormat("debugger write: {0} {1}", size, length); + var n = _instance._client.Send(_buffer, size, SocketFlags.None); + // Debug.LogWarningFormat("debugger sent: {0}", n); + if (n > 0) + { + return (uint)n; + } + _instance._client.Close(); + _instance._client = null; + } + } + catch (Exception exception) + { + Debug.LogWarningFormat("debugger connection lost: {0}", exception); + _instance._client = null; + } + return 0; + } + + [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_peek_function))] + private static uint duk_unity_debug_peek_function(int udata) + { + Debug.LogWarning("duk_unity_debug_peek_function"); + try + { + if (_instance != null && _instance._client != null) + { + if (_instance._client.Connected) + { + if (_instance._client.Poll(1000, SelectMode.SelectRead)) + { + var n = _instance._client.Available; + Debug.LogWarningFormat("peek available {0}", n); + if (n > 0) + { + return (uint)n; + } + Debug.LogWarningFormat("remote closed"); + } + else if (_instance._client.Poll(1000, SelectMode.SelectError)) + { + Debug.LogWarningFormat("peek error"); + } + else + { + Debug.LogWarningFormat("no data"); + return 0; + } + } + Debug.LogWarningFormat("closing"); + _instance._client.Close(); + _instance._client = null; + } + } + catch (Exception exception) + { + Debug.LogWarningFormat("debugger connection lost: {0}", exception); + _instance._client = null; + } + return 0; + } + + [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_read_flush_function))] + private static void duk_unity_debug_read_flush_function(int udata) + { + } + + [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_write_flush_function))] + private static void duk_unity_debug_write_flush_function(int udata) + { + } + + [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_request_function))] + private static int duk_unity_debug_request_function(IntPtr ctx, int udata, int nvalues) + { + Debug.LogWarningFormat("duk_unity_debug_request_function: {0}", nvalues); + return 0; + } + + [MonoPInvokeCallback(typeof(DuktapeDLL.duk_unity_debug_detached_function))] + private static void duk_unity_debug_detached_function(IntPtr ctx, int udata) + { + Debug.LogWarningFormat("duk_unity_debug_detached_function"); + } + } } \ No newline at end of file diff --git a/unity/Assets/Duktape/Source/DuktapeDelegate.cs b/unity/Assets/Duktape/Source/DuktapeDelegate.cs index 61fa2e6..6e4598f 100644 --- a/unity/Assets/Duktape/Source/DuktapeDelegate.cs +++ b/unity/Assets/Duktape/Source/DuktapeDelegate.cs @@ -1,82 +1,82 @@ -using System; -using UnityEngine; - -namespace Duktape -{ - // 包装了一个 js function/eventdispatcher - // 对于 function 调用时传入的 this 为 function 自身 - public class DuktapeDelegate : DuktapeValue - { - // cache the delegate object - public Delegate target; - private int _savedState; - - private DuktapeFunction _jsInvoker; - - public DuktapeDelegate(IntPtr ctx, uint refid) - : base(ctx, refid) - { - } - - private static void duk_unity_unref(IntPtr ctx, uint refid, object target) - { - DuktapeVM.GetObjectCache(ctx).RemoveJSValue(target); - DuktapeDLL.duk_unity_unref(ctx, refid); - } - - protected override void Dispose(bool bManaged) - { - _jsInvoker = null; - if (this._refid != 0 && this._context != null) - { - var vm = this._context.vm; - vm.GC(this._refid, this.target, duk_unity_unref); - this._refid = 0; - this.target = null; - } - } - - public void Invoke(IntPtr ctx) - { - this.BeginInvoke(ctx); - this.EndInvoke(ctx); - } - - // 记录栈状态 - public void BeginInvoke(IntPtr ctx) - { - // Debug.Log($"BeginInvoke: {_savedState}"); - if (_jsInvoker == null) - { - this.Push(ctx); // push this - if (!DuktapeDLL.duk_is_function(ctx, -1)) - { - // Debug.Log("DuktapeDelegate based on Dispatcher"); - DuktapeDLL.duk_get_prop_string(ctx, -1, "dispatch"); - DuktapeDLL.duk_remove(ctx, -2); // remove this - } - _jsInvoker = new DuktapeFunction(ctx, DuktapeDLL.duk_unity_ref(ctx)); - } - _jsInvoker.Push(ctx); // push function - this.Push(ctx); // push this - _savedState = DuktapeDLL.duk_get_top(ctx); - } - - // 根据当前栈参数数量调用函数 - public void EndInvoke(IntPtr ctx) - { - var nargs = DuktapeDLL.duk_get_top(ctx) - _savedState; - var ret = DuktapeDLL.duk_pcall_method(ctx, nargs); - if (ret != DuktapeDLL.DUK_EXEC_SUCCESS) - { - DuktapeAux.PrintError(ctx, -1); - DuktapeDLL.duk_pop(ctx); - // throw new Exception(err); - } - else - { - DuktapeDLL.duk_pop(ctx); - } - } - } -} +using System; +using UnityEngine; + +namespace Duktape +{ + // 包装了一个 js function/eventdispatcher + // 对于 function 调用时传入的 this 为 function 自身 + public class DuktapeDelegate : DuktapeValue + { + // cache the delegate object + public Delegate target; + private int _savedState; + + private DuktapeFunction _jsInvoker; + + public DuktapeDelegate(IntPtr ctx, uint refid) + : base(ctx, refid) + { + } + + private static void duk_unity_unref(IntPtr ctx, uint refid, object target) + { + DuktapeVM.GetObjectCache(ctx).RemoveJSValue(target); + DuktapeDLL.duk_unity_unref(ctx, refid); + } + + protected override void Dispose(bool bManaged) + { + _jsInvoker = null; + if (this._refid != 0 && this._context != null) + { + var vm = this._context.vm; + vm.GC(this._refid, this.target, duk_unity_unref); + this._refid = 0; + this.target = null; + } + } + + public void Invoke(IntPtr ctx) + { + this.BeginInvoke(ctx); + this.EndInvoke(ctx); + } + + // 记录栈状态 + public void BeginInvoke(IntPtr ctx) + { + // Debug.Log($"BeginInvoke: {_savedState}"); + if (_jsInvoker == null) + { + this.Push(ctx); // push this + if (!DuktapeDLL.duk_is_function(ctx, -1)) + { + // Debug.Log("DuktapeDelegate based on Dispatcher"); + DuktapeDLL.duk_get_prop_string(ctx, -1, "dispatch"); + DuktapeDLL.duk_remove(ctx, -2); // remove this + } + _jsInvoker = new DuktapeFunction(ctx, DuktapeDLL.duk_unity_ref(ctx)); + } + _jsInvoker.Push(ctx); // push function + this.Push(ctx); // push this + _savedState = DuktapeDLL.duk_get_top(ctx); + } + + // 根据当前栈参数数量调用函数 + public void EndInvoke(IntPtr ctx) + { + var nargs = DuktapeDLL.duk_get_top(ctx) - _savedState; + var ret = DuktapeDLL.duk_pcall_method(ctx, nargs); + if (ret != DuktapeDLL.DUK_EXEC_SUCCESS) + { + DuktapeAux.PrintError(ctx, -1); + DuktapeDLL.duk_pop(ctx); + // throw new Exception(err); + } + else + { + DuktapeDLL.duk_pop(ctx); + } + } + } +} diff --git a/unity/Assets/Duktape/Source/DuktapeRunner.cs b/unity/Assets/Duktape/Source/DuktapeRunner.cs index d894aef..bc16961 100644 --- a/unity/Assets/Duktape/Source/DuktapeRunner.cs +++ b/unity/Assets/Duktape/Source/DuktapeRunner.cs @@ -1,140 +1,140 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using AOT; - -namespace Duktape -{ - using UnityEngine; - - // 利用 coroutine 实现的简易定时运行管理器 (interval/timeout/loop) - public class DuktapeRunner : MonoBehaviour - { - private static DuktapeRunner _runner; - private static int _id; - private static Dictionary _timers = new Dictionary(); - - public static DuktapeRunner GetRunner() - { - if (_runner == null) - { - var go = new GameObject(); - go.hideFlags = HideFlags.HideAndDontSave; - GameObject.DontDestroyOnLoad(go); - _runner = go.AddComponent(); - } - return _runner; - } - - public static int SetLoop(Action fn) - { - var id = ++_id; - GetRunner().AddLoop(id, fn); - return id; - } - - public static int SetTimeout(DuktapeFunction fn, double ms) - { - return SetTimeout(fn, (float)ms); - } - - public static int SetTimeout(DuktapeFunction fn, float ms) - { - var id = ++_id; - GetRunner().AddTimeout(id, fn, ms * 0.001f); - return id; - } - - public static int SetInterval(DuktapeFunction fn, double ms) - { - return SetInterval(fn, (float)ms); - } - - public static int SetInterval(DuktapeFunction fn, float ms) - { - var id = ++_id; - GetRunner().AddInterval(id, fn, ms * 0.001f); - return id; - } - - public static int SetInterval(Action fn, float ms) - { - var id = ++_id; - GetRunner().AddInterval(id, fn, ms * 0.001f); - return id; - } - - public static bool Clear(int id) - { - return GetRunner().RemoveTimer(id); - } - - private void AddTimeout(int id, DuktapeFunction fn, float seconds) - { - _timers[id] = StartCoroutine(_Timeout(id, fn, seconds)); - } - - private void AddInterval(int id, DuktapeFunction fn, float seconds) - { - _timers[id] = StartCoroutine(_Interval(id, fn, seconds)); - } - - private void AddInterval(int id, Action fn, float seconds) - { - _timers[id] = StartCoroutine(_Interval(id, fn, seconds)); - } - - private void AddLoop(int id, Action fn) - { - _timers[id] = StartCoroutine(_Loop(id, fn)); - } - - private bool RemoveTimer(int id) - { - Coroutine coroutine; - if (_timers.TryGetValue(id, out coroutine)) - { - StopCoroutine(coroutine); - return _timers.Remove(id); - } - return false; - } - - private IEnumerator _Loop(int id, Action fn) - { - while (true) - { - yield return null; - fn(); - } - } - - private IEnumerator _Timeout(int id, DuktapeFunction fn, float seconds) - { - var wait = seconds > 0 ? new WaitForSeconds(seconds) : null; - yield return wait; - _timers.Remove(id); - fn.Invoke(); - } - - private IEnumerator _Interval(int id, DuktapeFunction fn, float seconds) - { - var wait = seconds > 0 ? new WaitForSeconds(seconds) : null; - while (true) - { - yield return wait; - fn.Invoke(); - } - } - - private IEnumerator _Interval(int id, Action fn, float seconds) - { - var wait = seconds > 0 ? new WaitForSeconds(seconds) : null; - while (true) - { - yield return wait; - fn(); - } - } - } -} +using System; +using System.Collections; +using System.Collections.Generic; +using AOT; + +namespace Duktape +{ + using UnityEngine; + + // 利用 coroutine 实现的简易定时运行管理器 (interval/timeout/loop) + public class DuktapeRunner : MonoBehaviour + { + private static DuktapeRunner _runner; + private static int _id; + private static Dictionary _timers = new Dictionary(); + + public static DuktapeRunner GetRunner() + { + if (_runner == null) + { + var go = new GameObject(); + go.hideFlags = HideFlags.HideAndDontSave; + GameObject.DontDestroyOnLoad(go); + _runner = go.AddComponent(); + } + return _runner; + } + + public static int SetLoop(Action fn) + { + var id = ++_id; + GetRunner().AddLoop(id, fn); + return id; + } + + public static int SetTimeout(DuktapeFunction fn, double ms) + { + return SetTimeout(fn, (float)ms); + } + + public static int SetTimeout(DuktapeFunction fn, float ms) + { + var id = ++_id; + GetRunner().AddTimeout(id, fn, ms * 0.001f); + return id; + } + + public static int SetInterval(DuktapeFunction fn, double ms) + { + return SetInterval(fn, (float)ms); + } + + public static int SetInterval(DuktapeFunction fn, float ms) + { + var id = ++_id; + GetRunner().AddInterval(id, fn, ms * 0.001f); + return id; + } + + public static int SetInterval(Action fn, float ms) + { + var id = ++_id; + GetRunner().AddInterval(id, fn, ms * 0.001f); + return id; + } + + public static bool Clear(int id) + { + return GetRunner().RemoveTimer(id); + } + + private void AddTimeout(int id, DuktapeFunction fn, float seconds) + { + _timers[id] = StartCoroutine(_Timeout(id, fn, seconds)); + } + + private void AddInterval(int id, DuktapeFunction fn, float seconds) + { + _timers[id] = StartCoroutine(_Interval(id, fn, seconds)); + } + + private void AddInterval(int id, Action fn, float seconds) + { + _timers[id] = StartCoroutine(_Interval(id, fn, seconds)); + } + + private void AddLoop(int id, Action fn) + { + _timers[id] = StartCoroutine(_Loop(id, fn)); + } + + private bool RemoveTimer(int id) + { + Coroutine coroutine; + if (_timers.TryGetValue(id, out coroutine)) + { + StopCoroutine(coroutine); + return _timers.Remove(id); + } + return false; + } + + private IEnumerator _Loop(int id, Action fn) + { + while (true) + { + yield return null; + fn(); + } + } + + private IEnumerator _Timeout(int id, DuktapeFunction fn, float seconds) + { + var wait = seconds > 0 ? new WaitForSeconds(seconds) : null; + yield return wait; + _timers.Remove(id); + fn.Invoke(); + } + + private IEnumerator _Interval(int id, DuktapeFunction fn, float seconds) + { + var wait = seconds > 0 ? new WaitForSeconds(seconds) : null; + while (true) + { + yield return wait; + fn.Invoke(); + } + } + + private IEnumerator _Interval(int id, Action fn, float seconds) + { + var wait = seconds > 0 ? new WaitForSeconds(seconds) : null; + while (true) + { + yield return wait; + fn(); + } + } + } +} diff --git a/unity/Assets/Duktape/Source/DuktapeVM.cs b/unity/Assets/Duktape/Source/DuktapeVM.cs index 5e364b4..a74f70f 100644 --- a/unity/Assets/Duktape/Source/DuktapeVM.cs +++ b/unity/Assets/Duktape/Source/DuktapeVM.cs @@ -1,528 +1,528 @@ -using System; -using System.Reflection; -using System.Collections.Generic; - -namespace Duktape -{ - using UnityEngine; - using duk_ret_t = System.Int32; - - public struct UnrefAction - { - public uint refid; - public object target; - public Action action; - } - - public class DuktapeVM // : Scripting.ScriptEngine - { - // duktape-unity 版本, 生成规则发生无法兼容的改变时增加版本号 - public const int VERSION = 0x10001; - public const string HEAP_STASH_PROPS_REGISTRY = "registry"; - // duk_add_event 生成的 object 中存储 this 的属性名 - public static readonly string EVENT_PROP_THIS = DuktapeDLL.DUK_HIDDEN_SYMBOL("this"); - // 在jsobject实例上记录关联的本地对象 object cache refid - public static readonly string OBJ_PROP_NATIVE = DuktapeDLL.DUK_HIDDEN_SYMBOL("1"); - public static readonly string OBJ_PROP_NATIVE_WEAK = DuktapeDLL.DUK_HIDDEN_SYMBOL("2"); - public static readonly string OBJ_PROP_TYPE = DuktapeDLL.DUK_HIDDEN_SYMBOL("3"); - // 导出类的js构造函数隐藏属性, 记录在vm中的注册id - public static readonly string OBJ_PROP_EXPORTED_REFID = DuktapeDLL.DUK_HIDDEN_SYMBOL("4"); - // public static readonly string OBJ_PROP_SPECIAL_REFID = DuktapeDLL.DUK_HIDDEN_SYMBOL("special-refid"); - - public const string _DuktapeDelegates = "_DuktapeDelegates"; - - public const string SPECIAL_ENUM = "Enum"; - public const string SPECIAL_DELEGATE = "Delegate"; - public const string SPECIAL_CSHARP = "CSharp"; - - private static DuktapeVM _instance; - private int _updateTimer; - private DuktapeContext _ctx; - private IFileSystem _fileManager; - private ObjectCache _objectCache = new ObjectCache(); - - private List _searchPaths = new List(); - - private Queue _unrefActions = new Queue(); - - // 已经导出的本地类 - private Dictionary _exported = new Dictionary(); - private Dictionary _exportedTypes = new Dictionary(); // 从 refid 反查 Type - - private Dictionary _specialTypes = new Dictionary(); // 从 refid 反查 Type - - private Dictionary _delegates = new Dictionary(); // 委托对应的 duktape 绑定函数 - - // private static int _thread = 0; - - private static Dictionary _contexts = new Dictionary(); - private static IntPtr _lastContextPtr; - private static DuktapeContext _lastContext; - - public DuktapeContext context - { - get { return _ctx; } - } - - public IntPtr ctx - { - get { return _ctx != null ? _ctx.rawValue : IntPtr.Zero; } - } - - public DuktapeVM() - { - _instance = this; - } - - public static DuktapeVM GetInstance() - { - return _instance; - } - - public static void addContext(DuktapeContext context) - { - var ctx = context.rawValue; - _contexts[ctx] = context; - _lastContext = context; - _lastContextPtr = ctx; - } - - public static DuktapeContext GetContext(IntPtr ctx) - { - if (_lastContextPtr == ctx) - { - return _lastContext; - } - DuktapeContext context; - if (_contexts.TryGetValue(ctx, out context)) - { - _lastContext = context; - _lastContextPtr = ctx; - return context; - } - // fixme 如果是 thread 则获取对应 main context - return null; - } - - // public static DuktapeContext GetContext(IntPtr ctx) - // { - // return DuktapeContext.GetContext(ctx); - // } - - public static DuktapeVM GetVM(IntPtr ctx) - { - return DuktapeContext.GetVM(ctx); - } - - public static ObjectCache GetObjectCache(IntPtr ctx) - { - return DuktapeContext.GetVM(ctx)._objectCache; - } - - public void AddSearchPath(string path) - { - if (!_searchPaths.Contains(path)) - { - _searchPaths.Add(path); - } - } - - public DuktapeFunction GetSpecial(string name) - { - DuktapeFunction val; - if (_specialTypes.TryGetValue(name, out val)) - { - return val; - } - return null; - } - - public uint AddSpecial(string name, DuktapeFunction val) - { - // Debug.LogFormat("Add Special {0} {1}", name, val.rawValue); - var refid = val.rawValue; - _specialTypes[name] = val; - return refid; - } - - public void AddDelegate(Type type, MethodInfo method) - { - _delegates[type] = method; - // Debug.LogFormat("Add Delegate {0} {1}", type, method); - } - - public Delegate CreateDelegate(Type type, DuktapeDelegate fn) - { - MethodInfo method; - if (_delegates.TryGetValue(type, out method)) - { - var target = Delegate.CreateDelegate(type, fn, method, true); - fn.target = target; - return target; - } - return null; - } - - public uint AddExported(Type type, DuktapeFunction fn) - { - var refid = fn.rawValue; - _exported.Add(type, fn); - _exportedTypes[refid] = type; - // Debug.Log($"add export: {type}"); - return refid; - } - - public Type GetExportedType(uint refid) - { - Type type; - return _exportedTypes.TryGetValue(refid, out type) ? type : null; - } - - // 得到注册在js中的类型对应的构造函数 - public DuktapeFunction GetExported(Type type) - { - DuktapeFunction value; - return _exported.TryGetValue(type, out value) ? value : null; - } - - public void GC(uint refid, object target, Action op) - { - var act = new UnrefAction() - { - refid = refid, - action = op, - target = target, - }; - lock (_unrefActions) - { - _unrefActions.Enqueue(act); - } - } - - private void OnUpdate() - { - var ctx = _ctx.rawValue; - lock (_unrefActions) - { - while (true) - { - var count = _unrefActions.Count; - if (count == 0) - { - break; - } - var act = _unrefActions.Dequeue(); - act.action(ctx, act.refid, act.target); - // Debug.LogFormat("duktape gc {0}", act.refid); - } - } - } - - public static void duk_open_module(IntPtr ctx) - { - DuktapeDLL.duk_push_object(ctx); - DuktapeDLL.duk_push_c_function(ctx, cb_resolve_module, DuktapeDLL.DUK_VARARGS); - DuktapeDLL.duk_put_prop_string(ctx, -2, "resolve"); - DuktapeDLL.duk_push_c_function(ctx, cb_load_module, DuktapeDLL.DUK_VARARGS); - DuktapeDLL.duk_put_prop_string(ctx, -2, "load"); - DuktapeDLL.duk_module_node_init(ctx); - } - - public static string EnsureExtension(string filename) - { - return filename != null && filename.EndsWith(".js") ? filename : filename + ".js"; - } - - public string ResolvePath(string filename) - { - if (_fileManager.Exists(filename)) - { - return filename; - } - for (int i = 0, count = _searchPaths.Count; i < count; i++) - { - var path = _searchPaths[i]; - var vpath = PathUtils.Combine(path, filename); - if (_fileManager.Exists(vpath)) - { - return vpath; - } - } - return null; - } - - [AOT.MonoPInvokeCallback(typeof(DuktapeDLL.duk_c_function))] - private static duk_ret_t cb_resolve_module(IntPtr ctx) - { - var module_id = EnsureExtension(DuktapeAux.duk_require_string(ctx, 0)); - var parent_id = DuktapeAux.duk_require_string(ctx, 1); - var resolve_to = module_id; - // Debug.LogFormat("cb_resolve_module module_id:'{0}', parent_id:'{1}'\n", module_id, parent_id); - - if (module_id.StartsWith("./") || module_id.StartsWith("../") || module_id.Contains("/./") || module_id.Contains("/../")) - { - // 显式相对路径直接从 parent 模块路径拼接 - var parent_path = PathUtils.GetDirectoryName(parent_id); - try - { - resolve_to = PathUtils.ExtractPath(PathUtils.Combine(parent_path, module_id), '/'); - } - catch - { - // 不能提升到源代码目录外面 - DuktapeDLL.duk_type_error(ctx, "invalid module path (out of sourceRoot): %s", module_id); - return 1; - } - } - // Debug.LogFormat("resolve_cb(1): id:{0}', parent-id:'{1}', resolve-to:'{2}'", module_id, parent_id, resolve_to); - // if (GetVM(ctx).ResolvePath(resolve_to) == null) - // { - // DuktapeDLL.duk_type_error(ctx, "cannot find module: %s", module_id); - // return 1; - // } - - if (resolve_to != null) - { - DuktapeDLL.duk_push_string(ctx, resolve_to); - } - else - { - DuktapeDLL.duk_type_error(ctx, "cannot find module: %s", module_id); - } - return 1; - } - - [AOT.MonoPInvokeCallback(typeof(DuktapeDLL.duk_c_function))] - private static duk_ret_t cb_load_module(IntPtr ctx) - { - var module_id = DuktapeAux.duk_require_string(ctx, 0); - DuktapeDLL.duk_get_prop_string(ctx, 2, "filename"); - var filename = DuktapeAux.duk_require_string(ctx, -1); - var resolvedPath = GetVM(ctx).ResolvePath(filename); - // Debug.LogFormat("cb_load_module module_id:'{0}', filename:'{1}', resolved:'{2}'\n", module_id, filename, resolvedPath); - - var source = GetVM(ctx)._fileManager.ReadAllText(resolvedPath); - if (source != null) - { - DuktapeDLL.duk_push_string(ctx, source); - } - else - { - DuktapeDLL.duk_type_error(ctx, "cannot load module: %s", module_id); - } - - return 1; - } - - public void Initialize(IFileSystem fs, IDuktapeListener listener) - { - this._fileManager = fs; - var ctx = DuktapeDLL.duk_create_heap_default(); - this._ctx = new DuktapeContext(this, ctx); - DuktapeAux.duk_open(ctx); - DuktapeVM.duk_open_module(ctx); - DuktapeDLL.duk_unity_open(ctx); - - DuktapeDLL.duk_push_global_object(ctx); - DuktapeJSBuiltins.reg(ctx); - if (listener != null) - { - listener.OnTypesBinding(this); - } - var exportedTypes = this.GetType().Assembly.GetExportedTypes(); - var ctx_t = new object[] { ctx }; - var numRegInvoked = 0; - for (int i = 0, size = exportedTypes.Length; i < size; i++) - { - var type = exportedTypes[i]; -#if UNITY_EDITOR - if (type.IsDefined(typeof(JSAutoRunAttribute), false)) - { - try - { - var run = type.GetMethod("Run", BindingFlags.Static | BindingFlags.Public); - if (run != null) - { - run.Invoke(null, null); - } - else - { - Debug.LogError("???"); - } - } - catch (Exception exception) - { - Debug.LogWarning(exception); - } - continue; - } -#endif - var attributes = type.GetCustomAttributes(typeof(JSBindingAttribute), false); - if (attributes.Length == 1) - { - var jsBinding = attributes[0] as JSBindingAttribute; - if (jsBinding.Version == 0 || jsBinding.Version == VERSION) - { - var reg = type.GetMethod("reg"); - if (reg != null) - { - reg.Invoke(null, ctx_t); - ++numRegInvoked; - } - } - else - { - if (listener != null) - { - listener.OnBindingError(this, type); - } - } - } - } - if (listener != null) - { - listener.OnBinded(this, numRegInvoked); - } - // Debug.LogFormat("exported {0} classes", _exported.Count); - - // 设置导出类的继承链 - foreach (var kv in _exported) - { - var type = kv.Key; - var baseType = type.BaseType; - if (baseType == null) - { - // Debug.Log($"baseType is null, for {type}"); - continue; - } - var fn = kv.Value; - fn.PushPrototype(ctx); - if (PushChainedPrototypeOf(ctx, baseType)) - { - // Debug.LogFormat($"set {type} super {baseType}"); - DuktapeDLL.duk_set_prototype(ctx, -2); - } - else - { - Debug.LogWarning($"fail to push prototype, for {type}: {baseType}"); - } - DuktapeDLL.duk_pop(ctx); - } - - DuktapeJSBuiltins.postreg(ctx); - DuktapeDLL.duk_pop(ctx); // pop global - - _updateTimer = DuktapeRunner.SetInterval(this.OnUpdate, 100f); - - if (listener != null) - { - listener.OnLoaded(this); - } - } - - // 将 type 的 prototype 压栈 (未导出则向父类追溯) - // 没有对应的基类 prototype 时, 不压栈 - public bool PushChainedPrototypeOf(IntPtr ctx, Type baseType) - { - if (baseType == null) - { - // Debug.LogFormat("super terminated {0}", baseType); - return false; - } - if (baseType == typeof(Enum)) - { - DuktapeFunction val; - if (_specialTypes.TryGetValue(SPECIAL_ENUM, out val)) - { - val.PushPrototype(ctx); - return true; - } - } - DuktapeFunction fn; - if (_exported.TryGetValue(baseType, out fn)) - { - fn.PushPrototype(ctx); - return true; - } - return PushChainedPrototypeOf(ctx, baseType.BaseType); - } - - public void EvalSource(string source, string filename) - { - if (filename == null) - { - filename = "eval"; - } - if (string.IsNullOrEmpty(source)) - { - return; - } - var ctx = _ctx.rawValue; - DuktapeDLL.duk_push_string(ctx, source); - DuktapeDLL.duk_push_string(ctx, filename); - if (DuktapeDLL.duk_pcompile(ctx, 0) != 0) - { - DuktapeAux.PrintError(ctx, -1, filename); - DuktapeDLL.duk_pop(ctx); - } - else - { - // Debug.LogFormat("check top {0}", DuktapeDLL.duk_get_top(ctx)); - if (DuktapeDLL.duk_pcall(ctx, 0) != DuktapeDLL.DUK_EXEC_SUCCESS) - { - DuktapeAux.PrintError(ctx, -1, filename); - } - DuktapeDLL.duk_pop(ctx); - // Debug.LogFormat("check top {0}", DuktapeDLL.duk_get_top(ctx)); - } - } - - public void EvalFile(string filename) - { - filename = EnsureExtension(filename); - var ctx = _ctx.rawValue; - var resolvedPath = ResolvePath(filename); - var source = _fileManager.ReadAllText(resolvedPath); - EvalSource(source, filename); - } - - public void EvalMain(string filename) - { - filename = EnsureExtension(filename); - var ctx = _ctx.rawValue; - var top = DuktapeDLL.duk_get_top(ctx); - var resolvedPath = ResolvePath(filename); - var source = _fileManager.ReadAllText(resolvedPath); - DuktapeDLL.duk_push_string(ctx, source); - var err = DuktapeDLL.duk_module_node_peval_main(ctx, filename); - // var err = DuktapeDLL.duk_peval(ctx); - // var err = DuktapeDLL.duk_peval_string_noresult(ctx, source); - // Debug.Log($"load main module: {filename} ({resolvedPath})"); - if (err != 0) - { - DuktapeAux.PrintError(ctx, -1, filename); - // Debug.LogErrorFormat("eval main error: {0}\n{1}", DuktapeDLL.duk_safe_to_string(ctx, -1), filename); - } - DuktapeDLL.duk_set_top(ctx, top); - } - - public void Destroy() - { - _instance = null; - if (_ctx != null) - { - var ctx = _ctx.rawValue; - _ctx.onDestroy(); - _ctx = null; - DuktapeDLL.duk_destroy_heap(ctx); - // Debug.LogWarning("duk_destroy_heap"); - } - - if (_updateTimer != 0) - { - DuktapeRunner.Clear(_updateTimer); - _updateTimer = 0; - } - } - } +using System; +using System.Reflection; +using System.Collections.Generic; + +namespace Duktape +{ + using UnityEngine; + using duk_ret_t = System.Int32; + + public struct UnrefAction + { + public uint refid; + public object target; + public Action action; + } + + public class DuktapeVM // : Scripting.ScriptEngine + { + // duktape-unity 版本, 生成规则发生无法兼容的改变时增加版本号 + public const int VERSION = 0x10001; + public const string HEAP_STASH_PROPS_REGISTRY = "registry"; + // duk_add_event 生成的 object 中存储 this 的属性名 + public static readonly string EVENT_PROP_THIS = DuktapeDLL.DUK_HIDDEN_SYMBOL("this"); + // 在jsobject实例上记录关联的本地对象 object cache refid + public static readonly string OBJ_PROP_NATIVE = DuktapeDLL.DUK_HIDDEN_SYMBOL("1"); + public static readonly string OBJ_PROP_NATIVE_WEAK = DuktapeDLL.DUK_HIDDEN_SYMBOL("2"); + public static readonly string OBJ_PROP_TYPE = DuktapeDLL.DUK_HIDDEN_SYMBOL("3"); + // 导出类的js构造函数隐藏属性, 记录在vm中的注册id + public static readonly string OBJ_PROP_EXPORTED_REFID = DuktapeDLL.DUK_HIDDEN_SYMBOL("4"); + // public static readonly string OBJ_PROP_SPECIAL_REFID = DuktapeDLL.DUK_HIDDEN_SYMBOL("special-refid"); + + public const string _DuktapeDelegates = "_DuktapeDelegates"; + + public const string SPECIAL_ENUM = "Enum"; + public const string SPECIAL_DELEGATE = "Delegate"; + public const string SPECIAL_CSHARP = "CSharp"; + + private static DuktapeVM _instance; + private int _updateTimer; + private DuktapeContext _ctx; + private IFileSystem _fileManager; + private ObjectCache _objectCache = new ObjectCache(); + + private List _searchPaths = new List(); + + private Queue _unrefActions = new Queue(); + + // 已经导出的本地类 + private Dictionary _exported = new Dictionary(); + private Dictionary _exportedTypes = new Dictionary(); // 从 refid 反查 Type + + private Dictionary _specialTypes = new Dictionary(); // 从 refid 反查 Type + + private Dictionary _delegates = new Dictionary(); // 委托对应的 duktape 绑定函数 + + // private static int _thread = 0; + + private static Dictionary _contexts = new Dictionary(); + private static IntPtr _lastContextPtr; + private static DuktapeContext _lastContext; + + public DuktapeContext context + { + get { return _ctx; } + } + + public IntPtr ctx + { + get { return _ctx != null ? _ctx.rawValue : IntPtr.Zero; } + } + + public DuktapeVM() + { + _instance = this; + } + + public static DuktapeVM GetInstance() + { + return _instance; + } + + public static void addContext(DuktapeContext context) + { + var ctx = context.rawValue; + _contexts[ctx] = context; + _lastContext = context; + _lastContextPtr = ctx; + } + + public static DuktapeContext GetContext(IntPtr ctx) + { + if (_lastContextPtr == ctx) + { + return _lastContext; + } + DuktapeContext context; + if (_contexts.TryGetValue(ctx, out context)) + { + _lastContext = context; + _lastContextPtr = ctx; + return context; + } + // fixme 如果是 thread 则获取对应 main context + return null; + } + + // public static DuktapeContext GetContext(IntPtr ctx) + // { + // return DuktapeContext.GetContext(ctx); + // } + + public static DuktapeVM GetVM(IntPtr ctx) + { + return DuktapeContext.GetVM(ctx); + } + + public static ObjectCache GetObjectCache(IntPtr ctx) + { + return DuktapeContext.GetVM(ctx)._objectCache; + } + + public void AddSearchPath(string path) + { + if (!_searchPaths.Contains(path)) + { + _searchPaths.Add(path); + } + } + + public DuktapeFunction GetSpecial(string name) + { + DuktapeFunction val; + if (_specialTypes.TryGetValue(name, out val)) + { + return val; + } + return null; + } + + public uint AddSpecial(string name, DuktapeFunction val) + { + // Debug.LogFormat("Add Special {0} {1}", name, val.rawValue); + var refid = val.rawValue; + _specialTypes[name] = val; + return refid; + } + + public void AddDelegate(Type type, MethodInfo method) + { + _delegates[type] = method; + // Debug.LogFormat("Add Delegate {0} {1}", type, method); + } + + public Delegate CreateDelegate(Type type, DuktapeDelegate fn) + { + MethodInfo method; + if (_delegates.TryGetValue(type, out method)) + { + var target = Delegate.CreateDelegate(type, fn, method, true); + fn.target = target; + return target; + } + return null; + } + + public uint AddExported(Type type, DuktapeFunction fn) + { + var refid = fn.rawValue; + _exported.Add(type, fn); + _exportedTypes[refid] = type; + // Debug.Log($"add export: {type}"); + return refid; + } + + public Type GetExportedType(uint refid) + { + Type type; + return _exportedTypes.TryGetValue(refid, out type) ? type : null; + } + + // 得到注册在js中的类型对应的构造函数 + public DuktapeFunction GetExported(Type type) + { + DuktapeFunction value; + return _exported.TryGetValue(type, out value) ? value : null; + } + + public void GC(uint refid, object target, Action op) + { + var act = new UnrefAction() + { + refid = refid, + action = op, + target = target, + }; + lock (_unrefActions) + { + _unrefActions.Enqueue(act); + } + } + + private void OnUpdate() + { + var ctx = _ctx.rawValue; + lock (_unrefActions) + { + while (true) + { + var count = _unrefActions.Count; + if (count == 0) + { + break; + } + var act = _unrefActions.Dequeue(); + act.action(ctx, act.refid, act.target); + // Debug.LogFormat("duktape gc {0}", act.refid); + } + } + } + + public static void duk_open_module(IntPtr ctx) + { + DuktapeDLL.duk_push_object(ctx); + DuktapeDLL.duk_push_c_function(ctx, cb_resolve_module, DuktapeDLL.DUK_VARARGS); + DuktapeDLL.duk_put_prop_string(ctx, -2, "resolve"); + DuktapeDLL.duk_push_c_function(ctx, cb_load_module, DuktapeDLL.DUK_VARARGS); + DuktapeDLL.duk_put_prop_string(ctx, -2, "load"); + DuktapeDLL.duk_module_node_init(ctx); + } + + public static string EnsureExtension(string filename) + { + return filename != null && filename.EndsWith(".js") ? filename : filename + ".js"; + } + + public string ResolvePath(string filename) + { + if (_fileManager.Exists(filename)) + { + return filename; + } + for (int i = 0, count = _searchPaths.Count; i < count; i++) + { + var path = _searchPaths[i]; + var vpath = PathUtils.Combine(path, filename); + if (_fileManager.Exists(vpath)) + { + return vpath; + } + } + return null; + } + + [AOT.MonoPInvokeCallback(typeof(DuktapeDLL.duk_c_function))] + private static duk_ret_t cb_resolve_module(IntPtr ctx) + { + var module_id = EnsureExtension(DuktapeAux.duk_require_string(ctx, 0)); + var parent_id = DuktapeAux.duk_require_string(ctx, 1); + var resolve_to = module_id; + // Debug.LogFormat("cb_resolve_module module_id:'{0}', parent_id:'{1}'\n", module_id, parent_id); + + if (module_id.StartsWith("./") || module_id.StartsWith("../") || module_id.Contains("/./") || module_id.Contains("/../")) + { + // 显式相对路径直接从 parent 模块路径拼接 + var parent_path = PathUtils.GetDirectoryName(parent_id); + try + { + resolve_to = PathUtils.ExtractPath(PathUtils.Combine(parent_path, module_id), '/'); + } + catch + { + // 不能提升到源代码目录外面 + DuktapeDLL.duk_type_error(ctx, "invalid module path (out of sourceRoot): %s", module_id); + return 1; + } + } + // Debug.LogFormat("resolve_cb(1): id:{0}', parent-id:'{1}', resolve-to:'{2}'", module_id, parent_id, resolve_to); + // if (GetVM(ctx).ResolvePath(resolve_to) == null) + // { + // DuktapeDLL.duk_type_error(ctx, "cannot find module: %s", module_id); + // return 1; + // } + + if (resolve_to != null) + { + DuktapeDLL.duk_push_string(ctx, resolve_to); + } + else + { + DuktapeDLL.duk_type_error(ctx, "cannot find module: %s", module_id); + } + return 1; + } + + [AOT.MonoPInvokeCallback(typeof(DuktapeDLL.duk_c_function))] + private static duk_ret_t cb_load_module(IntPtr ctx) + { + var module_id = DuktapeAux.duk_require_string(ctx, 0); + DuktapeDLL.duk_get_prop_string(ctx, 2, "filename"); + var filename = DuktapeAux.duk_require_string(ctx, -1); + var resolvedPath = GetVM(ctx).ResolvePath(filename); + // Debug.LogFormat("cb_load_module module_id:'{0}', filename:'{1}', resolved:'{2}'\n", module_id, filename, resolvedPath); + + var source = GetVM(ctx)._fileManager.ReadAllText(resolvedPath); + if (source != null) + { + DuktapeDLL.duk_push_string(ctx, source); + } + else + { + DuktapeDLL.duk_type_error(ctx, "cannot load module: %s", module_id); + } + + return 1; + } + + public void Initialize(IFileSystem fs, IDuktapeListener listener) + { + this._fileManager = fs; + var ctx = DuktapeDLL.duk_create_heap_default(); + this._ctx = new DuktapeContext(this, ctx); + DuktapeAux.duk_open(ctx); + DuktapeVM.duk_open_module(ctx); + DuktapeDLL.duk_unity_open(ctx); + + DuktapeDLL.duk_push_global_object(ctx); + DuktapeJSBuiltins.reg(ctx); + if (listener != null) + { + listener.OnTypesBinding(this); + } + var exportedTypes = this.GetType().Assembly.GetExportedTypes(); + var ctx_t = new object[] { ctx }; + var numRegInvoked = 0; + for (int i = 0, size = exportedTypes.Length; i < size; i++) + { + var type = exportedTypes[i]; +#if UNITY_EDITOR + if (type.IsDefined(typeof(JSAutoRunAttribute), false)) + { + try + { + var run = type.GetMethod("Run", BindingFlags.Static | BindingFlags.Public); + if (run != null) + { + run.Invoke(null, null); + } + else + { + Debug.LogError("???"); + } + } + catch (Exception exception) + { + Debug.LogWarning(exception); + } + continue; + } +#endif + var attributes = type.GetCustomAttributes(typeof(JSBindingAttribute), false); + if (attributes.Length == 1) + { + var jsBinding = attributes[0] as JSBindingAttribute; + if (jsBinding.Version == 0 || jsBinding.Version == VERSION) + { + var reg = type.GetMethod("reg"); + if (reg != null) + { + reg.Invoke(null, ctx_t); + ++numRegInvoked; + } + } + else + { + if (listener != null) + { + listener.OnBindingError(this, type); + } + } + } + } + if (listener != null) + { + listener.OnBinded(this, numRegInvoked); + } + // Debug.LogFormat("exported {0} classes", _exported.Count); + + // 设置导出类的继承链 + foreach (var kv in _exported) + { + var type = kv.Key; + var baseType = type.BaseType; + if (baseType == null) + { + // Debug.Log($"baseType is null, for {type}"); + continue; + } + var fn = kv.Value; + fn.PushPrototype(ctx); + if (PushChainedPrototypeOf(ctx, baseType)) + { + // Debug.LogFormat($"set {type} super {baseType}"); + DuktapeDLL.duk_set_prototype(ctx, -2); + } + else + { + Debug.LogWarning($"fail to push prototype, for {type}: {baseType}"); + } + DuktapeDLL.duk_pop(ctx); + } + + DuktapeJSBuiltins.postreg(ctx); + DuktapeDLL.duk_pop(ctx); // pop global + + _updateTimer = DuktapeRunner.SetInterval(this.OnUpdate, 100f); + + if (listener != null) + { + listener.OnLoaded(this); + } + } + + // 将 type 的 prototype 压栈 (未导出则向父类追溯) + // 没有对应的基类 prototype 时, 不压栈 + public bool PushChainedPrototypeOf(IntPtr ctx, Type baseType) + { + if (baseType == null) + { + // Debug.LogFormat("super terminated {0}", baseType); + return false; + } + if (baseType == typeof(Enum)) + { + DuktapeFunction val; + if (_specialTypes.TryGetValue(SPECIAL_ENUM, out val)) + { + val.PushPrototype(ctx); + return true; + } + } + DuktapeFunction fn; + if (_exported.TryGetValue(baseType, out fn)) + { + fn.PushPrototype(ctx); + return true; + } + return PushChainedPrototypeOf(ctx, baseType.BaseType); + } + + public void EvalSource(string source, string filename) + { + if (filename == null) + { + filename = "eval"; + } + if (string.IsNullOrEmpty(source)) + { + return; + } + var ctx = _ctx.rawValue; + DuktapeDLL.duk_push_string(ctx, source); + DuktapeDLL.duk_push_string(ctx, filename); + if (DuktapeDLL.duk_pcompile(ctx, 0) != 0) + { + DuktapeAux.PrintError(ctx, -1, filename); + DuktapeDLL.duk_pop(ctx); + } + else + { + // Debug.LogFormat("check top {0}", DuktapeDLL.duk_get_top(ctx)); + if (DuktapeDLL.duk_pcall(ctx, 0) != DuktapeDLL.DUK_EXEC_SUCCESS) + { + DuktapeAux.PrintError(ctx, -1, filename); + } + DuktapeDLL.duk_pop(ctx); + // Debug.LogFormat("check top {0}", DuktapeDLL.duk_get_top(ctx)); + } + } + + public void EvalFile(string filename) + { + filename = EnsureExtension(filename); + var ctx = _ctx.rawValue; + var resolvedPath = ResolvePath(filename); + var source = _fileManager.ReadAllText(resolvedPath); + EvalSource(source, filename); + } + + public void EvalMain(string filename) + { + filename = EnsureExtension(filename); + var ctx = _ctx.rawValue; + var top = DuktapeDLL.duk_get_top(ctx); + var resolvedPath = ResolvePath(filename); + var source = _fileManager.ReadAllText(resolvedPath); + DuktapeDLL.duk_push_string(ctx, source); + var err = DuktapeDLL.duk_module_node_peval_main(ctx, filename); + // var err = DuktapeDLL.duk_peval(ctx); + // var err = DuktapeDLL.duk_peval_string_noresult(ctx, source); + // Debug.Log($"load main module: {filename} ({resolvedPath})"); + if (err != 0) + { + DuktapeAux.PrintError(ctx, -1, filename); + // Debug.LogErrorFormat("eval main error: {0}\n{1}", DuktapeDLL.duk_safe_to_string(ctx, -1), filename); + } + DuktapeDLL.duk_set_top(ctx, top); + } + + public void Destroy() + { + _instance = null; + if (_ctx != null) + { + var ctx = _ctx.rawValue; + _ctx.onDestroy(); + _ctx = null; + DuktapeDLL.duk_destroy_heap(ctx); + // Debug.LogWarning("duk_destroy_heap"); + } + + if (_updateTimer != 0) + { + DuktapeRunner.Clear(_updateTimer); + _updateTimer = 0; + } + } + } } \ No newline at end of file diff --git a/unity/Assets/Duktape/Source/PathUtils.cs b/unity/Assets/Duktape/Source/PathUtils.cs index 7454494..3309b93 100644 --- a/unity/Assets/Duktape/Source/PathUtils.cs +++ b/unity/Assets/Duktape/Source/PathUtils.cs @@ -1,55 +1,55 @@ -using System; -using System.Collections.Generic; - -namespace Duktape -{ - public static class PathUtils - { - public static string GetDirectoryName(string path) - { - return string.IsNullOrEmpty(path) ? path : System.IO.Path.GetDirectoryName(path).Replace('\\', '/'); - } - - public static string Combine(string path1, string path2) - { - return System.IO.Path.Combine(path1, path2).Replace('\\', '/'); - } - - public static string Combine(string path1, string path2, string path3) - { - return System.IO.Path.Combine(path1, path2, path3).Replace('\\', '/'); - } - - public static string Combine(string path1, string path2, string path3, string path4) - { - return System.IO.Path.Combine(path1, path2, path3, path4).Replace('\\', '/'); - } - - public static string Combine(params string[] paths) - { - return System.IO.Path.Combine(paths).Replace('\\', '/'); - } - - /// 展开路径中的 ./.. - public static string ExtractPath(string path, char sp) - { - var items = path.Split(sp); - if (items.Length < 2) - { - return path; - } - var array = new List(items.Length); - for (var i = 0; i < items.Length; i++) - { - var item = items[i]; - switch (item) - { - case ".": break; - case "..": array.RemoveAt(array.Count - 1); break; - default: array.Add(item); break; - } - } - return Combine(array.ToArray()); - } - } +using System; +using System.Collections.Generic; + +namespace Duktape +{ + public static class PathUtils + { + public static string GetDirectoryName(string path) + { + return string.IsNullOrEmpty(path) ? path : System.IO.Path.GetDirectoryName(path).Replace('\\', '/'); + } + + public static string Combine(string path1, string path2) + { + return System.IO.Path.Combine(path1, path2).Replace('\\', '/'); + } + + public static string Combine(string path1, string path2, string path3) + { + return System.IO.Path.Combine(path1, path2, path3).Replace('\\', '/'); + } + + public static string Combine(string path1, string path2, string path3, string path4) + { + return System.IO.Path.Combine(path1, path2, path3, path4).Replace('\\', '/'); + } + + public static string Combine(params string[] paths) + { + return System.IO.Path.Combine(paths).Replace('\\', '/'); + } + + /// 展开路径中的 ./.. + public static string ExtractPath(string path, char sp) + { + var items = path.Split(sp); + if (items.Length < 2) + { + return path; + } + var array = new List(items.Length); + for (var i = 0; i < items.Length; i++) + { + var item = items[i]; + switch (item) + { + case ".": break; + case "..": array.RemoveAt(array.Count - 1); break; + default: array.Add(item); break; + } + } + return Combine(array.ToArray()); + } + } } \ No newline at end of file