Bug 1955298 - uniffi-bindgen-gecko-js: start using the IR pipeline, r=markh,frontend-codestyle-reviewers

Updated the uniffi-bindgen-gecko-js code to use two things I've been
experimenting with:

* Use the IR pipeline code to generate the structs used to render the
  templates.
* A new test fixture for bindings generators.  This one targets bindings
  generators specifically, it doesn't try test the scaffolding code and
  it's not based on real-world example code.  I originally thought it
  would be a single crate, but I ended up needed 2 in order to test
  external types. (https://bugzilla.mozilla.org/show_bug.cgi?id=1948961)

Differential Revision: https://phabricator.services.mozilla.com/D242385
This commit is contained in:
Ben Dean-Kawamura
2025-05-23 14:02:58 +00:00
committed by bdeankawamura@mozilla.com
parent 12aaef2d5a
commit fe3e643fee
214 changed files with 21235 additions and 37423 deletions

View File

@@ -1251,6 +1251,7 @@ browser/extensions/translations/extension/
# "scaffolding" used by uniffi which isn't valid JS in its original form.
toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/
toolkit/components/uniffi-bindgen-gecko-js/components/generated/*
toolkit/components/uniffi-bindgen-gecko-js/tests/generated/*
##############################################################################
# The list below is copied from ThirdPartyPaths.txt. Prettier doesn't currently

129
Cargo.lock generated
View File

@@ -2584,7 +2584,6 @@ dependencies = [
"gecko_logger",
"geckoservo",
"gkrust-uniffi-components",
"gkrust-uniffi-fixtures",
"gkrust_utils",
"http_sfv",
"idna_glue",
@@ -2635,6 +2634,7 @@ dependencies = [
"unic-langid-ffi",
"unicode-bidi",
"unicode-bidi-ffi",
"uniffi-bindgen-gecko-js-test-fixtures",
"url",
"viaduct",
"webext-storage",
@@ -2657,24 +2657,6 @@ dependencies = [
"webext-storage",
]
[[package]]
name = "gkrust-uniffi-fixtures"
version = "0.1.0"
dependencies = [
"uniffi",
"uniffi-example-arithmetic",
"uniffi-example-custom-types",
"uniffi-example-geometry",
"uniffi-example-rondpoint",
"uniffi-example-sprites",
"uniffi-example-todolist",
"uniffi-fixture-callbacks",
"uniffi-fixture-external-types",
"uniffi-fixture-futures",
"uniffi-fixture-refcounts",
"uniffi-trait-interfaces",
]
[[package]]
name = "gkrust_utils"
version = "0.1.0"
@@ -6992,101 +6974,21 @@ dependencies = [
"clap",
"extend",
"heck",
"indexmap",
"serde",
"textwrap",
"toml",
"uniffi",
"uniffi_bindgen",
"uniffi_pipeline",
]
[[package]]
name = "uniffi-example-arithmetic"
version = "0.22.0"
name = "uniffi-bindgen-gecko-js-test-fixtures"
version = "0.29.0"
dependencies = [
"thiserror 1.999.999",
"uniffi",
]
[[package]]
name = "uniffi-example-custom-types"
version = "0.19.6"
dependencies = [
"anyhow",
"bytes",
"serde_json",
"uniffi",
"url",
]
[[package]]
name = "uniffi-example-geometry"
version = "0.22.0"
dependencies = [
"uniffi",
]
[[package]]
name = "uniffi-example-rondpoint"
version = "0.22.0"
dependencies = [
"uniffi",
]
[[package]]
name = "uniffi-example-sprites"
version = "0.22.0"
dependencies = [
"uniffi",
]
[[package]]
name = "uniffi-example-todolist"
version = "0.22.0"
dependencies = [
"once_cell",
"thiserror 1.999.999",
"uniffi",
]
[[package]]
name = "uniffi-fixture-callbacks"
version = "0.21.0"
dependencies = [
"thiserror 1.999.999",
"uniffi",
]
[[package]]
name = "uniffi-fixture-external-types"
version = "0.21.0"
dependencies = [
"thiserror 1.999.999",
"uniffi",
"uniffi-example-geometry",
"uniffi-example-sprites",
]
[[package]]
name = "uniffi-fixture-futures"
version = "0.21.0"
dependencies = [
"log",
"moz_task",
"uniffi",
]
[[package]]
name = "uniffi-fixture-refcounts"
version = "0.21.0"
dependencies = [
"uniffi",
]
[[package]]
name = "uniffi-trait-interfaces"
version = "0.22.0"
dependencies = [
"uniffi",
"uniffi_bindings_tests",
"uniffi_bindings_tests_external_types",
]
[[package]]
@@ -7116,6 +7018,23 @@ dependencies = [
"uniffi_udl",
]
[[package]]
name = "uniffi_bindings_tests"
version = "0.29.0"
dependencies = [
"thiserror 1.999.999",
"uniffi",
]
[[package]]
name = "uniffi_bindings_tests_external_types"
version = "0.29.0"
dependencies = [
"thiserror 1.999.999",
"uniffi",
"uniffi_bindings_tests",
]
[[package]]
name = "uniffi_build"
version = "0.29.2"

View File

@@ -65,6 +65,7 @@ rust-version = "1.82.0"
# Shared across multiple UniFFI consumers.
uniffi = "0.29.2"
uniffi_bindgen = "0.29.2"
uniffi_pipeline = "0.29.2"
# Shared across multiple application-services consumers.
rusqlite = "0.33.0"
# Shared across multiple glean consumers.

View File

@@ -1,5 +1,4 @@
# RustContextId.sys.mjs
```{js:autoclass} RustContextId.sys.ContextIdComponent
:members:
:exclude-members: ContextIdComponent

View File

@@ -1,5 +1,4 @@
# RustErrorsupport.sys.mjs
```{js:autofunction} RustErrorsupport.sys.setApplicationErrorReporter
```
```{js:autofunction} RustErrorsupport.sys.unsetApplicationErrorReporter

View File

@@ -1,5 +1,8 @@
# RustRelevancy.sys.mjs
```{js:autoclass} RustRelevancy.sys.Interest
:members:
:exclude-members: Interest
```
```{js:autoclass} RustRelevancy.sys.InterestMetrics
:members:
:exclude-members: InterestMetrics
@@ -8,10 +11,6 @@
:members:
:exclude-members: InterestVector
```
```{js:autoclass} RustRelevancy.sys.Interest
:members:
:exclude-members: Interest
```
```{js:autoclass} RustRelevancy.sys.RelevancyApiError
:members:
:exclude-members: RelevancyApiError

View File

@@ -1,17 +1,24 @@
# RustRemoteSettings.sys.mjs
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsClient
:members:
:exclude-members: RemoteSettingsClient
```
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsService
:members:
:exclude-members: RemoteSettingsService
```
```{js:autoclass} RustRemoteSettings.sys.Attachment
:members:
:exclude-members: Attachment
```
```{js:autoclass} RustRemoteSettings.sys.Backoff
:members:
:exclude-members: Backoff
```
```{js:autoclass} RustRemoteSettings.sys.Network
:members:
:exclude-members: Network
```
```{js:autoclass} RustRemoteSettings.sys.Other
:members:
:exclude-members: Other
```
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsClient
:members:
:exclude-members: RemoteSettingsClient
```
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsConfig
:members:
:exclude-members: RemoteSettingsConfig
@@ -24,6 +31,10 @@
:members:
:exclude-members: RemoteSettingsContext
```
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsError
:members:
:exclude-members: RemoteSettingsError
```
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsRecord
:members:
:exclude-members: RemoteSettingsRecord
@@ -32,23 +43,11 @@
:members:
:exclude-members: RemoteSettingsResponse
```
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsError
:members:
:exclude-members: RemoteSettingsError
```
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsServer
:members:
:exclude-members: RemoteSettingsServer
```
```{js:autoclass} RustRemoteSettings.sys.Network
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsService
:members:
:exclude-members: Network
```
```{js:autoclass} RustRemoteSettings.sys.Backoff
:members:
:exclude-members: Backoff
```
```{js:autoclass} RustRemoteSettings.sys.Other
:members:
:exclude-members: Other
:exclude-members: RemoteSettingsService
```

View File

@@ -1,8 +1,7 @@
# RustSearch.sys.mjs
```{js:autoclass} RustSearch.sys.SearchEngineSelector
```{js:autoclass} RustSearch.sys.JsonEngineMethod
:members:
:exclude-members: SearchEngineSelector
:exclude-members: JsonEngineMethod
```
```{js:autoclass} RustSearch.sys.JsonEngineUrl
:members:
@@ -12,34 +11,14 @@
:members:
:exclude-members: JsonEngineUrls
```
```{js:autoclass} RustSearch.sys.Other
:members:
:exclude-members: Other
```
```{js:autoclass} RustSearch.sys.RefinedSearchConfig
:members:
:exclude-members: RefinedSearchConfig
```
```{js:autoclass} RustSearch.sys.SearchEngineDefinition
:members:
:exclude-members: SearchEngineDefinition
```
```{js:autoclass} RustSearch.sys.SearchEngineUrl
:members:
:exclude-members: SearchEngineUrl
```
```{js:autoclass} RustSearch.sys.SearchEngineUrls
:members:
:exclude-members: SearchEngineUrls
```
```{js:autoclass} RustSearch.sys.SearchUrlParam
:members:
:exclude-members: SearchUrlParam
```
```{js:autoclass} RustSearch.sys.SearchUserEnvironment
:members:
:exclude-members: SearchUserEnvironment
```
```{js:autoclass} RustSearch.sys.JsonEngineMethod
:members:
:exclude-members: JsonEngineMethod
```
```{js:autoclass} RustSearch.sys.SearchApplicationName
:members:
:exclude-members: SearchApplicationName
@@ -48,11 +27,31 @@
:members:
:exclude-members: SearchEngineClassification
```
```{js:autoclass} RustSearch.sys.SearchEngineDefinition
:members:
:exclude-members: SearchEngineDefinition
```
```{js:autoclass} RustSearch.sys.SearchEngineSelector
:members:
:exclude-members: SearchEngineSelector
```
```{js:autoclass} RustSearch.sys.SearchEngineUrl
:members:
:exclude-members: SearchEngineUrl
```
```{js:autoclass} RustSearch.sys.SearchEngineUrls
:members:
:exclude-members: SearchEngineUrls
```
```{js:autoclass} RustSearch.sys.SearchUpdateChannel
:members:
:exclude-members: SearchUpdateChannel
```
```{js:autoclass} RustSearch.sys.Other
```{js:autoclass} RustSearch.sys.SearchUrlParam
:members:
:exclude-members: Other
:exclude-members: SearchUrlParam
```
```{js:autoclass} RustSearch.sys.SearchUserEnvironment
:members:
:exclude-members: SearchUserEnvironment
```

View File

@@ -1,12 +1,7 @@
# RustSuggest.sys.mjs
```{js:autoclass} RustSuggest.sys.SuggestStore
```{js:autoclass} RustSuggest.sys.Backoff
:members:
:exclude-members: SuggestStore
```
```{js:autoclass} RustSuggest.sys.SuggestStoreBuilder
:members:
:exclude-members: SuggestStoreBuilder
:exclude-members: Backoff
```
```{js:autoclass} RustSuggest.sys.FtsMatchInfo
:members:
@@ -20,10 +15,34 @@
:members:
:exclude-members: GeonameMatch
```
```{js:autoclass} RustSuggest.sys.GeonameType
:members:
:exclude-members: GeonameType
```
```{js:autoclass} RustSuggest.sys.InterruptKind
:members:
:exclude-members: InterruptKind
```
```{js:autoclass} RustSuggest.sys.Interrupted
:members:
:exclude-members: Interrupted
```
```{js:autoclass} RustSuggest.sys.LabeledTimingSample
:members:
:exclude-members: LabeledTimingSample
```
```{js:autoclass} RustSuggest.sys.Network
:members:
:exclude-members: Network
```
```{js:autoclass} RustSuggest.sys.Other
:members:
:exclude-members: Other
```
```{js:autoclass} RustSuggest.sys.SuggestApiError
:members:
:exclude-members: SuggestApiError
```
```{js:autoclass} RustSuggest.sys.SuggestGlobalConfig
:members:
:exclude-members: SuggestGlobalConfig
@@ -36,30 +55,18 @@
:members:
:exclude-members: SuggestIngestionMetrics
```
```{js:autoclass} RustSuggest.sys.SuggestionProviderConstraints
:members:
:exclude-members: SuggestionProviderConstraints
```
```{js:autoclass} RustSuggest.sys.SuggestionQuery
:members:
:exclude-members: SuggestionQuery
```
```{js:autoclass} RustSuggest.sys.GeonameType
:members:
:exclude-members: GeonameType
```
```{js:autoclass} RustSuggest.sys.InterruptKind
:members:
:exclude-members: InterruptKind
```
```{js:autoclass} RustSuggest.sys.SuggestApiError
:members:
:exclude-members: SuggestApiError
```
```{js:autoclass} RustSuggest.sys.SuggestProviderConfig
:members:
:exclude-members: SuggestProviderConfig
```
```{js:autoclass} RustSuggest.sys.SuggestStore
:members:
:exclude-members: SuggestStore
```
```{js:autoclass} RustSuggest.sys.SuggestStoreBuilder
:members:
:exclude-members: SuggestStoreBuilder
```
```{js:autoclass} RustSuggest.sys.Suggestion
:members:
:exclude-members: Suggestion
@@ -68,25 +75,17 @@
:members:
:exclude-members: SuggestionProvider
```
```{js:autoclass} RustSuggest.sys.SuggestionProviderConstraints
:members:
:exclude-members: SuggestionProviderConstraints
```
```{js:autoclass} RustSuggest.sys.SuggestionQuery
:members:
:exclude-members: SuggestionQuery
```
```{js:autoclass} RustSuggest.sys.YelpSubjectType
:members:
:exclude-members: YelpSubjectType
```
```{js:autoclass} RustSuggest.sys.Network
:members:
:exclude-members: Network
```
```{js:autoclass} RustSuggest.sys.Backoff
:members:
:exclude-members: Backoff
```
```{js:autoclass} RustSuggest.sys.Interrupted
:members:
:exclude-members: Interrupted
```
```{js:autoclass} RustSuggest.sys.Other
:members:
:exclude-members: Other
```
```{js:autofunction} RustSuggest.sys.rawSuggestionUrlMatches
```

View File

@@ -1,5 +1,4 @@
# RustSync15.sys.mjs
```{js:autoclass} RustSync15.sys.DeviceType
:members:
:exclude-members: DeviceType

View File

@@ -1,9 +1,4 @@
# RustTabs.sys.mjs
```{js:autoclass} RustTabs.sys.TabsBridgedEngine
:members:
:exclude-members: TabsBridgedEngine
```
```{js:autoclass} RustTabs.sys.PendingCommand
:members:
:exclude-members: PendingCommand
@@ -12,13 +7,17 @@
:members:
:exclude-members: RemoteCommand
```
```{js:autoclass} RustTabs.sys.SqlError
:members:
:exclude-members: SqlError
```
```{js:autoclass} RustTabs.sys.SyncError
:members:
:exclude-members: SyncError
```
```{js:autoclass} RustTabs.sys.SqlError
```{js:autoclass} RustTabs.sys.TabsBridgedEngine
:members:
:exclude-members: SqlError
:exclude-members: TabsBridgedEngine
```
```{js:autoclass} RustTabs.sys.UnexpectedTabsError
:members:

View File

@@ -1,9 +1,4 @@
# RustWebextstorage.sys.mjs
```{js:autoclass} RustWebextstorage.sys.UnexpectedError
:members:
:exclude-members: UnexpectedError
```
```{js:autoclass} RustWebextstorage.sys.JsonError
:members:
:exclude-members: JsonError
@@ -12,3 +7,7 @@
:members:
:exclude-members: QuotaError
```
```{js:autoclass} RustWebextstorage.sys.UnexpectedError
:members:
:exclude-members: UnexpectedError
```

View File

@@ -143,7 +143,7 @@ if CONFIG["MOZ_BACKGROUNDTASKS"]:
DIRS += ["backgroundtasks"]
if CONFIG["MOZ_UNIFFI_FIXTURES"]:
DIRS += ["uniffi-bindgen-gecko-js/fixtures"]
DIRS += ["uniffi-bindgen-gecko-js/tests"]
if CONFIG["NIGHTLY_BUILD"]:
DIRS += ["aboutinference"]

View File

@@ -15,9 +15,11 @@ clap = { version = "4", default-features = false, features = ["std", "derive", "
cargo_metadata = "0.19"
extend = "1.1"
heck = "0.5"
indexmap = { version = "2.2", features = ["serde"] }
textwrap = { version = "0.16", features = ["smawk"], default-features = false }
uniffi = { workspace = true }
uniffi_bindgen = { workspace = true }
uniffi_pipeline = { workspace = true }
serde = "1"
toml = "0.5"
camino = "1.0.8"

View File

@@ -1,6 +0,0 @@
This directory is where modules generated by UniFFI will be created.
All files in this directory, other than this one, are generated and should
not be hand-edited.
To update these files, execute `./mach uniffi generate`

View File

@@ -1,274 +1,10 @@
// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
import { UniFFITypeError } from "moz-src:///toolkit/components/uniffi-js/js/UniFFI.sys.mjs";
// Objects intended to be used in the unit tests
export var UnitTestObjs = {};
let lazy = {};
ChromeUtils.defineLazyGetter(lazy, "decoder", () => new TextDecoder());
ChromeUtils.defineLazyGetter(lazy, "encoder", () => new TextEncoder());
// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
constructor(arrayBuffer) {
this.dataView = new DataView(arrayBuffer);
this.pos = 0;
}
readUint8() {
let rv = this.dataView.getUint8(this.pos);
this.pos += 1;
return rv;
}
writeUint8(value) {
this.dataView.setUint8(this.pos, value);
this.pos += 1;
}
readUint16() {
let rv = this.dataView.getUint16(this.pos);
this.pos += 2;
return rv;
}
writeUint16(value) {
this.dataView.setUint16(this.pos, value);
this.pos += 2;
}
readUint32() {
let rv = this.dataView.getUint32(this.pos);
this.pos += 4;
return rv;
}
writeUint32(value) {
this.dataView.setUint32(this.pos, value);
this.pos += 4;
}
readUint64() {
let rv = this.dataView.getBigUint64(this.pos);
this.pos += 8;
return Number(rv);
}
writeUint64(value) {
this.dataView.setBigUint64(this.pos, BigInt(value));
this.pos += 8;
}
readInt8() {
let rv = this.dataView.getInt8(this.pos);
this.pos += 1;
return rv;
}
writeInt8(value) {
this.dataView.setInt8(this.pos, value);
this.pos += 1;
}
readInt16() {
let rv = this.dataView.getInt16(this.pos);
this.pos += 2;
return rv;
}
writeInt16(value) {
this.dataView.setInt16(this.pos, value);
this.pos += 2;
}
readInt32() {
let rv = this.dataView.getInt32(this.pos);
this.pos += 4;
return rv;
}
writeInt32(value) {
this.dataView.setInt32(this.pos, value);
this.pos += 4;
}
readInt64() {
let rv = this.dataView.getBigInt64(this.pos);
this.pos += 8;
return Number(rv);
}
writeInt64(value) {
this.dataView.setBigInt64(this.pos, BigInt(value));
this.pos += 8;
}
readFloat32() {
let rv = this.dataView.getFloat32(this.pos);
this.pos += 4;
return rv;
}
writeFloat32(value) {
this.dataView.setFloat32(this.pos, value);
this.pos += 4;
}
readFloat64() {
let rv = this.dataView.getFloat64(this.pos);
this.pos += 8;
return rv;
}
writeFloat64(value) {
this.dataView.setFloat64(this.pos, value);
this.pos += 8;
}
writeString(value) {
// Note: in order to efficiently write this data, we first write the
// string data, reserving 4 bytes for the size.
const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
const encodeResult = lazy.encoder.encodeInto(value, dest);
if (encodeResult.read != value.length) {
throw new UniFFIError(
"writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
);
}
const size = encodeResult.written;
// Next, go back and write the size before the string data
this.dataView.setUint32(this.pos, size);
// Finally, advance our position past both the size and string data
this.pos += size + 4;
}
readString() {
const size = this.readUint32();
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
const value = lazy.decoder.decode(source);
this.pos += size;
return value;
}
readBytes() {
const size = this.readInt32();
const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
this.pos += size;
return bytes
}
writeBytes(value) {
this.writeUint32(value.length);
value.forEach((elt) => {
this.writeUint8(elt);
})
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
switch (result.code) {
case "success":
return liftCallback(result.data);
case "error":
throw liftErrCallback(result.data);
case "internal-error":
if (result.data) {
throw new UniFFIInternalError(FfiConverterString.lift(result.data));
} else {
throw new UniFFIInternalError("Unknown error");
}
default:
throw new UniFFIError(`Unexpected status code: ${result.code}`);
}
}
class UniFFIError {
constructor(message) {
this.message = message;
}
toString() {
return `UniFFIError: ${this.message}`
}
}
class UniFFIInternalError extends UniFFIError {}
// Base class for FFI converters
class FfiConverter {
// throw `UniFFITypeError` if a value to be converted has an invalid type
static checkType(value) {
if (value === undefined ) {
throw new UniFFITypeError(`undefined`);
}
if (value === null ) {
throw new UniFFITypeError(`null`);
}
}
}
// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
static lift(buf) {
return this.read(new ArrayBufferDataStream(buf));
}
static lower(value) {
const buf = new ArrayBuffer(this.computeSize(value));
const dataStream = new ArrayBufferDataStream(buf);
this.write(dataStream, value);
return buf;
}
/**
* Computes the size of the value.
*
* @param {*} _value
* @return {number}
*/
static computeSize(_value) {
throw new UniFFIInternalError("computeSize() should be declared in the derived class");
}
/**
* Reads the type from a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @returns {any}
*/
static read(_dataStream) {
throw new UniFFIInternalError("read() should be declared in the derived class");
}
/**
* Writes the type to a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @param {any} _value
*/
static write(_dataStream, _value) {
throw new UniFFIInternalError("write() should be declared in the derived class");
}
}
// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
/**
* Handler for a single UniFFI CallbackInterface
*
@@ -462,34 +198,318 @@ class UniFFICallbackHandleMapEntry {
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterU32 extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (!Number.isInteger(value)) {
throw new UniFFITypeError(`${value} is not an integer`);
let lazy = {};
ChromeUtils.defineLazyGetter(lazy, "decoder", () => new TextDecoder());
ChromeUtils.defineLazyGetter(lazy, "encoder", () => new TextEncoder());
// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
constructor(arrayBuffer) {
this.dataView = new DataView(arrayBuffer);
this.pos = 0;
}
if (value < 0 || value > 4294967295) {
throw new UniFFITypeError(`${value} exceeds the U32 bounds`);
readUint8() {
let rv = this.dataView.getUint8(this.pos);
this.pos += 1;
return rv;
}
writeUint8(value) {
this.dataView.setUint8(this.pos, value);
this.pos += 1;
}
static computeSize(_value) {
return 4;
readUint16() {
let rv = this.dataView.getUint16(this.pos);
this.pos += 2;
return rv;
}
static lift(value) {
writeUint16(value) {
this.dataView.setUint16(this.pos, value);
this.pos += 2;
}
readUint32() {
let rv = this.dataView.getUint32(this.pos);
this.pos += 4;
return rv;
}
writeUint32(value) {
this.dataView.setUint32(this.pos, value);
this.pos += 4;
}
readUint64() {
let rv = this.dataView.getBigUint64(this.pos);
this.pos += 8;
return Number(rv);
}
writeUint64(value) {
this.dataView.setBigUint64(this.pos, BigInt(value));
this.pos += 8;
}
readInt8() {
let rv = this.dataView.getInt8(this.pos);
this.pos += 1;
return rv;
}
writeInt8(value) {
this.dataView.setInt8(this.pos, value);
this.pos += 1;
}
readInt16() {
let rv = this.dataView.getInt16(this.pos);
this.pos += 2;
return rv;
}
writeInt16(value) {
this.dataView.setInt16(this.pos, value);
this.pos += 2;
}
readInt32() {
let rv = this.dataView.getInt32(this.pos);
this.pos += 4;
return rv;
}
writeInt32(value) {
this.dataView.setInt32(this.pos, value);
this.pos += 4;
}
readInt64() {
let rv = this.dataView.getBigInt64(this.pos);
this.pos += 8;
return Number(rv);
}
writeInt64(value) {
this.dataView.setBigInt64(this.pos, BigInt(value));
this.pos += 8;
}
readFloat32() {
let rv = this.dataView.getFloat32(this.pos);
this.pos += 4;
return rv;
}
writeFloat32(value) {
this.dataView.setFloat32(this.pos, value);
this.pos += 4;
}
readFloat64() {
let rv = this.dataView.getFloat64(this.pos);
this.pos += 8;
return rv;
}
writeFloat64(value) {
this.dataView.setFloat64(this.pos, value);
this.pos += 8;
}
writeString(value) {
// Note: in order to efficiently write this data, we first write the
// string data, reserving 4 bytes for the size.
const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
const encodeResult = lazy.encoder.encodeInto(value, dest);
if (encodeResult.read != value.length) {
throw new UniFFIError(
"writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
);
}
const size = encodeResult.written;
// Next, go back and write the size before the string data
this.dataView.setUint32(this.pos, size);
// Finally, advance our position past both the size and string data
this.pos += size + 4;
}
readString() {
const size = this.readUint32();
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
const value = lazy.decoder.decode(source);
this.pos += size;
return value;
}
static lower(value) {
return value;
readBytes() {
const size = this.readInt32();
const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
this.pos += size;
return bytes
}
static write(dataStream, value) {
dataStream.writeUint32(value)
writeBytes(value) {
this.writeUint32(value.length);
value.forEach((elt) => {
this.writeUint8(elt);
})
}
static read(dataStream) {
return dataStream.readUint32()
// Reads a pointer from the data stream
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointer(pointerId) {
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
}
// Writes a pointer into the data stream
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointer(pointerId, value) {
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
switch (result.code) {
case "success":
return liftCallback(result.data);
case "error":
throw liftErrCallback(result.data);
case "internal-error":
if (result.data) {
throw new UniFFIInternalError(FfiConverterString.lift(result.data));
} else {
throw new UniFFIInternalError("Unknown error");
}
default:
throw new UniFFIError(`Unexpected status code: ${result.code}`);
}
}
class UniFFIError {
constructor(message) {
this.message = message;
}
toString() {
return `UniFFIError: ${this.message}`
}
}
class UniFFIInternalError extends UniFFIError {}
// Base class for FFI converters
class FfiConverter {
// throw `UniFFITypeError` if a value to be converted has an invalid type
static checkType(value) {
if (value === undefined ) {
throw new UniFFITypeError(`undefined`);
}
if (value === null ) {
throw new UniFFITypeError(`null`);
}
}
}
// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
static lift(buf) {
return this.read(new ArrayBufferDataStream(buf));
}
static lower(value) {
const buf = new ArrayBuffer(this.computeSize(value));
const dataStream = new ArrayBufferDataStream(buf);
this.write(dataStream, value);
return buf;
}
/**
* Computes the size of the value.
*
* @param {*} _value
* @return {number}
*/
static computeSize(_value) {
throw new UniFFIInternalError("computeSize() should be declared in the derived class");
}
/**
* Reads the type from a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @returns {any}
*/
static read(_dataStream) {
throw new UniFFIInternalError("read() should be declared in the derived class");
}
/**
* Writes the type to a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @param {any} _value
*/
static write(_dataStream, _value) {
throw new UniFFIInternalError("write() should be declared in the derived class");
}
}
// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
/**
* Set the global error reporter. This is typically done early in startup.
*/
export async function setApplicationErrorReporter(
errorReporter) {
FfiConverterTypeApplicationErrorReporter.checkType(errorReporter);
const result = await UniFFIScaffolding.callAsyncWrapper(
5, // uniffi_error_support_fn_func_set_application_error_reporter
FfiConverterTypeApplicationErrorReporter.lower(errorReporter),
)
return handleRustResult(
result,
(result) => undefined,
null,
)
}
/**
* Unset the global error reporter. This is typically done at shutdown for
* platforms that want to cleanup references like Desktop.
*/
export async function unsetApplicationErrorReporter() {
const result = await UniFFIScaffolding.callAsyncWrapper(
6, // uniffi_error_support_fn_func_unset_application_error_reporter
)
return handleRustResult(
result,
(result) => undefined,
null,
)
}
// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
static checkType(value) {
@@ -519,16 +539,41 @@ export class FfiConverterString extends FfiConverter {
return 4 + lazy.encoder.encode(value).length
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterUInt32 extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (!Number.isInteger(value)) {
throw new UniFFITypeError(`${value} is not an integer`);
}
if (value < 0 || value > 4294967295) {
throw new UniFFITypeError(`${value} exceeds the U32 bounds`);
}
}
static computeSize(_value) {
return 4;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeUint32(value)
}
static read(dataStream) {
return dataStream.readUint32()
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeApplicationErrorReporter extends FfiConverter {
static lower(callbackObj) {
return callbackHandlerApplicationErrorReporter.storeCallbackObj(callbackObj)
return uniffiCallbackHandlerApplicationErrorReporter.storeCallbackObj(callbackObj)
}
static lift(handleId) {
return callbackHandlerApplicationErrorReporter.getCallbackObj(handleId)
return uniffiCallbackHandlerApplicationErrorReporter.getCallbackObj(handleId)
}
static read(dataStream) {
@@ -544,12 +589,9 @@ export class FfiConverterTypeApplicationErrorReporter extends FfiConverter {
}
}
// Define callback interface handlers, this must come after the type loop since they reference the FfiConverters defined above.
const callbackHandlerApplicationErrorReporter = new UniFFICallbackHandler(
"errorsupport:ApplicationErrorReporter",
1,
const uniffiCallbackHandlerApplicationErrorReporter = new UniFFICallbackHandler(
"ApplicationErrorReporter",
2,
[
new UniFFICallbackMethodHandler(
"reportError",
@@ -563,64 +605,39 @@ const callbackHandlerApplicationErrorReporter = new UniFFICallbackHandler(
[
FfiConverterString,
FfiConverterString,
FfiConverterU32,
FfiConverterU32,
FfiConverterUInt32,
FfiConverterUInt32,
],
),
]
);
// Allow the shutdown-related functionality to be tested in the unit tests
UnitTestObjs.callbackHandlerApplicationErrorReporter = callbackHandlerApplicationErrorReporter;
/**
* Set the global error reporter. This is typically done early in startup.
*/
export function setApplicationErrorReporter(errorReporter) {
const liftResult = (result) => undefined;
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeApplicationErrorReporter.checkType(errorReporter)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("errorReporter");
UnitTestObjs.uniffiCallbackHandlerApplicationErrorReporter = uniffiCallbackHandlerApplicationErrorReporter;
// Export the FFIConverter object to make external types work.
export class FfiConverterUInt8 extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (!Number.isInteger(value)) {
throw new UniFFITypeError(`${value} is not an integer`);
}
throw e;
if (value < 0 || value > 256) {
throw new UniFFITypeError(`${value} exceeds the U8 bounds`);
}
return UniFFIScaffolding.callAsyncWrapper(
4, // errorsupport:uniffi_error_support_fn_func_set_application_error_reporter
FfiConverterTypeApplicationErrorReporter.lower(errorReporter),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* Unset the global error reporter. This is typically done at shutdown for
* platforms that want to cleanup references like Desktop.
*/
export function unsetApplicationErrorReporter() {
const liftResult = (result) => undefined;
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsyncWrapper(
5, // errorsupport:uniffi_error_support_fn_func_unset_application_error_reporter
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
static computeSize(_value) {
return 1;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeUint8(value)
}
static read(dataStream) {
return dataStream.readUint8()
}
}

View File

@@ -1,9 +1,7 @@
// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
import { UniFFITypeError } from "moz-src:///toolkit/components/uniffi-js/js/UniFFI.sys.mjs";
// Objects intended to be used in the unit tests
export var UnitTestObjs = {};
@@ -170,6 +168,23 @@ class ArrayBufferDataStream {
this.writeUint8(elt);
})
}
// Reads a pointer from the data stream
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointer(pointerId) {
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
}
// Writes a pointer into the data stream
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointer(pointerId, value) {
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
@@ -268,36 +283,6 @@ const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (typeof value !== "string") {
throw new UniFFITypeError(`${value} is not a string`);
}
}
static lift(buf) {
const utf8Arr = new Uint8Array(buf);
return lazy.decoder.decode(utf8Arr);
}
static lower(value) {
return lazy.encoder.encode(value).buffer;
}
static write(dataStream, value) {
dataStream.writeString(value);
}
static read(dataStream) {
return dataStream.readString();
}
static computeSize(value) {
return 4 + lazy.encoder.encode(value).length
}
}
/**
* Enumeration for the different types of device.
@@ -311,36 +296,33 @@ export const DeviceType = {
/**
* DESKTOP
*/
DESKTOP:0,
DESKTOP: 1,
/**
* MOBILE
*/
MOBILE:1,
MOBILE: 2,
/**
* TABLET
*/
TABLET:2,
TABLET: 3,
/**
* VR
*/
VR:3,
VR: 4,
/**
* TV
*/
TV:4,
TV: 5,
/**
* UNKNOWN
*/
UNKNOWN:5,
UNKNOWN: 6,
};
Object.freeze(DeviceType);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeDeviceType extends FfiConverterArrayBuffer {
static #validValues = Object.values(DeviceType);
static read(dataStream) {
// Use sequential indices (1-based) for the wire format to match Python bindings
switch (dataStream.readInt32()) {
case 1:
return DeviceType.DESKTOP
@@ -392,14 +374,35 @@ export class FfiConverterTypeDeviceType extends FfiConverterArrayBuffer {
}
static checkType(value) {
// Check that the value is a valid enum variant
if (!this.#validValues.includes(value)) {
if (!Number.isInteger(value) || value < 1 || value > 6) {
throw new UniFFITypeError(`${value} is not a valid value for DeviceType`);
}
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterUInt8 extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (!Number.isInteger(value)) {
throw new UniFFITypeError(`${value} is not an integer`);
}
if (value < 0 || value > 256) {
throw new UniFFITypeError(`${value} exceeds the U8 bounds`);
}
}
static computeSize(_value) {
return 1;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeUint8(value)
}
static read(dataStream) {
return dataStream.readUint8()
}
}

View File

@@ -75,29 +75,45 @@ enable = true
[webext_storage.async_wrappers]
enable = true
[arithmetical.async_wrappers]
enable = true
[uniffi_custom_types.async_wrappers]
enable = true
[uniffi_fixture_callbacks.async_wrappers]
[uniffi_bindings_tests.async_wrappers]
enable = true
main_thread = [
"log_even_numbers_main_thread",
"clone_interface",
"func_with_default",
"func_with_error",
"func_with_flat_error",
"func_roundtrip_custom_type",
"swap_test_interfaces",
"invoke_test_callback_interface_method",
"roundtrip_u8",
"roundtrip_i8",
"roundtrip_u16",
"roundtrip_i16",
"roundtrip_u32",
"roundtrip_i32",
"roundtrip_u64",
"roundtrip_i64",
"roundtrip_f32",
"roundtrip_f64",
"roundtrip_bool",
"roundtrip_custom_type",
"roundtrip_string",
"sum_with_many_types",
"test_func",
"func_with_multi_word_arg",
"roundtrip_option",
"roundtrip_vec",
"roundtrip_hash_map",
"roundtrip_complex_compound",
"roundtrip_complex_rec",
"roundtrip_enum_no_data",
"roundtrip_enum_with_data",
"roundtrip_complex_enum",
"AsyncInterface.new",
"ComplexMethods.method_with_default",
"ComplexMethods.method_with_multi_word_arg",
"ComplexMethods.new",
"TestInterface.new",
"TestInterface.get_value",
"TestInterface.ref_count",
]
[uniffi_fixture_external_types.async_wrappers]
enable = true
[uniffi_geometry.async_wrappers]
enable = true
[uniffi_rondpoint.async_wrappers]
enable = true
[uniffi_sprites.async_wrappers]
enable = true
[uniffi_todolist.async_wrappers]
enable = true

View File

@@ -1,34 +0,0 @@
[package]
edition = "2015"
name = "gkrust-uniffi-fixtures"
version = "0.1.0"
authors = ["The Mozilla Project Developers"]
license = "MPL-2.0"
description = "UniFFI-enabled Rust components for libxul"
[lib]
crate-type = ["rlib", "staticlib"]
path = "lib.rs"
test = false
doctest = false
bench = false
doc = false
harness = false
[dependencies]
uniffi = { workspace = true }
uniffi-example-arithmetic = { path = "arithmetic/" }
uniffi-example-geometry = { path = "geometry/" }
uniffi-example-rondpoint = { path = "rondpoint/" }
uniffi-example-sprites = { path = "sprites/" }
uniffi-example-todolist = { path = "todolist/" }
uniffi-example-custom-types = { path = "custom-types/" }
uniffi-fixture-callbacks = { path = "callbacks/" }
uniffi-fixture-external-types = { path = "external-types/" }
uniffi-fixture-futures = { path = "futures/" }
uniffi-fixture-refcounts = { path = "refcounts/" }
uniffi-trait-interfaces = { path = "trait-interfaces/" }
[features]
# Should we depend on xpcom crates?
xpcom = ["uniffi-fixture-futures/moz_task"]

View File

@@ -1,36 +0,0 @@
This directory contains generated code for the UniFFI examples/fixtures and JS
unit tests for it.
This is only built if the `--enable-uniffi-fixtures` flag is present in
`mozconfig`. There's no benefit to including this in a release build.
To add additional examples/fixtures:
- For most of these steps, find the code for existing fixtures and use it as a template for the new code.
- Edit `toolkit/components/uniffi-bindgen-gecko-js/mach_commands.py`
- Add an entry to `FIXTURE_UDL_FILES`
- Edit `toolkit/library/rust/shared/Cargo.toml`
- Add an optional dependency for the fixture.
- Add the feature to the list of features enabled by `uniffi_fixtures`.
- Edit `toolkit/library/rust/shared/lib.rs`:
- Add an `extern crate [name]` to the `uniffi_fixtures` mod
- Note: [name] is the name from the `[lib]` section in the Cargo.toml
for the example/fixture crate. This does not always match the package
name for the crate.
- Add `[name]::reexport_uniffi_scaffolding` to the `uniffi_fixtures` mod
- Edit `toolkit/components/uniffi-bindgen-gecko-js/fixtures/moz.build` and add the fixture name to the `components`
list.
- Add a test module to the `toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/` directory and an entry for it
in `toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell.ini`
- Run `mach vendor rust` to vendor in the Rust code.
- Run `mach uniffi generate` to generate the scaffolding code.
- Check in any new files
To run the tests:
- Make sure you have a `mozconfig` file containing the line `ac_add_options --enable-uniffi-fixtures`
- Run `mach uniffi generate` if:
- You've added or updated a fixture
- You've made changes to `uniffi-bindgen-gecko-js`
- Run `mach build`
- Run `mach xpcshell-test toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/`
- You can also use a path to specific test file
- For subsequent runs, if you only modify the test files, then you can re-run this step directly

View File

@@ -1,21 +0,0 @@
[package]
name = "uniffi-example-arithmetic"
edition = "2021"
version = "0.22.0"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
license = "MPL-2.0"
publish = false
[lib]
crate-type = ["lib", "cdylib"]
name = "arithmetical"
[dependencies]
uniffi = { workspace = true }
thiserror = "1.0"
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }
[dev-dependencies]
uniffi = { workspace = true, features = ["bindgen-tests"] }

View File

@@ -1,16 +0,0 @@
[Error]
enum ArithmeticError {
"IntegerOverflow",
};
namespace arithmetic {
[Throws=ArithmeticError]
u64 add(u64 a, u64 b);
[Throws=ArithmeticError]
u64 sub(u64 a, u64 b);
u64 div(u64 dividend, u64 divisor);
boolean equal(u64 a, u64 b);
};

View File

@@ -1,34 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[derive(Debug, thiserror::Error)]
pub enum ArithmeticError {
#[error("Integer overflow on an operation with {a} and {b}")]
IntegerOverflow { a: u64, b: u64 },
}
fn add(a: u64, b: u64) -> Result<u64> {
a.checked_add(b)
.ok_or(ArithmeticError::IntegerOverflow { a, b })
}
fn sub(a: u64, b: u64) -> Result<u64> {
a.checked_sub(b)
.ok_or(ArithmeticError::IntegerOverflow { a, b })
}
fn div(dividend: u64, divisor: u64) -> u64 {
if divisor == 0 {
panic!("Can't divide by zero");
}
dividend / divisor
}
fn equal(a: u64, b: u64) -> bool {
a == b
}
type Result<T, E = ArithmeticError> = std::result::Result<T, E>;
uniffi::include_scaffolding!("arithmetic");

View File

@@ -1,29 +0,0 @@
import org.mozilla.uniffi.example.arithmetic.*;
assert(add(2u, 4u) == 6uL)
assert(add(4u, 8u) == 12uL)
try {
sub(0u, 2u)
throw RuntimeException("Should have thrown a IntegerOverflow exception!")
} catch (e: ArithmeticException) {
// It's okay!
}
assert(sub(4u, 2u) == 2uL)
assert(sub(8u, 4u) == 4uL)
assert(div(8u, 4u) == 2uL)
try {
div(8u, 0u)
throw RuntimeException("Should have panicked when dividing by zero")
} catch (e: InternalException) {
// It's okay!
}
assert(equal(2u, 2uL))
assert(equal(4u, 4uL))
assert(!equal(2u, 4uL))
assert(!equal(4u, 8uL))

View File

@@ -1,37 +0,0 @@
from arithmetic import InternalError, add, div, equal, sub
try:
add(18446744073709551615, 1)
assert not ("Should have thrown a IntegerOverflow exception!")
except ArithmeticError.IntegerOverflow:
# It's okay!
pass
assert add(2, 4) == 6
assert add(4, 8) == 12
try:
sub(0, 1)
assert not ("Should have thrown a IntegerOverflow exception!")
except ArithmeticError.IntegerOverflow:
# It's okay!
pass
assert sub(4, 2) == 2
assert sub(8, 4) == 4
assert div(8, 4) == 2
try:
div(8, 0)
except InternalError:
# It's okay!
pass
else:
assert not ("Should have panicked when dividing by zero")
assert equal(2, 2)
assert equal(4, 4)
assert not equal(2, 4)
assert not equal(4, 8)

View File

@@ -1,31 +0,0 @@
# frozen_string_literal: true
require 'test/unit'
require 'arithmetic'
include Test::Unit::Assertions
assert_raise Arithmetic::ArithmeticError::IntegerOverflow do
Arithmetic.add 18_446_744_073_709_551_615, 1
end
assert_equal Arithmetic.add(2, 4), 6
assert_equal Arithmetic.add(4, 8), 12
assert_raise Arithmetic::ArithmeticError::IntegerOverflow do
Arithmetic.sub 0, 1
end
assert_equal Arithmetic.sub(4, 2), 2
assert_equal Arithmetic.sub(8, 4), 4
assert_equal Arithmetic.div(8, 4), 2
assert_raise Arithmetic::InternalError do
Arithmetic.div 8, 0
end
assert Arithmetic.equal(2, 2)
assert Arithmetic.equal(4, 4)
assert !Arithmetic.equal(2, 4)
assert !Arithmetic.equal(4, 8)

View File

@@ -1,32 +0,0 @@
import arithmetic
do {
let _ = try add(a: 18446744073709551615, b: 1)
fatalError("Should have thrown a IntegerOverflow exception!")
} catch ArithmeticError.IntegerOverflow {
// It's okay!
}
assert(try! add(a: 2, b: 4) == 6, "add work")
assert(try! add(a: 4, b: 8) == 12, "add work")
do {
let _ = try sub(a: 0, b: 1)
fatalError("Should have thrown a IntegerOverflow exception!")
} catch ArithmeticError.IntegerOverflow {
// It's okay!
}
assert(try! sub(a: 4, b: 2) == 2, "sub work")
assert(try! sub(a: 8, b: 4) == 4, "sub work")
assert(div(dividend: 8, divisor: 4) == 2, "div works")
// We can't test panicking in Swift because we force unwrap the error in
// `div`, which we can't catch.
assert(equal(a: 2, b: 2), "equal works")
assert(equal(a: 4, b: 4), "equal works")
assert(!equal(a: 2, b: 4), "non-equal works")
assert(!equal(a: 4, b: 8), "non-equal works")

View File

@@ -1,6 +0,0 @@
uniffi::build_foreign_language_testcases!(
"tests/bindings/test_arithmetic.rb",
"tests/bindings/test_arithmetic.py",
"tests/bindings/test_arithmetic.kts",
"tests/bindings/test_arithmetic.swift",
);

View File

@@ -1,2 +0,0 @@
[bindings.kotlin]
package_name = "org.mozilla.uniffi.example.arithmetic"

View File

@@ -1,11 +0,0 @@
[package]
name = "uniffi-fixture-callbacks"
edition = "2021"
version = "0.21.0"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
license = "MPL-2.0"
publish = false
[dependencies]
uniffi = { workspace = true }
thiserror = "1.0"

View File

@@ -1,35 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[uniffi::export(callback_interface)]
pub trait Logger: Send + Sync {
fn log(&self, message: String);
// Log a message N times each prefixed with the current index, except when that index is one of
// the items in the exclude vec. The point here is to test sending a bunch of arguments at once
fn log_repeat(&self, message: String, count: u32, exclude: Vec<u32>);
fn finished(&self);
}
#[uniffi::export]
fn log_even_numbers(logger: Box<dyn Logger>, items: Vec<i32>) {
for i in items {
if i % 2 == 0 {
logger.log(format!("Saw even number: {i}"))
}
}
logger.finished();
}
#[uniffi::export]
fn log_even_numbers_main_thread(logger: Box<dyn Logger>, items: Vec<i32>) {
log_even_numbers(logger, items)
}
#[uniffi::export]
fn call_log_repeat(logger: Box<dyn Logger>, message: String, count: u32, exclude: Vec<u32>) {
logger.log_repeat(message, count, exclude);
logger.finished();
}
uniffi::setup_scaffolding!("fixture_callbacks");

View File

@@ -1,21 +0,0 @@
[package]
name = "uniffi-example-custom-types"
edition = "2021"
version = "0.19.6"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
license = "MPL-2.0"
publish = false
[lib]
crate-type = ["lib", "cdylib"]
name = "uniffi_custom_types"
[dependencies]
anyhow = "1"
bytes = "1.0"
serde_json = "1"
uniffi = { workspace = true }
url = "2.4"
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }

View File

@@ -1,7 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
fn main() {
uniffi::generate_scaffolding("./src/custom-types.udl").unwrap();
}

View File

@@ -1,28 +0,0 @@
[Custom]
typedef string Url;
[Custom]
typedef i64 Handle;
dictionary CustomTypesDemo {
Url url;
Handle handle;
};
// Enum with sequential values (default) - only defined in UDL
enum SequentialEnum {
"First",
"Second",
"Fourth",
"Tenth",
"Eleventh",
"Thirteenth"
};
namespace custom_types {
CustomTypesDemo get_custom_types_demo(CustomTypesDemo? demo);
// Functions for sequential enum (UDL-defined)
u8 get_sequential_discriminant(SequentialEnum value);
SequentialEnum echo_sequential_value(SequentialEnum value);
};

View File

@@ -1,125 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use url::Url;
// Custom Handle type which trivially wraps an i64.
pub struct Handle(pub i64);
// We must implement the UniffiCustomTypeConverter trait for each custom type on the scaffolding side
uniffi::custom_type!(Handle, i64, {
try_lift: |val| Ok(Handle(val)),
lower: |obj| obj.0,
});
// Use `url::Url` as a custom type, with `String` as the Builtin
uniffi::custom_type!(Url, String, {
remote,
try_lift: |val| Ok(Url::parse(&val)?),
lower: |obj| obj.to_string(),
});
// And a little struct and function that ties them together.
pub struct CustomTypesDemo {
url: Url,
handle: Handle,
}
// Define the enum that matches the UDL definition
// We don't need to derive uniffi::Enum for this one
#[derive(Debug, Clone, Copy)]
pub enum SequentialEnum {
First,
Second,
Fourth,
Tenth,
Eleventh,
Thirteenth,
}
// Test enum with explicit discriminant values and gaps
#[repr(u8)]
#[derive(Debug, Clone, Copy, uniffi::Enum)]
pub enum ExplicitValuedEnum {
First = 1,
Second = 2,
Fourth = 4,
Tenth = 10,
Eleventh = 11,
Thirteenth = 13,
}
// Example with sequential and explicit values mixed
#[repr(u8)]
#[derive(Debug, Clone, Copy, uniffi::Enum)]
pub enum GappedEnum {
One = 10,
Two, // should be 11
Three = 14,
}
pub fn get_custom_types_demo(v: Option<CustomTypesDemo>) -> CustomTypesDemo {
v.unwrap_or_else(|| CustomTypesDemo {
url: Url::parse("http://example.com/").unwrap(),
handle: Handle(123),
})
}
// Functions for sequential enum (UDL-defined)
pub fn get_sequential_discriminant(value: SequentialEnum) -> u8 {
value as u8
}
pub fn echo_sequential_value(value: SequentialEnum) -> SequentialEnum {
value
}
// Export proc-macro based enum to JS via scaffolding
#[uniffi::export]
pub fn get_explicit_enum_values() -> HashMap<String, u8> {
let mut values = HashMap::new();
values.insert("First".to_string(), ExplicitValuedEnum::First as u8);
values.insert("Second".to_string(), ExplicitValuedEnum::Second as u8);
values.insert("Fourth".to_string(), ExplicitValuedEnum::Fourth as u8);
values.insert("Tenth".to_string(), ExplicitValuedEnum::Tenth as u8);
values.insert("Eleventh".to_string(), ExplicitValuedEnum::Eleventh as u8);
values.insert(
"Thirteenth".to_string(),
ExplicitValuedEnum::Thirteenth as u8,
);
values
}
#[uniffi::export]
pub fn get_explicit_discriminant(value: ExplicitValuedEnum) -> u8 {
value as u8
}
#[uniffi::export]
pub fn echo_explicit_value(value: ExplicitValuedEnum) -> ExplicitValuedEnum {
value
}
// Functions for GappedEnum
#[uniffi::export]
pub fn get_gapped_enum_values() -> HashMap<String, u8> {
let mut values = HashMap::new();
values.insert("One".to_string(), GappedEnum::One as u8);
values.insert("Two".to_string(), GappedEnum::Two as u8);
values.insert("Three".to_string(), GappedEnum::Three as u8);
values
}
#[uniffi::export]
pub fn get_gapped_discriminant(value: GappedEnum) -> u8 {
value as u8
}
#[uniffi::export]
pub fn echo_gapped_value(value: GappedEnum) -> GappedEnum {
value
}
include!(concat!(env!("OUT_DIR"), "/custom-types.uniffi.rs"));

View File

@@ -1,21 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import java.net.URL
import customtypes.*
// TODO: use an actual test runner.
// Get the custom types and check their data
val demo = getCustomTypesDemo(null)
// URL is customized on the bindings side
assert(demo.url == URL("http://example.com/"))
// Handle isn't so it appears as a plain Long
assert(demo.handle == 123L)
// Change some data and ensure that the round-trip works
demo.url = URL("http://new.example.com/")
demo.handle = 456;
assert(demo == getCustomTypesDemo(demo))

View File

@@ -1,20 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import custom_types
import Foundation
// TODO: use an actual test runner.
do {
// Test simple values.
var demo = getCustomTypesDemo(demo: nil)
assert(demo.url == URL(string: "http://example.com/"))
assert(demo.handle == 123)
// Change some data and ensure that the round-trip works
demo.url = URL(string: "http://new.example.com/")!
demo.handle = 456
assert(demo == getCustomTypesDemo(demo: demo))
}

View File

@@ -1,7 +0,0 @@
uniffi::build_foreign_language_testcases!(
["src/custom-types.udl"],
[
"tests/bindings/test_custom_types.kts",
"tests/bindings/test_custom_types.swift",
]
);

View File

@@ -1,40 +0,0 @@
[bindings.swift]
cdylib_name = "custom_types"
[bindings.swift.custom_types.Url]
# Name of the type in the Swift code
type_name = "URL"
# Modules that need to be imported
imports = ["Foundation"]
# Functions to convert between strings and URLs
into_custom = "URL(string: {})!"
from_custom = "String(describing: {})"
[bindings.kotlin]
cdylib_name = "custom_types"
package_name = "customtypes"
[bindings.kotlin.custom_types.Url]
# Name of the type in the Kotlin code
type_name = "URL"
# Classes that need to be imported
imports = [ "java.net.URL" ]
# Functions to convert between strings and URLs
into_custom = "URL({})"
from_custom = "{}.toString()"
[bindings.python]
cdylib_name = "custom_types"
[bindings.python.custom_types.Url]
# We're going to be the urllib.parse.ParseResult class, which is the closest
# thing Python has to a Url class. No need to specify `type_name` though,
# since Python is loosely typed.
# modules to import
imports = ["urllib.parse"]
# Functions to convert between strings and the ParsedUrl class
into_custom = "urllib.parse.urlparse({})"
from_custom = "urllib.parse.urlunparse({})"
[bindings.ruby]
cdylib_name = "custom_types"

View File

@@ -1,16 +0,0 @@
[package]
name = "uniffi-fixture-external-types"
edition = "2021"
version = "0.21.0"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
license = "MPL-2.0"
publish = false
[dependencies]
uniffi-example-geometry = { path = "../geometry/" }
uniffi-example-sprites = { path = "../sprites/" }
uniffi = { workspace = true }
thiserror = "1.0"
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }

View File

@@ -1,27 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::sync::Arc;
use uniffi_geometry::{Line, Point};
#[uniffi::export]
pub fn gradient(value: Option<Line>) -> f64 {
match value {
None => 0.0,
Some(value) => uniffi_geometry::gradient(value),
}
}
#[uniffi::export]
pub fn intersection(ln1: Line, ln2: Line) -> Option<Point> {
uniffi_geometry::intersection(ln1, ln2)
}
#[uniffi::export]
pub fn move_sprite_to_origin(sprite: Arc<uniffi_sprites::Sprite>) {
sprite.move_to(uniffi_sprites::Point { x: 0.0, y: 0.0 })
}
uniffi::setup_scaffolding!("external_types");

View File

@@ -1,22 +0,0 @@
[package]
name = "uniffi-fixture-futures"
version = "0.21.0"
edition = "2021"
license = "MPL-2.0"
publish = false
[lib]
name = "uniffi_fixture_futures"
[dependencies]
log = "0.4"
# This needs to be optional, since we want to build this library to generate the bindings from, but
# moz-task can only currently be built from inside `./mach build`.
moz_task = { path = "../../../../../xpcom/rust/moz_task/", optional = true }
uniffi = { workspace = true }
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }
[dev-dependencies]
uniffi = { workspace = true, features = ["bindgen-tests"] }

View File

@@ -1,52 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Test basic interop between Rust futures and JS async code
//!
//! [FutureTester::make_future] returns a future and the other methods manipulate it manually.
use std::sync::Mutex;
use crate::oneshot;
#[derive(uniffi::Object, Default)]
pub struct FutureTester {
inner: Mutex<Vec<oneshot::Sender<u8>>>,
}
#[uniffi::export]
impl FutureTester {
#[uniffi::constructor]
pub fn init() -> Self {
Self::default()
}
/// Make a new future that can be manipulated with the other FutureTester methods
async fn make_future(&self) -> u8 {
let (tx, rx) = oneshot::channel();
self.inner.lock().unwrap().push(tx);
rx.await
}
/// Store a value in all futures created via `make_future()`, then wake up any wakers.
/// This will cause the C++ code to poll the future and get a `Ready` result.
///
/// Returns the number of futures completed
fn complete_futures(&self, value: u8) -> u32 {
let mut count = 0;
for sender in self.inner.lock().unwrap().drain(..) {
sender.send(value);
count += 1;
}
count
}
/// Wake up the waker for all futures created via `make_future()`. This will cause the C++
/// code to poll the future, but it will get another `Pending` result
fn wake_futures(&self) {
for sender in self.inner.lock().unwrap().iter() {
sender.wake()
}
}
}

View File

@@ -1,14 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
mod future_tester;
mod oneshot;
mod roundtrip;
mod wrapped_sync_call;
pub use future_tester::*;
pub use wrapped_sync_call::*;
pub use roundtrip::*;
uniffi::setup_scaffolding!("futures");

View File

@@ -1,66 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Simple oneshot channel implementation.
//!
//! In practice, we would probably use the `oneshot` crate for this, but it's not worth bringing in
//! that dependency for this test fixture.
use std::{
future::Future,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll, Waker},
};
struct Channel<T> {
value: Option<T>,
waker: Option<Waker>,
}
pub struct Sender<T>(Arc<Mutex<Channel<T>>>);
pub struct Receiver<T>(Arc<Mutex<Channel<T>>>);
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
let channel = Arc::new(Mutex::new(Channel {
value: None,
waker: None,
}));
(Sender(channel.clone()), Receiver(channel))
}
impl<T> Sender<T> {
pub fn send(self, value: T) {
let mut channel = self.0.lock().unwrap();
channel.value = Some(value);
if let Some(waker) = channel.waker.take() {
waker.wake();
}
}
// Wake all receivers, without sending a value.
//
// This causes them to poll again and receive another `Poll::Pending` result
pub fn wake(&self) {
let mut channel = self.0.lock().unwrap();
if let Some(waker) = channel.waker.take() {
waker.wake();
}
}
}
impl<T> Future for Receiver<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, context: &mut Context) -> Poll<T> {
let mut channel = self.0.lock().unwrap();
match channel.value.take() {
Some(v) => Poll::Ready(v),
None => {
channel.waker = Some(context.waker().clone());
Poll::Pending
}
}
}
}

