Skip to content

Commit

Permalink
add c# runtime test for numbers
Browse files Browse the repository at this point in the history
add features for the 2 dotnet runtime flavours, naot and mono
  • Loading branch information
yowl committed Nov 1, 2023
1 parent bfd57f9 commit dadf8ab
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 60 deletions.
25 changes: 24 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ jobs:
echo "WASI_SDK_PATH=`pwd`/wasi-sdk-16.0" >> $GITHUB_ENV
if : matrix.os == 'windows-latest'
- run: |
curl -O https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1
powershell -File dotnet-install.ps1 -Channel 8.0.1xx -Verbose
echo DOTNET_ROOT=$LOCALAPPDATA'\Microsoft\dotnet' >> $GITHUB_ENV
export DOTNET_ROOT=$LOCALAPPDATA\\Microsoft\\dotnet
echo $LOCALAPPDATA'\Microsoft\dotnet' >> $GITHUB_PATH
echo $LOCALAPPDATA'\Microsoft\dotnet\tools' >> $GITHUB_PATH
$LOCALAPPDATA/Microsoft/dotnet/dotnet --info
echo nativeaot-llvm requires emscripten for its version of clang as wasi-sdk 20 does not work see https://github.com/WebAssembly/wasi-sdk/issues/326
curl -OL https://github.com/emscripten-core/emsdk/archive/refs/heads/main.zip
unzip main.zip
cd emsdk-main
./emsdk.bat install 3.1.23
./emsdk.bat activate 3.1.23
if : matrix.os == 'windows-latest'
- run: ci/download-teavm.sh

- uses: actions/setup-node@v2
Expand All @@ -62,7 +78,14 @@ jobs:
- uses: acifani/setup-tinygo@v1
with:
tinygo-version: 0.30.0
- run: cargo test --workspace
- name: All but Windows, cargo test --workspace
if : matrix.os != 'windows-latest'
run: cargo test --workspace
- name: Windows, set EMSDK and run cargo test
if : matrix.os == 'windows-latest'
run: |
source ./emsdk-main/emsdk_env.sh
cargo test --workspace
- run: cargo build
- run: cargo build --no-default-features
- run: cargo build --no-default-features --features rust
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,16 @@ default = [
'markdown',
'teavm-java',
'go',
'csharp',
'csharp-naot',
]
c = ['dep:wit-bindgen-c']
rust = ['dep:wit-bindgen-rust']
markdown = ['dep:wit-bindgen-markdown']
teavm-java = ['dep:wit-bindgen-teavm-java']
go = ['dep:wit-bindgen-go']
csharp = ['dep:wit-bindgen-csharp']
csharp-naot = ['csharp']
csharp-mono = ['csharp']

