| // Copyright (c) 2020, 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. |
| |
| /// Contains all the neccesary classes required by config. |
| import 'dart:io'; |
| |
| import 'package:quiver/pattern.dart' as quiver; |
| |
| import 'path_finder.dart'; |
| |
| enum Language { c, objc } |
| |
| class CommentType { |
| CommentStyle style; |
| CommentLength length; |
| CommentType(this.style, this.length); |
| |
| /// Sets default style as [CommentStyle.doxygen], default length as |
| /// [CommentLength.full]. |
| CommentType.def() |
| : style = CommentStyle.doxygen, |
| length = CommentLength.full; |
| |
| /// Disables any comments. |
| CommentType.none() |
| : style = CommentStyle.doxygen, |
| length = CommentLength.none; |
| } |
| |
| enum CommentStyle { doxygen, any } |
| |
| enum CommentLength { none, brief, full } |
| |
| enum CompoundDependencies { full, opaque } |
| |
| /// Holds config for how Structs Packing will be overriden. |
| class StructPackingOverride { |
| final Map<RegExp, int?> _matcherMap; |
| |
| StructPackingOverride({Map<RegExp, int?>? matcherMap}) |
| : _matcherMap = matcherMap ?? {}; |
| |
| /// Returns true if the user has overriden the pack value. |
| bool isOverriden(String name) { |
| for (final key in _matcherMap.keys) { |
| if (quiver.matchesFull(key, name)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Returns pack value for [name]. Ensure that value [isOverriden] before |
| /// using the returned value. |
| int? getOverridenPackValue(String name) { |
| for (final opv in _matcherMap.entries) { |
| if (quiver.matchesFull(opv.key, name)) { |
| return opv.value; |
| } |
| } |
| return null; |
| } |
| } |
| |
| /// Represents a single specification in configurations. |
| /// |
| /// [E] is the return type of the extractedResult. |
| class Specification<E> { |
| final bool Function(List<String> name, dynamic value) validator; |
| final E Function(dynamic map) extractor; |
| final E Function()? defaultValue; |
| |
| final Requirement requirement; |
| final void Function(dynamic result) extractedResult; |
| |
| Specification({ |
| required this.extractedResult, |
| required this.validator, |
| required this.extractor, |
| this.defaultValue, |
| this.requirement = Requirement.no, |
| }); |
| } |
| |
| enum Requirement { yes, prefer, no } |
| |
| // Holds headers and filters for header. |
| class Headers { |
| /// Path to headers. |
| /// |
| /// This contains all the headers, after extraction from Globs. |
| final List<String> entryPoints; |
| |
| /// Include filter for headers. |
| final HeaderIncludeFilter includeFilter; |
| |
| Headers({List<String>? entryPoints, HeaderIncludeFilter? includeFilter}) |
| : entryPoints = entryPoints ?? [], |
| includeFilter = includeFilter ?? GlobHeaderFilter(); |
| } |
| |
| abstract class HeaderIncludeFilter { |
| bool shouldInclude(String headerSourceFile); |
| } |
| |
| class GlobHeaderFilter extends HeaderIncludeFilter { |
| List<quiver.Glob>? includeGlobs = []; |
| |
| GlobHeaderFilter({ |
| this.includeGlobs, |
| }); |
| |
| @override |
| bool shouldInclude(String headerSourceFile) { |
| // Return true if header was included. |
| for (final globPattern in includeGlobs!) { |
| if (quiver.matchesFull(globPattern, headerSourceFile)) { |
| return true; |
| } |
| } |
| |
| // If any includedInclusionHeaders is provided, return false. |
| if (includeGlobs!.isNotEmpty) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| } |
| |
| /// A generic declaration config, used for Functions, Structs, Enums, Macros, |
| /// unnamed Enums and Globals. |
| class Declaration { |
| final Includer _includer; |
| final Renamer _renamer; |
| final MemberRenamer _memberRenamer; |
| final Includer _symbolAddressIncluder; |
| |
| Declaration({ |
| Includer? includer, |
| Renamer? renamer, |
| MemberRenamer? memberRenamer, |
| Includer? symbolAddressIncluder, |
| }) : _includer = includer ?? Includer(), |
| _renamer = renamer ?? Renamer(), |
| _memberRenamer = memberRenamer ?? MemberRenamer(), |
| _symbolAddressIncluder = |
| symbolAddressIncluder ?? Includer.excludeByDefault(); |
| |
| /// Applies renaming and returns the result. |
| String renameUsingConfig(String name) => _renamer.rename(name); |
| |
| /// Applies member renaming and returns the result. |
| String renameMemberUsingConfig(String declaration, String member) => |
| _memberRenamer.rename(declaration, member); |
| |
| /// Checks if a name is allowed by a filter. |
| bool shouldInclude(String name, bool excludeAllByDefault) => |
| _includer.shouldInclude(name, excludeAllByDefault); |
| |
| /// Checks if the symbol address should be included for this name. |
| bool shouldIncludeSymbolAddress(String name) => |
| _symbolAddressIncluder.shouldInclude(name); |
| } |
| |
| /// Matches `$<single_digit_int>`, value can be accessed in group 1 of match. |
| final replaceGroupRegexp = RegExp(r'\$([0-9])'); |
| |
| /// Match/rename using [regExp]. |
| class RegExpRenamer { |
| final RegExp regExp; |
| final String replacementPattern; |
| |
| RegExpRenamer(this.regExp, this.replacementPattern); |
| |
| /// Returns true if [str] has a full match with [regExp]. |
| bool matches(String str) => quiver.matchesFull(regExp, str); |
| |
| /// Renames [str] according to [replacementPattern]. |
| /// |
| /// Returns [str] if [regExp] doesn't have a full match. |
| String rename(String str) { |
| if (matches(str)) { |
| // Get match. |
| final regExpMatch = regExp.firstMatch(str)!; |
| |
| /// Get group values. |
| /// E.g for `str`: `clang_dispose` and `regExp`: `clang_(.*)` |
| /// groups will be `0`: `clang_disponse`, `1`: `dispose`. |
| final groups = regExpMatch.groups( |
| List.generate(regExpMatch.groupCount, (index) => index) + |
| [regExpMatch.groupCount]); |
| |
| /// Replace all `$<int>` symbols with respective groups (if any). |
| final result = |
| replacementPattern.replaceAllMapped(replaceGroupRegexp, (match) { |
| final groupInt = int.parse(match.group(1)!); |
| return groups[groupInt]!; |
| }); |
| return result; |
| } else { |
| return str; |
| } |
| } |
| |
| @override |
| String toString() { |
| return 'Regexp: $regExp, ReplacementPattern: $replacementPattern'; |
| } |
| } |
| |
| /// Handles `include/exclude` logic for a declaration. |
| class Includer { |
| final List<RegExp> _includeMatchers; |
| final Set<String> _includeFull; |
| final List<RegExp> _excludeMatchers; |
| final Set<String> _excludeFull; |
| |
| Includer({ |
| List<RegExp>? includeMatchers, |
| Set<String>? includeFull, |
| List<RegExp>? excludeMatchers, |
| Set<String>? excludeFull, |
| }) : _includeMatchers = includeMatchers ?? [], |
| _includeFull = includeFull ?? {}, |
| _excludeMatchers = excludeMatchers ?? [], |
| _excludeFull = excludeFull ?? {}; |
| |
| Includer.excludeByDefault() |
| : _includeMatchers = [], |
| _includeFull = {}, |
| _excludeMatchers = [RegExp('.*', dotAll: true)], |
| _excludeFull = {}; |
| |
| /// Returns true if [name] is allowed. |
| /// |
| /// Exclude overrides include. |
| bool shouldInclude(String name, [bool excludeAllByDefault = false]) { |
| if (_excludeFull.contains(name)) { |
| return false; |
| } |
| |
| for (final em in _excludeMatchers) { |
| if (quiver.matchesFull(em, name)) { |
| return false; |
| } |
| } |
| |
| if (_includeFull.contains(name)) { |
| return true; |
| } |
| |
| for (final im in _includeMatchers) { |
| if (quiver.matchesFull(im, name)) { |
| return true; |
| } |
| } |
| |
| // If user has provided 'include' field in the filter, then default |
| // matching is false. |
| if (_includeMatchers.isNotEmpty || _includeFull.isNotEmpty) { |
| return false; |
| } else { |
| // Otherwise, fall back to the default behavior for empty filters. |
| return !excludeAllByDefault; |
| } |
| } |
| } |
| |
| /// Handles `full/regexp` renaming logic. |
| class Renamer { |
| final Map<String, String> _renameFull; |
| final List<RegExpRenamer> _renameMatchers; |
| |
| Renamer({ |
| List<RegExpRenamer>? renamePatterns, |
| Map<String, String>? renameFull, |
| }) : _renameMatchers = renamePatterns ?? [], |
| _renameFull = renameFull ?? {}; |
| |
| Renamer.noRename() |
| : _renameMatchers = [], |
| _renameFull = {}; |
| |
| String rename(String name) { |
| // Apply full rename (if any). |
| if (_renameFull.containsKey(name)) { |
| return _renameFull[name]!; |
| } |
| |
| // Apply rename regexp (if matches). |
| for (final renamer in _renameMatchers) { |
| if (renamer.matches(name)) { |
| return renamer.rename(name); |
| } |
| } |
| |
| // No renaming is provided for this declaration, return unchanged. |
| return name; |
| } |
| } |
| |
| /// Match declaration name using [declarationRegExp]. |
| class RegExpMemberRenamer { |
| final RegExp declarationRegExp; |
| final Renamer memberRenamer; |
| |
| RegExpMemberRenamer(this.declarationRegExp, this.memberRenamer); |
| |
| /// Returns true if [declaration] has a full match with [regExp]. |
| bool matchesDeclarationName(String declaration) => |
| quiver.matchesFull(declarationRegExp, declaration); |
| |
| @override |
| String toString() { |
| return 'DeclarationRegExp: $declarationRegExp, MemberRenamer: $memberRenamer'; |
| } |
| } |
| |
| /// Handles `full/regexp` member renaming. |
| class MemberRenamer { |
| final Map<String, Renamer> _memberRenameFull; |
| final List<RegExpMemberRenamer> _memberRenameMatchers; |
| |
| final Map<String, Renamer> _cache = {}; |
| |
| MemberRenamer({ |
| Map<String, Renamer>? memberRenameFull, |
| List<RegExpMemberRenamer>? memberRenamePattern, |
| }) : _memberRenameFull = memberRenameFull ?? {}, |
| _memberRenameMatchers = memberRenamePattern ?? []; |
| |
| String rename(String declaration, String member) { |
| if (_cache.containsKey(declaration)) { |
| return _cache[declaration]!.rename(member); |
| } |
| |
| // Apply full rename (if any). |
| if (_memberRenameFull.containsKey(declaration)) { |
| // Add to cache. |
| _cache[declaration] = _memberRenameFull[declaration]!; |
| return _cache[declaration]!.rename(member); |
| } |
| |
| // Apply rename regexp (if matches). |
| for (final renamer in _memberRenameMatchers) { |
| if (renamer.matchesDeclarationName(declaration)) { |
| // Add to cache. |
| _cache[declaration] = renamer.memberRenamer; |
| return _cache[declaration]!.rename(member); |
| } |
| } |
| |
| // No renaming is provided for this declaration, return unchanged. |
| return member; |
| } |
| } |
| |
| /// Handles config for automatically added compiler options. |
| class CompilerOptsAuto { |
| final bool macIncludeStdLib; |
| |
| CompilerOptsAuto({bool? macIncludeStdLib}) |
| : macIncludeStdLib = macIncludeStdLib ?? true; |
| |
| /// Extracts compiler options based on OS and config. |
| List<String> extractCompilerOpts() { |
| if (Platform.isMacOS && macIncludeStdLib) { |
| return getCStandardLibraryHeadersForMac(); |
| } |
| |
| return []; |
| } |
| } |
| |
| class _ObjCModulePrefixerEntry { |
| final RegExp pattern; |
| final String moduleName; |
| |
| _ObjCModulePrefixerEntry(this.pattern, this.moduleName); |
| } |
| |
| /// Handles applying module prefixes to ObjC classes. |
| class ObjCModulePrefixer { |
| final _prefixes = <_ObjCModulePrefixerEntry>[]; |
| |
| ObjCModulePrefixer(Map<String, String> prefixes) { |
| for (final entry in prefixes.entries) { |
| _prefixes.add(_ObjCModulePrefixerEntry(RegExp(entry.key), entry.value)); |
| } |
| } |
| |
| /// If any of the prefixing patterns match, applies that module prefix. |
| /// Otherwise returns the class name unmodified. |
| String applyPrefix(String className) { |
| for (final entry in _prefixes) { |
| if (quiver.matchesFull(entry.pattern, className)) { |
| return '${entry.moduleName}.$className'; |
| } |
| } |
| return className; |
| } |
| } |
| |
| class FfiNativeConfig { |
| final bool enabled; |
| final String? asset; |
| |
| const FfiNativeConfig({required this.enabled, this.asset}); |
| } |
| |
| class SymbolFile { |
| final String importPath; |
| final String output; |
| |
| SymbolFile(this.importPath, this.output); |
| } |
| |
| class OutputConfig { |
| final String output; |
| final SymbolFile? symbolFile; |
| |
| OutputConfig(this.output, this.symbolFile); |
| } |