View File

@@ -1,97 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Some barely async functions that round-trip data.
//!
//! The purpose of these is to test the lift/lower implementations in the async code.
use std::{collections::HashMap, sync::Arc};
#[uniffi::export]
async fn roundtrip_u8(v: u8) -> u8 {
v
}
#[uniffi::export]
async fn roundtrip_i8(v: i8) -> i8 {
v
}
#[uniffi::export]
async fn roundtrip_u16(v: u16) -> u16 {
v
}
#[uniffi::export]
async fn roundtrip_i16(v: i16) -> i16 {
v
}
#[uniffi::export]
async fn roundtrip_u32(v: u32) -> u32 {
v
}
#[uniffi::export]
async fn roundtrip_i32(v: i32) -> i32 {
v
}
#[uniffi::export]
async fn roundtrip_u64(v: u64) -> u64 {
v
}
#[uniffi::export]
async fn roundtrip_i64(v: i64) -> i64 {
v
}
#[uniffi::export]
async fn roundtrip_f32(v: f32) -> f32 {
v
}
#[uniffi::export]
async fn roundtrip_f64(v: f64) -> f64 {
v
}
#[uniffi::export]
async fn roundtrip_string(v: String) -> String {
v
}
#[uniffi::export]
async fn roundtrip_vec(v: Vec<u32>) -> Vec<u32> {
v
}
#[uniffi::export]
async fn roundtrip_map(v: HashMap<String, String>) -> HashMap<String, String> {
v
}
#[derive(uniffi::Object)]
struct Traveller {
name: String,
}
#[uniffi::export]
impl Traveller {
#[uniffi::constructor]
pub fn new(name: String) -> Arc<Self> {
Arc::new(Traveller { name })
}
pub fn name(&self) -> String {
self.name.clone()
}
}
#[uniffi::export]
async fn roundtrip_obj(v: Arc<Traveller>) -> Arc<Traveller> {
v
}

