| // Copyright (c) 2019, 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. |
| // |
| // This file generates the extension methods public API and the extension |
| // methods patch file for all integers, double, and float. |
| // The PointerPointer and PointerStruct extension are written by hand since |
| // those are not repetitive. |
| |
| import 'dart:io'; |
| |
| import 'package:args/args.dart'; |
| |
| // |
| // Configuration. |
| // |
| |
| const Map<String, String> nativeToDartType = { |
| "Int8": "int", |
| "Int16": "int", |
| "Int32": "int", |
| "Int64": "int", |
| "Uint8": "int", |
| "Uint16": "int", |
| "Uint32": "int", |
| "Uint64": "int", |
| "IntPtr": "int", |
| "Float": "double", |
| "Double": "double", |
| }; |
| |
| // |
| // Generator. |
| // |
| |
| main(List<String> arguments) { |
| final args = argParser().parse(arguments); |
| Uri path = Uri.parse(args['path']); |
| |
| generate(path, "ffi.g.dart", generatePublicExtension); |
| generate(path, "ffi_patch.g.dart", generatePatchExtension); |
| } |
| |
| void generate(Uri path, String fileName, |
| Function(StringBuffer, String, String) generator) { |
| final buffer = StringBuffer(); |
| generateHeader(buffer); |
| nativeToDartType.forEach((String nativeType, String dartType) { |
| generator(buffer, nativeType, dartType); |
| }); |
| generateFooter(buffer); |
| |
| final fullPath = path.resolve(fileName).path; |
| File(fullPath).writeAsStringSync(buffer.toString()); |
| final fmtResult = Process.runSync(dartfmtPath().path, ["-w", fullPath]); |
| if (fmtResult.exitCode != 0) { |
| throw Exception( |
| "Formatting failed:\n${fmtResult.stdout}\n${fmtResult.stderr}\n"); |
| } |
| print("Generated $fullPath."); |
| } |
| |
| void generateHeader(StringBuffer buffer) { |
| final header = """ |
| // |
| // The following code is generated, do not edit by hand. |
| // |
| // Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`. |
| // |
| |
| """; |
| |
| buffer.write(header); |
| } |
| |
| void generatePublicExtension( |
| StringBuffer buffer, String nativeType, String dartType) { |
| final storeTrunctateInt = """ |
| /// Note that ints which do not fit in `$nativeType` are truncated. |
| """; |
| |
| final storeTrunctateDouble = """ |
| /// Note that doubles stored into Pointer<`Float`> lose precision. |
| """; |
| |
| final storeTruncate = |
| _isInt(nativeType) ? storeTrunctateInt : storeTrunctateDouble; |
| |
| final loadSignExtendInt = """ |
| /// Note that ints are signextended. |
| """; |
| |
| final loadSignExtend = _isInt(nativeType) ? loadSignExtendInt : ""; |
| |
| // TODO(dartdoc-bug): Use [] instead of ``, once issue |
| // https://github.com/dart-lang/dartdoc/issues/2039 is fixed. |
| buffer.write(""" |
| /// Extension on [Pointer] specialized for the type argument [$nativeType]. |
| extension ${nativeType}Pointer on Pointer<$nativeType> { |
| /// Load a Dart value from this location. |
| /// |
| /// The value is automatically unmarshalled from its native representation. |
| $loadSignExtend /// |
| /// Note that `address` needs to be aligned to the size of `$nativeType`. |
| external $dartType get value; |
| |
| /// Store a Dart value into this location. |
| /// |
| /// The `value` is automatically marshalled into its native representation. |
| $storeTruncate /// |
| /// Note that `address` needs to be aligned to the size of `$nativeType`. |
| external void set value($dartType value); |
| |
| /// Load a Dart value from this location offset by `index`. |
| /// |
| /// The value is automatically unmarshalled from its native representation. |
| $loadSignExtend /// |
| /// Note that `address` needs to be aligned to the size of `$nativeType`. |
| external $dartType operator [](int index); |
| |
| /// Store a Dart value into this location offset by `index`. |
| /// |
| /// The `value` is automatically marshalled into its native representation. |
| $storeTruncate /// |
| /// Note that `address` needs to be aligned to the size of `$nativeType`. |
| external void operator []=(int index, $dartType value); |
| } |
| |
| """); |
| } |
| |
| void generatePatchExtension( |
| StringBuffer buffer, String nativeType, String dartType) { |
| buffer.write(""" |
| extension ${nativeType}Pointer on Pointer<$nativeType> { |
| @patch |
| $dartType get value => _load$nativeType(this, 0); |
| |
| @patch |
| set value($dartType value) => _store$nativeType(this, 0, value); |
| |
| @patch |
| $dartType operator [](int index) => _load$nativeType(this, index); |
| |
| @patch |
| operator []=(int index, $dartType value) => _store$nativeType(this, index, value); |
| } |
| |
| """); |
| } |
| |
| void generateFooter(StringBuffer buffer) { |
| final footer = """ |
| // |
| // End of generated code. |
| // |
| """; |
| |
| buffer.write(footer); |
| } |
| |
| // |
| // Helper functions. |
| // |
| |
| bool _isInt(String type) => type.startsWith("Int") || type.startsWith("Uint"); |
| |
| final Uri _containingFolder = File.fromUri(Platform.script).parent.uri; |
| |
| ArgParser argParser() { |
| final parser = ArgParser(allowTrailingOptions: false); |
| parser.addOption('path', |
| abbr: 'p', |
| help: 'Path to generate the files at.', |
| defaultsTo: _containingFolder.toString()); |
| parser.addFlag('help', abbr: 'h', help: 'Display usage information.', |
| callback: (help) { |
| if (help) print(parser.usage); |
| }); |
| return parser; |
| } |
| |
| Uri dartfmtPath() { |
| // TODO(dacoharkes): Use "../../../tools/sdks/dart-sdk/bin/dartfmt" when the |
| // pinned fully supports extension methods. |
| return Uri.parse("dartfmt"); |
| } |