Skip to content

Commit b08519c

Browse files
[native_doc_dartifier] Retry loop to fix compile errors of generated snippets (#2416)
* Add a simple retry loop that attempts to fix compilation errors in the generated Dart snippets. * Switched to using a chat-based interface instead of independent prompts.
1 parent 17484a8 commit b08519c

File tree

4 files changed

+176
-9
lines changed

4 files changed

+176
-9
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
class CodeProcessor {
8+
final Directory _tempDir;
9+
final String _dartifiedCodeFileName = 'dartified_code.dart';
10+
final String _helperCodeFileName = 'helper_code.dart';
11+
12+
CodeProcessor() : _tempDir = Directory('${Directory.current.path}/temp') {
13+
if (!_tempDir.existsSync()) {
14+
_tempDir.createSync(recursive: true);
15+
}
16+
}
17+
18+
Future<String> analyzeCode(String mainCode, String helperCode) async {
19+
await _saveCodeToFile(mainCode, helperCode);
20+
final dartifiedFile = File('${_tempDir.path}/$_dartifiedCodeFileName');
21+
final analysisResult = await Process.run('dart', [
22+
'analyze',
23+
dartifiedFile.absolute.path,
24+
], runInShell: true);
25+
26+
var errorMessage = '';
27+
if (analysisResult.exitCode != 0) {
28+
final allLines = analysisResult.stdout.toString().trim().split('\n');
29+
final errorLines =
30+
allLines.where((line) => line.trim().startsWith('error -')).toList();
31+
32+
errorMessage = errorLines.join('\n');
33+
print(errorMessage);
34+
} else {
35+
print('Dart analysis completed successfully with no errors.');
36+
}
37+
await _cleanUp();
38+
return errorMessage;
39+
}
40+
41+
Future<void> _saveCodeToFile(String mainCode, String helperCode) async {
42+
try {
43+
final dartifiedFile = File('${_tempDir.path}/$_dartifiedCodeFileName');
44+
await dartifiedFile.writeAsString(mainCode);
45+
46+
final helperFile = File('${_tempDir.path}/$_helperCodeFileName');
47+
await helperFile.writeAsString(helperCode);
48+
} catch (e) {
49+
print('Error saving code to file: $e');
50+
}
51+
}
52+
53+
String addImports(String code, List<String> imports) {
54+
final buffer = StringBuffer();
55+
for (final import in imports) {
56+
buffer.writeln("import '$import';");
57+
}
58+
buffer.writeln("import '$_helperCodeFileName';");
59+
buffer.writeln();
60+
buffer.write(code);
61+
return buffer.toString();
62+
}
63+
64+
String removeImports(String code) {
65+
final lines = code.split('\n');
66+
final filteredLines = lines.where((line) => !line.startsWith('import'));
67+
return filteredLines.join('\n');
68+
}
69+
70+
Future<void> _cleanUp() async {
71+
try {
72+
if (await _tempDir.exists()) {
73+
final files = _tempDir.listSync().whereType<File>();
74+
for (final file in files) {
75+
await file.delete();
76+
}
77+
}
78+
} catch (e) {
79+
print('Error cleaning up temporary directory: $e');
80+
}
81+
}
82+
}

pkgs/native_doc_dartifier/lib/src/dartify_code.dart

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
import 'dart:io';
66
import 'package:google_generative_ai/google_generative_ai.dart';
7+
import 'code_processor.dart';
78
import 'prompts.dart';
89
import 'public_abstractor.dart';
910

1011
Future<String> dartifyNativeCode(String sourceCode, String bindingsPath) async {
11-
final file = File(bindingsPath);
12+
final bindingsFile = File(bindingsPath);
1213

13-
if (!await file.exists()) {
14+
if (!await bindingsFile.exists()) {
1415
stderr.writeln('File not found: $bindingsPath');
1516
exit(1);
1617
}
@@ -22,7 +23,7 @@ Future<String> dartifyNativeCode(String sourceCode, String bindingsPath) async {
2223
exit(1);
2324
}
2425

25-
final bindings = await file.readAsString();
26+
final bindings = await bindingsFile.readAsString();
2627

2728
final model = GenerativeModel(
2829
model: 'gemini-2.0-flash',
@@ -41,12 +42,35 @@ Future<String> dartifyNativeCode(String sourceCode, String bindingsPath) async {
4142
generateBindingsSummary(bindings),
4243
);
4344

44-
print('Prompt:\n${translatePrompt.prompt}\n');
45+
final chatSession = model.startChat();
4546

46-
final content = [Content.text(translatePrompt.prompt)];
47+
final response = await chatSession.sendMessage(
48+
Content.text(translatePrompt.prompt),
49+
);
50+
var mainCode = translatePrompt.getParsedResponse(response.text ?? '');
51+
var helperCode = '';
52+
53+
final codeProcessor = CodeProcessor();
54+
mainCode = codeProcessor.addImports(mainCode, [
55+
'package:jni/jni.dart',
56+
bindingsFile.path,
57+
]);
4758

48-
final response = await model.generateContent(content);
49-
final dartCode = translatePrompt.getParsedResponse(response.text ?? '');
59+
for (var i = 0; i < 3; i++) {
60+
final errorMessage = await codeProcessor.analyzeCode(mainCode, helperCode);
61+
if (errorMessage.isEmpty) {
62+
break;
63+
}
64+
stderr.writeln('Dart analysis found issues: $errorMessage');
65+
final fixPrompt = FixPrompt(mainCode, helperCode, errorMessage);
66+
final fixResponse = await chatSession.sendMessage(
67+
Content.text(fixPrompt.prompt),
68+
);
69+
final fixedCode = fixPrompt.getParsedResponse(fixResponse.text ?? '');
5070

51-
return dartCode;
71+
mainCode = fixedCode.mainCode;
72+
helperCode = fixedCode.helperCode;
73+
}
74+
mainCode = codeProcessor.removeImports(mainCode);
75+
return mainCode;
5276
}

pkgs/native_doc_dartifier/lib/src/prompts.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,61 @@ $_schema
118118
return dartCode;
119119
}
120120
}
121+
122+
class FixPrompt implements Prompt {
123+
final String mainDartCode;
124+
final String helperDartCode;
125+
final String analysisResult;
126+
127+
FixPrompt(this.mainDartCode, this.helperDartCode, this.analysisResult);
128+
129+
// Change this will require changing the getParsedResponse() method.
130+
final String _schema = '''
131+
Output the response in JSON format:
132+
{
133+
"mainDartCode": "Dart code here",
134+
"helperDartCode": "Dart code here"
135+
}
136+
''';
137+
138+
@override
139+
String get prompt => '''
140+
You are an expert Dart code fixer. Your task is to fix the provided main Dart code based on the analysis results.
141+
Here is the main Dart file code that needs fixing:
142+
$mainDartCode
143+
Here is the helper Dart file that you can use to add initialization code or other necessary imports to fix the issues in the main Dart code file:
144+
$helperDartCode
145+
Here are the issues found in the main Dart code:
146+
$analysisResult
147+
148+
$_schema
149+
150+
try to add initialization or imports or whatever in the helper Dart file to fix the errors as the main Dart file import this helper Dart file.
151+
change the main Dart file only if you can't fix the issues by adding code to the helper Dart file.
152+
do not make a full initializaiton or completed code in the helper Dart file, just add the necessary code to fix the issues in the main Dart file even if not complete.
153+
154+
Make sure to not use a backslash (`\\`) before Dollar sign ('\$') in the Dart code, as it is not a valid Dart.
155+
''';
156+
157+
@override
158+
FixResponse getParsedResponse(String response) {
159+
print('Response: $response');
160+
final json = jsonDecode(response) as Map<String, dynamic>;
161+
var mainDartCode = '';
162+
var tempDartCode = '';
163+
if (json.containsKey('mainDartCode')) {
164+
mainDartCode = json['mainDartCode'].toString();
165+
}
166+
if (json.containsKey('helperDartCode')) {
167+
tempDartCode = json['helperDartCode'].toString();
168+
}
169+
return FixResponse(mainCode: mainDartCode, helperCode: tempDartCode);
170+
}
171+
}
172+
173+
class FixResponse {
174+
final String mainCode;
175+
final String helperCode;
176+
177+
FixResponse({required this.mainCode, required this.helperCode});
178+
}

pkgs/native_doc_dartifier/tool/prepare_dartify_test.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ void compileJavaPackage() {
5959
void generateDartSnippets() async {
6060
for (final snippet in snippets) {
6161
final sourceCode = snippet['code'] as String;
62-
final dartCode = await dartifyNativeCode(sourceCode, bindingsPath);
62+
final dartCode = await dartifyNativeCode(
63+
sourceCode,
64+
File(bindingsPath).absolute.path,
65+
);
6366
final fileName = snippet['fileName'];
6467
final outputFile = File('$workingDir/$dartifiedSnippetsDir/$fileName');
6568
if (!outputFile.parent.existsSync()) {

0 commit comments

Comments
 (0)