View File

@@ -1,131 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Test wrapping sync Rust calls and making them async.
//!
//! This is intended to mimic what we do in application-services: create a synchrounous API, then
//! wrap it so that most methods are async and run in a worker queue.
//!
//!
//! This provides the essentially same functionality as the automatic async wrapping that is
//! available by editing `config.toml` so why do it?
//!
//! - Implementing the async wrapping in Rust leads to better docs. The auto-generated API
//! reference will show which functions are sync and which are async. Telling users that
//! sync functions are actually actually async because of UniFFI magic would lead to confusion.
//!
//! - It opens the door to future performance improvements. Some methods could be async without
//! this wrapper and without sending anything to a background thread. Fxa API calls using an
//! async HTTP client would be one example. Some methods could be split into a "real" async part
//! and a wrapped async part.
//!
//! - Async Rust code allows for new patterns. Maybe the FxA state machine would be better
//! modelled as an async actor rather than a bunch of sync methods protected by a Mutex.
use std::{sync::{Arc, Mutex, OnceLock}, thread::sleep, time::Duration};
use crate::oneshot;
/// Worker queue scheduler trait
///
/// This is a trait that the foreign code implements to schedule Rust tasks in a worker queue. On
/// Swift/Kotlin, we can implement this trait using a DispatchQueue/CoroutineContext.
///
/// On JS we can't implement it directly, since JS is single-threaded and we don't want to start up
/// a web worker. Instead, we implement the trait in Rust using `moz_task`.
#[uniffi::export]
pub trait WorkerQueue: Send + Sync {
fn add_task(&self, task: Arc<dyn RustTask>);
}
#[uniffi::export]
pub trait RustTask: Send + Sync {
fn run(&self);
}
/// Initialize the global worker queue. The Rust code will use this schedule sync tasks in the
/// background in order to present an async interface.
#[uniffi::export]
pub fn initialize_global_worker_queue(worker_queue: Arc<dyn WorkerQueue>) {
GLOBAL_WORKER_QUEUE.set(worker_queue)
.unwrap_or_else(|_| panic!("init_global_worker_queue called twice"));
}
static GLOBAL_WORKER_QUEUE: OnceLock<Arc<dyn WorkerQueue>> = OnceLock::new();
/// Schedule a closure to run in the global worker queue. Returns the result of the closure
/// asynchronously
pub async fn run_in_background<T: Send + Sync + 'static>(task: impl FnOnce() -> T + Send + Sync + 'static) -> T {
let (tx, rx) = oneshot::channel();
GLOBAL_WORKER_QUEUE.get()
.unwrap_or_else(|| panic!("init_global_worker_queue never called"))
.add_task(RustTaskContainer::new_arc(move || {
tx.send(task())
}));
rx.await
}
/// Implements RustTask for any closure
///
/// The one tricky part is that the task can only be run once, but the foreign language gets a
/// shared reference to it. This implementation uses a Mutex, in the real world, `UnsafeCell` +
/// `Once` might be a better option.
struct RustTaskContainer<T: FnOnce() + Send + Sync>(Mutex<Option<T>>);
impl<T: FnOnce() + Send + Sync> RustTaskContainer<T> {
fn new_arc(task: T) -> Arc<Self> {
Arc::new(Self(Mutex::new(Some(task))))
}
}
impl<T: FnOnce() + Send + Sync> RustTask for RustTaskContainer<T> {
fn run(&self) {
if let Some(f) = self.0.lock().unwrap().take() {
f()
}
}
}
/// Initialize the global worker for JS.
///
/// All code above here is generalized task code that works for JS, Kotlin, and Swift.
///
/// This function and the GeckoWorkerQueue struct are Gecko-specific.
#[uniffi::export]
pub fn initialize_gecko_global_worker_queue() {
initialize_global_worker_queue(Arc::new(GeckoWorkerQueue));
}
struct GeckoWorkerQueue;
impl WorkerQueue for GeckoWorkerQueue {
// This version is what runs when we're linked into libgecko
#[cfg(feature = "moz_task")]
fn add_task(&self, task: Arc<dyn RustTask>) {
if let Err(e) = moz_task::dispatch_background_task("UniFFI task", move || task.run()) {
log::error!("Failed to dispatch background task: {e}");
}
}
// This is as stub that allows us to build a library for uniffi-bindgen to use, but without
// depending on gecko. Gecko can only be built inside of `./mach build` and we want to build
// the library using plain `cargo build`.
#[cfg(not(feature = "moz_task"))]
fn add_task(&self, _task: Arc<dyn RustTask>) {
panic!("moz_task not enabled");
}
}
/// Function to test the global worker queue
///
/// This is how Rust components can wrap synchronous tasks to present an async interface.
#[uniffi::export]
pub async fn expensive_computation() -> u32 {
run_in_background(|| {
sleep(Duration::from_millis(1));
1000
}).await
}

View File

@@ -1 +0,0 @@
This directory is where files generated by Uniffi will be created.

View File

