Skip to content

Commit f307bf1

Browse files
committed
C# RPC visitor wiring for running Java visitors on C# trees
1 parent 3cc5345 commit f307bf1

3 files changed

Lines changed: 49 additions & 7 deletions

File tree

rewrite-core/src/main/java/org/openrewrite/rpc/RewriteRpc.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,8 +482,9 @@ public <T> T getObject(String id, @Nullable String sourceFileType) {
482482
remoteObjects.remove(id);
483483
throw e;
484484
}
485-
if (q.take().getState() != END_OF_OBJECT) {
486-
throw new IllegalStateException("Expected END_OF_OBJECT");
485+
RpcObjectData endMarker = q.take();
486+
if (endMarker.getState() != END_OF_OBJECT) {
487+
throw new IllegalStateException("Expected END_OF_OBJECT but got: " + endMarker);
487488
}
488489

489490
//noinspection ConstantValue

rewrite-csharp/csharp/OpenRewrite/CSharp/Rpc/RewriteRpcServer.cs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ public class RewriteRpcServer
4141
/// </summary>
4242
public static RewriteRpcServer? Current => _current;
4343

44+
/// <summary>
45+
/// Sets the current RPC server instance. Used by test infrastructure to wire up
46+
/// an RPC connection to a Java process without going through RunAsync().
47+
/// Matches the JavaScript pattern: RewriteRpc.set(value) / RewriteRpc.get().
48+
/// </summary>
49+
public static void SetCurrent(RewriteRpcServer? server) => _current = server;
50+
4451
private readonly RecipeMarketplace _marketplace;
4552
private readonly Dictionary<string, Recipe> _preparedRecipes = new();
4653
private string? _recipesProjectDir;
@@ -66,6 +73,17 @@ public class RewriteRpcServer
6673
/// </summary>
6774
private readonly Dictionary<int, object> _remoteRefs = new();
6875

76+
/// <summary>
77+
/// Connects this server to a remote JSON-RPC peer. Used by test infrastructure
78+
/// to wire up an RPC connection to a Java process.
79+
/// </summary>
80+
public void Connect(JsonRpc jsonRpc)
81+
{
82+
_jsonRpc = jsonRpc;
83+
jsonRpc.AddLocalRpcTarget(this);
84+
jsonRpc.StartListening();
85+
}
86+
6987
public RewriteRpcServer(RecipeMarketplace marketplace)
7088
{
7189
_marketplace = marketplace;
@@ -247,6 +265,17 @@ public Task<List<RpcObjectData>> GetObject(GetObjectRequest request)
247265
});
248266
}
249267

268+
// ExecutionContext is sent as a typed shell with no data,
269+
// matching the JavaScript pattern (empty codec).
270+
if (after is ExecutionContext)
271+
{
272+
return Task.FromResult(new List<RpcObjectData>
273+
{
274+
new() { State = ADD, ValueType = "org.openrewrite.InMemoryExecutionContext" },
275+
new() { State = END_OF_OBJECT }
276+
});
277+
}
278+
250279
var before = _remoteObjects.GetValueOrDefault(request.Id);
251280
var sw = Stopwatch.StartNew();
252281

@@ -799,13 +828,17 @@ public PrepareRecipeResponse PrepareRecipeOnRemote(string recipeId, Dictionary<s
799828
/// so Java can fetch it via the GetObject callback.
800829
/// Returns the (possibly modified) tree.
801830
/// </summary>
802-
public Tree VisitOnRemote(string visitorName, string treeId, string? sourceFileType)
831+
public Tree VisitOnRemote(string visitorName, string treeId, string? sourceFileType,
832+
string? pId = null)
803833
{
804834
var response = _jsonRpc!.InvokeWithParameterObjectAsync<VisitResponse>(
805835
"Visit",
806-
new VisitRequest { VisitorName = visitorName, TreeId = treeId, SourceFileType = sourceFileType }
836+
new VisitRequest { VisitorName = visitorName, TreeId = treeId, SourceFileType = sourceFileType, PId = pId }
807837
).GetAwaiter().GetResult();
808838

839+
Log.Debug("RPC VisitOnRemote: {VisitorName} on {TreeId} => modified={Modified}",
840+
visitorName, treeId, response.Modified);
841+
809842
if (response.Modified)
810843
{
811844
return GetObjectFromRemoteAsync(treeId, sourceFileType).GetAwaiter().GetResult();
@@ -895,7 +928,7 @@ public class RecipeDescriptorDto
895928
public string Name { get; set; } = "";
896929
public string DisplayName { get; set; } = "";
897930
public string Description { get; set; } = "";
898-
public IReadOnlySet<string>? Tags { get; set; }
931+
public HashSet<string>? Tags { get; set; }
899932
public string? EstimatedEffortPerOccurrence { get; set; }
900933
public List<OptionDescriptorDto>? Options { get; set; }
901934
public List<RecipeDescriptorDto>? RecipeList { get; set; }
@@ -907,7 +940,7 @@ public static RecipeDescriptorDto FromDescriptor(RecipeDescriptor d)
907940
Name = d.Name,
908941
DisplayName = d.DisplayName,
909942
Description = d.Description,
910-
Tags = d.Tags.Count > 0 ? d.Tags : null,
943+
Tags = d.Tags.Count > 0 ? new HashSet<string>(d.Tags) : null,
911944
EstimatedEffortPerOccurrence = d.EstimatedEffortPerOccurrence?.ToString(),
912945
Options = d.Options.Count > 0
913946
? d.Options.Select(OptionDescriptorDto.FromDescriptor).ToList()
@@ -973,10 +1006,13 @@ public class PrepareRecipeResponse
9731006

9741007
public class VisitRequest
9751008
{
1009+
[JsonProperty("visitor")]
9761010
public string VisitorName { get; set; } = "";
9771011
public string? SourceFileType { get; set; }
9781012
public string TreeId { get; set; } = "";
1013+
[JsonProperty("p")]
9791014
public string? PId { get; set; }
1015+
[JsonProperty("cursor")]
9801016
public List<string>? CursorIds { get; set; }
9811017
}
9821018

rewrite-csharp/csharp/OpenRewrite/Core/Rpc/RpcVisitor.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ public RpcVisitor(RewriteRpcServer rpc, string visitorName)
4444
var treeId = sf.Id.ToString();
4545
_rpc.StoreLocalObject(treeId, sf);
4646

47-
var result = _rpc.VisitOnRemote(_visitorName, treeId, "Cs");
47+
// Store the execution context so Java can retrieve it via GetObject
48+
var ctxId = Guid.NewGuid().ToString();
49+
_rpc.StoreLocalObject(ctxId, ctx);
50+
51+
var result = _rpc.VisitOnRemote(_visitorName, treeId,
52+
"org.openrewrite.csharp.tree.Cs$CompilationUnit", ctxId);
4853
return result as J;
4954
}
5055
}

0 commit comments

Comments
 (0)