• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dart-lang / ffigen / 5305585135

18 Jun 2023 08:33PM CUT coverage: 85.041% (-6.8%) from 91.879%
5305585135

Pull #586

github

web-flow
Merge 27e2ea8df into 9030eb512
Pull Request #586: Add ffigen schema and refer them in example config yaml using modeline

603 of 603 new or added lines in 3 files covered. (100.0%)

3462 of 4071 relevant lines covered (85.04%)

25.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.57
/lib/src/config_provider/config_types.dart
1
// Copyright (c) 2020, 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
/// Contains all the neccesary classes required by config.
6
import 'dart:io';
7

8
import 'package:ffigen/src/code_generator.dart';
9
import 'package:quiver/pattern.dart' as quiver;
10

11
import 'path_finder.dart';
12

13
enum Language { c, objc }
14

15
class CommentType {
16
  CommentStyle style;
17
  CommentLength length;
18
  CommentType(this.style, this.length);
3✔
19

20
  /// Sets default style as [CommentStyle.doxygen], default length as
21
  /// [CommentLength.full].
22
  CommentType.def()
46✔
23
      : style = CommentStyle.doxygen,
24
        length = CommentLength.full;
25

26
  /// Disables any comments.
27
  CommentType.none()
1✔
28
      : style = CommentStyle.doxygen,
29
        length = CommentLength.none;
30
}
31

32
enum CommentStyle { doxygen, any }
33

34
enum CommentLength { none, brief, full }
35

36
enum CompoundDependencies { full, opaque }
37

38
/// Holds config for how Structs Packing will be overriden.
39
class StructPackingOverride {
40
  final Map<RegExp, int?> _matcherMap;
41

42
  StructPackingOverride({Map<RegExp, int?>? matcherMap})
50✔
43
      : _matcherMap = matcherMap ?? {};
49✔
44

45
  /// Returns true if the user has overriden the pack value.
46
  bool isOverriden(String name) {
25✔
47
    for (final key in _matcherMap.keys) {
51✔
48
      if (quiver.matchesFull(key, name)) {
1✔
49
        return true;
50
      }
51
    }
52
    return false;
53
  }
54

55
  /// Returns pack value for [name]. Ensure that value [isOverriden] before
56
  /// using the returned value.
57
  int? getOverridenPackValue(String name) {
1✔
58
    for (final opv in _matcherMap.entries) {
3✔
59
      if (quiver.matchesFull(opv.key, name)) {
2✔
60
        return opv.value;
1✔
61
      }
62
    }
63
    return null;
64
  }
65
}
66

67
/// Represents a single specification in configurations.
68
///
69
/// [E] is the return type of the extractedResult.
70
class Specification<E> {
71
  final bool Function(List<String> name, dynamic value) validator;
72
  final E Function(dynamic map) extractor;
73
  final E Function()? defaultValue;
74

75
  final Requirement requirement;
76
  final void Function(dynamic result) extractedResult;
77

78
  Specification({
×
79
    required this.extractedResult,
80
    required this.validator,
81
    required this.extractor,
82
    this.defaultValue,
83
    this.requirement = Requirement.no,
84
  });
85
}
86

87
enum Requirement { yes, prefer, no }
88

89
// Holds headers and filters for header.
90
class Headers {
91
  /// Path to headers.
92
  ///
93
  /// This contains all the headers, after extraction from Globs.
94
  final List<String> entryPoints;
95

96
  /// Include filter for headers.
97
  final HeaderIncludeFilter includeFilter;
98

99
  Headers({List<String>? entryPoints, HeaderIncludeFilter? includeFilter})
50✔
100
      : entryPoints = entryPoints ?? [],
×
101
        includeFilter = includeFilter ?? GlobHeaderFilter();
×
102
}
103

104
abstract class HeaderIncludeFilter {
105
  bool shouldInclude(String headerSourceFile);
106
}
107