@@ -1,558 +0,0 @@
// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
// Objects intended to be used in the unit tests
export var UnitTestObjs = {};
let lazy = {};
ChromeUtils.defineLazyGetter(lazy, "decoder", () => new TextDecoder());
ChromeUtils.defineLazyGetter(lazy, "encoder", () => new TextEncoder());
// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
constructor(arrayBuffer) {
this.dataView = new DataView(arrayBuffer);
this.pos = 0;
}
readUint8() {
let rv = this.dataView.getUint8(this.pos);
this.pos += 1;
return rv;
}
writeUint8(value) {
this.dataView.setUint8(this.pos, value);
this.pos += 1;
}
readUint16() {
let rv = this.dataView.getUint16(this.pos);
this.pos += 2;
return rv;
}
writeUint16(value) {
this.dataView.setUint16(this.pos, value);
this.pos += 2;
}
readUint32() {
let rv = this.dataView.getUint32(this.pos);
this.pos += 4;
return rv;
}
writeUint32(value) {
this.dataView.setUint32(this.pos, value);
this.pos += 4;
}
readUint64() {
let rv = this.dataView.getBigUint64(this.pos);
this.pos += 8;
return Number(rv);
}
writeUint64(value) {
this.dataView.setBigUint64(this.pos, BigInt(value));
this.pos += 8;
}
readInt8() {
let rv = this.dataView.getInt8(this.pos);
this.pos += 1;
return rv;
}
writeInt8(value) {
this.dataView.setInt8(this.pos, value);
this.pos += 1;
}
readInt16() {
let rv = this.dataView.getInt16(this.pos);
this.pos += 2;
return rv;
}
writeInt16(value) {
this.dataView.setInt16(this.pos, value);
this.pos += 2;
}
readInt32() {
let rv = this.dataView.getInt32(this.pos);
this.pos += 4;
return rv;
}
writeInt32(value) {
this.dataView.setInt32(this.pos, value);
this.pos += 4;
}
readInt64() {
let rv = this.dataView.getBigInt64(this.pos);
this.pos += 8;
return Number(rv);
}
writeInt64(value) {
this.dataView.setBigInt64(this.pos, BigInt(value));
this.pos += 8;
}
readFloat32() {
let rv = this.dataView.getFloat32(this.pos);
this.pos += 4;
return rv;
}
writeFloat32(value) {
this.dataView.setFloat32(this.pos, value);
this.pos += 4;
}
readFloat64() {
let rv = this.dataView.getFloat64(this.pos);
this.pos += 8;
return rv;
}
writeFloat64(value) {
this.dataView.setFloat64(this.pos, value);
this.pos += 8;
}
writeString(value) {
// Note: in order to efficiently write this data, we first write the
// string data, reserving 4 bytes for the size.
const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
const encodeResult = lazy.encoder.encodeInto(value, dest);
if (encodeResult.read != value.length) {
throw new UniFFIError(
"writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
);
}
const size = encodeResult.written;
// Next, go back and write the size before the string data
this.dataView.setUint32(this.pos, size);
// Finally, advance our position past both the size and string data
this.pos += size + 4;
}
readString() {
const size = this.readUint32();
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
const value = lazy.decoder.decode(source);
this.pos += size;
return value;
}
readBytes() {
const size = this.readInt32();
const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
this.pos += size;
return bytes
}
writeBytes(value) {
this.writeUint32(value.length);
value.forEach((elt) => {
this.writeUint8(elt);
})
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
switch (result.code) {
case "success":
return liftCallback(result.data);
case "error":
throw liftErrCallback(result.data);
case "internal-error":
if (result.data) {
throw new UniFFIInternalError(FfiConverterString.lift(result.data));
} else {
throw new UniFFIInternalError("Unknown error");
}
default:
throw new UniFFIError(`Unexpected status code: ${result.code}`);
}
}
class UniFFIError {
constructor(message) {
this.message = message;
}
toString() {
return `UniFFIError: ${this.message}`
}
}
class UniFFIInternalError extends UniFFIError {}
// Base class for FFI converters
class FfiConverter {
// throw `UniFFITypeError` if a value to be converted has an invalid type
static checkType(value) {
if (value === undefined ) {
throw new UniFFITypeError(`undefined`);
}
if (value === null ) {
throw new UniFFITypeError(`null`);
}
}
}
// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
static lift(buf) {
return this.read(new ArrayBufferDataStream(buf));
}
static lower(value) {
const buf = new ArrayBuffer(this.computeSize(value));
const dataStream = new ArrayBufferDataStream(buf);
this.write(dataStream, value);
return buf;
}
/**
* Computes the size of the value.
*
* @param {*} _value
* @return {number}
*/
static computeSize(_value) {
throw new UniFFIInternalError("computeSize() should be declared in the derived class");
}
/**
* Reads the type from a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @returns {any}
*/
static read(_dataStream) {
throw new UniFFIInternalError("read() should be declared in the derived class");
}
/**
* Writes the type to a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @param {any} _value
*/
static write(_dataStream, _value) {
throw new UniFFIInternalError("write() should be declared in the derived class");
}
}
// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
// Export the FFIConverter object to make external types work.
export class FfiConverterU64 extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (!Number.isSafeInteger(value)) {
throw new UniFFITypeError(`${value} exceeds the safe integer bounds`);
}
if (value < 0) {
throw new UniFFITypeError(`${value} exceeds the U64 bounds`);
}
}
static computeSize(_value) {
return 8;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeUint64(value)
}
static read(dataStream) {
return dataStream.readUint64()
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterBool extends FfiConverter {
static computeSize(_value) {
return 1;
}
static lift(value) {
return value == 1;
}
static lower(value) {
if (value) {
return 1;
} else {
return 0;
}
}
static write(dataStream, value) {
dataStream.writeUint8(this.lower(value))
}
static read(dataStream) {
return this.lift(dataStream.readUint8())
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (typeof value !== "string") {
throw new UniFFITypeError(`${value} is not a string`);
}
}
static lift(buf) {
const utf8Arr = new Uint8Array(buf);
return lazy.decoder.decode(utf8Arr);
}
static lower(value) {
return lazy.encoder.encode(value).buffer;
}
static write(dataStream, value) {
dataStream.writeString(value);
}
static read(dataStream) {
return dataStream.readString();
}
static computeSize(value) {
return 4 + lazy.encoder.encode(value).length
}
}
/**
* ArithmeticError
*/
export class ArithmeticError extends Error {}
/**
* INTEGER_OVERFLOW
*/
export class IntegerOverflow extends ArithmeticError {
constructor(message, ...params) {
super(...params);
this.message = message;
}
toString() {
return `IntegerOverflow: ${super.toString()}`
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeArithmeticError extends FfiConverterArrayBuffer {
static read(dataStream) {
switch (dataStream.readInt32()) {
case 1:
return new IntegerOverflow(FfiConverterString.read(dataStream));
default:
throw new UniFFITypeError("Unknown ArithmeticError variant");
}
}
static computeSize(value) {
// Size of the Int indicating the variant
let totalSize = 4;
if (value instanceof IntegerOverflow) {
return totalSize;
}
throw new UniFFITypeError("Unknown ArithmeticError variant");
}
static write(dataStream, value) {
if (value instanceof IntegerOverflow) {
dataStream.writeInt32(1);
return;
}
throw new UniFFITypeError("Unknown ArithmeticError variant");
}
static errorClass = ArithmeticError;
}
/**
* add
* @returns {number}
*/
export function add(a,b) {
const liftResult = (result) => FfiConverterU64.lift(result);
const liftError = (data) => FfiConverterTypeArithmeticError.lift(data);
const functionCall = () => {
try {
FfiConverterU64.checkType(a)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("a");
}
throw e;
}
try {
FfiConverterU64.checkType(b)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("b");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
109, // arithmetic:uniffi_arithmetical_fn_func_add
FfiConverterU64.lower(a),
FfiConverterU64.lower(b),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* div
* @returns {number}
*/
export function div(dividend,divisor) {
const liftResult = (result) => FfiConverterU64.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterU64.checkType(dividend)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("dividend");
}
throw e;
}
try {
FfiConverterU64.checkType(divisor)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("divisor");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
110, // arithmetic:uniffi_arithmetical_fn_func_div
FfiConverterU64.lower(dividend),
FfiConverterU64.lower(divisor),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* equal
* @returns {Boolean}
*/
export function equal(a,b) {
const liftResult = (result) => FfiConverterBool.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterU64.checkType(a)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("a");
}
throw e;
}
try {
FfiConverterU64.checkType(b)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("b");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
111, // arithmetic:uniffi_arithmetical_fn_func_equal
FfiConverterU64.lower(a),
FfiConverterU64.lower(b),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* sub
* @returns {number}
*/
export function sub(a,b) {
const liftResult = (result) => FfiConverterU64.lift(result);
const liftError = (data) => FfiConverterTypeArithmeticError.lift(data);
const functionCall = () => {
try {
FfiConverterU64.checkType(a)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("a");
}
throw e;
}
try {
FfiConverterU64.checkType(b)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("b");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
112, // arithmetic:uniffi_arithmetical_fn_func_sub
FfiConverterU64.lower(a),
FfiConverterU64.lower(b),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}

View File

@@ -1,509 +0,0 @@
// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
// Objects intended to be used in the unit tests
export var UnitTestObjs = {};
let lazy = {};
ChromeUtils.defineLazyGetter(lazy, "decoder", () => new TextDecoder());
ChromeUtils.defineLazyGetter(lazy, "encoder", () => new TextEncoder());
// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
constructor(arrayBuffer) {
this.dataView = new DataView(arrayBuffer);
this.pos = 0;
}
readUint8() {
let rv = this.dataView.getUint8(this.pos);
this.pos += 1;
return rv;
}
writeUint8(value) {
this.dataView.setUint8(this.pos, value);
this.pos += 1;
}
readUint16() {
let rv = this.dataView.getUint16(this.pos);
this.pos += 2;
return rv;
}
writeUint16(value) {
this.dataView.setUint16(this.pos, value);
this.pos += 2;
}
readUint32() {
let rv = this.dataView.getUint32(this.pos);
this.pos += 4;
return rv;
}
writeUint32(value) {
this.dataView.setUint32(this.pos, value);
this.pos += 4;
}
readUint64() {
let rv = this.dataView.getBigUint64(this.pos);
this.pos += 8;
return Number(rv);
}
writeUint64(value) {
this.dataView.setBigUint64(this.pos, BigInt(value));
this.pos += 8;
}
readInt8() {
let rv = this.dataView.getInt8(this.pos);
this.pos += 1;
return rv;
}
writeInt8(value) {
this.dataView.setInt8(this.pos, value);
this.pos += 1;
}
readInt16() {
let rv = this.dataView.getInt16(this.pos);
this.pos += 2;
return rv;
}
writeInt16(value) {
this.dataView.setInt16(this.pos, value);
this.pos += 2;
}
readInt32() {
let rv = this.dataView.getInt32(this.pos);
this.pos += 4;
return rv;
}
writeInt32(value) {
this.dataView.setInt32(this.pos, value);
this.pos += 4;
}
readInt64() {
let rv = this.dataView.getBigInt64(this.pos);
this.pos += 8;
return Number(rv);
}
writeInt64(value) {
this.dataView.setBigInt64(this.pos, BigInt(value));
this.pos += 8;
}
readFloat32() {
let rv = this.dataView.getFloat32(this.pos);
this.pos += 4;
return rv;
}
writeFloat32(value) {
this.dataView.setFloat32(this.pos, value);
this.pos += 4;
}
readFloat64() {
let rv = this.dataView.getFloat64(this.pos);
this.pos += 8;
return rv;
}
writeFloat64(value) {
this.dataView.setFloat64(this.pos, value);
this.pos += 8;
}
writeString(value) {
// Note: in order to efficiently write this data, we first write the
// string data, reserving 4 bytes for the size.
const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
const encodeResult = lazy.encoder.encodeInto(value, dest);
if (encodeResult.read != value.length) {
throw new UniFFIError(
"writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
);
}
const size = encodeResult.written;
// Next, go back and write the size before the string data
this.dataView.setUint32(this.pos, size);
// Finally, advance our position past both the size and string data
this.pos += size + 4;
}
readString() {
const size = this.readUint32();
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
const value = lazy.decoder.decode(source);
this.pos += size;
return value;
}
readBytes() {
const size = this.readInt32();
const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
this.pos += size;
return bytes
}
writeBytes(value) {
this.writeUint32(value.length);
value.forEach((elt) => {
this.writeUint8(elt);
})
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
switch (result.code) {
case "success":
return liftCallback(result.data);
case "error":
throw liftErrCallback(result.data);
case "internal-error":
if (result.data) {
throw new UniFFIInternalError(FfiConverterString.lift(result.data));
} else {
throw new UniFFIInternalError("Unknown error");
}
default:
throw new UniFFIError(`Unexpected status code: ${result.code}`);
}
}
class UniFFIError {
constructor(message) {
this.message = message;
}
toString() {
return `UniFFIError: ${this.message}`
}
}
class UniFFIInternalError extends UniFFIError {}
// Base class for FFI converters
class FfiConverter {
// throw `UniFFITypeError` if a value to be converted has an invalid type
static checkType(value) {
if (value === undefined ) {
throw new UniFFITypeError(`undefined`);
}
if (value === null ) {
throw new UniFFITypeError(`null`);
}
}
}
// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
static lift(buf) {
return this.read(new ArrayBufferDataStream(buf));
}
static lower(value) {
const buf = new ArrayBuffer(this.computeSize(value));
const dataStream = new ArrayBufferDataStream(buf);
this.write(dataStream, value);
return buf;
}
/**
* Computes the size of the value.
*
* @param {*} _value
* @return {number}
*/
static computeSize(_value) {
throw new UniFFIInternalError("computeSize() should be declared in the derived class");
}
/**
* Reads the type from a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @returns {any}
*/
static read(_dataStream) {
throw new UniFFIInternalError("read() should be declared in the derived class");
}
/**
* Writes the type to a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @param {any} _value
*/
static write(_dataStream, _value) {
throw new UniFFIInternalError("write() should be declared in the derived class");
}
}
// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
// Export the FFIConverter object to make external types work.
export class FfiConverterF64 extends FfiConverter {
static computeSize(_value) {
return 8;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeFloat64(value)
}
static read(dataStream) {
return dataStream.readFloat64()
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (typeof value !== "string") {
throw new UniFFITypeError(`${value} is not a string`);
}
}
static lift(buf) {
const utf8Arr = new Uint8Array(buf);
return lazy.decoder.decode(utf8Arr);
}
static lower(value) {
return lazy.encoder.encode(value).buffer;
}
static write(dataStream, value) {
dataStream.writeString(value);
}
static read(dataStream) {
return dataStream.readString();
}
static computeSize(value) {
return 4 + lazy.encoder.encode(value).length
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalTypeLine extends FfiConverterArrayBuffer {
static checkType(value) {
if (value !== undefined && value !== null) {
FfiConverterTypeLine.checkType(value)
}
}
static read(dataStream) {
const code = dataStream.readUint8(0);
switch (code) {
case 0:
return null
case 1:
return FfiConverterTypeLine.read(dataStream)
default:
throw new UniFFIError(`Unexpected code: ${code}`);
}
}
static write(dataStream, value) {
if (value === null || value === undefined) {
dataStream.writeUint8(0);
return;
}
dataStream.writeUint8(1);
FfiConverterTypeLine.write(dataStream, value)
}
static computeSize(value) {
if (value === null || value === undefined) {
return 1;
}
return 1 + FfiConverterTypeLine.computeSize(value)
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalTypePoint extends FfiConverterArrayBuffer {
static checkType(value) {
if (value !== undefined && value !== null) {
FfiConverterTypePoint.checkType(value)
}
}
static read(dataStream) {
const code = dataStream.readUint8(0);
switch (code) {
case 0:
return null
case 1:
return FfiConverterTypePoint.read(dataStream)
default:
throw new UniFFIError(`Unexpected code: ${code}`);
}
}
static write(dataStream, value) {
if (value === null || value === undefined) {
dataStream.writeUint8(0);
return;
}
dataStream.writeUint8(1);
FfiConverterTypePoint.write(dataStream, value)
}
static computeSize(value) {
if (value === null || value === undefined) {
return 1;
}
return 1 + FfiConverterTypePoint.computeSize(value)
}
}
import {
FfiConverterTypeSprite,
Sprite,
} from "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSprites.sys.mjs";
// Export the FFIConverter object to make external types work.
export { FfiConverterTypeSprite, Sprite };import {
FfiConverterTypeLine,
Line,
} from "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustGeometry.sys.mjs";
// Export the FFIConverter object to make external types work.
export { FfiConverterTypeLine, Line };import {
FfiConverterTypePoint,
Point,
} from "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustGeometry.sys.mjs";
// Export the FFIConverter object to make external types work.
export { FfiConverterTypePoint, Point };
/**
* gradient
* @returns {number}
*/
export function gradient(value) {
const liftResult = (result) => FfiConverterF64.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterOptionalTypeLine.checkType(value)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("value");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
122, // external_types:uniffi_uniffi_fixture_external_types_fn_func_gradient
FfiConverterOptionalTypeLine.lower(value),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* intersection
* @returns {?Point}
*/
export function intersection(ln1,ln2) {
const liftResult = (result) => FfiConverterOptionalTypePoint.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeLine.checkType(ln1)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("ln1");
}
throw e;
}
try {
FfiConverterTypeLine.checkType(ln2)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("ln2");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
123, // external_types:uniffi_uniffi_fixture_external_types_fn_func_intersection
FfiConverterTypeLine.lower(ln1),
FfiConverterTypeLine.lower(ln2),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* moveSpriteToOrigin
*/
export function moveSpriteToOrigin(sprite) {
const liftResult = (result) => undefined;
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeSprite.checkType(sprite)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("sprite");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
124, // external_types:uniffi_uniffi_fixture_external_types_fn_func_move_sprite_to_origin
FfiConverterTypeSprite.lower(sprite),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}

View File

@@ -1,822 +0,0 @@
// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
// Objects intended to be used in the unit tests
export var UnitTestObjs = {};
let lazy = {};
ChromeUtils.defineLazyGetter(lazy, "decoder", () => new TextDecoder());
ChromeUtils.defineLazyGetter(lazy, "encoder", () => new TextEncoder());
// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
constructor(arrayBuffer) {
this.dataView = new DataView(arrayBuffer);
this.pos = 0;
}
readUint8() {
let rv = this.dataView.getUint8(this.pos);
this.pos += 1;
return rv;
}
writeUint8(value) {
this.dataView.setUint8(this.pos, value);
this.pos += 1;
}
readUint16() {
let rv = this.dataView.getUint16(this.pos);
this.pos += 2;
return rv;
}
writeUint16(value) {
this.dataView.setUint16(this.pos, value);
this.pos += 2;
}
readUint32() {
let rv = this.dataView.getUint32(this.pos);
this.pos += 4;
return rv;
}
writeUint32(value) {
this.dataView.setUint32(this.pos, value);
this.pos += 4;
}
readUint64() {
let rv = this.dataView.getBigUint64(this.pos);
this.pos += 8;
return Number(rv);
}
writeUint64(value) {
this.dataView.setBigUint64(this.pos, BigInt(value));
this.pos += 8;
}
readInt8() {
let rv = this.dataView.getInt8(this.pos);
this.pos += 1;
return rv;
}
writeInt8(value) {
this.dataView.setInt8(this.pos, value);
this.pos += 1;
}
readInt16() {
let rv = this.dataView.getInt16(this.pos);
this.pos += 2;
return rv;
}
writeInt16(value) {
this.dataView.setInt16(this.pos, value);
this.pos += 2;
}
readInt32() {
let rv = this.dataView.getInt32(this.pos);
this.pos += 4;
return rv;
}
writeInt32(value) {
this.dataView.setInt32(this.pos, value);
this.pos += 4;
}
readInt64() {
let rv = this.dataView.getBigInt64(this.pos);
this.pos += 8;
return Number(rv);
}
writeInt64(value) {
this.dataView.setBigInt64(this.pos, BigInt(value));
this.pos += 8;
}
readFloat32() {
let rv = this.dataView.getFloat32(this.pos);
this.pos += 4;
return rv;
}
writeFloat32(value) {
this.dataView.setFloat32(this.pos, value);
this.pos += 4;
}
readFloat64() {
let rv = this.dataView.getFloat64(this.pos);
this.pos += 8;
return rv;
}
writeFloat64(value) {
this.dataView.setFloat64(this.pos, value);
this.pos += 8;
}
writeString(value) {
// Note: in order to efficiently write this data, we first write the
// string data, reserving 4 bytes for the size.
const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
const encodeResult = lazy.encoder.encodeInto(value, dest);
if (encodeResult.read != value.length) {
throw new UniFFIError(
"writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
);
}
const size = encodeResult.written;
// Next, go back and write the size before the string data
this.dataView.setUint32(this.pos, size);
// Finally, advance our position past both the size and string data
this.pos += size + 4;
}
readString() {
const size = this.readUint32();
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
const value = lazy.decoder.decode(source);
this.pos += size;
return value;
}
readBytes() {
const size = this.readInt32();
const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
this.pos += size;
return bytes
}
writeBytes(value) {
this.writeUint32(value.length);
value.forEach((elt) => {
this.writeUint8(elt);
})
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
switch (result.code) {
case "success":
return liftCallback(result.data);
case "error":
throw liftErrCallback(result.data);
case "internal-error":
if (result.data) {
throw new UniFFIInternalError(FfiConverterString.lift(result.data));
} else {
throw new UniFFIInternalError("Unknown error");
}
default:
throw new UniFFIError(`Unexpected status code: ${result.code}`);
}
}
class UniFFIError {
constructor(message) {
this.message = message;
}
toString() {
return `UniFFIError: ${this.message}`
}
}
class UniFFIInternalError extends UniFFIError {}
// Base class for FFI converters
class FfiConverter {
// throw `UniFFITypeError` if a value to be converted has an invalid type
static checkType(value) {
if (value === undefined ) {
throw new UniFFITypeError(`undefined`);
}
if (value === null ) {
throw new UniFFITypeError(`null`);
}
}
}
// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
static lift(buf) {
return this.read(new ArrayBufferDataStream(buf));
}
static lower(value) {
const buf = new ArrayBuffer(this.computeSize(value));
const dataStream = new ArrayBufferDataStream(buf);
this.write(dataStream, value);
return buf;
}
/**
* Computes the size of the value.
*
* @param {*} _value
* @return {number}
*/
static computeSize(_value) {
throw new UniFFIInternalError("computeSize() should be declared in the derived class");
}
/**
* Reads the type from a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @returns {any}
*/
static read(_dataStream) {
throw new UniFFIInternalError("read() should be declared in the derived class");
}
/**
* Writes the type to a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @param {any} _value
*/
static write(_dataStream, _value) {
throw new UniFFIInternalError("write() should be declared in the derived class");
}
}
// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
/**
* Handler for a single UniFFI CallbackInterface
*
* This class stores objects that implement a callback interface in a handle
* map, allowing them to be referenced by the Rust code using an integer
* handle.
*
* While the callback object is stored in the map, it allows the Rust code to
* call methods on the object using the callback object handle, a method id,
* and an ArrayBuffer packed with the method arguments.
*
* When the Rust code drops its reference, it sends a call with the methodId=0,
* which causes callback object to be removed from the map.
*/
class UniFFICallbackHandler {
#name;
#interfaceId;
#handleCounter;
#handleMap;
#methodHandlers;
#allowNewCallbacks
/**
* Create a UniFFICallbackHandler
* @param {string} name - Human-friendly name for this callback interface
* @param {int} interfaceId - Interface ID for this CallbackInterface.
* @param {UniFFICallbackMethodHandler[]} methodHandlers -- UniFFICallbackHandler for each method, in the same order as the UDL file
*/
constructor(name, interfaceId, methodHandlers) {
this.#name = name;
this.#interfaceId = interfaceId;
this.#handleCounter = 0;
this.#handleMap = new Map();
this.#methodHandlers = methodHandlers;
this.#allowNewCallbacks = true;
UniFFIScaffolding.registerCallbackHandler(this.#interfaceId, this);
Services.obs.addObserver(this, "xpcom-shutdown");
}
/**
* Store a callback object in the handle map and return the handle
*
* @param {obj} callbackObj - Object that implements the callback interface
* @returns {int} - Handle for this callback object, this is what gets passed back to Rust.
*/
storeCallbackObj(callbackObj) {
if (!this.#allowNewCallbacks) {
throw new UniFFIError(`No new callbacks allowed for ${this.#name}`);
}
const handle = this.#handleCounter;
this.#handleCounter += 1;
this.#handleMap.set(handle, new UniFFICallbackHandleMapEntry(callbackObj, Components.stack.caller.formattedStack.trim()));
return handle;
}
/**
* Get a previously stored callback object
*
* @param {int} handle - Callback object handle, returned from `storeCallbackObj()`
* @returns {obj} - Callback object
*/
getCallbackObj(handle) {
return this.#handleMap.get(handle).callbackObj;
}
/**
* Set if new callbacks are allowed for this handler
*
* This is called with false during shutdown to ensure the callback maps don't
* prevent JS objects from being GCed.
*/
setAllowNewCallbacks(allow) {
this.#allowNewCallbacks = allow
}
/**
* Check that no callbacks are currently registered
*
* If there are callbacks registered a UniFFIError will be thrown. This is
* called during shutdown to generate an alert if there are leaked callback
* interfaces.
*/
assertNoRegisteredCallbacks() {
if (this.#handleMap.size > 0) {
const entry = this.#handleMap.values().next().value;
throw new UniFFIError(`UniFFI interface ${this.#name} has ${this.#handleMap.size} registered callbacks at xpcom-shutdown. This likely indicates a UniFFI callback leak.\nStack trace for the first leaked callback:\n${entry.stackTrace}.`);
}
}
/**
* Invoke a method on a stored callback object
* @param {int} handle - Object handle
* @param {int} methodId - Method index (0-based)
* @param {UniFFIScaffoldingValue[]} args - Arguments to pass to the method
*/
call(handle, methodId, ...args) {
try {
this.#invokeCallbackInner(handle, methodId, args);
} catch (e) {
console.error(`internal error invoking callback: ${e}`)
}
}
/**
* Destroy a stored callback object
* @param {int} handle - Object handle
*/
destroy(handle) {
this.#handleMap.delete(handle);
}
#invokeCallbackInner(handle, methodId, args) {
const callbackObj = this.getCallbackObj(handle);
if (callbackObj === undefined) {
throw new UniFFIError(`${this.#name}: invalid callback handle id: ${handle}`);
}
// Get the method data, converting from 1-based indexing
const methodHandler = this.#methodHandlers[methodId];
if (methodHandler === undefined) {
throw new UniFFIError(`${this.#name}: invalid method id: ${methodId}`)
}
methodHandler.call(callbackObj, args);
}
/**
* xpcom-shutdown observer method
*
* This handles:
* - Deregistering ourselves as the UniFFI callback handler
* - Checks for any leftover stored callbacks which indicate memory leaks
*/
observe(aSubject, aTopic, aData) {
if (aTopic == "xpcom-shutdown") {
try {
this.setAllowNewCallbacks(false);
this.assertNoRegisteredCallbacks();
UniFFIScaffolding.deregisterCallbackHandler(this.#interfaceId);
} catch (ex) {
console.error(`UniFFI Callback interface error during xpcom-shutdown: ${ex}`);
Cc["@mozilla.org/xpcom/debug;1"]
.getService(Ci.nsIDebug2)
.abort(ex.filename, ex.lineNumber);
}
}
}
}
/**
* Handles calling a single method for a callback interface
*/
class UniFFICallbackMethodHandler {
#name;
#argsConverters;
/**
* Create a UniFFICallbackMethodHandler
* @param {string} name -- Name of the method to call on the callback object
* @param {FfiConverter[]} argsConverters - FfiConverter for each argument type
*/
constructor(name, argsConverters) {
this.#name = name;
this.#argsConverters = argsConverters;
}
/**
* Invoke the method
*
* @param {obj} callbackObj -- Object implementing the callback interface for this method
* @param {ArrayBuffer} argsArrayBuffer -- Arguments for the method, packed in an ArrayBuffer
*/
call(callbackObj, args) {
const convertedArgs = this.#argsConverters.map((converter, i) => converter.lift(args[i]));
return callbackObj[this.#name](...convertedArgs);
}
}
/**
* UniFFICallbackHandler.handleMap entry
*
* @property callbackObj - Callback object, this must implement the callback interface.
* @property {string} stackTrace - Stack trace from when the callback object was registered. This is used to proved extra context when debugging leaked callback objects.
*/
class UniFFICallbackHandleMapEntry {
constructor(callbackObj, stackTrace) {
this.callbackObj = callbackObj;
this.stackTrace = stackTrace
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterU32 extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (!Number.isInteger(value)) {
throw new UniFFITypeError(`${value} is not an integer`);
}
if (value < 0 || value > 4294967295) {
throw new UniFFITypeError(`${value} exceeds the U32 bounds`);
}
}
static computeSize(_value) {
return 4;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeUint32(value)
}
static read(dataStream) {
return dataStream.readUint32()
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterI32 extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (!Number.isInteger(value)) {
throw new UniFFITypeError(`${value} is not an integer`);
}
if (value < -2147483648 || value > 2147483647) {
throw new UniFFITypeError(`${value} exceeds the I32 bounds`);
}
}
static computeSize(_value) {
return 4;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeInt32(value)
}
static read(dataStream) {
return dataStream.readInt32()
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (typeof value !== "string") {
throw new UniFFITypeError(`${value} is not a string`);
}
}
static lift(buf) {
const utf8Arr = new Uint8Array(buf);
return lazy.decoder.decode(utf8Arr);
}
static lower(value) {
return lazy.encoder.encode(value).buffer;
}
static write(dataStream, value) {
dataStream.writeString(value);
}
static read(dataStream) {
return dataStream.readString();
}
static computeSize(value) {
return 4 + lazy.encoder.encode(value).length
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeLogger extends FfiConverter {
static lower(callbackObj) {
return callbackHandlerLogger.storeCallbackObj(callbackObj)
}
static lift(handleId) {
return callbackHandlerLogger.getCallbackObj(handleId)
}
static read(dataStream) {
return this.lift(dataStream.readInt64())
}
static write(dataStream, callbackObj) {
dataStream.writeInt64(this.lower(callbackObj))
}
static computeSize(callbackObj) {
return 8;
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterSequenceu32 extends FfiConverterArrayBuffer {
static read(dataStream) {
const len = dataStream.readInt32();
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(FfiConverterU32.read(dataStream));
}
return arr;
}
static write(dataStream, value) {
dataStream.writeInt32(value.length);
value.forEach((innerValue) => {
FfiConverterU32.write(dataStream, innerValue);
})
}
static computeSize(value) {
// The size of the length
let size = 4;
for (const innerValue of value) {
size += FfiConverterU32.computeSize(innerValue);
}
return size;
}
static checkType(value) {
if (!Array.isArray(value)) {
throw new UniFFITypeError(`${value} is not an array`);
}
value.forEach((innerValue, idx) => {
try {
FfiConverterU32.checkType(innerValue);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(`[${idx}]`);
}
throw e;
}
})
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterSequencei32 extends FfiConverterArrayBuffer {
static read(dataStream) {
const len = dataStream.readInt32();
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(FfiConverterI32.read(dataStream));
}
return arr;
}
static write(dataStream, value) {
dataStream.writeInt32(value.length);
value.forEach((innerValue) => {
FfiConverterI32.write(dataStream, innerValue);
})
}
static computeSize(value) {
// The size of the length
let size = 4;
for (const innerValue of value) {
size += FfiConverterI32.computeSize(innerValue);
}
return size;
}
static checkType(value) {
if (!Array.isArray(value)) {
throw new UniFFITypeError(`${value} is not an array`);
}
value.forEach((innerValue, idx) => {
try {
FfiConverterI32.checkType(innerValue);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(`[${idx}]`);
}
throw e;
}
})
}
}
// Define callback interface handlers, this must come after the type loop since they reference the FfiConverters defined above.
const callbackHandlerLogger = new UniFFICallbackHandler(
"fixture_callbacks:Logger",
2,
[
new UniFFICallbackMethodHandler(
"log",
[
FfiConverterString,
],
),
new UniFFICallbackMethodHandler(
"logRepeat",
[
FfiConverterString,
FfiConverterU32,
FfiConverterSequenceu32,
],
),
new UniFFICallbackMethodHandler(
"finished",
[
],
),
]
);
// Allow the shutdown-related functionality to be tested in the unit tests
UnitTestObjs.callbackHandlerLogger = callbackHandlerLogger;
/**
* callLogRepeat
*/
export function callLogRepeat(logger,message,count,exclude) {
const liftResult = (result) => undefined;
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeLogger.checkType(logger)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("logger");
}
throw e;
}
try {
FfiConverterString.checkType(message)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("message");
}
throw e;
}
try {
FfiConverterU32.checkType(count)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("count");
}
throw e;
}
try {
FfiConverterSequenceu32.checkType(exclude)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("exclude");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
125, // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_call_log_repeat
FfiConverterTypeLogger.lower(logger),
FfiConverterString.lower(message),
FfiConverterU32.lower(count),
FfiConverterSequenceu32.lower(exclude),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* logEvenNumbers
*/
export function logEvenNumbers(logger,items) {
const liftResult = (result) => undefined;
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeLogger.checkType(logger)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("logger");
}
throw e;
}
try {
FfiConverterSequencei32.checkType(items)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("items");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
126, // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers
FfiConverterTypeLogger.lower(logger),
FfiConverterSequencei32.lower(items),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* logEvenNumbersMainThread
*/
export function logEvenNumbersMainThread(logger,items) {
const liftResult = (result) => undefined;
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeLogger.checkType(logger)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("logger");
}
throw e;
}
try {
FfiConverterSequencei32.checkType(items)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("items");
}
throw e;
}
return UniFFIScaffolding.callSync(
127, // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread
FfiConverterTypeLogger.lower(logger),
FfiConverterSequencei32.lower(items),
)
}
return handleRustResult(functionCall(), liftResult, liftError);
}

View File

@@ -1,592 +0,0 @@
// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
// Objects intended to be used in the unit tests
export var UnitTestObjs = {};
let lazy = {};
ChromeUtils.defineLazyGetter(lazy, "decoder", () => new TextDecoder());
ChromeUtils.defineLazyGetter(lazy, "encoder", () => new TextEncoder());
// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
constructor(arrayBuffer) {
this.dataView = new DataView(arrayBuffer);
this.pos = 0;
}
readUint8() {
let rv = this.dataView.getUint8(this.pos);
this.pos += 1;
return rv;
}
writeUint8(value) {
this.dataView.setUint8(this.pos, value);
this.pos += 1;
}
readUint16() {
let rv = this.dataView.getUint16(this.pos);
this.pos += 2;
return rv;
}
writeUint16(value) {
this.dataView.setUint16(this.pos, value);
this.pos += 2;
}
readUint32() {
let rv = this.dataView.getUint32(this.pos);
this.pos += 4;
return rv;
}
writeUint32(value) {
this.dataView.setUint32(this.pos, value);
this.pos += 4;
}
readUint64() {
let rv = this.dataView.getBigUint64(this.pos);
this.pos += 8;
return Number(rv);
}
writeUint64(value) {
this.dataView.setBigUint64(this.pos, BigInt(value));
this.pos += 8;
}
readInt8() {
let rv = this.dataView.getInt8(this.pos);
this.pos += 1;
return rv;
}
writeInt8(value) {
this.dataView.setInt8(this.pos, value);
this.pos += 1;
}
readInt16() {
let rv = this.dataView.getInt16(this.pos);
this.pos += 2;
return rv;
}
writeInt16(value) {
this.dataView.setInt16(this.pos, value);
this.pos += 2;
}
readInt32() {
let rv = this.dataView.getInt32(this.pos);
this.pos += 4;
return rv;
}
writeInt32(value) {
this.dataView.setInt32(this.pos, value);
this.pos += 4;
}
readInt64() {
let rv = this.dataView.getBigInt64(this.pos);
this.pos += 8;
return Number(rv);
}
writeInt64(value) {
this.dataView.setBigInt64(this.pos, BigInt(value));
this.pos += 8;
}
readFloat32() {
let rv = this.dataView.getFloat32(this.pos);
this.pos += 4;
return rv;
}
writeFloat32(value) {
this.dataView.setFloat32(this.pos, value);
this.pos += 4;
}
readFloat64() {
let rv = this.dataView.getFloat64(this.pos);
this.pos += 8;
return rv;
}
writeFloat64(value) {
this.dataView.setFloat64(this.pos, value);
this.pos += 8;
}
writeString(value) {
// Note: in order to efficiently write this data, we first write the
// string data, reserving 4 bytes for the size.
const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
const encodeResult = lazy.encoder.encodeInto(value, dest);
if (encodeResult.read != value.length) {
throw new UniFFIError(
"writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
);
}
const size = encodeResult.written;
// Next, go back and write the size before the string data
this.dataView.setUint32(this.pos, size);
// Finally, advance our position past both the size and string data
this.pos += size + 4;
}
readString() {
const size = this.readUint32();
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
const value = lazy.decoder.decode(source);
this.pos += size;
return value;
}
readBytes() {
const size = this.readInt32();
const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
this.pos += size;
return bytes
}
writeBytes(value) {
this.writeUint32(value.length);
value.forEach((elt) => {
this.writeUint8(elt);
})
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
switch (result.code) {
case "success":
return liftCallback(result.data);
case "error":
throw liftErrCallback(result.data);
case "internal-error":
if (result.data) {
throw new UniFFIInternalError(FfiConverterString.lift(result.data));
} else {
throw new UniFFIInternalError("Unknown error");
}
default:
throw new UniFFIError(`Unexpected status code: ${result.code}`);
}
}
class UniFFIError {
constructor(message) {
this.message = message;
}
toString() {
return `UniFFIError: ${this.message}`
}
}
class UniFFIInternalError extends UniFFIError {}
// Base class for FFI converters
class FfiConverter {
// throw `UniFFITypeError` if a value to be converted has an invalid type
static checkType(value) {
if (value === undefined ) {
throw new UniFFITypeError(`undefined`);
}
if (value === null ) {
throw new UniFFITypeError(`null`);
}
}
}
// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
static lift(buf) {
return this.read(new ArrayBufferDataStream(buf));
}
static lower(value) {
const buf = new ArrayBuffer(this.computeSize(value));
const dataStream = new ArrayBufferDataStream(buf);
this.write(dataStream, value);
return buf;
}
/**
* Computes the size of the value.
*
* @param {*} _value
* @return {number}
*/
static computeSize(_value) {
throw new UniFFIInternalError("computeSize() should be declared in the derived class");
}
/**
* Reads the type from a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @returns {any}
*/
static read(_dataStream) {
throw new UniFFIInternalError("read() should be declared in the derived class");
}
/**
* Writes the type to a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @param {any} _value
*/
static write(_dataStream, _value) {
throw new UniFFIInternalError("write() should be declared in the derived class");
}
}
// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
// Export the FFIConverter object to make external types work.
export class FfiConverterF64 extends FfiConverter {
static computeSize(_value) {
return 8;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeFloat64(value)
}
static read(dataStream) {
return dataStream.readFloat64()
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (typeof value !== "string") {
throw new UniFFITypeError(`${value} is not a string`);
}
}
static lift(buf) {
const utf8Arr = new Uint8Array(buf);
return lazy.decoder.decode(utf8Arr);
}
static lower(value) {
return lazy.encoder.encode(value).buffer;
}
static write(dataStream, value) {
dataStream.writeString(value);
}
static read(dataStream) {
return dataStream.readString();
}
static computeSize(value) {
return 4 + lazy.encoder.encode(value).length
}
}
/**
* Line
*/
export class Line {
constructor({ start, end } = { start: undefined, end: undefined }) {
try {
FfiConverterTypePoint.checkType(start)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("start");
}
throw e;
}
try {
FfiConverterTypePoint.checkType(end)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("end");
}
throw e;
}
/**
* @type {Point}
*/
this.start = start;
/**
* @type {Point}
*/
this.end = end;
}
equals(other) {
return (
this.start.equals(other.start) &&
this.end.equals(other.end)
)
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeLine extends FfiConverterArrayBuffer {
static read(dataStream) {
return new Line({
start: FfiConverterTypePoint.read(dataStream),
end: FfiConverterTypePoint.read(dataStream),
});
}
static write(dataStream, value) {
FfiConverterTypePoint.write(dataStream, value.start);
FfiConverterTypePoint.write(dataStream, value.end);
}
static computeSize(value) {
let totalSize = 0;
totalSize += FfiConverterTypePoint.computeSize(value.start);
totalSize += FfiConverterTypePoint.computeSize(value.end);
return totalSize
}
static checkType(value) {
super.checkType(value);
if (!(value instanceof Line)) {
throw new UniFFITypeError(`Expected 'Line', found '${typeof value}'`);
}
try {
FfiConverterTypePoint.checkType(value.start);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(".start");
}
throw e;
}
try {
FfiConverterTypePoint.checkType(value.end);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(".end");
}
throw e;
}
}
}
/**
* Point
*/
export class Point {
constructor({ coordX, coordY } = { coordX: undefined, coordY: undefined }) {
try {
FfiConverterF64.checkType(coordX)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("coordX");
}
throw e;
}
try {
FfiConverterF64.checkType(coordY)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("coordY");
}
throw e;
}
/**
* @type {number}
*/
this.coordX = coordX;
/**
* @type {number}
*/
this.coordY = coordY;
}
equals(other) {
return (
this.coordX == other.coordX &&
this.coordY == other.coordY
)
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypePoint extends FfiConverterArrayBuffer {
static read(dataStream) {
return new Point({
coordX: FfiConverterF64.read(dataStream),
coordY: FfiConverterF64.read(dataStream),
});
}
static write(dataStream, value) {
FfiConverterF64.write(dataStream, value.coordX);
FfiConverterF64.write(dataStream, value.coordY);
}
static computeSize(value) {
let totalSize = 0;
totalSize += FfiConverterF64.computeSize(value.coordX);
totalSize += FfiConverterF64.computeSize(value.coordY);
return totalSize
}
static checkType(value) {
super.checkType(value);
if (!(value instanceof Point)) {
throw new UniFFITypeError(`Expected 'Point', found '${typeof value}'`);
}
try {
FfiConverterF64.checkType(value.coordX);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(".coordX");
}
throw e;
}
try {
FfiConverterF64.checkType(value.coordY);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(".coordY");
}
throw e;
}
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalTypePoint extends FfiConverterArrayBuffer {
static checkType(value) {
if (value !== undefined && value !== null) {
FfiConverterTypePoint.checkType(value)
}
}
static read(dataStream) {
const code = dataStream.readUint8(0);
switch (code) {
case 0:
return null
case 1:
return FfiConverterTypePoint.read(dataStream)
default:
throw new UniFFIError(`Unexpected code: ${code}`);
}
}
static write(dataStream, value) {
if (value === null || value === undefined) {
dataStream.writeUint8(0);
return;
}
dataStream.writeUint8(1);
FfiConverterTypePoint.write(dataStream, value)
}
static computeSize(value) {
if (value === null || value === undefined) {
return 1;
}
return 1 + FfiConverterTypePoint.computeSize(value)
}
}
/**
* gradient
* @returns {number}
*/
export function gradient(ln) {
const liftResult = (result) => FfiConverterF64.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeLine.checkType(ln)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("ln");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
153, // geometry:uniffi_uniffi_geometry_fn_func_gradient
FfiConverterTypeLine.lower(ln),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* intersection
* @returns {?Point}
*/
export function intersection(ln1,ln2) {
const liftResult = (result) => FfiConverterOptionalTypePoint.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeLine.checkType(ln1)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("ln1");
}
throw e;
}
try {
FfiConverterTypeLine.checkType(ln2)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("ln2");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
154, // geometry:uniffi_uniffi_geometry_fn_func_intersection
FfiConverterTypeLine.lower(ln1),
FfiConverterTypeLine.lower(ln2),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}

View File

@@ -1,768 +0,0 @@
// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
// Objects intended to be used in the unit tests
export var UnitTestObjs = {};
let lazy = {};
ChromeUtils.defineLazyGetter(lazy, "decoder", () => new TextDecoder());
ChromeUtils.defineLazyGetter(lazy, "encoder", () => new TextEncoder());
// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
constructor(arrayBuffer) {
this.dataView = new DataView(arrayBuffer);
this.pos = 0;
}
readUint8() {
let rv = this.dataView.getUint8(this.pos);
this.pos += 1;
return rv;
}
writeUint8(value) {
this.dataView.setUint8(this.pos, value);
this.pos += 1;
}
readUint16() {
let rv = this.dataView.getUint16(this.pos);
this.pos += 2;
return rv;
}
writeUint16(value) {
this.dataView.setUint16(this.pos, value);
this.pos += 2;
}
readUint32() {
let rv = this.dataView.getUint32(this.pos);
this.pos += 4;
return rv;
}
writeUint32(value) {
this.dataView.setUint32(this.pos, value);
this.pos += 4;
}
readUint64() {
let rv = this.dataView.getBigUint64(this.pos);
this.pos += 8;
return Number(rv);
}
writeUint64(value) {
this.dataView.setBigUint64(this.pos, BigInt(value));
this.pos += 8;
}
readInt8() {
let rv = this.dataView.getInt8(this.pos);
this.pos += 1;
return rv;
}
writeInt8(value) {
this.dataView.setInt8(this.pos, value);
this.pos += 1;
}
readInt16() {
let rv = this.dataView.getInt16(this.pos);
this.pos += 2;
return rv;
}
writeInt16(value) {
this.dataView.setInt16(this.pos, value);
this.pos += 2;
}
readInt32() {
let rv = this.dataView.getInt32(this.pos);
this.pos += 4;
return rv;
}
writeInt32(value) {
this.dataView.setInt32(this.pos, value);
this.pos += 4;
}
readInt64() {
let rv = this.dataView.getBigInt64(this.pos);
this.pos += 8;
return Number(rv);
}
writeInt64(value) {
this.dataView.setBigInt64(this.pos, BigInt(value));
this.pos += 8;
}
readFloat32() {
let rv = this.dataView.getFloat32(this.pos);
this.pos += 4;
return rv;
}
writeFloat32(value) {
this.dataView.setFloat32(this.pos, value);
this.pos += 4;
}
readFloat64() {
let rv = this.dataView.getFloat64(this.pos);
this.pos += 8;
return rv;
}
writeFloat64(value) {
this.dataView.setFloat64(this.pos, value);
this.pos += 8;
}
writeString(value) {
// Note: in order to efficiently write this data, we first write the
// string data, reserving 4 bytes for the size.
const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
const encodeResult = lazy.encoder.encodeInto(value, dest);
if (encodeResult.read != value.length) {
throw new UniFFIError(
"writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
);
}
const size = encodeResult.written;
// Next, go back and write the size before the string data
this.dataView.setUint32(this.pos, size);
// Finally, advance our position past both the size and string data
this.pos += size + 4;
}
readString() {
const size = this.readUint32();
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
const value = lazy.decoder.decode(source);
this.pos += size;
return value;
}
readBytes() {
const size = this.readInt32();
const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
this.pos += size;
return bytes
}
writeBytes(value) {
this.writeUint32(value.length);
value.forEach((elt) => {
this.writeUint8(elt);
})
}
// Reads a Sprite pointer from the data stream
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerSprite() {
const pointerId = 21; // sprites:Sprite
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
}
// Writes a Sprite pointer into the data stream
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerSprite(value) {
const pointerId = 21; // sprites:Sprite
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
switch (result.code) {
case "success":
return liftCallback(result.data);
case "error":
throw liftErrCallback(result.data);
case "internal-error":
if (result.data) {
throw new UniFFIInternalError(FfiConverterString.lift(result.data));
} else {
throw new UniFFIInternalError("Unknown error");
}
default:
throw new UniFFIError(`Unexpected status code: ${result.code}`);
}
}
class UniFFIError {
constructor(message) {
this.message = message;
}
toString() {
return `UniFFIError: ${this.message}`
}
}
class UniFFIInternalError extends UniFFIError {}
// Base class for FFI converters
class FfiConverter {
// throw `UniFFITypeError` if a value to be converted has an invalid type
static checkType(value) {
if (value === undefined ) {
throw new UniFFITypeError(`undefined`);
}
if (value === null ) {
throw new UniFFITypeError(`null`);
}
}
}
// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
static lift(buf) {
return this.read(new ArrayBufferDataStream(buf));
}
static lower(value) {
const buf = new ArrayBuffer(this.computeSize(value));
const dataStream = new ArrayBufferDataStream(buf);
this.write(dataStream, value);
return buf;
}
/**
* Computes the size of the value.
*
* @param {*} _value
* @return {number}
*/
static computeSize(_value) {
throw new UniFFIInternalError("computeSize() should be declared in the derived class");
}
/**
* Reads the type from a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @returns {any}
*/
static read(_dataStream) {
throw new UniFFIInternalError("read() should be declared in the derived class");
}
/**
* Writes the type to a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @param {any} _value
*/
static write(_dataStream, _value) {
throw new UniFFIInternalError("write() should be declared in the derived class");
}
}
// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
// Export the FFIConverter object to make external types work.
export class FfiConverterF64 extends FfiConverter {
static computeSize(_value) {
return 8;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeFloat64(value)
}
static read(dataStream) {
return dataStream.readFloat64()
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (typeof value !== "string") {
throw new UniFFITypeError(`${value} is not a string`);
}
}
static lift(buf) {
const utf8Arr = new Uint8Array(buf);
return lazy.decoder.decode(utf8Arr);
}
static lower(value) {
return lazy.encoder.encode(value).buffer;
}
static write(dataStream, value) {
dataStream.writeString(value);
}
static read(dataStream) {
return dataStream.readString();
}
static computeSize(value) {
return 4 + lazy.encoder.encode(value).length
}
}
/**
* Sprite
*/
export class Sprite {
// Use `init` to instantiate this class.
// DO NOT USE THIS CONSTRUCTOR DIRECTLY
constructor(opts) {
if (!Object.prototype.hasOwnProperty.call(opts, constructUniffiObject)) {
throw new UniFFIError("Attempting to construct an object using the JavaScript constructor directly" +
"Please use a UDL defined constructor, or the init function for the primary constructor")
}
if (!(opts[constructUniffiObject] instanceof UniFFIPointer)) {
throw new UniFFIError("Attempting to create a UniFFI object with a pointer that is not an instance of UniFFIPointer")
}
this[uniffiObjectPtr] = opts[constructUniffiObject];
}
/**
* init
* @returns {Sprite}
*/
static init(initialPosition) {
const liftResult = (result) => FfiConverterTypeSprite.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterOptionalTypePoint.checkType(initialPosition)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("initialPosition");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
222, // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new
FfiConverterOptionalTypePoint.lower(initialPosition),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}}
/**
* newRelativeTo
* @returns {Sprite}
*/
static newRelativeTo(reference,direction) {
const liftResult = (result) => FfiConverterTypeSprite.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypePoint.checkType(reference)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("reference");
}
throw e;
}
try {
FfiConverterTypeVector.checkType(direction)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("direction");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
223, // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to
FfiConverterTypePoint.lower(reference),
FfiConverterTypeVector.lower(direction),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}}
/**
* getPosition
* @returns {Point}
*/
getPosition() {
const liftResult = (result) => FfiConverterTypePoint.lift(result);
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsyncWrapper(
219, // sprites:uniffi_uniffi_sprites_fn_method_sprite_get_position
FfiConverterTypeSprite.lower(this),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* moveBy
*/
moveBy(direction) {
const liftResult = (result) => undefined;
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypeVector.checkType(direction)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("direction");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
220, // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_by
FfiConverterTypeSprite.lower(this),
FfiConverterTypeVector.lower(direction),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
/**
* moveTo
*/
moveTo(position) {
const liftResult = (result) => undefined;
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypePoint.checkType(position)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("position");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
221, // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_to
FfiConverterTypeSprite.lower(this),
FfiConverterTypePoint.lower(position),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeSprite extends FfiConverter {
static lift(value) {
const opts = {};
opts[constructUniffiObject] = value;
return new Sprite(opts);
}
static lower(value) {
const ptr = value[uniffiObjectPtr];
if (!(ptr instanceof UniFFIPointer)) {
throw new UniFFITypeError("Object is not a 'Sprite' instance");
}
return ptr;
}
static read(dataStream) {
return this.lift(dataStream.readPointerSprite());
}
static write(dataStream, value) {
dataStream.writePointerSprite(value[uniffiObjectPtr]);
}
static computeSize(value) {
return 8;
}
}
/**
* Point
*/
export class Point {
constructor({ x, y } = { x: undefined, y: undefined }) {
try {
FfiConverterF64.checkType(x)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("x");
}
throw e;
}
try {
FfiConverterF64.checkType(y)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("y");
}
throw e;
}
/**
* @type {number}
*/
this.x = x;
/**
* @type {number}
*/
this.y = y;
}
equals(other) {
return (
this.x == other.x &&
this.y == other.y
)
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypePoint extends FfiConverterArrayBuffer {
static read(dataStream) {
return new Point({
x: FfiConverterF64.read(dataStream),
y: FfiConverterF64.read(dataStream),
});
}
static write(dataStream, value) {
FfiConverterF64.write(dataStream, value.x);
FfiConverterF64.write(dataStream, value.y);
}
static computeSize(value) {
let totalSize = 0;
totalSize += FfiConverterF64.computeSize(value.x);
totalSize += FfiConverterF64.computeSize(value.y);
return totalSize
}
static checkType(value) {
super.checkType(value);
if (!(value instanceof Point)) {
throw new UniFFITypeError(`Expected 'Point', found '${typeof value}'`);
}
try {
FfiConverterF64.checkType(value.x);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(".x");
}
throw e;
}
try {
FfiConverterF64.checkType(value.y);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(".y");
}
throw e;
}
}
}
/**
* Vector
*/
export class Vector {
constructor({ dx, dy } = { dx: undefined, dy: undefined }) {
try {
FfiConverterF64.checkType(dx)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("dx");
}
throw e;
}
try {
FfiConverterF64.checkType(dy)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("dy");
}
throw e;
}
/**
* @type {number}
*/
this.dx = dx;
/**
* @type {number}
*/
this.dy = dy;
}
equals(other) {
return (
this.dx == other.dx &&
this.dy == other.dy
)
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeVector extends FfiConverterArrayBuffer {
static read(dataStream) {
return new Vector({
dx: FfiConverterF64.read(dataStream),
dy: FfiConverterF64.read(dataStream),
});
}
static write(dataStream, value) {
FfiConverterF64.write(dataStream, value.dx);
FfiConverterF64.write(dataStream, value.dy);
}
static computeSize(value) {
let totalSize = 0;
totalSize += FfiConverterF64.computeSize(value.dx);
totalSize += FfiConverterF64.computeSize(value.dy);
return totalSize
}
static checkType(value) {
super.checkType(value);
if (!(value instanceof Vector)) {
throw new UniFFITypeError(`Expected 'Vector', found '${typeof value}'`);
}
try {
FfiConverterF64.checkType(value.dx);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(".dx");
}
throw e;
}
try {
FfiConverterF64.checkType(value.dy);
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart(".dy");
}
throw e;
}
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalTypePoint extends FfiConverterArrayBuffer {
static checkType(value) {
if (value !== undefined && value !== null) {
FfiConverterTypePoint.checkType(value)
}
}
static read(dataStream) {
const code = dataStream.readUint8(0);
switch (code) {
case 0:
return null
case 1:
return FfiConverterTypePoint.read(dataStream)
default:
throw new UniFFIError(`Unexpected code: ${code}`);
}
}
static write(dataStream, value) {
if (value === null || value === undefined) {
dataStream.writeUint8(0);
return;
}
dataStream.writeUint8(1);
FfiConverterTypePoint.write(dataStream, value)
}
static computeSize(value) {
if (value === null || value === undefined) {
return 1;
}
return 1 + FfiConverterTypePoint.computeSize(value)
}
}
/**
* translate
* @returns {Point}
*/
export function translate(p,v) {
const liftResult = (result) => FfiConverterTypePoint.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterTypePoint.checkType(p)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("p");
}
throw e;
}
try {
FfiConverterTypeVector.checkType(v)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("v");
}
throw e;
}
return UniFFIScaffolding.callAsyncWrapper(
218, // sprites:uniffi_uniffi_sprites_fn_func_translate
FfiConverterTypePoint.lower(p),
FfiConverterTypeVector.lower(v),
)
}
try {
return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
} catch (error) {
return Promise.reject(error)
}
}

View File

@@ -1,465 +0,0 @@
// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
// Objects intended to be used in the unit tests
export var UnitTestObjs = {};
let lazy = {};
ChromeUtils.defineLazyGetter(lazy, "decoder", () => new TextDecoder());
ChromeUtils.defineLazyGetter(lazy, "encoder", () => new TextEncoder());
// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
constructor(arrayBuffer) {
this.dataView = new DataView(arrayBuffer);
this.pos = 0;
}
readUint8() {
let rv = this.dataView.getUint8(this.pos);
this.pos += 1;
return rv;
}
writeUint8(value) {
this.dataView.setUint8(this.pos, value);
this.pos += 1;
}
readUint16() {
let rv = this.dataView.getUint16(this.pos);
this.pos += 2;
return rv;
}
writeUint16(value) {
this.dataView.setUint16(this.pos, value);
this.pos += 2;
}
readUint32() {
let rv = this.dataView.getUint32(this.pos);
this.pos += 4;
return rv;
}
writeUint32(value) {
this.dataView.setUint32(this.pos, value);
this.pos += 4;
}
readUint64() {
let rv = this.dataView.getBigUint64(this.pos);
this.pos += 8;
return Number(rv);
}
writeUint64(value) {
this.dataView.setBigUint64(this.pos, BigInt(value));
this.pos += 8;
}
readInt8() {
let rv = this.dataView.getInt8(this.pos);
this.pos += 1;
return rv;
}
writeInt8(value) {
this.dataView.setInt8(this.pos, value);
this.pos += 1;
}
readInt16() {
let rv = this.dataView.getInt16(this.pos);
this.pos += 2;
return rv;
}
writeInt16(value) {
this.dataView.setInt16(this.pos, value);
this.pos += 2;
}
readInt32() {
let rv = this.dataView.getInt32(this.pos);
this.pos += 4;
return rv;
}
writeInt32(value) {
this.dataView.setInt32(this.pos, value);
this.pos += 4;
}
readInt64() {
let rv = this.dataView.getBigInt64(this.pos);
this.pos += 8;
return Number(rv);
}
writeInt64(value) {
this.dataView.setBigInt64(this.pos, BigInt(value));
this.pos += 8;
}
readFloat32() {
let rv = this.dataView.getFloat32(this.pos);
this.pos += 4;
return rv;
}
writeFloat32(value) {
this.dataView.setFloat32(this.pos, value);
this.pos += 4;
}
readFloat64() {
let rv = this.dataView.getFloat64(this.pos);
this.pos += 8;
return rv;
}
writeFloat64(value) {
this.dataView.setFloat64(this.pos, value);
this.pos += 8;
}
writeString(value) {
// Note: in order to efficiently write this data, we first write the
// string data, reserving 4 bytes for the size.
const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
const encodeResult = lazy.encoder.encodeInto(value, dest);
if (encodeResult.read != value.length) {
throw new UniFFIError(
"writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
);
}
const size = encodeResult.written;
// Next, go back and write the size before the string data
this.dataView.setUint32(this.pos, size);
// Finally, advance our position past both the size and string data
this.pos += size + 4;
}
readString() {
const size = this.readUint32();
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
const value = lazy.decoder.decode(source);
this.pos += size;
return value;
}
readBytes() {
const size = this.readInt32();
const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
this.pos += size;
return bytes
}
writeBytes(value) {
this.writeUint32(value.length);
value.forEach((elt) => {
this.writeUint8(elt);
})
}
// Reads a Calc pointer from the data stream
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerCalc() {
const pointerId = 23; // uniffi_trait_interfaces:Calc
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
}
// Writes a Calc pointer into the data stream
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerCalc(value) {
const pointerId = 23; // uniffi_trait_interfaces:Calc
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
}
function handleRustResult(result, liftCallback, liftErrCallback) {
switch (result.code) {
case "success":
return liftCallback(result.data);
case "error":
throw liftErrCallback(result.data);
case "internal-error":
if (result.data) {
throw new UniFFIInternalError(FfiConverterString.lift(result.data));
} else {
throw new UniFFIInternalError("Unknown error");
}
default:
throw new UniFFIError(`Unexpected status code: ${result.code}`);
}
}
class UniFFIError {
constructor(message) {
this.message = message;
}
toString() {
return `UniFFIError: ${this.message}`
}
}
class UniFFIInternalError extends UniFFIError {}
// Base class for FFI converters
class FfiConverter {
// throw `UniFFITypeError` if a value to be converted has an invalid type
static checkType(value) {
if (value === undefined ) {
throw new UniFFITypeError(`undefined`);
}
if (value === null ) {
throw new UniFFITypeError(`null`);
}
}
}
// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
static lift(buf) {
return this.read(new ArrayBufferDataStream(buf));
}
static lower(value) {
const buf = new ArrayBuffer(this.computeSize(value));
const dataStream = new ArrayBufferDataStream(buf);
this.write(dataStream, value);
return buf;
}
/**
* Computes the size of the value.
*
* @param {*} _value
* @return {number}
*/
static computeSize(_value) {
throw new UniFFIInternalError("computeSize() should be declared in the derived class");
}
/**
* Reads the type from a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @returns {any}
*/
static read(_dataStream) {
throw new UniFFIInternalError("read() should be declared in the derived class");
}
/**
* Writes the type to a data stream.
*
* @param {ArrayBufferDataStream} _dataStream
* @param {any} _value
*/
static write(_dataStream, _value) {
throw new UniFFIInternalError("write() should be declared in the derived class");
}
}
// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
// Export the FFIConverter object to make external types work.
export class FfiConverterU32 extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (!Number.isInteger(value)) {
throw new UniFFITypeError(`${value} is not an integer`);
}
if (value < 0 || value > 4294967295) {
throw new UniFFITypeError(`${value} exceeds the U32 bounds`);
}
}
static computeSize(_value) {
return 4;
}
static lift(value) {
return value;
}
static lower(value) {
return value;
}
static write(dataStream, value) {
dataStream.writeUint32(value)
}
static read(dataStream) {
return dataStream.readUint32()
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
static checkType(value) {
super.checkType(value);
if (typeof value !== "string") {
throw new UniFFITypeError(`${value} is not a string`);
}
}
static lift(buf) {
const utf8Arr = new Uint8Array(buf);
return lazy.decoder.decode(utf8Arr);
}
static lower(value) {
return lazy.encoder.encode(value).buffer;
}
static write(dataStream, value) {
dataStream.writeString(value);
}
static read(dataStream) {
return dataStream.readString();
}
static computeSize(value) {
return 4 + lazy.encoder.encode(value).length
}
}
/**
* Calc
*/
export class Calc {
// Use `init` to instantiate this class.
// DO NOT USE THIS CONSTRUCTOR DIRECTLY
constructor(opts) {
if (!Object.prototype.hasOwnProperty.call(opts, constructUniffiObject)) {
throw new UniFFIError("Attempting to construct an object using the JavaScript constructor directly" +
"Please use a UDL defined constructor, or the init function for the primary constructor")
}
if (!(opts[constructUniffiObject] instanceof UniFFIPointer)) {
throw new UniFFIError("Attempting to create a UniFFI object with a pointer that is not an instance of UniFFIPointer")
}
this[uniffiObjectPtr] = opts[constructUniffiObject];
}
/**
* add
* @returns {number}
*/
add(a,b) {
const liftResult = (result) => FfiConverterU32.lift(result);
const liftError = null;
const functionCall = () => {
try {
FfiConverterU32.checkType(a)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("a");
}
throw e;
}
try {
FfiConverterU32.checkType(b)
} catch (e) {
if (e instanceof UniFFITypeError) {
e.addItemDescriptionPart("b");
}
throw e;
}
return UniFFIScaffolding.callSync(
241, // uniffi_trait_interfaces:uniffi_uniffi_trait_interfaces_fn_method_calc_add
FfiConverterTypeCalc.lower(this),
FfiConverterU32.lower(a),
FfiConverterU32.lower(b),
)
}
return handleRustResult(functionCall(), liftResult, liftError);
}
}
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeCalc extends FfiConverter {
static lift(value) {
const opts = {};
opts[constructUniffiObject] = value;
return new Calc(opts);
}
static lower(value) {
const ptr = value[uniffiObjectPtr];
if (!(ptr instanceof UniFFIPointer)) {
throw new UniFFITypeError("Object is not a 'Calc' instance");
}
return ptr;
}
static read(dataStream) {
return this.lift(dataStream.readPointerCalc());
}
static write(dataStream, value) {
dataStream.writePointerCalc(value[uniffiObjectPtr]);
}
static computeSize(value) {
return 8;
}
}
/**
* makeBuggyCalculator
* @returns {Calc}
*/
export function makeBuggyCalculator() {
const liftResult = (result) => FfiConverterTypeCalc.lift(result);
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callSync(
239, // uniffi_trait_interfaces:uniffi_uniffi_trait_interfaces_fn_func_make_buggy_calculator
)
}
return handleRustResult(functionCall(), liftResult, liftError);
}
/**
* makeCalculator
* @returns {Calc}
*/
export function makeCalculator() {
const liftResult = (result) => FfiConverterTypeCalc.lift(result);
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callSync(
240, // uniffi_trait_interfaces:uniffi_uniffi_trait_interfaces_fn_func_make_calculator
)
}
return handleRustResult(functionCall(), liftResult, liftError);
}

