mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-09 16:48:43 +08:00
[flutter] Switch to wasm_ffi
This commit is contained in:
parent
4a715633a7
commit
d059ac01fd
@ -1610,13 +1610,12 @@ ${declaration} {`;
|
||||
lines.push('');
|
||||
lines.push('// ignore_for_file: type_argument_not_matching_bounds');
|
||||
lines.push(`import 'package:flutter/services.dart';`);
|
||||
lines.push(`import 'package:inject_js/inject_js.dart' as js;`);
|
||||
lines.push(`import 'web_ffi/web_ffi.dart';`);
|
||||
lines.push(`import 'web_ffi/web_ffi_modules.dart';`);
|
||||
lines.push(`import 'package:wasm_ffi/ffi.dart';`);
|
||||
lines.push('');
|
||||
lines.push(`import 'generated/spine_dart_bindings_generated.dart';`);
|
||||
lines.push('');
|
||||
lines.push('Module? _module;');
|
||||
lines.push('// Export this so malloc_web.dart can access it');
|
||||
lines.push('DynamicLibrary? dylibInstance;');
|
||||
lines.push('');
|
||||
lines.push('class SpineDartFFI {');
|
||||
lines.push(' final DynamicLibrary dylib;');
|
||||
@ -1626,8 +1625,7 @@ ${declaration} {`;
|
||||
lines.push('}');
|
||||
lines.push('');
|
||||
lines.push('Future<SpineDartFFI> initSpineDartFFI(bool useStaticLinkage) async {');
|
||||
lines.push(' if (_module == null) {');
|
||||
lines.push(' Memory.init();');
|
||||
lines.push(' if (dylibInstance == null) {');
|
||||
lines.push('');
|
||||
|
||||
// Collect all wrapper types
|
||||
@ -1652,25 +1650,21 @@ ${declaration} {`;
|
||||
wrapperTypes.add('spine_skin_entries_wrapper');
|
||||
wrapperTypes.add('spine_texture_loader_wrapper');
|
||||
|
||||
lines.push('');
|
||||
lines.push(` // Load the wasm module first - this calls initTypes()`);
|
||||
lines.push(` dylibInstance = await DynamicLibrary.open('assets/packages/spine_flutter/lib/assets/libspine_flutter.js');`);
|
||||
lines.push('');
|
||||
lines.push(' // Now register all the opaque types');
|
||||
|
||||
// Sort and write all registerOpaqueType calls
|
||||
const sortedTypes = Array.from(wrapperTypes).sort();
|
||||
for (const type of sortedTypes) {
|
||||
lines.push(` registerOpaqueType<${type}>();`);
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
lines.push(` await js.importLibrary('assets/packages/spine_flutter/lib/assets/libspine_flutter.js');`);
|
||||
lines.push(` Uint8List wasmBinaries = (await rootBundle.load(`);
|
||||
lines.push(` 'packages/spine_flutter/lib/assets/libspine_flutter.wasm',`);
|
||||
lines.push(` ))`);
|
||||
lines.push(` .buffer`);
|
||||
lines.push(` .asUint8List();`);
|
||||
lines.push(` _module = await EmscriptenModule.compile(wasmBinaries, 'libspine_flutter');`);
|
||||
lines.push(' }');
|
||||
lines.push(' Module? m = _module;');
|
||||
lines.push(' if (m != null) {');
|
||||
lines.push(' final dylib = DynamicLibrary.fromModule(m);');
|
||||
lines.push(' return SpineDartFFI(dylib, dylib.boundMemory);');
|
||||
lines.push(' ');
|
||||
lines.push(' if (dylibInstance != null) {');
|
||||
lines.push(' return SpineDartFFI(dylibInstance!, dylibInstance!.allocator);');
|
||||
lines.push(' } else {');
|
||||
lines.push(` throw Exception("Couldn't load libspine-flutter.js/.wasm");`);
|
||||
lines.push(' }');
|
||||
|
||||
@ -45,10 +45,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.1.4"
|
||||
flame:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -74,10 +74,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.4.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -138,10 +138,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.1"
|
||||
raw_image_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -202,6 +202,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
wasm_ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wasm_ffi
|
||||
sha256: f4539052e4d80575bb4820c836a2d6400af061b71c1605af60ebfb30b7c5369a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
dart: ">=3.7.0 <4.0.0"
|
||||
flutter: ">=3.27.1"
|
||||
|
||||
@ -27,7 +27,8 @@
|
||||
/// THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
///
|
||||
|
||||
export 'dart:ffi' if (dart.library.html) 'web_ffi/web_ffi.dart';
|
||||
export 'dart:ffi' if (dart.library.html) 'package:wasm_ffi/ffi.dart';
|
||||
|
||||
export 'package:ffi/ffi.dart' if (dart.library.html) 'web_ffi/ffi/utf8.dart';
|
||||
export 'malloc_native.dart' if (dart.library.html) 'malloc_web.dart';
|
||||
export 'package:ffi/ffi.dart' if (dart.library.html) 'package:wasm_ffi/ffi_utils.dart';
|
||||
// Only export our custom malloc on native platforms
|
||||
export 'malloc_native.dart' if (dart.library.io) 'malloc_native.dart';
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import 'web_ffi/web_ffi.dart';
|
||||
import 'web_ffi/web_ffi_modules.dart';
|
||||
import 'package:wasm_ffi/ffi.dart';
|
||||
import 'spine_dart_init_web.dart';
|
||||
|
||||
/// Web implementation of malloc that uses the Memory allocator
|
||||
/// Memory.global will be set when the DynamicLibrary is loaded
|
||||
/// Web implementation of malloc that uses the DynamicLibrary's allocator
|
||||
/// The allocator will be available after spine is initialized
|
||||
Allocator get malloc {
|
||||
if (Memory.global == null) {
|
||||
throw StateError('Memory.global is not initialized. Make sure spine is initialized before using malloc.');
|
||||
// Get the allocator from the current dylib
|
||||
final dylib = dylibInstance;
|
||||
if (dylib == null) {
|
||||
throw StateError('DynamicLibrary is not initialized. Make sure spine is initialized before using malloc.');
|
||||
}
|
||||
return Memory.global!;
|
||||
return dylib.allocator;
|
||||
}
|
||||
|
||||
@ -31,13 +31,12 @@
|
||||
|
||||
// ignore_for_file: type_argument_not_matching_bounds
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:inject_js/inject_js.dart' as js;
|
||||
import 'web_ffi/web_ffi.dart';
|
||||
import 'web_ffi/web_ffi_modules.dart';
|
||||
import 'package:wasm_ffi/ffi.dart';
|
||||
|
||||
import 'generated/spine_dart_bindings_generated.dart';
|
||||
|
||||
Module? _module;
|
||||
// Export this so malloc_web.dart can access it
|
||||
DynamicLibrary? dylibInstance;
|
||||
|
||||
class SpineDartFFI {
|
||||
final DynamicLibrary dylib;
|
||||
@ -47,9 +46,11 @@ class SpineDartFFI {
|
||||
}
|
||||
|
||||
Future<SpineDartFFI> initSpineDartFFI(bool useStaticLinkage) async {
|
||||
if (_module == null) {
|
||||
Memory.init();
|
||||
if (dylibInstance == null) {
|
||||
// Load the wasm module first - this calls initTypes()
|
||||
dylibInstance = await DynamicLibrary.open('assets/packages/spine_flutter/lib/assets/libspine_flutter.js');
|
||||
|
||||
// Now register all the opaque types
|
||||
registerOpaqueType<spine_alpha_timeline_wrapper>();
|
||||
registerOpaqueType<spine_animation_state_data_wrapper>();
|
||||
registerOpaqueType<spine_animation_state_events_wrapper>();
|
||||
@ -208,19 +209,10 @@ Future<SpineDartFFI> initSpineDartFFI(bool useStaticLinkage) async {
|
||||
registerOpaqueType<spine_translate_y_timeline_wrapper>();
|
||||
registerOpaqueType<spine_update_wrapper>();
|
||||
registerOpaqueType<spine_vertex_attachment_wrapper>();
|
||||
|
||||
await js.importLibrary('assets/packages/spine_flutter/lib/assets/libspine_flutter.js');
|
||||
Uint8List wasmBinaries = (await rootBundle.load(
|
||||
'packages/spine_flutter/lib/assets/libspine_flutter.wasm',
|
||||
))
|
||||
.buffer
|
||||
.asUint8List();
|
||||
_module = await EmscriptenModule.compile(wasmBinaries, 'libspine_flutter');
|
||||
}
|
||||
Module? m = _module;
|
||||
if (m != null) {
|
||||
final dylib = DynamicLibrary.fromModule(m);
|
||||
return SpineDartFFI(dylib, dylib.boundMemory);
|
||||
|
||||
if (dylibInstance != null) {
|
||||
return SpineDartFFI(dylibInstance!, dylibInstance!.allocator);
|
||||
} else {
|
||||
throw Exception("Couldn't load libspine-flutter.js/.wasm");
|
||||
}
|
||||
|
||||
@ -1,253 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
import 'types.dart';
|
||||
|
||||
import '../modules/memory.dart';
|
||||
import '../modules/module.dart';
|
||||
import '../internal/marshaller.dart';
|
||||
|
||||
import '../web_ffi_meta.dart';
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [NativeFunction].
|
||||
extension NativeFunctionPointer<NF extends Function> on Pointer<NativeFunction<NF>> {
|
||||
/// Convert to Dart function, automatically marshalling the arguments and return value.
|
||||
///
|
||||
/// There are several rules that apply for the return type of `DF`, see
|
||||
/// the list of [allowed return types](https://github.com/EPNW/web_ffi/blob/master/return_types.md).
|
||||
/// If marshalling failes, a [MarshallingException] is thrown.
|
||||
///
|
||||
/// If this is called on a pointer that does not point to a function,
|
||||
/// a [ArgumentError](https://api.dart.dev/stable/dart-core/ArgumentError-class.html) is thrown.
|
||||
DF asFunction<DF extends Function>() {
|
||||
WasmSymbol symbol = symbolByAddress(boundMemory, address);
|
||||
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)!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DynamicLibraryExtension on DynamicLibrary {
|
||||
/// Helper that combines lookup and cast to a Dart function.
|
||||
///
|
||||
/// This simply calles [DynamicLibrary.lookup] and [NativeFunctionPointer.asFunction]
|
||||
/// internally, so see this two methods for additional insights.
|
||||
F lookupFunction<T extends Function, F extends Function>(String name) =>
|
||||
this.lookup<NativeFunction<T>>(name).asFunction<F>();
|
||||
}
|
||||
|
||||
/// Extension on [Allocator] to provide allocation with [NativeType].
|
||||
extension AllocatorAlloc on Allocator {
|
||||
/// Allocates `sizeOf<T>() * count` bytes of memory using [Allocator.allocate].
|
||||
///
|
||||
/// Since this calls [sizeOf<T>] internally, an exception will be thrown if this
|
||||
/// method is called with an @[unsized] type or before [Memory.init] was called.
|
||||
Pointer<T> call<T extends NativeType>([int count = 1]) => allocate(sizeOf<T>() * count);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Float].
|
||||
extension FloatPointer on Pointer<Float> {
|
||||
/// The float at address.
|
||||
double get value => this[0];
|
||||
void set value(double value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Float32List asTypedList(int length) => boundMemory.buffer.asFloat32List(address, length);
|
||||
|
||||
/// The float at `address + size * index`.
|
||||
double operator [](int index) => viewSingle(index).getFloat32(0, Memory.endianess);
|
||||
void operator []=(int index, double value) => viewSingle(index).setFloat32(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Double].
|
||||
extension DoublePointer on Pointer<Double> {
|
||||
/// The double at address.
|
||||
double get value => this[0];
|
||||
void set value(double value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Float64List asTypedList(int length) => boundMemory.buffer.asFloat64List(address, length);
|
||||
|
||||
/// The double at `address + size * index`.
|
||||
double operator [](int index) => viewSingle(index).getFloat64(0, Memory.endianess);
|
||||
void operator []=(int index, double value) => viewSingle(index).setFloat64(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Int8].
|
||||
extension Int8Pointer on Pointer<Int8> {
|
||||
/// The 8-bit integer at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Int8List asTypedList(int length) => boundMemory.buffer.asInt8List(address, length);
|
||||
|
||||
/// The 8-bit integer at `address + size * index`.
|
||||
int operator [](int index) => viewSingle(index).getInt8(0);
|
||||
void operator []=(int index, int value) => viewSingle(index).setInt8(0, value);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Int16].
|
||||
extension Int16Pointer on Pointer<Int16> {
|
||||
/// The 16-bit integer at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Int16List asTypedList(int length) => boundMemory.buffer.asInt16List(address, length);
|
||||
|
||||
/// The 16-bit integer at `address + size * index`.
|
||||
int operator [](int index) => viewSingle(index).getInt16(0, Memory.endianess);
|
||||
void operator []=(int index, int value) => viewSingle(index).setInt16(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Int32].
|
||||
extension Int32Pointer on Pointer<Int32> {
|
||||
/// The 32-bit integer at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Int32List asTypedList(int length) => boundMemory.buffer.asInt32List(address, length);
|
||||
|
||||
/// The 32-bit integer at `address + size * index`.
|
||||
int operator [](int index) => viewSingle(index).getInt32(0, Memory.endianess);
|
||||
void operator []=(int index, int value) => viewSingle(index).setInt32(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Int64].
|
||||
extension Int64Pointer on Pointer<Int64> {
|
||||
/// The 64-bit integer at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Int64List asTypedList(int length) => boundMemory.buffer.asInt64List(address, length);
|
||||
|
||||
/// The 64-bit integer at `address + size * index`.
|
||||
int operator [](int index) => viewSingle(index).getInt64(0, Memory.endianess);
|
||||
void operator []=(int index, int value) => viewSingle(index).setInt64(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Uint8].
|
||||
extension Uint8Pointer on Pointer<Uint8> {
|
||||
/// The 8-bit unsigned integer at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Uint8List asTypedList(int length) => boundMemory.buffer.asUint8List(address, length);
|
||||
|
||||
/// The 8-bit unsigned integer at `address + size * index`.
|
||||
int operator [](int index) => viewSingle(index).getUint8(0);
|
||||
void operator []=(int index, int value) => viewSingle(index).setUint8(0, value);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Uint16].
|
||||
extension Uint16Pointer on Pointer<Uint16> {
|
||||
/// The 16-bit unsigned integer at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Uint16List asTypedList(int length) => boundMemory.buffer.asUint16List(address, length);
|
||||
|
||||
/// The 16-bit unsigned integer at `address + size * index`.
|
||||
int operator [](int index) => viewSingle(index).getUint16(0, Memory.endianess);
|
||||
void operator []=(int index, int value) => viewSingle(index).setUint16(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Uint32].
|
||||
extension Uint32Pointer on Pointer<Uint32> {
|
||||
/// The 32-bit unsigned integer at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Uint32List asTypedList(int length) => boundMemory.buffer.asUint32List(address, length);
|
||||
|
||||
/// The 32-bit unsigned integer at `address + size * index`.
|
||||
int operator [](int index) => viewSingle(index).getUint32(0, Memory.endianess);
|
||||
void operator []=(int index, int value) => viewSingle(index).setUint32(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Uint64].
|
||||
extension Uint64Pointer on Pointer<Uint64> {
|
||||
/// The 64-bit unsigned integer at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Creates a typed list view backed by memory in the address space.
|
||||
///
|
||||
/// The returned view will allow access to the memory range
|
||||
/// from address to `address + size * length`.
|
||||
Uint64List asTypedList(int length) => boundMemory.buffer.asUint64List(address, length);
|
||||
|
||||
/// The 64-bit unsigned integer at `address + size * index`.
|
||||
int operator [](int index) => viewSingle(index).getUint64(0, Memory.endianess);
|
||||
void operator []=(int index, int value) => viewSingle(index).setUint64(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [IntPtr].
|
||||
extension IntPtrPointer on Pointer<IntPtr> {
|
||||
/// The 32-bit or 64-bit value at `address`.
|
||||
int get value => this[0];
|
||||
void set value(int value) => this[0] = value;
|
||||
|
||||
/// Returns `true` if the size of a pointer is 64-bit, `false` otherwise.
|
||||
@extra
|
||||
bool get is64Bit => size == 8;
|
||||
|
||||
/// The 32-bit or 64-bit integer at `address + size * index`.
|
||||
int operator [](int index) =>
|
||||
is64Bit ? viewSingle(index).getUint64(0, Memory.endianess) : viewSingle(index).getUint32(0, Memory.endianess);
|
||||
void operator []=(int index, int value) => is64Bit
|
||||
? viewSingle(index).setUint64(0, value, Memory.endianess)
|
||||
: viewSingle(index).setUint32(0, value, Memory.endianess);
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Pointer].
|
||||
extension PointerPointer<T extends NativeType> on Pointer<Pointer<T>> {
|
||||
/// The pointer at `address`.
|
||||
Pointer<T> get value => this[0];
|
||||
void set value(Pointer<T> value) => this[0] = value;
|
||||
|
||||
/// Returns `true` if the size of a pointer is 64-bit, `false` otherwise.
|
||||
@extra
|
||||
bool get is64Bit => size == 8;
|
||||
|
||||
/// The pointer at `address + size * index`.
|
||||
Pointer<T> operator [](int index) => new Pointer<T>.fromAddress(
|
||||
is64Bit ? viewSingle(index).getUint64(0, Memory.endianess) : viewSingle(index).getUint32(0, Memory.endianess),
|
||||
boundMemory);
|
||||
void operator []=(int index, Pointer<T> value) => is64Bit
|
||||
? viewSingle(index).setUint64(0, value.address, Memory.endianess)
|
||||
: viewSingle(index).setUint32(0, value.address, Memory.endianess);
|
||||
}
|
||||
@ -1,418 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../modules/module.dart';
|
||||
import '../modules/memory.dart';
|
||||
import '../modules/null_memory.dart';
|
||||
|
||||
import '../internal/type_utils.dart';
|
||||
|
||||
import '../web_ffi_meta.dart';
|
||||
|
||||
/// Represents a pointer into the native C memory corresponding to "NULL",
|
||||
/// e.g. a pointer with address 0.
|
||||
///
|
||||
/// You can compare any other pointer with this pointer using == to check
|
||||
/// if it's also an nullpointer.
|
||||
///
|
||||
/// Any other operation than comparing (e.g. calling [Pointer.cast])
|
||||
/// will result in exceptions.
|
||||
final Pointer<Never> nullptr = new Pointer<Never>._null();
|
||||
|
||||
/// Number of bytes used by native type T.
|
||||
///
|
||||
/// MUST NOT be called with types annoteted with @[unsized] or
|
||||
/// before [Memory.init()] was called or else an exception will be thrown.
|
||||
int sizeOf<T extends NativeType>() {
|
||||
int? size;
|
||||
if (isPointerType<T>()) {
|
||||
size = sizeMap[IntPtr];
|
||||
} else {
|
||||
size = sizeMap[T];
|
||||
}
|
||||
if (size != null) {
|
||||
return size;
|
||||
} else {
|
||||
throw new ArgumentError('The type $T is not known!');
|
||||
}
|
||||
}
|
||||
|
||||
bool _isUnsizedType<T extends NativeType>() {
|
||||
return isNativeFunctionType<T>() || isVoidType<T>();
|
||||
}
|
||||
|
||||
/// [NativeType]'s subtypes represent a native type in C.
|
||||
///
|
||||
/// [NativeType]'s subtypes (except [Pointer]) are not constructible
|
||||
/// in the Dart code and serve purely as markers in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class NativeType {
|
||||
const NativeType();
|
||||
}
|
||||
|
||||
/// Represents a native 64 bit double in C.
|
||||
///
|
||||
/// Double is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Double extends NativeType {
|
||||
const Double();
|
||||
}
|
||||
|
||||
/// Represents a native 32 bit float in C.
|
||||
///
|
||||
/// Float is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Float extends NativeType {
|
||||
const Float();
|
||||
}
|
||||
|
||||
/// Represents a native signed 8 bit integer in C.
|
||||
///
|
||||
/// Int8 is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Int8 extends NativeType {
|
||||
const Int8();
|
||||
}
|
||||
|
||||
/// Represents a native signed 16 bit integer in C.
|
||||
///
|
||||
/// Int16 is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Int16 extends NativeType {
|
||||
const Int16();
|
||||
}
|
||||
|
||||
/// Represents a native signed 32 bit integer in C.
|
||||
///
|
||||
/// Int32 is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Int32 extends NativeType {
|
||||
const Int32();
|
||||
}
|
||||
|
||||
/// Represents a native signed 64 bit integer in C.
|
||||
///
|
||||
/// Int64 is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Int64 extends NativeType {
|
||||
const Int64();
|
||||
}
|
||||
|
||||
/// Alias for Int64 (long long in C)
|
||||
class LongLong extends Int64 {
|
||||
const LongLong();
|
||||
}
|
||||
|
||||
/// Represents a native unsigned 8 bit integer in C.
|
||||
///
|
||||
/// Uint8 is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Uint8 extends NativeType {
|
||||
const Uint8();
|
||||
}
|
||||
|
||||
/// Alias for [Uint8]
|
||||
/// Use Uint8 directly as typedef only works for function types
|
||||
class UnsignedChar extends Uint8 {
|
||||
const UnsignedChar();
|
||||
}
|
||||
|
||||
/// Represents a native unsigned 16 bit integer in C.
|
||||
///
|
||||
/// Uint16 is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Uint16 extends NativeType {
|
||||
const Uint16();
|
||||
}
|
||||
|
||||
/// Alias for [Uint16]
|
||||
/// Use Uint16 directly as typedef only works for function types
|
||||
class UnsignedShort extends Uint16 {
|
||||
const UnsignedShort();
|
||||
}
|
||||
|
||||
/// Represents a native unsigned 32 bit integer in C.
|
||||
///
|
||||
/// Uint32 is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Uint32 extends NativeType {
|
||||
const Uint32();
|
||||
}
|
||||
|
||||
/// Represents a native unsigned 64 bit integer in C.
|
||||
///
|
||||
/// Uint64 is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Uint64 extends NativeType {
|
||||
const Uint64();
|
||||
}
|
||||
|
||||
/// Represents a native pointer-sized integer in C.
|
||||
///
|
||||
/// IntPtr is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class IntPtr extends NativeType {}
|
||||
|
||||
/// Represents a native integer in C.
|
||||
///
|
||||
/// The size is ABI-specific. For common platforms, this is usually 4 bytes
|
||||
/// on 32-bit architectures and 8 bytes on 64-bit architectures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Int extends NativeType {}
|
||||
|
||||
/// Represents a native size_t in C.
|
||||
///
|
||||
/// The size is ABI-specific, typically same as pointer size.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Size extends NativeType {}
|
||||
|
||||
/// Represents a native bool in C.
|
||||
///
|
||||
/// The size is typically 1 byte.
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Bool extends NativeType {}
|
||||
|
||||
/// Represents a function type in C.
|
||||
///
|
||||
/// NativeFunction is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
@unsized
|
||||
class NativeFunction<T extends Function> extends NativeType {}
|
||||
|
||||
/// Opaque's subtypes represent opaque types in C.
|
||||
///
|
||||
/// Classes that extend Opaque MUST NOT have a type argument!
|
||||
///
|
||||
/// Opaque's subtypes are not constructible in the Dart code and serve
|
||||
/// purely as markers in type signatures.
|
||||
@noGeneric
|
||||
@notConstructible
|
||||
class Opaque extends NativeType {}
|
||||
|
||||
/// Represents a void type in C.
|
||||
///
|
||||
/// Void is not constructible in the Dart code and serves
|
||||
/// purely as marker in type signatures.
|
||||
@sealed
|
||||
@notConstructible
|
||||
@unsized
|
||||
class Void extends NativeType {}
|
||||
|
||||
/// Represents a char type in C
|
||||
///
|
||||
/// Char is not constructible in the Dart code and serves
|
||||
/// purely as a marker in type signatures
|
||||
@sealed
|
||||
@notConstructible
|
||||
class Char extends Int8 {
|
||||
const Char();
|
||||
}
|
||||
|
||||
/// Represents a pointer into the native C memory. Cannot be extended.
|
||||
@sealed
|
||||
class Pointer<T extends NativeType> extends NativeType {
|
||||
//static Pointer<NativeFunction<T>> fromFunction<T extends Function>(Function f,
|
||||
// [Object? exceptionalReturn]) =>
|
||||
// throw new UnimplementedError();
|
||||
|
||||
/// Access to the raw pointer value.
|
||||
final int address;
|
||||
|
||||
/// The [Memory] object this pointer is bound to.
|
||||
///
|
||||
/// The `Memory` object backs this pointer, if the value of
|
||||
/// this pointer is accessed.
|
||||
@extra
|
||||
final Memory boundMemory;
|
||||
|
||||
/// How much memory in bytes the type this pointer points to occupies,
|
||||
/// or `null` for @[unsized] types.
|
||||
@extra
|
||||
final int? size;
|
||||
|
||||
factory Pointer._null() {
|
||||
return new Pointer._(0, new NullMemory(), null);
|
||||
}
|
||||
|
||||
/// Constructs a pointer from an address.
|
||||
///
|
||||
/// The optional parameter `bindTo` can be ommited, if and only if
|
||||
/// [Memory.global] is set, which is then used as `Memory` to bind to.
|
||||
factory Pointer.fromAddress(int ptr, [Memory? bindTo]) {
|
||||
bindTo = Memory.global;
|
||||
Memory m;
|
||||
if (bindTo != null) {
|
||||
m = bindTo;
|
||||
} else {
|
||||
throw new StateError('No global memory set and no explcity memory to bind to given!');
|
||||
}
|
||||
return new Pointer._(ptr, m, _isUnsizedType<T>() ? null : sizeOf<T>());
|
||||
}
|
||||
|
||||
Pointer._(this.address, this.boundMemory, this.size);
|
||||
|
||||
/// Casts this pointer to an other type.
|
||||
Pointer<U> cast<U extends NativeType>() =>
|
||||
new Pointer<U>._(address, boundMemory, _isUnsizedType<U>() ? null : sizeOf<U>());
|
||||
|
||||
/// Pointer arithmetic (takes element size into account).
|
||||
///
|
||||
/// Throws an [UnsupportedError] if called on a pointer with an @[unsized]
|
||||
/// type argument.
|
||||
Pointer<T> elementAt(int index) {
|
||||
int? s = size;
|
||||
if (s != null) {
|
||||
return new Pointer<T>._(address + index * s, boundMemory, s);
|
||||
} else {
|
||||
throw new UnsupportedError('elementAt is not supported for unsized types!');
|
||||
}
|
||||
}
|
||||
|
||||
/// The hash code for a Pointer only depends on its address.
|
||||
@override
|
||||
int get hashCode => address;
|
||||
|
||||
/// Two pointers are equal if their address is the same, independently
|
||||
/// of their type argument and of the memory they are bound to.
|
||||
@override
|
||||
bool operator ==(Object other) => (other is Pointer && other.address == address);
|
||||
|
||||
/// Returns a view of a single element at [index] (takes element
|
||||
/// size into account).
|
||||
///
|
||||
/// Any modifications to the data will also alter the [Memory] object.
|
||||
///
|
||||
/// Throws an [UnsupportedError] if called on a pointer with an @[unsized]
|
||||
/// type argument.
|
||||
@extra
|
||||
ByteData viewSingle(int index) {
|
||||
int? s = size;
|
||||
if (s != null) {
|
||||
return boundMemory.buffer.asByteData(address + index * s, s);
|
||||
} else {
|
||||
throw new UnsupportedError('viewSingle is not supported for unsized types!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a dynamically loaded C library.
|
||||
class DynamicLibrary {
|
||||
@extra
|
||||
final Memory boundMemory;
|
||||
|
||||
/// Creates a new instance based on the given module.
|
||||
///
|
||||
/// While for each [DynamicLibrary] a new [Memory] object is
|
||||
/// created, the [Memory] objects share the backing memory if
|
||||
/// they are created based on the same module.
|
||||
///
|
||||
/// The [registerMode] parameter can be used to control if the
|
||||
/// newly created [Memory] object should be registered as
|
||||
/// [Memory.global].
|
||||
@extra
|
||||
factory DynamicLibrary.fromModule(Module module,
|
||||
[MemoryRegisterMode registerMode = MemoryRegisterMode.onlyIfGlobalNotSet]) {
|
||||
Memory memory = createMemory(module);
|
||||
switch (registerMode) {
|
||||
case MemoryRegisterMode.yes:
|
||||
Memory.global = memory;
|
||||
break;
|
||||
case MemoryRegisterMode.no:
|
||||
break;
|
||||
case MemoryRegisterMode.onlyIfGlobalNotSet:
|
||||
if (Memory.global == null) {
|
||||
Memory.global = memory;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return new DynamicLibrary._(memory);
|
||||
}
|
||||
|
||||
DynamicLibrary._(this.boundMemory);
|
||||
|
||||
/// Looks up a symbol in the DynamicLibrary and returns its address in memory.
|
||||
///
|
||||
/// Throws an [ArgumentError] if it fails to lookup the symbol.
|
||||
///
|
||||
/// While this method checks if the underyling wasm symbol is a actually
|
||||
/// a function when you lookup a [NativeFunction]`<T>`, it does not check if
|
||||
/// the return type and parameters of `T` match the wasm function.
|
||||
Pointer<T> lookup<T extends NativeType>(String name) {
|
||||
WasmSymbol symbol = symbolByName(boundMemory, name);
|
||||
if (isNativeFunctionType<T>()) {
|
||||
if (symbol is FunctionDescription) {
|
||||
return new Pointer<T>.fromAddress(symbol.tableIndex, boundMemory);
|
||||
} else {
|
||||
throw new ArgumentError('Tried to look up $name as a function, but it seems it is NOT a function!');
|
||||
}
|
||||
} else {
|
||||
return new Pointer<T>.fromAddress(symbol.address, boundMemory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
abstract class Allocator {
|
||||
/// Allocates byteCount bytes of memory on the native heap.
|
||||
///
|
||||
/// The parameter `alignment` is ignored.
|
||||
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment});
|
||||
|
||||
/// Releases memory allocated on the native heap.
|
||||
void free(Pointer<NativeType> pointer);
|
||||
}
|
||||
|
||||
/// Base class for all FFI struct types.
|
||||
///
|
||||
/// This is a stub implementation for web_ffi compatibility.
|
||||
/// Structs are not fully supported in web_ffi.
|
||||
@unsized
|
||||
class Struct extends NativeType {}
|
||||
|
||||
/// Base class for all FFI union types.
|
||||
///
|
||||
/// This is a stub implementation for web_ffi compatibility.
|
||||
/// Unions are not fully supported in web_ffi.
|
||||
@unsized
|
||||
class Union extends NativeType {}
|
||||
|
||||
/// Represents a fixed-size array of [T] in C.
|
||||
///
|
||||
/// This is a stub implementation for web_ffi compatibility.
|
||||
/// Arrays are not fully supported in web_ffi.
|
||||
@unsized
|
||||
class Array<T extends NativeType> extends NativeType {
|
||||
/// Constructor to allow [Array] declarations.
|
||||
const Array(int dimension1, [int? dimension2, int? dimension3, int? dimension4, int? dimension5]);
|
||||
|
||||
/// Constructor to allow [Array] declarations.
|
||||
const Array.multi(List<int> dimensions);
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
// Copyright (c) 2019, 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 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'types.dart';
|
||||
import 'extensions.dart';
|
||||
import '../modules/memory.dart';
|
||||
|
||||
/// The contents of a native zero-terminated array of UTF-8 code units.
|
||||
///
|
||||
/// The Utf8 type itself has no functionality, it's only intended to be used
|
||||
/// through a `Pointer<Utf8>` representing the entire array. This pointer is
|
||||
/// the equivalent of a char pointer (`const char*`) in C code.
|
||||
class Utf8 extends Opaque {}
|
||||
|
||||
/// Extension method for converting a`Pointer<Utf8>` to a [String].
|
||||
extension Utf8Pointer on Pointer<Utf8> {
|
||||
/// The number of UTF-8 code units in this zero-terminated UTF-8 string.
|
||||
///
|
||||
/// The UTF-8 code units of the strings are the non-zero code units up to the
|
||||
/// first zero code unit.
|
||||
int get length {
|
||||
_ensureNotNullptr('length');
|
||||
final codeUnits = cast<Uint8>();
|
||||
return _length(codeUnits);
|
||||
}
|
||||
|
||||
/// Converts this UTF-8 encoded string to a Dart string.
|
||||
///
|
||||
/// Decodes the UTF-8 code units of this zero-terminated byte array as
|
||||
/// Unicode code points and creates a Dart string containing those code
|
||||
/// points.
|
||||
///
|
||||
/// If [length] is provided, zero-termination is ignored and the result can
|
||||
/// contain NUL characters.
|
||||
///
|
||||
/// If [length] is not provided, the returned string is the string up til
|
||||
/// but not including the first NUL character.
|
||||
String toDartString({int? length}) {
|
||||
_ensureNotNullptr('toDartString');
|
||||
final codeUnits = cast<Uint8>();
|
||||
if (length != null) {
|
||||
RangeError.checkNotNegative(length, 'length');
|
||||
} else {
|
||||
length = _length(codeUnits);
|
||||
}
|
||||
return utf8.decode(codeUnits.asTypedList(length));
|
||||
}
|
||||
|
||||
static int _length(Pointer<Uint8> codeUnits) {
|
||||
var length = 0;
|
||||
while (codeUnits[length] != 0) {
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
void _ensureNotNullptr(String operation) {
|
||||
if (this == nullptr) {
|
||||
throw UnsupportedError("Operation '$operation' not allowed on a 'nullptr'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension method for converting a [String] to a `Pointer<Utf8>`.
|
||||
extension StringUtf8Pointer on String {
|
||||
/// Creates a zero-terminated [Utf8] code-unit array from this String.
|
||||
///
|
||||
/// If this [String] contains NUL characters, converting it back to a string
|
||||
/// using [Utf8Pointer.toDartString] will truncate the result if a length is
|
||||
/// not passed.
|
||||
///
|
||||
/// Unpaired surrogate code points in this [String] will be encoded as
|
||||
/// replacement characters (U+FFFD, encoded as the bytes 0xEF 0xBF 0xBD) in
|
||||
/// the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
|
||||
///
|
||||
/// Returns an [allocator]-allocated pointer to the result.
|
||||
Pointer<Utf8> toNativeUtf8({Allocator? allocator}) {
|
||||
final alloc = allocator ?? Memory.global ?? (throw StateError('No allocator available'));
|
||||
final units = utf8.encode(this);
|
||||
final Pointer<Uint8> result = alloc<Uint8>(units.length + 1);
|
||||
final Uint8List nativeString = result.asTypedList(units.length + 1);
|
||||
nativeString.setAll(0, units);
|
||||
nativeString[units.length] = 0;
|
||||
return result.cast();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,278 +0,0 @@
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../ffi/types.dart';
|
||||
import '../modules/exceptions.dart';
|
||||
import '../modules/memory.dart';
|
||||
import 'invoker_generated.dart';
|
||||
import 'type_utils.dart';
|
||||
|
||||
// Called from the invokers
|
||||
T execute<T>(Function base, List<Object> args, Memory memory) {
|
||||
if (T == DartVoidType) {
|
||||
Function.apply(base, args.map(_toJsType).toList());
|
||||
return null as T;
|
||||
} else {
|
||||
Object result = Function.apply(base, args.map(_toJsType).toList());
|
||||
return _toDartType<T>(result, memory);
|
||||
}
|
||||
}
|
||||
|
||||
DF marshall<NF extends Function, DF extends Function>(Function base, Memory memory) {
|
||||
return _inferFromSignature(DF.toString()).copyWith(base, memory).run as DF;
|
||||
}
|
||||
|
||||
Object _toJsType(Object dartObject) {
|
||||
if (dartObject is int || dartObject is double || dartObject is bool) {
|
||||
return dartObject;
|
||||
} else if (dartObject is Pointer) {
|
||||
return dartObject.address;
|
||||
} else {
|
||||
throw new MarshallingException('Could not convert dart type ${dartObject.runtimeType} to a JavaScript type!');
|
||||
}
|
||||
}
|
||||
|
||||
InvokeHelper _inferFromSignature(String signature) {
|
||||
String returnType = signature.split('=>').last.trim();
|
||||
if (returnType.startsWith(pointerPointerPointerPrefix)) {
|
||||
throw new MarshallingException('Nesting pointers is only supported to a deepth of 2!' +
|
||||
'\nThis means that you can write Pointer<Pointer<X>> but not Pointer<Pointer<Pointer<X>>>, ...');
|
||||
}
|
||||
InvokeHelper? h = _knownTypes[returnType];
|
||||
if (h != null) {
|
||||
return h;
|
||||
} else {
|
||||
if (returnType.startsWith(pointerNativeFunctionPrefix)) {
|
||||
throw new MarshallingException(
|
||||
'Using pointers to native functions as return type is only allowed if the type of the native function is dynamic!' +
|
||||
'\nThis means that only Pointer<NativeFunction<dynamic>> is allowed!');
|
||||
} else {
|
||||
throw new MarshallingException(
|
||||
'Unknown type $returnType (infered from $signature), all marshallable types: ${listKnownTypes()}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
List<String> listKnownTypes() => new List<String>.of(_knownTypes.keys, growable: false);
|
||||
|
||||
final Map<String, InvokeHelper> _knownTypes = {
|
||||
typeString<int>(): new InvokeHelper<int>(null, null),
|
||||
typeString<double>(): new InvokeHelper<double>(null, null),
|
||||
typeString<bool>(): new InvokeHelper<bool>(null, null),
|
||||
typeString<void>(): new InvokeHelper<void>(null, null)
|
||||
};
|
||||
|
||||
void registerNativeMarshallerType<T extends NativeType>() {
|
||||
_knownTypes[typeString<Pointer<T>>()] = new InvokeHelper<Pointer<T>>(null, null);
|
||||
_knownTypes[typeString<Pointer<Pointer<T>>>()] = new InvokeHelper<Pointer<Pointer<T>>>(null, null);
|
||||
}
|
||||
|
||||
void registerNativeMarshallerOpaque<T extends Opaque>() {
|
||||
_knownTypes[typeString<Pointer<T>>()] = new OpaqueInvokeHelper<T>(null, null);
|
||||
_knownTypes[typeString<Pointer<Pointer<T>>>()] = new OpaqueInvokeHelperSquare<T>(null, null);
|
||||
}
|
||||
|
||||
T _toDartType<T>(Object o, Memory bind) {
|
||||
if (T == int) {
|
||||
if (o is int) {
|
||||
return o as T;
|
||||
} else {
|
||||
throw new MarshallingException.typeMissmatch(T, o);
|
||||
}
|
||||
} else if (T == double) {
|
||||
if (o is double) {
|
||||
return o as T;
|
||||
} else {
|
||||
throw new MarshallingException.typeMissmatch(T, o);
|
||||
}
|
||||
} else if (T == bool) {
|
||||
if (o is bool) {
|
||||
return o as T;
|
||||
} else if (o is int) {
|
||||
// Handle C convention where bool is represented as int (0 = false, non-zero = true)
|
||||
return (o != 0) as T;
|
||||
} else {
|
||||
throw new MarshallingException.typeMissmatch(T, o);
|
||||
}
|
||||
} else {
|
||||
if (T == Pointer_Void) {
|
||||
if (o is int) {
|
||||
return new Pointer<Void>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_IntPtr) {
|
||||
if (o is int) {
|
||||
return new Pointer<IntPtr>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Int8) {
|
||||
if (o is int) {
|
||||
return new Pointer<Int8>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Int16) {
|
||||
if (o is int) {
|
||||
return new Pointer<Int16>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Int32) {
|
||||
if (o is int) {
|
||||
return new Pointer<Int32>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Int64) {
|
||||
if (o is int) {
|
||||
return new Pointer<Int64>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Double) {
|
||||
if (o is int) {
|
||||
return new Pointer<Double>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Uint8) {
|
||||
if (o is int) {
|
||||
return new Pointer<Uint8>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Uint16) {
|
||||
if (o is int) {
|
||||
return new Pointer<Uint16>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Uint32) {
|
||||
if (o is int) {
|
||||
return new Pointer<Uint32>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Uint64) {
|
||||
if (o is int) {
|
||||
return new Pointer<Uint64>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Float) {
|
||||
if (o is int) {
|
||||
return new Pointer<Float>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Char) {
|
||||
if (o is int) {
|
||||
return new Pointer<Char>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Opaque) {
|
||||
if (o is int) {
|
||||
return new Pointer<Opaque>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_NativeFunction_dynamic) {
|
||||
if (o is int) {
|
||||
return new Pointer<NativeFunction<dynamic>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else {
|
||||
if (T == Pointer_Pointer_Void) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Void>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_IntPtr) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<IntPtr>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Int8) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Int8>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Int16) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Int16>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Int32) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Int32>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Int64) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Int64>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Double) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Double>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Uint8) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Uint8>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Uint16) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Uint16>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Uint32) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Uint32>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Uint64) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Uint64>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Char) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Char>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Float) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Float>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else if (T == Pointer_Pointer_Opaque) {
|
||||
if (o is int) {
|
||||
return new Pointer<Pointer<Opaque>>.fromAddress(o, bind) as T;
|
||||
} else {
|
||||
throw new MarshallingException.noAddress(o);
|
||||
}
|
||||
} else {
|
||||
throw new MarshallingException('Can not back-marshall to type $T (object type is ${o.runtimeType}');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
import '../ffi/types.dart';
|
||||
|
||||
/// Hacky workadround, see https://github.com/dart-lang/language/issues/123
|
||||
Type _extractType<T>() => T;
|
||||
String typeString<T>() => _extractType<T>().toString();
|
||||
|
||||
// Variable names begin with a capital letter on purpose (opposing dart conventions) to hilight that
|
||||
// they are treated like types (which are written with a captial letter in dart).
|
||||
final Type Pointer_IntPtr = _extractType<Pointer<IntPtr>>();
|
||||
final Type Pointer_Void = _extractType<Pointer<Void>>();
|
||||
final Type Pointer_Int8 = _extractType<Pointer<Int8>>();
|
||||
final Type Pointer_Int16 = _extractType<Pointer<Int16>>();
|
||||
final Type Pointer_Int32 = _extractType<Pointer<Int32>>();
|
||||
final Type Pointer_Int64 = _extractType<Pointer<Int64>>();
|
||||
final Type Pointer_Double = _extractType<Pointer<Double>>();
|
||||
final Type Pointer_Uint8 = _extractType<Pointer<Uint8>>();
|
||||
final Type Pointer_Uint16 = _extractType<Pointer<Uint16>>();
|
||||
final Type Pointer_Uint32 = _extractType<Pointer<Uint32>>();
|
||||
final Type Pointer_Uint64 = _extractType<Pointer<Uint64>>();
|
||||
final Type Pointer_Char = _extractType<Pointer<Char>>();
|
||||
final Type Pointer_Float = _extractType<Pointer<Float>>();
|
||||
final Type Pointer_Opaque = _extractType<Pointer<Opaque>>();
|
||||
final Type Pointer_Pointer_IntPtr = _extractType<Pointer<Pointer<IntPtr>>>();
|
||||
final Type Pointer_Pointer_Void = _extractType<Pointer<Pointer<Void>>>();
|
||||
final Type Pointer_Pointer_Int8 = _extractType<Pointer<Pointer<Int8>>>();
|
||||
final Type Pointer_Pointer_Int16 = _extractType<Pointer<Pointer<Int16>>>();
|
||||
final Type Pointer_Pointer_Int32 = _extractType<Pointer<Pointer<Int32>>>();
|
||||
final Type Pointer_Pointer_Int64 = _extractType<Pointer<Pointer<Int64>>>();
|
||||
final Type Pointer_Pointer_Double = _extractType<Pointer<Pointer<Double>>>();
|
||||
final Type Pointer_Pointer_Uint8 = _extractType<Pointer<Pointer<Uint8>>>();
|
||||
final Type Pointer_Pointer_Uint16 = _extractType<Pointer<Pointer<Uint16>>>();
|
||||
final Type Pointer_Pointer_Uint32 = _extractType<Pointer<Pointer<Uint32>>>();
|
||||
final Type Pointer_Pointer_Uint64 = _extractType<Pointer<Pointer<Uint64>>>();
|
||||
final Type Pointer_Pointer_Char = _extractType<Pointer<Pointer<Char>>>();
|
||||
final Type Pointer_Pointer_Float = _extractType<Pointer<Pointer<Float>>>();
|
||||
final Type Pointer_Pointer_Opaque = _extractType<Pointer<Pointer<Opaque>>>();
|
||||
final Type Pointer_NativeFunction_dynamic = _extractType<Pointer<NativeFunction<dynamic>>>();
|
||||
final Type DartVoidType = _extractType<void>();
|
||||
final Type FfiVoidType = _extractType<Void>();
|
||||
|
||||
final String _dynamicTypeString = typeString<dynamic>();
|
||||
|
||||
final String pointerPointerPointerPrefix =
|
||||
typeString<Pointer<Pointer<Pointer<dynamic>>>>().split(_dynamicTypeString).first;
|
||||
|
||||
final String pointerNativeFunctionPrefix =
|
||||
typeString<Pointer<NativeFunction<dynamic>>>().split(_dynamicTypeString).first;
|
||||
|
||||
final String _nativeFunctionPrefix = typeString<NativeFunction<dynamic>>().split(_dynamicTypeString).first;
|
||||
bool isNativeFunctionType<T extends NativeType>() => typeString<T>().startsWith(_nativeFunctionPrefix);
|
||||
|
||||
final String _pointerPrefix = typeString<Pointer<dynamic>>().split(_dynamicTypeString).first;
|
||||
bool isPointerType<T extends NativeType>() => typeString<T>().startsWith(_pointerPrefix);
|
||||
|
||||
bool isVoidType<T extends NativeType>() => _extractType<T>() == FfiVoidType;
|
||||
@ -1,35 +0,0 @@
|
||||
class _Extra {
|
||||
const _Extra();
|
||||
}
|
||||
|
||||
/// A class, field or method annotated with extra is present in `web_ffi`,
|
||||
/// but not in `dart:ffi`.
|
||||
const _Extra extra = const _Extra();
|
||||
|
||||
class _NoGeneric {
|
||||
const _NoGeneric();
|
||||
}
|
||||
|
||||
/// If a class which is annotead with [noGeneric] is extended or implemented,
|
||||
/// the derived class MUST NOT impose a type argument!
|
||||
const _NoGeneric noGeneric = const _NoGeneric();
|
||||
|
||||
class _NotConstructible {
|
||||
const _NotConstructible();
|
||||
}
|
||||
|
||||
/// A [NativeType] annotated with unsized should not be instantiated.
|
||||
///
|
||||
/// However, they are not marked as `abstract` to meet the dart:ffi API.
|
||||
const _NotConstructible notConstructible = const _NotConstructible();
|
||||
|
||||
class _Unsized {
|
||||
const _Unsized();
|
||||
}
|
||||
|
||||
/// A [NativeType] annotated with unsized does not have a predefined size.
|
||||
///
|
||||
/// Unsized [NativeType]s do not support [sizeOf] because their size is unknown,
|
||||
/// so calling [sizeOf] with an @[unsized] [NativeType] will throw an exception.
|
||||
/// Consequently, [Pointer.elementAt] is not available and will also throw an exception.
|
||||
const _Unsized unsized = const _Unsized();
|
||||
@ -1,186 +0,0 @@
|
||||
@JS()
|
||||
library emscripten_module;
|
||||
|
||||
import 'dart:typed_data';
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart';
|
||||
import '../module.dart';
|
||||
import '../../web_ffi_meta.dart';
|
||||
|
||||
@JS('globalThis')
|
||||
external Object get _globalThis;
|
||||
|
||||
@JS('Object.entries')
|
||||
external List? _entries(Object? o);
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class _EmscriptenModuleJs {
|
||||
external Uint8List? get wasmBinary;
|
||||
external Uint8List? get HEAPU8;
|
||||
external Object? get asm;
|
||||
external Object? get wasmExports;
|
||||
|
||||
// Must have an unnamed factory constructor with named arguments.
|
||||
external factory _EmscriptenModuleJs({Uint8List wasmBinary});
|
||||
}
|
||||
|
||||
const String _github = r'https://github.com/EPNW/web_ffi';
|
||||
String _adu(WasmSymbol? original, WasmSymbol? tried) =>
|
||||
'CRITICAL EXCEPTION! Address double use! This should never happen, please report this issue on github immediately at $_github' +
|
||||
'\r\nOriginal: $original' +
|
||||
'\r\nTried: $tried';
|
||||
|
||||
typedef int _Malloc(int size);
|
||||
typedef void _Free(int address);
|
||||
|
||||
FunctionDescription _fromWasmFunction(String name, Function func) {
|
||||
String? s = getProperty(func, 'name');
|
||||
if (s != null) {
|
||||
int? index = int.tryParse(s);
|
||||
if (index != null) {
|
||||
int? length = getProperty(func, 'length');
|
||||
if (length != null) {
|
||||
return new FunctionDescription(tableIndex: index, name: name, function: func, argumentCount: length);
|
||||
} else {
|
||||
throw new ArgumentError('$name does not seem to be a function symbol!');
|
||||
}
|
||||
} else {
|
||||
throw new ArgumentError('$name does not seem to be a function symbol!');
|
||||
}
|
||||
} else {
|
||||
throw new ArgumentError('$name does not seem to be a function symbol!');
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation is in `emscripten_module_stub.dart`!
|
||||
@extra
|
||||
class EmscriptenModule extends Module {
|
||||
static Function _moduleFunction(String moduleName) {
|
||||
Function? moduleFunction = getProperty(_globalThis, moduleName);
|
||||
if (moduleFunction != null) {
|
||||
return moduleFunction;
|
||||
} else {
|
||||
throw StateError('Could not find a emscripten module named $moduleName');
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation is in `emscripten_module_stub.dart`!
|
||||
static Future<EmscriptenModule> process(String moduleName) async {
|
||||
Function moduleFunction = _moduleFunction(moduleName);
|
||||
_EmscriptenModuleJs module = new _EmscriptenModuleJs();
|
||||
Object? o = moduleFunction(module);
|
||||
if (o != null) {
|
||||
await promiseToFuture(o);
|
||||
return new EmscriptenModule._fromJs(module);
|
||||
} else {
|
||||
throw new StateError('Could not instantiate an emscripten module!');
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation is in `emscripten_module_stub.dart`!
|
||||
static Future<EmscriptenModule> compile(Uint8List wasmBinary, String moduleName) async {
|
||||
Function moduleFunction = _moduleFunction(moduleName);
|
||||
_EmscriptenModuleJs module = new _EmscriptenModuleJs(wasmBinary: wasmBinary);
|
||||
Object? o = moduleFunction(module);
|
||||
if (o != null) {
|
||||
await promiseToFuture(o);
|
||||
return new EmscriptenModule._fromJs(module);
|
||||
} else {
|
||||
throw new StateError('Could not instantiate an emscripten module!');
|
||||
}
|
||||
}
|
||||
|
||||
final _EmscriptenModuleJs _emscriptenModuleJs;
|
||||
final List<WasmSymbol> _exports;
|
||||
final _Malloc _malloc;
|
||||
final _Free _free;
|
||||
|
||||
@override
|
||||
List<WasmSymbol> get exports => _exports;
|
||||
|
||||
EmscriptenModule._(this._emscriptenModuleJs, this._exports, this._malloc, this._free);
|
||||
|
||||
factory EmscriptenModule._fromJs(_EmscriptenModuleJs module) {
|
||||
Object? asm;
|
||||
if (module.wasmExports != null) {
|
||||
asm = module.wasmExports;
|
||||
} else if (module.asm != null) {
|
||||
asm = module.asm;
|
||||
} else {
|
||||
throw Exception('Neither wasmExports nor asm is available in the module');
|
||||
}
|
||||
if (asm != null) {
|
||||
Map<int, WasmSymbol> knownAddresses = {};
|
||||
_Malloc? malloc;
|
||||
_Free? free;
|
||||
List<WasmSymbol> exports = [];
|
||||
List? entries = _entries(asm);
|
||||
if (entries != null) {
|
||||
for (dynamic entry in entries) {
|
||||
if (entry is List) {
|
||||
Object value = entry.last;
|
||||
if (value is int) {
|
||||
Global g = new Global(address: value, name: entry.first as String);
|
||||
if (knownAddresses.containsKey(value) && knownAddresses[value] is! Global) {
|
||||
throw new StateError(_adu(knownAddresses[value], g));
|
||||
}
|
||||
knownAddresses[value] = g;
|
||||
exports.add(g);
|
||||
} else if (value is Function) {
|
||||
FunctionDescription description = _fromWasmFunction(entry.first as String, value);
|
||||
// It might happen that there are two different c functions that do nothing else than calling the same underlying c function
|
||||
// In this case, a compiler might substitute both functions with the underlying c function
|
||||
// So we got two functions with different names at the same table index
|
||||
// So it is actually ok if there are two things at the same address, as long as they are both functions
|
||||
if (knownAddresses.containsKey(description.tableIndex) &&
|
||||
knownAddresses[description.tableIndex] is! FunctionDescription) {
|
||||
throw new StateError(_adu(knownAddresses[description.tableIndex], description));
|
||||
}
|
||||
knownAddresses[description.tableIndex] = description;
|
||||
exports.add(description);
|
||||
if (description.name == 'malloc') {
|
||||
malloc = description.function as _Malloc;
|
||||
} else if (description.name == 'free') {
|
||||
free = description.function as _Free;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new StateError('Unexpected entry in entries(Module[\'asm\'])!');
|
||||
}
|
||||
}
|
||||
if (malloc != null) {
|
||||
if (free != null) {
|
||||
return new EmscriptenModule._(module, exports, malloc, free);
|
||||
} else {
|
||||
throw new StateError('Module does not export the free function!');
|
||||
}
|
||||
} else {
|
||||
throw new StateError('Module does not export the malloc function!');
|
||||
}
|
||||
} else {
|
||||
throw new StateError('JavaScript error: Could not access entries of Module[\'asm\']!');
|
||||
}
|
||||
} else {
|
||||
throw new StateError(
|
||||
'Could not access Module[\'asm\'], are your sure your module was compiled using emscripten?');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void free(int pointer) => _free(pointer);
|
||||
|
||||
@override
|
||||
ByteBuffer get heap => _getHeap();
|
||||
ByteBuffer _getHeap() {
|
||||
Uint8List? h = _emscriptenModuleJs.HEAPU8;
|
||||
if (h != null) {
|
||||
return h.buffer;
|
||||
} else {
|
||||
throw StateError('Unexpected memory error!');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int malloc(int size) => _malloc(size);
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
import '../module.dart';
|
||||
|
||||
import '../../web_ffi_meta.dart';
|
||||
|
||||
/// Provides access to WebAssembly compiled with [emscripten](https://emscripten.org).
|
||||
///
|
||||
/// WebAssembly compiled with emscripten comes with an `<moduleName>.wasm`
|
||||
/// and an additional `<moduleName>.js` glue JavaScript file. The later is
|
||||
/// required to be loaded on the page before calling any of this classes
|
||||
/// functions.
|
||||
///
|
||||
/// The WebAssembly must have been compiled with the `-s MODULARIZE=1`
|
||||
/// and `-s EXPORT_NAME=<moduleName>` flags. Futhermore the `<moduleName.js>`
|
||||
/// must contain all exported WebAssembly functions that should be usable from
|
||||
/// dart, so using `-s MAIN_MODULE=1` might be advisable.
|
||||
///
|
||||
/// For a detailed walkthrough on how to create and inject these files,
|
||||
/// see the [example](https://github.com/EPNW/web_ffi/blob/master/example/README.md).
|
||||
///
|
||||
/// On platforms where [dart:js](https://api.dart.dev/stable/dart-js/dart-js-library.html)
|
||||
/// is not available, all methods throw [UnsupportedError]s.
|
||||
@extra
|
||||
class EmscriptenModule extends Module {
|
||||
/// Connects to the JavaScript glue of the emscripten module.
|
||||
///
|
||||
/// This happens in the following way:
|
||||
/// First, a JavaScript property named `moduleName` of the global object
|
||||
/// is accessed, which should contain a function. Then this function is
|
||||
/// called and expected to return a JavaScript emscripten module.
|
||||
///
|
||||
/// The JavaScript emscripten module is responsible for retriving the
|
||||
/// WebAssembly and compile it accordingly.
|
||||
///
|
||||
/// On platforms where [dart:js](https://api.dart.dev/stable/dart-js/dart-js-library.html)
|
||||
/// is not available, an [UnsupportedError] is thrown.
|
||||
static Future<EmscriptenModule> process(String moduleName) =>
|
||||
throw new UnsupportedError('Emscripten operations are only allowed on the web (where dart:js is present)!');
|
||||
|
||||
/// Connects to the JavaScript glue of the emscripten module.
|
||||
///
|
||||
/// Works like [process], except that the bytes of the WebAssembly
|
||||
/// are passed to the JavaScript emscripten module, so it is
|
||||
/// your responsibility to fetch it.
|
||||
///
|
||||
/// On platforms where [dart:js](https://api.dart.dev/stable/dart-js/dart-js-library.html)
|
||||
/// is not available, an [UnsupportedError] is thrown.
|
||||
static Future<EmscriptenModule> compile(Uint8List wasmBinary, String moduleName) =>
|
||||
throw new UnsupportedError('Emscripten operations are only allowed on the web (where dart:js is present)!');
|
||||
|
||||
EmscriptenModule._();
|
||||
|
||||
@override
|
||||
List<WasmSymbol> get exports =>
|
||||
throw new UnsupportedError('Emscripten operations are only allowed on the web (where dart:js is present)!');
|
||||
|
||||
@override
|
||||
void free(int pointer) =>
|
||||
throw new UnsupportedError('Emscripten operations are only allowed on the web (where dart:js is present)!');
|
||||
|
||||
@override
|
||||
ByteBuffer get heap =>
|
||||
throw new UnsupportedError('Emscripten operations are only allowed on the web (where dart:js is present)!');
|
||||
|
||||
@override
|
||||
int malloc(int size) =>
|
||||
throw new UnsupportedError('Emscripten operations are only allowed on the web (where dart:js is present)!');
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
/// Occures if it's not possible to convert dart types to JavaScript types.
|
||||
///
|
||||
/// This usually happens if a not allowed type is uses as a [NativeType]'s
|
||||
/// type argument, or a not allowed return value of a [NativeFunction] is
|
||||
/// used.
|
||||
class MarshallingException implements Exception {
|
||||
final dynamic message;
|
||||
const MarshallingException([this.message]);
|
||||
|
||||
MarshallingException.noAddress(Object o) : this('Expected a address (int) but found ${o.runtimeType}');
|
||||
|
||||
MarshallingException.typeMissmatch(Type t, Object o)
|
||||
: this('Expected a type of $t but object has type ${o.runtimeType}');
|
||||
|
||||
@override
|
||||
String toString() => new Exception(message).toString();
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../ffi/utf8.dart';
|
||||
import 'module.dart';
|
||||
import '../ffi/types.dart';
|
||||
import '../internal/marshaller.dart';
|
||||
import '../web_ffi_meta.dart';
|
||||
|
||||
final Map<Type, int> sizeMap = {};
|
||||
|
||||
/// Must be called with each type that extends Opaque before
|
||||
/// attemtping to use that type.
|
||||
@extra
|
||||
void registerOpaqueType<T extends Opaque>() {
|
||||
sizeMap[T] = sizeOf<Opaque>();
|
||||
registerNativeMarshallerOpaque<T>();
|
||||
}
|
||||
|
||||
void _registerType<T extends NativeType>(int size) {
|
||||
sizeMap[T] = size;
|
||||
registerNativeMarshallerType<T>();
|
||||
}
|
||||
|
||||
/// Represents the native heap.
|
||||
@extra
|
||||
class Memory implements Allocator {
|
||||
/// The endianess of data stored.
|
||||
///
|
||||
/// The WebAssembly speficiation defines little endianess, so this is a constant.
|
||||
static const Endian endianess = Endian.little;
|
||||
|
||||
/// Must be called before working with `web_ffi` to initalize all type sizes.
|
||||
///
|
||||
/// The optional parameter [pointerSizeBytes] can be used to adjust the size
|
||||
/// of pointers. It defaults to `4` since WebAssembly usually uses 32 bit pointers.
|
||||
/// If you want to use wasm64, set [pointerSizeBytes] to `8` to denote 64 bit pointers.
|
||||
static void init([int pointerSizeBytes = 4]) {
|
||||
_registerType<Float>(4);
|
||||
_registerType<Double>(8);
|
||||
_registerType<Int8>(1);
|
||||
_registerType<Uint8>(1);
|
||||
_registerType<Int16>(2);
|
||||
_registerType<Uint16>(2);
|
||||
_registerType<Int32>(4);
|
||||
_registerType<Uint32>(4);
|
||||
_registerType<Int64>(8);
|
||||
_registerType<LongLong>(8);
|
||||
_registerType<Uint64>(8);
|
||||
_registerType<Utf8>(1);
|
||||
_registerType<Char>(1);
|
||||
_registerType<IntPtr>(pointerSizeBytes);
|
||||
_registerType<Int>(4); // int in C is typically 4 bytes (even on 64-bit)
|
||||
_registerType<Size>(pointerSizeBytes); // size_t matches pointer size
|
||||
_registerType<Bool>(1); // bool/_Bool is 1 byte
|
||||
_registerType<UnsignedChar>(1);
|
||||
_registerType<UnsignedShort>(2);
|
||||
_registerType<Opaque>(pointerSizeBytes);
|
||||
registerNativeMarshallerType<Void>();
|
||||
registerNativeMarshallerType<NativeFunction<dynamic>>();
|
||||
}
|
||||
|
||||
/// The default [Memory] object to use.
|
||||
///
|
||||
/// This field is null until it is either manually set to a [Memory] object,
|
||||
/// or automatically set by [DynamicLibrary.fromModule].
|
||||
///
|
||||
/// This is most notably used when creating a pointer using [Pointer.fromAddress]
|
||||
/// with no explicite memory to bind to given.
|
||||
static Memory? global;
|
||||
|
||||
/// Can be used to directly access the memory of this object.
|
||||
///
|
||||
/// The value of this field should not be stored in a state variable,
|
||||
/// since the returned buffer may change over time.
|
||||
@doNotStore
|
||||
ByteBuffer get buffer => _module.heap;
|
||||
|
||||
final Module _module;
|
||||
final Map<String, WasmSymbol> _symbolsByName;
|
||||
final Map<int, WasmSymbol> _symbolsByAddress;
|
||||
|
||||
Memory._(this._module)
|
||||
: _symbolsByAddress = new Map<int, WasmSymbol>.fromEntries(_module.exports.map<MapEntry<int, WasmSymbol>>(
|
||||
(WasmSymbol symbol) => new MapEntry<int, WasmSymbol>(symbol.address, symbol))),
|
||||
_symbolsByName = new Map<String, WasmSymbol>.fromEntries(_module.exports.map<MapEntry<String, WasmSymbol>>(
|
||||
(WasmSymbol symbol) => new MapEntry<String, WasmSymbol>(symbol.name, symbol)));
|
||||
|
||||
@override
|
||||
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
|
||||
return new Pointer<T>.fromAddress(_module.malloc(byteCount), this);
|
||||
}
|
||||
|
||||
@override
|
||||
void free(Pointer<NativeType> pointer) {
|
||||
_module.free(pointer.address);
|
||||
}
|
||||
}
|
||||
|
||||
Memory createMemory(Module module) => new Memory._(module);
|
||||
|
||||
WasmSymbol symbolByAddress(Memory m, int address) {
|
||||
WasmSymbol? s = m._symbolsByAddress[address];
|
||||
if (s != null) {
|
||||
return s;
|
||||
} else {
|
||||
throw new ArgumentError('Could not find symbol at $address!');
|
||||
}
|
||||
}
|
||||
|
||||
WasmSymbol symbolByName(Memory m, String name) {
|
||||
WasmSymbol? s = m._symbolsByName[name];
|
||||
if (s != null) {
|
||||
return s;
|
||||
} else {
|
||||
throw new ArgumentError('Could not find symbol $name!');
|
||||
}
|
||||
}
|
||||
|
||||
/// Used on [DynamicLibrary] creation to control if the therby newly created
|
||||
/// [Memory] object should be registered as [Memory.global].
|
||||
@extra
|
||||
enum MemoryRegisterMode { yes, no, onlyIfGlobalNotSet }
|
||||
@ -1,107 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:meta/meta.dart';
|
||||
import '../web_ffi_meta.dart';
|
||||
|
||||
/// Base class to interact with the WebAssembly.
|
||||
///
|
||||
/// Currently, only [emscripten](https://emscripten.org) compiled WebAssembly is supported,
|
||||
/// so the only concrete implementation if this class is [EmscriptenModule].
|
||||
///
|
||||
/// To support additional mechanisms/frameworks/compilers, create a subclass of
|
||||
/// [Module].
|
||||
@extra
|
||||
abstract class Module {
|
||||
/// Provides access to the malloc function in WebAssembly.
|
||||
///
|
||||
/// Allocates `size` bytes of memory and returns the corresponding
|
||||
/// address.
|
||||
///
|
||||
/// Memory allocated by this should be [free]d afterwards.
|
||||
int malloc(int size);
|
||||
|
||||
/// Provides access to the free function in WebAssembly.
|
||||
///
|
||||
/// Frees the memory region at `pointer` that was previously
|
||||
/// allocated with [malloc].
|
||||
void free(int pointer);
|
||||
|
||||
/// Provides access to the [WebAssemblys memory](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory) buffer.
|
||||
///
|
||||
/// The actual [ByteBuffer] object returned by this getter is allowed to change;
|
||||
/// It should not be cached in a state variable and is thus annotated with @[doNotStore].
|
||||
@doNotStore
|
||||
ByteBuffer get heap;
|
||||
|
||||
/// A list containing everything exported by the underlying
|
||||
/// [WebAssembly instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance).
|
||||
List<WasmSymbol> get exports;
|
||||
}
|
||||
|
||||
/// Describes something exported by the WebAssembly.
|
||||
@extra
|
||||
@sealed
|
||||
abstract class WasmSymbol {
|
||||
/// The address of the exported thing.
|
||||
final int address;
|
||||
|
||||
/// The name of the exported thing.
|
||||
final String name;
|
||||
|
||||
const WasmSymbol({required this.address, required this.name});
|
||||
|
||||
@override
|
||||
int get hashCode => toString().hashCode;
|
||||
|
||||
@override
|
||||
String toString() => '[address=$address\tname=$name]';
|
||||
}
|
||||
|
||||
/// A global is a symbol exported by the WebAssembly,
|
||||
/// that is not a function.
|
||||
@extra
|
||||
@sealed
|
||||
class Global extends WasmSymbol {
|
||||
const Global({required int address, required String name}) : super(address: address, name: name);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is Global) {
|
||||
return name == other.name && address == other.address;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a function exported from WebAssembly.
|
||||
@extra
|
||||
@sealed
|
||||
class FunctionDescription extends WasmSymbol {
|
||||
/// The index of this function in the [WebAssembly table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table).
|
||||
/// This is the same as its address.
|
||||
int get tableIndex => address;
|
||||
|
||||
/// The amount of arguments the underyling function has.
|
||||
final int argumentCount;
|
||||
|
||||
/// The actual function.
|
||||
final Function function;
|
||||
const FunctionDescription(
|
||||
{required int tableIndex, required String name, required this.argumentCount, required this.function})
|
||||
: super(address: tableIndex, name: name);
|
||||
|
||||
@override
|
||||
int get hashCode => '$name$argumentCount$tableIndex'.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is FunctionDescription) {
|
||||
return argumentCount == other.argumentCount && name == other.name && tableIndex == other.tableIndex;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => '[tableIndex=$tableIndex\tname=$name\targumentCount=$argumentCount\tfunction=$function]';
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
import 'memory.dart';
|
||||
import '../ffi/types.dart';
|
||||
|
||||
class NullMemory implements Memory {
|
||||
@override
|
||||
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
|
||||
throw new UnsupportedError('Can not use the null memory to allocate space!');
|
||||
}
|
||||
|
||||
@override
|
||||
ByteBuffer get buffer => throw new UnsupportedError('The null memory has no buffer!');
|
||||
|
||||
@override
|
||||
void free(Pointer<NativeType> pointer) {
|
||||
throw new UnsupportedError('Can not use the null memory to free pointers!');
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
/// Provides mechanisms to use a [dart:ffi 2.12.0](https://api.dart.dev/stable/2.12.0/dart-ffi/dart-ffi-library.html) like API on the web but using [dart:js](https://api.dart.dev/stable/dart-js/dart-js-library.html).
|
||||
/// While some things are missing, new things were added, identifiable by the @[extra] annotation.
|
||||
library web_ffi;
|
||||
|
||||
export './ffi/types.dart';
|
||||
export './ffi/extensions.dart';
|
||||
export './ffi/utf8.dart';
|
||||
@ -1,4 +0,0 @@
|
||||
/// This library contains and explains the annotations for `web_ffi`.
|
||||
library web_ffi_meta;
|
||||
|
||||
export 'meta/meta.dart';
|
||||
@ -1,10 +0,0 @@
|
||||
/// Provides additional classes that are needed for web_ffi,
|
||||
/// but are not present in [dart:ffi](https://api.dart.dev/stable/2.12.0/dart-ffi/dart-ffi-library.html).
|
||||
library web_ffi_modules;
|
||||
|
||||
export 'modules/exceptions.dart';
|
||||
export 'modules/module.dart';
|
||||
export 'modules/memory.dart' show registerOpaqueType, Memory, MemoryRegisterMode;
|
||||
|
||||
export 'modules/emscripten/emscripten_module_stub.dart'
|
||||
if (dart.library.js) 'modules/emscripten/emscripten_module.dart' show EmscriptenModule;
|
||||
@ -20,6 +20,7 @@ dependencies:
|
||||
http: ^1.1.0
|
||||
path: ^1.8.2
|
||||
crypto: ^3.0.3
|
||||
wasm_ffi: ^2.0.7
|
||||
|
||||
dev_dependencies:
|
||||
ffigen: ^10.0.0
|
||||
|
||||
@ -329,7 +329,7 @@ packages:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "4.2.36"
|
||||
version: "4.3.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -426,14 +426,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
web_ffi_fork:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_ffi_fork
|
||||
sha256: "557b3008bb3c8547ee63eac6e53c0ebebb443da9d7558b3f1b98e1ed59989a11"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.5"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user