108
class GlobHeaderFilter extends HeaderIncludeFilter {
109
  List<quiver.Glob>? includeGlobs = [];
110

111
  GlobHeaderFilter({
50✔
112
    this.includeGlobs,
113
  });
114

115
  @override
33✔
116
  bool shouldInclude(String headerSourceFile) {
117
    // Return true if header was included.
118
    for (final globPattern in includeGlobs!) {
76✔
119
      if (quiver.matchesFull(globPattern, headerSourceFile)) {
10✔
120
        return true;
121
      }
122
    }
123

124
    // If any includedInclusionHeaders is provided, return false.
125
    if (includeGlobs!.isNotEmpty) {
66✔
126
      return false;
127
    } else {
128
      return true;
129
    }
130
  }
131
}
132

133
/// A generic declaration config, used for Functions, Structs, Enums, Macros,
134
/// unnamed Enums and Globals.
135
class Declaration {
136
  final Includer _includer;
137
  final Renamer _renamer;
138
  final MemberRenamer _memberRenamer;
139
  final Includer _symbolAddressIncluder;
140

141
  Declaration({
50✔
142
    Includer? includer,
143
    Renamer? renamer,
144
    MemberRenamer? memberRenamer,
145
    Includer? symbolAddressIncluder,
146
  })  : _includer = includer ?? Includer(),
×
147
        _renamer = renamer ?? Renamer(),
×
148
        _memberRenamer = memberRenamer ?? MemberRenamer(),
×
149
        _symbolAddressIncluder =
150
            symbolAddressIncluder ?? Includer.excludeByDefault();
50✔
151

152
  /// Applies renaming and returns the result.
153
  String renameUsingConfig(String name) => _renamer.rename(name);
99✔
154

155
  /// Applies member renaming and returns the result.
156
  String renameMemberUsingConfig(String declaration, String member) =>
29✔
157
      _memberRenamer.rename(declaration, member);
58✔
158

159
  /// Checks if a name is allowed by a filter.
160
  bool shouldInclude(String name, bool excludeAllByDefault) =>
33✔
161
      _includer.shouldInclude(name, excludeAllByDefault);
66✔
162

163
  /// Checks if the symbol address should be included for this name.
164
  bool shouldIncludeSymbolAddress(String name) =>
24✔
165
      _symbolAddressIncluder.shouldInclude(name);
48✔
166
}
167

168
/// Matches `$<single_digit_int>`, value can be accessed in group 1 of match.
169
final replaceGroupRegexp = RegExp(r'\$([0-9])');
3✔
170

171
/// Match/rename using [regExp].
172
class RegExpRenamer {
173
  final RegExp regExp;
174
  final String replacementPattern;
175

176
  RegExpRenamer(this.regExp, this.replacementPattern);
2✔
177

178
  /// Returns true if [str] has a full match with [regExp].
179
  bool matches(String str) => quiver.matchesFull(regExp, str);
3✔
180

181
  /// Renames [str] according to [replacementPattern].
182
  ///
183
  /// Returns [str] if [regExp] doesn't have a full match.
184
  String rename(String str) {
1✔
185
    if (matches(str)) {
1✔
186
      // Get match.
187
      final regExpMatch = regExp.firstMatch(str)!;
2✔
188

189
      /// Get group values.
190
      /// E.g for `str`: `clang_dispose` and `regExp`: `clang_(.*)`
191
      /// groups will be `0`: `clang_disponse`, `1`: `dispose`.
192
      final groups = regExpMatch.groups(
1✔
193
          List.generate(regExpMatch.groupCount, (index) => index) +
4✔
194
              [regExpMatch.groupCount]);
2✔
195

196
      /// Replace all `$<int>` symbols with respective groups (if any).
197
      final result =
198
          replacementPattern.replaceAllMapped(replaceGroupRegexp, (match) {
4✔
199
        final groupInt = int.parse(match.group(1)!);
2✔
200
        return groups[groupInt]!;
1✔
201
      });
202
      return result;
203
    } else {
204
      return str;
205
    }
206
  }
207

208
  @override
×
209
  String toString() {
210
    return 'Regexp: $regExp, ReplacementPattern: $replacementPattern';
×
211
  }
212
}
213

