1+ using Microsoft . Extensions . Logging ;
2+ using Microsoft . ClearScript ;
3+ using System ;
4+ using System . Reflection ;
5+ using System . Reflection . Metadata ;
6+ using System . Reflection . Metadata . Ecma335 ;
7+ using System . Reflection . PortableExecutable ;
8+ using System . Collections . Generic ;
9+ using System . Linq ;
10+
11+ namespace BetterGenshinImpact . Core . Script . Dependence ;
12+
13+ public class TypeHelper
14+ {
15+ private HostFunctions hostFunctions = new HostFunctions ( ) ;
16+ public TypeHelper ( IScriptEngine engine )
17+ {
18+ }
19+ private readonly ILogger < TypeHelper > _logger = App . GetLogger < TypeHelper > ( ) ;
20+
21+ public bool isTypeObj ( object obj )
22+ {
23+ if ( obj == null ) return false ;
24+ return hostFunctions . isTypeObj ( obj ) ;
25+ }
26+
27+ private Type getRealType ( object obj )
28+ {
29+ var asm = typeof ( HostItemFlags ) . Assembly ;
30+
31+ // public Type[] Types { get; }
32+ var hostType = asm . GetType ( "Microsoft.ClearScript.HostType" , true ) ;
33+ if ( hostType == null ) throw new InvalidOperationException ( "HostType type not found" ) ;
34+ var typesProp = hostType . GetProperty ( "Types" , BindingFlags . Public | BindingFlags . Instance ) ;
35+ if ( typesProp == null ) throw new InvalidOperationException ( "HostType.Types property not found" ) ;
36+ var types = ( Type [ ] ? ) typesProp . GetValue ( obj ) ;
37+ if ( types == null || types . Length == 0 ) throw new InvalidOperationException ( "HostType.Types is null or empty" ) ;
38+ if ( types . Length > 1 ) throw new InvalidOperationException ( "HostType.Types has multiple types, cannot determine which one to create" ) ;
39+ var type = types [ 0 ] ;
40+ return type ;
41+ }
42+
43+ /// <summary>
44+ /// 泛型重载:ClearScript 在传入 HostType 参数时会将其解释为泛型类型参数,
45+ /// 即 JS 调用 TypeHelper.GetNullInstance(Mat) 会被解析为 GetNullInstance<Mat>()。
46+ /// </summary>
47+ public object GetNullInstance < T > ( )
48+ {
49+ return GetNullInstanceFromType ( typeof ( T ) ) ;
50+ }
51+
52+ public object GetNullInstance ( object obj )
53+ {
54+ // obj是HostType,通过反射获取真实Type,然后创建一个nullwrapper
55+ return GetNullInstanceFromType ( getRealType ( obj ) ) ;
56+ }
57+
58+ private object GetNullInstanceFromType ( Type type )
59+ {
60+ // 通过反射创建一个 null wrapper
61+ var asm = typeof ( HostItemFlags ) . Assembly ;
62+ // _logger.LogDebug($"[Script] 创建类型 {type.FullName} 的 null HostObject");
63+
64+ // private static HostObject GetNullWrapper(Type type)
65+ var hostObject = asm . GetType ( "Microsoft.ClearScript.HostObject" , true ) ;
66+ if ( hostObject == null ) throw new InvalidOperationException ( "HostObject type not found" ) ;
67+ var getNullWrapperMethod = hostObject . GetMethod ( "GetNullWrapper" , BindingFlags . NonPublic | BindingFlags . Static ) ;
68+ if ( getNullWrapperMethod == null ) throw new InvalidOperationException ( "HostObject.GetNullWrapper method not found" ) ;
69+ var instance = getNullWrapperMethod . Invoke ( null , [ type] ) ;
70+ if ( instance == null ) throw new InvalidOperationException ( "HostObject.GetNullWrapper returned null" ) ;
71+
72+ return instance ;
73+ }
74+
75+ public Type GetType ( object obj )
76+ {
77+ if ( obj == null ) throw new ArgumentNullException ( nameof ( obj ) ) ;
78+ _logger . LogDebug ( $ "[Script] 获取对象 { obj } 的类型 { obj ? . GetType ( ) . FullName } ") ;
79+ return obj . GetType ( ) ;
80+ }
81+
82+ private Dictionary < string , object > dumpMethodInfo ( MethodInfo methodInfo , MetadataReader ? pdbReader = null )
83+ {
84+ var parameters = methodInfo . GetParameters ( ) ;
85+ var paramStrs = new List < string > ( ) ;
86+ foreach ( var param in parameters )
87+ {
88+ var paramStr = $ "{ param . ParameterType . Name } { param . Name } ";
89+ if ( param . IsOptional )
90+ {
91+ paramStr += $ " = { param . DefaultValue ?? "null" } ";
92+ }
93+ paramStrs . Add ( paramStr ) ;
94+ }
95+ var definition = $ "{ methodInfo . Name } ({ string . Join ( ", " , paramStrs ) } )";
96+ _logger . LogDebug ( $ "[Script] 方法定义: { definition } ") ;
97+ var definitionDict = new Dictionary < string , object >
98+ {
99+ { "name" , methodInfo . Name } ,
100+ { "definition" , definition } ,
101+ { "parameterTypes" , parameters . AsEnumerable ( ) . Select ( p => p . ParameterType . FullName ) } ,
102+ } ;
103+
104+ // 尝试从 PDB 获取源码位置
105+ if ( pdbReader != null )
106+ {
107+ try
108+ {
109+ var methodHandle = MetadataTokens . MethodDefinitionHandle ( methodInfo . MetadataToken ) ;
110+ var methodDebugInfo = pdbReader . GetMethodDebugInformation ( methodHandle ) ;
111+ if ( ! methodDebugInfo . SequencePointsBlob . IsNil )
112+ {
113+ foreach ( var sp in methodDebugInfo . GetSequencePoints ( ) )
114+ {
115+ if ( ! sp . Document . IsNil && ! sp . IsHidden )
116+ {
117+ var document = pdbReader . GetDocument ( sp . Document ) ;
118+ var filePath = pdbReader . GetString ( document . Name ) ;
119+ definitionDict [ "sourceFile" ] = filePath ;
120+ definitionDict [ "sourceLine" ] = sp . StartLine ;
121+ break ;
122+ }
123+ }
124+ }
125+ }
126+ catch ( Exception ex )
127+ {
128+ _logger . LogDebug ( $ "[Script] 无法获取方法 { methodInfo . Name } 的源码位置: { ex . Message } ") ;
129+ }
130+ }
131+
132+ return definitionDict ;
133+ }
134+
135+ public string GetMethodDefinition ( object method )
136+ {
137+ _logger . LogDebug ( $ "[Script] 获取方法 { method } 的定义") ;
138+ var asm = typeof ( HostItemFlags ) . Assembly ;
139+ var hostMethod = asm . GetType ( "Microsoft.ClearScript.HostMethod" , true ) ;
140+ if ( hostMethod == null ) throw new InvalidOperationException ( "HostMethod type not found" ) ;
141+ // private readonly HostItem target;
142+ var targetField = hostMethod . GetField ( "target" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
143+ if ( targetField == null ) throw new InvalidOperationException ( "HostMethod.target field not found" ) ;
144+ var target = targetField . GetValue ( method ) ;
145+ if ( target == null ) throw new InvalidOperationException ( "HostMethod.target is null" ) ;
146+ var targetType = target . GetType ( ) ;
147+
148+ // assert target type is HostItem
149+ var hostItem = asm . GetType ( "Microsoft.ClearScript.HostItem" , true ) ;
150+ if ( hostItem == null ) throw new InvalidOperationException ( "HostItem type not found" ) ;
151+ if ( ! hostItem . IsAssignableFrom ( targetType ) )
152+ {
153+ throw new InvalidOperationException ( $ "HostMethod.target is not a HostItem, but { targetType . FullName } ") ;
154+ }
155+
156+ // private readonly string name;
157+ var nameField = hostMethod . GetField ( "name" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
158+ if ( nameField == null ) throw new InvalidOperationException ( "HostItem.name field not found" ) ;
159+ var name = ( string ? ) nameField . GetValue ( method ) ;
160+ if ( name == null ) throw new InvalidOperationException ( "HostItem.name is null" ) ;
161+
162+ _logger . LogDebug ( $ "[Script] 获取方法定义 { target } { name } ") ;
163+
164+ // public HostTarget Target { get; }
165+ var targetProp = hostItem . GetProperty ( "Target" , BindingFlags . Public | BindingFlags . Instance ) ;
166+ if ( targetProp == null ) throw new InvalidOperationException ( "HostItem.Target property not found" ) ;
167+ var targetValue = targetProp . GetValue ( target ) ; // should be hostType
168+ _logger . LogDebug ( $ "[Script] 方法目标类型 { targetValue } { targetValue . GetType ( ) . FullName } ") ;
169+
170+ // if type is HostType, get the real Type
171+ if ( isTypeObj ( targetValue ) )
172+ {
173+ return GetMethodDefinitionForType ( targetValue , name ) ;
174+ }
175+ else
176+ {
177+ throw new InvalidOperationException ( $ "HostItem.Target is not a HostType, but { targetValue ? . GetType ( ) . FullName } ") ;
178+ }
179+ }
180+
181+ private string dumpMethodDefinitionForType ( Type type , string methodName )
182+ {
183+ _logger . LogDebug ( $ "[Script] 获取类型 { type . FullName } 的方法 { methodName } 定义") ;
184+ var matchedMethods = new List < MethodInfo > ( ) ;
185+ foreach ( var methodInfo in type . GetMethods ( BindingFlags . Public | BindingFlags . Instance | BindingFlags . Static ) )
186+ {
187+ if ( methodInfo . Name . Equals ( methodName , StringComparison . OrdinalIgnoreCase ) )
188+ {
189+ matchedMethods . Add ( methodInfo ) ;
190+ }
191+ }
192+ if ( matchedMethods . Count == 0 )
193+ {
194+ throw new InvalidOperationException ( $ "类型 { type . FullName } 没有找到方法 { methodName } ") ;
195+ }
196+
197+ // 尝试打开 Portable PDB 获取源码位置
198+ System . IO . FileStream ? fs = null ;
199+ PEReader ? peReader = null ;
200+ MetadataReaderProvider ? pdbProvider = null ;
201+ MetadataReader ? pdbReader = null ;
202+
203+ try
204+ {
205+ var location = type . Assembly . Location ;
206+ if ( ! string . IsNullOrEmpty ( location ) && System . IO . File . Exists ( location ) )
207+ {
208+ fs = System . IO . File . OpenRead ( location ) ;
209+ peReader = new PEReader ( fs ) ;
210+
211+ // 优先尝试嵌入式 Portable PDB
212+ foreach ( var entry in peReader . ReadDebugDirectory ( ) )
213+ {
214+ if ( entry . Type == DebugDirectoryEntryType . EmbeddedPortablePdb )
215+ {
216+ pdbProvider = peReader . ReadEmbeddedPortablePdbDebugDirectoryData ( entry ) ;
217+ break ;
218+ }
219+ }
220+
221+ // 其次尝试关联的 .pdb 文件
222+ if ( pdbProvider == null )
223+ {
224+ peReader . TryOpenAssociatedPortablePdb ( location , System . IO . File . OpenRead , out pdbProvider , out _ ) ;
225+ }
226+
227+ pdbReader = pdbProvider ? . GetMetadataReader ( ) ;
228+ }
229+ }
230+ catch ( Exception ex )
231+ {
232+ _logger . LogDebug ( $ "[Script] 无法打开 PDB 以读取源码位置: { ex . Message } ") ;
233+ }
234+
235+ try
236+ {
237+ // convert method info to string function definition
238+ var definitions = new List < Dictionary < string , object > > ( ) ;
239+ foreach ( var methodInfo in matchedMethods )
240+ {
241+ definitions . Add ( dumpMethodInfo ( methodInfo , pdbReader ) ) ;
242+ }
243+ // return json string
244+ return System . Text . Json . JsonSerializer . Serialize ( definitions ) ;
245+ }
246+ finally
247+ {
248+ pdbProvider ? . Dispose ( ) ;
249+ peReader ? . Dispose ( ) ;
250+ fs ? . Dispose ( ) ;
251+ }
252+ }
253+
254+ public string GetMethodDefinitionForType ( object typeObj , string methodName )
255+ {
256+ if ( ! isTypeObj ( typeObj ) )
257+ {
258+ throw new ArgumentException ( "参数不是类型对象" , nameof ( typeObj ) ) ;
259+ }
260+ return dumpMethodDefinitionForType ( getRealType ( typeObj ) , methodName ) ;
261+ }
262+ public string GetMethodDefinitionForType < T > ( string methodName )
263+ {
264+ return dumpMethodDefinitionForType ( typeof ( T ) , methodName ) ;
265+ }
266+ }
0 commit comments