55using System . Reflection ;
66using System . Threading ;
77using System . Threading . Tasks ;
8+ using Kscript . CSharp . Parser . Core ;
9+ using Microsoft . CodeAnalysis ;
10+ using Microsoft . CodeAnalysis . CSharp ;
11+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
812using Microsoft . CodeAnalysis . CSharp . Scripting ;
913using Microsoft . CodeAnalysis . Scripting ;
1014using KitX . Core . Contract . Workflow ;
@@ -24,6 +28,7 @@ public class BlockScriptExecutor : IBlockScriptExecutor
2428 private BlockScript ? _currentScript ;
2529 private List < string > _output = new ( ) ;
2630 private BlockScriptExecutionGlobals ? _globals ;
31+ private IPluginManager ? _pluginManager ;
2732
2833 /// <summary>
2934 /// Creates a new block script executor
@@ -41,6 +46,22 @@ public BlockScriptExecutor(BlockScopeManager scopeManager)
4146 _scopeManager = scopeManager ;
4247 }
4348
49+ /// <summary>
50+ /// Creates a new block script executor with plugin manager support
51+ /// </summary>
52+ public BlockScriptExecutor ( IPluginManager ? pluginManager ) : this ( )
53+ {
54+ _pluginManager = pluginManager ;
55+ }
56+
57+ /// <summary>
58+ /// Sets the plugin manager for plugin function calls during execution.
59+ /// </summary>
60+ public void SetPluginManager ( IPluginManager ? pluginManager )
61+ {
62+ _pluginManager = pluginManager ;
63+ }
64+
4465 /// <summary>
4566 /// Executes a block script
4667 /// </summary>
@@ -488,6 +509,9 @@ private async Task ExecuteExpressionAsync(
488509 {
489510 try
490511 {
512+ // Pre-process: rewrite dotted plugin calls (e.g. "Plugin.Func()") to PluginCall(...)
513+ expression = PreProcessPluginCalls ( expression ) ;
514+
491515 // Build a complete statement for execution
492516 var code = WrapExpressionAsStatement ( expression ) ;
493517
@@ -524,7 +548,7 @@ private async Task ExecuteExpressionAsync(
524548 else
525549 {
526550 // First evaluation: create initial script state using RunAsync to get ScriptState
527- _globals = new BlockScriptExecutionGlobals ( _scopeManager , _output ) ;
551+ _globals = new BlockScriptExecutionGlobals ( _scopeManager , _output , _pluginManager ) ;
528552 var globals = _globals ;
529553 _scriptState = await CSharpScript . RunAsync (
530554 fullCode ,
@@ -560,6 +584,106 @@ private string WrapExpressionAsStatement(string expression)
560584 return expression + ";" ;
561585 }
562586
587+ /// <summary>
588+ /// Preprocesses an expression to rewrite dotted plugin calls into PluginCall() invocations.
589+ /// Uses Roslyn SyntaxRewriter for safe, AST-level transformation that correctly handles
590+ /// nested parentheses, complex expressions, and all edge cases.
591+ /// Transforms: TestPlugin.WPF.Core.HelloKitX(arg1, arg2)
592+ /// Into: PluginCall("TestPlugin.WPF.Core", "HelloKitX", arg1, arg2)
593+ /// Transforms: TestPlugin.WPF.Core.HelloKitX()
594+ /// Into: PluginCall("TestPlugin.WPF.Core", "HelloKitX")
595+ /// </summary>
596+ private static string PreProcessPluginCalls ( string expression )
597+ {
598+ try
599+ {
600+ var wrappedCode = "_ = " + expression + ";" ;
601+ var syntaxTree = CSharpSyntaxTree . ParseText ( wrappedCode ) ;
602+ var root = syntaxTree . GetCompilationUnitRoot ( ) ;
603+
604+ var rewriter = new PluginCallRewriter ( ) ;
605+ var rewritten = rewriter . Visit ( root ) ;
606+
607+ // Extract the expression back from "_ = ...;"
608+ if ( rewritten is CompilationUnitSyntax cu
609+ && cu . Members . FirstOrDefault ( ) is GlobalStatementSyntax gs
610+ && gs . Statement is ExpressionStatementSyntax ess
611+ && ess . Expression is AssignmentExpressionSyntax aes )
612+ {
613+ var result = aes . Right . ToString ( ) ;
614+ if ( result != expression )
615+ {
616+ Log . Debug ( "[BlockScriptExecutor] PreProcessPluginCalls: '{Original}' → '{Result}'" ,
617+ expression , result ) ;
618+ }
619+ return result ;
620+ }
621+
622+ Log . Warning ( "[BlockScriptExecutor] PreProcessPluginCalls: extraction failed for '{Expression}', " +
623+ "rewritten type={Type}" , expression , rewritten ? . GetType ( ) . Name ) ;
624+ return expression ;
625+ }
626+ catch ( Exception ex )
627+ {
628+ Log . Warning ( ex , "[BlockScriptExecutor] PreProcessPluginCalls: exception for '{Expression}'" ,
629+ expression ) ;
630+ return expression ;
631+ }
632+ }
633+
634+ /// <summary>
635+ /// Roslyn SyntaxRewriter that transforms dotted member-access invocations
636+ /// (e.g. TestPlugin.WPF.Core.HelloKitX()) into PluginCall("...", "...", args) calls.
637+ /// Operates at the AST level, preserving all expression structure and handling nesting.
638+ /// </summary>
639+ private class PluginCallRewriter : CSharpSyntaxRewriter
640+ {
641+ public override SyntaxNode VisitInvocationExpression ( InvocationExpressionSyntax node )
642+ {
643+ // First, recursively rewrite any nested invocations in arguments
644+ node = ( InvocationExpressionSyntax ) base . VisitInvocationExpression ( node ) ! ;
645+
646+ // Only rewrite member-access invocations (e.g. A.B.C.Method())
647+ if ( node . Expression is not MemberAccessExpressionSyntax member )
648+ return node ;
649+
650+ // Only rewrite when the expression has dots (plugin namespace path)
651+ var fullExpression = member . Expression . ToString ( ) ;
652+ if ( ! fullExpression . Contains ( '.' ) )
653+ return node ;
654+
655+ var pluginName = fullExpression ;
656+ var funcName = member . Name . Identifier . Text ;
657+
658+ Log . Debug ( "[PluginCallRewriter] Rewriting: {Plugin}.{Method}() → PluginCall()" ,
659+ pluginName , funcName ) ;
660+
661+ // Build: PluginCall("pluginName", "funcName" [, existingArgs])
662+ // 所有分发策略(类型化调用、f-a-f vs 同步等待)由 Manager 内部自动处理
663+ var args = new List < ArgumentSyntax >
664+ {
665+ SyntaxFactory . Argument (
666+ SyntaxFactory . LiteralExpression (
667+ SyntaxKind . StringLiteralExpression ,
668+ SyntaxFactory . Literal ( pluginName ) ) ) ,
669+ SyntaxFactory . Argument (
670+ SyntaxFactory . LiteralExpression (
671+ SyntaxKind . StringLiteralExpression ,
672+ SyntaxFactory . Literal ( funcName ) ) )
673+ } ;
674+
675+ // Append original arguments
676+ args . AddRange ( node . ArgumentList . Arguments ) ;
677+
678+ var newArgsList = SyntaxFactory . SeparatedList ( args ) ;
679+ var newInvocation = SyntaxFactory . InvocationExpression (
680+ SyntaxFactory . IdentifierName ( "PluginCall" ) ,
681+ SyntaxFactory . ArgumentList ( newArgsList ) ) ;
682+
683+ return newInvocation ;
684+ }
685+ }
686+
563687 /// <summary>
564688 /// Builds helper function code from a list of helper functions
565689 /// </summary>
@@ -644,7 +768,7 @@ private async Task InitializeScriptSessionAsync(CancellationToken cancellationTo
644768 if ( string . IsNullOrWhiteSpace ( initCode ) )
645769 {
646770 // No initialization needed, just do a simple first evaluation to establish session
647- _globals = new BlockScriptExecutionGlobals ( _scopeManager , _output ) ;
771+ _globals = new BlockScriptExecutionGlobals ( _scopeManager , _output , _pluginManager ) ;
648772 _scriptState = await CSharpScript . RunAsync (
649773 "0" , // Simple expression to establish session
650774 ScriptOptions . Default
@@ -659,7 +783,7 @@ private async Task InitializeScriptSessionAsync(CancellationToken cancellationTo
659783
660784 try
661785 {
662- _globals = new BlockScriptExecutionGlobals ( _scopeManager , _output ) ;
786+ _globals = new BlockScriptExecutionGlobals ( _scopeManager , _output , _pluginManager ) ;
663787 _scriptState = await CSharpScript . RunAsync (
664788 initCode ,
665789 ScriptOptions . Default
@@ -672,7 +796,7 @@ private async Task InitializeScriptSessionAsync(CancellationToken cancellationTo
672796 {
673797 Log . Warning ( ex , "[BlockScriptExecutor] Initialization script failed, will try without it" ) ;
674798 // Fall back to simple session establishment
675- _globals = new BlockScriptExecutionGlobals ( _scopeManager , _output ) ;
799+ _globals = new BlockScriptExecutionGlobals ( _scopeManager , _output , _pluginManager ) ;
676800 _scriptState = await CSharpScript . RunAsync (
677801 "0" ,
678802 ScriptOptions . Default
0 commit comments