214
/// Handles `include/exclude` logic for a declaration.
215
class Includer {
216
  final List<RegExp> _includeMatchers;
217
  final Set<String> _includeFull;
218
  final List<RegExp> _excludeMatchers;
219
  final Set<String> _excludeFull;
220

221
  Includer({
50✔
222
    List<RegExp>? includeMatchers,
223
    Set<String>? includeFull,
224
    List<RegExp>? excludeMatchers,
225
    Set<String>? excludeFull,
226
  })  : _includeMatchers = includeMatchers ?? [],
×
227
        _includeFull = includeFull ?? {},
228
        _excludeMatchers = excludeMatchers ?? [],
×
229
        _excludeFull = excludeFull ?? {};
230

231
  Includer.excludeByDefault()
50✔
232
      : _includeMatchers = [],
50✔
233
        _includeFull = {},
234
        _excludeMatchers = [RegExp('.*', dotAll: true)],
100✔
235
        _excludeFull = {};
236

237
  /// Returns true if [name] is allowed.
238
  ///
239
  /// Exclude overrides include.
240
  bool shouldInclude(String name, [bool excludeAllByDefault = false]) {
33✔
241
    if (_excludeFull.contains(name)) {
66✔
242
      return false;
243
    }
244

245
    for (final em in _excludeMatchers) {
57✔
246
      if (quiver.matchesFull(em, name)) {
24✔
247
        return false;
248
      }
249
    }
250

251
    if (_includeFull.contains(name)) {
66✔
252
      return true;
253
    }
254

255
    for (final im in _includeMatchers) {
34✔
256
      if (quiver.matchesFull(im, name)) {
1✔
257
        return true;
258
      }
259
    }
260

261
    // If user has provided 'include' field in the filter, then default
262
    // matching is false.
263
    if (_includeMatchers.isNotEmpty || _includeFull.isNotEmpty) {
132✔
264
      return false;
265
    } else {
266
      // Otherwise, fall back to the default behavior for empty filters.
267
      return !excludeAllByDefault;
268
    }
269
  }
270
}
271

272
/// Handles `full/regexp` renaming logic.
273
class Renamer {
274
  final Map<String, String> _renameFull;
275
  final List<RegExpRenamer> _renameMatchers;
276

277
  Renamer({
50✔
278
    List<RegExpRenamer>? renamePatterns,
279
    Map<String, String>? renameFull,
280
  })  : _renameMatchers = renamePatterns ?? [],
×
281
        _renameFull = renameFull ?? {};
×
282

283
  Renamer.noRename()
×
284
      : _renameMatchers = [],
×
285
        _renameFull = {};
×
286

287
  String rename(String name) {
33✔
288
    // Apply full rename (if any).
289
    if (_renameFull.containsKey(name)) {
66✔
290
      return _renameFull[name]!;
2✔
291
    }
292

293
    // Apply rename regexp (if matches).
294
    for (final renamer in _renameMatchers) {
34✔
295
      if (renamer.matches(name)) {
1✔
296
        return renamer.rename(name);
1✔
297
      }
298
    }
299

300
    // No renaming is provided for this declaration, return unchanged.
301
    return name;
302
  }
303
}
304

305
/// Match declaration name using [declarationRegExp].
306
class RegExpMemberRenamer {
307
  final RegExp declarationRegExp;
308
  final Renamer memberRenamer;
309

310
  RegExpMemberRenamer(this.declarationRegExp, this.memberRenamer);
1✔
311

312
  /// Returns true if [declaration] has a full match with [regExp].
313
  bool matchesDeclarationName(String declaration) =>
1✔
314
      quiver.matchesFull(declarationRegExp, declaration);
2✔
315

316
  @override
×
317
  String toString() {
318
    return 'DeclarationRegExp: $declarationRegExp, MemberRenamer: $memberRenamer';
×
319
  }
320
}
321

