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

Commit

Permalink
Added support for Global variables (#139) (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
mannprerak2 authored Mar 1, 2021
1 parent f0d4d9a commit 5f12c73
Show file tree
Hide file tree
Showing 14 changed files with 374 additions and 13 deletions.
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 @@ -144,7 +144,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 @@ -170,6 +170,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;
75 changes: 75 additions & 0 deletions test/header_parser_tests/globals_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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
# Needed for stdbool.h in MacOS
${strings.compilerOpts}: '-I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Headers/ -Wno-nullability-completeness'
''') 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

0 comments on commit 5f12c73

Please sign in to comment.