Revert "Bug 1965313 - Reimplement enum discriminents, r=bgruber"
This reverts commit02d50105b9. Revert "Bug 1955298 - uniffi-bindgen-gecko-js: start using the IR pipeline, r=markh,frontend-codestyle-reviewers" This reverts commit90368cc237.
This commit is contained in:
committed by
smolnar@mozilla.com
parent
60643a0d2d
commit
54f63424a1
@@ -1251,7 +1251,6 @@ 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
129
Cargo.lock
generated
@@ -2565,6 +2565,7 @@ dependencies = [
|
||||
"gecko_logger",
|
||||
"geckoservo",
|
||||
"gkrust-uniffi-components",
|
||||
"gkrust-uniffi-fixtures",
|
||||
"gkrust_utils",
|
||||
"http_sfv",
|
||||
"idna_glue",
|
||||
@@ -2615,7 +2616,6 @@ dependencies = [
|
||||
"unic-langid-ffi",
|
||||
"unicode-bidi",
|
||||
"unicode-bidi-ffi",
|
||||
"uniffi-bindgen-gecko-js-test-fixtures",
|
||||
"url",
|
||||
"viaduct",
|
||||
"webext-storage",
|
||||
@@ -2637,6 +2637,24 @@ 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"
|
||||
@@ -6957,21 +6975,101 @@ dependencies = [
|
||||
"clap",
|
||||
"extend",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"textwrap",
|
||||
"toml",
|
||||
"uniffi",
|
||||
"uniffi_bindgen",
|
||||
"uniffi_pipeline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uniffi-bindgen-gecko-js-test-fixtures"
|
||||
version = "0.29.0"
|
||||
name = "uniffi-example-arithmetic"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"uniffi_bindings_tests",
|
||||
"uniffi_bindings_tests_external_types",
|
||||
"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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7001,23 +7099,6 @@ 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"
|
||||
|
||||
@@ -65,7 +65,6 @@ 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.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# RustErrorsupport.sys.mjs
|
||||
|
||||
```{js:autofunction} RustErrorsupport.sys.setApplicationErrorReporter
|
||||
```
|
||||
```{js:autofunction} RustErrorsupport.sys.unsetApplicationErrorReporter
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# RustRelevancy.sys.mjs
|
||||
```{js:autoclass} RustRelevancy.sys.Interest
|
||||
:members:
|
||||
:exclude-members: Interest
|
||||
```
|
||||
|
||||
```{js:autoclass} RustRelevancy.sys.InterestMetrics
|
||||
:members:
|
||||
:exclude-members: InterestMetrics
|
||||
@@ -11,6 +8,10 @@
|
||||
:members:
|
||||
:exclude-members: InterestVector
|
||||
```
|
||||
```{js:autoclass} RustRelevancy.sys.Interest
|
||||
:members:
|
||||
:exclude-members: Interest
|
||||
```
|
||||
```{js:autoclass} RustRelevancy.sys.RelevancyApiError
|
||||
:members:
|
||||
:exclude-members: RelevancyApiError
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
# RustRemoteSettings.sys.mjs
|
||||
```{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.RemoteSettingsService
|
||||
:members:
|
||||
:exclude-members: RemoteSettingsService
|
||||
```
|
||||
```{js:autoclass} RustRemoteSettings.sys.Attachment
|
||||
:members:
|
||||
:exclude-members: Attachment
|
||||
```
|
||||
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsConfig
|
||||
:members:
|
||||
:exclude-members: RemoteSettingsConfig
|
||||
@@ -31,10 +24,6 @@
|
||||
:members:
|
||||
:exclude-members: RemoteSettingsContext
|
||||
```
|
||||
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsError
|
||||
:members:
|
||||
:exclude-members: RemoteSettingsError
|
||||
```
|
||||
```{js:autoclass} RustRemoteSettings.sys.RemoteSettingsRecord
|
||||
:members:
|
||||
:exclude-members: RemoteSettingsRecord
|
||||
@@ -43,11 +32,23 @@
|
||||
: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.RemoteSettingsService
|
||||
```{js:autoclass} RustRemoteSettings.sys.Network
|
||||
:members:
|
||||
:exclude-members: RemoteSettingsService
|
||||
:exclude-members: Network
|
||||
```
|
||||
```{js:autoclass} RustRemoteSettings.sys.Backoff
|
||||
:members:
|
||||
:exclude-members: Backoff
|
||||
```
|
||||
```{js:autoclass} RustRemoteSettings.sys.Other
|
||||
:members:
|
||||
:exclude-members: Other
|
||||
```
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# RustSearch.sys.mjs
|
||||
```{js:autoclass} RustSearch.sys.JsonEngineMethod
|
||||
|
||||
```{js:autoclass} RustSearch.sys.SearchEngineSelector
|
||||
:members:
|
||||
:exclude-members: JsonEngineMethod
|
||||
:exclude-members: SearchEngineSelector
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.JsonEngineUrl
|
||||
:members:
|
||||
@@ -11,30 +12,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.SearchApplicationName
|
||||
:members:
|
||||
:exclude-members: SearchApplicationName
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.SearchEngineClassification
|
||||
: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
|
||||
@@ -43,10 +28,6 @@
|
||||
:members:
|
||||
:exclude-members: SearchEngineUrls
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.SearchUpdateChannel
|
||||
:members:
|
||||
:exclude-members: SearchUpdateChannel
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.SearchUrlParam
|
||||
:members:
|
||||
:exclude-members: SearchUrlParam
|
||||
@@ -55,3 +36,23 @@
|
||||
:members:
|
||||
:exclude-members: SearchUserEnvironment
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.JsonEngineMethod
|
||||
:members:
|
||||
:exclude-members: JsonEngineMethod
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.SearchApplicationName
|
||||
:members:
|
||||
:exclude-members: SearchApplicationName
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.SearchEngineClassification
|
||||
:members:
|
||||
:exclude-members: SearchEngineClassification
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.SearchUpdateChannel
|
||||
:members:
|
||||
:exclude-members: SearchUpdateChannel
|
||||
```
|
||||
```{js:autoclass} RustSearch.sys.Other
|
||||
:members:
|
||||
:exclude-members: Other
|
||||
```
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
# RustSuggest.sys.mjs
|
||||
```{js:autoclass} RustSuggest.sys.Backoff
|
||||
|
||||
```{js:autoclass} RustSuggest.sys.SuggestStore
|
||||
:members:
|
||||
:exclude-members: Backoff
|
||||
:exclude-members: SuggestStore
|
||||
```
|
||||
```{js:autoclass} RustSuggest.sys.SuggestStoreBuilder
|
||||
:members:
|
||||
:exclude-members: SuggestStoreBuilder
|
||||
```
|
||||
```{js:autoclass} RustSuggest.sys.FtsMatchInfo
|
||||
:members:
|
||||
@@ -15,34 +20,10 @@
|
||||
: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
|
||||
@@ -55,18 +36,30 @@
|
||||
: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
|
||||
@@ -75,13 +68,21 @@
|
||||
:members:
|
||||
:exclude-members: SuggestionProvider
|
||||
```
|
||||
```{js:autoclass} RustSuggest.sys.SuggestionProviderConstraints
|
||||
```{js:autoclass} RustSuggest.sys.Network
|
||||
:members:
|
||||
:exclude-members: SuggestionProviderConstraints
|
||||
:exclude-members: Network
|
||||
```
|
||||
```{js:autoclass} RustSuggest.sys.SuggestionQuery
|
||||
```{js:autoclass} RustSuggest.sys.Backoff
|
||||
:members:
|
||||
:exclude-members: SuggestionQuery
|
||||
: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
|
||||
```
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# RustSync15.sys.mjs
|
||||
|
||||
```{js:autoclass} RustSync15.sys.DeviceType
|
||||
:members:
|
||||
:exclude-members: DeviceType
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
# RustTabs.sys.mjs
|
||||
|
||||
```{js:autoclass} RustTabs.sys.TabsBridgedEngine
|
||||
:members:
|
||||
:exclude-members: TabsBridgedEngine
|
||||
```
|
||||
```{js:autoclass} RustTabs.sys.PendingCommand
|
||||
:members:
|
||||
:exclude-members: PendingCommand
|
||||
@@ -7,17 +12,13 @@
|
||||
: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.TabsBridgedEngine
|
||||
```{js:autoclass} RustTabs.sys.SqlError
|
||||
:members:
|
||||
:exclude-members: TabsBridgedEngine
|
||||
:exclude-members: SqlError
|
||||
```
|
||||
```{js:autoclass} RustTabs.sys.UnexpectedTabsError
|
||||
:members:
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
# RustWebextstorage.sys.mjs
|
||||
|
||||
```{js:autoclass} RustWebextstorage.sys.UnexpectedError
|
||||
:members:
|
||||
:exclude-members: UnexpectedError
|
||||
```
|
||||
```{js:autoclass} RustWebextstorage.sys.JsonError
|
||||
:members:
|
||||
:exclude-members: JsonError
|
||||
@@ -7,7 +12,3 @@
|
||||
:members:
|
||||
:exclude-members: QuotaError
|
||||
```
|
||||
```{js:autoclass} RustWebextstorage.sys.UnexpectedError
|
||||
:members:
|
||||
:exclude-members: UnexpectedError
|
||||
```
|
||||
|
||||
@@ -144,7 +144,7 @@ if CONFIG["MOZ_BACKGROUNDTASKS"]:
|
||||
DIRS += ["backgroundtasks"]
|
||||
|
||||
if CONFIG["MOZ_UNIFFI_FIXTURES"]:
|
||||
DIRS += ["uniffi-bindgen-gecko-js/tests"]
|
||||
DIRS += ["uniffi-bindgen-gecko-js/fixtures"]
|
||||
|
||||
if CONFIG["NIGHTLY_BUILD"]:
|
||||
DIRS += ["aboutinference"]
|
||||
|
||||
@@ -15,11 +15,9 @@ 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"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
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`
|
||||
@@ -3,8 +3,269 @@
|
||||
|
||||
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
|
||||
|
||||
|
||||
|
||||
// Objects intended to be used in the unit tests
|
||||
export var UnitTestObjs = {};
|
||||
|
||||
// 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) {
|
||||
const encoder = new TextEncoder();
|
||||
// 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 = 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 decoder = new TextDecoder();
|
||||
const size = this.readUint32();
|
||||
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
|
||||
const value = 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
|
||||
*
|
||||
@@ -198,315 +459,34 @@ class UniFFICallbackHandleMapEntry {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
const encoder = new TextEncoder();
|
||||
// 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 = 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 decoder = new TextDecoder();
|
||||
const size = this.readUint32();
|
||||
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
|
||||
const value = 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 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
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export class FfiConverterU32 extends FfiConverter {
|
||||
static checkType(value) {
|
||||
if (value === undefined ) {
|
||||
throw new UniFFITypeError(`undefined`);
|
||||
super.checkType(value);
|
||||
if (!Number.isInteger(value)) {
|
||||
throw new UniFFITypeError(`${value} is not an integer`);
|
||||
}
|
||||
if (value === null ) {
|
||||
throw new UniFFITypeError(`null`);
|
||||
if (value < 0 || value > 4294967295) {
|
||||
throw new UniFFITypeError(`${value} exceeds the U32 bounds`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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");
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
static lift(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
static lower(value) {
|
||||
return value;
|
||||
}
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeUint32(value)
|
||||
}
|
||||
static read(dataStream) {
|
||||
return dataStream.readUint32()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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(
|
||||
1, // 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(
|
||||
2, // 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) {
|
||||
@@ -539,41 +519,16 @@ export class FfiConverterString extends FfiConverter {
|
||||
return 4 + 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 uniffiCallbackHandlerApplicationErrorReporter.storeCallbackObj(callbackObj)
|
||||
return callbackHandlerApplicationErrorReporter.storeCallbackObj(callbackObj)
|
||||
}
|
||||
|
||||
static lift(handleId) {
|
||||
return uniffiCallbackHandlerApplicationErrorReporter.getCallbackObj(handleId)
|
||||
return callbackHandlerApplicationErrorReporter.getCallbackObj(handleId)
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
@@ -589,9 +544,12 @@ export class FfiConverterTypeApplicationErrorReporter extends FfiConverter {
|
||||
}
|
||||
}
|
||||
|
||||
const uniffiCallbackHandlerApplicationErrorReporter = new UniFFICallbackHandler(
|
||||
"ApplicationErrorReporter",
|
||||
1,
|
||||
|
||||
// Define callback interface handlers, this must come after the type loop since they reference the FfiConverters defined above.
|
||||
|
||||
const callbackHandlerApplicationErrorReporter = new UniFFICallbackHandler(
|
||||
"errorsupport:ApplicationErrorReporter",
|
||||
0,
|
||||
[
|
||||
new UniFFICallbackMethodHandler(
|
||||
"reportError",
|
||||
@@ -605,39 +563,64 @@ const uniffiCallbackHandlerApplicationErrorReporter = new UniFFICallbackHandler(
|
||||
[
|
||||
FfiConverterString,
|
||||
FfiConverterString,
|
||||
FfiConverterUInt32,
|
||||
FfiConverterUInt32,
|
||||
FfiConverterU32,
|
||||
FfiConverterU32,
|
||||
],
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
// Allow the shutdown-related functionality to be tested in the unit tests
|
||||
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`);
|
||||
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");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return UniFFIScaffolding.callAsyncWrapper(
|
||||
0, // errorsupport:uniffi_error_support_fn_func_set_application_error_reporter
|
||||
FfiConverterTypeApplicationErrorReporter.lower(errorReporter),
|
||||
)
|
||||
}
|
||||
if (value < 0 || value > 256) {
|
||||
throw new UniFFITypeError(`${value} exceeds the U8 bounds`);
|
||||
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(
|
||||
1, // 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()
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
|
||||
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
|
||||
|
||||
|
||||
|
||||
// Objects intended to be used in the unit tests
|
||||
export var UnitTestObjs = {};
|
||||
|
||||
@@ -165,23 +167,6 @@ 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) {
|
||||
@@ -280,10 +265,43 @@ 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 decoder = new TextDecoder();
|
||||
const utf8Arr = new Uint8Array(buf);
|
||||
return decoder.decode(utf8Arr);
|
||||
}
|
||||
static lower(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(value).buffer;
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeString(value);
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return dataStream.readString();
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return 4 + encoder.encode(value).length
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enumeration for the different types of device.
|
||||
*
|
||||
*
|
||||
* Firefox Accounts separates devices into broad categories for display purposes,
|
||||
* such as distinguishing a desktop PC from a mobile phone. Upon signin, the
|
||||
* application should inspect the device it is running on and select an appropriate
|
||||
@@ -293,34 +311,36 @@ export const DeviceType = {
|
||||
/**
|
||||
* DESKTOP
|
||||
*/
|
||||
DESKTOP: 0,
|
||||
DESKTOP:0,
|
||||
/**
|
||||
* MOBILE
|
||||
*/
|
||||
MOBILE: 1,
|
||||
MOBILE:1,
|
||||
/**
|
||||
* TABLET
|
||||
*/
|
||||
TABLET: 2,
|
||||
TABLET:2,
|
||||
/**
|
||||
* VR
|
||||
*/
|
||||
VR: 3,
|
||||
VR:3,
|
||||
/**
|
||||
* TV
|
||||
*/
|
||||
TV: 4,
|
||||
TV:4,
|
||||
/**
|
||||
* UNKNOWN
|
||||
*/
|
||||
UNKNOWN: 5,
|
||||
UNKNOWN:5,
|
||||
};
|
||||
Object.freeze(DeviceType);
|
||||
|
||||
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 the Rust scaffolding
|
||||
// Use sequential indices (1-based) for the wire format to match Python bindings
|
||||
switch (dataStream.readInt32()) {
|
||||
case 1:
|
||||
return DeviceType.DESKTOP
|
||||
@@ -340,7 +360,6 @@ export class FfiConverterTypeDeviceType extends FfiConverterArrayBuffer {
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
// Use sequential indices (1-based) for the wire format to match the Rust scaffolding
|
||||
if (value === DeviceType.DESKTOP) {
|
||||
dataStream.writeInt32(1);
|
||||
return;
|
||||
@@ -373,35 +392,14 @@ export class FfiConverterTypeDeviceType extends FfiConverterArrayBuffer {
|
||||
}
|
||||
|
||||
static checkType(value) {
|
||||
if (!Number.isInteger(value) || value < 1 || value > 6) {
|
||||
// Check that the value is a valid enum variant
|
||||
if (!this.#validValues.includes(value)) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -69,45 +69,29 @@ enable = true
|
||||
[webext_storage.async_wrappers]
|
||||
enable = true
|
||||
|
||||
[uniffi_bindings_tests.async_wrappers]
|
||||
[arithmetical.async_wrappers]
|
||||
enable = true
|
||||
|
||||
[uniffi_custom_types.async_wrappers]
|
||||
enable = true
|
||||
|
||||
[uniffi_fixture_callbacks.async_wrappers]
|
||||
enable = true
|
||||
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",
|
||||
"log_even_numbers_main_thread",
|
||||
]
|
||||
|
||||
[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
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
[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"]
|
||||
@@ -0,0 +1,36 @@
|
||||
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
|
||||
@@ -0,0 +1,21 @@
|
||||
[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"] }
|
||||
@@ -2,10 +2,6 @@
|
||||
* 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 super::*;
|
||||
use heck::ToUpperCamelCase;
|
||||
|
||||
pub fn pass(module: &mut Module) -> Result<()> {
|
||||
module.js_filename = format!("Rust{}.sys.mjs", module.name.to_upper_camel_case());
|
||||
Ok(())
|
||||
fn main() {
|
||||
uniffi::generate_scaffolding("src/arithmetic.udl").unwrap();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
[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);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/* 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");
|
||||
@@ -0,0 +1,29 @@
|
||||
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))
|
||||
@@ -0,0 +1,37 @@
|
||||
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)
|
||||
@@ -0,0 +1,31 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,32 @@
|
||||
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")
|
||||
@@ -0,0 +1,6 @@
|
||||
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",
|
||||
);
|
||||
@@ -0,0 +1,2 @@
|
||||
[bindings.kotlin]
|
||||
package_name = "org.mozilla.uniffi.example.arithmetic"
|
||||
@@ -0,0 +1,11 @@
|
||||
[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"
|
||||
@@ -0,0 +1,35 @@
|
||||
/* 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");
|
||||
@@ -0,0 +1,21 @@
|
||||
[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"] }
|
||||
@@ -2,12 +2,6 @@
|
||||
* 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 super::*;
|
||||
|
||||
pub fn pass(root: &mut Root) -> Result<()> {
|
||||
root.visit_mut(|node: &mut TypeNode| {
|
||||
node.ffi_converter = format!("FfiConverter{}", node.canonical_name);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
fn main() {
|
||||
uniffi::generate_scaffolding("./src/custom-types.udl").unwrap();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
[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);
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
/* 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"));
|
||||
@@ -0,0 +1,21 @@
|
||||
/* 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))
|
||||
@@ -0,0 +1,20 @@
|
||||
/* 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))
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
uniffi::build_foreign_language_testcases!(
|
||||
["src/custom-types.udl"],
|
||||
[
|
||||
"tests/bindings/test_custom_types.kts",
|
||||
"tests/bindings/test_custom_types.swift",
|
||||
]
|
||||
);
|
||||
@@ -0,0 +1,40 @@
|
||||
[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"
|
||||
@@ -0,0 +1,16 @@
|
||||
[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"] }
|
||||
@@ -0,0 +1,27 @@
|
||||
/* 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");
|
||||
@@ -0,0 +1,22 @@
|
||||
[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"] }
|
||||
@@ -0,0 +1,52 @@
|
||||
/* 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* 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");
|
||||
@@ -0,0 +1,66 @@
|
||||
/* 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/* 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
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/* 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
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
This directory is where files generated by Uniffi will be created.
|
||||
@@ -0,0 +1,558 @@
|
||||
// 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 = {};
|
||||
|
||||
// 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) {
|
||||
const encoder = new TextEncoder();
|
||||
// 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 = 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 decoder = new TextDecoder();
|
||||
const size = this.readUint32();
|
||||
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
|
||||
const value = 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 decoder = new TextDecoder();
|
||||
const utf8Arr = new Uint8Array(buf);
|
||||
return decoder.decode(utf8Arr);
|
||||
}
|
||||
static lower(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(value).buffer;
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeString(value);
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return dataStream.readString();
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return 4 + 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(
|
||||
105, // 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(
|
||||
106, // 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(
|
||||
107, // 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(
|
||||
108, // 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)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,509 @@
|
||||
// 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 = {};
|
||||
|
||||
// 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) {
|
||||
const encoder = new TextEncoder();
|
||||
// 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 = 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 decoder = new TextDecoder();
|
||||
const size = this.readUint32();
|
||||
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
|
||||
const value = 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 decoder = new TextDecoder();
|
||||
const utf8Arr = new Uint8Array(buf);
|
||||
return decoder.decode(utf8Arr);
|
||||
}
|
||||
static lower(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(value).buffer;
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeString(value);
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return dataStream.readString();
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return 4 + 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 "resource://gre/modules/RustSprites.sys.mjs";
|
||||
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export { FfiConverterTypeSprite, Sprite };import {
|
||||
FfiConverterTypeLine,
|
||||
Line,
|
||||
} from "resource://gre/modules/RustGeometry.sys.mjs";
|
||||
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export { FfiConverterTypeLine, Line };import {
|
||||
FfiConverterTypePoint,
|
||||
Point,
|
||||
} from "resource://gre/modules/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(
|
||||
118, // 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(
|
||||
119, // 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(
|
||||
120, // 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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,822 @@
|
||||
// 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 = {};
|
||||
|
||||
// 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) {
|
||||
const encoder = new TextEncoder();
|
||||
// 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 = 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 decoder = new TextDecoder();
|
||||
const size = this.readUint32();
|
||||
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
|
||||
const value = 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 decoder = new TextDecoder();
|
||||
const utf8Arr = new Uint8Array(buf);
|
||||
return decoder.decode(utf8Arr);
|
||||
}
|
||||
static lower(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(value).buffer;
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeString(value);
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return dataStream.readString();
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return 4 + 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",
|
||||
1,
|
||||
[
|
||||
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(
|
||||
121, // 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(
|
||||
122, // 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(
|
||||
123, // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread
|
||||
FfiConverterTypeLogger.lower(logger),
|
||||
FfiConverterSequencei32.lower(items),
|
||||
)
|
||||
}
|
||||
return handleRustResult(functionCall(), liftResult, liftError);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,592 @@
|
||||
// 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 = {};
|
||||
|
||||
// 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) {
|
||||
const encoder = new TextEncoder();
|
||||
// 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 = 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 decoder = new TextDecoder();
|
||||
const size = this.readUint32();
|
||||
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
|
||||
const value = 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 decoder = new TextDecoder();
|
||||
const utf8Arr = new Uint8Array(buf);
|
||||
return decoder.decode(utf8Arr);
|
||||
}
|
||||
static lower(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(value).buffer;
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeString(value);
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return dataStream.readString();
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return 4 + 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(
|
||||
149, // 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(
|
||||
150, // 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)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
|
||||
|
||||
|
||||
|
||||
// Objects intended to be used in the unit tests
|
||||
export var UnitTestObjs = {};
|
||||
|
||||
@@ -166,22 +168,25 @@ class ArrayBufferDataStream {
|
||||
})
|
||||
}
|
||||
|
||||
// Reads a pointer from the data stream
|
||||
// Reads a SingletonObject pointer from the data stream
|
||||
// UniFFI Pointers are **always** 8 bytes long. That is enforced
|
||||
// by the C++ and Rust Scaffolding code.
|
||||
readPointer(pointerId) {
|
||||
readPointerSingletonObject() {
|
||||
const pointerId = 16; // refcounts:SingletonObject
|
||||
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
|
||||
this.pos += 8;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Writes a pointer into the data stream
|
||||
// Writes a SingletonObject 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) {
|
||||
writePointerSingletonObject(value) {
|
||||
const pointerId = 16; // refcounts:SingletonObject
|
||||
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
|
||||
this.pos += 8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function handleRustResult(result, liftCallback, liftErrCallback) {
|
||||
@@ -279,143 +284,20 @@ class FfiConverterArrayBuffer extends FfiConverter {
|
||||
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
|
||||
const constructUniffiObject = Symbol("constructUniffiObject");
|
||||
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
|
||||
/**
|
||||
* roundtripExtCustomType
|
||||
*/
|
||||
export function roundtripExtCustomType(
|
||||
custom) {
|
||||
|
||||
FfiConverterTypeHandle.checkType(custom);
|
||||
const result = UniFFIScaffolding.callSync(
|
||||
159, // uniffi_uniffi_bindings_tests_external_types_fn_func_roundtrip_ext_custom_type
|
||||
FfiConverterTypeHandle.lower(custom),
|
||||
)
|
||||
return handleRustResult(
|
||||
result,
|
||||
FfiConverterTypeHandle.lift.bind(FfiConverterTypeHandle),
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* roundtripExtEnum
|
||||
*/
|
||||
export function roundtripExtEnum(
|
||||
en) {
|
||||
|
||||
FfiConverterTypeEnumWithData.checkType(en);
|
||||
const result = UniFFIScaffolding.callSync(
|
||||
160, // uniffi_uniffi_bindings_tests_external_types_fn_func_roundtrip_ext_enum
|
||||
FfiConverterTypeEnumWithData.lower(en),
|
||||
)
|
||||
return handleRustResult(
|
||||
result,
|
||||
FfiConverterTypeEnumWithData.lift.bind(FfiConverterTypeEnumWithData),
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* roundtripExtInterface
|
||||
*/
|
||||
export function roundtripExtInterface(
|
||||
int) {
|
||||
|
||||
FfiConverterTypeTestInterface.checkType(int);
|
||||
const result = UniFFIScaffolding.callSync(
|
||||
161, // uniffi_uniffi_bindings_tests_external_types_fn_func_roundtrip_ext_interface
|
||||
FfiConverterTypeTestInterface.lower(int),
|
||||
)
|
||||
return handleRustResult(
|
||||
result,
|
||||
FfiConverterTypeTestInterface.lift.bind(FfiConverterTypeTestInterface),
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* roundtripExtRecord
|
||||
*/
|
||||
export function roundtripExtRecord(
|
||||
rec) {
|
||||
|
||||
FfiConverterTypeSimpleRec.checkType(rec);
|
||||
const result = UniFFIScaffolding.callSync(
|
||||
162, // uniffi_uniffi_bindings_tests_external_types_fn_func_roundtrip_ext_record
|
||||
FfiConverterTypeSimpleRec.lower(rec),
|
||||
)
|
||||
return handleRustResult(
|
||||
result,
|
||||
FfiConverterTypeSimpleRec.lift.bind(FfiConverterTypeSimpleRec),
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
import {
|
||||
FfiConverterTypeHandle,
|
||||
} from "./RustUniffiBindingsTests.sys.mjs";
|
||||
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export { FfiConverterTypeHandle };
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export class FfiConverterUInt64 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()
|
||||
}
|
||||
}
|
||||
import {
|
||||
FfiConverterTypeEnumWithData,
|
||||
} from "./RustUniffiBindingsTests.sys.mjs";
|
||||
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export { FfiConverterTypeEnumWithData };
|
||||
import {
|
||||
FfiConverterTypeTestInterface,
|
||||
} from "./RustUniffiBindingsTests.sys.mjs";
|
||||
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export { FfiConverterTypeTestInterface };
|
||||
import {
|
||||
FfiConverterTypeSimpleRec,
|
||||
} from "./RustUniffiBindingsTests.sys.mjs";
|
||||
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export { FfiConverterTypeSimpleRec };
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export class FfiConverterUInt8 extends FfiConverter {
|
||||
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 < 0 || value > 256) {
|
||||
throw new UniFFITypeError(`${value} exceeds the U8 bounds`);
|
||||
if (value < -2147483648 || value > 2147483647) {
|
||||
throw new UniFFITypeError(`${value} exceeds the I32 bounds`);
|
||||
}
|
||||
}
|
||||
static computeSize(_value) {
|
||||
return 1;
|
||||
return 4;
|
||||
}
|
||||
static lift(value) {
|
||||
return value;
|
||||
@@ -424,9 +306,141 @@ export class FfiConverterUInt8 extends FfiConverter {
|
||||
return value;
|
||||
}
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeUint8(value)
|
||||
dataStream.writeInt32(value)
|
||||
}
|
||||
static read(dataStream) {
|
||||
return dataStream.readUint8()
|
||||
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 decoder = new TextDecoder();
|
||||
const utf8Arr = new Uint8Array(buf);
|
||||
return decoder.decode(utf8Arr);
|
||||
}
|
||||
static lower(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(value).buffer;
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeString(value);
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return dataStream.readString();
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return 4 + encoder.encode(value).length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SingletonObject
|
||||
*/
|
||||
export class SingletonObject {
|
||||
// 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];
|
||||
}
|
||||
|
||||
/**
|
||||
* method
|
||||
*/
|
||||
method() {
|
||||
const liftResult = (result) => undefined;
|
||||
const liftError = null;
|
||||
const functionCall = () => {
|
||||
return UniFFIScaffolding.callSync(
|
||||
153, // refcounts:uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method
|
||||
FfiConverterTypeSingletonObject.lower(this),
|
||||
)
|
||||
}
|
||||
return handleRustResult(functionCall(), liftResult, liftError);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Export the FFIConverter object to make external types work.
|
||||
export class FfiConverterTypeSingletonObject extends FfiConverter {
|
||||
static lift(value) {
|
||||
const opts = {};
|
||||
opts[constructUniffiObject] = value;
|
||||
return new SingletonObject(opts);
|
||||
}
|
||||
|
||||
static lower(value) {
|
||||
const ptr = value[uniffiObjectPtr];
|
||||
if (!(ptr instanceof UniFFIPointer)) {
|
||||
throw new UniFFITypeError("Object is not a 'SingletonObject' instance");
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return this.lift(dataStream.readPointerSingletonObject());
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writePointerSingletonObject(value[uniffiObjectPtr]);
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* getJsRefcount
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getJsRefcount() {
|
||||
|
||||
const liftResult = (result) => FfiConverterI32.lift(result);
|
||||
const liftError = null;
|
||||
const functionCall = () => {
|
||||
return UniFFIScaffolding.callSync(
|
||||
151, // refcounts:uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount
|
||||
)
|
||||
}
|
||||
return handleRustResult(functionCall(), liftResult, liftError);
|
||||
}
|
||||
|
||||
/**
|
||||
* getSingleton
|
||||
* @returns {SingletonObject}
|
||||
*/
|
||||
export function getSingleton() {
|
||||
|
||||
const liftResult = (result) => FfiConverterTypeSingletonObject.lift(result);
|
||||
const liftError = null;
|
||||
const functionCall = () => {
|
||||
return UniFFIScaffolding.callSync(
|
||||
152, // refcounts:uniffi_uniffi_fixture_refcounts_fn_func_get_singleton
|
||||
)
|
||||
}
|
||||
return handleRustResult(functionCall(), liftResult, liftError);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,768 @@
|
||||
// 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 = {};
|
||||
|
||||
// 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) {
|
||||
const encoder = new TextEncoder();
|
||||
// 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 = 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 decoder = new TextDecoder();
|
||||
const size = this.readUint32();
|
||||
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
|
||||
const value = 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 = 20; // 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 = 20; // 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 decoder = new TextDecoder();
|
||||
const utf8Arr = new Uint8Array(buf);
|
||||
return decoder.decode(utf8Arr);
|
||||
}
|
||||
static lower(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(value).buffer;
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeString(value);
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return dataStream.readString();
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return 4 + 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(
|
||||
218, // 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(
|
||||
219, // 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(
|
||||
215, // 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(
|
||||
216, // 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(
|
||||
217, // 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(
|
||||
214, // 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)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,465 @@
|
||||
// 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 = {};
|
||||
|
||||
// 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) {
|
||||
const encoder = new TextEncoder();
|
||||
// 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 = 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 decoder = new TextDecoder();
|
||||
const size = this.readUint32();
|
||||
const source = new Uint8Array(this.dataView.buffer, this.pos, size)
|
||||
const value = 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 = 22; // 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 = 22; // 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 decoder = new TextDecoder();
|
||||
const utf8Arr = new Uint8Array(buf);
|
||||
return decoder.decode(utf8Arr);
|
||||
}
|
||||
static lower(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(value).buffer;
|
||||
}
|
||||
|
||||
static write(dataStream, value) {
|
||||
dataStream.writeString(value);
|
||||
}
|
||||
|
||||
static read(dataStream) {
|
||||
return dataStream.readString();
|
||||
}
|
||||
|
||||
static computeSize(value) {
|
||||
const encoder = new TextEncoder();
|
||||
return 4 + 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(
|
||||
237, // 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(
|
||||
235, // 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(
|
||||
236, // uniffi_trait_interfaces:uniffi_uniffi_trait_interfaces_fn_func_make_calculator
|
||||
)
|
||||
}
|
||||
return handleRustResult(functionCall(), liftResult, liftError);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[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"] }
|
||||
@@ -0,0 +1,49 @@
|
||||
/* 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");
|
||||
@@ -0,0 +1,10 @@
|
||||
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 )
|
||||
@@ -0,0 +1,10 @@
|
||||
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
|
||||
@@ -0,0 +1,16 @@
|
||||
# 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?
|
||||
@@ -0,0 +1,10 @@
|
||||
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)
|
||||
@@ -0,0 +1,6 @@
|
||||
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",
|
||||
);
|
||||
15
toolkit/components/uniffi-bindgen-gecko-js/fixtures/lib.rs
Normal file
15
toolkit/components/uniffi-bindgen-gecko-js/fixtures/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
// 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!();
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- 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"]
|
||||
@@ -0,0 +1,12 @@
|
||||
[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"] }
|
||||
@@ -0,0 +1,7 @@
|
||||
/* 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/refcounts.udl").unwrap();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* 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"));
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace refcounts {
|
||||
SingletonObject get_singleton();
|
||||
i32 get_js_refcount();
|
||||
};
|
||||
|
||||
interface SingletonObject {
|
||||
void method();
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
[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"] }
|
||||
@@ -0,0 +1,7 @@
|
||||
/* 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();
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/* 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");
|
||||
@@ -0,0 +1,146 @@
|
||||
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;
|
||||
};
|
||||
@@ -0,0 +1,250 @@
|
||||
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()
|
||||
@@ -0,0 +1,183 @@
|
||||
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,
|
||||
)
|
||||
@@ -0,0 +1,147 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,232 @@
|
||||
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)
|
||||
@@ -0,0 +1,6 @@
|
||||
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",
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
[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"] }
|
||||
@@ -0,0 +1,69 @@
|
||||
/* 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(¤t_position, direction)
|
||||
}
|
||||
}
|
||||
|
||||
uniffi::setup_scaffolding!("sprites");
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
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);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
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) )
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
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)
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,16 @@
|
||||
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) )
|
||||
@@ -0,0 +1,6 @@
|
||||
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",
|
||||
);
|
||||
@@ -0,0 +1,48 @@
|
||||
/* 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)));
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
/* 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"]);
|
||||
});
|
||||
@@ -0,0 +1,174 @@
|
||||
/* 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"
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
/* 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 }));
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
/* 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));
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user