Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate net48 compliant bindings #68

Merged
merged 1 commit into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,26 @@ Generates bindings file `path/to/definitions.cs`
# How to integrate bindings

To integrate the bindings into your projects, simply add the generated bindings file to your project.
There are a couple of requirements to compile the generated bindings file:
There are a few requirements depending on your target framework version.

- .NET core `6.0` or higher
- allow `unsafe` code
- allow `Nullable`

```xml
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
</PropertyGroup>
```
```xml
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
```

- .NET framework `4.8`
```xml
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<LangVersion>10.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageReference Include="IsExternalInit" Version="1.0.3"/>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</PropertyGroup>
```

# Unsupported features

Expand Down
9 changes: 7 additions & 2 deletions bindgen/templates/BigEndianStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ public void WriteInt(int value) {
}

public void WriteFloat(float value) {
WriteInt(BitConverter.SingleToInt32Bits(value));
unsafe {
WriteInt(*((int*)&value));
}
}

public void WriteLong(long value) {
Expand Down Expand Up @@ -116,7 +118,10 @@ public int ReadInt() {
}

public float ReadFloat() {
return BitConverter.Int32BitsToSingle(ReadInt());
unsafe {
int value = ReadInt();
return *((float*)&value);
}
}

public long ReadLong() {
Expand Down
3 changes: 2 additions & 1 deletion bindgen/templates/CallbackInterfaceRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ public bool Remove(ulong handle, out T result) {
lock (lock_) {
// Possible null reference assignment
#pragma warning disable 8601
if (leftMap.Remove(handle, out result)) {
if (leftMap.TryGetValue(handle, out result)) {
#pragma warning restore 8601
leftMap.Remove(handle);
rightMap.Remove(result);
return true;
} else {
Expand Down
7 changes: 5 additions & 2 deletions bindgen/templates/TimestampHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ class FfiConverterTimestamp: FfiConverterRustBuffer<DateTime> {
// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs
private const uint NanosecondsPerTick = 100;

// DateTime.UnixEpoch is not available in net48
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

public override DateTime Read(BigEndianStream stream) {
var seconds = stream.ReadLong();
var nanoseconds = stream.ReadUInt();
Expand All @@ -17,7 +20,7 @@ public override DateTime Read(BigEndianStream stream) {
}
var ticks = seconds * TimeSpan.TicksPerSecond;
ticks += (nanoseconds / NanosecondsPerTick) * sign;
return DateTime.UnixEpoch.AddTicks(ticks);
return UnixEpoch.AddTicks(ticks);
}

public override int AllocationSize(DateTime value) {
Expand All @@ -26,7 +29,7 @@ public override int AllocationSize(DateTime value) {
}

public override void Write(DateTime value, BigEndianStream stream) {
var epochOffset = value.Subtract(DateTime.UnixEpoch);
var epochOffset = value.Subtract(UnixEpoch);

int sign = 1;
if (epochOffset.Ticks < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ public void OptionalParameter_CanBeSpecified()
string message = Hello(person);
Assert.Equal("Hello John Connor!", message);
}
}
}
98 changes: 98 additions & 0 deletions dotnet-tests/UniffiCS.BindingTests/TestNumericLimits.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using uniffi.stringify;

namespace UniffiCS.BindingTests;

public class TestNumericLimits
{
[Fact]
public void NumericLimitsAreTheSame()
{
// At first, I tried to write this test by stringifying values in C# and Rust, then
// comparing the stringified result. Turns out C# and Rust format floating point values
// differently, so comparing the result is useless. I tried to change the formatting
// settings in few ways, but I couldn't find formatting settings that would produce
// exact results.

var meanValue = 0x1234_5678_9123_4567;

ParseTest<sbyte>(
StringifyMethods.ParseI8,
SByte.MinValue,
SByte.MaxValue,
(sbyte)meanValue);

ParseTest<short>(
StringifyMethods.ParseI16,
Int16.MinValue,
Int16.MaxValue,
(short)meanValue);

ParseTest<int>(
StringifyMethods.ParseI32,
Int32.MinValue,
Int32.MaxValue,
(int)meanValue);

ParseTest<long>(
StringifyMethods.ParseI64,
Int64.MinValue,
Int64.MaxValue,
(long)meanValue);

ParseTest<byte>(
StringifyMethods.ParseU8,
Byte.MinValue,
Byte.MaxValue,
(byte)meanValue);

ParseTest<ushort>(
StringifyMethods.ParseU16,
UInt16.MinValue,
UInt16.MaxValue,
(ushort)meanValue);

ParseTest<uint>(
StringifyMethods.ParseU32,
UInt32.MinValue,
UInt32.MaxValue,
(uint)meanValue);

ParseTest<ulong>(
StringifyMethods.ParseU64,
UInt64.MinValue,
UInt64.MaxValue,
(ulong)meanValue);

ParseTest<float>(
StringifyMethods.ParseF32,
Single.MinValue,
Single.MaxValue,
Single.Epsilon);

ParseTest<double>(
StringifyMethods.ParseF64,
Double.MinValue,
Double.MaxValue,
Double.Epsilon);
}

static void ParseTest<T>(
Func<String, T> parseMethod,
T minValue,
T maxValue,
T meanValue
)
{
// Possible null reference assignment
#pragma warning disable 8602
#pragma warning disable 8604
Assert.Equal(minValue, parseMethod(minValue.ToString()));
Assert.Equal(maxValue, parseMethod(maxValue.ToString()));
Assert.Equal(meanValue, parseMethod(meanValue.ToString()));
#pragma warning restore 8602
#pragma warning restore 8604
}
}
6 changes: 4 additions & 2 deletions dotnet-tests/UniffiCS/UniffiCS.csproj
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
</PropertyGroup>

<!-- Allow testing internals of generated code -->
<ItemGroup>
<InternalsVisibleTo Include="UniffiCS.BindingTests" />
<PackageReference Include="IsExternalInit" Version="1.0.3"/>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions fixtures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ global-methods-class-name = { path = "global-methods-class-name" }
uniffi-cs-custom-types-builtin = { path = "custom-types-builtin" }
uniffi-cs-disposable-fixture = { path = "disposable" }
uniffi-cs-optional-parameters-fixture = { path = "optional-parameters" }
uniffi-cs-stringify = { path = "stringify" }
uniffi-example-arithmetic = { path = "../3rd-party/uniffi-rs/examples/arithmetic" }
uniffi-example-callbacks = { path = "../3rd-party/uniffi-rs/examples/callbacks" }
uniffi-example-custom-types = { path = "../3rd-party/uniffi-rs/examples/custom-types" }
Expand Down
1 change: 1 addition & 0 deletions fixtures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ mod uniffi_fixtures {
uniffi_cs_custom_types_builtin::uniffi_reexport_scaffolding!();
uniffi_cs_disposable::uniffi_reexport_scaffolding!();
uniffi_cs_optional_parameters::uniffi_reexport_scaffolding!();
stringify::uniffi_reexport_scaffolding!();
}
17 changes: 17 additions & 0 deletions fixtures/stringify/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "uniffi-cs-stringify"
version = "1.0.0"
edition = "2021"
publish = false

[lib]
crate-type = ["lib", "cdylib"]
name = "stringify"

[dependencies]
paste = "1.0"
uniffi = {path = "../../3rd-party/uniffi-rs/uniffi", features=["build"]}
uniffi_macros = {path = "../../3rd-party/uniffi-rs/uniffi_macros"}

[build-dependencies]
uniffi = {path = "../../3rd-party/uniffi-rs/uniffi", features=["bindgen-tests"]}
34 changes: 34 additions & 0 deletions fixtures/stringify/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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/. */

use paste::paste;

uniffi::setup_scaffolding!();

macro_rules! define_parse_function {
($type:ty) => {
paste! {
#[uniffi::export]
fn [<to_string_ $type>](value: $type) -> String {
value.to_string()
}
arg0d marked this conversation as resolved.
Show resolved Hide resolved

#[uniffi::export]
fn [<parse_ $type>](value: String) -> $type {
value.parse::<$type>().unwrap()
}
}
};
}

define_parse_function!(i8);
define_parse_function!(i16);
define_parse_function!(i32);
define_parse_function!(i64);
define_parse_function!(u8);
define_parse_function!(u16);
define_parse_function!(u32);
define_parse_function!(u64);
define_parse_function!(f32);
define_parse_function!(f64);
Loading