blob: 657920d445458344d32072e39275a7fbe8cee306 [file] [log] [blame]
// 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 configuration = [
Config("Int8", "int", "Int8List", 1),
Config("Int16", "int", "Int16List", 2),
Config("Int32", "int", "Int32List", 4),
Config("Int64", "int", "Int64List", 8),
Config("Uint8", "int", "Uint8List", 1),
Config("Uint16", "int", "Uint16List", 2),
Config("Uint32", "int", "Uint32List", 4),
Config("Uint64", "int", "Uint64List", 8),
Config("IntPtr", "int", kDoNotEmit, kIntPtrElementSize),
Config("Float", "double", "Float32List", 4),
Config("Double", "double", "Float64List", 8),
];
//
// 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, Config) generator) {
final buffer = StringBuffer();
generateHeader(buffer);
configuration.forEach((Config c) => generator(buffer, c));
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, Config config) {
final nativeType = config.nativeType;
final dartType = config.dartType;
final typedListType = config.typedListType;
final elementSize = config.elementSize;
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 : "";
final asTypedList = typedListType == kDoNotEmit
? ""
: """
/// Creates a typed list view backed by memory in the address space.
///
/// The returned view will allow access to the memory range from `address`
/// to `address + ${elementSize > 1 ? '$elementSize * ' : ''}length`.
///
/// The user has to ensure the memory range is accessible while using the
/// returned list.
external $typedListType asTypedList(int length);
""";
// 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);
$asTypedList
}
""");
}
void generatePatchExtension(StringBuffer buffer, Config config) {
final nativeType = config.nativeType;
final dartType = config.dartType;
final typedListType = config.typedListType;
final asTypedList = typedListType == kDoNotEmit
? ""
: """
@patch
$typedListType asTypedList(int elements) => _asExternalTypedData(this, elements);
""";
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);
$asTypedList
}
""");
}
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");
}
class Config {
final String nativeType;
final String dartType;
final String typedListType;
final int elementSize;
const Config(
this.nativeType, this.dartType, this.typedListType, this.elementSize);
}
const String kDoNotEmit = "donotemit";
const int kIntPtrElementSize = -1;