|
| 1 | +// Copyright (c) 2019, 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 | +// This file generates the extension methods public API and the extension |
| 6 | +// methods patch file for all integers, double, and float. |
| 7 | +// The PointerPointer and PointerStruct extension are written by hand since |
| 8 | +// those are not repetitive. |
| 9 | + |
| 10 | +import 'dart:io'; |
| 11 | + |
| 12 | +// |
| 13 | +// Configuration. |
| 14 | +// |
| 15 | + |
| 16 | +const Map<String, String> nativeToDartType = { |
| 17 | + "Int8": "int", |
| 18 | + "Int16": "int", |
| 19 | + "Int32": "int", |
| 20 | + "Int64": "int", |
| 21 | + "Uint8": "int", |
| 22 | + "Uint16": "int", |
| 23 | + "Uint32": "int", |
| 24 | + "Uint64": "int", |
| 25 | + "IntPtr": "int", |
| 26 | + "Float": "double", |
| 27 | + "Double": "double", |
| 28 | +}; |
| 29 | + |
| 30 | +// |
| 31 | +// Generator. |
| 32 | +// |
| 33 | + |
| 34 | +main(List<String> arguments) { |
| 35 | + final parsedArgs = parseArguments(arguments); |
| 36 | + |
| 37 | + generate(parsedArgs.path, "ffi.g.dart", generatePublicExtension); |
| 38 | + generate(parsedArgs.path, "ffi_patch.g.dart", generatePatchExtension); |
| 39 | +} |
| 40 | + |
| 41 | +void generate(Uri path, String fileName, |
| 42 | + Function(StringBuffer, String, String) generator) { |
| 43 | + final buffer = StringBuffer(); |
| 44 | + generateHeader(buffer); |
| 45 | + nativeToDartType.forEach((String nativeType, String dartType) { |
| 46 | + generator(buffer, nativeType, dartType); |
| 47 | + }); |
| 48 | + generateFooter(buffer); |
| 49 | + |
| 50 | + final fullPath = path.resolve(fileName).path; |
| 51 | + new File(fullPath).writeAsStringSync(buffer.toString()); |
| 52 | + final fmtResult = Process.runSync(dartfmtPath().path, ["-w", fullPath]); |
| 53 | + if (fmtResult.exitCode != 0) { |
| 54 | + throw Exception( |
| 55 | + "Formatting failed:\n${fmtResult.stdout}\n${fmtResult.stderr}\n"); |
| 56 | + } |
| 57 | + print("Generated $fullPath."); |
| 58 | +} |
| 59 | + |
| 60 | +void generateHeader(StringBuffer buffer) { |
| 61 | + final header = """ |
| 62 | +// |
| 63 | +// The following code is generated, do not edit by hand. |
| 64 | +// |
| 65 | +// Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`. |
| 66 | +// |
| 67 | +
|
| 68 | +"""; |
| 69 | + |
| 70 | + buffer.write(header); |
| 71 | +} |
| 72 | + |
| 73 | +void generatePublicExtension( |
| 74 | + StringBuffer buffer, String nativeType, String dartType) { |
| 75 | + final storeTrunctateInt = """ |
| 76 | + /// Note that ints which do not fit in [$nativeType] are truncated. |
| 77 | +"""; |
| 78 | + |
| 79 | + final storeTrunctateDouble = """ |
| 80 | + /// Note that doubles stored into Pointer<[Float]> lose precision. |
| 81 | +"""; |
| 82 | + |
| 83 | + final storeTruncate = |
| 84 | + isInt(nativeType) ? storeTrunctateInt : storeTrunctateDouble; |
| 85 | + |
| 86 | + final loadSignExtendInt = """ |
| 87 | + /// Note that ints are signextended. |
| 88 | +"""; |
| 89 | + |
| 90 | + final loadSignExtend = isInt(nativeType) ? loadSignExtendInt : ""; |
| 91 | + |
| 92 | + buffer.write(""" |
| 93 | +/// Extension on [Pointer] specialized for the type argument [$nativeType]. |
| 94 | +extension ${nativeType}Pointer on Pointer<$nativeType> { |
| 95 | + /// Load a Dart value from this location. |
| 96 | + /// |
| 97 | + /// The value is automatically unmarshalled from its native representation. |
| 98 | +$loadSignExtend /// |
| 99 | + /// Note that [address] needs to be aligned to the size of [$nativeType]. |
| 100 | + external $dartType get value; |
| 101 | +
|
| 102 | + /// Store a Dart value into this location. |
| 103 | + /// |
| 104 | + /// The [value] is automatically marshalled into its native representation. |
| 105 | +$storeTruncate /// |
| 106 | + /// Note that [address] needs to be aligned to the size of [$nativeType]. |
| 107 | + external void set value($dartType value); |
| 108 | +
|
| 109 | + /// Load a Dart value from this location offset by [index]. |
| 110 | + /// |
| 111 | + /// The value is automatically unmarshalled from its native representation. |
| 112 | +$loadSignExtend /// |
| 113 | + /// Note that [address] needs to be aligned to the size of [$nativeType]. |
| 114 | + external $dartType operator [](int index); |
| 115 | +
|
| 116 | + /// Store a Dart value into this location offset by [index]. |
| 117 | + /// |
| 118 | + /// The [value] is automatically marshalled into its native representation. |
| 119 | +$storeTruncate /// |
| 120 | + /// Note that [address] needs to be aligned to the size of [$nativeType]. |
| 121 | + external void operator []=(int index, $dartType value); |
| 122 | +} |
| 123 | +
|
| 124 | +"""); |
| 125 | +} |
| 126 | + |
| 127 | +void generatePatchExtension( |
| 128 | + StringBuffer buffer, String nativeType, String dartType) { |
| 129 | + buffer.write(""" |
| 130 | +extension ${nativeType}Pointer on Pointer<$nativeType> { |
| 131 | + @patch |
| 132 | + $dartType get value => _load$nativeType(this); |
| 133 | +
|
| 134 | + @patch |
| 135 | + void set value($dartType value) => _store$nativeType(this, value); |
| 136 | +
|
| 137 | + @patch |
| 138 | + $dartType operator [](int index) => this.elementAt(index).value; |
| 139 | +
|
| 140 | + @patch |
| 141 | + void operator []=(int index, $dartType value) => this.elementAt(index).value = value; |
| 142 | +} |
| 143 | +
|
| 144 | +"""); |
| 145 | +} |
| 146 | + |
| 147 | +void generateFooter(StringBuffer buffer) { |
| 148 | + final footer = """ |
| 149 | +// |
| 150 | +// End of generated code. |
| 151 | +// |
| 152 | +"""; |
| 153 | + |
| 154 | + buffer.write(footer); |
| 155 | +} |
| 156 | + |
| 157 | +// |
| 158 | +// Helper functions. |
| 159 | +// |
| 160 | + |
| 161 | +bool isInt(String type) => type.startsWith("Int") || type.startsWith("Uint"); |
| 162 | + |
| 163 | +class Arguments { |
| 164 | + final Uri path; |
| 165 | + Arguments(this.path); |
| 166 | +} |
| 167 | + |
| 168 | +Arguments parseArguments(List<String> arguments) { |
| 169 | + final parsedArgs = Map<String, dynamic>(); |
| 170 | + String flag = null; |
| 171 | + for (final String arg in arguments) { |
| 172 | + if (flag == "path") { |
| 173 | + parsedArgs[flag] = Uri.parse(arg); |
| 174 | + flag = null; |
| 175 | + } else if (arg == "-p" || arg == "--path") { |
| 176 | + flag = "path"; |
| 177 | + } else { |
| 178 | + throw Exception("Unknown argument: $arg"); |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + Uri path = parsedArgs["path"]; |
| 183 | + if (path == null) { |
| 184 | + path = Platform.script; |
| 185 | + print("No path provided, generating files next to generator."); |
| 186 | + } |
| 187 | + |
| 188 | + return Arguments(path); |
| 189 | +} |
| 190 | + |
| 191 | +Uri dartfmtPath() { |
| 192 | + // TODO(dacoharkes): Use "../../../tools/sdks/dart-sdk/bin/dartfmt" when the |
| 193 | + // pinned fully supports extension methods. |
| 194 | + return Uri.parse("dartfmt"); |
| 195 | +} |
0 commit comments