blob: 28ba0e9c3a124f414ab152edd88a3da4a45d16c9 [file]
// 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 '../../code_generator.dart';
import '../../config_provider/config.dart';
import '../../config_provider/config_types.dart';
import '../../context.dart';
import '../../strings.dart';
import '../clang_bindings/clang_bindings.dart' as clang_types;
import '../utils.dart';
import 'api_availability.dart';
/// Parses a function declaration.
List<Func> parseFunctionDeclaration(
Context context,
clang_types.CXCursor cursor,
) {
final config = context.config;
final logger = context.logger;
/// Multiple values are since there may be more than one instance of the
/// same base C function with different variadic arguments.
final funcs = <Func>[];
final funcUsr = cursor.usr();
final funcName = cursor.spelling();
final apiAvailability = ApiAvailability.fromCursor(cursor, context);
if (apiAvailability.availability == Availability.none) {
logger.info('Omitting deprecated function $funcName');
return funcs;
}
final decl = Declaration(usr: funcUsr, originalName: funcName);
final cachedFunc = context.bindingsIndex.getSeenFunc(funcUsr);
if (cachedFunc != null) {
funcs.add(cachedFunc);
} else {
logger.fine('++++ Adding Function: ${cursor.completeStringRepr()}');
final returnType = cursor.returnType().toCodeGenType(context);
final (:parameters, :hasIncompleteStruct, :hasUnimplementedType) =
parseParameters(context, cursor);
if (clang.clang_Cursor_isFunctionInlined(cursor) != 0 &&
clang.clang_Cursor_getStorageClass(cursor) !=
clang_types.CX_StorageClass.CX_SC_Extern) {
logger.fine(
'---- Removed Function, reason: inline function: '
'${cursor.completeStringRepr()}',
);
logger.warning(
"Skipped Function '$funcName', inline functions are not supported.",
);
// Returning empty so that [addToBindings] function excludes this.
return funcs;
}
if (returnType.isIncompleteCompound || hasIncompleteStruct) {
logger.fine(
'---- Removed Function, reason: Incomplete struct pass/return by '
'value: ${cursor.completeStringRepr()}',
);
logger.warning(
"Skipped Function '$funcName', Incomplete struct pass/return by "
'value not supported.',
);
// Returning null so that [addToBindings] function excludes this.
return funcs;
}
if (returnType.baseType is UnimplementedType || hasUnimplementedType) {
logger.fine(
'---- Removed Function, reason: unsupported return type or '
'parameter type: ${cursor.completeStringRepr()}',
);
logger.warning(
"Skipped Function '$funcName', function has unsupported return type "
'or parameter type.',
);
// Returning null so that [addToBindings] function excludes this.
return funcs;
}
// Look for any annotations on the function.
final objCReturnsRetained = cursor.hasChildWithKind(
clang_types.CXCursorKind.CXCursor_NSReturnsRetained,
);
// Initialized with a single value with no prefix and empty var args.
var varArgFunctions = <VarArgFunction?>[null];
if (config.functions.varArgs.containsKey(funcName)) {
if (clang.clang_isFunctionTypeVariadic(cursor.type()) == 1) {
varArgFunctions = config.functions.varArgs[funcName]!;
} else {
logger.warning(
'Skipping variadic-argument config for function '
"'$funcName' since its not variadic.",
);
}
}
for (final vaFunc in varArgFunctions) {
var usr = funcUsr;
if (vaFunc != null) usr += '$synthUsrChar vaFunc: ${vaFunc.postfix}';
funcs.add(
Func(
dartDoc: getCursorDocComment(
context,
cursor,
indent: nesting.length + commentPrefix.length,
availability: apiAvailability.dartDoc,
),
usr: usr,
name: funcName + (vaFunc?.postfix ?? ''),
originalName: funcName,
returnType: returnType,
parameters: parameters.map((p) => p.clone()).toList(),
varArgParameters: [
for (final ta in vaFunc?.types ?? const <Type>[])
Parameter(type: ta, name: 'va', objCConsumed: false),
],
exposeFunctionTypedefs: config.functions.includeTypedef(decl),
objCReturnsRetained: objCReturnsRetained,
loadFromNativeAsset: config.output.style is NativeExternalBindings,
apiAvailability: apiAvailability,
),
);
}
context.bindingsIndex.addFuncToSeen(funcUsr, funcs.last);
}
return funcs;
}
({
List<Parameter> parameters,
bool hasIncompleteStruct,
bool hasUnimplementedType,
})
parseParameters(
Context context,
clang_types.CXCursor cursor, {
String Function(String)? renameFn,
}) {
final parameters = <Parameter>[];
var incompleteStructParameter = false;
var unimplementedParameterType = false;
final totalArgs = clang.clang_Cursor_getNumArguments(cursor);
for (var i = 0; i < totalArgs; i++) {
final paramCursor = clang.clang_Cursor_getArgument(cursor, i);
context.logger.finer(
'===== parameter: ${paramCursor.completeStringRepr()}',
);
final paramType = paramCursor.toCodeGenType(context);
if (paramType.isIncompleteCompound) {
incompleteStructParameter = true;
} else if (paramType.baseType is UnimplementedType) {
context.logger.finer('Unimplemented type: ${paramType.baseType}');
unimplementedParameterType = true;
}
final spelling = paramCursor.spelling();
final name = spelling.isEmpty ? 'arg$i' : spelling;
final objCConsumed = paramCursor.hasChildWithKind(
clang_types.CXCursorKind.CXCursor_NSConsumed,
);
parameters.add(
Parameter(
originalName: spelling,
name: name,
type: paramType,
objCConsumed: objCConsumed,
),
);
}
return (
parameters: parameters,
hasIncompleteStruct: incompleteStructParameter,
hasUnimplementedType: unimplementedParameterType,
);
}