Skip to content
This repository was 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 all 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 ${w.ffiLibraryPrefix}.Pointer<$dartType> $pointerName = ${w.dylibIdentifier}.lookup<$cType>('$originalName');\n\n");
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
47 changes: 47 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,47 @@
// Copyright (c) 2021, 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 '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 name = cursor.spelling();
final usr = cursor.usr();
if (bindingsIndex.isSeenGlobalVar(usr)) {
return bindingsIndex.getSeenGlobalVar(usr);
}
if (!shouldIncludeGlobalVar(usr, name)) {
return null;
}

_logger.fine('++++ Adding Global: ${cursor.completeStringRepr()}');

final type = cursor.type().toCodeGenType();
if (type.getBaseType().broadType == BroadType.Unimplemented ||
type.getBaseType().isIncompleteStruct) {
_logger.fine(
'---- Removed Global, reason: unsupported type: ${cursor.completeStringRepr()}');
_logger.warning("Skipped global variable '$name', type not supported.");
return null;
}

final global = Global(
originalName: name,
name: config.globals.renameUsingConfig(name),
usr: usr,
type: type,
dartDoc: getCursorDocComment(cursor),
);
bindingsIndex.addGlobalVarToSeen(usr, global);
return global;
}
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
13 changes: 13 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 @@ -367,6 +368,18 @@ class BindingsIndex {
return _unnamedEnumConstants[usr];
}

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

void addGlobalVarToSeen(String usr, Global global) {
_globals[usr] = global;
}

Global? getSeenGlobalVar(String usr) {
return _globals[usr];
}

bool isSeenMacro(String usr) {
return _macros.containsKey(usr);
}
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
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# 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.

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
15 changes: 15 additions & 0 deletions test/header_parser_tests/globals.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2021, 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.

#include <stdint.h>
#include <stdbool.h>

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

// This should be ignored
int GlobalIgnore;
73 changes: 73 additions & 0 deletions test/header_parser_tests/globals_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) 2021, 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 '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.boolean(), name: 'coolGlobal'),
Global(type: Type.nativeType(SupportedNativeType.Int32), name: 'myInt'),
Global(
type: Type.pointer(Type.nativeType(SupportedNativeType.Int32)),
name: 'aGlobalPointer'),
],
);
}
Loading