Skip to content

Commit

Permalink
fix: MoonBit update and refactor organization (#1069)
Browse files Browse the repository at this point in the history
* feat(moonbit): reorganize directory and add exportation appointment

* fix(moonbit): api change

* doc(moonbit): update readme

* fix(moonbit): add more reserved words

* fix(moonbit): ignore more stubs
  • Loading branch information
peter-jerry-ye authored Oct 28, 2024
1 parent f2f444d commit 20d33b5
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 42 deletions.
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

0 comments on commit 20d33b5

Please sign in to comment.