Skip to content

[JExtract] Unify mechanisms between value types and reference types #240

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

Merged
merged 3 commits into from
Jun 2, 2025

Conversation

rintaro
Copy link
Member

@rintaro rintaro commented Jun 1, 2025

Part of #236 with minimal Sources/JExtractSwift changes. All the changes for it are going to be replaced with the remaining changes in #236 . This PR is mostly for reviewing SwiftKit changes.

Unify memory and instance management mechanism between value types (e.g. string or enum) and reference types (e.g. class and actor). Now all imported nominal types are allocated using the value witness table, are returned indirectly, and are destroyed in the same manner.

SwiftInstance is now the abstract base class for all the imported types including reference types. Concrete types can simply construct it by calling super(memorySegment, arena).

Unify memory and instance management mechanism between value types (e.g.
`string` or `enum`) and reference types (e.g. `class` and `actor`).
Now all imported nominal types are allocated using the value witness
table, are returned indirectly, and are destroyed in the same manner.

`SwiftInstance` is now the abstract base class for all the imported
types including reference types. Concrete types can simply construct it
by calling `super(memorySegment, arena)`.
Comment on lines -288 to +285
try (var arena = Arena.ofConfined()) {
// we need to make a pointer to the self pointer when calling witness table functions:
MemorySegment indirectDest = arena.allocate(SwiftValueLayout.SWIFT_POINTER);
MemorySegmentUtils.setSwiftPointerAddress(indirectDest, dest);
MemorySegment indirectSrc = arena.allocate(SwiftValueLayout.SWIFT_POINTER);
MemorySegmentUtils.setSwiftPointerAddress(indirectSrc, src);

var returnedDest = (MemorySegment) mh.invokeExact(indirectDest, indirectSrc, wtable);
return returnedDest;
try {
return (MemorySegment) mh.invokeExact(dest, src, wtable);
Copy link
Member Author

@rintaro rintaro Jun 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This initializeWithCopy change was not included in #236. But I realized this has the same issue with destroy. No need for extra indirection anymore.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

@@ -290,11 +290,8 @@ extension Swift2JavaTranslator {
parentProtocol = "SwiftValue"
}

printer.printTypeDecl("public final class \(decl.javaClassName) implements \(parentProtocol)") {
printer.printTypeDecl("public final class \(decl.javaClassName) extends SwiftInstance implements \(parentProtocol)") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok 👍

@@ -472,30 +440,11 @@ extension Swift2JavaTranslator {
\(decl.renderCommentSnippet ?? " *")
*/
public \(parentName.unqualifiedJavaTypeName)(\(renderJavaParamDecls(decl, paramPassingStyle: .wrapper))) {
this(/*arena=*/null, \(renderForwardJavaParams(decl, paramPassingStyle: .wrapper)));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should remove the constructors without an arena passed in actually… fine to keep in this pr, wdyt tho?

Copy link
Member Author

@rintaro rintaro Jun 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with removing this constructor as we don't want users to use ofAuto() arena as much as possible.
Also mixing different arena in a session sounds scary 😅 E.g.

try(var arena = SwiftArena.ofConfined()) {
  var obj = new MyClass(arena);
  var value = new MyStruct(); // Accidental use of "auto" arena.
  obj.process(value); // Probably fine, but...
}

This is actually probably fine, but still it's not ideal.
Removing this convenience constructor prevents this kind of issues.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it’ll be more consistent to just always pass arenas, might save us weird headaches down the line.

you can remove here or we’ll do later, your call

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do it in this PR 👍

Comment on lines 37 to 43
// public SwiftAnyType(SwiftHeapObject object) {
// if (object.$layout().name().isEmpty()) {
// throw new IllegalArgumentException("SwiftHeapObject must have a mangled name in order to obtain its SwiftType.");
// }
//
// String mangledName = object.$layout().name().get();
// var type = SwiftKit.getTypeByMangledNameInEnvironment(mangledName);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel we need this function anymore. Every instance has $swiftType(). I don't even think object.$layout().name() is a mangled name (i.e. it's initialized with .withName(SwiftKit.nameOfSwiftType(typeMetadata, true));)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah let's remove, it is from the time when we were still calling directly into initializers from the java side I think

Users should consitently use the same arena. Also since nothing was
retaining the '.ofAuto()' arena was evil.
@rintaro rintaro force-pushed the jextract-value-or-reference branch from 91ca289 to 971bb92 Compare June 1, 2025 13:35
Because that seems to be more common
public \(parentName.unqualifiedJavaTypeName)(SwiftArena arena, \(renderJavaParamDecls(decl, paramPassingStyle: .wrapper))) {
var mh$ = \(descClassIdentifier).HANDLE;
try {
public \(parentName.unqualifiedJavaTypeName)(\(renderJavaParamDecls(decl, paramPassingStyle: .wrapper)), SwiftArena arena) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 90% sure that arena parameter at the end is more common practice.
Although I haven't been able to find any wrapper constructor examples,

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last sounds ok me, makes the calls more similar to their swift equivalents and only the arena added on.

@rintaro rintaro merged commit 476087b into swiftlang:main Jun 2, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants