diff --git a/crates/js-component-bindgen/src/function_bindgen.rs b/crates/js-component-bindgen/src/function_bindgen.rs index 60bf93d75..9c31d4aa0 100644 --- a/crates/js-component-bindgen/src/function_bindgen.rs +++ b/crates/js-component-bindgen/src/function_bindgen.rs @@ -44,6 +44,7 @@ pub enum ErrHandling { pub struct ResourceTable { pub id: u32, pub imported: bool, + pub local_name: String, } pub type ResourceMap = BTreeMap; @@ -1118,7 +1119,11 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::HandleLift { handle, name, .. } => { let (Handle::Own(ty) | Handle::Borrow(ty)) = handle; - let ResourceTable { id, imported } = self.resource_map[ty]; + let ResourceTable { + id, + imported, + local_name, + } = &self.resource_map[ty]; let is_own = matches!(handle, Handle::Own(_)); let rsc = format!("rsc{}", self.tmp()); @@ -1128,7 +1133,7 @@ impl Bindgen for FunctionBindgen<'_> { let resource_symbol = self.intrinsic(Intrinsic::ResourceSymbol); uwrite!( self.src, - "const {rsc} = new.target === {class_name} ? this : Object.create({class_name}.prototype); + "const {rsc} = new.target === {local_name} ? this : Object.create({local_name}.prototype); Object.defineProperty({rsc}, {resource_symbol}, {{ writable: true, value: handleTable{id}.get({}).rep }}); ", operands[0], @@ -1168,7 +1173,11 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::HandleLower { handle, name, .. } => { let (Handle::Own(ty) | Handle::Borrow(ty)) = handle; let is_own = matches!(handle, Handle::Own(_)); - let ResourceTable { id, imported } = self.resource_map[ty]; + let ResourceTable { + id, + imported, + local_name, + } = &self.resource_map[ty]; let class_name = name.to_upper_camel_case(); let handle = format!("handle{}", self.tmp()); if !imported { @@ -1204,7 +1213,7 @@ impl Bindgen for FunctionBindgen<'_> { // their assigned rep is deduped across usage though uwriteln!( self.src, - "if (!({} instanceof {class_name})) {{ + "if (!({} instanceof {local_name})) {{ throw new Error('Not a valid \"{class_name}\" resource.'); }} const {handle} = handleCnt{id}++; diff --git a/crates/js-component-bindgen/src/transpile_bindgen.rs b/crates/js-component-bindgen/src/transpile_bindgen.rs index d52803759..384cfd5bc 100644 --- a/crates/js-component-bindgen/src/transpile_bindgen.rs +++ b/crates/js-component-bindgen/src/transpile_bindgen.rs @@ -807,10 +807,23 @@ impl<'a> Instantiator<'a, '_> { .defined_resource_index(self.types[*t2].ty) .is_none(); self.ensure_resource_table(*t2); + + let ty = &self.resolve.types[*t1]; + let resource = ty.name.as_ref().unwrap(); + let (local_name, _) = self.gen.local_names.get_or_create( + &if imported { + format!("import_resource:{resource}") + } else { + format!("resource:{resource}") + }, + &resource.to_upper_camel_case(), + ); + map.insert( *t1, ResourceTable { id: t2.as_u32(), + local_name: local_name.to_string(), imported, }, ); @@ -1175,7 +1188,7 @@ impl<'a> Instantiator<'a, '_> { let ty = &self.resolve.types[ty]; let resource = ty.name.as_ref().unwrap(); self.gen.local_names.get_or_create( - &format!("resource:{export_name}:{resource}"), + &format!("resource:{resource}"), &resource.to_upper_camel_case(), ) } else { diff --git a/test/codegen.js b/test/codegen.js index 5f6e535a5..5798d5f53 100644 --- a/test/codegen.js +++ b/test/codegen.js @@ -1,6 +1,8 @@ import { readFile } from 'node:fs/promises'; import { exec, jcoPath } from './helpers.js'; import { strictEqual } from 'node:assert'; +import { componentNew, componentEmbed, transpile } from '@bytecodealliance/jco'; +import { ok } from 'node:assert'; const eslintPath = `node_modules/eslint/bin/eslint.js`; @@ -39,4 +41,21 @@ export async function codegenTest (fixtures) { }); } }); + + suite(`Naming`, () => { + test(`Resource deduping`, async () => { + const component = await componentNew(await componentEmbed({ + witSource: await readFile(`test/fixtures/wits/resource-naming/resource-naming.wit`, 'utf8'), + dummy: true, + metadata: [['language', [['javascript', '']]], ['processed-by', [['dummy-gen', 'test']]]] + })); + + const { files } = await transpile(component, { name: 'resource-naming' }); + + const bindingsSource = new TextDecoder().decode(files['resource-naming.js']); + + ok(bindingsSource.includes('const Thing$1 = class Thing')); + ok(bindingsSource.includes('Thing: Thing$1')); + }); + }); } diff --git a/test/fixtures/wits/resource-naming.wit b/test/fixtures/wits/resource-naming.wit new file mode 100644 index 000000000..a4dfd1bc7 --- /dev/null +++ b/test/fixtures/wits/resource-naming.wit @@ -0,0 +1,17 @@ +package test:resource-naming + +interface resource-import-and-export { + resource thing { + constructor(v: u32) + + foo: func() -> u32 + bar: func(v: u32) + + baz: static func(a: thing, b: thing) -> thing + } +} + +world resource-naming { + import resource-import-and-export + export resource-import-and-export +} diff --git a/test/fixtures/wits/resource-naming/resource-naming.wit b/test/fixtures/wits/resource-naming/resource-naming.wit new file mode 100644 index 000000000..7d6d7029c --- /dev/null +++ b/test/fixtures/wits/resource-naming/resource-naming.wit @@ -0,0 +1,17 @@ +package test:test + +interface resource-import-and-export { + resource thing { + constructor(v: u32) + + foo: func() -> u32 + bar: func(v: u32) + + baz: static func(a: thing, b: thing) -> thing + } +} + +world test { + import resource-import-and-export + export resource-import-and-export +}