Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add resource type #189

Merged
merged 8 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ pub struct TypeInfos {

impl TypeInfos {
#[must_use]
pub fn collect_from_functions(typedefs: &TypeDefArena, functions: &[Function]) -> Self {
pub fn collect_from_functions<'a>(
typedefs: &TypeDefArena,
functions: impl Iterator<Item = &'a Function>,
) -> Self {
let mut this = Self::default();

for func in functions {
Expand Down
55 changes: 48 additions & 7 deletions crates/gen-guest-js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,22 @@ pub struct Builder {

impl GeneratorBuilder for Builder {
fn build(self, interface: Interface) -> Box<dyn Generate> {
let infos = TypeInfos::collect_from_functions(&interface.typedefs, &interface.functions);
let methods = interface
.typedefs
.iter()
.filter_map(|(_, typedef)| {
if let TypeDefKind::Resource(methods) = &typedef.kind {
Some(methods.iter())
} else {
None
}
})
.flatten();

let infos = TypeInfos::collect_from_functions(
&interface.typedefs,
interface.functions.iter().chain(methods),
);

let serde_utils =
SerdeUtils::collect_from_functions(&interface.typedefs, &interface.functions);
Expand Down Expand Up @@ -80,6 +95,7 @@ export async function {ident} ({params}) {{

fn print_resource(
&self,
mod_ident: &str,
docs: &str,
ident: &str,
functions: &[Function],
Expand All @@ -91,13 +107,32 @@ export async function {ident} ({params}) {{
.iter()
.map(|func| {
let docs = self.print_docs(func);
let mod_ident = mod_ident.to_snake_case();
let resource_ident = ident.to_snake_case();
let ident = func.ident.to_lower_camel_case();
let params = print_function_params(&func.params);

let deserialize_result = func
.result
.as_ref()
.map(|res| self.print_deserialize_function_result(res))
.unwrap_or_default();

let serialize_params = func
.params
.iter()
.map(|(ident, ty)| self.print_serialize_ty(&ident.to_lower_camel_case(), ty))
.collect::<Vec<_>>()
.join(";\n");

format!(
r#"
{docs}
r#"{docs}
async {ident} ({params}) {{
const out = []
serializeU32(out, this.#id);
{serialize_params}

await fetch('ipc://localhost/{mod_ident}::resource::{resource_ident}/{ident}', {{ method: "POST", body: Uint8Array.from(out), headers: {{ 'Content-Type': 'application/octet-stream' }} }}){deserialize_result}
}}
"#
)
Expand All @@ -106,9 +141,9 @@ async {ident} ({params}) {{

let deserialize = if info.contains(TypeInfo::RESULT) {
format!(
"deserialize(de) {{
"static deserialize(de) {{
const self = new {ident}();
self.#id = deserializeU64(de);
self.#id = deserializeU32(de);
return self
}}"
)
Expand All @@ -117,7 +152,7 @@ async {ident} ({params}) {{
};

format!(
"{docs}\nclass {ident} {{
"{docs}\nexport class {ident} {{
#id;
{functions}
{deserialize}
Expand Down Expand Up @@ -292,7 +327,13 @@ impl Generate for JavaScript {
.filter_map(|(id, typedef)| {
let info = self.infos[id];
if let TypeDefKind::Resource(functions) = &typedef.kind {
Some(self.print_resource(&typedef.docs, &typedef.ident, functions, info))
Some(self.print_resource(
&self.interface.ident,
&typedef.docs,
&typedef.ident,
functions,
info,
))
} else {
None
}
Expand Down
21 changes: 14 additions & 7 deletions crates/gen-guest-js/tests/lists.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,35 +335,42 @@ serializeS64(out, val.c4)
}function serializeOtherVariant(out, val) {
if (val.A) {
serializeU32(out, 0);
return

return
}
if (val.B) {
serializeU32(out, 1);
return serializeU32(out, val.B)
serializeU32(out, val.B)
return
}
if (val.C) {
serializeU32(out, 2);
return serializeString(out, val.C)
serializeString(out, val.C)
return
}


throw new Error("unknown variant case")
}function serializeSomeVariant(out, val) {
if (val.A) {
serializeU32(out, 0);
return serializeString(out, val.A)
serializeString(out, val.A)
return
}
if (val.B) {
serializeU32(out, 1);
return

return
}
if (val.C) {
serializeU32(out, 2);
return serializeU32(out, val.C)
serializeU32(out, val.C)
return
}
if (val.D) {
serializeU32(out, 3);
return serializeList(out, (out, v) => serializeOtherVariant(out, v), val.D)
serializeList(out, (out, v) => serializeOtherVariant(out, v), val.D)
return
}


Expand Down
133 changes: 119 additions & 14 deletions crates/gen-guest-js/tests/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,68 @@ class Deserializer {
return out
}
}
// function varint_max(bits) {
// const BITS_PER_BYTE = 8;
// const BITS_PER_VARINT_BYTE = 7;

// const roundup_bits = bits + (BITS_PER_BYTE - 1);

// return Math.floor(roundup_bits / BITS_PER_VARINT_BYTE);
// }

const varint_max = {
16: 3,
32: 5,
64: 10,
128: 19
}
function max_of_last_byte(type) {
let extra_bits = type % 7;
return (1 << extra_bits) - 1;
}

function de_varint(de, bits) {
let out = 0;

for (let i = 0; i < varint_max[bits]; i++) {
const val = de.pop();
const carry = val & 0x7F;
out |= carry << (7 * i);

if ((val & 0x80) === 0) {
if (i === varint_max[bits] - 1 && val > max_of_last_byte(bits)) {
throw new Error('deserialize bad variant')
} else {
return out
}
}
}

throw new Error('deserialize bad variant')
}

function de_varint_big(de, bits) {
let out = 0n;

for (let i = 0; i < varint_max[bits]; i++) {
const val = de.pop();
const carry = BigInt(val) & 0x7Fn;
out |= carry << (7n * BigInt(i));

if ((val & 0x80) === 0) {
if (i === varint_max[bits] - 1 && val > max_of_last_byte(bits)) {
throw new Error('deserialize bad variant')
} else {
return out
}
}
}

throw new Error('deserialize bad variant')
}
function deserializeU32(de) {
return de_varint(de, 32)
}


/**
Expand Down Expand Up @@ -52,59 +114,102 @@ export async function constructorB () {
}


class A {
export class A {
#id;

/**
/**
*/
async f1 () {
}
const out = []
serializeU32(out, this.#id);


await fetch('ipc://localhost/resources::resource::a/f1', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
}
/**
* @param {number} a
*/
async f2 (a) {
}
const out = []
serializeU32(out, this.#id);
serializeU32(out, a)

await fetch('ipc://localhost/resources::resource::a/f2', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
}
/**
* @param {number} a
* @param {number} b
*/
async f3 (a, b) {
const out = []
serializeU32(out, this.#id);
serializeU32(out, a);
serializeU32(out, b)

await fetch('ipc://localhost/resources::resource::a/f3', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
}

deserialize(de) {
static deserialize(de) {
const self = new A();
self.#id = deserializeU64(de);
self.#id = deserializeU32(de);
return self
}
}
class B {
export class B {
#id;

/**
/**
* @returns {Promise<A>}
*/
async f1 () {
}
const out = []
serializeU32(out, this.#id);


await fetch('ipc://localhost/resources::resource::b/f1', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
.then(r => r.arrayBuffer())
.then(bytes => {
const de = new Deserializer(new Uint8Array(bytes))

return A.deserialize(de)
})
}
/**
* @param {A} x
* @returns {Promise<Result<number, _>>}
*/
async f2 (x) {
}
const out = []
serializeU32(out, this.#id);
x.serialize(out)

await fetch('ipc://localhost/resources::resource::b/f2', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
.then(r => r.arrayBuffer())
.then(bytes => {
const de = new Deserializer(new Uint8Array(bytes))

return deserializeResult(de, (de) => deserializeU32(de), () => {})
})
}
/**
* @param {A[] | null} x
* @returns {Promise<Result<A, _>>}
*/
async f3 (x) {
const out = []
serializeU32(out, this.#id);
serializeOption(out, (out, v) => serializeList(out, (out, v) => v.serialize(out), v), x)

await fetch('ipc://localhost/resources::resource::b/f3', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
.then(r => r.arrayBuffer())
.then(bytes => {
const de = new Deserializer(new Uint8Array(bytes))

return deserializeResult(de, (de) => A.deserialize(de), () => {})
})
}

deserialize(de) {
static deserialize(de) {
const self = new B();
self.#id = deserializeU64(de);
self.#id = deserializeU32(de);
return self
}
}
Loading