Skip to content
This repository has been archived by the owner on Jan 28, 2024. It is now read-only.

Globals #139

Merged
merged 10 commits into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 2.0.0-dev.4
- Add support for parsing and generating globals.

# 2.0.0-dev.3
- Removed the usage of `--no-sound-null-safety` flag.

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ compiler-opts: '-I/usr/lib/llvm-9/include/'
</td>
</tr>
<tr>
<td>functions<br>structs<br>enums<br>unnamed-enums<br>macros</td>
<td>functions<br>structs<br>enums<br>unnamed-enums<br>macros<br>globals</td>
<td>Filters for declarations.<br><b>Default: all are included</b></td>
<td>

Expand All @@ -171,6 +171,12 @@ enums:
'CXTypeKind': # Full names have higher priority.
# $1 keeps only the 1st group i.e '(.*)'.
'CXType(.*)': '$1'
globals:
exclude:
- aGlobal
rename:
# Removes '_' from beginning of a name.
- '_(.*)': '$1'
```
</td>
</tr>
Expand Down
32 changes: 27 additions & 5 deletions lib/src/code_generator/global.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// 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 'package:ffigen/src/code_generator/typedef.dart';

import 'binding.dart';
import 'binding_string.dart';
import 'type.dart';
Expand Down Expand Up @@ -34,20 +36,40 @@ class Global extends LookUpBinding {
dartDoc: dartDoc,
);

List<Typedef>? _typedefDependencies;
@override
List<Typedef> getTypedefDependencies(Writer w) {
if (_typedefDependencies == null) {
_typedefDependencies = <Typedef>[];

// Add typedef's required by the variable's type.
final valueType = type.getBaseType();
if (valueType.broadType == BroadType.NativeFunction) {
_typedefDependencies!.addAll(valueType.nativeFunc!.getDependencies());
}
}
return _typedefDependencies!;
}

@override
BindingString toBindingString(Writer w) {
final s = StringBuffer();
final globalVarName = name;
if (dartDoc != null) {
s.write(makeDartDoc(dartDoc!));
}
final pointerName = w.wrapperLevelUniqueNamer.makeUnique('_$globalVarName');
final dartType = type.getDartType(w);
final cType = type.getCType(w);
final refOrValue = type.broadType == BroadType.Struct ? 'ref' : 'value';

final holderVarName =
w.wrapperLevelUniqueNamer.makeUnique('_$globalVarName');
s.write(
'${w.ffiLibraryPrefix}.Pointer<${type.getCType(w)}> $holderVarName;\n');
s.write(
"${type.getDartType(w)} get $globalVarName => ($holderVarName ??= ${w.dylibIdentifier}.lookup<${type.getCType(w)}>('$originalName')).value;\n\n");
"late final ffi.Pointer<$dartType> $pointerName = ${w.dylibIdentifier}.lookup<$cType>('$originalName');\n\n");
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved
s.write('$dartType get $globalVarName => $pointerName.$refOrValue;\n\n');
if (type.broadType != BroadType.Struct) {
s.write(
'set $globalVarName($dartType value) => $pointerName.value = value;\n\n');
}

return BindingString(type: BindingStringType.global, string: s.toString());
}
Expand Down
13 changes: 13 additions & 0 deletions lib/src/config_provider/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class Config {
Declaration get unnamedEnumConstants => _unnamedEnumConstants;
late Declaration _unnamedEnumConstants;

/// Declaration config for Globals.
Declaration get globals => _globals;
late Declaration _globals;

/// Declaration config for Macro constants.
Declaration get macroDecl => _macroDecl;
late Declaration _macroDecl;
Expand Down Expand Up @@ -222,6 +226,15 @@ class Config {
extractedResult: (dynamic result) =>
_unnamedEnumConstants = result as Declaration,
),
strings.globals: Specification<Declaration>(
requirement: Requirement.no,
validator: declarationConfigValidator,
extractor: declarationConfigExtractor,
defaultValue: () => Declaration(),
extractedResult: (dynamic result) {
_globals = result as Declaration;
},
),
strings.macros: Specification<Declaration>(
requirement: Requirement.no,
validator: declarationConfigValidator,
Expand Down
10 changes: 10 additions & 0 deletions lib/src/header_parser/includer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ bool shouldIncludeUnnamedEnumConstant(String usr, String name) {
}
}

bool shouldIncludeGlobalVar(String usr, String name) {
if (bindingsIndex.isSeenGlobalVar(usr) || name == '') {
return false;
} else if (config.globals.shouldInclude(name)) {
return true;
} else {
return false;
}
}

bool shouldIncludeMacro(String usr, String name) {
if (bindingsIndex.isSeenMacro(usr) || name == '') {
return false;
Expand Down
39 changes: 39 additions & 0 deletions lib/src/header_parser/sub_parsers/var_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved
// 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 'package:ffigen/src/code_generator.dart';
import 'package:ffigen/src/header_parser/data.dart';
import 'package:ffigen/src/header_parser/includer.dart';
import 'package:logging/logging.dart';

import '../clang_bindings/clang_bindings.dart' as clang_types;
import '../data.dart';
import '../utils.dart';

final _logger = Logger('ffigen.header_parser.var_parser');

/// Parses a global variable
Global? parseVarDeclaration(clang_types.CXCursor cursor) {
final type = cursor.type().toCodeGenType();
if (type.broadType == BroadType.Unimplemented ||
type.getBaseType().isIncompleteStruct ||
type.getBaseType().unimplementedReason != null) {
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved
_logger.fine(
'---- Removed Global, reason: unsupported type: ${cursor.completeStringRepr()}');
_logger.warning(
"Skipped global variable '${cursor.spelling()}', type not supported.");
return null;
}
if (!shouldIncludeGlobalVar(cursor.usr(), cursor.spelling())) {
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved
return null;
}
final g = Global(
originalName: cursor.spelling(),
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved
name: config.globals.renameUsingConfig(cursor.spelling()),
usr: cursor.usr(),
type: cursor.type().toCodeGenType(),
dartDoc: getCursorDocComment(cursor),
);
return g;
}
4 changes: 4 additions & 0 deletions lib/src/header_parser/translation_unit_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:ffi';

import 'package:ffigen/src/code_generator.dart';
import 'package:ffigen/src/header_parser/sub_parsers/macro_parser.dart';
import 'package:ffigen/src/header_parser/sub_parsers/var_parser.dart';
import 'package:logging/logging.dart';

import 'clang_bindings/clang_bindings.dart' as clang_types;
Expand Down Expand Up @@ -58,6 +59,9 @@ int _rootCursorVisitor(clang_types.CXCursor cursor, clang_types.CXCursor parent,
case clang_types.CXCursorKind.CXCursor_MacroDefinition:
saveMacroDefinition(cursor);
break;
case clang_types.CXCursorKind.CXCursor_VarDecl:
addToBindings(parseVarDeclaration(cursor));
break;
default:
_logger.finer('rootCursorVisitor: CursorKind not implemented');
}
Expand Down
5 changes: 5 additions & 0 deletions lib/src/header_parser/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ class BindingsIndex {
final Map<String, EnumClass> _enumClass = {};
final Map<String, Constant> _unnamedEnumConstants = {};
final Map<String, String> _macros = {};
final Map<String, Global> _globals = {};
// Stores only named typedefC used in NativeFunc.
final Map<String, Typedef> _functionTypedefs = {};

Expand Down Expand Up @@ -359,6 +360,10 @@ class BindingsIndex {
return _unnamedEnumConstants.containsKey(usr);
}

bool isSeenGlobalVar(String usr) {
return _globals.containsKey(usr);
}

void addUnnamedEnumConstantToSeen(String usr, Constant enumConstant) {
_unnamedEnumConstants[usr] = enumConstant;
}
Expand Down
1 change: 1 addition & 0 deletions lib/src/strings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const functions = 'functions';
const structs = 'structs';
const enums = 'enums';
const unnamedEnums = 'unnamed-enums';
const globals = 'globals';
const macros = 'macros';

// Sub-fields of Declarations.
Expand Down
14 changes: 7 additions & 7 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
# BSD-style license that can be found in the LICENSE file.

name: ffigen
version: 2.0.0-dev.3
version: 2.0.0-dev.4
homepage: https://github.com/dart-lang/ffigen
description: Experimental generator for FFI bindings, using LibClang to parse C header files.

environment:
sdk: '>=2.12.0-198.0.dev <3.0.0'
sdk: ">=2.12.0-198.0.dev <3.0.0"
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved

dependencies:
ffi: ^0.2.0-nullsafety.1
yaml: ^3.0.0-nullsafety.0
path: ^1.8.0-nullsafety.3
quiver: ^3.0.0-nullsafety.2
args: ^1.6.0 # Not available
logging: ^0.11.4 # test->coverage->logging
cli_util: ^0.2.0 # test->analyzer->cli_util
glob: ^1.0.3 # test->analyzer->glob
pub_semver: ^1.4.4 # test->analyzer->pub_semver
args: ^1.6.0 # Not available
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved
logging: ^0.11.4 # test->coverage->logging
cli_util: ^0.2.0 # test->analyzer->cli_util
glob: ^1.0.3 # test->analyzer->glob
pub_semver: ^1.4.4 # test->analyzer->pub_semver

dev_dependencies:
pedantic: ^1.10.0-nullsafety.3
Expand Down
21 changes: 15 additions & 6 deletions test/code_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -436,14 +436,23 @@ final ffi.DynamicLibrary _dylib;
/// The symbols are looked up in [dynamicLibrary].
Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;

ffi.Pointer<ffi.Int32> _test1;
int get test1 => (_test1 ??= _dylib.lookup<ffi.Int32>('test1')).value;
late final ffi.Pointer<int> _test1 = _dylib.lookup<ffi.Int32>('test1');

ffi.Pointer<ffi.Pointer<ffi.Float>> _test2;
ffi.Pointer<ffi.Float> get test2 => (_test2 ??= _dylib.lookup<ffi.Pointer<ffi.Float>>('test2')).value;
int get test1 => _test1.value;

ffi.Pointer<ffi.Pointer<Some>> _test5;
ffi.Pointer<Some> get test5 => (_test5 ??= _dylib.lookup<ffi.Pointer<Some>>('test5')).value;
set test1(int value) => _test1.value = value;

late final ffi.Pointer<ffi.Pointer<ffi.Float>> _test2 = _dylib.lookup<ffi.Pointer<ffi.Float>>('test2');

ffi.Pointer<ffi.Float> get test2 => _test2.value;

set test2(ffi.Pointer<ffi.Float> value) => _test2.value = value;

late final ffi.Pointer<ffi.Pointer<Some>> _test5 = _dylib.lookup<ffi.Pointer<Some>>('test5');

ffi.Pointer<Some> get test5 => _test5.value;

set test5(ffi.Pointer<Some> value) => _test5.value = value;

}

Expand Down
14 changes: 14 additions & 0 deletions test/header_parser_tests/globals.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved
// 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.

#include <stdint.h>

bool coolGlobal;
int32_t myInt;
int32_t *aGlobalPointer;
long double longDouble;
long double *pointerToLongDouble;

// This should be ignored
int GlobalIgnore;
74 changes: 74 additions & 0 deletions test/header_parser_tests/globals_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
TimWhiting marked this conversation as resolved.
Show resolved Hide resolved
// 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 'package:ffigen/src/code_generator.dart';
import 'package:ffigen/src/header_parser.dart' as parser;
import 'package:ffigen/src/config_provider.dart';
import 'package:test/test.dart';
import 'package:yaml/yaml.dart' as yaml;
import 'package:ffigen/src/strings.dart' as strings;

import '../test_utils.dart';

late Library actual, expected;

void main() {
group('globals_test', () {
setUpAll(() {
logWarnings();
expected = expectedLibrary();
actual = parser.parse(
Config.fromYaml(yaml.loadYaml('''
${strings.name}: 'NativeLibrary'
${strings.description}: 'Globals Test'
${strings.output}: 'unused'
${strings.headers}:
${strings.entryPoints}:
- 'test/header_parser_tests/globals.h'
${strings.includeDirectives}:
- '**globals.h'
${strings.globals}:
${strings.exclude}:
- GlobalIgnore
''') as yaml.YamlMap),
);
});

test('Total bindings count', () {
expect(actual.bindings.length, expected.bindings.length);
});

test('Parse global Values', () {
expect(actual.getBindingAsString('coolGlobal'),
expected.getBindingAsString('coolGlobal'));
expect(actual.getBindingAsString('myInt'),
expected.getBindingAsString('myInt'));
expect(actual.getBindingAsString('aGlobalPointer'),
expected.getBindingAsString('aGlobalPointer'));
});

test('Ignore global values', () {
expect(() => actual.getBindingAsString('GlobalIgnore'),
throwsA(TypeMatcher<NotFoundException>()));
expect(() => actual.getBindingAsString('longDouble'),
throwsA(TypeMatcher<NotFoundException>()));
expect(() => actual.getBindingAsString('pointerToLongDouble'),
throwsA(TypeMatcher<NotFoundException>()));
});
});
}

Library expectedLibrary() {
return Library(
name: 'Bindings',
bindings: [
Global(
type: Type.nativeType(SupportedNativeType.Int32), name: 'coolGlobal'),
Global(type: Type.nativeType(SupportedNativeType.Int32), name: 'myInt'),
Global(
type: Type.pointer(Type.nativeType(SupportedNativeType.Int32)),
name: 'aGlobalPointer'),
],
);
}
Loading