View File

@@ -1,20 +0,0 @@
[package]
name = "uniffi-example-geometry"
edition = "2021"
version = "0.22.0"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
license = "MPL-2.0"
publish = false
[lib]
crate-type = ["lib", "cdylib"]
name = "uniffi_geometry"
[dependencies]
uniffi = { workspace = true }
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }
[dev-dependencies]
uniffi = { workspace = true, features = ["bindgen-tests"] }

View File

@@ -1,49 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
// Silence, clippy!
const EPSILON: f64 = 0.0001f64;
#[derive(Debug, Clone, uniffi::Record)]
pub struct Point {
coord_x: f64,
coord_y: f64,
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct Line {
start: Point,
end: Point,
}
#[uniffi::export]
pub fn gradient(ln: Line) -> f64 {
let rise = ln.end.coord_y - ln.start.coord_y;
let run = ln.end.coord_x - ln.start.coord_x;
rise / run
}
#[uniffi::export]
pub fn intersection(ln1: Line, ln2: Line) -> Option<Point> {
// TODO: yuck, should be able to take &Line as argument here
// and have rust figure it out with a bunch of annotations...
let g1 = gradient(ln1.clone());
let z1 = ln1.start.coord_y - g1 * ln1.start.coord_x;
let g2 = gradient(ln2.clone());
let z2 = ln2.start.coord_y - g2 * ln2.start.coord_x;
// Parallel lines do not intersect.
if (g1 - g2).abs() < EPSILON {
return None;
}
// Otherwise, they intersect at this fancy calculation that
// I found on wikipedia.
let x = (z2 - z1) / (g1 - g2);
Some(Point {
coord_x: x,
coord_y: g1 * x + z1,
})
}
uniffi::setup_scaffolding!("geometry");

View File

@@ -1,10 +0,0 @@
import uniffi.geometry.*;
val ln1 = Line(Point(0.0,0.0), Point(1.0,2.0))
val ln2 = Line(Point(1.0,1.0), Point(2.0,2.0))
assert( gradient(ln1) == 2.0 )
assert( gradient(ln2) == 1.0 )
assert( intersection(ln1, ln2) == Point(0.0, 0.0) )
assert( intersection(ln1, ln1) == null )

View File

@@ -1,10 +0,0 @@
from geometry import Line, Point, gradient, intersection
ln1 = Line(start=Point(coord_x=0, coord_y=0), end=Point(coord_x=1, coord_y=2))
ln2 = Line(start=Point(coord_x=1, coord_y=1), end=Point(coord_x=2, coord_y=2))
assert gradient(ln1) == 2
assert gradient(ln2) == 1
assert intersection(ln1, ln2) == Point(coord_x=0, coord_y=0)
assert intersection(ln1, ln1) is None

View File

@@ -1,16 +0,0 @@
# frozen_string_literal: true
require 'test/unit'
require 'geometry'
include Test::Unit::Assertions
include Geometry
ln1 = Line.new(start: Point.new(coord_x: 0.0, coord_y: 0.0), _end: Point.new(coord_x: 1.0, coord_y: 2.0))
ln2 = Line.new(start: Point.new(coord_x: 1.0, coord_y: 1.0), _end: Point.new(coord_x: 2.0, coord_y: 2.0))
assert_equal Geometry.gradient(ln1), 2
assert_equal Geometry.gradient(ln2), 1
assert_equal Geometry.intersection(ln1, ln2), Point.new(coord_x: 0, coord_y: 0)
assert Geometry.intersection(ln1, ln1).nil?

View File

@@ -1,10 +0,0 @@
import geometry
let ln1 = Line(start: Point(coordX: 0, coordY: 0), end: Point(coordX: 1, coordY: 2))
let ln2 = Line(start: Point(coordX: 1, coordY: 1), end: Point(coordX: 2, coordY: 2))
assert(gradient(ln: ln1) == 2.0)
assert(gradient(ln: ln2) == 1.0)
assert(intersection(ln1: ln1, ln2: ln2) == Point(coordX: 0, coordY: 0))
assert(intersection(ln1: ln1, ln2: ln1) == nil)

View File

@@ -1,6 +0,0 @@
uniffi::build_foreign_language_testcases!(
"tests/bindings/test_geometry.py",
"tests/bindings/test_geometry.rb",
"tests/bindings/test_geometry.kts",
"tests/bindings/test_geometry.swift",
);

View File

@@ -1,15 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
arithmetical::uniffi_reexport_scaffolding!();
uniffi_fixture_callbacks::uniffi_reexport_scaffolding!();
uniffi_custom_types::uniffi_reexport_scaffolding!();
uniffi_fixture_external_types::uniffi_reexport_scaffolding!();
uniffi_fixture_futures::uniffi_reexport_scaffolding!();
uniffi_fixture_refcounts::uniffi_reexport_scaffolding!();
uniffi_geometry::uniffi_reexport_scaffolding!();
uniffi_rondpoint::uniffi_reexport_scaffolding!();
uniffi_sprites::uniffi_reexport_scaffolding!();
uniffi_todolist::uniffi_reexport_scaffolding!();
uniffi_trait_interfaces::uniffi_reexport_scaffolding!();

View File

@@ -1,27 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
FINAL_LIBRARY = "xul"
components = [
"Arithmetic",
"CustomTypes",
"ExternalTypes",
"FixtureCallbacks",
"Futures",
"Geometry",
"Refcounts",
"Rondpoint",
"Sprites",
"Todolist",
"UniffiTraitInterfaces",
]
EXTRA_JS_MODULES += [
"generated/Rust{}.sys.mjs".format(component) for component in components
]
XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.toml"]

View File

@@ -1,12 +0,0 @@
[package]
name = "uniffi-fixture-refcounts"
edition = "2021"
version = "0.21.0"
license = "MPL-2.0"
publish = false
[dependencies]
uniffi = { workspace = true }
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }

View File

@@ -1,32 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// This crate exists to test managing the Rust Arc strong counts as JS objects are
/// created/destroyed. See `test_refcounts.js` for how it's used.
use std::sync::{Arc, Mutex};
pub struct SingletonObject;
impl SingletonObject {
pub fn method(&self) {}
}
static SINGLETON: Mutex<Option<Arc<SingletonObject>>> = Mutex::new(None);
pub fn get_singleton() -> Arc<SingletonObject> {
Arc::clone(
SINGLETON
.lock()
.unwrap()
.get_or_insert_with(|| Arc::new(SingletonObject)),
)
}
pub fn get_js_refcount() -> i32 {
// Subtract 2: one for the reference in the Mutex and one for the temporary reference that
// we're calling Arc::strong_count on.
(Arc::strong_count(&get_singleton()) as i32) - 2
}
include!(concat!(env!("OUT_DIR"), "/refcounts.uniffi.rs"));

View File

@@ -1,8 +0,0 @@
namespace refcounts {
SingletonObject get_singleton();
i32 get_js_refcount();
};
interface SingletonObject {
void method();
};

View File

@@ -1,20 +0,0 @@
[package]
name = "uniffi-example-rondpoint"
edition = "2021"
version = "0.22.0"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
license = "MPL-2.0"
publish = false
[lib]
crate-type = ["lib", "cdylib"]
name = "uniffi_rondpoint"
[dependencies]
uniffi = { workspace = true }
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }
[dev-dependencies]
uniffi = { workspace = true, features = ["bindgen-tests"] }

View File

@@ -1,7 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
fn main() {
uniffi::generate_scaffolding("src/rondpoint.udl").unwrap();
}

View File

@@ -1,293 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Dictionnaire {
un: Enumeration,
deux: bool,
petit_nombre: u8,
gros_nombre: u64,
}
#[derive(Debug, Clone)]
pub struct DictionnaireNombres {
petit_nombre: u8,
court_nombre: u16,
nombre_simple: u32,
gros_nombre: u64,
}
#[derive(Debug, Clone)]
pub struct DictionnaireNombresSignes {
petit_nombre: i8,
court_nombre: i16,
nombre_simple: i32,
gros_nombre: i64,
}
#[derive(Debug, Clone)]
pub enum Enumeration {
Un,
Deux,
Trois,
}
#[derive(Debug, Clone)]
pub enum EnumerationAvecDonnees {
Zero,
Un { premier: u32 },
Deux { premier: u32, second: String },
}
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub struct minusculeMAJUSCULEDict {
minusculeMAJUSCULEField: bool,
}
#[allow(non_camel_case_types)]
pub enum minusculeMAJUSCULEEnum {
minusculeMAJUSCULEVariant,
}
fn copie_enumeration(e: Enumeration) -> Enumeration {
e
}
fn copie_enumerations(e: Vec<Enumeration>) -> Vec<Enumeration> {
e
}
fn copie_carte(
e: HashMap<String, EnumerationAvecDonnees>,
) -> HashMap<String, EnumerationAvecDonnees> {
e
}
fn copie_dictionnaire(d: Dictionnaire) -> Dictionnaire {
d
}
fn switcheroo(b: bool) -> bool {
!b
}
// Test that values can traverse both ways across the FFI.
// Even if roundtripping works, it's possible we have
// symmetrical errors that cancel each other out.
#[derive(Debug, Clone)]
struct Retourneur;
impl Retourneur {
fn new() -> Self {
Retourneur
}
fn identique_i8(&self, value: i8) -> i8 {
value
}
fn identique_u8(&self, value: u8) -> u8 {
value
}
fn identique_i16(&self, value: i16) -> i16 {
value
}
fn identique_u16(&self, value: u16) -> u16 {
value
}
fn identique_i32(&self, value: i32) -> i32 {
value
}
fn identique_u32(&self, value: u32) -> u32 {
value
}
fn identique_i64(&self, value: i64) -> i64 {
value
}
fn identique_u64(&self, value: u64) -> u64 {
value
}
fn identique_float(&self, value: f32) -> f32 {
value
}
fn identique_double(&self, value: f64) -> f64 {
value
}
fn identique_boolean(&self, value: bool) -> bool {
value
}
fn identique_string(&self, value: String) -> String {
value
}
fn identique_nombres_signes(
&self,
value: DictionnaireNombresSignes,
) -> DictionnaireNombresSignes {
value
}
fn identique_nombres(&self, value: DictionnaireNombres) -> DictionnaireNombres {
value
}
fn identique_optionneur_dictionnaire(
&self,
value: OptionneurDictionnaire,
) -> OptionneurDictionnaire {
value
}
}
#[derive(Debug, Clone)]
struct Stringifier;
#[allow(dead_code)]
impl Stringifier {
fn new() -> Self {
Stringifier
}
fn to_string_i8(&self, value: i8) -> String {
value.to_string()
}
fn to_string_u8(&self, value: u8) -> String {
value.to_string()
}
fn to_string_i16(&self, value: i16) -> String {
value.to_string()
}
fn to_string_u16(&self, value: u16) -> String {
value.to_string()
}
fn to_string_i32(&self, value: i32) -> String {
value.to_string()
}
fn to_string_u32(&self, value: u32) -> String {
value.to_string()
}
fn to_string_i64(&self, value: i64) -> String {
value.to_string()
}
fn to_string_u64(&self, value: u64) -> String {
value.to_string()
}
fn to_string_float(&self, value: f32) -> String {
value.to_string()
}
fn to_string_double(&self, value: f64) -> String {
value.to_string()
}
fn to_string_boolean(&self, value: bool) -> String {
value.to_string()
}
fn well_known_string(&self, value: String) -> String {
format!("uniffi 💚 {value}!")
}
}
#[derive(Debug, Clone)]
struct Optionneur;
impl Optionneur {
fn new() -> Self {
Optionneur
}
fn sinon_string(&self, value: String) -> String {
value
}
fn sinon_null(&self, value: Option<String>) -> Option<String> {
value
}
fn sinon_boolean(&self, value: bool) -> bool {
value
}
fn sinon_sequence(&self, value: Vec<String>) -> Vec<String> {
value
}
fn sinon_zero(&self, value: Option<i32>) -> Option<i32> {
value
}
fn sinon_u8_dec(&self, value: u8) -> u8 {
value
}
fn sinon_i8_dec(&self, value: i8) -> i8 {
value
}
fn sinon_u16_dec(&self, value: u16) -> u16 {
value
}
fn sinon_i16_dec(&self, value: i16) -> i16 {
value
}
fn sinon_u32_dec(&self, value: u32) -> u32 {
value
}
fn sinon_i32_dec(&self, value: i32) -> i32 {
value
}
fn sinon_u64_dec(&self, value: u64) -> u64 {
value
}
fn sinon_i64_dec(&self, value: i64) -> i64 {
value
}
fn sinon_u8_hex(&self, value: u8) -> u8 {
value
}
fn sinon_i8_hex(&self, value: i8) -> i8 {
value
}
fn sinon_u16_hex(&self, value: u16) -> u16 {
value
}
fn sinon_i16_hex(&self, value: i16) -> i16 {
value
}
fn sinon_u32_hex(&self, value: u32) -> u32 {
value
}
fn sinon_i32_hex(&self, value: i32) -> i32 {
value
}
fn sinon_u64_hex(&self, value: u64) -> u64 {
value
}
fn sinon_i64_hex(&self, value: i64) -> i64 {
value
}
fn sinon_u32_oct(&self, value: u32) -> u32 {
value
}
fn sinon_f32(&self, value: f32) -> f32 {
value
}
fn sinon_f64(&self, value: f64) -> f64 {
value
}
fn sinon_enum(&self, value: Enumeration) -> Enumeration {
value
}
}
pub struct OptionneurDictionnaire {
i8_var: i8,
u8_var: u8,
i16_var: i16,
u16_var: u16,
i32_var: i32,
u32_var: u32,
i64_var: i64,
u64_var: u64,
float_var: f32,
double_var: f64,
boolean_var: bool,
string_var: String,
list_var: Vec<String>,
enumeration_var: Enumeration,
dictionnaire_var: Option<minusculeMAJUSCULEEnum>,
}
uniffi::include_scaffolding!("rondpoint");

View File

@@ -1,146 +0,0 @@
namespace rondpoint {
Dictionnaire copie_dictionnaire(Dictionnaire d);
Enumeration copie_enumeration(Enumeration e);
sequence<Enumeration> copie_enumerations(sequence<Enumeration> e);
record<string, EnumerationAvecDonnees> copie_carte(record<string, EnumerationAvecDonnees> c);
boolean switcheroo(boolean b);
};
dictionary minusculeMAJUSCULEDict {
boolean minusculeMAJUSCULEField;
};
enum minusculeMAJUSCULEEnum {
"minusculeMAJUSCULEVariant",
};
enum Enumeration {
"Un",
"Deux",
"Trois",
};
[Enum]
interface EnumerationAvecDonnees {
Zero();
Un(u32 premier);
Deux(u32 premier, string second);
};
dictionary Dictionnaire {
Enumeration un;
boolean deux;
u8 petit_nombre;
u64 gros_nombre;
};
dictionary DictionnaireNombres {
u8 petit_nombre;
u16 court_nombre;
u32 nombre_simple;
u64 gros_nombre;
};
dictionary DictionnaireNombresSignes {
i8 petit_nombre;
i16 court_nombre;
i32 nombre_simple;
i64 gros_nombre;
};
interface Retourneur {
constructor();
i8 identique_i8(i8 value);
u8 identique_u8(u8 value);
i16 identique_i16(i16 value);
u16 identique_u16(u16 value);
i32 identique_i32(i32 value);
u32 identique_u32(u32 value);
i64 identique_i64(i64 value);
u64 identique_u64(u64 value);
float identique_float(float value);
double identique_double(double value);
boolean identique_boolean(boolean value);
string identique_string(string value);
DictionnaireNombresSignes identique_nombres_signes(DictionnaireNombresSignes value);
DictionnaireNombres identique_nombres(DictionnaireNombres value);
OptionneurDictionnaire identique_optionneur_dictionnaire(OptionneurDictionnaire value);
};
interface Stringifier {
constructor();
string well_known_string(string value);
string to_string_i8(i8 value);
string to_string_u8(u8 value);
string to_string_i16(i16 value);
string to_string_u16(u16 value);
string to_string_i32(i32 value);
string to_string_u32(u32 value);
string to_string_i64(i64 value);
string to_string_u64(u64 value);
string to_string_float(float value);
string to_string_double(double value);
string to_string_boolean(boolean value);
};
interface Optionneur {
constructor();
boolean sinon_boolean(optional boolean value = false);
string sinon_string(optional string value = "default");
sequence<string> sinon_sequence(optional sequence<string> value = []);
// Either sides of nullable.
string? sinon_null(optional string? value = null);
i32? sinon_zero(optional i32? value = 0);
// Decimal integers, all 42.
u8 sinon_u8_dec(optional u8 value = 42);
i8 sinon_i8_dec(optional i8 value = -42);
u16 sinon_u16_dec(optional u16 value = 42);
i16 sinon_i16_dec(optional i16 value = 42);
u32 sinon_u32_dec(optional u32 value = 42);
i32 sinon_i32_dec(optional i32 value = 42);
u64 sinon_u64_dec(optional u64 value = 42);
i64 sinon_i64_dec(optional i64 value = 42);
// Hexadecimal, including negatgives.
u8 sinon_u8_hex(optional u8 value = 0xff);
i8 sinon_i8_hex(optional i8 value = -0x7f);
u16 sinon_u16_hex(optional u16 value = 0xffff);
i16 sinon_i16_hex(optional i16 value = 0x7f);
u32 sinon_u32_hex(optional u32 value = 0xffffffff);
i32 sinon_i32_hex(optional i32 value = 0x7fffffff);
u64 sinon_u64_hex(optional u64 value = 0xffffffffffffffff);
i64 sinon_i64_hex(optional i64 value = 0x7fffffffffffffff);
// Octal, FWIW.
u32 sinon_u32_oct(optional u32 value = 0755);
// Floats
f32 sinon_f32(optional f32 value = 42.0);
f64 sinon_f64(optional f64 value = 42.1);
// Enums, which we have to treat as strings in the UDL frontend.
Enumeration sinon_enum(optional Enumeration value = "Trois");
};
dictionary OptionneurDictionnaire {
i8 i8_var = -8;
u8 u8_var = 8;
i16 i16_var = -0x10;
u16 u16_var = 0x10;
i32 i32_var = -32;
u32 u32_var = 32;
i64 i64_var = -64;
u64 u64_var = 64;
float float_var = 4.0;
double double_var = 8.0;
boolean boolean_var = true;
string string_var = "default";
sequence<string> list_var = [];
Enumeration enumeration_var = "DEUX";
minusculeMAJUSCULEEnum? dictionnaire_var = null;
};

View File

