Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions example/web/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Future<Element> runTests(String source, String name) async {
container.append(
createKeyVal('Size of Pointer', runner.pointerSize().toString()),
);
container.append(createKeyVal('Sum4', runner.sum4().toString()));
container.append(
createKeyVal('Static Init Check', runner.staticInitCheck().toString()),
);
Expand Down
7 changes: 4 additions & 3 deletions example_flutter/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ build: assets/standalone/native_example.wasm assets/emscripten/native_example.js

SRC=src/native_example.c src/native_example_cpp.cpp
DEPS=$(SRC) $(SRC:.c=.h)
EMCC ?= emcc

# Wasm options
ifdef DEBUG
Expand All @@ -22,16 +23,16 @@ clean:
rm -rf assets/emscripten/*

lib/native_example_bindings.dart: $(DEPS)
dart run ffigen --config ffigen.yaml && sed -i "s#'dart:ffi'#'package:wasm_ffi/ffi.dart'#g" $@
dart run ffigen --config ffigen.yaml && perl -pi -e "s#(['\"])dart:ffi\\1#'package:wasm_ffi/ffi.dart'#g" $@

assets/standalone/native_example.wasm: $(DEPS) lib/native_example_bindings.dart
emcc -o assets/standalone/native_example.wasm $(COMPILER_OPTIONS) $(LINKER_OPTIONS) \
$(EMCC) -o assets/standalone/native_example.wasm $(COMPILER_OPTIONS) $(LINKER_OPTIONS) \
$(SRC) \
-s STANDALONE_WASM=1 \
-s $(COMPILED_EXPORTS)

assets/emscripten/native_example.js: $(DEPS) lib/native_example_bindings.dart
emcc -o assets/emscripten/native_example.js $(COMPILER_OPTIONS) $(LINKER_OPTIONS) \
$(EMCC) -o assets/emscripten/native_example.js $(COMPILER_OPTIONS) $(LINKER_OPTIONS) \
$(SRC) \
-s MODULARIZE=1 -s 'EXPORT_NAME="native_example"' -s ALLOW_MEMORY_GROWTH=1 \
-s EXPORTED_RUNTIME_METHODS=HEAPU8 \
Expand Down
23 changes: 21 additions & 2 deletions example_flutter/assets/emscripten/native_example.js

Large diffs are not rendered by default.

Binary file modified example_flutter/assets/emscripten/native_example.wasm
Binary file not shown.
Binary file modified example_flutter/assets/standalone/native_example.wasm
Binary file not shown.
2 changes: 2 additions & 0 deletions example_flutter/lib/example_library_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ class ExampleLibraryWrapper {

int pointerSize() => bindings.pointerSize();

int sum4() => bindings.sum4(1, 2, 3, 4);

bool staticInitCheck() => bindings.static_init_check() != 0;
}
1 change: 1 addition & 0 deletions example_flutter/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class _AsyncRunnerWidgetState extends State<AsyncRunnerWidget> {
'Size of Int': runner.intSize().toString(),
'Size of Bool': runner.boolSize().toString(),
'Size of Pointer': runner.pointerSize().toString(),
'Sum4': runner.sum4().toString(),
'Static Init Check': runner.staticInitCheck().toString(),
};
}
Expand Down
11 changes: 11 additions & 0 deletions example_flutter/lib/native_example_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ class NativeExampleBindings {
);
late final _pointerSize = _pointerSizePtr.asFunction<int Function()>();

/// sum four numbers
int sum4(int a, int b, int c, int d) {
return _sum4(a, b, c, d);
}

late final _sum4Ptr =
_lookup<
ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int, ffi.Int, ffi.Int)>
>('sum4');
late final _sum4 = _sum4Ptr.asFunction<int Function(int, int, int, int)>();

/// check if static initialization happened
int static_init_check() {
return _static_init_check();
Expand Down
6 changes: 6 additions & 0 deletions example_flutter/src/native_example.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,9 @@ EXPORT int pointerSize(void)
{
return (int)sizeof(void*);
}

/// sum four numbers
EXPORT int sum4(int a, int b, int c, int d)
{
return a + b + c + d;
}
5 changes: 4 additions & 1 deletion example_flutter/src/native_example.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ EXPORT int boolSize(void);
/// size of a pointer
EXPORT int pointerSize(void);

/// sum four numbers
EXPORT int sum4(int a, int b, int c, int d);

/// check if static initialization happened
EXPORT int static_init_check(void);

#ifdef __cplusplus
}
#endif

#endif
#endif
6 changes: 2 additions & 4 deletions lib/src/ffi/extensions.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'dart:js_interop';
import 'dart:typed_data';
import 'annotations.dart';
import 'exceptions.dart';
Expand All @@ -21,9 +20,8 @@ extension NativeFunctionPointer<NF extends Function>
DF asFunction<DF extends Function>() {
// ignore: prefer_final_locals
WasmSymbol symbol = symbolByAddress(boundMemory, address);
if (symbol is FunctionDescription && symbol.function.isA<JSFunction>()) {
// ignore: invalid_runtime_check_with_js_interop_types
return marshall<NF, DF>(symbol.function as Function, boundMemory);
if (symbol is FunctionDescription) {
return marshall<NF, DF>(symbol.function, boundMemory);
} else {
throw ArgumentError(
'No function at address $address was found (but a global symbol)!',
Expand Down
10 changes: 5 additions & 5 deletions lib/src/ffi/invoker_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class OpaqueInvokeHelper<T extends Opaque> extends InvokeHelper<Pointer<T>> {
OpaqueInvokeHelper(super.base, super.memory);

@override
InvokeHelper<Pointer<T>> copyWith(Function base, Memory memory) {
InvokeHelper<Pointer<T>> copyWith(Object base, Memory memory) {
return OpaqueInvokeHelper<T>(base, memory);
}

Expand Down Expand Up @@ -282,7 +282,7 @@ class OpaqueInvokeHelperSquare<T extends Opaque>
OpaqueInvokeHelperSquare(super.base, super.memory);

@override
InvokeHelper<Pointer<Pointer<T>>> copyWith(Function base, Memory memory) {
InvokeHelper<Pointer<Pointer<T>>> copyWith(Object base, Memory memory) {
return OpaqueInvokeHelperSquare<T>(base, memory);
}

Expand Down Expand Up @@ -549,11 +549,11 @@ class OpaqueInvokeHelperSquare<T extends Opaque>

class InvokeHelper<T> {
final Memory? _memory;
final Function? _base;
final Object? _base;

const InvokeHelper(this._base, this._memory);

InvokeHelper<T> copyWith(Function base, Memory memory) {
InvokeHelper<T> copyWith(Object base, Memory memory) {
return InvokeHelper(base, memory);
}

Expand Down Expand Up @@ -688,7 +688,7 @@ class InvokeHelper<T> {
if (_base == null || _memory == null) {
throw StateError('Call copyWith first!');
}
final Function base = _base;
final Object base = _base;
final Memory memory = _memory;
final List<Object> args = [
arg0,
Expand Down
2 changes: 2 additions & 0 deletions lib/src/ffi/js_function_apply.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'js_function_apply_stub.dart'
if (dart.library.js_interop) 'js_function_apply_web.dart';
33 changes: 33 additions & 0 deletions lib/src/ffi/js_function_apply_stub.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'exceptions.dart';
import 'memory.dart';
import 'types.dart';

Object? applyJsFunction(
Object base,
List<Object> args, [
Set<int> jsBigIntArgumentIndexes = const {},
]) {
throw const MarshallingException(
'JavaScript function invocation is only available in JavaScript runtimes!',
);
}

Object? toJsFunctionArgument(Object dartObject, {bool asBigInt = false}) {
if (dartObject is int || dartObject is double || dartObject is bool) {
return dartObject;
} else if (dartObject is Pointer) {
return dartObject.address;
}

throw MarshallingException(
'Could not convert dart type ${dartObject.runtimeType} to a JavaScript type!',
);
}

T jsResultToDartType<T>(
Object result,
Memory memory,
R Function<R>(Object value, Memory memory) toDartType,
) {
return toDartType<T>(result, memory);
}
93 changes: 93 additions & 0 deletions lib/src/ffi/js_function_apply_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import 'dart:js_interop';
import 'dart:js_interop_unsafe';

import 'exceptions.dart';
import 'memory.dart';
import 'types.dart';

@JS('BigInt')
external JSBigInt _bigInt(JSAny? obj);

extension type _WrappedJSAny._(JSAny _) implements JSAny {
@JS('toString')
external JSString _toString();
}

Object? applyJsFunction(
Object base,
List<Object> args, [
Set<int> jsBigIntArgumentIndexes = const {},
]) {
final jsFunction = base as JSFunction;
final JSFunction apply = (jsFunction as JSObject).getProperty<JSFunction>(
'apply'.toJS,
);
final jsArgs = <JSAny?>[
for (var i = 0; i < args.length; i++)
_toJsAny(args[i], asBigInt: jsBigIntArgumentIndexes.contains(i)),
];
return apply.callAsFunction(jsFunction, null, jsArgs.toJS);
}

Object? toJsFunctionArgument(Object dartObject, {bool asBigInt = false}) {
return _toJsAny(dartObject, asBigInt: asBigInt);
}

T jsResultToDartType<T>(
Object result,
Memory memory,
R Function<R>(Object value, Memory memory) toDartType,
) {
final jsResult = result as JSAny;
if (T == int) {
if (jsResult.typeofEquals('number')) {
return (jsResult as JSNumber).toDartInt as T;
}
if (jsResult.typeofEquals('bigint')) {
return _jsBigIntToDartInt(jsResult) as T;
}
} else if (T == double) {
if (jsResult.typeofEquals('number')) {
return (jsResult as JSNumber).toDartDouble as T;
}
} else if (T == bool) {
if (jsResult.typeofEquals('boolean')) {
return (jsResult as JSBoolean).toDart as T;
}
if (jsResult.typeofEquals('number')) {
return toDartType<T>((jsResult as JSNumber).toDartInt, memory);
}
} else if (jsResult.typeofEquals('number')) {
return toDartType<T>((jsResult as JSNumber).toDartInt, memory);
} else if (jsResult.typeofEquals('bigint')) {
return toDartType<T>(_jsBigIntToDartInt(jsResult), memory);
}

final Object? dartified = jsResult.dartify();
if (dartified == null) {
return null as T;
}
return toDartType<T>(dartified, memory);
}

int _jsBigIntToDartInt(JSAny jsBigInt) {
return BigInt.parse((jsBigInt as _WrappedJSAny)._toString().toDart).toInt();
}

JSAny? _toJsAny(Object dartObject, {bool asBigInt = false}) {
if (dartObject is int) {
return asBigInt ? _bigInt(dartObject.toString().toJS) : dartObject.toJS;
} else if (dartObject is double) {
return dartObject.toJS;
} else if (dartObject is bool) {
return dartObject.toJS;
} else if (dartObject is Pointer) {
return asBigInt
? _bigInt(dartObject.address.toString().toJS)
: dartObject.address.toJS;
} else {
throw MarshallingException(
'Could not convert dart type ${dartObject.runtimeType} to a JavaScript type!',
);
}
}
Loading