blob: 55a451ac1408319ef680716ac6f29a62553f9015 [file] [log] [blame]
// 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.
import 'package:ffigen/src/code_generator/utils.dart';
import 'package:meta/meta.dart';
import 'binding.dart';
import 'typedef.dart';
/// To store generated String bindings.
class Writer {
final String header;
/// Holds bindings, which lookup symbols.
final List<Binding> lookUpBindings;
/// Holds bindings which don't lookup symbols.
final List<Binding> noLookUpBindings;
String _className;
final String classDocComment;
String _ffiLibraryPrefix;
String get ffiLibraryPrefix => _ffiLibraryPrefix;
String _dylibIdentifier;
String get dylibIdentifier => _dylibIdentifier;
UniqueNamer _topLevelUniqueNamer, _wrapperLevelUniqueNamer;
UniqueNamer get topLevelUniqueNamer => _topLevelUniqueNamer;
UniqueNamer get wrapperLevelUniqueNamer => _wrapperLevelUniqueNamer;
String _arrayHelperClassPrefix;
/// Guaranteed to be a unique prefix.
String get arrayHelperClassPrefix => _arrayHelperClassPrefix;
/// [_usedUpNames] should contain names of all the declarations which are
/// already used. This is used to avoid name collisions.
Writer({
@required this.lookUpBindings,
@required this.noLookUpBindings,
@required String className,
this.classDocComment,
this.header,
}) : assert(className != null) {
final globalLevelNameSet = noLookUpBindings.map((e) => e.name).toSet();
final wrapperLevelNameSet = lookUpBindings.map((e) => e.name).toSet();
final allNameSet = <String>{}
..addAll(globalLevelNameSet)
..addAll(wrapperLevelNameSet);
_topLevelUniqueNamer = UniqueNamer(globalLevelNameSet);
_wrapperLevelUniqueNamer = UniqueNamer(wrapperLevelNameSet);
final allLevelsUniqueNamer = UniqueNamer(allNameSet);
/// Wrapper class name must be unique among all names.
_className = allLevelsUniqueNamer.makeUnique(className);
wrapperLevelUniqueNamer.markUsed(_className);
topLevelUniqueNamer.markUsed(_className);
/// [_ffiLibraryPrefix] should be unique in top level.
_ffiLibraryPrefix = topLevelUniqueNamer.makeUnique('ffi');
/// [_dylibIdentifier] should be unique in top level.
_dylibIdentifier = wrapperLevelUniqueNamer.makeUnique('_dylib');
/// Finding a unique prefix for Array Helper Classes and store into
/// [_arrayHelperClassPrefix].
final base = 'ArrayHelper';
_arrayHelperClassPrefix = base;
int suffixInt = 0;
for (int i = 0; i < allNameSet.length; i++) {
if (allNameSet.elementAt(i).startsWith(_arrayHelperClassPrefix)) {
// Not a unique prefix, start over with a new suffix.
i = -1;
suffixInt++;
_arrayHelperClassPrefix = '${base}${suffixInt}';
}
}
}
/// Writes all bindings to a String.
String generate() {
final s = StringBuffer();
// Write file header (if any).
if (header != null) {
s.write(header);
s.write('\n');
} else {
// Write default header, in case none was provided.
s.write(makeDartDoc(
'AUTO GENERATED FILE, DO NOT EDIT.\n\nGenerated by `package:ffigen`.'));
}
// Write neccesary imports.
s.write("import 'dart:ffi' as $ffiLibraryPrefix;\n");
s.write('\n');
final dependencies = <Typedef>[];
/// Get typedef dependencies, these will be written at the end.
for (final b in lookUpBindings) {
dependencies.addAll(b.getTypedefDependencies(this));
}
for (final b in noLookUpBindings) {
dependencies.addAll(b.getTypedefDependencies(this));
}
/// Write [lookUpBindings].
if (lookUpBindings.isNotEmpty) {
// Write doc comment for wrapper class.
if (classDocComment != null) {
s.write(makeDartDoc(classDocComment));
}
// Write wrapper classs.
s.write('class $_className{\n');
// Write dylib.
s.write('/// Holds the Dynamic library.\n');
s.write('final $ffiLibraryPrefix.DynamicLibrary ${dylibIdentifier};\n');
s.write('\n');
//Write doc comment for wrapper class constructor.
s.write(makeDartDoc('The symbols are looked up in [dynamicLibrary].'));
// Write wrapper class constructor.
s.write(
'${_className}($ffiLibraryPrefix.DynamicLibrary dynamicLibrary): $dylibIdentifier = dynamicLibrary;\n\n');
for (final b in lookUpBindings) {
s.write(b.toBindingString(this).string);
}
s.write('}\n\n');
}
/// Write [noLookUpBindings].
for (final b in noLookUpBindings) {
s.write(b.toBindingString(this).string);
}
// Write typedef dependencies.
for (final d in dependencies) {
s.write(d.toTypedefString(this));
}
return s.toString();
}
}