├── deno_bindgen_macro ├── tests │ └── fn │ │ ├── add.test.rs │ │ ├── pointer.test.rs │ │ ├── buffer.test.rs │ │ ├── add.test.out.rs │ │ ├── pointer.test.out.rs │ │ └── buffer.test.out.rs ├── src │ ├── struct_.rs │ ├── util.rs │ ├── lib.rs │ ├── impl_.rs │ └── fn_.rs └── Cargo.toml ├── .gitignore ├── .vscode └── settings.json ├── assets ├── logo.png ├── favicon.ico ├── illustration.png ├── release.sh └── logo.svg ├── .rustfmt.toml ├── example ├── lsusb.ts ├── Cargo.toml ├── README.md ├── lib.rs └── mod.ts ├── Cargo.toml ├── deno.json ├── deno_bindgen_ir ├── inventory.rs ├── Cargo.toml ├── codegen │ ├── mod.rs │ └── deno.rs └── lib.rs ├── deno_bindgen ├── tests │ ├── ui.rs │ ├── compile_fail │ │ ├── impl_registration.rs │ │ ├── struct_by_val.rs │ │ ├── struct_by_val.stderr │ │ └── impl_registration.stderr │ └── pass │ │ ├── constructor_override.rs │ │ └── simple.rs ├── lib.rs └── Cargo.toml ├── .bmp.yml ├── e2e_test ├── Cargo.toml ├── bench.js ├── src │ └── lib.rs ├── bindings_test.ts ├── Cargo.lock └── bindings │ └── mod.ts ├── Makefile ├── deno_bindgen_cli ├── Cargo.toml ├── dlfcn.rs ├── main.rs └── cargo.rs ├── LICENSE ├── .github └── workflows │ └── ci.yml ├── README.md └── Cargo.lock /deno_bindgen_macro/tests/fn/add.test.rs: -------------------------------------------------------------------------------- 1 | fn add(a: i32, b: i32) -> i32 { a + b } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | example/Cargo.lock 3 | bindings.json 4 | .DS_Store 5 | deno.lock -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.unstable": true 4 | } 5 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denoland/deno_bindgen/main/assets/logo.png -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denoland/deno_bindgen/main/assets/favicon.ico -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | tab_spaces = 2 3 | edition = "2021" 4 | imports_granularity = "Item" -------------------------------------------------------------------------------- /assets/illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denoland/deno_bindgen/main/assets/illustration.png -------------------------------------------------------------------------------- /example/lsusb.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "./mod.ts"; 2 | 3 | const context = new Context(); 4 | context.lsusb(); 5 | -------------------------------------------------------------------------------- /assets/release.sh: -------------------------------------------------------------------------------- 1 | # TODO: bmp 2 | cargo publish -p deno_bindgen_ir 3 | cargo publish -p deno_bindgen_macro 4 | cargo publish -p deno_bindgen_cli 5 | cargo publish -p deno_bindgen -------------------------------------------------------------------------------- /deno_bindgen_macro/tests/fn/pointer.test.rs: -------------------------------------------------------------------------------- 1 | fn is_utf8(ptr: *const u8, len: usize) -> i32 { 2 | std::str::from_utf8(unsafe { std::slice::from_raw_parts(ptr, len) }).is_ok() as i32 3 | } -------------------------------------------------------------------------------- /deno_bindgen_macro/tests/fn/buffer.test.rs: -------------------------------------------------------------------------------- 1 | fn write_hello(buf: &mut [u8]) { 2 | buf[0] = b'H'; 3 | buf[1] = b'e'; 4 | buf[2] = b'l'; 5 | buf[3] = b'l'; 6 | buf[4] = b'o'; 7 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "deno_bindgen_macro", 5 | "deno_bindgen", 6 | "deno_bindgen_ir", 7 | "deno_bindgen_cli" 8 | ] 9 | exclude = ["e2e_test/", "example/"] -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "patch": "deno run --allow-read=. --allow-write=. https://deno.land/x/bmp@v0.2.0/cli.ts -p", 4 | "minor": "deno run --allow-read=. --allow-write=. https://deno.land/x/bmp@v0.2.0/cli.ts -m" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /deno_bindgen_ir/inventory.rs: -------------------------------------------------------------------------------- 1 | use crate::Symbol; 2 | 3 | #[derive(Debug)] 4 | pub struct Struct { 5 | pub name: &'static str, 6 | pub methods: &'static [Symbol], 7 | } 8 | 9 | pub enum Inventory { 10 | Symbol(Symbol), 11 | Struct(Struct), 12 | } 13 | -------------------------------------------------------------------------------- /deno_bindgen/tests/ui.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn ui_pass() { 3 | let t = trybuild::TestCases::new(); 4 | t.pass("tests/pass/*.rs"); 5 | } 6 | 7 | #[test] 8 | fn ui_compile_fail() { 9 | let t = trybuild::TestCases::new(); 10 | t.compile_fail("tests/compile_fail/*.rs"); 11 | } 12 | -------------------------------------------------------------------------------- /.bmp.yml: -------------------------------------------------------------------------------- 1 | version: 0.8.1 2 | commit: '%.%.%' 3 | files: 4 | README.md: deno_bindgen = "%.%.%" 5 | deno_bindgen/Cargo.toml: 6 | - version = "%.%.%" 7 | - 'deno_bindgen_macro = { path = "../deno_bindgen_macro", version = "%.%.%" }' 8 | deno_bindgen_macro/Cargo.toml: version = "%.%.%" 9 | -------------------------------------------------------------------------------- /e2e_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno_bindgen_e2e" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | deno_bindgen = { path = "../deno_bindgen/" } 8 | serde = { version = "1", features = ["derive"] } 9 | linkme = "0.3" 10 | 11 | [lib] 12 | name = "deno_bindgen_e2e" 13 | crate-type = ["cdylib"] 14 | -------------------------------------------------------------------------------- /deno_bindgen/tests/compile_fail/impl_registration.rs: -------------------------------------------------------------------------------- 1 | use deno_bindgen::deno_bindgen; 2 | 3 | // struct Foo is not "registered" in the inventory, so it `impl Foo` 4 | // is not allowed. 5 | struct Foo; 6 | 7 | #[deno_bindgen] 8 | impl Foo { 9 | #[constructor] 10 | fn new() -> Foo { 11 | Foo 12 | } 13 | } 14 | 15 | fn main() {} -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "usb_example" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | deno_bindgen = { path = "../deno_bindgen/" } 8 | webusb = "0.5.0" 9 | serde = { version = "1", features = ["derive"] } 10 | linkme = "0.3" 11 | 12 | [lib] 13 | name = "deno_usb" 14 | path = "./lib.rs" 15 | crate-type = ["cdylib"] 16 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | ## `deno_usb_example` 2 | 3 | ```bash 4 | $> deno_bindgen -o mod.ts 5 | Finished dev [unoptimized + debuginfo] target(s) in 0.02s 6 | Initializing usb_example 7 | Ready usb_example 8 | 9 | $> deno run --allow-ffi --unstable lsusb.ts 10 | Product Name: G102 LIGHTSYNC Gaming Mouse 11 | Vendor ID: 1133 12 | Product ID: 49298 13 | ``` 14 | -------------------------------------------------------------------------------- /deno_bindgen/tests/compile_fail/struct_by_val.rs: -------------------------------------------------------------------------------- 1 | use deno_bindgen::deno_bindgen; 2 | 3 | #[deno_bindgen] 4 | struct Foo; 5 | 6 | #[deno_bindgen] 7 | impl Foo { 8 | #[constructor] 9 | fn new() -> Foo { 10 | Foo 11 | } 12 | } 13 | 14 | #[deno_bindgen] 15 | fn foo(_foo: Foo) {} // Fail 16 | 17 | #[deno_bindgen] 18 | fn foo2(_foo: &mut Foo) {} // Pass 19 | 20 | fn main() {} -------------------------------------------------------------------------------- /e2e_test/bench.js: -------------------------------------------------------------------------------- 1 | import { add, bytelen, Foo, make_foo } from "./bindings/mod.ts"; 2 | 3 | Deno.bench("add", () => add(1, 2)); 4 | 5 | const b = new Uint8Array([1, 2, 3, 4]); 6 | Deno.bench("bytelen", () => bytelen(b)); 7 | 8 | Deno.bench("make_foo", () => make_foo(21)); 9 | Deno.bench("new Foo", () => new Foo(21)); 10 | 11 | const foo = new Foo(21); 12 | Deno.bench("Foo#bar", () => foo.bar(1)); 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | fmt: 2 | cargo fmt 3 | deno fmt --ignore=target/,e2e_test/target/,e2e_test/bindings/,example/target/ 4 | 5 | build: 6 | cargo build 7 | 8 | test: build 9 | cargo test 10 | cd e2e_test && ../target/debug/deno_bindgen -o bindings/mod.ts && deno test -A --unstable 11 | 12 | bench: build 13 | cd e2e_test && ../target/debug/deno_bindgen -o bindings/mod.ts && deno bench -A --unstable bench.js 14 | -------------------------------------------------------------------------------- /deno_bindgen/tests/pass/constructor_override.rs: -------------------------------------------------------------------------------- 1 | use deno_bindgen::deno_bindgen; 2 | 3 | #[deno_bindgen] 4 | struct Input { 5 | a: i32, 6 | b: i32, 7 | } 8 | 9 | #[deno_bindgen] 10 | impl Input { 11 | #[constructor] 12 | fn new(a: i32, b: i32) -> Input { 13 | Input { a, b } 14 | } 15 | 16 | #[constructor] 17 | fn new2() -> Input { 18 | Input { a: 0, b: 0 } 19 | } 20 | } 21 | 22 | fn main() {} -------------------------------------------------------------------------------- /deno_bindgen/tests/compile_fail/struct_by_val.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/compile_fail/struct_by_val.rs:15:8 3 | | 4 | 15 | fn foo(_foo: Foo) {} // Fail 5 | | --- ^^^^ expected `Foo`, found `&mut _` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | = note: expected struct `Foo` 10 | found mutable reference `&mut _` 11 | note: function defined here 12 | --> tests/compile_fail/struct_by_val.rs:15:4 13 | | 14 | 15 | fn foo(_foo: Foo) {} // Fail 15 | | ^^^ --------- 16 | -------------------------------------------------------------------------------- /deno_bindgen/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 the Deno authors. All rights reserved. MIT license. 2 | pub use ::serde_json; 3 | use deno_bindgen_ir::codegen::Options; 4 | pub use deno_bindgen_ir::*; 5 | pub use deno_bindgen_macro::deno_bindgen; 6 | pub use linkme; 7 | use linkme::distributed_slice; 8 | 9 | #[distributed_slice] 10 | pub static INVENTORY: [Inventory]; 11 | 12 | pub trait BindgenType { 13 | fn type_name() -> &'static str; 14 | } 15 | 16 | #[no_mangle] 17 | fn init_deno_bindgen(opt: Options) { 18 | deno_bindgen_ir::codegen::generate(&INVENTORY, opt).unwrap(); 19 | } 20 | -------------------------------------------------------------------------------- /deno_bindgen/tests/compile_fail/impl_registration.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `Foo: BindgenType` is not satisfied 2 | --> tests/compile_fail/impl_registration.rs:8:6 3 | | 4 | 8 | impl Foo { 5 | | ^^^ the trait `BindgenType` is not implemented for `Foo` 6 | | 7 | note: required by a bound in `_assert_impl` 8 | --> tests/compile_fail/impl_registration.rs:7:1 9 | | 10 | 7 | #[deno_bindgen] 11 | | ^^^^^^^^^^^^^^^ required by this bound in `_assert_impl` 12 | = note: this error originates in the attribute macro `deno_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) 13 | -------------------------------------------------------------------------------- /deno_bindgen_macro/tests/fn/add.test.out.rs: -------------------------------------------------------------------------------- 1 | const _: () = { 2 | #[deno_bindgen::linkme::distributed_slice(deno_bindgen::INVENTORY)] 3 | pub static _A: deno_bindgen::Inventory = deno_bindgen::Inventory::Symbol(deno_bindgen::Symbol { 4 | name: stringify!(add), 5 | parameters: &[deno_bindgen::Type::Int32, deno_bindgen::Type::Int32], 6 | return_type: deno_bindgen::Type::Int32, 7 | non_blocking: false, 8 | internal: false, 9 | is_constructor: false, 10 | }); 11 | }; 12 | #[no_mangle] 13 | extern "C" fn add(a: i32, b: i32) -> i32 { 14 | fn add(a: i32, b: i32) -> i32 { 15 | a + b 16 | } 17 | let ret = add(a, b); 18 | ret 19 | } 20 | -------------------------------------------------------------------------------- /deno_bindgen_ir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno_bindgen_ir" 3 | version = "0.1.0" 4 | description = "This tool aims to simplify glue code generation for Deno FFI libraries written in Rust." 5 | documentation = "https://docs.rs/deno_bindgen" 6 | homepage = "https://github.com/denoland/deno_bindgen" 7 | repository = "https://github.com/denoland/deno_bindgen" 8 | keywords = ["deno", "ffi", "bindgen", "bindings", "macro"] 9 | categories = ["development-tools::ffi", "development-tools"] 10 | readme = "../README.md" 11 | license = "MIT" 12 | edition = "2021" 13 | 14 | [lib] 15 | path = "./lib.rs" 16 | 17 | [dependencies] 18 | quote = "1.0" 19 | proc-macro2 = "1.0" 20 | syn = { version = "2.0", features = ["full", "extra-traits"] } -------------------------------------------------------------------------------- /deno_bindgen_macro/src/struct_.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream as TokenStream2; 2 | use syn::ItemStruct; 3 | 4 | use crate::util::Result; 5 | use crate::util::{self}; 6 | 7 | pub fn handle(struct_: ItemStruct) -> Result { 8 | if struct_.generics.params.first().is_some() { 9 | return Err(util::Error::Generics); 10 | } 11 | 12 | if struct_.generics.where_clause.is_some() { 13 | return Err(util::Error::WhereClause); 14 | } 15 | 16 | let ref ty_str @ _ = struct_.ident; 17 | Ok(quote::quote! { 18 | #struct_ 19 | 20 | impl ::deno_bindgen::BindgenType for #ty_str { 21 | fn type_name() -> &'static str { 22 | stringify!(#ty_str) 23 | } 24 | } 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /deno_bindgen_cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno_bindgen_cli" 3 | version = "0.1.0" 4 | description = "This tool aims to simplify glue code generation for Deno FFI libraries written in Rust." 5 | documentation = "https://docs.rs/deno_bindgen" 6 | homepage = "https://github.com/denoland/deno_bindgen" 7 | repository = "https://github.com/denoland/deno_bindgen" 8 | keywords = ["deno", "ffi", "bindgen", "bindings", "macro"] 9 | categories = ["development-tools::ffi", "development-tools"] 10 | readme = "../README.md" 11 | license = "MIT" 12 | edition = "2021" 13 | 14 | [[bin]] 15 | name = "deno_bindgen" 16 | path = "./main.rs" 17 | 18 | [dependencies] 19 | deno_bindgen_ir = { path = "../deno_bindgen_ir", version = "0.1.0" } 20 | 21 | structopt = "0.3.26" 22 | dlopen2 = "0.6.1" 23 | cargo_metadata = "0.18.1" -------------------------------------------------------------------------------- /deno_bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno_bindgen" 3 | version = "0.9.0-alpha" 4 | description = "Write high-level Deno FFI libraries in Rust." 5 | documentation = "https://docs.rs/deno_bindgen" 6 | homepage = "https://github.com/denoland/deno_bindgen" 7 | repository = "https://github.com/denoland/deno_bindgen" 8 | keywords = ["deno", "ffi", "bindgen", "bindings", "macro"] 9 | categories = ["development-tools::ffi", "development-tools"] 10 | readme = "../README.md" 11 | license = "MIT" 12 | edition = "2021" 13 | 14 | [lib] 15 | path = "./lib.rs" 16 | 17 | [dependencies] 18 | deno_bindgen_macro = { path = "../deno_bindgen_macro", version = "0.9.0-alpha" } 19 | deno_bindgen_ir = { path = "../deno_bindgen_ir", version = "0.1.0" } 20 | serde = { version = "1", features = ["derive"] } 21 | serde_json = "1" 22 | linkme = "0.3" 23 | 24 | [dev-dependencies] 25 | trybuild = "1.0.85" -------------------------------------------------------------------------------- /deno_bindgen_cli/dlfcn.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use std::path::PathBuf; 3 | 4 | use dlopen2::wrapper::Container; 5 | use dlopen2::wrapper::WrapperApi; 6 | 7 | #[derive(WrapperApi)] 8 | struct Api { 9 | init_deno_bindgen: unsafe fn(opt: deno_bindgen_ir::codegen::Options), 10 | } 11 | 12 | pub unsafe fn load_and_init( 13 | path: &Path, 14 | out: Option, 15 | lazy_init: bool, 16 | ) -> std::io::Result<()> { 17 | let cont: Container = Container::load(path).map_err(|e| { 18 | std::io::Error::new( 19 | std::io::ErrorKind::Other, 20 | format!("failed to load library: {}", e), 21 | ) 22 | })?; 23 | 24 | cont.init_deno_bindgen(deno_bindgen_ir::codegen::Options { 25 | target: deno_bindgen_ir::codegen::Target::Deno, 26 | out, 27 | local_dylib_path: path.to_path_buf(), 28 | lazy_init, 29 | }); 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /deno_bindgen_macro/tests/fn/pointer.test.out.rs: -------------------------------------------------------------------------------- 1 | const _: () = { 2 | #[deno_bindgen::linkme::distributed_slice(deno_bindgen::INVENTORY)] 3 | pub static _A: deno_bindgen::Inventory = deno_bindgen::Inventory::Symbol(deno_bindgen::Symbol { 4 | name: stringify!(is_utf8), 5 | parameters: &[deno_bindgen::Type::Pointer, deno_bindgen::Type::Uint64], 6 | return_type: deno_bindgen::Type::Int32, 7 | non_blocking: false, 8 | internal: false, 9 | is_constructor: false, 10 | }); 11 | }; 12 | #[no_mangle] 13 | extern "C" fn is_utf8(__arg_0: *const (), len: usize) -> i32 { 14 | fn is_utf8(ptr: *const u8, len: usize) -> i32 { 15 | std::str::from_utf8(unsafe { std::slice::from_raw_parts(ptr, len) }).is_ok() 16 | as i32 17 | } 18 | let ptr = __arg_0 as _; 19 | let ret = is_utf8(ptr, len); 20 | ret 21 | } 22 | -------------------------------------------------------------------------------- /deno_bindgen_macro/src/util.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum Error { 3 | Asyncness, 4 | Reciever, 5 | UnsupportedType, 6 | Generics, 7 | WhereClause, 8 | MissingReceiver, 9 | } 10 | 11 | impl std::fmt::Display for Error { 12 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 13 | match self { 14 | Error::Asyncness => write!(f, "async functions are not supported"), 15 | Error::Reciever => write!(f, "methods are not supported"), 16 | Error::UnsupportedType => write!(f, "unsupported type"), 17 | Error::Generics => write!(f, "generics are not supported"), 18 | Error::WhereClause => write!(f, "where clauses are not supported"), 19 | Error::MissingReceiver => write!(f, "missing receiver"), 20 | } 21 | } 22 | } 23 | 24 | impl std::error::Error for Error {} 25 | 26 | pub type Result = std::result::Result; 27 | -------------------------------------------------------------------------------- /deno_bindgen_macro/tests/fn/buffer.test.out.rs: -------------------------------------------------------------------------------- 1 | const _: () = { 2 | #[deno_bindgen::linkme::distributed_slice(deno_bindgen::INVENTORY)] 3 | pub static _A: deno_bindgen::Inventory = deno_bindgen::Inventory::Symbol(deno_bindgen::Symbol { 4 | name: stringify!(write_hello), 5 | parameters: &[deno_bindgen::Type::Buffer], 6 | return_type: deno_bindgen::Type::Void, 7 | non_blocking: false, 8 | internal: false, 9 | is_constructor: false, 10 | }); 11 | }; 12 | #[no_mangle] 13 | extern "C" fn write_hello(__arg_0: *const (), __arg_1: u32) { 14 | fn write_hello(buf: &mut [u8]) { 15 | buf[0] = b'H'; 16 | buf[1] = b'e'; 17 | buf[2] = b'l'; 18 | buf[3] = b'l'; 19 | buf[4] = b'o'; 20 | } 21 | let buf = unsafe { std::slice::from_raw_parts_mut(__arg_0 as _, __arg_1 as usize) }; 22 | let ret = write_hello(buf); 23 | ret 24 | } 25 | -------------------------------------------------------------------------------- /deno_bindgen_ir/codegen/mod.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use crate::inventory::Inventory; 4 | 5 | mod deno; 6 | 7 | pub struct Options { 8 | pub target: Target, 9 | pub out: Option, 10 | pub local_dylib_path: PathBuf, 11 | pub lazy_init: bool, 12 | } 13 | 14 | pub enum Target { 15 | Deno, 16 | } 17 | 18 | pub trait Generator { 19 | fn generate(&mut self, writer: W) -> std::io::Result<()>; 20 | } 21 | 22 | pub fn generate( 23 | symbols: &'static [Inventory], 24 | opt: Options, 25 | ) -> std::io::Result<()> { 26 | let mut codegen = match opt.target { 27 | Target::Deno => { 28 | deno::Codegen::new(symbols, &opt.local_dylib_path, opt.lazy_init) 29 | } 30 | }; 31 | 32 | if let Some(out) = opt.out { 33 | let mut writer = std::fs::File::create(out)?; 34 | codegen.generate(&mut writer)?; 35 | return Ok(()); 36 | } 37 | 38 | let writer = std::io::stdout(); 39 | codegen.generate(writer) 40 | } 41 | -------------------------------------------------------------------------------- /deno_bindgen_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deno_bindgen_macro" 3 | version = "0.9.0-alpha" 4 | description = "Write high-level Deno FFI libraries in Rust." 5 | documentation = "https://docs.rs/deno_bindgen_macro" 6 | homepage = "https://github.com/denoland/deno_bindgen" 7 | repository = "https://github.com/denoland/deno_bindgen" 8 | keywords = ["deno", "ffi", "bindgen", "bindings", "macro"] 9 | categories = ["development-tools::ffi", "development-tools"] 10 | readme = "../README.md" 11 | license = "MIT" 12 | edition = "2021" 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [dependencies] 18 | deno_bindgen_ir = { path = "../deno_bindgen_ir", version = "0.1.0" } 19 | 20 | proc-macro2 = "1.0" 21 | quote = "1.0" 22 | syn = { version = "2.0", features = ["full", "extra-traits"] } 23 | serde = { version = "1.0.59", features = ["derive"] } 24 | serde_json = "1.0.59" 25 | Inflector = "0.11.4" 26 | 27 | [dev-dependencies] 28 | prettyplease = "0.2.15" 29 | testing_macros = "0.2.11" -------------------------------------------------------------------------------- /deno_bindgen/tests/pass/simple.rs: -------------------------------------------------------------------------------- 1 | use deno_bindgen_macro::deno_bindgen; 2 | 3 | #[deno_bindgen] 4 | fn add(a: i32, b: i32) -> i32 { 5 | a + b 6 | } 7 | 8 | #[deno_bindgen] 9 | fn buf_mut(b: &mut [u8]) { 10 | b[0] = 99; 11 | } 12 | 13 | #[deno_bindgen] 14 | fn cstr() -> *const u8 { 15 | b"Hello, World!\0".as_ptr() 16 | } 17 | 18 | #[deno_bindgen] 19 | fn strlen(s: *const u8) -> u32 { 20 | let mut len = 0; 21 | unsafe { 22 | while *s.add(len as usize) != 0 { 23 | len += 1; 24 | } 25 | } 26 | len 27 | } 28 | 29 | #[deno_bindgen(non_blocking)] 30 | fn non_blocking() -> i32 { 31 | 42 32 | } 33 | 34 | #[deno_bindgen] 35 | struct Foo { 36 | internal: i32, 37 | } 38 | 39 | #[deno_bindgen] 40 | impl Foo { 41 | #[constructor] 42 | fn new(internal: i32) -> Foo { 43 | Foo { internal } 44 | } 45 | 46 | fn bar(&self) -> i32 { 47 | 42 48 | } 49 | 50 | fn baz(&self, a: i32) -> i32 { 51 | a 52 | } 53 | 54 | fn qux(&self, a: i32, b: i32) -> i32 { 55 | a + b 56 | } 57 | 58 | fn quux(&mut self) { 59 | self.internal += 1; 60 | } 61 | } 62 | 63 | fn main() {} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Divy Srivastava 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /deno_bindgen_cli/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use cargo::Artifact; 4 | use structopt::StructOpt; 5 | 6 | mod cargo; 7 | mod dlfcn; 8 | 9 | #[derive(Debug, StructOpt)] 10 | #[structopt(name = "deno_bindgen_cli", about = "A CLI for deno_bindgen")] 11 | struct Opt { 12 | #[structopt(short, long)] 13 | /// Build in release mode 14 | release: bool, 15 | 16 | #[structopt(short, long)] 17 | out: Option, 18 | 19 | #[structopt(short, long)] 20 | lazy_init: bool, 21 | } 22 | 23 | fn main() -> std::io::Result<()> { 24 | let opt = Opt::from_args(); 25 | 26 | let cwd = std::env::current_dir().unwrap(); 27 | let Artifact { path, .. } = 28 | cargo::Build::new().release(opt.release).build(&cwd)?; 29 | 30 | let name = cargo::metadata()?; 31 | println!("Initializing {name}"); 32 | 33 | let path = PathBuf::from(path); 34 | // https://github.com/denoland/deno/issues/21172 35 | #[cfg(target_os = "windows")] 36 | let path = path 37 | .strip_prefix(&cwd) 38 | .expect("path is not a prefix of cwd"); 39 | 40 | unsafe { dlfcn::load_and_init(&path, opt.out, opt.lazy_init)? }; 41 | 42 | println!("Ready {name}"); 43 | Ok(()) 44 | } 45 | -------------------------------------------------------------------------------- /e2e_test/src/lib.rs: -------------------------------------------------------------------------------- 1 | use deno_bindgen::deno_bindgen; 2 | 3 | #[deno_bindgen] 4 | fn add(a: i32, b: i32) -> i32 { 5 | a + b 6 | } 7 | 8 | #[deno_bindgen] 9 | struct Input { 10 | a: i32, 11 | b: i32, 12 | } 13 | 14 | #[deno_bindgen] 15 | impl Input { 16 | #[constructor] 17 | fn new(a: i32, b: i32) -> Input { 18 | Input { a, b } 19 | } 20 | } 21 | 22 | #[deno_bindgen] 23 | fn add2(input: &Input) -> i32 { 24 | input.a + input.b 25 | } 26 | 27 | #[deno_bindgen] 28 | fn bytelen(b: &[u8]) -> u32 { 29 | b.len() as u32 30 | } 31 | 32 | #[deno_bindgen] 33 | fn buf_mut(b: &mut [u8]) { 34 | b[0] = 99; 35 | } 36 | 37 | #[deno_bindgen] 38 | fn cstr() -> *const u8 { 39 | b"Hello, World!\0".as_ptr() 40 | } 41 | 42 | #[deno_bindgen] 43 | fn strlen(s: *const u8) -> u32 { 44 | let mut len = 0; 45 | unsafe { 46 | while *s.add(len as usize) != 0 { 47 | len += 1; 48 | } 49 | } 50 | len 51 | } 52 | 53 | #[deno_bindgen(non_blocking)] 54 | fn non_blocking() -> i32 { 55 | 42 56 | } 57 | 58 | #[deno_bindgen] 59 | fn make_foo() -> Foo { 60 | Foo { internal: 42 } 61 | } 62 | 63 | #[deno_bindgen] 64 | fn inc_foo(foo: &mut Foo) { 65 | foo.internal += 1; 66 | } 67 | 68 | #[deno_bindgen] 69 | pub struct Foo { 70 | internal: u32, 71 | } 72 | 73 | #[deno_bindgen] 74 | impl Foo { 75 | #[constructor] 76 | fn new(internal: u32) -> Foo { 77 | Foo { internal } 78 | } 79 | 80 | fn inc(&mut self) { 81 | self.internal += 1; 82 | } 83 | 84 | fn bar(&self, a: u32) -> u32 { 85 | self.internal + a 86 | } 87 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: main 6 | pull_request: 7 | branches: main 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | os: [ windows-latest, macos-latest, ubuntu-latest ] 14 | toolchain: [nightly] 15 | deno_version: [1.38.1] 16 | runs-on: ${{ matrix.os }} 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: Swatinem/rust-cache@v2 20 | with: 21 | save-if: ${{ github.ref == 'refs/heads/main' }} 22 | - name: Setup Rust toolchain 23 | uses: actions-rs/toolchain@v1 24 | with: 25 | toolchain: ${{ matrix.toolchain }} 26 | override: true 27 | - uses: denoland/setup-deno@v1 28 | with: 29 | deno-version: ${{ matrix.deno_version }} 30 | - name: Setup Rust toolchain 31 | uses: actions-rs/toolchain@v1 32 | with: 33 | toolchain: ${{ matrix.toolchain }} 34 | override: true 35 | components: rustfmt, clippy 36 | - name: Build 37 | run: cargo build --locked --release 38 | - name: Test (debug) 39 | working-directory: ./e2e_test 40 | run: | 41 | ../target/release/deno_bindgen -o bindings/mod.ts 42 | deno test -A --unstable 43 | - name: Test (release) 44 | working-directory: ./e2e_test 45 | shell: bash 46 | run: | 47 | rm -rf target 48 | ../target/release/deno_bindgen -o bindings/mod.ts --release 49 | deno test -A --unstable 50 | - name: Bench 51 | working-directory: ./e2e_test 52 | shell: bash 53 | run: deno bench -A --unstable 54 | -------------------------------------------------------------------------------- /example/lib.rs: -------------------------------------------------------------------------------- 1 | use deno_bindgen::deno_bindgen; 2 | 3 | #[deno_bindgen] 4 | pub struct Context { 5 | context: webusb::Context, 6 | } 7 | 8 | #[deno_bindgen] 9 | impl Context { 10 | #[constructor] 11 | pub fn init() -> Context { 12 | let context = webusb::Context::init().expect("Unable to create context"); 13 | Context { context } 14 | } 15 | 16 | pub fn lsusb(&self) { 17 | let devices = self.context.devices().expect("Unable to get devices"); 18 | for device in devices { 19 | if let Some(name) = device.product_name { 20 | println!("Product Name: {}", name); 21 | } 22 | 23 | println!("Vendor ID: {}", device.vendor_id); 24 | println!("Product ID: {}\n", device.product_id); 25 | } 26 | } 27 | 28 | pub fn open(&mut self, vendor_id: u16, product_id: u16) -> Device { 29 | let devices = self.context.devices().expect("Unable to get devices"); 30 | let mut device = devices 31 | .into_iter() 32 | .find(|d| d.vendor_id == vendor_id && d.product_id == product_id) 33 | .expect("Device not found."); 34 | 35 | device.open().expect("Unable to open device."); 36 | 37 | Device { device } 38 | } 39 | } 40 | 41 | #[deno_bindgen] 42 | pub struct Device { 43 | device: webusb::UsbDevice, 44 | } 45 | 46 | impl Drop for Device { 47 | fn drop(&mut self) { 48 | self.device.close().expect("Unable to close device."); 49 | } 50 | } 51 | 52 | #[deno_bindgen] 53 | impl Device { 54 | pub fn claim_interface(&mut self, interface_number: u8) { 55 | self 56 | .device 57 | .claim_interface(interface_number) 58 | .expect("Unable to claim interface."); 59 | } 60 | 61 | pub fn select_alternate_interface( 62 | &mut self, 63 | interface_number: u8, 64 | alternate_setting: u8, 65 | ) { 66 | self 67 | .device 68 | .select_alternate_interface(interface_number, alternate_setting) 69 | .expect("Unable to select alternate interface."); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /e2e_test/bindings_test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | add, 3 | add2, 4 | buf_mut, 5 | bytelen, 6 | cstr, 7 | Foo, 8 | inc_foo, 9 | Input, 10 | make_foo, 11 | non_blocking, 12 | strlen, 13 | } from "./bindings/mod.ts"; 14 | import { 15 | assert, 16 | assertEquals, 17 | } from "https://deno.land/std@0.178.0/testing/asserts.ts"; 18 | 19 | Deno.test({ 20 | name: "add#test", 21 | fn: () => { 22 | assertEquals(add(1, 2), 3); 23 | 24 | using input = new Input(-1, 1); 25 | assertEquals(add2(input), 0); 26 | }, 27 | }); 28 | 29 | Deno.test({ 30 | name: "bytelen#test", 31 | fn: () => { 32 | assertEquals(bytelen(new TextEncoder().encode("hello")), 5); 33 | }, 34 | }); 35 | 36 | Deno.test({ 37 | name: "buf_mut#test", 38 | fn: () => { 39 | const buf = new Uint8Array(1); 40 | buf_mut(buf); 41 | assertEquals(buf[0], 99); 42 | }, 43 | }); 44 | 45 | Deno.test({ 46 | name: "cstr#test", 47 | fn: () => { 48 | const ptr = cstr(); 49 | const str = Deno.UnsafePointerView.getCString(ptr!); 50 | assertEquals(str, "Hello, World!"); 51 | }, 52 | }); 53 | 54 | Deno.test({ 55 | name: "strlen#test", 56 | fn: () => { 57 | const ptr = strlen(cstr()); 58 | assertEquals(ptr, 13); 59 | }, 60 | }); 61 | 62 | Deno.test({ 63 | name: "non_blocking#test", 64 | fn: async () => { 65 | const result = await non_blocking(); 66 | assertEquals(result, 42); 67 | }, 68 | }); 69 | 70 | Deno.test({ 71 | name: "make_foo#test", 72 | fn: () => { 73 | const foo = make_foo(); 74 | assert(foo instanceof Foo); 75 | assertEquals(foo.bar(1), 43); 76 | }, 77 | }); 78 | 79 | Deno.test({ 80 | name: "Foo#constructor", 81 | fn() { 82 | const foo = new Foo(42); 83 | assertEquals(foo.bar(1), 43); 84 | }, 85 | }); 86 | 87 | Deno.test({ 88 | name: "Foo#using", 89 | fn() { 90 | using foo = new Foo(1); 91 | foo.inc(); 92 | assertEquals(foo.bar(1), 3); 93 | }, 94 | }); 95 | 96 | Deno.test({ 97 | name: "Foo#using explicit", 98 | fn() { 99 | using foo = make_foo(); 100 | 101 | // Multiple dipose calls are nop. 102 | foo[Symbol.dispose](); 103 | foo[Symbol.dispose](); 104 | }, 105 | }); 106 | 107 | Deno.test({ 108 | name: "inc_foo#test", 109 | fn: () => { 110 | using foo = new Foo(22); 111 | inc_foo(foo); 112 | assertEquals(foo.bar(0), 23); 113 | }, 114 | }); 115 | -------------------------------------------------------------------------------- /deno_bindgen_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use proc_macro::TokenStream; 4 | use syn::meta::ParseNestedMeta; 5 | use syn::parse2; 6 | use syn::parse_macro_input; 7 | use syn::Item; 8 | 9 | mod fn_; 10 | mod impl_; 11 | mod struct_; 12 | mod util; 13 | 14 | #[derive(Default)] 15 | pub(crate) struct FnAttributes { 16 | pub(crate) non_blocking: bool, 17 | pub(crate) constructor: bool, 18 | 19 | pub(crate) internal: bool, 20 | } 21 | 22 | impl FnAttributes { 23 | fn parse(&mut self, meta: ParseNestedMeta) -> syn::parse::Result<()> { 24 | if meta.path.is_ident("non_blocking") { 25 | self.non_blocking = true; 26 | Ok(()) 27 | } else { 28 | Err(meta.error("unsupported attribute")) 29 | } 30 | } 31 | } 32 | 33 | #[proc_macro_attribute] 34 | pub fn deno_bindgen(args: TokenStream, input: TokenStream) -> TokenStream { 35 | match parse2::(input.into()).unwrap() { 36 | Item::Fn(input) => { 37 | let mut attrs = FnAttributes::default(); 38 | let attrs_parser = syn::meta::parser(|meta| attrs.parse(meta)); 39 | parse_macro_input!(args with attrs_parser); 40 | 41 | fn_::handle(input, attrs).unwrap().into() 42 | } 43 | Item::Struct(input) => struct_::handle(input).unwrap().into(), 44 | Item::Impl(input) => impl_::handle(input).unwrap().into(), 45 | _ => panic!("only functions are supported"), 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use std::path::PathBuf; 52 | 53 | #[testing_macros::fixture("tests/fn/*.test.rs")] 54 | fn test_codegen_fn(input: PathBuf) { 55 | let update_expected = std::env::var("UPDATE_EXPECTED").is_ok(); 56 | 57 | let source = 58 | std::fs::read_to_string(&input).expect("failed to read test case"); 59 | let item_fn = syn::parse_str::(&source) 60 | .expect("failed to parse test case"); 61 | 62 | let tokens = crate::fn_::handle(item_fn, Default::default()).unwrap(); 63 | let tree = syn::parse2(tokens).unwrap(); 64 | let actual = prettyplease::unparse(&tree); 65 | 66 | let expected_out = input.with_extension("out.rs"); 67 | if update_expected { 68 | std::fs::write(expected_out, actual) 69 | .expect("Failed to write expectation file"); 70 | } else { 71 | let expected = std::fs::read_to_string(expected_out) 72 | .expect("Failed to read expectation file"); 73 | assert_eq!( 74 | expected, actual, 75 | "Failed to match expectation. Use UPDATE_EXPECTED=1." 76 | ); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /deno_bindgen_cli/cargo.rs: -------------------------------------------------------------------------------- 1 | use std::io::Result; 2 | use std::path::Path; 3 | use std::path::PathBuf; 4 | use std::process::Command; 5 | use std::process::Stdio; 6 | 7 | pub struct Artifact { 8 | pub path: PathBuf, 9 | pub manifest_path: PathBuf, 10 | } 11 | 12 | #[derive(Default)] 13 | pub struct Build { 14 | release: bool, 15 | } 16 | 17 | impl Build { 18 | pub fn new() -> Self { 19 | Self::default() 20 | } 21 | 22 | pub fn release(mut self, release: bool) -> Self { 23 | self.release = release; 24 | self 25 | } 26 | 27 | pub fn build(self, path: &Path) -> Result { 28 | let mut cmd = Command::new("cargo"); 29 | cmd 30 | .current_dir(path) 31 | .arg("build") 32 | .arg("--lib") 33 | .arg("--message-format=json") 34 | .stdout(Stdio::piped()); 35 | 36 | if self.release { 37 | cmd.arg("--release"); 38 | } 39 | 40 | let status = cmd.status()?; 41 | let output = cmd.output()?; 42 | if status.success() { 43 | let reader = std::io::BufReader::new(output.stdout.as_slice()); 44 | let mut artifacts = vec![]; 45 | for message in cargo_metadata::Message::parse_stream(reader) { 46 | match message.unwrap() { 47 | cargo_metadata::Message::CompilerArtifact(artifact) => { 48 | if artifact.target.kind.contains(&"cdylib".to_string()) { 49 | artifacts.push(Artifact { 50 | path: PathBuf::from(artifact.filenames[0].to_string()), 51 | manifest_path: PathBuf::from( 52 | artifact.manifest_path.to_string(), 53 | ), 54 | }); 55 | } 56 | } 57 | _ => {} 58 | } 59 | } 60 | 61 | // TODO: Fix. Not an ideal way to get the artifact of the desired crate, but it 62 | // works for most case. 63 | if let Some(artifact) = artifacts.pop() { 64 | return Ok(artifact); 65 | } 66 | 67 | Err(std::io::Error::new( 68 | std::io::ErrorKind::Other, 69 | "failed to parse cargo output", 70 | ))? 71 | } else { 72 | println!( 73 | "failed to execute `cargo`: exited with {}\n full command: {:?}", 74 | status, cmd, 75 | ); 76 | 77 | std::process::exit(1); 78 | } 79 | } 80 | } 81 | 82 | pub fn metadata() -> Result { 83 | let metadata = cargo_metadata::MetadataCommand::new() 84 | .exec() 85 | .map_err(|e| { 86 | println!("failed to execute `cargo metadata`: {}", e); 87 | std::process::exit(1); 88 | }) 89 | .unwrap(); 90 | 91 | Ok(metadata.root_package().unwrap().name.clone()) 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `deno_bindgen` 2 | 3 | 4 | 5 | [![ci](https://github.com/denoland/deno_bindgen/workflows/ci/badge.svg?branch=main)](https://github.com/denoland/deno_bindgen/actions) 6 | [![crates](https://img.shields.io/crates/v/deno_bindgen.svg)](https://crates.io/crates/deno_bindgen) 7 | [![docs](https://docs.rs/deno_bindgen/badge.svg)](https://docs.rs/deno_bindgen) 8 | 9 | This tool aims to simplify glue code generation for Deno FFI libraries written 10 | in Rust. 11 | 12 | ## Install 13 | 14 | Install the command-line via `cargo`: 15 | 16 | ```bash 17 | cargo install deno_bindgen_cli 18 | ``` 19 | 20 | ## Usage 21 | 22 | ```rust 23 | use deno_bindgen::deno_bindgen; 24 | 25 | // Export `add` function to JavaScript. 26 | #[deno_bindgen] 27 | fn add(a: u32, b: u32) -> u32 { 28 | a + b 29 | } 30 | ``` 31 | 32 | Use the exported functions directly in ESM with TypeScript typings 33 | 34 | ```typescript 35 | import { add } from "./bindings/mod.ts"; 36 | 37 | add(1, 2); 38 | ``` 39 | 40 | ## Design 41 | 42 | The tool is designed to make it very easy to write high performance FFI 43 | bindings. A lot of the things have been redesigned in `0.10` to prevent perf 44 | footguns. 45 | 46 | TypeScript types are generated and supported OOTB. 47 | 48 | All class handles support disposing memory via the Explicit Resource Management 49 | API (`using`). 50 | 51 | ```rust 52 | #[deno_bindgen] 53 | pub struct Foo; 54 | 55 | #[deno_bindgen] 56 | impl Foo { 57 | #[constructor] 58 | pub fn new() -> Self { 59 | Self 60 | } 61 | 62 | pub fn bar(&self) { 63 | // ... 64 | } 65 | } 66 | ``` 67 | 68 | ```js 69 | import { Foo } from "@ffi/example"; 70 | 71 | { 72 | using foo = new Foo(); 73 | foo.bar(); 74 | // foo is disposed here... 75 | } 76 | ``` 77 | 78 | High performance. Codegen tries its best to take the fastest possible path for 79 | all bindings as-if they were written by hand to properly leverage the power of 80 | the Deno FFI JIT calls. 81 | 82 | ``` 83 | > make bench 84 | cpu: Apple M1 85 | runtime: deno 1.38.0 (aarch64-apple-darwin) 86 | 87 | file:///Users/divy/gh/deno_bindgen/example/bench.js 88 | benchmark time (avg) iter/s (min … max) p75 p99 p995 89 | --------------------------------------------------------------- ----------------------------- 90 | add 6.88 ns/iter 145,297,626.6 (6.78 ns … 13.33 ns) 6.81 ns 8.22 ns 9.4 ns 91 | bytelen 8.05 ns/iter 124,278,976.3 (7.81 ns … 18.1 ns) 8.09 ns 10.39 ns 11.64 ns 92 | ``` 93 | 94 | ## Publishing 95 | 96 | By default, deno_bindgen generates bindings for local development. To publish a 97 | cross-platform binding, you can use the `--lazy-init` flag, this gives you full 98 | control on how you want to host pre-built shared libraries and pull them in at 99 | runtime. 100 | 101 | ```bash 102 | deno_bindgen --release --lazy-init 103 | ``` 104 | 105 | ```typescript 106 | import { add, load } from "./example/mod.ts"; 107 | import { cache } from "https://deno.land/x/cache/mod.ts"; 108 | 109 | // Download the shared library from a CDN 110 | const file = await cache("https://example.com/example.so"); 111 | load(file.path); 112 | 113 | add(1, 2); 114 | ``` 115 | -------------------------------------------------------------------------------- /example/mod.ts: -------------------------------------------------------------------------------- 1 | // deno-lint-ignore-file 2 | 3 | // This file is automatically generated by deno_bindgen. 4 | // Do not edit this file directly. 5 | 6 | const { dlopen } = Deno; 7 | 8 | const { symbols } = dlopen( 9 | "/Users/divy/gh/deno_bindgen/example/target/debug/libdeno_usb.dylib", 10 | { 11 | __Context_init: { 12 | parameters: [], 13 | result: "pointer", 14 | nonblocking: false, 15 | }, 16 | __Context_lsusb: { 17 | parameters: [ 18 | "pointer", 19 | ], 20 | result: "void", 21 | nonblocking: false, 22 | }, 23 | __Context_open: { 24 | parameters: [ 25 | "pointer", 26 | "u16", 27 | "u16", 28 | ], 29 | result: "pointer", 30 | nonblocking: false, 31 | }, 32 | __Context_dealloc: { 33 | parameters: [ 34 | "pointer", 35 | ], 36 | result: "void", 37 | nonblocking: false, 38 | }, 39 | __Device_claim_interface: { 40 | parameters: [ 41 | "pointer", 42 | "u8", 43 | ], 44 | result: "void", 45 | nonblocking: false, 46 | }, 47 | __Device_select_alternate_interface: { 48 | parameters: [ 49 | "pointer", 50 | "u8", 51 | "u8", 52 | ], 53 | result: "void", 54 | nonblocking: false, 55 | }, 56 | __Device_dealloc: { 57 | parameters: [ 58 | "pointer", 59 | ], 60 | result: "void", 61 | nonblocking: false, 62 | }, 63 | }, 64 | ); 65 | 66 | function __Context_init(): Context { 67 | const ret = symbols.__Context_init(); 68 | return Context.__constructor(ret); 69 | } 70 | 71 | function __Context_lsusb( 72 | arg0: Deno.PointerObject | null, 73 | ): void { 74 | return symbols.__Context_lsusb( 75 | arg0, 76 | ); 77 | } 78 | 79 | function __Context_open( 80 | arg0: Deno.PointerObject | null, 81 | arg1: number, 82 | arg2: number, 83 | ): Device { 84 | const ret = symbols.__Context_open( 85 | arg0, 86 | arg1, 87 | arg2, 88 | ); 89 | return Device.__constructor(ret); 90 | } 91 | 92 | function __Context_dealloc( 93 | arg0: Deno.PointerObject | null, 94 | ): void { 95 | return symbols.__Context_dealloc( 96 | arg0, 97 | ); 98 | } 99 | 100 | export class Context { 101 | ptr: Deno.PointerObject | null = null; 102 | 103 | static __constructor(ptr: Deno.PointerObject | null) { 104 | const self = Object.create(Context.prototype); 105 | self.ptr = ptr; 106 | return self; 107 | } 108 | 109 | [Symbol.dispose]() { 110 | this.dealloc(); 111 | this.ptr = null; 112 | } 113 | 114 | constructor() { 115 | return __Context_init(); 116 | } 117 | 118 | lsusb(): void { 119 | return __Context_lsusb( 120 | this.ptr, 121 | ); 122 | } 123 | 124 | open(arg0: number, arg1: number): Device { 125 | return __Context_open( 126 | this.ptr, 127 | arg0, 128 | arg1, 129 | ); 130 | } 131 | 132 | dealloc(): void { 133 | return __Context_dealloc( 134 | this.ptr, 135 | ); 136 | } 137 | } 138 | 139 | function __Device_claim_interface( 140 | arg0: Deno.PointerObject | null, 141 | arg1: number, 142 | ): void { 143 | return symbols.__Device_claim_interface( 144 | arg0, 145 | arg1, 146 | ); 147 | } 148 | 149 | function __Device_select_alternate_interface( 150 | arg0: Deno.PointerObject | null, 151 | arg1: number, 152 | arg2: number, 153 | ): void { 154 | return symbols.__Device_select_alternate_interface( 155 | arg0, 156 | arg1, 157 | arg2, 158 | ); 159 | } 160 | 161 | function __Device_dealloc( 162 | arg0: Deno.PointerObject | null, 163 | ): void { 164 | return symbols.__Device_dealloc( 165 | arg0, 166 | ); 167 | } 168 | 169 | export class Device { 170 | ptr: Deno.PointerObject | null = null; 171 | 172 | static __constructor(ptr: Deno.PointerObject | null) { 173 | const self = Object.create(Device.prototype); 174 | self.ptr = ptr; 175 | return self; 176 | } 177 | 178 | [Symbol.dispose]() { 179 | this.dealloc(); 180 | this.ptr = null; 181 | } 182 | 183 | claim_interface(arg0: number): void { 184 | return __Device_claim_interface( 185 | this.ptr, 186 | arg0, 187 | ); 188 | } 189 | 190 | select_alternate_interface(arg0: number, arg1: number): void { 191 | return __Device_select_alternate_interface( 192 | this.ptr, 193 | arg0, 194 | arg1, 195 | ); 196 | } 197 | 198 | dealloc(): void { 199 | return __Device_dealloc( 200 | this.ptr, 201 | ); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /deno_bindgen_macro/src/impl_.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream as TokenStream2; 2 | use quote::format_ident; 3 | use syn::parse_quote; 4 | use syn::punctuated::Punctuated; 5 | use syn::ImplItemFn; 6 | use syn::ItemImpl; 7 | 8 | use crate::util::Result; 9 | use crate::util::{self}; 10 | 11 | pub fn handle(mut impl_: ItemImpl) -> Result { 12 | if impl_.generics.params.first().is_some() { 13 | return Err(util::Error::Generics); 14 | } 15 | 16 | if impl_.generics.where_clause.is_some() { 17 | return Err(util::Error::WhereClause); 18 | } 19 | 20 | let self_ty = match *impl_.self_ty { 21 | syn::Type::Path(ref type_path) => type_path.path.clone(), 22 | _ => return Err(util::Error::UnsupportedType), 23 | }; 24 | 25 | let ref ty_str @ _ = self_ty.get_ident().unwrap(); 26 | 27 | let mut methods = Vec::new(); 28 | let mut syms = Punctuated::::new(); 29 | for item in impl_.items.iter_mut() { 30 | match item { 31 | syn::ImplItem::Fn(ImplItemFn { sig, attrs, .. }) => { 32 | let mut is_constructor = false; 33 | if let Some(attr) = attrs.first() { 34 | let path = attr.path(); 35 | is_constructor = path.is_ident("constructor"); 36 | 37 | attrs.clear(); 38 | } 39 | 40 | // TODO: Add common name magling util. 41 | let method_name = sig.ident.clone(); 42 | let mangled_name = format_ident!("__{}_{}", ty_str, method_name); 43 | // ... 44 | let ref out = sig.output; 45 | let inputs = sig.inputs.iter(); 46 | 47 | fn idents_with_skip<'a>( 48 | arg: syn::punctuated::Iter<'a, syn::FnArg>, 49 | skip: usize, 50 | ) -> Vec<&'a syn::Ident> { 51 | arg 52 | .skip(skip) 53 | .map(|arg| match arg { 54 | syn::FnArg::Receiver(_) => unreachable!(), 55 | syn::FnArg::Typed(pat_type) => match &*pat_type.pat { 56 | syn::Pat::Ident(ident) => &ident.ident, 57 | _ => unreachable!(), 58 | }, 59 | }) 60 | .collect::>() 61 | } 62 | 63 | let method = if sig.receiver().is_some() { 64 | let idents = idents_with_skip(inputs.clone(), 1); 65 | // First argument is the receiver, we skip it. 66 | let inputs = inputs.skip(1); 67 | 68 | parse_quote! { 69 | #[allow(non_snake_case)] 70 | fn #mangled_name (self_: *mut #ty_str, #(#inputs),*) #out { 71 | let self_ = unsafe { &mut *self_ }; 72 | self_. #method_name (#(#idents),*) 73 | } 74 | } 75 | } else if is_constructor { 76 | let idents = idents_with_skip(inputs.clone(), 0); 77 | parse_quote!( 78 | #[allow(non_snake_case)] 79 | fn #mangled_name (#(#inputs),*) #out { 80 | #ty_str:: #method_name (#(#idents),*) 81 | } 82 | ) 83 | } else { 84 | return Err(util::Error::MissingReceiver); 85 | }; 86 | 87 | let (generated, mut sym) = crate::fn_::handle_inner( 88 | method, 89 | crate::FnAttributes { 90 | internal: true, 91 | constructor: is_constructor, 92 | ..Default::default() 93 | }, 94 | )?; 95 | 96 | // Set method name to the original name as the 97 | // managed name is used for the internal symbol. 98 | sym.set_name(method_name); 99 | 100 | methods.push(generated); 101 | syms.push(quote::quote! { #sym }); 102 | } 103 | _ => {} 104 | } 105 | } 106 | 107 | // Generate a dealloc method. 108 | { 109 | let ident = format_ident!("__{}_dealloc", ty_str); 110 | let dispose = parse_quote! { 111 | #[allow(non_snake_case)] 112 | fn #ident(self_: *mut #ty_str) { 113 | if self_.is_null() { 114 | return; 115 | } 116 | unsafe { drop(Box::from_raw(self_)) } 117 | } 118 | }; 119 | let (generated, mut sym) = crate::fn_::handle_inner( 120 | dispose, 121 | crate::FnAttributes { 122 | internal: true, 123 | ..Default::default() 124 | }, 125 | )?; 126 | 127 | sym.set_name(format_ident!("dealloc")); 128 | 129 | methods.push(generated); 130 | syms.push(quote::quote! { #sym }); 131 | } 132 | 133 | Ok(quote::quote! { 134 | #impl_ 135 | #(#methods)* 136 | const _: () = { 137 | // Assert that the type implements `BindgenType`. 138 | const fn _assert_impl() {} 139 | _assert_impl::<#ty_str>(); 140 | 141 | #[deno_bindgen::linkme::distributed_slice(deno_bindgen::INVENTORY)] 142 | pub static _B: deno_bindgen::Inventory = deno_bindgen::Inventory::Struct( 143 | deno_bindgen::inventory::Struct { 144 | name: stringify!(#ty_str), 145 | methods: &[#syms], 146 | } 147 | ); 148 | }; 149 | }) 150 | } 151 | -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /e2e_test/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "Inflector" 7 | version = "0.11.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" 10 | dependencies = [ 11 | "lazy_static", 12 | "regex", 13 | ] 14 | 15 | [[package]] 16 | name = "aho-corasick" 17 | version = "1.1.2" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 20 | dependencies = [ 21 | "memchr", 22 | ] 23 | 24 | [[package]] 25 | name = "deno_bindgen" 26 | version = "0.8.1" 27 | dependencies = [ 28 | "deno_bindgen_ir", 29 | "deno_bindgen_macro", 30 | "linkme", 31 | "serde", 32 | "serde_json", 33 | ] 34 | 35 | [[package]] 36 | name = "deno_bindgen_e2e" 37 | version = "0.1.0" 38 | dependencies = [ 39 | "deno_bindgen", 40 | "linkme", 41 | "serde", 42 | ] 43 | 44 | [[package]] 45 | name = "deno_bindgen_ir" 46 | version = "0.1.0" 47 | dependencies = [ 48 | "proc-macro2", 49 | "quote", 50 | "syn", 51 | ] 52 | 53 | [[package]] 54 | name = "deno_bindgen_macro" 55 | version = "0.8.1" 56 | dependencies = [ 57 | "Inflector", 58 | "deno_bindgen_ir", 59 | "proc-macro2", 60 | "quote", 61 | "serde", 62 | "serde_json", 63 | "syn", 64 | ] 65 | 66 | [[package]] 67 | name = "itoa" 68 | version = "1.0.9" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 71 | 72 | [[package]] 73 | name = "lazy_static" 74 | version = "1.4.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 77 | 78 | [[package]] 79 | name = "linkme" 80 | version = "0.3.17" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "91ed2ee9464ff9707af8e9ad834cffa4802f072caad90639c583dd3c62e6e608" 83 | dependencies = [ 84 | "linkme-impl", 85 | ] 86 | 87 | [[package]] 88 | name = "linkme-impl" 89 | version = "0.3.17" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "ba125974b109d512fccbc6c0244e7580143e460895dfd6ea7f8bbb692fd94396" 92 | dependencies = [ 93 | "proc-macro2", 94 | "quote", 95 | "syn", 96 | ] 97 | 98 | [[package]] 99 | name = "memchr" 100 | version = "2.6.4" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 103 | 104 | [[package]] 105 | name = "proc-macro2" 106 | version = "1.0.69" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" 109 | dependencies = [ 110 | "unicode-ident", 111 | ] 112 | 113 | [[package]] 114 | name = "quote" 115 | version = "1.0.33" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 118 | dependencies = [ 119 | "proc-macro2", 120 | ] 121 | 122 | [[package]] 123 | name = "regex" 124 | version = "1.10.2" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" 127 | dependencies = [ 128 | "aho-corasick", 129 | "memchr", 130 | "regex-automata", 131 | "regex-syntax", 132 | ] 133 | 134 | [[package]] 135 | name = "regex-automata" 136 | version = "0.4.3" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" 139 | dependencies = [ 140 | "aho-corasick", 141 | "memchr", 142 | "regex-syntax", 143 | ] 144 | 145 | [[package]] 146 | name = "regex-syntax" 147 | version = "0.8.2" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 150 | 151 | [[package]] 152 | name = "ryu" 153 | version = "1.0.15" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 156 | 157 | [[package]] 158 | name = "serde" 159 | version = "1.0.190" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" 162 | dependencies = [ 163 | "serde_derive", 164 | ] 165 | 166 | [[package]] 167 | name = "serde_derive" 168 | version = "1.0.190" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" 171 | dependencies = [ 172 | "proc-macro2", 173 | "quote", 174 | "syn", 175 | ] 176 | 177 | [[package]] 178 | name = "serde_json" 179 | version = "1.0.108" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" 182 | dependencies = [ 183 | "itoa", 184 | "ryu", 185 | "serde", 186 | ] 187 | 188 | [[package]] 189 | name = "syn" 190 | version = "2.0.38" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" 193 | dependencies = [ 194 | "proc-macro2", 195 | "quote", 196 | "unicode-ident", 197 | ] 198 | 199 | [[package]] 200 | name = "unicode-ident" 201 | version = "1.0.12" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 204 | -------------------------------------------------------------------------------- /e2e_test/bindings/mod.ts: -------------------------------------------------------------------------------- 1 | // deno-lint-ignore-file 2 | 3 | // This file is automatically generated by deno_bindgen. 4 | // Do not edit this file directly. 5 | 6 | const { dlopen } = Deno; 7 | 8 | const { symbols } = dlopen("/Users/divy/gh/deno_bindgen/e2e_test/target/debug/libdeno_bindgen_e2e.dylib", { 9 | add: { 10 | parameters: [ 11 | 'i32', 12 | 'i32', 13 | ], 14 | result: 'i32', 15 | nonblocking: false 16 | }, 17 | __Input_new: { 18 | parameters: [ 19 | 'i32', 20 | 'i32', 21 | ], 22 | result: 'pointer', 23 | nonblocking: false 24 | }, 25 | __Input_dealloc: { 26 | parameters: [ 27 | 'pointer', 28 | ], 29 | result: 'void', 30 | nonblocking: false 31 | }, 32 | add2: { 33 | parameters: [ 34 | 'pointer', 35 | ], 36 | result: 'i32', 37 | nonblocking: false 38 | }, 39 | bytelen: { 40 | parameters: [ 41 | 'buffer', 42 | 'usize', 43 | ], 44 | result: 'u32', 45 | nonblocking: false 46 | }, 47 | buf_mut: { 48 | parameters: [ 49 | 'buffer', 50 | 'usize', 51 | ], 52 | result: 'void', 53 | nonblocking: false 54 | }, 55 | cstr: { 56 | parameters: [], 57 | result: 'pointer', 58 | nonblocking: false 59 | }, 60 | strlen: { 61 | parameters: [ 62 | 'pointer', 63 | ], 64 | result: 'u32', 65 | nonblocking: false 66 | }, 67 | non_blocking: { 68 | parameters: [], 69 | result: 'i32', 70 | nonblocking: true 71 | }, 72 | make_foo: { 73 | parameters: [], 74 | result: 'pointer', 75 | nonblocking: false 76 | }, 77 | inc_foo: { 78 | parameters: [ 79 | 'pointer', 80 | ], 81 | result: 'void', 82 | nonblocking: false 83 | }, 84 | __Foo_new: { 85 | parameters: [ 86 | 'u32', 87 | ], 88 | result: 'pointer', 89 | nonblocking: false 90 | }, 91 | __Foo_inc: { 92 | parameters: [ 93 | 'pointer', 94 | ], 95 | result: 'void', 96 | nonblocking: false 97 | }, 98 | __Foo_bar: { 99 | parameters: [ 100 | 'pointer', 101 | 'u32', 102 | ], 103 | result: 'u32', 104 | nonblocking: false 105 | }, 106 | __Foo_dealloc: { 107 | parameters: [ 108 | 'pointer', 109 | ], 110 | result: 'void', 111 | nonblocking: false 112 | }, 113 | }); 114 | 115 | export function add( 116 | arg0: number, 117 | arg1: number, 118 | ): number { 119 | return symbols.add( 120 | arg0, 121 | arg1, 122 | ) 123 | } 124 | 125 | function __Input_new( 126 | arg0: number, 127 | arg1: number, 128 | ): Input { 129 | const ret = symbols.__Input_new( 130 | arg0, 131 | arg1, 132 | ) 133 | return Input.__constructor(ret); 134 | } 135 | 136 | function __Input_dealloc( 137 | arg0: Deno.PointerObject | null, 138 | ): void { 139 | return symbols.__Input_dealloc( 140 | arg0, 141 | ) 142 | } 143 | 144 | export class Input { 145 | ptr: Deno.PointerObject | null = null; 146 | 147 | static __constructor(ptr: Deno.PointerObject | null) { 148 | const self = Object.create(Input.prototype); 149 | self.ptr = ptr; 150 | return self; 151 | } 152 | 153 | [Symbol.dispose]() { 154 | this.dealloc(); 155 | this.ptr = null; 156 | } 157 | 158 | constructor(arg0: number, arg1: number) { 159 | return __Input_new( 160 | arg0, 161 | arg1, 162 | ) 163 | } 164 | 165 | dealloc(): void { 166 | return __Input_dealloc( 167 | this.ptr, 168 | ) 169 | } 170 | } 171 | 172 | export function add2( 173 | arg0: Input, 174 | ): number { 175 | return symbols.add2( 176 | arg0.ptr, 177 | ) 178 | } 179 | 180 | export function bytelen( 181 | arg0: Uint8Array, 182 | ): number { 183 | return symbols.bytelen( 184 | arg0, 185 | arg0.byteLength, 186 | ) 187 | } 188 | 189 | export function buf_mut( 190 | arg0: Uint8Array, 191 | ): void { 192 | return symbols.buf_mut( 193 | arg0, 194 | arg0.byteLength, 195 | ) 196 | } 197 | 198 | export function cstr(): Deno.PointerObject | null { 199 | return symbols.cstr() 200 | } 201 | 202 | export function strlen( 203 | arg0: Deno.PointerObject | null, 204 | ): number { 205 | return symbols.strlen( 206 | arg0, 207 | ) 208 | } 209 | 210 | export function non_blocking(): Promise { 211 | return symbols.non_blocking() 212 | } 213 | 214 | export function make_foo(): Foo { 215 | const ret = symbols.make_foo() 216 | return Foo.__constructor(ret); 217 | } 218 | 219 | export function inc_foo( 220 | arg0: Foo, 221 | ): void { 222 | return symbols.inc_foo( 223 | arg0.ptr, 224 | ) 225 | } 226 | 227 | function __Foo_new( 228 | arg0: number, 229 | ): Foo { 230 | const ret = symbols.__Foo_new( 231 | arg0, 232 | ) 233 | return Foo.__constructor(ret); 234 | } 235 | 236 | function __Foo_inc( 237 | arg0: Deno.PointerObject | null, 238 | ): void { 239 | return symbols.__Foo_inc( 240 | arg0, 241 | ) 242 | } 243 | 244 | function __Foo_bar( 245 | arg0: Deno.PointerObject | null, 246 | arg1: number, 247 | ): number { 248 | return symbols.__Foo_bar( 249 | arg0, 250 | arg1, 251 | ) 252 | } 253 | 254 | function __Foo_dealloc( 255 | arg0: Deno.PointerObject | null, 256 | ): void { 257 | return symbols.__Foo_dealloc( 258 | arg0, 259 | ) 260 | } 261 | 262 | export class Foo { 263 | ptr: Deno.PointerObject | null = null; 264 | 265 | static __constructor(ptr: Deno.PointerObject | null) { 266 | const self = Object.create(Foo.prototype); 267 | self.ptr = ptr; 268 | return self; 269 | } 270 | 271 | [Symbol.dispose]() { 272 | this.dealloc(); 273 | this.ptr = null; 274 | } 275 | 276 | constructor(arg0: number) { 277 | return __Foo_new( 278 | arg0, 279 | ) 280 | } 281 | 282 | inc(): void { 283 | return __Foo_inc( 284 | this.ptr, 285 | ) 286 | } 287 | 288 | bar(arg0: number): number { 289 | return __Foo_bar( 290 | this.ptr, 291 | arg0, 292 | ) 293 | } 294 | 295 | dealloc(): void { 296 | return __Foo_dealloc( 297 | this.ptr, 298 | ) 299 | } 300 | } 301 | 302 | -------------------------------------------------------------------------------- /deno_bindgen_ir/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Ident; 2 | use quote::quote; 3 | use quote::ToTokens; 4 | use syn::parse_quote; 5 | use syn::Pat; 6 | 7 | pub mod codegen; 8 | pub mod inventory; 9 | 10 | pub use inventory::Inventory; 11 | 12 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] 13 | pub enum Type { 14 | #[default] 15 | Void, 16 | Uint8, 17 | Uint16, 18 | Uint32, 19 | Uint64, 20 | Int8, 21 | Int16, 22 | Int32, 23 | Int64, 24 | Float32, 25 | Float64, 26 | Pointer, 27 | Buffer, 28 | 29 | CustomType(&'static str), 30 | } 31 | 32 | pub type RawTypes = &'static [Type]; 33 | 34 | impl Type { 35 | pub fn raw(&self) -> RawTypes { 36 | match self { 37 | Self::Buffer => &[Self::Pointer, Self::Uint32], 38 | Self::Pointer | Self::CustomType(..) => &[Self::Pointer], 39 | _ => &[], 40 | } 41 | } 42 | 43 | pub fn is_number(&self) -> bool { 44 | !matches!( 45 | self, 46 | Self::Void | Self::Pointer | Self::Buffer | Self::CustomType(_) 47 | ) 48 | } 49 | 50 | pub fn apply_arg_transform( 51 | &self, 52 | name: &mut Box, 53 | args: &[Ident], 54 | ) -> Option { 55 | match self { 56 | Self::Buffer => { 57 | let pointer = &args[0]; 58 | let length = &args[1]; 59 | Some(quote! { 60 | let #name = unsafe { 61 | std::slice::from_raw_parts_mut(#pointer as _, #length as usize) 62 | }; 63 | }) 64 | } 65 | Self::CustomType(_) => { 66 | let pointer = &args[0]; 67 | Some(quote! { 68 | debug_assert!(!#pointer.is_null()); 69 | let #name = unsafe { &mut *(#pointer as *mut _) }; 70 | }) 71 | } 72 | Self::Pointer => { 73 | let pointer = &args[0]; 74 | Some(quote! { 75 | let #name = #pointer as _; 76 | }) 77 | } 78 | _ => None, 79 | } 80 | } 81 | 82 | pub fn apply_ret_transform( 83 | &self, 84 | name: &mut Box, 85 | arg: Ident, 86 | ) -> Option { 87 | match self { 88 | Self::Pointer => Some(quote! { 89 | let #name = #arg as _; 90 | }), 91 | Self::CustomType(_) => Some(quote! { 92 | let #name = Box::into_raw(Box::new(#arg)) as *mut _; 93 | }), 94 | _ => None, 95 | } 96 | } 97 | 98 | pub fn to_ident(&self) -> syn::Expr { 99 | match self { 100 | Self::Void => parse_quote!(deno_bindgen::Type::Void), 101 | Self::Uint8 => parse_quote!(deno_bindgen::Type::Uint8), 102 | Self::Uint16 => parse_quote!(deno_bindgen::Type::Uint16), 103 | Self::Uint32 => parse_quote!(deno_bindgen::Type::Uint32), 104 | Self::Uint64 => parse_quote!(deno_bindgen::Type::Uint64), 105 | Self::Int8 => parse_quote!(deno_bindgen::Type::Int8), 106 | Self::Int16 => parse_quote!(deno_bindgen::Type::Int16), 107 | Self::Int32 => parse_quote!(deno_bindgen::Type::Int32), 108 | Self::Int64 => parse_quote!(deno_bindgen::Type::Int64), 109 | Self::Float32 => parse_quote!(deno_bindgen::Type::Float32), 110 | Self::Float64 => parse_quote!(deno_bindgen::Type::Float64), 111 | Self::Pointer => parse_quote!(deno_bindgen::Type::Pointer), 112 | Self::Buffer => parse_quote!(deno_bindgen::Type::Buffer), 113 | Self::CustomType(s) => parse_quote!(deno_bindgen::Type::CustomType(#s)), 114 | } 115 | } 116 | } 117 | 118 | impl ToTokens for Type { 119 | fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 120 | let ty = match self { 121 | Self::Void => quote! { () }, 122 | Self::Uint8 => quote! { u8 }, 123 | Self::Uint16 => quote! { u16 }, 124 | Self::Uint32 => quote! { u32 }, 125 | Self::Uint64 => quote! { u64 }, 126 | Self::Int8 => quote! { i8 }, 127 | Self::Int16 => quote! { i16 }, 128 | Self::Int32 => quote! { i32 }, 129 | Self::Int64 => quote! { i64 }, 130 | Self::Float32 => quote! { f32 }, 131 | Self::Float64 => quote! { f64 }, 132 | Self::CustomType(_) | Self::Pointer => quote! { *const () }, 133 | Self::Buffer => quote! { *mut u8 }, 134 | }; 135 | 136 | tokens.extend(ty); 137 | } 138 | } 139 | 140 | #[derive(Debug)] 141 | pub struct Symbol { 142 | pub name: &'static str, 143 | pub parameters: &'static [Type], 144 | pub return_type: Type, 145 | pub non_blocking: bool, 146 | pub internal: bool, 147 | pub is_constructor: bool, 148 | } 149 | 150 | pub struct SymbolBuilder { 151 | name: Ident, 152 | parameters: Vec, 153 | return_type: Type, 154 | non_blocking: bool, 155 | internal: bool, 156 | is_constructor: bool, 157 | } 158 | 159 | impl SymbolBuilder { 160 | pub fn new(name: Ident) -> Self { 161 | Self { 162 | name, 163 | parameters: Vec::new(), 164 | return_type: Default::default(), 165 | non_blocking: false, 166 | internal: false, 167 | is_constructor: false, 168 | } 169 | } 170 | 171 | pub fn set_name(&mut self, name: Ident) { 172 | self.name = name; 173 | } 174 | 175 | pub fn push(&mut self, ty: Type) { 176 | self.parameters.push(ty); 177 | } 178 | 179 | pub fn return_type(&mut self, ty: Type) { 180 | self.return_type = ty; 181 | } 182 | 183 | pub fn non_blocking(&mut self, non_blocking: bool) { 184 | self.non_blocking = non_blocking; 185 | } 186 | 187 | pub fn internal(&mut self, internal: bool) { 188 | self.internal = internal; 189 | } 190 | 191 | pub fn is_constructor(&mut self, is_constructor: bool) { 192 | self.is_constructor = is_constructor; 193 | } 194 | } 195 | 196 | impl ToTokens for SymbolBuilder { 197 | fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 198 | let parameters = &self 199 | .parameters 200 | .iter() 201 | .map(|ty| ty.to_ident()) 202 | .collect::>(); 203 | let return_type = &self.return_type.to_ident(); 204 | let non_blocking = &self.non_blocking; 205 | let name = &self.name; 206 | let internal = &self.internal; 207 | let is_constructor = &self.is_constructor; 208 | 209 | tokens.extend(quote! { 210 | deno_bindgen::Symbol { 211 | name: stringify!(#name), 212 | parameters: &[#(#parameters),*], 213 | return_type: #return_type, 214 | non_blocking: #non_blocking, 215 | internal: #internal, 216 | is_constructor: #is_constructor, 217 | } 218 | }); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /deno_bindgen_macro/src/fn_.rs: -------------------------------------------------------------------------------- 1 | use deno_bindgen_ir::SymbolBuilder; 2 | use deno_bindgen_ir::Type; 3 | use proc_macro2::Ident; 4 | use proc_macro2::Span; 5 | use proc_macro2::TokenStream as TokenStream2; 6 | use syn::parse_quote; 7 | use syn::punctuated::Punctuated; 8 | use syn::spanned::Spanned; 9 | use syn::token::Comma; 10 | use syn::FnArg; 11 | use syn::ItemFn; 12 | use syn::PatType; 13 | use syn::ReturnType; 14 | use syn::TypePath; 15 | use syn::TypePtr; 16 | use syn::TypeReference; 17 | use syn::TypeSlice; 18 | 19 | use crate::util::Error; 20 | use crate::util::Result; 21 | use crate::FnAttributes; 22 | 23 | fn custom_type(ty: &str) -> Type { 24 | // yeah, don't worry about it. 25 | Type::CustomType(Box::leak(ty.to_string().into_boxed_str())) 26 | } 27 | 28 | fn parse_type(ty: &Box) -> Result { 29 | match **ty { 30 | syn::Type::Path(TypePath { ref path, .. }) => { 31 | if let Some(ident) = path.get_ident() { 32 | match ident.to_string().as_str() { 33 | "u8" => return Ok(Type::Uint8), 34 | "u16" => return Ok(Type::Uint16), 35 | "u32" => return Ok(Type::Uint32), 36 | "u64" => return Ok(Type::Uint64), 37 | "i8" => return Ok(Type::Int8), 38 | "i16" => return Ok(Type::Int16), 39 | "i32" => return Ok(Type::Int32), 40 | "i64" => return Ok(Type::Int64), 41 | "f32" => return Ok(Type::Float32), 42 | "f64" => return Ok(Type::Float64), 43 | "usize" => return Ok(Type::Uint64), 44 | "isize" => return Ok(Type::Int64), 45 | ty_str => { 46 | return Ok(custom_type(ty_str)); 47 | } 48 | } 49 | } 50 | 51 | Err(Error::UnsupportedType) 52 | } 53 | syn::Type::Reference(TypeReference { ref elem, .. }) => { 54 | if let syn::Type::Slice(TypeSlice { ref elem, .. }) = *elem.as_ref() { 55 | if parse_type(elem)?.is_number() { 56 | return Ok(Type::Buffer); 57 | } 58 | } 59 | 60 | if let syn::Type::Path(TypePath { ref path, .. }) = *elem.as_ref() { 61 | if let Some(ident) = path.get_ident() { 62 | let ref ty_str = ident.to_string(); 63 | return Ok(custom_type(ty_str)); 64 | } 65 | } 66 | 67 | Err(Error::UnsupportedType) 68 | } 69 | 70 | syn::Type::Ptr(TypePtr { .. }) => Ok(Type::Pointer), 71 | _ => Err(Error::UnsupportedType), 72 | } 73 | } 74 | 75 | pub(crate) fn handle_inner( 76 | fn_: ItemFn, 77 | attrs: FnAttributes, 78 | ) -> Result<(TokenStream2, SymbolBuilder)> { 79 | if fn_.sig.asyncness.is_some() { 80 | return Err(Error::Asyncness); 81 | } 82 | 83 | if fn_.sig.receiver().is_some() { 84 | return Err(Error::Reciever); 85 | } 86 | 87 | // TODO: check right ABI 88 | 89 | let mut ffi_fn = fn_.clone(); 90 | ffi_fn.sig.abi.get_or_insert_with(|| { 91 | parse_quote!( 92 | extern "C" 93 | ) 94 | }); 95 | 96 | let mut inputs: Punctuated = Punctuated::new(); 97 | let mut transforms: Vec = Vec::new(); 98 | 99 | let mut symbol = SymbolBuilder::new(fn_.sig.ident.clone()); 100 | symbol.non_blocking(attrs.non_blocking); 101 | symbol.internal(attrs.internal); 102 | symbol.is_constructor(attrs.constructor); 103 | 104 | // Cannot use enumerate here, there can be multiple raw args per type. 105 | let mut i = 0; 106 | for arg in ffi_fn.sig.inputs.iter_mut() { 107 | match *arg { 108 | FnArg::Receiver(_) => unreachable!(), 109 | FnArg::Typed(PatType { 110 | ref mut pat, 111 | ref mut ty, 112 | .. 113 | }) => { 114 | let ty = parse_type(ty)?; 115 | symbol.push(ty); 116 | 117 | const X_ARG_PREFIX: &str = "__arg_"; 118 | // Divide the type into its raw components. 119 | let raw_ty = ty.raw(); 120 | 121 | // Complex types, that need transforms. 122 | let mut idents = Vec::new(); 123 | for ty in raw_ty { 124 | let arg_name = Ident::new( 125 | &format!("{}{}", X_ARG_PREFIX, i), 126 | Span::mixed_site().located_at(pat.span()), 127 | ); 128 | inputs.push(parse_quote!(#arg_name: #ty)); 129 | idents.push(arg_name); 130 | i += 1; 131 | } 132 | 133 | // Apply the transform. 134 | if let Some(transform) = ty.apply_arg_transform(pat, &idents) { 135 | transforms.push(transform); 136 | } 137 | 138 | // Simple type. 139 | if raw_ty.len() == 0 { 140 | inputs.push(arg.clone()); 141 | i += 1; 142 | } 143 | } 144 | } 145 | } 146 | 147 | let ret_ident = Ident::new("ret", Span::mixed_site()); 148 | let mut ret = Box::new(syn::Pat::Ident(syn::PatIdent { 149 | attrs: Vec::new(), 150 | by_ref: None, 151 | mutability: None, 152 | ident: ret_ident.clone(), 153 | subpat: None, 154 | })); 155 | let mut ret_transform = TokenStream2::new(); 156 | match ffi_fn.sig.output { 157 | ReturnType::Default => {} 158 | ReturnType::Type(_, ref mut ty) => { 159 | let t = parse_type(ty)?; 160 | 161 | if let Some(transform) = 162 | t.apply_ret_transform(&mut ret, ret_ident.clone()) 163 | { 164 | ret_transform = transform; 165 | } 166 | 167 | symbol.return_type(t); 168 | **ty = parse_quote!(#t) 169 | } 170 | } 171 | 172 | let idents = ffi_fn 173 | .sig 174 | .inputs 175 | .iter() 176 | .map(|arg| match arg { 177 | FnArg::Receiver(_) => unreachable!(), 178 | FnArg::Typed(PatType { ref pat, .. }) => match &**pat { 179 | syn::Pat::Ident(ident) => ident.ident.clone(), 180 | _ => unreachable!(), 181 | }, 182 | }) 183 | .collect::>(); 184 | 185 | let name = fn_.sig.ident.clone(); 186 | ffi_fn.sig.inputs = inputs; 187 | 188 | ffi_fn.block = parse_quote!({ 189 | #fn_ 190 | 191 | #(#transforms)* 192 | 193 | let #ret_ident = #name(#(#idents),*); 194 | #ret_transform 195 | #ret_ident 196 | }); 197 | 198 | Ok(( 199 | quote::quote! { 200 | const _: () = { 201 | #[deno_bindgen::linkme::distributed_slice(deno_bindgen::INVENTORY)] 202 | pub static _A: deno_bindgen::Inventory = deno_bindgen::Inventory::Symbol(#symbol); 203 | }; 204 | 205 | #[no_mangle] 206 | #ffi_fn 207 | }, 208 | symbol, 209 | )) 210 | } 211 | 212 | pub(crate) fn handle(fn_: ItemFn, attrs: FnAttributes) -> Result { 213 | let (ffi_fn, _) = handle_inner(fn_, attrs)?; 214 | Ok(ffi_fn) 215 | } 216 | -------------------------------------------------------------------------------- /deno_bindgen_ir/codegen/deno.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::io::Result; 3 | use std::io::Write; 4 | use std::path::Path; 5 | 6 | use super::Generator; 7 | use crate::inventory::Inventory; 8 | use crate::inventory::Struct; 9 | use crate::Type; 10 | 11 | // (ident, is_custom_type) 12 | struct TypeScriptType<'a>(&'a str, bool); 13 | 14 | impl std::fmt::Display for TypeScriptType<'_> { 15 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 16 | f.write_str(&self.0)?; 17 | Ok(()) 18 | } 19 | } 20 | 21 | impl TypeScriptType<'_> { 22 | fn into_raw<'a>(&self, ident: &'a str) -> Cow<'a, str> { 23 | match self { 24 | Self("Uint8Array", false) => { 25 | Cow::Owned(format!("{ident},\n {ident}.byteLength")) 26 | } 27 | Self(_, true) => Cow::Owned(format!("{ident}.ptr")), 28 | _ => Cow::Borrowed(ident), 29 | } 30 | } 31 | 32 | fn from_raw<'a>(&self, ident: &'a str) -> Option { 33 | match self { 34 | Self(ty_str, true) => Some(format!("{ty_str}.__constructor({ident})")), 35 | _ => None, 36 | } 37 | } 38 | 39 | fn apply_promise(&self, non_blocking: bool) -> Cow<'_, str> { 40 | if non_blocking { 41 | Cow::Owned(format!("Promise<{}>", self.0)) 42 | } else { 43 | Cow::Borrowed(self.0) 44 | } 45 | } 46 | } 47 | 48 | impl From for TypeScriptType<'_> { 49 | fn from(value: Type) -> Self { 50 | Self( 51 | match value { 52 | Type::Void => "void", 53 | Type::Uint8 54 | | Type::Uint16 55 | | Type::Uint32 56 | | Type::Uint64 57 | | Type::Int8 58 | | Type::Int16 59 | | Type::Int32 60 | | Type::Int64 61 | | Type::Float32 62 | | Type::Float64 => "number", 63 | Type::Pointer => "Deno.PointerObject | null", 64 | Type::Buffer => "Uint8Array", 65 | Type::CustomType(name) => name, 66 | }, 67 | matches!(value, Type::CustomType(_)), 68 | ) 69 | } 70 | } 71 | 72 | struct DenoFfiType(String); 73 | 74 | impl std::fmt::Display for DenoFfiType { 75 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 76 | f.write_str(&self.0)?; 77 | Ok(()) 78 | } 79 | } 80 | 81 | impl From for DenoFfiType { 82 | fn from(value: Type) -> Self { 83 | let ty = match value { 84 | Type::Void => "void", 85 | Type::Uint8 => "u8", 86 | Type::Uint16 => "u16", 87 | Type::Uint32 => "u32", 88 | Type::Uint64 => "u64", 89 | Type::Int8 => "i8", 90 | Type::Int16 => "i16", 91 | Type::Int32 => "i32", 92 | Type::Int64 => "i64", 93 | Type::Float32 => "f32", 94 | Type::Float64 => "f64", 95 | Type::CustomType(..) | Type::Pointer => "pointer", 96 | Type::Buffer => "buffer", 97 | }; 98 | 99 | let mut raw = format!("'{}'", ty); 100 | 101 | // Complex types. 102 | if value == Type::Buffer { 103 | raw.push_str(",\n 'usize'"); 104 | } 105 | 106 | Self(raw) 107 | } 108 | } 109 | 110 | pub struct Codegen<'a> { 111 | symbols: &'a [Inventory], 112 | target: &'a Path, 113 | lazy: bool, 114 | } 115 | 116 | impl<'a> Codegen<'a> { 117 | pub fn new(symbols: &'a [Inventory], target: &'a Path, lazy: bool) -> Self { 118 | Self { 119 | symbols, 120 | target, 121 | lazy, 122 | } 123 | } 124 | 125 | fn dlopen(&self, writer: &mut W) -> Result<()> { 126 | if self.lazy { 127 | return self.lazy_dlopen(writer); 128 | } 129 | writeln!(writer, "const {{ dlopen }} = Deno;\n")?; 130 | writeln!(writer, "const {{ symbols }} = dlopen({:?}, {{", self.target)?; 131 | self.write_symbols(writer)?; 132 | writeln!(writer, "}});\n")?; 133 | 134 | Ok(()) 135 | } 136 | 137 | fn lazy_dlopen(&self, writer: &mut W) -> Result<()> { 138 | writeln!(writer, "let symbols: any;\n")?; 139 | writeln!( 140 | writer, 141 | "export function load(path: string = {:?}) {{", 142 | self.target 143 | )?; 144 | writeln!(writer, " const {{ dlopen }} = Deno;\n")?; 145 | writeln!(writer, " const {{ symbols: symbols_ }} = dlopen(path, {{")?; 146 | struct WrapperWriter<'a, W: Write> { 147 | writer: &'a mut W, 148 | indent: usize, 149 | } 150 | impl Write for WrapperWriter<'_, W> { 151 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 152 | // Find newlines and indent them. 153 | for byte in buf { 154 | if *byte == b'\n' { 155 | self.writer.write_all(b"\n")?; 156 | self.writer.write_all(&vec![b' '; self.indent])?; 157 | } else { 158 | self.writer.write_all(&[*byte])?; 159 | } 160 | } 161 | 162 | Ok(buf.len()) 163 | } 164 | 165 | fn flush(&mut self) -> std::io::Result<()> { 166 | self.writer.flush() 167 | } 168 | } 169 | write!(writer, " ")?; 170 | let mut wr = WrapperWriter { writer, indent: 2 }; 171 | self.write_symbols(&mut wr)?; 172 | writeln!(wr, "}});\n")?; 173 | write!(wr, "symbols = symbols_;")?; 174 | writeln!(writer, "\n}}\n")?; 175 | 176 | Ok(()) 177 | } 178 | 179 | fn write_symbols(&self, writer: &mut W) -> Result<()> { 180 | fn format_bracket( 181 | writer: &mut W, 182 | items: &[T], 183 | callback: impl Fn(&mut W, &[T]) -> Result<()>, 184 | ) -> Result<()> { 185 | write!(writer, "[")?; 186 | if items.len() > 0 { 187 | write!(writer, "\n")?; 188 | callback(writer, items)?; 189 | writeln!(writer, " ],")?; 190 | } else { 191 | writeln!(writer, "],")?; 192 | } 193 | 194 | Ok(()) 195 | } 196 | 197 | for symbol in self.symbols { 198 | match symbol { 199 | Inventory::Symbol(symbol) => { 200 | writeln!(writer, " {}: {{", symbol.name)?; 201 | write!(writer, " parameters: ")?; 202 | format_bracket(writer, symbol.parameters, |writer, parameters| { 203 | for parameter in parameters { 204 | writeln!(writer, " {},", DenoFfiType::from(*parameter))?; 205 | } 206 | Ok(()) 207 | })?; 208 | writeln!( 209 | writer, 210 | " result: {},", 211 | DenoFfiType::from(symbol.return_type) 212 | )?; 213 | writeln!(writer, " nonblocking: {}", symbol.non_blocking)?; 214 | writeln!(writer, " }},")?; 215 | } 216 | _ => {} 217 | } 218 | } 219 | 220 | Ok(()) 221 | } 222 | 223 | fn exports(&self, writer: &mut W) -> Result<()> { 224 | fn format_paren( 225 | writer: &mut W, 226 | items: &[T], 227 | allow_empty: bool, 228 | callback: impl Fn(&mut W, &[T]) -> Result<()>, 229 | nesting_spaces: usize, 230 | delim: (char, &str), 231 | ) -> Result<()> { 232 | let (start, end) = delim; 233 | write!(writer, "{start}")?; 234 | if items.len() > 0 || allow_empty { 235 | write!(writer, "\n")?; 236 | callback(writer, items)?; 237 | write!(writer, "{:indent$}{end}", "", indent = nesting_spaces)?; 238 | } else { 239 | write!(writer, "{end}")?; 240 | } 241 | 242 | Ok(()) 243 | } 244 | 245 | for symbol in self.symbols { 246 | match symbol { 247 | Inventory::Symbol(symbol) => { 248 | if !symbol.internal { 249 | write!(writer, "export ")?; 250 | } 251 | write!(writer, "function {}", symbol.name)?; 252 | format_paren( 253 | writer, 254 | symbol.parameters, 255 | false, 256 | |writer, parameters| { 257 | for (i, parameter) in parameters.iter().enumerate() { 258 | writeln!( 259 | writer, 260 | " arg{}: {},", 261 | i, 262 | TypeScriptType::from(*parameter) 263 | )?; 264 | } 265 | Ok(()) 266 | }, 267 | 0, 268 | ('(', ")"), 269 | )?; 270 | let ret_ty = TypeScriptType::from(symbol.return_type); 271 | writeln!( 272 | writer, 273 | ": {} {{", 274 | ret_ty.apply_promise(symbol.non_blocking) 275 | )?; 276 | let maybe_ret_transform = ret_ty.from_raw("ret"); 277 | if maybe_ret_transform.is_some() { 278 | write!(writer, " const ret = ")?; 279 | } else { 280 | write!(writer, " return ")?; 281 | } 282 | write!(writer, "symbols.{}", symbol.name)?; 283 | format_paren( 284 | writer, 285 | symbol.parameters, 286 | false, 287 | |writer, parameters| { 288 | for (i, parameter) in parameters.iter().enumerate() { 289 | let ident = format!("arg{}", i); 290 | writeln!( 291 | writer, 292 | " {},", 293 | TypeScriptType::from(*parameter).into_raw(&ident) 294 | )?; 295 | } 296 | Ok(()) 297 | }, 298 | 2, 299 | ('(', ")"), 300 | )?; 301 | 302 | if let Some(ret_transform) = maybe_ret_transform { 303 | write!(writer, "\n return {ret_transform};")?; 304 | } 305 | writeln!(writer, "\n}}\n")?; 306 | } 307 | Inventory::Struct(Struct { name, methods }) => { 308 | write!(writer, "export class {name} ")?; 309 | 310 | format_paren( 311 | writer, 312 | &methods, 313 | false, 314 | |writer, methods| { 315 | writeln!(writer, " ptr: Deno.PointerObject | null = null;\n")?; 316 | 317 | // Internal constructor. 318 | writeln!( 319 | writer, 320 | " static __constructor(ptr: Deno.PointerObject | null) {{" 321 | )?; 322 | writeln!( 323 | writer, 324 | " const self = Object.create({name}.prototype);" 325 | )?; 326 | writeln!(writer, " self.ptr = ptr;")?; 327 | writeln!(writer, " return self;")?; 328 | writeln!(writer, " }}\n")?; 329 | 330 | // Dispose method (explicit resource management) 331 | writeln!(writer, " [Symbol.dispose]() {{")?; 332 | writeln!(writer, " this.dealloc();")?; 333 | writeln!(writer, " this.ptr = null;")?; 334 | writeln!(writer, " }}")?; 335 | 336 | for method in methods { 337 | let mut parameters = method.parameters; 338 | 339 | if !method.is_constructor { 340 | // Skip the self ptr argument. 341 | parameters = &method.parameters[1..]; 342 | } 343 | 344 | let method_name = if method.is_constructor { 345 | "constructor" 346 | } else { 347 | &method.name 348 | }; 349 | 350 | write!( 351 | writer, 352 | "\n {name}({parameters})", 353 | name = method_name, 354 | parameters = parameters 355 | .iter() 356 | .enumerate() 357 | .map(|(i, parameter)| { 358 | format!("arg{}: {}", i, TypeScriptType::from(*parameter)) 359 | }) 360 | .collect::>() 361 | .join(", "), 362 | )?; 363 | 364 | if !method.is_constructor { 365 | let return_type = TypeScriptType::from(method.return_type); 366 | writeln!(writer, ": {return_type} {{")?; 367 | } else { 368 | // Typescript doesn't allow constructors to have a return type. 369 | writeln!(writer, " {{")?; 370 | } 371 | 372 | // Apply name mangling. 373 | write!(writer, " return __{}_{}", name, method.name)?; 374 | format_paren( 375 | writer, 376 | parameters, 377 | !method.is_constructor, 378 | |writer, parameters| { 379 | if !method.is_constructor { 380 | writeln!(writer, " this.ptr,",)?; 381 | } 382 | 383 | for (i, parameter) in parameters.iter().enumerate() { 384 | let ident = format!("arg{}", i); 385 | writeln!( 386 | writer, 387 | " {},", 388 | TypeScriptType::from(*parameter).into_raw(&ident) 389 | )?; 390 | } 391 | 392 | Ok(()) 393 | }, 394 | 4, 395 | ('(', ")"), 396 | )?; 397 | 398 | writeln!(writer, "\n }}")?; 399 | } 400 | Ok(()) 401 | }, 402 | 0, 403 | ('{', "}\n\n"), 404 | )?; 405 | } 406 | } 407 | } 408 | 409 | Ok(()) 410 | } 411 | } 412 | 413 | impl<'a> Generator for Codegen<'a> { 414 | fn generate(&mut self, mut writer: W) -> Result<()> { 415 | fn prelude(writer: &mut W) -> Result<()> { 416 | writeln!(writer, "// deno-lint-ignore-file\n")?; 417 | writeln!( 418 | writer, 419 | "// This file is automatically generated by deno_bindgen." 420 | )?; 421 | writeln!(writer, "// Do not edit this file directly.\n")?; 422 | Ok(()) 423 | } 424 | 425 | prelude(&mut writer)?; 426 | self.dlopen(&mut writer)?; 427 | self.exports(&mut writer)?; 428 | 429 | Ok(()) 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "Inflector" 7 | version = "0.11.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" 10 | dependencies = [ 11 | "lazy_static", 12 | "regex", 13 | ] 14 | 15 | [[package]] 16 | name = "aho-corasick" 17 | version = "0.7.18" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 20 | dependencies = [ 21 | "memchr", 22 | ] 23 | 24 | [[package]] 25 | name = "ansi_term" 26 | version = "0.12.1" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 29 | dependencies = [ 30 | "winapi", 31 | ] 32 | 33 | [[package]] 34 | name = "anyhow" 35 | version = "1.0.75" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 38 | 39 | [[package]] 40 | name = "atty" 41 | version = "0.2.14" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 44 | dependencies = [ 45 | "hermit-abi", 46 | "libc", 47 | "winapi", 48 | ] 49 | 50 | [[package]] 51 | name = "basic-toml" 52 | version = "0.1.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "2f2139706359229bfa8f19142ac1155b4b80beafb7a60471ac5dd109d4a19778" 55 | dependencies = [ 56 | "serde", 57 | ] 58 | 59 | [[package]] 60 | name = "bitflags" 61 | version = "1.3.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 64 | 65 | [[package]] 66 | name = "camino" 67 | version = "1.1.6" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" 70 | dependencies = [ 71 | "serde", 72 | ] 73 | 74 | [[package]] 75 | name = "cargo-platform" 76 | version = "0.1.4" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36" 79 | dependencies = [ 80 | "serde", 81 | ] 82 | 83 | [[package]] 84 | name = "cargo_metadata" 85 | version = "0.18.1" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" 88 | dependencies = [ 89 | "camino", 90 | "cargo-platform", 91 | "semver", 92 | "serde", 93 | "serde_json", 94 | "thiserror", 95 | ] 96 | 97 | [[package]] 98 | name = "clap" 99 | version = "2.34.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 102 | dependencies = [ 103 | "ansi_term", 104 | "atty", 105 | "bitflags", 106 | "strsim", 107 | "textwrap", 108 | "unicode-width", 109 | "vec_map", 110 | ] 111 | 112 | [[package]] 113 | name = "deno_bindgen" 114 | version = "0.9.0-alpha" 115 | dependencies = [ 116 | "deno_bindgen_ir", 117 | "deno_bindgen_macro", 118 | "linkme", 119 | "serde", 120 | "serde_json", 121 | "trybuild", 122 | ] 123 | 124 | [[package]] 125 | name = "deno_bindgen_cli" 126 | version = "0.1.0" 127 | dependencies = [ 128 | "cargo_metadata", 129 | "deno_bindgen_ir", 130 | "dlopen2", 131 | "structopt", 132 | ] 133 | 134 | [[package]] 135 | name = "deno_bindgen_ir" 136 | version = "0.1.0" 137 | dependencies = [ 138 | "proc-macro2", 139 | "quote", 140 | "syn 2.0.39", 141 | ] 142 | 143 | [[package]] 144 | name = "deno_bindgen_macro" 145 | version = "0.9.0-alpha" 146 | dependencies = [ 147 | "Inflector", 148 | "deno_bindgen_ir", 149 | "prettyplease", 150 | "proc-macro2", 151 | "quote", 152 | "serde", 153 | "serde_json", 154 | "syn 2.0.39", 155 | "testing_macros", 156 | ] 157 | 158 | [[package]] 159 | name = "dlopen2" 160 | version = "0.6.1" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "6bc2c7ed06fd72a8513ded8d0d2f6fd2655a85d6885c48cae8625d80faf28c03" 163 | dependencies = [ 164 | "dlopen2_derive", 165 | "libc", 166 | "once_cell", 167 | "winapi", 168 | ] 169 | 170 | [[package]] 171 | name = "dlopen2_derive" 172 | version = "0.4.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" 175 | dependencies = [ 176 | "proc-macro2", 177 | "quote", 178 | "syn 2.0.39", 179 | ] 180 | 181 | [[package]] 182 | name = "glob" 183 | version = "0.3.1" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 186 | 187 | [[package]] 188 | name = "heck" 189 | version = "0.3.3" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 192 | dependencies = [ 193 | "unicode-segmentation", 194 | ] 195 | 196 | [[package]] 197 | name = "hermit-abi" 198 | version = "0.1.19" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 201 | dependencies = [ 202 | "libc", 203 | ] 204 | 205 | [[package]] 206 | name = "itoa" 207 | version = "1.0.9" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 210 | 211 | [[package]] 212 | name = "lazy_static" 213 | version = "1.4.0" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 216 | 217 | [[package]] 218 | name = "libc" 219 | version = "0.2.150" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" 222 | 223 | [[package]] 224 | name = "linkme" 225 | version = "0.3.17" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "91ed2ee9464ff9707af8e9ad834cffa4802f072caad90639c583dd3c62e6e608" 228 | dependencies = [ 229 | "linkme-impl", 230 | ] 231 | 232 | [[package]] 233 | name = "linkme-impl" 234 | version = "0.3.17" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "ba125974b109d512fccbc6c0244e7580143e460895dfd6ea7f8bbb692fd94396" 237 | dependencies = [ 238 | "proc-macro2", 239 | "quote", 240 | "syn 2.0.39", 241 | ] 242 | 243 | [[package]] 244 | name = "memchr" 245 | version = "2.4.1" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 248 | 249 | [[package]] 250 | name = "once_cell" 251 | version = "1.18.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 254 | 255 | [[package]] 256 | name = "pmutil" 257 | version = "0.6.1" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" 260 | dependencies = [ 261 | "proc-macro2", 262 | "quote", 263 | "syn 2.0.39", 264 | ] 265 | 266 | [[package]] 267 | name = "prettyplease" 268 | version = "0.2.15" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" 271 | dependencies = [ 272 | "proc-macro2", 273 | "syn 2.0.39", 274 | ] 275 | 276 | [[package]] 277 | name = "proc-macro-error" 278 | version = "1.0.4" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 281 | dependencies = [ 282 | "proc-macro-error-attr", 283 | "proc-macro2", 284 | "quote", 285 | "syn 1.0.74", 286 | "version_check", 287 | ] 288 | 289 | [[package]] 290 | name = "proc-macro-error-attr" 291 | version = "1.0.4" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 294 | dependencies = [ 295 | "proc-macro2", 296 | "quote", 297 | "version_check", 298 | ] 299 | 300 | [[package]] 301 | name = "proc-macro2" 302 | version = "1.0.69" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" 305 | dependencies = [ 306 | "unicode-ident", 307 | ] 308 | 309 | [[package]] 310 | name = "quote" 311 | version = "1.0.33" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 314 | dependencies = [ 315 | "proc-macro2", 316 | ] 317 | 318 | [[package]] 319 | name = "regex" 320 | version = "1.5.4" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 323 | dependencies = [ 324 | "aho-corasick", 325 | "memchr", 326 | "regex-syntax", 327 | ] 328 | 329 | [[package]] 330 | name = "regex-syntax" 331 | version = "0.6.25" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 334 | 335 | [[package]] 336 | name = "relative-path" 337 | version = "1.9.0" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca" 340 | 341 | [[package]] 342 | name = "ryu" 343 | version = "1.0.5" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 346 | 347 | [[package]] 348 | name = "semver" 349 | version = "1.0.20" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" 352 | dependencies = [ 353 | "serde", 354 | ] 355 | 356 | [[package]] 357 | name = "serde" 358 | version = "1.0.192" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" 361 | dependencies = [ 362 | "serde_derive", 363 | ] 364 | 365 | [[package]] 366 | name = "serde_derive" 367 | version = "1.0.192" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" 370 | dependencies = [ 371 | "proc-macro2", 372 | "quote", 373 | "syn 2.0.39", 374 | ] 375 | 376 | [[package]] 377 | name = "serde_json" 378 | version = "1.0.108" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" 381 | dependencies = [ 382 | "itoa", 383 | "ryu", 384 | "serde", 385 | ] 386 | 387 | [[package]] 388 | name = "strsim" 389 | version = "0.8.0" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 392 | 393 | [[package]] 394 | name = "structopt" 395 | version = "0.3.26" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" 398 | dependencies = [ 399 | "clap", 400 | "lazy_static", 401 | "structopt-derive", 402 | ] 403 | 404 | [[package]] 405 | name = "structopt-derive" 406 | version = "0.4.18" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 409 | dependencies = [ 410 | "heck", 411 | "proc-macro-error", 412 | "proc-macro2", 413 | "quote", 414 | "syn 1.0.74", 415 | ] 416 | 417 | [[package]] 418 | name = "syn" 419 | version = "1.0.74" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" 422 | dependencies = [ 423 | "proc-macro2", 424 | "quote", 425 | "unicode-xid", 426 | ] 427 | 428 | [[package]] 429 | name = "syn" 430 | version = "2.0.39" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" 433 | dependencies = [ 434 | "proc-macro2", 435 | "quote", 436 | "unicode-ident", 437 | ] 438 | 439 | [[package]] 440 | name = "termcolor" 441 | version = "1.3.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" 444 | dependencies = [ 445 | "winapi-util", 446 | ] 447 | 448 | [[package]] 449 | name = "testing_macros" 450 | version = "0.2.11" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "d1c15b796025051a07f1ac695ee0cac0883f05a0d510c9d171ef8d31a992e6a5" 453 | dependencies = [ 454 | "anyhow", 455 | "glob", 456 | "once_cell", 457 | "pmutil", 458 | "proc-macro2", 459 | "quote", 460 | "regex", 461 | "relative-path", 462 | "syn 2.0.39", 463 | ] 464 | 465 | [[package]] 466 | name = "textwrap" 467 | version = "0.11.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 470 | dependencies = [ 471 | "unicode-width", 472 | ] 473 | 474 | [[package]] 475 | name = "thiserror" 476 | version = "1.0.50" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" 479 | dependencies = [ 480 | "thiserror-impl", 481 | ] 482 | 483 | [[package]] 484 | name = "thiserror-impl" 485 | version = "1.0.50" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" 488 | dependencies = [ 489 | "proc-macro2", 490 | "quote", 491 | "syn 2.0.39", 492 | ] 493 | 494 | [[package]] 495 | name = "trybuild" 496 | version = "1.0.85" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "196a58260a906cedb9bf6d8034b6379d0c11f552416960452f267402ceeddff1" 499 | dependencies = [ 500 | "basic-toml", 501 | "glob", 502 | "once_cell", 503 | "serde", 504 | "serde_derive", 505 | "serde_json", 506 | "termcolor", 507 | ] 508 | 509 | [[package]] 510 | name = "unicode-ident" 511 | version = "1.0.12" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 514 | 515 | [[package]] 516 | name = "unicode-segmentation" 517 | version = "1.10.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 520 | 521 | [[package]] 522 | name = "unicode-width" 523 | version = "0.1.11" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 526 | 527 | [[package]] 528 | name = "unicode-xid" 529 | version = "0.2.2" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 532 | 533 | [[package]] 534 | name = "vec_map" 535 | version = "0.8.2" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 538 | 539 | [[package]] 540 | name = "version_check" 541 | version = "0.9.4" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 544 | 545 | [[package]] 546 | name = "winapi" 547 | version = "0.3.9" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 550 | dependencies = [ 551 | "winapi-i686-pc-windows-gnu", 552 | "winapi-x86_64-pc-windows-gnu", 553 | ] 554 | 555 | [[package]] 556 | name = "winapi-i686-pc-windows-gnu" 557 | version = "0.4.0" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 560 | 561 | [[package]] 562 | name = "winapi-util" 563 | version = "0.1.6" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 566 | dependencies = [ 567 | "winapi", 568 | ] 569 | 570 | [[package]] 571 | name = "winapi-x86_64-pc-windows-gnu" 572 | version = "0.4.0" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 575 | --------------------------------------------------------------------------------