blob: d52db6d3ba2674712cf8aff639bebd8b24f26816 [file] [log] [blame]
// Copyright (c) 2023, 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 'c_types.dart';
import 'utils.dart';
void main() async {
await Future.wait([
writeC(),
for (bool isNative in [true, false]) writeDart(isNative: isNative),
]);
}
final elementTypes = [
int8,
int16,
int32,
int64,
uint8,
uint16,
uint32,
uint64,
float,
double_,
];
const generatorPath =
'tests/ffi/generator/unwrap_typeddata_test_generator.dart';
Future<void> writeDart({
required bool isNative,
}) async {
final StringBuffer buffer = StringBuffer();
buffer.write(headerDart(
copyrightYear: 2023,
));
final forceDlOpen = !isNative
? ''
: '''
// Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
dlopenGlobalPlatformSpecific('ffi_test_functions');
''';
buffer.write("""
void main() {$forceDlOpen
for (int i = 0; i < 100; ++i) {
""");
for (final elementType in elementTypes) {
final pointerType = PointerType(elementType);
buffer.write("""
testUnwrap${pointerType.dartTypedData}();
testUnwrap${pointerType.dartTypedData}View();
testUnwrap${pointerType.dartTypedData}Many();
""");
}
buffer.write("""
}
}
""");
for (final elementType in elementTypes) {
final pointerType = PointerType(elementType);
String value = elementType.isSigned ? 'i % 2 == 0 ? i : -i' : 'i';
if (elementType.isFloatingPoint) {
value = '($value).toDouble()';
}
final equals = elementType.isFloatingPoint ? 'approxEquals' : 'equals';
final manyDartCTypes = Iterable.generate(
many,
(i) => '${pointerType.dartCType}',
).join(',');
final manyArgs = Iterable.generate(
many,
(i) => '${pointerType.dartTypedData} typedData$i',
).join(',');
final manyBodies = Iterable.generate(
many,
(i) =>
'${pointerType.dartTypedData}.view(source.buffer, elementSize * $i, 1)',
).join(',');
if (isNative) {
buffer.write("""
@Native<${elementType.dartCType} Function(${pointerType.dartCType}, Size)>(symbol: 'Unwrap${pointerType.dartTypedData}', isLeaf: true)
external ${elementType.dartType} unwrap${pointerType.dartTypedData}(${pointerType.dartTypedData} typedData, int length);
@Native<${elementType.dartCType} Function($manyDartCTypes,)>(symbol: 'Unwrap${pointerType.dartTypedData}Many', isLeaf: true)
external ${elementType.dartType} unwrap${pointerType.dartTypedData}Many($manyArgs,);
""");
} else {
buffer.write("""
final unwrap${pointerType.dartTypedData} = ffiTestFunctions.lookupFunction<
${elementType.dartCType} Function(${pointerType.dartCType}, Size),
${elementType.dartType} Function(${pointerType.dartTypedData}, int)>('Unwrap${pointerType.dartTypedData}', isLeaf: true);
final unwrap${pointerType.dartTypedData}Many = ffiTestFunctions.lookupFunction<
${elementType.dartCType} Function($manyDartCTypes,),
${elementType.dartType} Function($manyArgs,)>('Unwrap${pointerType.dartTypedData}Many', isLeaf: true);
""");
}
buffer.write("""
void testUnwrap${pointerType.dartTypedData}() {
const length = 10;
final typedData = ${pointerType.dartTypedData}(length);
${elementType.dartType} expectedResult = 0;
for (int i = 0; i < length; i++) {
final value = $value;
typedData[i] = value;
expectedResult += value;
}
final result = unwrap${pointerType.dartTypedData}(typedData, typedData.length);
Expect.$equals(expectedResult, result);
}
void testUnwrap${pointerType.dartTypedData}View() {
const sourceLength = 30;
const elementSize = ${elementType.size};
const viewStart = 10;
const viewOffsetInBytes = viewStart * elementSize;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = ${pointerType.dartTypedData}(sourceLength);
final view = ${pointerType.dartTypedData}.view(source.buffer, viewOffsetInBytes, viewLength);
${elementType.dartType} expectedResult = 0;
for (int i = 0; i < sourceLength; i++) {
final value = $value;
source[i] = value;
if (viewStart <= i && i < viewEnd) {
expectedResult += value;
}
}
final result = unwrap${pointerType.dartTypedData}(view, view.length);
Expect.$equals(expectedResult, result);
}
void testUnwrap${pointerType.dartTypedData}Many() {
const length = 20;
const elementSize = ${elementType.size};
final source = ${pointerType.dartTypedData}(length);
${elementType.dartType} expectedResult = 0;
for (int i = 0; i < length; i++) {
final value = $value;
source[i] = value;
expectedResult += value;
}
final result = unwrap${pointerType.dartTypedData}Many(
$manyBodies,
);
Expect.$equals(expectedResult, result);
}
""");
}
final path = testPath(isNative: isNative);
await File(path).writeAsString(buffer.toString());
await runProcess(Platform.resolvedExecutable, ["format", path]);
}
String testPath({
required bool isNative,
}) {
final suffix = '${isNative ? '_native' : ''}';
return Platform.script
.resolve("../../ffi/unwrap_typeddata_generated${suffix}_test.dart")
.toFilePath();
}
String headerDart({
required int copyrightYear,
}) {
return """
${headerCommon(copyrightYear: copyrightYear, generatorPath: generatorPath)}
//
// SharedObjects=ffi_test_functions
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
import 'dylib_utils.dart';
final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
""";
}
const many = 20;
Future<void> writeC() async {
final StringBuffer buffer = StringBuffer();
buffer.write(headerC(copyrightYear: 2023, generatorPath: generatorPath));
for (final elementType in elementTypes) {
final pointerType = PointerType(elementType);
String coutCast(String input) {
if (elementType == uint8 || elementType == int8) {
return "static_cast<int>($input)";
}
return input;
}
final manyArgs = Iterable.generate(
many,
(i) => '${pointerType.cType} data$i',
).join(',');
final manyBodies = Iterable.generate(
many,
(i) => '''
std::cout << "data$i[0] = " << ${coutCast('data$i[0]')} << "\\n";
result += data$i[0];''',
).join('\n');
buffer.write('''
DART_EXPORT ${elementType.cType} Unwrap${pointerType.dartTypedData}(${pointerType.cType} data, size_t length) {
${elementType.cType} result = 0;
for (size_t i = 0; i < length; i++) {
std::cout << "data[" << i << "] = " << ${coutCast('data[i]')} << "\\n";
result += data[i];
}
return result;
}
DART_EXPORT ${elementType.cType} Unwrap${pointerType.dartTypedData}Many($manyArgs) {
${elementType.cType} result = 0;
$manyBodies
return result;
}
''');
}
buffer.write(footerC);
await File(ccPath).writeAsString(buffer.toString());
await runProcess("clang-format", ["-i", ccPath]);
}
final ccPath = Platform.script
.resolve("../../../runtime/bin/ffi_test/ffi_test_functions_generated_2.cc")
.toFilePath();