@@ -1,250 +0,0 @@
import uniffi.rondpoint.*
val dico = Dictionnaire(Enumeration.DEUX, true, 0u, 123456789u)
val copyDico = copieDictionnaire(dico)
assert(dico == copyDico)
assert(copieEnumeration(Enumeration.DEUX) == Enumeration.DEUX)
assert(copieEnumerations(listOf(Enumeration.UN, Enumeration.DEUX)) == listOf(Enumeration.UN, Enumeration.DEUX))
assert(copieCarte(mapOf(
"0" to EnumerationAvecDonnees.Zero,
"1" to EnumerationAvecDonnees.Un(1u),
"2" to EnumerationAvecDonnees.Deux(2u, "deux")
)) == mapOf(
"0" to EnumerationAvecDonnees.Zero,
"1" to EnumerationAvecDonnees.Un(1u),
"2" to EnumerationAvecDonnees.Deux(2u, "deux")
))
val var1: EnumerationAvecDonnees = EnumerationAvecDonnees.Zero
val var2: EnumerationAvecDonnees = EnumerationAvecDonnees.Un(1u)
val var3: EnumerationAvecDonnees = EnumerationAvecDonnees.Un(2u)
assert(var1 != var2)
assert(var2 != var3)
assert(var1 == EnumerationAvecDonnees.Zero)
assert(var1 != EnumerationAvecDonnees.Un(1u))
assert(var2 == EnumerationAvecDonnees.Un(1u))
assert(switcheroo(false))
// Test the roundtrip across the FFI.
// This shows that the values we send come back in exactly the same state as we sent them.
// i.e. it shows that lowering from kotlin and lifting into rust is symmetrical with
// lowering from rust and lifting into kotlin.
val rt = Retourneur()
fun <T> List<T>.affirmAllerRetour(fn: (T) -> T) {
this.forEach { v ->
assert(fn.invoke(v) == v) { "$fn($v)" }
}
}
// Booleans
listOf(true, false).affirmAllerRetour(rt::identiqueBoolean)
// Bytes.
listOf(Byte.MIN_VALUE, Byte.MAX_VALUE).affirmAllerRetour(rt::identiqueI8)
listOf(0x00, 0xFF).map { it.toUByte() }.affirmAllerRetour(rt::identiqueU8)
// Shorts
listOf(Short.MIN_VALUE, Short.MAX_VALUE).affirmAllerRetour(rt::identiqueI16)
listOf(0x0000, 0xFFFF).map { it.toUShort() }.affirmAllerRetour(rt::identiqueU16)
// Ints
listOf(0, 1, -1, Int.MIN_VALUE, Int.MAX_VALUE).affirmAllerRetour(rt::identiqueI32)
listOf(0x00000000, 0xFFFFFFFF).map { it.toUInt() }.affirmAllerRetour(rt::identiqueU32)
// Longs
listOf(0L, 1L, -1L, Long.MIN_VALUE, Long.MAX_VALUE).affirmAllerRetour(rt::identiqueI64)
listOf(0u, 1u, ULong.MIN_VALUE, ULong.MAX_VALUE).affirmAllerRetour(rt::identiqueU64)
// Floats
listOf(0.0F, 0.5F, 0.25F, Float.MIN_VALUE, Float.MAX_VALUE).affirmAllerRetour(rt::identiqueFloat)
// Doubles
listOf(0.0, 1.0, Double.MIN_VALUE, Double.MAX_VALUE).affirmAllerRetour(rt::identiqueDouble)
// Strings
listOf("", "abc", "null\u0000byte", "été", "ښي لاس ته لوستلو لوستل", "😻emoji 👨👧👦multi-emoji, 🇨🇭a flag, a canal, panama")
.affirmAllerRetour(rt::identiqueString)
listOf(-1, 0, 1).map { DictionnaireNombresSignes(it.toByte(), it.toShort(), it.toInt(), it.toLong()) }
.affirmAllerRetour(rt::identiqueNombresSignes)
listOf(0, 1).map { DictionnaireNombres(it.toUByte(), it.toUShort(), it.toUInt(), it.toULong()) }
.affirmAllerRetour(rt::identiqueNombres)
rt.destroy()
// Test one way across the FFI.
//
// We send one representation of a value to lib.rs, and it transforms it into another, a string.
// lib.rs sends the string back, and then we compare here in kotlin.
//
// This shows that the values are transformed into strings the same way in both kotlin and rust.
// i.e. if we assume that the string return works (we test this assumption elsewhere)
// we show that lowering from kotlin and lifting into rust has values that both kotlin and rust
// both stringify in the same way. i.e. the same values.
//
// If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust t here,
// and this convinces us that lowering/lifting from here to rust is correct, then
// together, we've shown the correctness of the return leg.
val st = Stringifier()
typealias StringyEquals<T> = (observed: String, expected: T) -> Boolean
fun <T> List<T>.affirmEnchaine(
fn: (T) -> String,
equals: StringyEquals<T> = { obs, exp -> obs == exp.toString() }
) {
this.forEach { exp ->
val obs = fn.invoke(exp)
assert(equals(obs, exp)) { "$fn($exp): observed=$obs, expected=$exp" }
}
}
// Test the efficacy of the string transport from rust. If this fails, but everything else
// works, then things are very weird.
val wellKnown = st.wellKnownString("kotlin")
assert("uniffi 💚 kotlin!" == wellKnown) { "wellKnownString 'uniffi 💚 kotlin!' == '$wellKnown'" }
// Booleans
listOf(true, false).affirmEnchaine(st::toStringBoolean)
// Bytes.
listOf(Byte.MIN_VALUE, Byte.MAX_VALUE).affirmEnchaine(st::toStringI8)
listOf(UByte.MIN_VALUE, UByte.MAX_VALUE).affirmEnchaine(st::toStringU8)
// Shorts
listOf(Short.MIN_VALUE, Short.MAX_VALUE).affirmEnchaine(st::toStringI16)
listOf(UShort.MIN_VALUE, UShort.MAX_VALUE).affirmEnchaine(st::toStringU16)
// Ints
listOf(0, 1, -1, Int.MIN_VALUE, Int.MAX_VALUE).affirmEnchaine(st::toStringI32)
listOf(0u, 1u, UInt.MIN_VALUE, UInt.MAX_VALUE).affirmEnchaine(st::toStringU32)
// Longs
listOf(0L, 1L, -1L, Long.MIN_VALUE, Long.MAX_VALUE).affirmEnchaine(st::toStringI64)
listOf(0u, 1u, ULong.MIN_VALUE, ULong.MAX_VALUE).affirmEnchaine(st::toStringU64)
// Floats
// MIN_VALUE is 1.4E-45. Accuracy and formatting get weird at small sizes.
listOf(0.0F, 1.0F, -1.0F, Float.MIN_VALUE, Float.MAX_VALUE).affirmEnchaine(st::toStringFloat) { s, n -> s.toFloat() == n }
// Doubles
// MIN_VALUE is 4.9E-324. Accuracy and formatting get weird at small sizes.
listOf(0.0, 1.0, -1.0, Double.MIN_VALUE, Double.MAX_VALUE).affirmEnchaine(st::toStringDouble) { s, n -> s.toDouble() == n }
st.destroy()
// Prove to ourselves that default arguments are being used.
// Step 1: call the methods without arguments, and check against the UDL.
val op = Optionneur()
assert(op.sinonString() == "default")
assert(op.sinonBoolean() == false)
assert(op.sinonSequence() == listOf<String>())
// optionals
assert(op.sinonNull() == null)
assert(op.sinonZero() == 0)
// decimal integers
assert(op.sinonI8Dec() == (-42).toByte())
assert(op.sinonU8Dec() == 42.toUByte())
assert(op.sinonI16Dec() == 42.toShort())
assert(op.sinonU16Dec() == 42.toUShort())
assert(op.sinonI32Dec() == 42)
assert(op.sinonU32Dec() == 42.toUInt())
assert(op.sinonI64Dec() == 42L)
assert(op.sinonU64Dec() == 42uL)
// hexadecimal integers
assert(op.sinonI8Hex() == (-0x7f).toByte())
assert(op.sinonU8Hex() == 0xff.toUByte())
assert(op.sinonI16Hex() == 0x7f.toShort())
assert(op.sinonU16Hex() == 0xffff.toUShort())
assert(op.sinonI32Hex() == 0x7fffffff)
assert(op.sinonU32Hex() == 0xffffffff.toUInt())
assert(op.sinonI64Hex() == 0x7fffffffffffffffL)
assert(op.sinonU64Hex() == 0xffffffffffffffffuL)
// octal integers
assert(op.sinonU32Oct() == 493u) // 0o755
// floats
assert(op.sinonF32() == 42.0f)
assert(op.sinonF64() == 42.1)
// enums
assert(op.sinonEnum() == Enumeration.TROIS)
// Step 2. Convince ourselves that if we pass something else, then that changes the output.
// We have shown something coming out of the sinon methods, but without eyeballing the Rust
// we can't be sure that the arguments will change the return value.
listOf("foo", "bar").affirmAllerRetour(op::sinonString)
listOf(true, false).affirmAllerRetour(op::sinonBoolean)
listOf(listOf("a", "b"), listOf()).affirmAllerRetour(op::sinonSequence)
// optionals
listOf("0", "1").affirmAllerRetour(op::sinonNull)
listOf(0, 1).affirmAllerRetour(op::sinonZero)
// integers
listOf(0, 1).map { it.toUByte() }.affirmAllerRetour(op::sinonU8Dec)
listOf(0, 1).map { it.toByte() }.affirmAllerRetour(op::sinonI8Dec)
listOf(0, 1).map { it.toUShort() }.affirmAllerRetour(op::sinonU16Dec)
listOf(0, 1).map { it.toShort() }.affirmAllerRetour(op::sinonI16Dec)
listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Dec)
listOf(0, 1).map { it.toInt() }.affirmAllerRetour(op::sinonI32Dec)
listOf(0, 1).map { it.toULong() }.affirmAllerRetour(op::sinonU64Dec)
listOf(0, 1).map { it.toLong() }.affirmAllerRetour(op::sinonI64Dec)
listOf(0, 1).map { it.toUByte() }.affirmAllerRetour(op::sinonU8Hex)
listOf(0, 1).map { it.toByte() }.affirmAllerRetour(op::sinonI8Hex)
listOf(0, 1).map { it.toUShort() }.affirmAllerRetour(op::sinonU16Hex)
listOf(0, 1).map { it.toShort() }.affirmAllerRetour(op::sinonI16Hex)
listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Hex)
listOf(0, 1).map { it.toInt() }.affirmAllerRetour(op::sinonI32Hex)
listOf(0, 1).map { it.toULong() }.affirmAllerRetour(op::sinonU64Hex)
listOf(0, 1).map { it.toLong() }.affirmAllerRetour(op::sinonI64Hex)
listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Oct)
// floats
listOf(0.0f, 1.0f).affirmAllerRetour(op::sinonF32)
listOf(0.0, 1.0).affirmAllerRetour(op::sinonF64)
// enums
Enumeration.values().toList().affirmAllerRetour(op::sinonEnum)
op.destroy()
// Testing defaulting properties in record types.
val defaultes = OptionneurDictionnaire()
val explicites = OptionneurDictionnaire(
i8Var = -8,
u8Var = 8u,
i16Var = -16,
u16Var = 0x10u,
i32Var = -32,
u32Var = 32u,
i64Var = -64L,
u64Var = 64uL,
floatVar = 4.0f,
doubleVar = 8.0,
booleanVar = true,
stringVar = "default",
listVar = listOf(),
enumerationVar = Enumeration.DEUX,
dictionnaireVar = null
)
assert(defaultes == explicites)
// …and makes sure they travel across and back the FFI.
val rt2 = Retourneur()
listOf(defaultes).affirmAllerRetour(rt2::identiqueOptionneurDictionnaire)
rt2.destroy()

View File

@@ -1,183 +0,0 @@
import ctypes
import sys
from rondpoint import (
Dictionnaire,
Enumeration,
EnumerationAvecDonnees,
Retourneur,
Stringifier,
copie_carte,
copie_dictionnaire,
copie_enumeration,
copie_enumerations,
switcheroo,
)
dico = Dictionnaire(
un=Enumeration.DEUX, deux=True, petit_nombre=0, gros_nombre=123456789
)
copyDico = copie_dictionnaire(dico)
assert dico == copyDico
assert copie_enumeration(Enumeration.DEUX) == Enumeration.DEUX
assert copie_enumerations([Enumeration.UN, Enumeration.DEUX]) == [
Enumeration.UN,
Enumeration.DEUX,
]
assert copie_carte(
{
"0": EnumerationAvecDonnees.ZERO(),
"1": EnumerationAvecDonnees.UN(1),
"2": EnumerationAvecDonnees.DEUX(2, "deux"),
}
) == {
"0": EnumerationAvecDonnees.ZERO(),
"1": EnumerationAvecDonnees.UN(1),
"2": EnumerationAvecDonnees.DEUX(2, "deux"),
}
assert switcheroo(False) is True
assert EnumerationAvecDonnees.ZERO() != EnumerationAvecDonnees.UN(1)
assert EnumerationAvecDonnees.UN(1) == EnumerationAvecDonnees.UN(1)
assert EnumerationAvecDonnees.UN(1) != EnumerationAvecDonnees.UN(2)
# Test the roundtrip across the FFI.
# This shows that the values we send come back in exactly the same state as we sent them.
# i.e. it shows that lowering from python and lifting into rust is symmetrical with
# lowering from rust and lifting into python.
rt = Retourneur()
def affirmAllerRetour(vals, identique):
for v in vals:
id_v = identique(v)
assert id_v == v, f"Round-trip failure: {v} => {id_v}"
MIN_I8 = -1 * 2**7
MAX_I8 = 2**7 - 1
MIN_I16 = -1 * 2**15
MAX_I16 = 2**15 - 1
MIN_I32 = -1 * 2**31
MAX_I32 = 2**31 - 1
MIN_I64 = -1 * 2**31
MAX_I64 = 2**31 - 1
# Python floats are always doubles, so won't round-trip through f32 correctly.
# This truncates them appropriately.
F32_ONE_THIRD = ctypes.c_float(1.0 / 3).value
# Booleans
affirmAllerRetour([True, False], rt.identique_boolean)
# Bytes.
affirmAllerRetour([MIN_I8, -1, 0, 1, MAX_I8], rt.identique_i8)
affirmAllerRetour([0x00, 0x12, 0xFF], rt.identique_u8)
# Shorts
affirmAllerRetour([MIN_I16, -1, 0, 1, MAX_I16], rt.identique_i16)
affirmAllerRetour([0x0000, 0x1234, 0xFFFF], rt.identique_u16)
# Ints
affirmAllerRetour([MIN_I32, -1, 0, 1, MAX_I32], rt.identique_i32)
affirmAllerRetour([0x00000000, 0x12345678, 0xFFFFFFFF], rt.identique_u32)
# Longs
affirmAllerRetour([MIN_I64, -1, 0, 1, MAX_I64], rt.identique_i64)
affirmAllerRetour(
[0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], rt.identique_u64
)
# Floats
affirmAllerRetour([0.0, 0.5, 0.25, 1.0, F32_ONE_THIRD], rt.identique_float)
# Doubles
affirmAllerRetour(
[0.0, 0.5, 0.25, 1.0, 1.0 / 3, sys.float_info.max, sys.float_info.min],
rt.identique_double,
)
# Strings
affirmAllerRetour(
[
"",
"abc",
"été",
"ښي لاس ته لوستلو لوستل",
"😻emoji 👨👧👦multi-emoji, 🇨🇭a flag, a canal, panama",
],
rt.identique_string,
)
# Test one way across the FFI.
#
# We send one representation of a value to lib.rs, and it transforms it into another, a string.
# lib.rs sends the string back, and then we compare here in python.
#
# This shows that the values are transformed into strings the same way in both python and rust.
# i.e. if we assume that the string return works (we test this assumption elsewhere)
# we show that lowering from python and lifting into rust has values that both python and rust
# both stringify in the same way. i.e. the same values.
#
# If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust to here,
# and this convinces us that lowering/lifting from here to rust is correct, then
# together, we've shown the correctness of the return leg.
st = Stringifier()
def affirmEnchaine(vals, toString, rustyStringify=lambda v: str(v).lower()):
for v in vals:
str_v = toString(v)
assert rustyStringify(v) == str_v, f"String compare error {v} => {str_v}"
# Test the efficacy of the string transport from rust. If this fails, but everything else
# works, then things are very weird.
wellKnown = st.well_known_string("python")
assert "uniffi 💚 python!" == wellKnown
# Booleans
affirmEnchaine([True, False], st.to_string_boolean)
# Bytes.
affirmEnchaine([MIN_I8, -1, 0, 1, MAX_I8], st.to_string_i8)
affirmEnchaine([0x00, 0x12, 0xFF], st.to_string_u8)
# Shorts
affirmEnchaine([MIN_I16, -1, 0, 1, MAX_I16], st.to_string_i16)
affirmEnchaine([0x0000, 0x1234, 0xFFFF], st.to_string_u16)
# Ints
affirmEnchaine([MIN_I32, -1, 0, 1, MAX_I32], st.to_string_i32)
affirmEnchaine([0x00000000, 0x12345678, 0xFFFFFFFF], st.to_string_u32)
# Longs
affirmEnchaine([MIN_I64, -1, 0, 1, MAX_I64], st.to_string_i64)
affirmEnchaine(
[0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], st.to_string_u64
)
# Floats
def rustyFloatToStr(v):
"""Stringify a float in the same way that rust seems to."""
# Rust doesn't include the decimal part of whole enumber floats when stringifying.
if int(v) == v:
return str(int(v))
return str(v)
affirmEnchaine([0.0, 0.5, 0.25, 1.0], st.to_string_float, rustyFloatToStr)
assert (
st.to_string_float(F32_ONE_THIRD) == "0.33333334"
) # annoyingly different string repr
# Doubles
# TODO: float_info.max/float_info.min don't stringify-roundtrip properly yet, TBD.
affirmEnchaine(
[0.0, 0.5, 0.25, 1.0, 1.0 / 3],
st.to_string_double,
rustyFloatToStr,
)

View File

@@ -1,147 +0,0 @@
# frozen_string_literal: true
require 'test/unit'
require 'rondpoint'
include Test::Unit::Assertions
include Rondpoint
dico = Dictionnaire.new(
un: Enumeration::DEUX,
deux: true,
petit_nombre: 0,
gros_nombre: 123_456_789
)
assert_equal dico, Rondpoint.copie_dictionnaire(dico)
assert_equal Rondpoint.copie_enumeration(Enumeration::DEUX), Enumeration::DEUX
assert_equal Rondpoint.copie_enumerations([
Enumeration::UN,
Enumeration::DEUX
]), [Enumeration::UN, Enumeration::DEUX]
assert_equal Rondpoint.copie_carte({
'0' => EnumerationAvecDonnees::ZERO.new,
'1' => EnumerationAvecDonnees::UN.new(1),
'2' => EnumerationAvecDonnees::DEUX.new(2, 'deux')
}), {
'0' => EnumerationAvecDonnees::ZERO.new,
'1' => EnumerationAvecDonnees::UN.new(1),
'2' => EnumerationAvecDonnees::DEUX.new(2, 'deux')
}
assert Rondpoint.switcheroo(false)
assert_not_equal EnumerationAvecDonnees::ZERO.new, EnumerationAvecDonnees::UN.new(1)
assert_equal EnumerationAvecDonnees::UN.new(1), EnumerationAvecDonnees::UN.new(1)
assert_not_equal EnumerationAvecDonnees::UN.new(1), EnumerationAvecDonnees::UN.new(2)
# Test the roundtrip across the FFI.
# This shows that the values we send come back in exactly the same state as we sent them.
# i.e. it shows that lowering from ruby and lifting into rust is symmetrical with
# lowering from rust and lifting into ruby.
RT = Retourneur.new
def affirm_aller_retour(vals, fn_name)
vals.each do |v|
id_v = RT.public_send fn_name, v
assert_equal id_v, v, "Round-trip failure: #{v} => #{id_v}"
end
end
MIN_I8 = -1 * 2**7
MAX_I8 = 2**7 - 1
MIN_I16 = -1 * 2**15
MAX_I16 = 2**15 - 1
MIN_I32 = -1 * 2**31
MAX_I32 = 2**31 - 1
MIN_I64 = -1 * 2**31
MAX_I64 = 2**31 - 1
# Ruby floats are always doubles, so won't round-trip through f32 correctly.
# This truncates them appropriately.
F32_ONE_THIRD = [1.0 / 3].pack('f').unpack('f')[0]
# Booleans
affirm_aller_retour([true, false], :identique_boolean)
# Bytes.
affirm_aller_retour([MIN_I8, -1, 0, 1, MAX_I8], :identique_i8)
affirm_aller_retour([0x00, 0x12, 0xFF], :identique_u8)
# Shorts
affirm_aller_retour([MIN_I16, -1, 0, 1, MAX_I16], :identique_i16)
affirm_aller_retour([0x0000, 0x1234, 0xFFFF], :identique_u16)
# Ints
affirm_aller_retour([MIN_I32, -1, 0, 1, MAX_I32], :identique_i32)
affirm_aller_retour([0x00000000, 0x12345678, 0xFFFFFFFF], :identique_u32)
# Longs
affirm_aller_retour([MIN_I64, -1, 0, 1, MAX_I64], :identique_i64)
affirm_aller_retour([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], :identique_u64)
# Floats
affirm_aller_retour([0.0, 0.5, 0.25, 1.0, F32_ONE_THIRD], :identique_float)
# Doubles
affirm_aller_retour(
[0.0, 0.5, 0.25, 1.0, 1.0 / 3, Float::MAX, Float::MIN],
:identique_double
)
# Strings
affirm_aller_retour(
['', 'abc', 'été', 'ښي لاس ته لوستلو لوستل',
'😻emoji 👨👧👦multi-emoji, 🇨🇭a flag, a canal, panama'],
:identique_string
)
# Test one way across the FFI.
#
# We send one representation of a value to lib.rs, and it transforms it into another, a string.
# lib.rs sends the string back, and then we compare here in ruby.
#
# This shows that the values are transformed into strings the same way in both ruby and rust.
# i.e. if we assume that the string return works (we test this assumption elsewhere)
# we show that lowering from ruby and lifting into rust has values that both ruby and rust
# both stringify in the same way. i.e. the same values.
#
# If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust to here,
# and this convinces us that lowering/lifting from here to rust is correct, then
# together, we've shown the correctness of the return leg.
ST = Stringifier.new
def affirm_enchaine(vals, fn_name)
vals.each do |v|
str_v = ST.public_send fn_name, v
assert_equal v.to_s, str_v, "String compare error #{v} => #{str_v}"
end
end
# Test the efficacy of the string transport from rust. If this fails, but everything else
# works, then things are very weird.
assert_equal ST.well_known_string('ruby'), 'uniffi 💚 ruby!'
# Booleans
affirm_enchaine([true, false], :to_string_boolean)
# Bytes.
affirm_enchaine([MIN_I8, -1, 0, 1, MAX_I8], :to_string_i8)
affirm_enchaine([0x00, 0x12, 0xFF], :to_string_u8)
# Shorts
affirm_enchaine([MIN_I16, -1, 0, 1, MAX_I16], :to_string_i16)
affirm_enchaine([0x0000, 0x1234, 0xFFFF], :to_string_u16)
# Ints
affirm_enchaine([MIN_I32, -1, 0, 1, MAX_I32], :to_string_i32)
affirm_enchaine([0x00000000, 0x12345678, 0xFFFFFFFF], :to_string_u32)
# Longs
affirm_enchaine([MIN_I64, -1, 0, 1, MAX_I64], :to_string_i64)
affirm_enchaine([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], :to_string_u64)

View File