322
/// Handles `full/regexp` member renaming.
323
class MemberRenamer {
324
  final Map<String, Renamer> _memberRenameFull;
325
  final List<RegExpMemberRenamer> _memberRenameMatchers;
326

327
  final Map<String, Renamer> _cache = {};
328

329
  MemberRenamer({
50✔
330
    Map<String, Renamer>? memberRenameFull,
331
    List<RegExpMemberRenamer>? memberRenamePattern,
332
  })  : _memberRenameFull = memberRenameFull ?? {},
×
333
        _memberRenameMatchers = memberRenamePattern ?? [];
×
334

335
  String rename(String declaration, String member) {
29✔
336
    if (_cache.containsKey(declaration)) {
58✔
337
      return _cache[declaration]!.rename(member);
3✔
338
    }
339

340
    // Apply full rename (if any).
341
    if (_memberRenameFull.containsKey(declaration)) {
58✔
342
      // Add to cache.
343
      _cache[declaration] = _memberRenameFull[declaration]!;
4✔
344
      return _cache[declaration]!.rename(member);
3✔
345
    }
346

347
    // Apply rename regexp (if matches).
348
    for (final renamer in _memberRenameMatchers) {
30✔
349
      if (renamer.matchesDeclarationName(declaration)) {
1✔
350
        // Add to cache.
351
        _cache[declaration] = renamer.memberRenamer;
3✔
352
        return _cache[declaration]!.rename(member);
3✔
353
      }
354
    }
355

356
    // No renaming is provided for this declaration, return unchanged.
357
    return member;
358
  }
359
}
360

361
/// Handles config for automatically added compiler options.
362
class CompilerOptsAuto {
363
  final bool macIncludeStdLib;
364

365
  CompilerOptsAuto({bool? macIncludeStdLib})
50✔
366
      : macIncludeStdLib = macIncludeStdLib ?? true;
367

368
  /// Extracts compiler options based on OS and config.
369
  List<String> extractCompilerOpts() {
50✔
370
    if (Platform.isMacOS && macIncludeStdLib) {
100✔
371
      return getCStandardLibraryHeadersForMac();
49✔
372
    }
373

374
    return [];
1✔
375
  }
376
}
377

378
class _ObjCModulePrefixerEntry {
379
  final RegExp pattern;
380
  final String moduleName;
381

382
  _ObjCModulePrefixerEntry(this.pattern, this.moduleName);
2✔
383
}
384

385
/// Handles applying module prefixes to ObjC classes.
386
class ObjCModulePrefixer {
387
  final _prefixes = <_ObjCModulePrefixerEntry>[];
388

389
  ObjCModulePrefixer(Map<String, String> prefixes) {
50✔
390
    for (final entry in prefixes.entries) {
52✔
391
      _prefixes.add(_ObjCModulePrefixerEntry(RegExp(entry.key), entry.value));
12✔
392
    }
393
  }
394

395
  /// If any of the prefixing patterns match, applies that module prefix.
396
  /// Otherwise returns the class name unmodified.
397
  String applyPrefix(String className) {
2✔
398
    for (final entry in _prefixes) {
3✔
399
      if (quiver.matchesFull(entry.pattern, className)) {
2✔
400
        return '${entry.moduleName}.$className';
2✔
401
      }
402
    }
403
    return className;
404
  }
405
}
406

407
class FfiNativeConfig {
408
  final bool enabled;
409
  final String? asset;
410

411
  const FfiNativeConfig({required this.enabled, this.asset});
104✔
412
}
413

414
class SymbolFile {
415
  final String importPath;
416
  final String output;
417

418
  SymbolFile(this.importPath, this.output);
1✔
419
}
420

421
class OutputConfig {
422
  final String output;
423
  final SymbolFile? symbolFile;
424

425
  OutputConfig(this.output, this.symbolFile);
50✔
426
}
427

428
class RawVarArgFunction {
429
  String? postfix;
430
  final List<String> rawTypeStrings;
431

432
  RawVarArgFunction(this.postfix, this.rawTypeStrings);
1✔
433
}
434

435
class VarArgFunction {
436
  final String postfix;
437
  final List<Type> types;
438

439
  VarArgFunction(this.postfix, this.types);
23✔
440
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc