Skip to content

Commit

Permalink
[Dev/UnderAudit]: Dev<->Master (#4046)
Browse files Browse the repository at this point in the history
* Kotlin multiplatform leaking memory (#4037)

* Add deinit for KMP iOS and JVM targets

* Add deinit for JS target

* Add deinit for JS target

* Fix JVM native name

* Reuse one thread on JVM

---------

Co-authored-by: satoshiotomakan <[email protected]>

* [KMP] Fix issue: memory leak found in Base58.decode in iOS (#4031)

* Fix kmp issue: memory leak found in Base58.decode in iOS

* Remove unused functions

* Fix failed test cases

* Revert "Fix failed test cases"

This reverts commit 57eee39.

* Revert val -> value argument name refactoring

* Output better indentation

* Revert changes in TWEthereumAbiFunction.h

* Fix inconsistent naming

---------

Co-authored-by: satoshiotomakan <[email protected]>

* [TON]: Add support for TON 24-words mnemonic (#3998)

* feat(ton): Add support for TON 24-words mnemonic in Rust

* feat(ton): Add tw_ton_wallet FFIs

* feat(ton): Add TWTONWallet FFI in C++

* feat(ton): Add tonMnemonic StoredKey type

* feat(ton): Add StoredKey TON tests

* feat(ton): Add TWStoredKey TON tests

* feat(ton): Add TONWallet support in Swift

* TODO add iOS tests

* feat(ton): Add `KeyStore` iOS tests

* feat(ton): Add TONWallet support in JavaScript

* Add `KeyStore` TypeScript tests

* feat(ton): Remove `TonMnemonic` structure, replace with a `validate_mnemonic_words` function

* [CI] Trigger CI

* feat(ton): Fix rustfmt

* feat(ton): Fix C++ build

* feat(ton): Fix C++ build

* feat(ton): Fix C++ build

* feat(ton): Fix C++ address analyzer

* feat(ton): Fix C++ tests

* feat(ton): Add Android tests

* feat(ton): Bump `actions/upload-artifact` to v4

* Bump `dawidd6/action-download-artifact` to v6

* feat(eth): Fix PR comments

* Fix Java JVM leak (#4092)

* [Chore]: Fix Android bindings (#4095)

* [Chore]: Add GenericPhantomReference.java

* [Chore]: Fix unnecessary null assertion in WalletCoreLibLoader.kt

* Fix memory lead found in public key in kmp binding (#4097)

* Update Callisto explorer (#4131)

* Revert "[TON]: Add support for TON 24-words mnemonic (#3998)" (#4148)

This reverts commit 0b16771.

---------

Co-authored-by: Viacheslav Kulish <[email protected]>
Co-authored-by: 10gic <[email protected]>
  • Loading branch information
3 people authored Jan 3, 2025
1 parent 7b1bee5 commit 11d320b
Show file tree
Hide file tree
Showing 21 changed files with 310 additions and 82 deletions.
25 changes: 12 additions & 13 deletions codegen/lib/kotlin_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ def self.js_parameters(params)
def self.calling_parameters_ios(params)
names = params.map do |param|
name = fix_name(param.name)
if param.type.name == :data
"#{name}Data#{convert_calling_type_ios(param.type)}"
elsif param.type.name == :string
"#{name}String#{convert_calling_type_ios(param.type)}"
else
"#{name}#{convert_calling_type_ios(param.type)}"
end
end
names.join(', ')
end
Expand All @@ -56,7 +62,7 @@ def self.fix_name(name)
when ''
"value"
when 'val'
"value"
"value"
when 'return'
'`return`'
else
Expand All @@ -65,19 +71,12 @@ def self.fix_name(name)
end

def self.convert_calling_type_ios(t)
case t.name
when :data
"#{if t.is_nullable then '?' else '' end}.toTwData()"
when :string
"#{if t.is_nullable then '?' else '' end}.toTwString()"
if t.is_enum
"#{if t.is_nullable then '?' else '' end}.nativeValue"
elsif t.is_class
"#{if t.is_nullable then '?' else '' end}.pointer"
else
if t.is_enum
"#{if t.is_nullable then '?' else '' end}.nativeValue"
elsif t.is_class
"#{if t.is_nullable then '?' else '' end}.pointer"
else
''
end
''
end
end

Expand Down
30 changes: 3 additions & 27 deletions codegen/lib/templates/java/class.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import java.security.InvalidParameterException;
import java.util.HashSet;
import wallet.core.java.GenericPhantomReference;

<% less = entity.static_methods.detect{ |i| i.name == 'Less' } -%>
<% equal = entity.static_methods.detect{ |i| i.name == 'Equal' } -%>
Expand All @@ -21,7 +21,7 @@ public final class <%= entity.name %> {
<%= entity.name %> instance = new <%= entity.name %>();
instance.nativeHandle = nativeHandle;
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
<%= entity.name %>PhantomReference.register(instance, nativeHandle);
GenericPhantomReference.register(instance, nativeHandle, <%= entity.name %>::nativeDelete);
<% end -%>
return instance;
}
Expand Down Expand Up @@ -96,32 +96,8 @@ public final class <%= entity.name %> {
throw new InvalidParameterException();
}

<%= entity.name %>PhantomReference.register(this, nativeHandle);
GenericPhantomReference.register(this, nativeHandle, <%= entity.name %>::nativeDelete);
}

<%- end -%>
}
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
class <%= entity.name %>PhantomReference extends java.lang.ref.PhantomReference<<%= entity.name %>> {
private static java.util.Set<<%= entity.name %>PhantomReference> references = new HashSet<<%= entity.name %>PhantomReference>();
private static java.lang.ref.ReferenceQueue<<%= entity.name %>> queue = new java.lang.ref.ReferenceQueue<<%= entity.name %>>();
private long nativeHandle;

private <%= entity.name %>PhantomReference(<%= entity.name %> referent, long nativeHandle) {
super(referent, queue);
this.nativeHandle = nativeHandle;
}

static void register(<%= entity.name %> referent, long nativeHandle) {
references.add(new <%= entity.name %>PhantomReference(referent, nativeHandle));
}

public static void doDeletes() {
<%= entity.name %>PhantomReference ref = (<%= entity.name %>PhantomReference) queue.poll();
for (; ref != null; ref = (<%= entity.name %>PhantomReference) queue.poll()) {
<%= entity.name %>.nativeDelete(ref.nativeHandle);
references.remove(ref);
}
}
}
<% end -%>
9 changes: 9 additions & 0 deletions codegen/lib/templates/kotlin/android_class.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ actual class <%= entity.name %> private constructor(

init {
if (nativeHandle == 0L) throw IllegalArgumentException()
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
GenericPhantomReference.register(this, nativeHandle, ::delete)
<% end -%>
}
<%# Constructors -%>
<%- constructors.each do |constructor| -%>
Expand Down Expand Up @@ -52,6 +55,12 @@ actual class <%= entity.name %> private constructor(
@JvmStatic
@JvmName("createFromNative")
private fun createFromNative(nativeHandle: Long) = <%= entity.name %>(nativeHandle)

<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
@JvmStatic
@JvmName("delete")
private external fun delete(handle: Long)
<%- end -%>
<%- constructors.each do |constructor| -%>

@JvmStatic
Expand Down
56 changes: 45 additions & 11 deletions codegen/lib/templates/kotlin/ios_class.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@

import cnames.structs.TW<%= entity.name %>
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.toCValues

<% constructors = entity.static_methods.select { |method| method.name.start_with?('Create') } -%>
<% methods = entity.methods.select { |method| not method.name.start_with?('Delete') } -%>
<% static_methods = entity.static_methods.select { |method| not method.name.start_with?('Create') } -%>
actual class <%= entity.name %> constructor(
val pointer: CPointer<TW<%= entity.name %>>,
) {
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
@OptIn(ExperimentalStdlibApi::class)
private val cleaner = kotlin.native.internal.createCleaner(pointer) { ptr ->
TW<%= entity.name %>Delete(ptr)
}
<% end -%>
<%# Constructors -%>
<%- constructors.each do |constructor| -%>

<% if constructor.return_type.is_nullable -%>
@Throws(IllegalArgumentException::class)
actual constructor(<%= KotlinHelper.parameters(constructor.parameters) %>) : this(
TW<%= entity.name %><%= constructor.name %>(<%= KotlinHelper.calling_parameters_ios(constructor.parameters) %>) ?: throw IllegalArgumentException()
<% else -%>
actual constructor(<%= KotlinHelper.parameters(constructor.parameters) %>) : this(
TW<%= entity.name %><%= constructor.name %>(<%= KotlinHelper.calling_parameters_ios(constructor.parameters) %>)!!
<% end -%>
actual constructor(<%= KotlinHelper.parameters(constructor.parameters) %>) : this(
wrapperTW<%= entity.name %><%= constructor.name %>(<%= KotlinHelper.arguments(constructor.parameters) %>)
)
<% end -%>
<%# Property declarations -%>
Expand All @@ -31,12 +35,38 @@ actual class <%= entity.name %> constructor(
<%# Method declarations -%>
<% methods.each do |method| -%>

actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> =
<%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(pointer#{', ' if not method.parameters.one?}#{KotlinHelper.calling_parameters_ios(method.parameters.drop(1))})") %>
actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> {
<%= render('kotlin/ios_parameter_access.erb', { method: method }) -%>
val result = <%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(pointer#{', ' if not method.parameters.one?}#{KotlinHelper.calling_parameters_ios(method.parameters.drop(1))})") %>
<%= render('kotlin/ios_parameter_release.erb', { method: method }) -%>
return result
}
<% end -%>
<% if entity.static_properties.any? || static_methods.any? -%>
<% if entity.static_properties.any? || static_methods.any? || constructors.any? -%>

<%= if entity.static_properties.any? || static_methods.any? then "actual" else "private" end %> companion object {
<%# Constructor wrappers -%>
<% if constructors.any? -%>
<% constructors.each do |constructor| -%>

actual companion object {
<% if constructor.return_type.is_nullable -%>
@Throws(IllegalArgumentException::class)
<% end -%>
private fun wrapperTW<%= entity.name %><%= constructor.name %>(<%= KotlinHelper.parameters(constructor.parameters) %>): CPointer<TW<%= entity.name %>> {
<%= render('kotlin/ios_parameter_access.erb', { method: constructor, more_index: 4 }) -%>
val result = TW<%= entity.name %><%= constructor.name %>(<%= KotlinHelper.calling_parameters_ios(constructor.parameters) %>)
<%= render('kotlin/ios_parameter_release.erb', { method: constructor, more_index: 4 }) -%>
<% if constructor.return_type.is_nullable -%>
if (result == null) {
throw IllegalArgumentException()
}
return result
<% else -%>
return result!!
<% end -%>
}
<% end -%>
<% end -%>
<%# Static property declarations -%>
<% entity.static_properties.each do |property| -%>

Expand All @@ -46,8 +76,12 @@ actual class <%= entity.name %> constructor(
<%# Static method declarations -%>
<% static_methods.each do |method| -%>

actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> =
<%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(#{KotlinHelper.calling_parameters_ios(method.parameters)})") %>
actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> {
<%= render('kotlin/ios_parameter_access.erb', { method: method, more_index: 4 }) -%>
val result = <%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(#{KotlinHelper.calling_parameters_ios(method.parameters)})") %>
<%= render('kotlin/ios_parameter_release.erb', { method: method, more_index: 4 }) -%>
return result
}
<% end -%>
}
<% end -%>
Expand Down
8 changes: 6 additions & 2 deletions codegen/lib/templates/kotlin/ios_enum.erb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ actual enum class <%= entity.name %>(
<%- entity.methods.each do |method| -%>
<%- next if method.name.start_with?('Delete') -%>

actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> =
TW<%= entity.name %><%= method.name %>(value<%= ', ' if not method.parameters.one? %><%= KotlinHelper.calling_parameters_ios(method.parameters.drop(1)) %>)<%= KotlinHelper.convert_calling_return_type_ios(method.return_type) %>
actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.return_type(method.return_type) %> {
<%= render('kotlin/ios_parameter_access.erb', { method: method }) -%>
val result = TW<%= entity.name %><%= method.name %>(value<%= ', ' if not method.parameters.one? %><%= KotlinHelper.calling_parameters_ios(method.parameters.drop(1)) %>)<%= KotlinHelper.convert_calling_return_type_ios(method.return_type) %>
<%= render('kotlin/ios_parameter_release.erb', { method: method }) -%>
return result
}
<%- end -%>
<%# Value -%>
<% if entity.cases.any? { |e| !e.value.nil? } -%>
Expand Down
27 changes: 27 additions & 0 deletions codegen/lib/templates/kotlin/ios_parameter_access.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<%
method = locals[:method]
more_index = locals[:more_index] || 0

method.parameters.each do |param| -%>
<% if param.type.name == :data -%>
<% if param.type.is_nullable -%>
<%= ' ' * more_index %> val <%= KotlinHelper.fix_name(param.name) %>Data = if (<%= KotlinHelper.fix_name(param.name) %> == null) {
<%= ' ' * more_index %> null
<%= ' ' * more_index %> } else {
<%= ' ' * more_index %> TWDataCreateWithBytes(<%= KotlinHelper.fix_name(param.name) %>.toUByteArray().toCValues(), <%= KotlinHelper.fix_name(param.name) %>.size.toULong())
<%= ' ' * more_index %> }
<% else -%>
<%= ' ' * more_index %> val <%= KotlinHelper.fix_name(param.name) %>Data = TWDataCreateWithBytes(<%= KotlinHelper.fix_name(param.name) %>.toUByteArray().toCValues(), <%= KotlinHelper.fix_name(param.name) %>.size.toULong())
<% end -%>
<% elsif param.type.name == :string -%>
<% if param.type.is_nullable -%>
<%= ' ' * more_index %> val <%= KotlinHelper.fix_name(param.name) %>String = if (<%= KotlinHelper.fix_name(param.name) %> == null) {
<%= ' ' * more_index %> null
<%= ' ' * more_index %> } else {
<%= ' ' * more_index %> TWStringCreateWithUTF8Bytes(<%= KotlinHelper.fix_name(param.name) %>)
<%= ' ' * more_index %> }
<% else -%>
<%= ' ' * more_index %> val <%= KotlinHelper.fix_name(param.name) %>String = TWStringCreateWithUTF8Bytes(<%= KotlinHelper.fix_name(param.name) %>)
<% end -%>
<% end -%>
<% end -%>
23 changes: 23 additions & 0 deletions codegen/lib/templates/kotlin/ios_parameter_release.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<%
method = locals[:method]
more_index = locals[:more_index] || 0

method.parameters.each do |param| -%>
<% if param.type.name == :data -%>
<% if param.type.is_nullable -%>
<%= ' ' * more_index %> if (<%= KotlinHelper.fix_name(param.name) %>Data != null) {
<%= ' ' * more_index %> TWDataDelete(<%= KotlinHelper.fix_name(param.name) %>Data)
<%= ' ' * more_index %> }
<% else -%>
<%= ' ' * more_index %> TWDataDelete(<%= KotlinHelper.fix_name(param.name) %>Data)
<% end -%>
<% elsif param.type.name == :string -%>
<% if param.type.is_nullable -%>
<%= ' ' * more_index %> if (<%= KotlinHelper.fix_name(param.name) %>String != null) {
<%= ' ' * more_index %> TWStringDelete(<%= KotlinHelper.fix_name(param.name) %>String)
<%= ' ' * more_index %> }
<% else -%>
<%= ' ' * more_index %> TWStringDelete(<%= KotlinHelper.fix_name(param.name) %>String)
<% end -%>
<% end -%>
<% end -%>
10 changes: 8 additions & 2 deletions codegen/lib/templates/kotlin/ios_struct.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<%= render('kotlin/package.erb') %>

import kotlinx.cinterop.toCValues

actual object <%= entity.name %> {
<%# Static property declarations -%>
<% entity.static_properties.each do |property| -%>
Expand All @@ -10,7 +12,11 @@ actual object <%= entity.name %> {
<% entity.static_methods.each do |method| -%>
<% next if method.name.start_with?('Create') -%>

actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> =
<%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(#{KotlinHelper.calling_parameters_ios(method.parameters)})") %>
actual fun <%= KotlinHelper.format_name(method.name) %>(<%= KotlinHelper.parameters(method.parameters) %>)<%= KotlinHelper.return_type(method.return_type) %> {
<%= render('kotlin/ios_parameter_access.erb', { method: method }) -%>
val result = <%= KotlinHelper.convert_calling_return_type_ios(method.return_type, "TW#{entity.name}#{method.name}(#{KotlinHelper.calling_parameters_ios(method.parameters)})") %>
<%= render('kotlin/ios_parameter_release.erb', { method: method }) -%>
return result
}
<% end -%>
}
3 changes: 3 additions & 0 deletions codegen/lib/templates/kotlin/js_accessors_class.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ external interface Js<%= entity.name %> {
<%- entity.properties.each do |property| -%>
fun <%= KotlinHelper.fix_name(WasmCppHelper.format_name(property.name)) %>()<%= KotlinHelper.js_return_type(property.return_type) %>
<%- end -%>
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
fun delete()
<% end -%>
<% entity.methods.each do |method| -%>
<% next if method.name == "Delete" -%>
fun <%= KotlinHelper.fix_name(WasmCppHelper.format_name(method.name)) %>(<%= KotlinHelper.js_parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.js_return_type(method.return_type) %>
Expand Down
15 changes: 15 additions & 0 deletions codegen/lib/templates/kotlin/js_class.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@
actual class <%= entity.name %> constructor(
val jsValue: Js<%= entity.name %>,
) {
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
private val finalizationRegistry =
js(
"""
new FinalizationRegistry(function(heldValue) {
heldValue.delete();
})
"""
)

init {
finalizationRegistry.register(this, jsValue)
}
<% end -%>

<%# Constructors -%>
<%- constructors.each do |constructor| -%>

Expand Down
4 changes: 3 additions & 1 deletion jni/cpp/TWJNIData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ jbyteArray TWDataJByteArray(TWData *_Nonnull data, JNIEnv *env) {
TWData *_Nonnull TWDataCreateWithJByteArray(JNIEnv *env, jbyteArray _Nonnull array) {
jsize size = env->GetArrayLength(array);
jbyte *bytes = env->GetByteArrayElements(array, nullptr);
return TWDataCreateWithBytes((uint8_t *) bytes, size);
const auto *twdata = TWDataCreateWithBytes((uint8_t *) bytes, size);
env->ReleaseByteArrayElements(array, bytes, JNI_ABORT);
return twdata;
}
51 changes: 51 additions & 0 deletions jni/java/wallet/core/java/GenericPhantomReference.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package wallet.core.java;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.Set;
import java.util.HashSet;

public class GenericPhantomReference extends PhantomReference<Object> {
private final long nativeHandle;
private final OnDeleteCallback onDeleteCallback;

private static final Set<GenericPhantomReference> references = new HashSet<>();
private static final ReferenceQueue<Object> queue = new ReferenceQueue<>();

static {
Thread finalizingDaemon = new Thread(() -> {
try {
doDeletes();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
finalizingDaemon.setName("WCFinalizingDaemon");
finalizingDaemon.setDaemon(true);
finalizingDaemon.setPriority(Thread.NORM_PRIORITY);
finalizingDaemon.start();
}

private GenericPhantomReference(Object referent, long handle, OnDeleteCallback onDelete) {
super(referent, queue);
this.nativeHandle = handle;
this.onDeleteCallback = onDelete;
}

public static void register(Object referent, long handle, OnDeleteCallback onDelete) {
references.add(new GenericPhantomReference(referent, handle, onDelete));
}

private static void doDeletes() throws InterruptedException {
GenericPhantomReference ref = (GenericPhantomReference) queue.remove();
for (; ref != null; ref = (GenericPhantomReference) queue.remove()) {
ref.onDeleteCallback.nativeDelete(ref.nativeHandle);
references.remove(ref);
}
}

@FunctionalInterface
public interface OnDeleteCallback {
void nativeDelete(long handle);
}
}
Loading

0 comments on commit 11d320b

Please sign in to comment.