[dev-dependencies]
heck = { workspace = true }
Expand Down
78 changes: 30 additions & 48 deletions crates/csharp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1341,28 +1341,26 @@ impl Bindgen for FunctionBindgen<'_, '_> {
Instruction::F32Store { .. } => todo!("F32Store"),
Instruction::F64Store { .. } => todo!("F64Store"),

Instruction::I64FromU64 => results.push(format!("(long)({})", operands[0].clone())),

//This is handled in the C interface, so we just pass the value as is.
Instruction::I32FromChar
| Instruction::I64FromS64
| Instruction::I32FromU32
| Instruction::I32FromS32
Instruction::I64FromU64 => results.push(format!("unchecked((long)({}))", operands[0])),
Instruction::I32FromChar => results.push(format!("((int){})", operands[0])),
Instruction::I32FromU32 => results.push(format!("unchecked((int)({}))", operands[0])),
Instruction::U8FromI32 => results.push(format!("((byte){})", operands[0])),
Instruction::S8FromI32 => results.push(format!("((sbyte){})", operands[0])),
Instruction::U16FromI32 => results.push(format!("((ushort){})", operands[0])),
Instruction::S16FromI32 => results.push(format!("((short){})", operands[0])),
Instruction::U32FromI32 => results.push(format!("unchecked((uint)({}))", operands[0])),
Instruction::U64FromI64 => results.push(format!("unchecked((ulong)({}))", operands[0])),
Instruction::CharFromI32 => results.push(format!("unchecked((uint)({}))", operands[0])),
Instruction::I64FromS64
| Instruction::I32FromU16
| Instruction::I32FromS16
| Instruction::I32FromU8
| Instruction::I32FromS8
| Instruction::I32FromS32
| Instruction::F32FromFloat32
| Instruction::F64FromFloat64
| Instruction::S8FromI32
| Instruction::U8FromI32
| Instruction::S16FromI32
| Instruction::U16FromI32
| Instruction::S32FromI32
| Instruction::U32FromI32
| Instruction::S64FromI64
| Instruction::U64FromI64
| Instruction::CharFromI32
| Instruction::Float32FromF32
| Instruction::Float64FromF64 => results.push(operands[0].clone()),

Expand Down Expand Up @@ -1495,22 +1493,10 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let module = self.gen.name.to_upper_camel_case();
let func_name = self.func_name.to_upper_camel_case();
let class_name = CSharp::get_class_name_from_qualified_name(module);

let params_cast = func
.params
.iter()
.map(|(_, ty)| {
let ty = self.gen.type_name(ty);

format!("{ty}")
})
.collect::<Vec<String>>();

let mut oper = String::new();

for (i, param) in operands.iter().enumerate() {
let cast = params_cast.get(i).unwrap();
oper.push_str(&format!("({cast}){param}"));
oper.push_str(&format!("({param})"));

if i < operands.len() && operands.len() != i + 1 {
oper.push_str(", ");
Expand All @@ -1526,28 +1512,24 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
}

Instruction::Return { amt, func } => {
let sig = self
.gen
.resolve()
.wasm_signature(AbiVariant::GuestExport, func);

let cast = sig
.results
.into_iter()
.map(|ty| wasm_type(ty))
.collect::<Vec<&str>>()
.join(", ");

match *amt {
0 => (),
1 => uwriteln!(self.src, "return {};", operands[0]),
_ => {
let results = operands.join(", ");
uwriteln!(self.src, "return ({cast})({results});")
}
Instruction::Return { amt, func } => match func.results.len() {
0 => (),
1 => uwriteln!(self.src, "return {};", operands[0]),
_ => {
let results = operands.join(", ");
let sig = self
.gen
.resolve()
.wasm_signature(AbiVariant::GuestExport, func);
let cast = sig
.results
.into_iter()
.map(|ty| wasm_type(ty))
.collect::<Vec<&str>>()
.join(", ");
uwriteln!(self.src, "return ({cast})({results});")
}
}
},

Instruction::Malloc { .. } => unimplemented!(),

Expand Down
33 changes: 23 additions & 10 deletions tests/runtime/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use anyhow::Result;
use heck::ToUpperCamelCase;

use std::borrow::Cow;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};
use wasm_encoder::{Encode, Section};
use wasmtime::component::{Component, Instance, Linker};
use wasmtime::{Config, Engine, Store};
Expand Down Expand Up @@ -115,15 +115,19 @@ fn tests(name: &str, dir_name: &str) -> Result<Vec<PathBuf>> {
let mut c = Vec::new();
let mut java = Vec::new();
let mut go = Vec::new();
let mut c_sharp = Vec::new();
let mut c_sharp: Vec<PathBuf> = Vec::new();
for file in dir.read_dir()? {
let path = file?.path();
match path.extension().and_then(|s| s.to_str()) {
Some("c") => c.push(path),
Some("java") => java.push(path),
Some("rs") => rust.push(path),
Some("go") => go.push(path),
Some("cs") => c_sharp.push(path),
Some("cs") => {
if cfg!(windows) {
c_sharp.push(path);
}
}
_ => {}
}
}
Expand All @@ -135,7 +139,6 @@ fn tests(name: &str, dir_name: &str) -> Result<Vec<PathBuf>> {
out_dir.push("runtime-tests");
out_dir.push(name);

println!("wasi adapter = {:?}", test_artifacts::ADAPTER);
let wasi_adapter = std::fs::read(&test_artifacts::ADAPTER)?;

drop(std::fs::remove_dir_all(&out_dir));
Expand Down Expand Up @@ -474,9 +477,13 @@ fn tests(name: &str, dir_name: &str) -> Result<Vec<PathBuf>> {
result.push(component_path);
}

#[cfg(feature = "csharp")]
#[cfg(feature = "csharp-mono")]
for path in c_sharp.iter() {
todo!()
}

#[cfg(feature = "csharp-naot")]
for path in c_sharp.iter() {
println!("running for {}", path.display());
let world_name = &resolve.worlds[world].name;
let out_dir = out_dir.join(format!("csharp-{}", world_name));
drop(fs::remove_dir_all(&out_dir));
Expand Down Expand Up @@ -637,12 +644,20 @@ fn tests(name: &str, dir_name: &str) -> Result<Vec<PathBuf>> {
let file_name = path.file_name().unwrap();
fs::copy(path, out_dir.join(file_name.to_str().unwrap()))?;

let mut cmd = Command::new("dotnet");
let dotnet_root_env = "DOTNET_ROOT";
let dotnet_cmd: PathBuf;
match env::var(dotnet_root_env) {
Ok(val) => dotnet_cmd = Path::new(&val).join("dotnet"),
Err(_e) => dotnet_cmd = "dotnet".into(),
}

let mut cmd = Command::new(dotnet_cmd);
let mut wasm_filename = out_wasm.join(assembly_name);
wasm_filename.set_extension("wasm");

cmd.current_dir(&out_dir);

// add .arg("/bl") to diagnose dotnet build problems
cmd.arg("publish")
.arg(out_dir.join(format!("{camel}.csproj")))
.arg("-r")
Expand All @@ -653,14 +668,12 @@ fn tests(name: &str, dir_name: &str) -> Result<Vec<PathBuf>> {
.arg("/p:MSBuildEnableWorkloadResolver=false")
.arg("--self-contained")
.arg("/p:UseAppHost=false")
.arg("/bl")
.arg("-o")
.arg(&out_wasm);
let output = match cmd.output() {
Ok(output) => output,
Err(e) => panic!("failed to spawn compiler: {}", e),
};
println!("{:?} completed", cmd);

if !output.status.success() {
println!("status: {}", output.status);
Expand Down
133 changes: 133 additions & 0 deletions tests/runtime/numbers/wasm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using wit_numbers.Wit.imports.test.numbers.Test;

namespace wit_numbers;

public class NumbersWorldImpl : NumbersWorld
{
public static void TestImports()
{
Debug.Assert(TestInterop.RoundtripU8(1) == 1);
Debug.Assert(TestInterop.RoundtripU8(0) == 0);
Debug.Assert(TestInterop.RoundtripU8(Byte.MaxValue) == Byte.MaxValue);

Debug.Assert(TestInterop.RoundtripS8(1) == 1);
Debug.Assert(TestInterop.RoundtripS8(SByte.MinValue) == SByte.MinValue);
Debug.Assert(TestInterop.RoundtripS8(SByte.MaxValue) == SByte.MaxValue);

Debug.Assert(TestInterop.RoundtripU16(1) == 1);
Debug.Assert(TestInterop.RoundtripU16(0) == 0);
Debug.Assert(TestInterop.RoundtripU16(UInt16.MaxValue) == UInt16.MaxValue);

Debug.Assert(TestInterop.RoundtripS16(1) == 1);
Debug.Assert(TestInterop.RoundtripS16(Int16.MinValue) == Int16.MinValue);
Debug.Assert(TestInterop.RoundtripS16(Int16.MaxValue) == Int16.MaxValue);

Debug.Assert(TestInterop.RoundtripU32(1) == 1);
Debug.Assert(TestInterop.RoundtripU32(0) == 0);
Debug.Assert(TestInterop.RoundtripU32(UInt32.MaxValue) == UInt32.MaxValue);

Debug.Assert(TestInterop.RoundtripS32(1) == 1);
Debug.Assert(TestInterop.RoundtripS32(Int32.MinValue) == Int32.MinValue);
Debug.Assert(TestInterop.RoundtripS32(Int32.MaxValue) == Int32.MaxValue);

Debug.Assert(TestInterop.RoundtripU64(1) == 1);
Debug.Assert(TestInterop.RoundtripU64(0) == 0);
Debug.Assert(TestInterop.RoundtripU64(UInt64.MaxValue) == UInt64.MaxValue);

Debug.Assert(TestInterop.RoundtripS64(1) == 1);
Debug.Assert(TestInterop.RoundtripS64(Int64.MinValue) == Int64.MinValue);
Debug.Assert(TestInterop.RoundtripS64(Int64.MaxValue) == Int64.MaxValue);

Debug.Assert(TestInterop.RoundtripFloat32(1.0f) == 1.0f);
Debug.Assert(TestInterop.RoundtripFloat32(Single.PositiveInfinity) == Single.PositiveInfinity);
Debug.Assert(TestInterop.RoundtripFloat32(Single.NegativeInfinity) == Single.NegativeInfinity);
Debug.Assert(float.IsNaN(TestInterop.RoundtripFloat32(Single.NaN)));

Debug.Assert(TestInterop.RoundtripFloat64(1.0) == 1.0);
Debug.Assert(TestInterop.RoundtripFloat64(Double.PositiveInfinity) == Double.PositiveInfinity);
Debug.Assert(TestInterop.RoundtripFloat64(Double.NegativeInfinity) == Double.NegativeInfinity);
Debug.Assert(double.IsNaN(TestInterop.RoundtripFloat64(Double.NaN)));

Debug.Assert(TestInterop.RoundtripChar('a') == 'a');
Debug.Assert(TestInterop.RoundtripChar(' ') == ' ');
Debug.Assert(Char.ConvertFromUtf32((int)TestInterop.RoundtripChar((uint)Char.ConvertToUtf32("🚩", 0))) == "🚩"); // This is 2 chars long as it contains a surrogate pair

TestInterop.SetScalar(2);
Debug.Assert(TestInterop.GetScalar() == 2);
TestInterop.SetScalar(4);
Debug.Assert(TestInterop.GetScalar() == 4);
}
}

public class TestImpl : wit_numbers.Wit.exports.test.numbers.Test.Test
{
static uint SCALAR = 0;

public static byte RoundtripU8(byte p0)
{
return p0;
}

public static sbyte RoundtripS8(sbyte p0)
{
return p0;
}

public static ushort RoundtripU16(ushort p0)
{
return p0;
}

public static short RoundtripS16(short p0)
{
return p0;
}

public static uint RoundtripU32(uint p0)
{
return p0;
}

public static int RoundtripS32(int p0)
{
return p0;
}

public static ulong RoundtripU64(ulong p0)
{
return p0;
}

public static long RoundtripS64(long p0)
{
return p0;
}

public static float RoundtripFloat32(float p0)
{
return p0;
}

public static double RoundtripFloat64(double p0)
{
return p0;
}

public static uint RoundtripChar(uint p0)
{
return p0;
}

public static void SetScalar(uint p0)
{
SCALAR = p0;
}

public static uint GetScalar()
{
return SCALAR;
}
}

0 comments on commit dadf8ab

Please sign in to comment.