| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:io'; |
| import 'dart:ffi'; |
| |
| final repoRoot = File.fromUri(Platform.script).parent.parent.parent.path; |
| final runtimeRoot = '$repoRoot/runtime'; |
| final buildtoolsRoot = '$repoRoot/buildtools'; |
| final clangBinDir = |
| '$buildtoolsRoot/$currentPlatformBuildtoolsSubdir/clang/bin'; |
| final clangFormatBin = '$clangBinDir/clang-format'; |
| |
| final currentPlatformBuildtoolsSubdir = switch (Abi.current()) { |
| Abi.macosX64 => 'mac-x64', |
| Abi.macosArm64 => 'mac-arm64', |
| Abi.linuxX64 => 'linux-x64', |
| Abi.linuxArm64 => 'linux-arm64', |
| Abi.windowsX64 => 'win-x64', |
| Abi.windowsArm64 => 'win-arm64', |
| _ => throw UnimplementedError(), |
| }; |
| |
| void main(List<String> args) { |
| final checkUpToDate = args.contains('--check-up-to-date'); |
| |
| final dartApiHFile = File('$runtimeRoot/include/dart_api.h'); |
| final dartNativeApiHFile = File('$runtimeRoot/include/dart_native_api.h'); |
| final dartApiWinCFile = File('$runtimeRoot/bin/dart_api_win.c'); |
| final dartApiWinCTmpFile = File('$runtimeRoot/bin/dart_api_win_tmp.c'); |
| final dartApiHContents = dartApiHFile.readAsStringSync(); |
| final dartNativeApiHContents = dartNativeApiHFile.readAsStringSync(); |
| |
| final procedureRegexp = RegExp( |
| r'(DART_\w+\s+)+(?<returnType>[\w\s\*]+)\s+(?<name>\w+)\((?<arguments>[^)]*)\);', |
| multiLine: true, |
| ); |
| |
| final matches = [ |
| ...procedureRegexp.allMatches(dartApiHContents), |
| ...procedureRegexp.allMatches(dartNativeApiHContents), |
| ]; |
| |
| final procedures = <Procedure>[]; |
| |
| for (final match in matches) { |
| final returnType = match.namedGroup('returnType')!; |
| final name = match.namedGroup('name')!; |
| final argumentsString = match.namedGroup('arguments') ?? ''; |
| final argumentList = argumentsString |
| .split(',') |
| .where((arg) => arg != 'void') |
| .map((arg) { |
| final parts = arg.trim().split(' '); |
| return ( |
| type: parts.sublist(0, parts.length - 1).join(' '), |
| name: parts[parts.length - 1], |
| ); |
| }) |
| .toList(); |
| procedures.add(( |
| name: name, |
| returnType: returnType, |
| arguments: argumentList, |
| )); |
| } |
| |
| final buffer = StringBuffer(); |
| |
| buffer.writeln(''' |
| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // DO NOT EDIT. This file is generated by runtime/tools/generate_dart_api_win_c.dart. |
| |
| #include <windows.h> |
| |
| #include <include/dart_api.h> |
| #include <include/dart_native_api.h> |
| |
| '''); |
| |
| // Generate typedefs for all procedures. |
| for (final procedure in procedures) { |
| buffer.write('typedef '); |
| buffer.write(procedure.returnType); |
| buffer.write(' (*'); |
| buffer.write(procedure.typedefName); |
| buffer.write(')('); |
| for (final (i, argument) in procedure.arguments.indexed) { |
| buffer.write(argument.type); |
| if (i < procedure.arguments.length - 1) { |
| buffer.write(', '); |
| } |
| } |
| buffer.writeln(');'); |
| } |
| |
| buffer.writeln(); |
| |
| // Generate function pointers for all procedures. |
| for (final procedure in procedures) { |
| buffer.write('static '); |
| buffer.write(procedure.typedefName); |
| buffer.write(' '); |
| buffer.write(procedure.functionPointerName); |
| buffer.writeln(' = NULL;'); |
| } |
| |
| buffer.writeln(); |
| |
| // Generate the DllMain function that initializes all function pointers. |
| buffer.writeln(''' |
| BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { |
| if (fdwReason == DLL_PROCESS_ATTACH) { |
| HMODULE process = GetModuleHandle(NULL); |
| '''); |
| |
| for (final procedure in procedures) { |
| buffer.write(' '); |
| buffer.write(procedure.functionPointerName); |
| buffer.write(' = ('); |
| buffer.write(procedure.typedefName); |
| buffer.write(') GetProcAddress(process, "'); |
| buffer.write(procedure.name); |
| buffer.writeln('");'); |
| } |
| |
| buffer.writeln(''' |
| } |
| |
| return TRUE; |
| }'''); |
| |
| buffer.writeln(); |
| |
| // Generate redirecting implementations for all procedures. |
| for (final procedure in procedures) { |
| final (:name, :returnType, :arguments) = procedure; |
| buffer.write(returnType); |
| buffer.write(' '); |
| buffer.write(name); |
| buffer.write('('); |
| for (final (i, (:name, :type)) in arguments.indexed) { |
| buffer.write(type); |
| buffer.write(' '); |
| buffer.write(name); |
| if (i < arguments.length - 1) { |
| buffer.write(', '); |
| } |
| } |
| buffer.writeln(') {'); |
| buffer.write(' '); |
| if (procedure.returnType != 'void') { |
| buffer.write('return '); |
| } |
| buffer.write(procedure.functionPointerName); |
| buffer.write('('); |
| for (final (i, argument) in arguments.indexed) { |
| buffer.write(argument.name); |
| if (i < arguments.length - 1) { |
| buffer.write(', '); |
| } |
| } |
| buffer.writeln(');'); |
| buffer.writeln('}'); |
| buffer.writeln(); |
| } |
| |
| buffer.writeln(); |
| |
| dartApiWinCTmpFile.writeAsStringSync(buffer.toString()); |
| |
| try { |
| // Run clang-format on the generated file. |
| final clangFormatResult = Process.runSync( |
| clangFormatBin, |
| ['-i', dartApiWinCTmpFile.path], |
| // Allows us to specify the path to the clang-format binary without the |
| // .exe extension on Windows. |
| runInShell: Platform.isWindows, |
| ); |
| if (clangFormatResult.exitCode != 0) { |
| print(clangFormatResult.stdout); |
| print(clangFormatResult.stderr); |
| exitCode = 1; |
| } else { |
| final changed = |
| !dartApiWinCFile.existsSync() || |
| dartApiWinCTmpFile.readAsStringSync() != |
| dartApiWinCFile.readAsStringSync(); |
| if (changed) { |
| if (checkUpToDate) { |
| exitCode = 1; |
| } else { |
| dartApiWinCTmpFile.copySync(dartApiWinCFile.path); |
| } |
| } |
| } |
| } finally { |
| dartApiWinCTmpFile.deleteSync(); |
| } |
| } |
| |
| typedef Procedure = ({ |
| String name, |
| String returnType, |
| List<({String name, String type})> arguments, |
| }); |
| |
| extension on Procedure { |
| String get typedefName => '${name}Type'; |
| String get functionPointerName => '${name}Fn'; |
| } |