blob: cfe006da7c7fad90e01818bba1ca2a0480f747bb [file] [log] [blame]
// Copyright (c) 2021, 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.
// @dart=2.9
import 'dart:ffi';
/// Retained because of being by-value return type in FFI call.
class Struct1 extends Struct {
Pointer notEmpty;
}
/// Retained because of being by-value return type in FFI call.
class Struct2 extends Struct {
Pointer notEmpty;
}
/// Retained because of being by-value argument type in FFI callback.
class Struct3 extends Struct {
Pointer notEmpty;
}
/// Class not retained, not referenced at all.
class Struct4 extends Struct {
Pointer notEmpty;
}
/// Constructor not retained, only referenced as argument type in FFI
/// call but never instantiated in Dart code.
class Struct5 extends Struct {
Pointer notEmpty;
}
/// Constructor not retained, only referenced as argument type in FFI
/// call but never instantiated in Dart code.
class Struct6 extends Struct {
Pointer notEmpty;
}
/// Constructor not retained, only referenced as return value type in FFI
/// callback but never instantiated in Dart code.
class Struct7 extends Struct {
Pointer notEmpty;
}
/// Not retained because of FFI call not being reachable.
class Struct8 extends Struct {
Pointer notEmpty;
}
/// Not retained because of FFI call not being reachable.
class Struct9 extends Struct {
Pointer notEmpty;
}
/// Not tetained because of FFI callback not being reachable.
class Struct10 extends Struct {
Pointer notEmpty;
}
/// Retained by CFE rewrite of load from pointer.
class Struct11 extends Struct {
Struct12 nested;
}
/// Retained by rewrite of load from surrounding struct.
class Struct12 extends Struct {
Pointer notEmpty;
}
void main() {
testLookupFunctionReturn();
testLookupFunctionArgument();
testAsFunctionReturn();
testAsFunctionArgument();
testFromFunctionArgument();
testFromFunctionReturn();
testPointerLoad();
testNestedLoad();
}
/// This forces retaining [Struct1], because it is constructed as return
/// value in the FFI trampoline.
void testLookupFunctionReturn() {
final dylib = DynamicLibrary.executable();
final function1 =
dylib.lookupFunction<Struct1 Function(), Struct1 Function()>('function1');
final struct1 = function1();
print(struct1);
}
/// This forces retaining [Struct2], because it is constructed as return
/// value in the FFI trampoline.
void testAsFunctionReturn() {
final pointer =
Pointer<NativeFunction<Struct2 Function()>>.fromAddress(0xdeadbeef);
final function2 = pointer.asFunction<Struct2 Function()>();
final struct2 = function2();
print(struct2);
}
int useStruct3(Struct3 struct3) {
return 42;
}
/// This forces retaining [Struct3], because it is constructed as an argument
/// in the FFI callback.
///
/// We're not doing data-flow analysis to see if this pointer ever makes it to
/// C. We're assuming that all pointers from [fromFunction] calls that are not
/// dead code will be passed to C.
void testFromFunctionArgument() {
final pointer = Pointer.fromFunction<Int32 Function(Struct3)>(useStruct3, 0);
print(pointer);
}
void testLookupFunctionArgument() {
final dylib = DynamicLibrary.executable();
final function5 =
dylib.lookupFunction<Void Function(Struct5), void Function(Struct5)>(
'function5');
print(function5);
}
void testAsFunctionArgument() {
final pointer =
Pointer<NativeFunction<Void Function(Struct6)>>.fromAddress(0xdeadbeef);
final function6 = pointer.asFunction<void Function(Struct6)>();
print(function6);
}
Struct7 returnStruct7() {
throw "I don't want to create a Struct7!";
}
void testFromFunctionReturn() {
final pointer = Pointer.fromFunction<Struct7 Function()>(returnStruct7);
print(pointer);
}
/// This does not force retaining [Struct8], because it is not reachable.
void notInvokedLookupFunctionReturn() {
final dylib = DynamicLibrary.executable();
final function8 =
dylib.lookupFunction<Struct8 Function(), Struct8 Function()>('function8');
final struct8 = function8();
print(struct8);
}
/// This does not force retaining [Struct9], because it is not reachable.
void notInvokedAsFunctionReturn() {
final pointer =
Pointer<NativeFunction<Struct9 Function()>>.fromAddress(0xdeadbeef);
final function9 = pointer.asFunction<Struct9 Function()>();
final struct9 = function9();
print(struct9);
}
int useStruct10(Struct10 struct10) {
return 42;
}
/// This does not force retaining [Struct10], because it is not reachable.
void notInvokedFromFunctionArgument() {
final pointer =
Pointer.fromFunction<Int32 Function(Struct10)>(useStruct10, 0);
print(pointer);
}
void testPointerLoad() {
final pointer = Pointer<Struct11>.fromAddress(0xdeadbeef);
final struct11 = pointer.ref;
print(struct11);
}
void testNestedLoad() {
final pointer = Pointer<Struct11>.fromAddress(0xdeadbeef);
final struct11 = pointer.ref;
final struct12 = struct11.nested;
print(struct12);
}