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

fix: MoonBit update and refactor organization #1069

Merged
merged 5 commits into from
Oct 28, 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
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,19 +384,26 @@ wasm-tools validate main.component.wasm --features component-model
MoonBit can be compiled to WebAssembly using [its toolchain](https://moonbitlang.com/download):

```sh
moon build --target wasm # -g to keep symbols
moon build --target wasm # --debug to keep symbols
```

The generarted core wasm will be found under `target/wasm/release/build/gen/gen.wasm` by default. Then you can use `wasm-tools` to componentize the module:
The generated core wasm will be found under `target/wasm/release/build/gen/gen.wasm` by default. Then you can use `wasm-tools` to componentize the module:

```
wasm-tools component embed wit target/wasm/release/build/gen/gen.wasm -o target/gen.wasm
wasm-tools component new target/gen.wasm -o target/gen.component.wasm
```

When using `wit-bindgen moonbit`, you may use `--derive-show` or `--derive-eq` to derive `Show` or `Eq` for all types.
You may use `--gen-dir` to specify which package should be responsible for the exportation. The default is `gen` as mentioned above.
This can be useful having one project that exports multiple worlds.

When using `wit-bindgen moonbit`, you may use `--derive-show` or `--derive-eq` to derive `Show` or `Eq` traits for all types.
You may also use `--derive-error`, which will make types containing `Error` as error types in MoonBit.

You will find the files to be modified with the name `**/stub.mbt`.
To avoid touching the files during regeneration (including `moon.pkg.json` or `moon.mod.json`) you may use `--ignore-stub`.

/!\ MoonBit is still evolving, so please check out the [Weekly Updates](https://www.moonbitlang.com/weekly-updates/) for any breaking changes or deprecations.

### Guest: Other Languages

Expand Down
98 changes: 59 additions & 39 deletions crates/moonbit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ use wit_bindgen_core::{
// TODO: Export will share the type signatures with the import by using a newtype alias
// TODO: Export resource is not handled correctly : resource.new / resource.drop / resource.rep / dtor

const EXPORT_DIR: &str = "gen";

const FFI_DIR: &str = "ffi";

const FFI: &str = r#"
Expand Down Expand Up @@ -90,7 +88,7 @@ pub fn malloc(size : Int) -> Int {
let words = size / 4 + 1
let address = malloc_inline(8 + words * 4)
store32(address, 1)
store32(address + 4, words.lsl(8).lor(246))
store32(address + 4, (words << 8) | 246)
store8(address + words * 4 + 7, 3 - size % 4)
address + 8
}
Expand Down Expand Up @@ -175,7 +173,7 @@ pub fn ptr2double_array(ptr : Int) -> FixedArray[Double] {

fn set_64_header_ffi(offset : Int) -> Unit {
let len = load32(offset)
store32(offset, len.lsr(1))
store32(offset, len >> 1)
store8(offset, 241)
}

Expand All @@ -202,6 +200,9 @@ pub struct Opts {
/// Whether or not to generate stub files ; useful for update after WIT change
#[cfg_attr(feature = "clap", arg(long, default_value_t = false))]
pub ignore_stub: bool,
/// The package/dir to generate the program entrance
#[cfg_attr(feature = "clap", arg(long, default_value = "gen"))]
pub gen_dir: String,
}

impl Opts {
Expand Down Expand Up @@ -281,7 +282,7 @@ impl WorldGenerator for MoonBit {
id: InterfaceId,
files: &mut Files,
) -> Result<()> {
let name = interface_name(resolve, key, Direction::Import);
let name = interface_name(resolve, key);
let name = self.interface_ns.tmp(&name);
self.import_interface_names.insert(id, name.clone());

Expand Down Expand Up @@ -331,7 +332,7 @@ impl WorldGenerator for MoonBit {
id: InterfaceId,
files: &mut Files,
) -> Result<()> {
let name = interface_name(resolve, key, Direction::Export);
let name = format!("{}.{}", self.opts.gen_dir, interface_name(resolve, key));
let name = self.interface_ns.tmp(&name);
self.export_interface_names.insert(id, name.clone());

Expand Down Expand Up @@ -363,7 +364,7 @@ impl WorldGenerator for MoonBit {
funcs: &[(&str, &Function)],
_files: &mut Files,
) -> Result<()> {
let name = world_name(resolve, world);
let name = format!("{}.{}", self.opts.gen_dir, world_name(resolve, world));
let mut gen = self.interface(resolve, &name, "$root", Direction::Export);

for (_, func) in funcs {
Expand Down Expand Up @@ -412,7 +413,7 @@ impl WorldGenerator for MoonBit {

let version = env!("CARGO_PKG_VERSION");

let mut generate_pkg_definition = |name: &str, files: &mut Files| {
let mut generate_pkg_definition = |name: &String, files: &mut Files| {
let directory = name.replace('.', "/");
let imports: Option<&mut Imports> = self.package_import.get_mut(name);
if let Some(imports) = imports {
Expand Down Expand Up @@ -472,7 +473,11 @@ impl WorldGenerator for MoonBit {

files.push(&format!("{directory}/top.mbt"), indent(&src).as_bytes());
if !self.opts.ignore_stub {
files.push(&format!("{directory}/stub.mbt"), indent(&stub).as_bytes());
files.push(
&format!("{}/{directory}/stub.mbt", self.opts.gen_dir),
indent(&stub).as_bytes(),
);
generate_pkg_definition(&format!("{}.{}", self.opts.gen_dir, name), files);
}

let generate_ffi =
Expand All @@ -488,7 +493,11 @@ impl WorldGenerator for MoonBit {
uwriteln!(&mut body, "{b}");

files.push(
&format!("{EXPORT_DIR}/{}_export.mbt", directory.to_snake_case()),
&format!(
"{}/{}_export.mbt",
self.opts.gen_dir,
directory.to_snake_case()
),
indent(&body).as_bytes(),
);
};
Expand Down Expand Up @@ -528,8 +537,8 @@ impl WorldGenerator for MoonBit {
files.push(&format!("{directory}/top.mbt"), indent(&src).as_bytes());
if !self.opts.ignore_stub {
files.push(&format!("{directory}/stub.mbt"), indent(&stub).as_bytes());
generate_pkg_definition(&name, files);
}
generate_pkg_definition(&name, files);
generate_ffi(directory, fragments, files);
}

Expand All @@ -541,12 +550,16 @@ impl WorldGenerator for MoonBit {
files.push(&format!("{FFI_DIR}/moon.pkg.json"), "{}".as_bytes());

// Export project files
let mut body = Source::default();
uwriteln!(&mut body, "{{ \"name\": \"{project_name}\" }}");
files.push(&format!("moon.mod.json"), body.as_bytes());
if !self.opts.ignore_stub {
let mut body = Source::default();
uwriteln!(&mut body, "{{ \"name\": \"{project_name}\" }}");
files.push(&format!("moon.mod.json"), body.as_bytes());
}

let export_dir = self.opts.gen_dir.clone();

// Export project entry point
let mut gen = self.interface(resolve, EXPORT_DIR, "", Direction::Export);
let mut gen = self.interface(resolve, &export_dir.as_str(), "", Direction::Export);
let ffi_qualifier = gen.qualify_package(&FFI_DIR.to_string());

let mut body = Source::default();
Expand Down Expand Up @@ -586,7 +599,10 @@ impl WorldGenerator for MoonBit {
self.return_area_size,
);
}
files.push(&format!("{EXPORT_DIR}/ffi.mbt"), indent(&body).as_bytes());
files.push(
&format!("{}/ffi.mbt", self.opts.gen_dir),
indent(&body).as_bytes(),
);
self.export
.insert("cabi_realloc".into(), "cabi_realloc".into());

Expand All @@ -612,7 +628,7 @@ impl WorldGenerator for MoonBit {
"#,
exports.join(", ")
);
if let Some(imports) = self.package_import.get_mut(EXPORT_DIR) {
if let Some(imports) = self.package_import.get_mut(&self.opts.gen_dir) {
let mut deps = imports
.packages
.iter()
Expand All @@ -635,7 +651,7 @@ impl WorldGenerator for MoonBit {
",
);
files.push(
&format!("{EXPORT_DIR}/moon.pkg.json"),
&format!("{}/moon.pkg.json", self.opts.gen_dir,),
indent(&body).as_bytes(),
);

Expand Down Expand Up @@ -830,9 +846,14 @@ impl InterfaceGenerator<'_> {

let export_name = func.legacy_core_export_name(interface_name);

let mut toplevel_generator =
self.gen
.interface(self.resolve, EXPORT_DIR, self.module, Direction::Export);
let export_dir = self.gen.opts.gen_dir.clone();

let mut toplevel_generator = self.gen.interface(
self.resolve,
export_dir.as_str(),
self.module,
Direction::Export,
);

let mut bindgen = FunctionBindgen::new(
&mut toplevel_generator,
Expand Down Expand Up @@ -1220,9 +1241,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {

let func_name = self.gen.export_ns.tmp(&format!("wasmExport{name}Dtor"));

let mut gen = self
.gen
.interface(self.resolve, EXPORT_DIR, "", Direction::Export);
let export_dir = self.gen.opts.gen_dir.clone();

let mut gen =
self.gen
.interface(self.resolve, export_dir.as_str(), "", Direction::Export);

uwrite!(
self.ffi,
Expand Down Expand Up @@ -1768,8 +1791,12 @@ impl Bindgen for FunctionBindgen<'_, '_> {
results.push(format!("({}).reinterpret_as_int()", operands[0]))
}

Instruction::U64FromI64 => results.push(format!("({}).to_uint64()", operands[0])),
Instruction::I64FromU64 => results.push(format!("({}).to_int64()", operands[0])),
Instruction::U64FromI64 => {
results.push(format!("({}).reinterpret_as_uint64()", operands[0]))
}
Instruction::I64FromU64 => {
results.push(format!("({}).reinterpret_as_int64()", operands[0]))
}

Instruction::I32FromBool => {
results.push(format!("(if {} {{ 1 }} else {{ 0 }})", operands[0]));
Expand Down Expand Up @@ -1833,7 +1860,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
Int::U64 => {
results.push(format!(
"{}(({}).reinterpret_as_uint().to_uint64().lor(({}).reinterpret_as_uint().to_uint64.lsl(32)))",
"{}(({}).reinterpret_as_uint().to_uint64() | (({}).reinterpret_as_uint().to_uint64() << 32))",
self.gen.type_name(&Type::Id(*ty), true),
operands[0],
operands[1]
Expand Down Expand Up @@ -2714,13 +2741,10 @@ fn indent(code: &str) -> Source {
}

fn world_name(resolve: &Resolve, world: WorldId) -> String {
format!(
"worlds.{}",
resolve.worlds[world].name.to_lower_camel_case()
)
format!("world.{}", resolve.worlds[world].name.to_lower_camel_case())
}

fn interface_name(resolve: &Resolve, name: &WorldKey, direction: Direction) -> String {
fn interface_name(resolve: &Resolve, name: &WorldKey) -> String {
let pkg = match name {
WorldKey::Name(_) => None,
WorldKey::Interface(id) => {
Expand All @@ -2736,11 +2760,7 @@ fn interface_name(resolve: &Resolve, name: &WorldKey, direction: Direction) -> S
.to_lower_camel_case();

format!(
"interface.{}.{}{name}",
match direction {
Direction::Import => "imports",
Direction::Export => "exports",
},
"interface.{}{name}",
if let Some(name) = &pkg {
format!(
"{}.{}.",
Expand All @@ -2764,7 +2784,7 @@ impl ToMoonBitIdent for str {
"continue" | "for" | "match" | "if" | "pub" | "priv" | "readonly" | "break"
| "raise" | "try" | "except" | "catch" | "else" | "enum" | "struct" | "type"
| "trait" | "return" | "let" | "mut" | "while" | "loop" | "extern" | "with"
| "throw" | "init" | "main" | "test" | "in" | "guard" => {
| "throw" | "init" | "main" | "test" | "in" | "guard" | "typealias" => {
format!("{self}_")
}
_ => self.to_snake_case(),
Expand All @@ -2782,7 +2802,7 @@ impl ToMoonBitTypeIdent for str {
match self.to_upper_camel_case().as_str() {
type_name @ ("Bool" | "Byte" | "Int" | "Int64" | "UInt" | "UInt64" | "Float"
| "Double" | "Error" | "Buffer" | "Bytes" | "Array" | "FixedArray"
| "Map" | "String" | "Option" | "Result" | "Char") => {
| "Map" | "String" | "Option" | "Result" | "Char" | "Json") => {
format!("{type_name}_")
}
type_name => type_name.to_owned(),
Expand Down
1 change: 1 addition & 0 deletions crates/moonbit/tests/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ macro_rules! codegen_test {
derive_eq: true,
derive_error: true,
ignore_stub: false,
gen_dir: "gen".to_string(),
}
.build()
.generate(resolve, world, files)
Expand Down
Loading