@@ -1,232 +0,0 @@
import rondpoint
let dico = Dictionnaire(un: .deux, deux: false, petitNombre: 0, grosNombre: 123456789)
let copyDico = copieDictionnaire(d: dico)
assert(dico == copyDico)
assert(copieEnumeration(e: .deux) == .deux)
assert(copieEnumerations(e: [.un, .deux]) == [.un, .deux])
assert(copieCarte(c:
["0": .zero,
"1": .un(premier: 1),
"2": .deux(premier: 2, second: "deux")
]) == [
"0": .zero,
"1": .un(premier: 1),
"2": .deux(premier: 2, second: "deux")
])
assert(EnumerationAvecDonnees.zero != EnumerationAvecDonnees.un(premier: 1))
assert(EnumerationAvecDonnees.un(premier: 1) == EnumerationAvecDonnees.un(premier: 1))
assert(EnumerationAvecDonnees.un(premier: 1) != EnumerationAvecDonnees.un(premier: 2))
assert(switcheroo(b: false))
// Test the roundtrip across the FFI.
// This shows that the values we send come back in exactly the same state as we sent them.
// i.e. it shows that lowering from swift and lifting into rust is symmetrical with
// lowering from rust and lifting into swift.
let rt = Retourneur()
// Booleans
[true, false].affirmAllerRetour(rt.identiqueBoolean)
// Bytes.
[.min, .max].affirmAllerRetour(rt.identiqueI8)
[0x00, 0xFF].map { $0 as UInt8 }.affirmAllerRetour(rt.identiqueU8)
// Shorts
[.min, .max].affirmAllerRetour(rt.identiqueI16)
[0x0000, 0xFFFF].map { $0 as UInt16 }.affirmAllerRetour(rt.identiqueU16)
// Ints
[0, 1, -1, .min, .max].affirmAllerRetour(rt.identiqueI32)
[0x00000000, 0xFFFFFFFF].map { $0 as UInt32 }.affirmAllerRetour(rt.identiqueU32)
// Longs
[.zero, 1, -1, .min, .max].affirmAllerRetour(rt.identiqueI64)
[.zero, 1, .min, .max].affirmAllerRetour(rt.identiqueU64)
// Floats
[.zero, 1, 0.25, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmAllerRetour(rt.identiqueFloat)
// Doubles
[0.0, 1.0, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmAllerRetour(rt.identiqueDouble)
// Strings
["", "abc", "null\0byte", "été", "ښي لاس ته لوستلو لوستل", "😻emoji 👨👧👦multi-emoji, 🇨🇭a flag, a canal, panama"]
.affirmAllerRetour(rt.identiqueString)
// Test one way across the FFI.
//
// We send one representation of a value to lib.rs, and it transforms it into another, a string.
// lib.rs sends the string back, and then we compare here in swift.
//
// This shows that the values are transformed into strings the same way in both swift and rust.
// i.e. if we assume that the string return works (we test this assumption elsewhere)
// we show that lowering from swift and lifting into rust has values that both swift and rust
// both stringify in the same way. i.e. the same values.
//
// If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust t here,
// and this convinces us that lowering/lifting from here to rust is correct, then
// together, we've shown the correctness of the return leg.
let st = Stringifier()
// Test the effigacy of the string transport from rust. If this fails, but everything else
// works, then things are very weird.
let wellKnown = st.wellKnownString(value: "swift")
assert("uniffi 💚 swift!" == wellKnown, "wellKnownString 'uniffi 💚 swift!' == '\(wellKnown)'")
// Booleans
[true, false].affirmEnchaine(st.toStringBoolean)
// Bytes.
[.min, .max].affirmEnchaine(st.toStringI8)
[.min, .max].affirmEnchaine(st.toStringU8)
// Shorts
[.min, .max].affirmEnchaine(st.toStringI16)
[.min, .max].affirmEnchaine(st.toStringU16)
// Ints
[0, 1, -1, .min, .max].affirmEnchaine(st.toStringI32)
[0, 1, .min, .max].affirmEnchaine(st.toStringU32)
// Longs
[.zero, 1, -1, .min, .max].affirmEnchaine(st.toStringI64)
[.zero, 1, .min, .max].affirmEnchaine(st.toStringU64)
// Floats
[.zero, 1, -1, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmEnchaine(st.toStringFloat) { Float.init($0) == $1 }
// Doubles
[.zero, 1, -1, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmEnchaine(st.toStringDouble) { Double.init($0) == $1 }
// Some extension functions for testing the results of roundtripping and stringifying
extension Array where Element: Equatable {
static func defaultEquals(_ observed: String, expected: Element) -> Bool {
let exp = "\(expected)"
return observed == exp
}
func affirmEnchaine(_ fn: (Element) -> String, equals: (String, Element) -> Bool = defaultEquals) {
self.forEach { v in
let obs = fn(v)
assert(equals(obs, v), "toString_\(type(of:v))(\(v)): observed=\(obs), expected=\(v)")
}
}
func affirmAllerRetour(_ fn: (Element) -> Element) {
self.forEach { v in
assert(fn(v) == v, "identique_\(type(of:v))(\(v))")
}
}
}
// Prove to ourselves that default arguments are being used.
// Step 1: call the methods without arguments, and check against the UDL.
let op = Optionneur()
assert(op.sinonString() == "default")
assert(op.sinonBoolean() == false)
assert(op.sinonSequence() == [])
// optionals
assert(op.sinonNull() == nil)
assert(op.sinonZero() == 0)
// decimal integers
assert(op.sinonU8Dec() == UInt8(42))
assert(op.sinonI8Dec() == Int8(-42))
assert(op.sinonU16Dec() == UInt16(42))
assert(op.sinonI16Dec() == Int16(42))
assert(op.sinonU32Dec() == UInt32(42))
assert(op.sinonI32Dec() == Int32(42))
assert(op.sinonU64Dec() == UInt64(42))
assert(op.sinonI64Dec() == Int64(42))
// hexadecimal integers
assert(op.sinonU8Hex() == UInt8(0xff))
assert(op.sinonI8Hex() == Int8(-0x7f))
assert(op.sinonU16Hex() == UInt16(0xffff))
assert(op.sinonI16Hex() == Int16(0x7f))
assert(op.sinonU32Hex() == UInt32(0xffffffff))
assert(op.sinonI32Hex() == Int32(0x7fffffff))
assert(op.sinonU64Hex() == UInt64(0xffffffffffffffff))
assert(op.sinonI64Hex() == Int64(0x7fffffffffffffff))
// octal integers
assert(op.sinonU32Oct() == UInt32(0o755))
// floats
assert(op.sinonF32() == 42.0)
assert(op.sinonF64() == Double(42.1))
// enums
assert(op.sinonEnum() == .trois)
// Step 2. Convince ourselves that if we pass something else, then that changes the output.
// We have shown something coming out of the sinon methods, but without eyeballing the Rust
// we can't be sure that the arguments will change the return value.
["foo", "bar"].affirmAllerRetour(op.sinonString)
[true, false].affirmAllerRetour(op.sinonBoolean)
[["a", "b"], []].affirmAllerRetour(op.sinonSequence)
// optionals
["0", "1"].affirmAllerRetour(op.sinonNull)
[0, 1].affirmAllerRetour(op.sinonZero)
// integers
[0, 1].affirmAllerRetour(op.sinonU8Dec)
[0, 1].affirmAllerRetour(op.sinonI8Dec)
[0, 1].affirmAllerRetour(op.sinonU16Dec)
[0, 1].affirmAllerRetour(op.sinonI16Dec)
[0, 1].affirmAllerRetour(op.sinonU32Dec)
[0, 1].affirmAllerRetour(op.sinonI32Dec)
[0, 1].affirmAllerRetour(op.sinonU64Dec)
[0, 1].affirmAllerRetour(op.sinonI64Dec)
[0, 1].affirmAllerRetour(op.sinonU8Hex)
[0, 1].affirmAllerRetour(op.sinonI8Hex)
[0, 1].affirmAllerRetour(op.sinonU16Hex)
[0, 1].affirmAllerRetour(op.sinonI16Hex)
[0, 1].affirmAllerRetour(op.sinonU32Hex)
[0, 1].affirmAllerRetour(op.sinonI32Hex)
[0, 1].affirmAllerRetour(op.sinonU64Hex)
[0, 1].affirmAllerRetour(op.sinonI64Hex)
[0, 1].affirmAllerRetour(op.sinonU32Oct)
// floats
[0.0, 1.0].affirmAllerRetour(op.sinonF32)
[0.0, 1.0].affirmAllerRetour(op.sinonF64)
// enums
[.un, .deux, .trois].affirmAllerRetour(op.sinonEnum)
// Testing defaulting properties in record types.
let defaultes = OptionneurDictionnaire()
let explicites = OptionneurDictionnaire(
i8Var: Int8(-8),
u8Var: UInt8(8),
i16Var: Int16(-16),
u16Var: UInt16(0x10),
i32Var: -32,
u32Var: UInt32(32),
i64Var: Int64(-64),
u64Var: UInt64(64),
floatVar: Float(4.0),
doubleVar: Double(8.0),
booleanVar: true,
stringVar: "default",
listVar: [],
enumerationVar: .deux,
dictionnaireVar: nil
)
// and makes sure they travel across and back the FFI.
assert(defaultes == explicites)
[defaultes].affirmAllerRetour(rt.identiqueOptionneurDictionnaire)

View File

@@ -1,6 +0,0 @@
uniffi::build_foreign_language_testcases!(
"tests/bindings/test_rondpoint.kts",
"tests/bindings/test_rondpoint.swift",
"tests/bindings/test_rondpoint.py",
"tests/bindings/test_rondpoint.rb",
);

View File

@@ -1,20 +0,0 @@
[package]
name = "uniffi-example-sprites"
edition = "2021"
version = "0.22.0"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
license = "MPL-2.0"
publish = false
[lib]
crate-type = ["lib", "cdylib"]
name = "uniffi_sprites"
[dependencies]
uniffi = { workspace = true }
[build-dependencies]
uniffi = { workspace = true, features = ["build"] }
[dev-dependencies]
uniffi = { workspace = true, features = ["bindgen-tests"] }

View File

@@ -1,69 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::sync::RwLock;
// A point in two-dimensional space.
#[derive(Debug, Clone, uniffi::Record)]
pub struct Point {
pub x: f64,
pub y: f64,
}
// A magnitude and direction in two-dimensional space.
// For simplicity we represent this as a point relative to the origin.
#[derive(Debug, Clone, uniffi::Record)]
pub struct Vector {
pub dx: f64,
pub dy: f64,
}
// Move from the given Point, according to the given Vector.
#[uniffi::export]
pub fn translate(p: &Point, v: Vector) -> Point {
Point {
x: p.x + v.dx,
y: p.y + v.dy,
}
}
// An entity in our imaginary world, which occupies a position in space
// and which can move about over time.
#[derive(Debug, uniffi::Object)]
pub struct Sprite {
// We must use interior mutability for managing mutable state, hence the `RwLock`.
current_position: RwLock<Point>,
}
#[uniffi::export]
impl Sprite {
#[uniffi::constructor]
pub fn new(initial_position: Option<Point>) -> Sprite {
Sprite {
current_position: RwLock::new(initial_position.unwrap_or(Point { x: 0.0, y: 0.0 })),
}
}
#[uniffi::constructor]
pub fn new_relative_to(reference: Point, direction: Vector) -> Sprite {
Sprite {
current_position: RwLock::new(translate(&reference, direction)),
}
}
pub fn get_position(&self) -> Point {
self.current_position.read().unwrap().clone()
}
pub fn move_to(&self, position: Point) {
*self.current_position.write().unwrap() = position;
}
pub fn move_by(&self, direction: Vector) {
let mut current_position = self.current_position.write().unwrap();
*current_position = translate(&current_position, direction)
}
}
uniffi::setup_scaffolding!("sprites");

View File

@@ -1,22 +0,0 @@
namespace sprites {
Point translate([ByRef] Point position, Vector direction);
};
dictionary Point {
double x;
double y;
};
dictionary Vector {
double dx;
double dy;
};
interface Sprite {
constructor(Point? initial_position);
[Name=new_relative_to] constructor(Point reference, Vector direction);
Point get_position();
void move_to(Point position);
void move_by(Vector direction);
};

View File

@@ -1,25 +0,0 @@
import uniffi.sprites.*;
val sempty = Sprite(null)
assert( sempty.getPosition() == Point(0.0, 0.0) )
val s = Sprite(Point(0.0, 1.0))
assert( s.getPosition() == Point(0.0, 1.0) )
s.moveTo(Point(1.0, 2.0))
assert( s.getPosition() == Point(1.0, 2.0) )
s.moveBy(Vector(-4.0, 2.0))
assert( s.getPosition() == Point(-3.0, 4.0) )
s.destroy()
try {
s.moveBy(Vector(0.0, 0.0))
assert(false) { "Should not be able to call anything after `destroy`" }
} catch(e: IllegalStateException) {
assert(true)
}
val srel = Sprite.newRelativeTo(Point(0.0, 1.0), Vector(1.0, 1.5))
assert( srel.getPosition() == Point(1.0, 2.5) )

View File

@@ -1,16 +0,0 @@
from sprites import Point, Sprite, Vector
sempty = Sprite(None)
assert sempty.get_position() == Point(x=0, y=0)
s = Sprite(Point(x=0, y=1))
assert s.get_position() == Point(x=0, y=1)
s.move_to(Point(x=1, y=2))
assert s.get_position() == Point(x=1, y=2)
s.move_by(Vector(dx=-4, dy=2))
assert s.get_position() == Point(x=-3, y=4)
srel = Sprite.new_relative_to(Point(x=0, y=1), Vector(dx=1, dy=1.5))
assert srel.get_position() == Point(x=1, y=2.5)

View File

@@ -1,22 +0,0 @@
# frozen_string_literal: true
require 'test/unit'
require 'sprites'
include Test::Unit::Assertions
include Sprites
sempty = Sprite.new(nil)
assert_equal sempty.get_position, Point.new(x: 0, y: 0)
s = Sprite.new(Point.new(x: 0, y: 1))
assert_equal s.get_position, Point.new(x: 0, y: 1)
s.move_to(Point.new(x: 1, y: 2))
assert_equal s.get_position, Point.new(x: 1, y: 2)
s.move_by(Vector.new(dx: -4, dy: 2))
assert_equal s.get_position, Point.new(x: -3, y: 4)
srel = Sprite.new_relative_to(Point.new(x: 0, y: 1), Vector.new(dx: 1, dy: 1.5))
assert_equal srel.get_position, Point.new(x: 1, y: 2.5)

View File

@@ -1,16 +0,0 @@
import sprites
let sempty = Sprite(initialPosition: nil)
assert( sempty.getPosition() == Point(x: 0, y: 0))
let s = Sprite(initialPosition: Point(x: 0, y: 1))
assert( s.getPosition() == Point(x: 0, y: 1))
s.moveTo(position: Point(x: 1.0, y: 2.0))
assert( s.getPosition() == Point(x: 1, y: 2))
s.moveBy(direction: Vector(dx: -4, dy: 2))
assert( s.getPosition() == Point(x: -3, y: 4))
let srel = Sprite.newRelativeTo(reference: Point(x: 0.0, y: 1.0), direction: Vector(dx: 1, dy: 1.5))
assert( srel.getPosition() == Point(x: 1.0, y: 2.5) )

View File

@@ -1,6 +0,0 @@
uniffi::build_foreign_language_testcases!(
"tests/bindings/test_sprites.py",
"tests/bindings/test_sprites.rb",
"tests/bindings/test_sprites.kts",
"tests/bindings/test_sprites.swift",
);

View File

@@ -1,48 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const Arithmetic = ChromeUtils.importESModule(
"resource://gre/modules/RustArithmetic.sys.mjs"
);
add_task(async function () {
Assert.ok(Arithmetic.IntegerOverflow);
Assert.equal(await Arithmetic.add(2, 4), 6);
Assert.equal(await Arithmetic.add(4, 8), 12);
// For other backends we would have this test:
// await Assert.rejects(
// Arithmetic.add(18446744073709551615, 1),
// Arithmetic.IntegerOverflow,
// "add() should throw IntegerOverflow")
//
// However, this doesn't work because JS number values are actually 64-bit
// floats, and that number is greater than the maximum "safe" integer.
//
// Instead, let's test that we reject numbers that are that big
await Assert.rejects(
Arithmetic.add(Number.MAX_SAFE_INTEGER + 1, 0),
/TypeError/,
"add() should throw TypeError when an input is > MAX_SAFE_INTEGER"
);
Assert.equal(await Arithmetic.sub(4, 2), 2);
Assert.equal(await Arithmetic.sub(8, 4), 4);
await Assert.rejects(
Arithmetic.sub(0, 1),
Arithmetic.IntegerOverflow,
"sub() should throw IntegerOverflow"
);
Assert.equal(await Arithmetic.div(8, 4), 2);
// Can't test this, because we don't allow Rust panics in FF
// Assert.rejects(
// Arithmetic.div(8, 0),
// (e) => Assert.equal(e, Arithmetic.UniFFIInternalError),
// "Divide by 0 should throw UniFFIInternalError")
//
Assert.ok(await Arithmetic.equal(2, 2));
Assert.ok(await Arithmetic.equal(4, 4));
Assert.ok(!(await Arithmetic.equal(2, 4)));
Assert.ok(!(await Arithmetic.equal(4, 8)));
});

View File

@@ -1,63 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { callLogRepeat, logEvenNumbers, logEvenNumbersMainThread } =
ChromeUtils.importESModule(
"resource://gre/modules/RustFixtureCallbacks.sys.mjs"
);
class Logger {
constructor() {
this.messages = [];
this.finishedPromise = new Promise((resolve, reject) => {
this.finishedResolve = resolve;
this.finishedReject = reject;
});
}
log(message) {
this.messages.push(message);
}
logRepeat(message, count, exclude) {
for (var i = 0; i < count; i++) {
if (!exclude.includes(i)) {
this.messages.push(`${i}: ${message}`);
}
}
}
finished() {
this.finishedResolve(true);
}
async waitForFinish() {
// Set a timeout to avoid hanging the tests if the Rust code fails to call finished().
do_timeout(2000, () =>
this.finishedReject("Timeout waiting for finished()")
);
return this.finishedPromise;
}
}
add_task(async function testLogEvenNumbers() {
async function runTest(logEvenNumbersFunc) {
const logger = new Logger();
logEvenNumbersFunc(logger, [1, 1, 2, 3, 5, 8, 13]);
await logger.waitForFinish();
Assert.deepEqual(logger.messages, [
"Saw even number: 2",
"Saw even number: 8",
]);
}
await runTest(logEvenNumbers);
await runTest(logEvenNumbersMainThread);
});
add_task(async function testLogRepeat() {
const logger = new Logger();
callLogRepeat(logger, "Hello", 5, [2, 3]);
await logger.waitForFinish();
Assert.deepEqual(logger.messages, ["0: Hello", "1: Hello", "4: Hello"]);
});

View File

@@ -1,174 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const CustomTypes = ChromeUtils.importESModule(
"resource://gre/modules/RustCustomTypes.sys.mjs"
);
add_task(async function testCustomTypes() {
// JS right now doesn't treat custom types as anything but it's native counterparts
let demo = await CustomTypes.getCustomTypesDemo();
Assert.equal(demo.url, "http://example.com/");
Assert.equal(demo.handle, 123);
});
add_task(async function testExplicitEnumValues() {
// Test that the enum values are preserved correctly, including gaps
Assert.equal(CustomTypes.ExplicitValuedEnum.FIRST, 1);
Assert.equal(CustomTypes.ExplicitValuedEnum.SECOND, 2);
Assert.equal(CustomTypes.ExplicitValuedEnum.FOURTH, 4);
Assert.equal(CustomTypes.ExplicitValuedEnum.TENTH, 10);
Assert.equal(CustomTypes.ExplicitValuedEnum.ELEVENTH, 11);
Assert.equal(CustomTypes.ExplicitValuedEnum.THIRTEENTH, 13);
// Test that the discriminant function returns the expected values
Assert.equal(
await CustomTypes.getExplicitDiscriminant(
CustomTypes.ExplicitValuedEnum.FIRST
),
1
);
Assert.equal(
await CustomTypes.getExplicitDiscriminant(
CustomTypes.ExplicitValuedEnum.SECOND
),
2
);
Assert.equal(
await CustomTypes.getExplicitDiscriminant(
CustomTypes.ExplicitValuedEnum.FOURTH
),
4
);
Assert.equal(
await CustomTypes.getExplicitDiscriminant(
CustomTypes.ExplicitValuedEnum.TENTH
),
10
);
Assert.equal(
await CustomTypes.getExplicitDiscriminant(
CustomTypes.ExplicitValuedEnum.ELEVENTH
),
11
);
Assert.equal(
await CustomTypes.getExplicitDiscriminant(
CustomTypes.ExplicitValuedEnum.THIRTEENTH
),
13
);
// Test that the enum values work correctly when passed back to Rust
Assert.equal(
await CustomTypes.echoExplicitValue(CustomTypes.ExplicitValuedEnum.FIRST),
CustomTypes.ExplicitValuedEnum.FIRST
);
Assert.equal(
await CustomTypes.echoExplicitValue(CustomTypes.ExplicitValuedEnum.FOURTH),
CustomTypes.ExplicitValuedEnum.FOURTH
);
Assert.equal(
await CustomTypes.echoExplicitValue(
CustomTypes.ExplicitValuedEnum.THIRTEENTH
),
CustomTypes.ExplicitValuedEnum.THIRTEENTH
);
});
add_task(async function testGappedEnumValues() {
// Import UniFFITypeError to check the error type
const { UniFFITypeError } = ChromeUtils.importESModule(
"resource://gre/modules/UniFFI.sys.mjs"
);
// Test that the enum values are preserved correctly for mixed sequential/explicit values
Assert.equal(CustomTypes.GappedEnum.ONE, 10);
Assert.equal(CustomTypes.GappedEnum.TWO, 11); // Sequential value after ONE (10+1)
Assert.equal(CustomTypes.GappedEnum.THREE, 14); // Explicit value again
// Verify with values from Rust
const valueRecord = await CustomTypes.getGappedEnumValues();
Assert.equal(valueRecord.One, CustomTypes.GappedEnum.ONE);
Assert.equal(valueRecord.Two, CustomTypes.GappedEnum.TWO);
Assert.equal(valueRecord.Three, CustomTypes.GappedEnum.THREE);
// Test discriminant function
Assert.equal(
await CustomTypes.getGappedDiscriminant(CustomTypes.GappedEnum.ONE),
10
);
Assert.equal(
await CustomTypes.getGappedDiscriminant(CustomTypes.GappedEnum.TWO),
11
);
Assert.equal(
await CustomTypes.getGappedDiscriminant(CustomTypes.GappedEnum.THREE),
14
);
// Test echo function
Assert.equal(
await CustomTypes.echoGappedValue(CustomTypes.GappedEnum.ONE),
CustomTypes.GappedEnum.ONE
);
Assert.equal(
await CustomTypes.echoGappedValue(CustomTypes.GappedEnum.TWO),
CustomTypes.GappedEnum.TWO
);
Assert.equal(
await CustomTypes.echoGappedValue(CustomTypes.GappedEnum.THREE),
CustomTypes.GappedEnum.THREE
);
// Test validation for non-existent values
// Values in gaps should be rejected
await Assert.rejects(
CustomTypes.getGappedDiscriminant(0),
UniFFITypeError,
"Should reject value 0 which is below the lowest enum variant"
);
await Assert.rejects(
CustomTypes.getGappedDiscriminant(9),
UniFFITypeError,
"Should reject value 9 which is just below a valid enum variant"
);
await Assert.rejects(
CustomTypes.getGappedDiscriminant(12),
UniFFITypeError,
"Should reject value 12 which is in a gap between valid enum variants"
);
await Assert.rejects(
CustomTypes.getGappedDiscriminant(13),
UniFFITypeError,
"Should reject value 13 which is in a gap between valid enum variants"
);
await Assert.rejects(
CustomTypes.getGappedDiscriminant(15),
UniFFITypeError,
"Should reject value 15 which is above the highest enum variant"
);
// Test non-integer values
await Assert.rejects(
CustomTypes.getGappedDiscriminant("ONE"),
UniFFITypeError,
"Should reject string value instead of enum"
);
await Assert.rejects(
CustomTypes.getGappedDiscriminant(null),
UniFFITypeError,
"Should reject null value instead of enum"
);
await Assert.rejects(
CustomTypes.getGappedDiscriminant(undefined),
UniFFITypeError,
"Should reject undefined value instead of enum"
);
});

View File

@@ -1,25 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const ExternalTypes = ChromeUtils.importESModule(
"resource://gre/modules/RustExternalTypes.sys.mjs"
);
const Sprites = ChromeUtils.importESModule(
"resource://gre/modules/RustSprites.sys.mjs"
);
add_task(async function testDataTypes() {
const line = new ExternalTypes.Line({
start: await new ExternalTypes.Point({ coordX: 0, coordY: 0 }),
end: await new ExternalTypes.Point({ coordX: 2, coordY: 1 }),
});
Assert.equal(await ExternalTypes.gradient(line), 0.5);
Assert.equal(await ExternalTypes.gradient(null), 0.0);
});
add_task(async function testInterface() {
const s = await Sprites.Sprite.init(new Sprites.Point({ x: 100, y: 100 }));
await ExternalTypes.moveSpriteToOrigin(s);
Assert.deepEqual(await s.getPosition(), new Sprites.Point({ x: 0, y: 0 }));
});

View File

@@ -1,90 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
const RustFutures = ChromeUtils.importESModule(
"resource://gre/modules/RustFutures.sys.mjs"
);
RustFutures.initializeGeckoGlobalWorkerQueue();
add_task(async function simpleTest() {
const tester = RustFutures.FutureTester.init();
const f = tester.makeFuture();
shortDelay().then(() => runCompleteFutures(tester, 42, 1));
Assert.equal(await f, 42);
});
async function runCompleteFutures(tester, value, futureCount) {
var completedCount = 0;
// Call completeFutures in a loop until we've completed the expected number of futures.
// Since makeFuture is an async call, we need to make sure that completeFutures runs after all
// the futures have been made and inserted into the internal vec.
for (;;) {
completedCount += tester.completeFutures(value);
if (completedCount >= futureCount) {
return;
}
await shortDelay();
}
}
add_task(async function twoFutures() {
const tester = RustFutures.FutureTester.init();
// Create a future and complete it using an async task
const f1 = tester.makeFuture();
const f2 = tester.makeFuture();
runCompleteFutures(tester, 84, 2);
Assert.deepEqual(await Promise.all([f1, f2]), [84, 84]);
});
add_task(async function roundtripFunctions() {
Assert.equal(await RustFutures.roundtripU8(42), 42);
Assert.equal(await RustFutures.roundtripI8(-42), -42);
Assert.equal(await RustFutures.roundtripU16(42), 42);
Assert.equal(await RustFutures.roundtripI16(-42), -42);
Assert.equal(await RustFutures.roundtripU32(42), 42);
Assert.equal(await RustFutures.roundtripI32(-42), -42);
Assert.equal(await RustFutures.roundtripU64(42), 42);
Assert.equal(await RustFutures.roundtripI64(-42), -42);
Assert.equal(await RustFutures.roundtripF32(0.5), 0.5);
Assert.equal(await RustFutures.roundtripF64(-0.5), -0.5);
Assert.equal(await RustFutures.roundtripString("hi"), "hi");
Assert.deepEqual(await RustFutures.roundtripVec([42]), [42]);
Assert.deepEqual(await RustFutures.roundtripMap({ hello: "world" }), {
hello: "world",
});
const obj = RustFutures.Traveller.init("Alice");
Assert.equal((await RustFutures.roundtripObj(obj)).name(), "Alice");
});
add_task(async function wakeWhenNotReady() {
const tester = RustFutures.FutureTester.init();
var isResolved = false;
const f = tester.makeFuture().then(value => {
isResolved = true;
return value;
});
// This will wake up the Rust wakers, but while the futures are still not ready
// The JS promises should stay unresolved
tester.wakeFutures();
await shortDelay();
Assert.equal(isResolved, false);
// Okay, now let test revolving the futures
runCompleteFutures(tester, 42, 1);
Assert.equal(await f, 42);
Assert.equal(isResolved, true);
});
add_task(async function testWorkerQueue() {
Assert.equal(await RustFutures.expensiveComputation(), 1000);
});
// Utility function that sleeps for 10ms.
async function shortDelay() {
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 10));
}

View File

@@ -1,21 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const Geometry = ChromeUtils.importESModule(
"resource://gre/modules/RustGeometry.sys.mjs"
);
add_task(async function () {
const ln1 = new Geometry.Line({
start: new Geometry.Point({ coordX: 0, coordY: 0 }),
end: new Geometry.Point({ coordX: 1, coordY: 2 }),
});
const ln2 = new Geometry.Line({
start: new Geometry.Point({ coordX: 1, coordY: 1 }),
end: new Geometry.Point({ coordX: 2, coordY: 2 }),
});
const origin = new Geometry.Point({ coordX: 0, coordY: 0 });
Assert.ok((await Geometry.intersection(ln1, ln2)).equals(origin));
Assert.deepEqual(await Geometry.intersection(ln1, ln2), origin);
Assert.strictEqual(await Geometry.intersection(ln1, ln1), null);
});

Some files were not shown because too many files have changed in this diff Show More