4cdb3f2c3a
- When return type is not directly supported and just a Dart_Handle, automatically create persistent handles - Change `Dart_LoadLibrary` to `Dart_LookupLibrary` - switch from iostream to cstdio, I have no idea why, but it doesn't work for me in emulators and it's not a big difference. TEST=ci,manual Change-Id: I9b6539c7f16e81af9e8a9ab3995d6040bac64a1d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/429962 Reviewed-by: Tess Strickland <sstrickl@google.com> Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com>
685 lines
19 KiB
Dart
685 lines
19 KiB
Dart
// Copyright (c) 2025, 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:kernel/ast.dart';
|
|
import 'package:kernel/core_types.dart' show CoreTypes;
|
|
|
|
void _convertDartTypeToC(
|
|
StringBuffer buffer,
|
|
CoreTypes? coreTypes,
|
|
DartType type,
|
|
) {
|
|
if (coreTypes == null) {
|
|
buffer.write('Dart_Handle');
|
|
} else if (type is VoidType) {
|
|
buffer.write('void');
|
|
} else if (type == coreTypes.intNonNullableRawType) {
|
|
buffer.write('int64_t');
|
|
} else if (type == coreTypes.doubleNonNullableRawType) {
|
|
buffer.write('double');
|
|
} else {
|
|
buffer.write('Dart_Handle');
|
|
}
|
|
}
|
|
|
|
String _getCReturnType(CoreTypes? coreTypes, DartType type) {
|
|
if (coreTypes == null) {
|
|
return 'Dart_Handle';
|
|
} else if (type is VoidType) {
|
|
return 'void';
|
|
} else if (type == coreTypes.intNonNullableRawType) {
|
|
return 'int64_t';
|
|
} else if (type == coreTypes.doubleNonNullableRawType) {
|
|
return 'double';
|
|
} else {
|
|
return 'Dart_Handle';
|
|
}
|
|
}
|
|
|
|
void _convertCValueToDart(
|
|
StringBuffer buffer,
|
|
CoreTypes coreTypes,
|
|
DartType type,
|
|
void Function(StringBuffer) bodyWriter,
|
|
) {
|
|
assert(type is! VoidType);
|
|
if (type == coreTypes.intNonNullableRawType) {
|
|
buffer.write('Dart_NewInteger(');
|
|
bodyWriter(buffer);
|
|
buffer.write(')');
|
|
} else if (type == coreTypes.doubleNonNullableRawType) {
|
|
buffer.write('Dart_NewDouble(');
|
|
bodyWriter(buffer);
|
|
buffer.write(')');
|
|
} else {
|
|
bodyWriter(buffer);
|
|
}
|
|
}
|
|
|
|
void _convertDartValueToC(
|
|
StringBuffer buffer,
|
|
CoreTypes coreTypes,
|
|
DartType type,
|
|
void Function(StringBuffer) bodyWriter,
|
|
) {
|
|
if (type is VoidType) {
|
|
buffer.write('CheckError(');
|
|
bodyWriter(buffer);
|
|
buffer.write(')');
|
|
} else if (type == coreTypes.intNonNullableRawType) {
|
|
buffer.write('IntFromHandle(');
|
|
bodyWriter(buffer);
|
|
buffer.write(')');
|
|
} else if (type == coreTypes.doubleNonNullableRawType) {
|
|
buffer.write('DoubleFromHandle(');
|
|
bodyWriter(buffer);
|
|
buffer.write(')');
|
|
} else {
|
|
bodyWriter(buffer);
|
|
}
|
|
}
|
|
|
|
class EntryPointShimParameter {
|
|
final String name;
|
|
final DartType type;
|
|
|
|
EntryPointShimParameter._(this.name, {DartType? type})
|
|
: type = type ?? DynamicType();
|
|
|
|
void writeAsParameter(StringBuffer buffer, [CoreTypes? coreTypes]) {
|
|
assert(type is! VoidType);
|
|
_convertDartTypeToC(buffer, coreTypes, type);
|
|
buffer.write(' $name');
|
|
}
|
|
|
|
void writeAsArgument(StringBuffer buffer, [CoreTypes? coreTypes]) {
|
|
if (coreTypes != null) {
|
|
_convertCValueToDart(buffer, coreTypes, type, (b) => b.write(name));
|
|
} else {
|
|
buffer.write(name);
|
|
}
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final b = StringBuffer();
|
|
writeAsArgument(b);
|
|
return b.toString();
|
|
}
|
|
}
|
|
|
|
class EntryPointShimTypeParameter extends EntryPointShimParameter {
|
|
static int counter = 0;
|
|
static String get gensym => 'tp${counter++}';
|
|
|
|
EntryPointShimTypeParameter._(String name) : super._('t_' + name);
|
|
|
|
factory EntryPointShimTypeParameter.fromTypeParameter(TypeParameter p) {
|
|
return EntryPointShimTypeParameter._(p.name ?? gensym);
|
|
}
|
|
}
|
|
|
|
class EntryPointShimValueParameter extends EntryPointShimParameter {
|
|
static int counter = 0;
|
|
static String get gensym => '${counter++}';
|
|
|
|
EntryPointShimValueParameter._(String name, {super.type})
|
|
: super._('v_' + name);
|
|
|
|
factory EntryPointShimValueParameter.fromDeclaration(VariableDeclaration v) {
|
|
return EntryPointShimValueParameter._(v.name ?? gensym, type: v.type);
|
|
}
|
|
}
|
|
|
|
abstract class EntryPointFunctionShim {
|
|
final NamedNode node;
|
|
final CoreTypes coreTypes;
|
|
final List<EntryPointShimParameter> parameters;
|
|
// The target may be the instance for instance members or a type if the
|
|
// enclosing class is generic and thus needs instantiation.
|
|
final EntryPointShimParameter? target;
|
|
|
|
static EntryPointShimParameter? targetOf(
|
|
NamedNode node,
|
|
CoreTypes coreTypes,
|
|
) {
|
|
if (node is! Member) return null;
|
|
|
|
if (node.isInstanceMember) {
|
|
// For instance methods, the instance contains the instantiation of the
|
|
// class type parameters.
|
|
return EntryPointShimValueParameter._(
|
|
"_inst",
|
|
type: node.enclosingClass!.getThisType(
|
|
coreTypes,
|
|
Nullability.nonNullable,
|
|
),
|
|
);
|
|
}
|
|
final cls = node.enclosingClass;
|
|
if (cls != null && cls.typeParameters.isNotEmpty) {
|
|
// Need to handle type parameters on the enclosing class. Defer this
|
|
// by requiring the user provide the appropriate instantiated
|
|
// non-nullable type.
|
|
return EntryPointShimTypeParameter._("_T");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static List<EntryPointShimParameter> parametersOf(NamedNode node) {
|
|
if (node is Procedure || node is Constructor) {
|
|
final function = (node as dynamic).function as FunctionNode;
|
|
assert(function.namedParameters.isEmpty);
|
|
assert(
|
|
function.requiredParameterCount == function.positionalParameters.length,
|
|
);
|
|
return function.positionalParameters
|
|
.map(EntryPointShimValueParameter.fromDeclaration)
|
|
.toList();
|
|
}
|
|
if (node is Class) {
|
|
return node.typeParameters
|
|
.map(EntryPointShimTypeParameter.fromTypeParameter)
|
|
.toList();
|
|
}
|
|
return const [];
|
|
}
|
|
|
|
EntryPointFunctionShim._({
|
|
required this.node,
|
|
required this.coreTypes,
|
|
List<EntryPointShimParameter>? parameters,
|
|
EntryPointShimParameter? target,
|
|
}) : parameters = parameters ?? parametersOf(node),
|
|
target = target ?? targetOf(node, coreTypes);
|
|
|
|
static final _cachedBaseNames = <Reference, String>{};
|
|
static final _cachedPrefixedNames = <Reference, String>{};
|
|
|
|
static String _baseName(NamedNode node) {
|
|
if (!_cachedBaseNames.containsKey(node.reference)) {
|
|
var name = (node as dynamic).name;
|
|
if (name is Name) {
|
|
assert(name.isPrivate == name.text.startsWith('_'));
|
|
name = name.text;
|
|
}
|
|
_cachedBaseNames[node.reference] = name as String;
|
|
}
|
|
return _cachedBaseNames[node.reference]!;
|
|
}
|
|
|
|
static String _prefixedName(NamedNode node) {
|
|
if (!_cachedPrefixedNames.containsKey(node.reference)) {
|
|
String prefixedName = _baseName(node);
|
|
if (node is Member) {
|
|
final cls = node.enclosingClass;
|
|
if (cls != null) {
|
|
final prefix = _prefixedName(cls);
|
|
if (prefixedName.isEmpty || prefixedName == '_') {
|
|
prefixedName = '$prefix$prefixedName';
|
|
} else {
|
|
prefixedName = '${prefix}_$prefixedName';
|
|
}
|
|
}
|
|
}
|
|
_cachedPrefixedNames[node.reference] = prefixedName;
|
|
}
|
|
return _cachedPrefixedNames[node.reference]!;
|
|
}
|
|
|
|
String get baseName => _baseName(node);
|
|
String get prefixedName => _prefixedName(node);
|
|
|
|
String get functionName;
|
|
DartType get returnType;
|
|
|
|
void _writeTarget(StringBuffer buffer) {
|
|
if (target != null) {
|
|
target!.writeAsArgument(buffer, coreTypes);
|
|
return;
|
|
}
|
|
final n = node;
|
|
Class? cls;
|
|
if (n is Member) {
|
|
cls = n.enclosingClass;
|
|
if (cls != null && cls.typeParameters.isNotEmpty) {
|
|
// The shim should have had a target parameter created.
|
|
throw "Unexpected attempt to write target";
|
|
}
|
|
} else if (n is Class) {
|
|
cls = n;
|
|
}
|
|
if (cls == null) {
|
|
buffer.write('PackageState::instance()->PackageLibrary()');
|
|
} else if (cls.typeParameters.isEmpty) {
|
|
buffer.write(
|
|
'PackageState::instance()->TypeWithDefaults("${_baseName(cls)}")',
|
|
);
|
|
} else {
|
|
assert(node is Class);
|
|
assert(parameters.isNotEmpty);
|
|
buffer.write(
|
|
'Dart_GetNonNullableType(PackageState::instance()->PackageLibrary(), Dart_NewStringFromCString("$baseName"), ',
|
|
);
|
|
_writeArgumentsListCountAndPointer(buffer);
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
|
|
void _writeArguments(StringBuffer buffer) {
|
|
bool first = true;
|
|
for (final p in parameters) {
|
|
if (!first) buffer.write(', ');
|
|
p.writeAsArgument(buffer, coreTypes);
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
void _writeArgumentsListCountAndPointer(StringBuffer buffer) {
|
|
buffer.write('${parameters.length}, ');
|
|
if (parameters.isEmpty) {
|
|
// No arguments list created.
|
|
buffer.write('nullptr');
|
|
} else {
|
|
buffer.write('parameter_list.data()');
|
|
}
|
|
}
|
|
|
|
void _writeReturnType(StringBuffer buffer) {
|
|
_convertDartTypeToC(buffer, coreTypes, returnType);
|
|
}
|
|
|
|
String get isolateName => 'dart_isolate';
|
|
|
|
void _writeFunctionHeader(StringBuffer buffer) {
|
|
buffer.write('PACKAGE_EXPORT ');
|
|
_writeReturnType(buffer);
|
|
buffer.write(' $functionName(Dart_Isolate $isolateName');
|
|
if (target != null) {
|
|
buffer.write(', ');
|
|
target!.writeAsParameter(buffer, coreTypes);
|
|
}
|
|
for (final p in parameters) {
|
|
buffer.write(', ');
|
|
p.writeAsParameter(buffer, coreTypes);
|
|
}
|
|
buffer.write(')');
|
|
}
|
|
|
|
void writeDeclaration(StringBuffer buffer) {
|
|
_writeFunctionHeader(buffer);
|
|
buffer.writeln(';');
|
|
}
|
|
|
|
void _writeReturnBody(StringBuffer buffer);
|
|
|
|
void writeDefinition(StringBuffer buffer) {
|
|
_writeFunctionHeader(buffer);
|
|
buffer.writeln(''' {
|
|
IsolateScope isolate_scope($isolateName);
|
|
DartScope scope;''');
|
|
if (parameters.isNotEmpty) {
|
|
buffer.write('''
|
|
std::vector parameter_list{''');
|
|
_writeArguments(buffer);
|
|
buffer.writeln('};');
|
|
}
|
|
buffer.write(' ');
|
|
if (returnType is! VoidType) {
|
|
buffer.write('return ');
|
|
}
|
|
_convertDartValueToC(buffer, coreTypes, returnType, _writeReturnBody);
|
|
buffer
|
|
..writeln(';')
|
|
..writeln('}')
|
|
..writeln('');
|
|
}
|
|
|
|
void write(StringBuffer declaration, StringBuffer definition) {
|
|
writeDeclaration(declaration);
|
|
writeDefinition(definition);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final b = StringBuffer();
|
|
writeDefinition(b);
|
|
return b.toString();
|
|
}
|
|
}
|
|
|
|
class EntryPointGetterShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Member node;
|
|
|
|
EntryPointGetterShim._({required this.node, required super.coreTypes})
|
|
: super._(node: node, parameters: const []);
|
|
|
|
factory EntryPointGetterShim.fromMember(Member node, CoreTypes coreTypes) {
|
|
assert(node is Field || (node is Procedure && node.isGetter));
|
|
return EntryPointGetterShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
@override
|
|
String get functionName => 'Get_$prefixedName';
|
|
|
|
@override
|
|
DartType get returnType => node.getterType;
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) {
|
|
buffer.write('Dart_GetField(');
|
|
_writeTarget(buffer);
|
|
buffer.write(', Dart_NewStringFromCString("$baseName"))');
|
|
}
|
|
}
|
|
|
|
class EntryPointClosureShim extends EntryPointGetterShim {
|
|
@override
|
|
final Procedure node;
|
|
|
|
EntryPointClosureShim._({required this.node, required super.coreTypes})
|
|
: super._(node: node);
|
|
|
|
factory EntryPointClosureShim.fromProcedure(
|
|
Procedure node,
|
|
CoreTypes coreTypes,
|
|
) {
|
|
assert(!node.isGetter && !node.isFactory);
|
|
return EntryPointClosureShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
@override
|
|
String get functionName => '${super.functionName}_Closure';
|
|
}
|
|
|
|
class EntryPointSetterShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Member node;
|
|
|
|
EntryPointSetterShim._({required this.node, required super.coreTypes})
|
|
: super._(
|
|
node: node,
|
|
parameters: [
|
|
EntryPointShimValueParameter._("value", type: node.setterType),
|
|
],
|
|
);
|
|
|
|
factory EntryPointSetterShim.fromMember(Member node, CoreTypes coreTypes) {
|
|
assert(node is Field || (node is Procedure && node.isSetter));
|
|
return EntryPointSetterShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
@override
|
|
String get functionName => 'Set_$prefixedName';
|
|
|
|
@override
|
|
DartType get returnType => VoidType();
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) {
|
|
buffer.write('Dart_SetField(');
|
|
_writeTarget(buffer);
|
|
buffer.write(', Dart_NewStringFromCString("$baseName"), ');
|
|
_writeArguments(buffer);
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
|
|
class EntryPointClassShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Class node;
|
|
|
|
EntryPointClassShim._({required this.node, required super.coreTypes})
|
|
: super._(node: node, parameters: const []);
|
|
|
|
factory EntryPointClassShim.fromClass(Class node, CoreTypes coreTypes) {
|
|
return EntryPointClassShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
@override
|
|
String get functionName => 'Get_${prefixedName}_Class';
|
|
|
|
@override
|
|
DartType get returnType => coreTypes.typeNonNullableRawType;
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) =>
|
|
// Don't use _writeTarget, since we want the default type arguments for
|
|
// generic classes.
|
|
buffer.write('PackageState::instance()->TypeWithDefaults("$baseName")');
|
|
}
|
|
|
|
class EntryPointNullableTypeShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Class node;
|
|
|
|
EntryPointNullableTypeShim._({required this.node, required super.coreTypes})
|
|
: super._(node: node);
|
|
|
|
factory EntryPointNullableTypeShim.fromClass(
|
|
Class node,
|
|
CoreTypes coreTypes,
|
|
) {
|
|
return EntryPointNullableTypeShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
@override
|
|
String get functionName => 'Get_${prefixedName}_NullableType';
|
|
|
|
@override
|
|
DartType get returnType => coreTypes.typeNonNullableRawType;
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) {
|
|
buffer.write('Dart_TypeToNullableType(');
|
|
_writeTarget(buffer);
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
|
|
class EntryPointNonNullableTypeShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Class node;
|
|
|
|
EntryPointNonNullableTypeShim._({
|
|
required this.node,
|
|
required super.coreTypes,
|
|
}) : super._(node: node);
|
|
|
|
factory EntryPointNonNullableTypeShim.fromClass(
|
|
Class node,
|
|
CoreTypes coreTypes,
|
|
) {
|
|
return EntryPointNonNullableTypeShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
@override
|
|
String get functionName => 'Get_${prefixedName}_NonNullableType';
|
|
|
|
@override
|
|
DartType get returnType => coreTypes.typeNonNullableRawType;
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) => _writeTarget(buffer);
|
|
}
|
|
|
|
class EntryPointAllocationShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Class node;
|
|
|
|
EntryPointAllocationShim._({required this.node, required super.coreTypes})
|
|
: super._(node: node);
|
|
|
|
factory EntryPointAllocationShim.fromClass(Class node, CoreTypes coreTypes) {
|
|
return EntryPointAllocationShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
@override
|
|
String get functionName => 'Allocate_${prefixedName}';
|
|
|
|
@override
|
|
DartType get returnType => DynamicType(); // to avoid C conversions
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) {
|
|
buffer.write('Dart_Allocate(');
|
|
_writeTarget(buffer);
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
|
|
class EntryPointNewShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Constructor node;
|
|
|
|
EntryPointNewShim._({required this.node, required super.coreTypes})
|
|
: super._(node: node);
|
|
|
|
factory EntryPointNewShim.fromConstructor(
|
|
Constructor node,
|
|
CoreTypes coreTypes,
|
|
) {
|
|
return EntryPointNewShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
static FunctionType functionType(Constructor node) =>
|
|
node.function.computeThisFunctionType(Nullability.nonNullable);
|
|
|
|
@override
|
|
String get functionName => 'New_$prefixedName';
|
|
|
|
@override
|
|
DartType get returnType => node.function.returnType;
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) {
|
|
buffer.write('Dart_New(');
|
|
_writeTarget(buffer);
|
|
buffer.write(', Dart_NewStringFromCString("$baseName"), ');
|
|
_writeArgumentsListCountAndPointer(buffer);
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
|
|
class EntryPointInitializationShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Constructor node;
|
|
|
|
EntryPointInitializationShim._({required this.node, required super.coreTypes})
|
|
: super._(
|
|
node: node,
|
|
target: EntryPointShimValueParameter._(
|
|
"_instance",
|
|
type: node.enclosingClass.getThisType(
|
|
coreTypes,
|
|
Nullability.nonNullable,
|
|
),
|
|
),
|
|
);
|
|
|
|
factory EntryPointInitializationShim.fromConstructor(
|
|
Constructor node,
|
|
CoreTypes coreTypes,
|
|
) {
|
|
return EntryPointInitializationShim._(node: node, coreTypes: coreTypes);
|
|
}
|
|
|
|
static FunctionType functionType(Constructor node) {
|
|
final type = node.function.computeThisFunctionType(Nullability.nonNullable);
|
|
return FunctionType(
|
|
type.positionalParameters,
|
|
VoidType(),
|
|
type.declaredNullability,
|
|
namedParameters: type.namedParameters,
|
|
typeParameters: type.typeParameters,
|
|
requiredParameterCount: type.requiredParameterCount,
|
|
);
|
|
}
|
|
|
|
@override
|
|
String get functionName => 'Initialize_$prefixedName';
|
|
|
|
@override
|
|
DartType get returnType => VoidType();
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) {
|
|
buffer.write('Dart_InvokeConstructor(');
|
|
_writeTarget(buffer);
|
|
buffer.write(', Dart_NewStringFromCString("$baseName"), ');
|
|
_writeArgumentsListCountAndPointer(buffer);
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
|
|
class EntryPointCallShim extends EntryPointFunctionShim {
|
|
@override
|
|
final Member node;
|
|
|
|
EntryPointCallShim._({
|
|
required this.node,
|
|
required super.coreTypes,
|
|
super.parameters,
|
|
}) : super._(node: node);
|
|
|
|
factory EntryPointCallShim.fromMember(Member node, CoreTypes coreTypes) {
|
|
final FunctionType type = functionType(node);
|
|
assert(type.typeParameters.isEmpty);
|
|
assert(type.namedParameters.isEmpty);
|
|
assert(
|
|
type.requiredPositionalParameterCount == type.positionalParameters.length,
|
|
);
|
|
List<EntryPointShimParameter>? parameters;
|
|
if (_isGetter(node)) {
|
|
parameters = <EntryPointShimParameter>[];
|
|
for (int i = 0; i < type.positionalParameters.length; ++i) {
|
|
parameters.add(
|
|
EntryPointShimValueParameter._(
|
|
i.toString(),
|
|
type: type.positionalParameters[i],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
return EntryPointCallShim._(
|
|
node: node,
|
|
coreTypes: coreTypes,
|
|
parameters: parameters,
|
|
);
|
|
}
|
|
|
|
static bool _isGetter(Member node) =>
|
|
node is Field || (node is Procedure && node.isGetter);
|
|
|
|
static FunctionType functionType(Member node) =>
|
|
_isGetter(node)
|
|
? node.getterType as FunctionType
|
|
: (node as Procedure).computeSignatureOrFunctionType();
|
|
|
|
@override
|
|
String get functionName => 'Call_$prefixedName';
|
|
|
|
@override
|
|
DartType get returnType => functionType(node).returnType;
|
|
|
|
@override
|
|
void _writeReturnBody(StringBuffer buffer) {
|
|
var isDartHandle = _getCReturnType(coreTypes, returnType) == 'Dart_Handle';
|
|
|
|
if (isDartHandle) {
|
|
buffer.write('Dart_NewPersistentHandle(');
|
|
}
|
|
buffer.write('Dart_Invoke(');
|
|
_writeTarget(buffer);
|
|
buffer.write(', Dart_NewStringFromCString("$baseName"), ');
|
|
_writeArgumentsListCountAndPointer(buffer);
|
|
buffer.write(')');
|
|
if (isDartHandle) {
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
}
|