From 82132cf30e3d715a934e80389b100058940c9ebd Mon Sep 17 00:00:00 2001 From: Walter Gray <walter@0m.dev> Date: Thu, 16 Jan 2025 15:34:08 -0800 Subject: [PATCH 1/3] Add support for nested namespaces for C++ --- tool/src/cpp/formatter.rs | 2 ++ tool/src/cpp/header.rs | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tool/src/cpp/formatter.rs b/tool/src/cpp/formatter.rs index f9abef2f9..231a5e01a 100644 --- a/tool/src/cpp/formatter.rs +++ b/tool/src/cpp/formatter.rs @@ -56,6 +56,7 @@ impl<'tcx> Cpp2Formatter<'tcx> { .rename .apply(resolved.name().as_str().into()); if let Some(ref ns) = resolved.attrs().namespace { + let ns = ns.replace("::", std::path::MAIN_SEPARATOR_STR); format!("{ns}/{type_name}.d.hpp") } else { format!("{type_name}.d.hpp") @@ -70,6 +71,7 @@ impl<'tcx> Cpp2Formatter<'tcx> { .rename .apply(resolved.name().as_str().into()); if let Some(ref ns) = resolved.attrs().namespace { + let ns = ns.replace("::", std::path::MAIN_SEPARATOR_STR); format!("{ns}/{type_name}.hpp") } else { format!("{type_name}.hpp") diff --git a/tool/src/cpp/header.rs b/tool/src/cpp/header.rs index 0224ed32f..d5cf5818b 100644 --- a/tool/src/cpp/header.rs +++ b/tool/src/cpp/header.rs @@ -121,9 +121,12 @@ impl fmt::Write for Header { impl fmt::Display for Header { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let header_guard = &self.path; - let header_guard = header_guard.replace(".d.hpp", "_D_HPP").replace('/', "_"); - let header_guard = header_guard.replace(".hpp", "_HPP").replace('/', "_"); + let header_guard = &self + .path + .replace(".d.hpp", "_D_HPP") + .replace(".hpp", "_HPP") + .replace("\\", "_") + .replace("/", "_"); let body: Cow<str> = if self.body.is_empty() { "// No Content\n\n".into() } else { @@ -146,10 +149,20 @@ impl fmt::Display for Header { if ns == other_ns { Cow::Borrowed(file) } else { - Cow::Owned(format!("../{s}")) + Cow::Owned(format!( + "{}{s}", + "../".repeat( + s.chars().filter(|c| *c == '/' || *c == '\\').count() + 1 + ) + )) } } else { - Cow::Owned(format!("../{s}")) + Cow::Owned(format!( + "{}{s}", + "../".repeat( + ns.chars().filter(|c| *c == '/' || *c == '\\').count() + 1 + ) + )) } } else { Cow::Borrowed(s.as_str()) From 098fee94054c1a900945f91fbc46fd4d08d3d58f Mon Sep 17 00:00:00 2001 From: Walter Gray <walter@0m.dev> Date: Tue, 21 Jan 2025 17:36:29 -0800 Subject: [PATCH 2/3] Improve logic for computing paths to relative files for C++ --- .../cpp/include/nested/ns/Nested.d.hpp | 38 +++++++++ .../cpp/include/nested/ns/Nested.hpp | 47 +++++++++++ .../cpp/include/nested/ns2/Nested.d.hpp | 38 +++++++++ .../cpp/include/nested/ns2/Nested.hpp | 47 +++++++++++ feature_tests/cpp/tests/attrs.cpp | 13 +-- feature_tests/src/attrs.rs | 10 +++ tool/src/cpp/formatter.rs | 4 +- tool/src/cpp/header.rs | 81 +++++++++++++------ 8 files changed, 246 insertions(+), 32 deletions(-) create mode 100644 feature_tests/cpp/include/nested/ns/Nested.d.hpp create mode 100644 feature_tests/cpp/include/nested/ns/Nested.hpp create mode 100644 feature_tests/cpp/include/nested/ns2/Nested.d.hpp create mode 100644 feature_tests/cpp/include/nested/ns2/Nested.hpp diff --git a/feature_tests/cpp/include/nested/ns/Nested.d.hpp b/feature_tests/cpp/include/nested/ns/Nested.d.hpp new file mode 100644 index 000000000..ad331baee --- /dev/null +++ b/feature_tests/cpp/include/nested/ns/Nested.d.hpp @@ -0,0 +1,38 @@ +#ifndef nested_ns_Nested_D_HPP +#define nested_ns_Nested_D_HPP + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <memory> +#include <optional> +#include "../../diplomat_runtime.hpp" + + +namespace nested::ns { +namespace capi { + struct Nested; +} // namespace capi +} // namespace + +namespace nested::ns { +class Nested { +public: + + inline const nested::ns::capi::Nested* AsFFI() const; + inline nested::ns::capi::Nested* AsFFI(); + inline static const nested::ns::Nested* FromFFI(const nested::ns::capi::Nested* ptr); + inline static nested::ns::Nested* FromFFI(nested::ns::capi::Nested* ptr); + inline static void operator delete(void* ptr); +private: + Nested() = delete; + Nested(const nested::ns::Nested&) = delete; + Nested(nested::ns::Nested&&) noexcept = delete; + Nested operator=(const nested::ns::Nested&) = delete; + Nested operator=(nested::ns::Nested&&) noexcept = delete; + static void operator delete[](void*, size_t) = delete; +}; + +} // namespace +#endif // nested_ns_Nested_D_HPP diff --git a/feature_tests/cpp/include/nested/ns/Nested.hpp b/feature_tests/cpp/include/nested/ns/Nested.hpp new file mode 100644 index 000000000..8bec22bc1 --- /dev/null +++ b/feature_tests/cpp/include/nested/ns/Nested.hpp @@ -0,0 +1,47 @@ +#ifndef nested_ns_Nested_HPP +#define nested_ns_Nested_HPP + +#include "Nested.d.hpp" + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <memory> +#include <optional> +#include "../../diplomat_runtime.hpp" + + +namespace nested::ns { +namespace capi { + extern "C" { + + + void namespace_Nested_destroy(Nested* self); + + } // extern "C" +} // namespace capi +} // namespace + +inline const nested::ns::capi::Nested* nested::ns::Nested::AsFFI() const { + return reinterpret_cast<const nested::ns::capi::Nested*>(this); +} + +inline nested::ns::capi::Nested* nested::ns::Nested::AsFFI() { + return reinterpret_cast<nested::ns::capi::Nested*>(this); +} + +inline const nested::ns::Nested* nested::ns::Nested::FromFFI(const nested::ns::capi::Nested* ptr) { + return reinterpret_cast<const nested::ns::Nested*>(ptr); +} + +inline nested::ns::Nested* nested::ns::Nested::FromFFI(nested::ns::capi::Nested* ptr) { + return reinterpret_cast<nested::ns::Nested*>(ptr); +} + +inline void nested::ns::Nested::operator delete(void* ptr) { + nested::ns::capi::namespace_Nested_destroy(reinterpret_cast<nested::ns::capi::Nested*>(ptr)); +} + + +#endif // nested_ns_Nested_HPP diff --git a/feature_tests/cpp/include/nested/ns2/Nested.d.hpp b/feature_tests/cpp/include/nested/ns2/Nested.d.hpp new file mode 100644 index 000000000..9c81a5e30 --- /dev/null +++ b/feature_tests/cpp/include/nested/ns2/Nested.d.hpp @@ -0,0 +1,38 @@ +#ifndef nested_ns2_Nested_D_HPP +#define nested_ns2_Nested_D_HPP + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <memory> +#include <optional> +#include "../../diplomat_runtime.hpp" + + +namespace nested::ns2 { +namespace capi { + struct Nested; +} // namespace capi +} // namespace + +namespace nested::ns2 { +class Nested { +public: + + inline const nested::ns2::capi::Nested* AsFFI() const; + inline nested::ns2::capi::Nested* AsFFI(); + inline static const nested::ns2::Nested* FromFFI(const nested::ns2::capi::Nested* ptr); + inline static nested::ns2::Nested* FromFFI(nested::ns2::capi::Nested* ptr); + inline static void operator delete(void* ptr); +private: + Nested() = delete; + Nested(const nested::ns2::Nested&) = delete; + Nested(nested::ns2::Nested&&) noexcept = delete; + Nested operator=(const nested::ns2::Nested&) = delete; + Nested operator=(nested::ns2::Nested&&) noexcept = delete; + static void operator delete[](void*, size_t) = delete; +}; + +} // namespace +#endif // nested_ns2_Nested_D_HPP diff --git a/feature_tests/cpp/include/nested/ns2/Nested.hpp b/feature_tests/cpp/include/nested/ns2/Nested.hpp new file mode 100644 index 000000000..b6ca0ccf9 --- /dev/null +++ b/feature_tests/cpp/include/nested/ns2/Nested.hpp @@ -0,0 +1,47 @@ +#ifndef nested_ns2_Nested_HPP +#define nested_ns2_Nested_HPP + +#include "Nested.d.hpp" + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <memory> +#include <optional> +#include "../../diplomat_runtime.hpp" + + +namespace nested::ns2 { +namespace capi { + extern "C" { + + + void namespace_Nested2_destroy(Nested* self); + + } // extern "C" +} // namespace capi +} // namespace + +inline const nested::ns2::capi::Nested* nested::ns2::Nested::AsFFI() const { + return reinterpret_cast<const nested::ns2::capi::Nested*>(this); +} + +inline nested::ns2::capi::Nested* nested::ns2::Nested::AsFFI() { + return reinterpret_cast<nested::ns2::capi::Nested*>(this); +} + +inline const nested::ns2::Nested* nested::ns2::Nested::FromFFI(const nested::ns2::capi::Nested* ptr) { + return reinterpret_cast<const nested::ns2::Nested*>(ptr); +} + +inline nested::ns2::Nested* nested::ns2::Nested::FromFFI(nested::ns2::capi::Nested* ptr) { + return reinterpret_cast<nested::ns2::Nested*>(ptr); +} + +inline void nested::ns2::Nested::operator delete(void* ptr) { + nested::ns2::capi::namespace_Nested2_destroy(reinterpret_cast<nested::ns2::capi::Nested*>(ptr)); +} + + +#endif // nested_ns2_Nested_HPP diff --git a/feature_tests/cpp/tests/attrs.cpp b/feature_tests/cpp/tests/attrs.cpp index 932b5d509..ad616579a 100644 --- a/feature_tests/cpp/tests/attrs.cpp +++ b/feature_tests/cpp/tests/attrs.cpp @@ -2,18 +2,21 @@ #include "../include/ns/AttrOpaque1Renamed.hpp" #include "../include/ns/RenamedAttrEnum.hpp" #include "../include/Unnamespaced.hpp" +#include "../include/nested/ns/Nested.hpp" +#include "../include/nested/ns2/Nested.hpp" #include "assert.hpp" -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ std::unique_ptr<ns::AttrOpaque1Renamed> r = ns::AttrOpaque1Renamed::totally_not_new(); simple_assert_eq("method should call", r->method_renamed(), 77); simple_assert_eq("method should call", r->abirenamed(), 123); // These C names should also resolve - void* renamed = (void*)ns::capi::renamed_on_abi_only; - std::cout<<"Renamed function at "<<renamed<<std::endl; - renamed = (void*)ns::capi::namespace_AttrOpaque1_method; - std::cout<<"Renamed function at "<<renamed<<std::endl; + void *renamed = (void *)ns::capi::renamed_on_abi_only; + std::cout << "Renamed function at " << renamed << std::endl; + renamed = (void *)ns::capi::namespace_AttrOpaque1_method; + std::cout << "Renamed function at " << renamed << std::endl; ns::RenamedAttrEnum e = ns::RenamedAttrEnum::A; diff --git a/feature_tests/src/attrs.rs b/feature_tests/src/attrs.rs index b0f565f32..89c0c8950 100644 --- a/feature_tests/src/attrs.rs +++ b/feature_tests/src/attrs.rs @@ -60,6 +60,16 @@ pub mod ffi { pub fn use_namespaced(&self, _n: &AttrOpaque1) {} } + #[diplomat::opaque] + #[diplomat::attr(auto, namespace = "nested::ns")] + #[diplomat::attr(not(kotlin), rename = "Nested")] + pub struct Nested; + + #[diplomat::opaque] + #[diplomat::attr(auto, namespace = "nested::ns2")] + #[diplomat::attr(not(kotlin), rename = "Nested")] + pub struct Nested2; + #[diplomat::opaque] #[diplomat::attr(not(supports = comparators), disable)] pub struct Comparable(u8); diff --git a/tool/src/cpp/formatter.rs b/tool/src/cpp/formatter.rs index 231a5e01a..1ac5a9fab 100644 --- a/tool/src/cpp/formatter.rs +++ b/tool/src/cpp/formatter.rs @@ -56,7 +56,7 @@ impl<'tcx> Cpp2Formatter<'tcx> { .rename .apply(resolved.name().as_str().into()); if let Some(ref ns) = resolved.attrs().namespace { - let ns = ns.replace("::", std::path::MAIN_SEPARATOR_STR); + let ns = ns.replace("::", "/"); format!("{ns}/{type_name}.d.hpp") } else { format!("{type_name}.d.hpp") @@ -71,7 +71,7 @@ impl<'tcx> Cpp2Formatter<'tcx> { .rename .apply(resolved.name().as_str().into()); if let Some(ref ns) = resolved.attrs().namespace { - let ns = ns.replace("::", std::path::MAIN_SEPARATOR_STR); + let ns = ns.replace("::", "/"); format!("{ns}/{type_name}.hpp") } else { format!("{type_name}.hpp") diff --git a/tool/src/cpp/header.rs b/tool/src/cpp/header.rs index d5cf5818b..7ea5e6bd0 100644 --- a/tool/src/cpp/header.rs +++ b/tool/src/cpp/header.rs @@ -143,31 +143,7 @@ impl fmt::Display for Header { includes: self .includes .iter() - .map(|s| { - if let Some((ns, _)) = self.path.split_once('/') { - if let Some((other_ns, file)) = s.split_once('/') { - if ns == other_ns { - Cow::Borrowed(file) - } else { - Cow::Owned(format!( - "{}{s}", - "../".repeat( - s.chars().filter(|c| *c == '/' || *c == '\\').count() + 1 - ) - )) - } - } else { - Cow::Owned(format!( - "{}{s}", - "../".repeat( - ns.chars().filter(|c| *c == '/' || *c == '\\').count() + 1 - ) - )) - } - } else { - Cow::Borrowed(s.as_str()) - } - }) + .map(|s| path_diff(&self.path, s)) .collect(), forwards: &self.forwards, body, @@ -177,3 +153,58 @@ impl fmt::Display for Header { f.write_char('\n') } } + +// As rsplit_once, except the first of the tuple will include the delimiter pattern +fn rsplit_once_inclusive(str: &str, delim: char) -> Option<(&str, &str)> { + str.rfind(delim).map(|i| str.split_at(i + 1)) +} + +/// Returns the path to 'path', relative to 'base' +fn path_diff<'a>(base: &'a str, path: &'a str) -> Cow<'a, str> { + let (mut base_ns, _) = rsplit_once_inclusive(base, '/').unwrap_or(("", base)); + let (mut path_ns, file) = rsplit_once_inclusive(path, '/').unwrap_or(("", path)); + + let mut matching_chars = 0; + // Consume and count the length of the matching section + loop { + let b = base_ns.split_once('/'); + let p = path_ns.split_once('/'); + if let (Some(b), Some(p)) = (b, p) { + if b.0 == p.0 { + base_ns = b.1; + path_ns = p.1; + matching_chars += b.0.len() + 1; // 1 for the consumed delimiter + continue; + } + } + break; + } + + // Base has run out without a mismatch, the relative path is a strict subset of path & can be borrowed + if base_ns.len() == 0 { + return path.split_at(matching_chars).1.into(); + } else { + let up_dirs = base_ns.matches('/').count(); + return ("../".repeat(up_dirs) + path_ns + file).into(); + } +} + +#[test] +fn test_path_diff() { + let a = "a/same.hpp"; + let b = "a/same2.hpp"; + assert_eq!(path_diff(a, b), "same2.hpp"); + + let a = "root.hpp"; + let b = "a/nested.hpp"; + assert_eq!(path_diff(a, b), "a/nested.hpp"); + + let a = "a/nested.hpp"; + let b = "root.hpp"; + assert_eq!(path_diff(a, b), "../root.hpp"); + + let a = "a/b/c/d.hpp"; + let b = "a/b/z/c/d.hpp"; + + assert_eq!(path_diff(a, b), "../z/c/d.hpp"); +} From 50d135a5d0f210a5abe44afd3abcad35d37f5cce Mon Sep 17 00:00:00 2001 From: Walter Gray <walter@0m.dev> Date: Wed, 22 Jan 2025 13:47:39 -0800 Subject: [PATCH 3/3] Fix lint & CI errors --- feature_tests/c/include/Nested.d.h | 19 +++++++++ feature_tests/c/include/Nested.h | 25 +++++++++++ feature_tests/c/include/Nested2.d.h | 19 +++++++++ feature_tests/c/include/Nested2.h | 25 +++++++++++ .../dart/lib/src/RenamedNested.g.dart | 28 +++++++++++++ .../dart/lib/src/RenamedNested2.g.dart | 28 +++++++++++++ feature_tests/dart/lib/src/lib.g.dart | 2 + feature_tests/js/api/RenamedNested.d.ts | 9 ++++ feature_tests/js/api/RenamedNested.mjs | 41 +++++++++++++++++++ feature_tests/js/api/RenamedNested2.d.ts | 9 ++++ feature_tests/js/api/RenamedNested2.mjs | 41 +++++++++++++++++++ feature_tests/js/api/index.d.ts | 4 ++ feature_tests/js/api/index.mjs | 4 ++ .../kotlin/dev/diplomattest/somelib/Nested.kt | 31 ++++++++++++++ .../dev/diplomattest/somelib/Nested2.kt | 31 ++++++++++++++ feature_tests/src/attrs.rs | 4 +- tool/src/cpp/header.rs | 6 +-- 17 files changed, 321 insertions(+), 5 deletions(-) create mode 100644 feature_tests/c/include/Nested.d.h create mode 100644 feature_tests/c/include/Nested.h create mode 100644 feature_tests/c/include/Nested2.d.h create mode 100644 feature_tests/c/include/Nested2.h create mode 100644 feature_tests/dart/lib/src/RenamedNested.g.dart create mode 100644 feature_tests/dart/lib/src/RenamedNested2.g.dart create mode 100644 feature_tests/js/api/RenamedNested.d.ts create mode 100644 feature_tests/js/api/RenamedNested.mjs create mode 100644 feature_tests/js/api/RenamedNested2.d.ts create mode 100644 feature_tests/js/api/RenamedNested2.mjs create mode 100644 feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Nested.kt create mode 100644 feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Nested2.kt diff --git a/feature_tests/c/include/Nested.d.h b/feature_tests/c/include/Nested.d.h new file mode 100644 index 000000000..f25e915ce --- /dev/null +++ b/feature_tests/c/include/Nested.d.h @@ -0,0 +1,19 @@ +#ifndef Nested_D_H +#define Nested_D_H + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include "diplomat_runtime.h" + + + + + +typedef struct Nested Nested; + + + + +#endif // Nested_D_H diff --git a/feature_tests/c/include/Nested.h b/feature_tests/c/include/Nested.h new file mode 100644 index 000000000..c5af245e9 --- /dev/null +++ b/feature_tests/c/include/Nested.h @@ -0,0 +1,25 @@ +#ifndef Nested_H +#define Nested_H + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include "diplomat_runtime.h" + + +#include "Nested.d.h" + + + + + + + +void namespace_Nested_destroy(Nested* self); + + + + + +#endif // Nested_H diff --git a/feature_tests/c/include/Nested2.d.h b/feature_tests/c/include/Nested2.d.h new file mode 100644 index 000000000..3dc7e3fff --- /dev/null +++ b/feature_tests/c/include/Nested2.d.h @@ -0,0 +1,19 @@ +#ifndef Nested2_D_H +#define Nested2_D_H + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include "diplomat_runtime.h" + + + + + +typedef struct Nested2 Nested2; + + + + +#endif // Nested2_D_H diff --git a/feature_tests/c/include/Nested2.h b/feature_tests/c/include/Nested2.h new file mode 100644 index 000000000..2e91d3265 --- /dev/null +++ b/feature_tests/c/include/Nested2.h @@ -0,0 +1,25 @@ +#ifndef Nested2_H +#define Nested2_H + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include "diplomat_runtime.h" + + +#include "Nested2.d.h" + + + + + + + +void namespace_Nested2_destroy(Nested2* self); + + + + + +#endif // Nested2_H diff --git a/feature_tests/dart/lib/src/RenamedNested.g.dart b/feature_tests/dart/lib/src/RenamedNested.g.dart new file mode 100644 index 000000000..71c07778a --- /dev/null +++ b/feature_tests/dart/lib/src/RenamedNested.g.dart @@ -0,0 +1,28 @@ +// generated by diplomat-tool + +part of 'lib.g.dart'; + +final class RenamedNested implements ffi.Finalizable { + final ffi.Pointer<ffi.Opaque> _ffi; + + // These are "used" in the sense that they keep dependencies alive + // ignore: unused_field + final core.List<Object> _selfEdge; + + // This takes in a list of lifetime edges (including for &self borrows) + // corresponding to data this may borrow from. These should be flat arrays containing + // references to objects, and this object will hold on to them to keep them alive and + // maintain borrow validity. + RenamedNested._fromFfi(this._ffi, this._selfEdge) { + if (_selfEdge.isEmpty) { + _finalizer.attach(this, _ffi.cast()); + } + } + + static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_namespace_Nested_destroy)); +} + +@meta.RecordUse() +@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>(isLeaf: true, symbol: 'namespace_Nested_destroy') +// ignore: non_constant_identifier_names +external void _namespace_Nested_destroy(ffi.Pointer<ffi.Void> self); diff --git a/feature_tests/dart/lib/src/RenamedNested2.g.dart b/feature_tests/dart/lib/src/RenamedNested2.g.dart new file mode 100644 index 000000000..d9c75a4fb --- /dev/null +++ b/feature_tests/dart/lib/src/RenamedNested2.g.dart @@ -0,0 +1,28 @@ +// generated by diplomat-tool + +part of 'lib.g.dart'; + +final class RenamedNested2 implements ffi.Finalizable { + final ffi.Pointer<ffi.Opaque> _ffi; + + // These are "used" in the sense that they keep dependencies alive + // ignore: unused_field + final core.List<Object> _selfEdge; + + // This takes in a list of lifetime edges (including for &self borrows) + // corresponding to data this may borrow from. These should be flat arrays containing + // references to objects, and this object will hold on to them to keep them alive and + // maintain borrow validity. + RenamedNested2._fromFfi(this._ffi, this._selfEdge) { + if (_selfEdge.isEmpty) { + _finalizer.attach(this, _ffi.cast()); + } + } + + static final _finalizer = ffi.NativeFinalizer(ffi.Native.addressOf(_namespace_Nested2_destroy)); +} + +@meta.RecordUse() +@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>(isLeaf: true, symbol: 'namespace_Nested2_destroy') +// ignore: non_constant_identifier_names +external void _namespace_Nested2_destroy(ffi.Pointer<ffi.Void> self); diff --git a/feature_tests/dart/lib/src/lib.g.dart b/feature_tests/dart/lib/src/lib.g.dart index d3f59089f..2804db3d5 100644 --- a/feature_tests/dart/lib/src/lib.g.dart +++ b/feature_tests/dart/lib/src/lib.g.dart @@ -47,6 +47,8 @@ part 'RenamedComparable.g.dart'; part 'RenamedMyIndexer.g.dart'; part 'RenamedMyIterable.g.dart'; part 'RenamedMyIterator.g.dart'; +part 'RenamedNested.g.dart'; +part 'RenamedNested2.g.dart'; part 'RenamedOpaqueIterable.g.dart'; part 'RenamedOpaqueIterator.g.dart'; part 'ResultOpaque.g.dart'; diff --git a/feature_tests/js/api/RenamedNested.d.ts b/feature_tests/js/api/RenamedNested.d.ts new file mode 100644 index 000000000..0789b90cd --- /dev/null +++ b/feature_tests/js/api/RenamedNested.d.ts @@ -0,0 +1,9 @@ +// generated by diplomat-tool +import type { pointer, codepoint } from "./diplomat-runtime.d.ts"; + + + +export class RenamedNested { + + get ffiValue(): pointer; +} \ No newline at end of file diff --git a/feature_tests/js/api/RenamedNested.mjs b/feature_tests/js/api/RenamedNested.mjs new file mode 100644 index 000000000..4966774a4 --- /dev/null +++ b/feature_tests/js/api/RenamedNested.mjs @@ -0,0 +1,41 @@ +// generated by diplomat-tool +import wasm from "./diplomat-wasm.mjs"; +import * as diplomatRuntime from "./diplomat-runtime.mjs"; + +const RenamedNested_box_destroy_registry = new FinalizationRegistry((ptr) => { + wasm.namespace_Nested_destroy(ptr); +}); + +export class RenamedNested { + + // Internal ptr reference: + #ptr = null; + + // Lifetimes are only to keep dependencies alive. + // Since JS won't garbage collect until there are no incoming edges. + #selfEdge = []; + + #internalConstructor(symbol, ptr, selfEdge) { + if (symbol !== diplomatRuntime.internalConstructor) { + console.error("RenamedNested is an Opaque type. You cannot call its constructor."); + return; + } + + this.#ptr = ptr; + this.#selfEdge = selfEdge; + + // Are we being borrowed? If not, we can register. + if (this.#selfEdge.length === 0) { + RenamedNested_box_destroy_registry.register(this, this.#ptr); + } + + return this; + } + get ffiValue() { + return this.#ptr; + } + + constructor(symbol, ptr, selfEdge) { + return this.#internalConstructor(...arguments) + } +} \ No newline at end of file diff --git a/feature_tests/js/api/RenamedNested2.d.ts b/feature_tests/js/api/RenamedNested2.d.ts new file mode 100644 index 000000000..3bf349a79 --- /dev/null +++ b/feature_tests/js/api/RenamedNested2.d.ts @@ -0,0 +1,9 @@ +// generated by diplomat-tool +import type { pointer, codepoint } from "./diplomat-runtime.d.ts"; + + + +export class RenamedNested2 { + + get ffiValue(): pointer; +} \ No newline at end of file diff --git a/feature_tests/js/api/RenamedNested2.mjs b/feature_tests/js/api/RenamedNested2.mjs new file mode 100644 index 000000000..f03b6d503 --- /dev/null +++ b/feature_tests/js/api/RenamedNested2.mjs @@ -0,0 +1,41 @@ +// generated by diplomat-tool +import wasm from "./diplomat-wasm.mjs"; +import * as diplomatRuntime from "./diplomat-runtime.mjs"; + +const RenamedNested2_box_destroy_registry = new FinalizationRegistry((ptr) => { + wasm.namespace_Nested2_destroy(ptr); +}); + +export class RenamedNested2 { + + // Internal ptr reference: + #ptr = null; + + // Lifetimes are only to keep dependencies alive. + // Since JS won't garbage collect until there are no incoming edges. + #selfEdge = []; + + #internalConstructor(symbol, ptr, selfEdge) { + if (symbol !== diplomatRuntime.internalConstructor) { + console.error("RenamedNested2 is an Opaque type. You cannot call its constructor."); + return; + } + + this.#ptr = ptr; + this.#selfEdge = selfEdge; + + // Are we being borrowed? If not, we can register. + if (this.#selfEdge.length === 0) { + RenamedNested2_box_destroy_registry.register(this, this.#ptr); + } + + return this; + } + get ffiValue() { + return this.#ptr; + } + + constructor(symbol, ptr, selfEdge) { + return this.#internalConstructor(...arguments) + } +} \ No newline at end of file diff --git a/feature_tests/js/api/index.d.ts b/feature_tests/js/api/index.d.ts index 53f4f56e9..e89fbbc6e 100644 --- a/feature_tests/js/api/index.d.ts +++ b/feature_tests/js/api/index.d.ts @@ -40,6 +40,10 @@ export { RenamedMyIterable } from "./RenamedMyIterable" export { RenamedMyIterator } from "./RenamedMyIterator" +export { RenamedNested } from "./RenamedNested" + +export { RenamedNested2 } from "./RenamedNested2" + export { RenamedOpaqueIterable } from "./RenamedOpaqueIterable" export { RenamedOpaqueIterator } from "./RenamedOpaqueIterator" diff --git a/feature_tests/js/api/index.mjs b/feature_tests/js/api/index.mjs index bd45d4014..984b3b25c 100644 --- a/feature_tests/js/api/index.mjs +++ b/feature_tests/js/api/index.mjs @@ -38,6 +38,10 @@ export { RenamedMyIterable } from "./RenamedMyIterable.mjs" export { RenamedMyIterator } from "./RenamedMyIterator.mjs" +export { RenamedNested } from "./RenamedNested.mjs" + +export { RenamedNested2 } from "./RenamedNested2.mjs" + export { RenamedOpaqueIterable } from "./RenamedOpaqueIterable.mjs" export { RenamedOpaqueIterator } from "./RenamedOpaqueIterator.mjs" diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Nested.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Nested.kt new file mode 100644 index 000000000..04e4a49d1 --- /dev/null +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Nested.kt @@ -0,0 +1,31 @@ +package dev.diplomattest.somelib; +import com.sun.jna.Callback +import com.sun.jna.Library +import com.sun.jna.Native +import com.sun.jna.Pointer +import com.sun.jna.Structure + + +internal interface NestedLib: Library { + fun namespace_Nested_destroy(handle: Pointer) +} + +class Nested internal constructor ( + internal val handle: Pointer, + // These ensure that anything that is borrowed is kept alive and not cleaned + // up by the garbage collector. + internal val selfEdges: List<Any>, +) { + + internal class NestedCleaner(val handle: Pointer, val lib: NestedLib) : Runnable { + override fun run() { + lib.namespace_Nested_destroy(handle) + } + } + + companion object { + internal val libClass: Class<NestedLib> = NestedLib::class.java + internal val lib: NestedLib = Native.load("somelib", libClass) + } + +} diff --git a/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Nested2.kt b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Nested2.kt new file mode 100644 index 000000000..7ff17e3a1 --- /dev/null +++ b/feature_tests/kotlin/somelib/src/main/kotlin/dev/diplomattest/somelib/Nested2.kt @@ -0,0 +1,31 @@ +package dev.diplomattest.somelib; +import com.sun.jna.Callback +import com.sun.jna.Library +import com.sun.jna.Native +import com.sun.jna.Pointer +import com.sun.jna.Structure + + +internal interface Nested2Lib: Library { + fun namespace_Nested2_destroy(handle: Pointer) +} + +class Nested2 internal constructor ( + internal val handle: Pointer, + // These ensure that anything that is borrowed is kept alive and not cleaned + // up by the garbage collector. + internal val selfEdges: List<Any>, +) { + + internal class Nested2Cleaner(val handle: Pointer, val lib: Nested2Lib) : Runnable { + override fun run() { + lib.namespace_Nested2_destroy(handle) + } + } + + companion object { + internal val libClass: Class<Nested2Lib> = Nested2Lib::class.java + internal val lib: Nested2Lib = Native.load("somelib", libClass) + } + +} diff --git a/feature_tests/src/attrs.rs b/feature_tests/src/attrs.rs index 89c0c8950..8f01fe8a5 100644 --- a/feature_tests/src/attrs.rs +++ b/feature_tests/src/attrs.rs @@ -62,12 +62,12 @@ pub mod ffi { #[diplomat::opaque] #[diplomat::attr(auto, namespace = "nested::ns")] - #[diplomat::attr(not(kotlin), rename = "Nested")] + #[diplomat::attr(supports = namespacing, rename = "Nested")] pub struct Nested; #[diplomat::opaque] #[diplomat::attr(auto, namespace = "nested::ns2")] - #[diplomat::attr(not(kotlin), rename = "Nested")] + #[diplomat::attr(supports = namespacing, rename = "Nested")] pub struct Nested2; #[diplomat::opaque] diff --git a/tool/src/cpp/header.rs b/tool/src/cpp/header.rs index 7ea5e6bd0..642357867 100644 --- a/tool/src/cpp/header.rs +++ b/tool/src/cpp/header.rs @@ -181,11 +181,11 @@ fn path_diff<'a>(base: &'a str, path: &'a str) -> Cow<'a, str> { } // Base has run out without a mismatch, the relative path is a strict subset of path & can be borrowed - if base_ns.len() == 0 { - return path.split_at(matching_chars).1.into(); + if base_ns.is_empty() { + path.split_at(matching_chars).1.into() } else { let up_dirs = base_ns.matches('/').count(); - return ("../".repeat(up_dirs) + path_ns + file).into(); + ("../".repeat(up_dirs) + path_ns + file).into() } }