Skip to content
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: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

### Fixes

- Dont use `Companion` in JNI calls and properly release JNI refs ([#3354](https://github.com/getsentry/sentry-dart/pull/3354))
- This potentially fixes segfault crashes related to JNI

### Enhancements

- Flush logs if client/hub/sdk is closed ([#3335](https://github.com/getsentry/sentry-dart/pull/3335)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ class _AndroidReplayHandler extends WorkerHandler {
late final native.ReplayIntegration _nativeReplay;

_AndroidReplayHandler(this._config) {
_nativeReplay = native.SentryFlutterPlugin.Companion
.privateSentryGetReplayIntegration()!;
_nativeReplay =
native.SentryFlutterPlugin.privateSentryGetReplayIntegration()!;
}

@override
Expand Down
79 changes: 40 additions & 39 deletions packages/flutter/lib/src/native/java/sentry_native_java.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ class SentryNativeJava extends SentryNativeChannel {

// NOTE: when instructionAddressSet is empty, loadDebugImagesAsBytes will return
// all debug images as fallback.
imagesUtf8JsonBytes = native.SentryFlutterPlugin.Companion
.loadDebugImagesAsBytes(instructionAddressSet);
imagesUtf8JsonBytes = native.SentryFlutterPlugin.loadDebugImagesAsBytes(
instructionAddressSet);
if (imagesUtf8JsonBytes == null) return null;

final byteRange =
Expand Down Expand Up @@ -112,8 +112,7 @@ class SentryNativeJava extends SentryNativeChannel {
// is significantly faster because contexts can be large and contain many nested
// objects. Local benchmarks show this method is ~4x faster than the alternative
// approach of converting JNI objects to Dart objects one by one.
contextsUtf8JsonBytes =
native.SentryFlutterPlugin.Companion.loadContextsAsBytes();
contextsUtf8JsonBytes = native.SentryFlutterPlugin.loadContextsAsBytes();
if (contextsUtf8JsonBytes == null) return null;

final byteRange =
Expand All @@ -136,8 +135,7 @@ class SentryNativeJava extends SentryNativeChannel {

@override
int? displayRefreshRate() => tryCatchSync('displayRefreshRate', () {
return native.SentryFlutterPlugin.Companion
.getDisplayRefreshRate()
return native.SentryFlutterPlugin.getDisplayRefreshRate()
?.intValue(releaseOriginal: true);
});

Expand All @@ -150,7 +148,7 @@ class SentryNativeJava extends SentryNativeChannel {
return null;
}
appStartUtf8JsonBytes =
native.SentryFlutterPlugin.Companion.fetchNativeAppStartAsBytes();
native.SentryFlutterPlugin.fetchNativeAppStartAsBytes();
if (appStartUtf8JsonBytes == null) return null;

final byteRange =
Expand All @@ -166,7 +164,7 @@ class SentryNativeJava extends SentryNativeChannel {

@override
void nativeCrash() {
native.SentryFlutterPlugin.Companion.crash();
native.SentryFlutterPlugin.crash();
}

@override
Expand Down Expand Up @@ -194,10 +192,12 @@ class SentryNativeJava extends SentryNativeChannel {
final nativeOptions = native.ScopesAdapter.getInstance()?.getOptions()
?..releasedBy(arena);
if (nativeOptions == null) return;
final jMap = _dartToJMap(breadcrumb.toJson(), arena);
final jMap = _dartToJMap(breadcrumb.toJson());
final nativeBreadcrumb =
native.Breadcrumb.fromMap(jMap, nativeOptions)
?..releasedBy(arena);
// release jMap directly after use
jMap.release();
if (nativeBreadcrumb == null) return;
native.Sentry.addBreadcrumb$1(nativeBreadcrumb);
});
Expand All @@ -219,9 +219,11 @@ class SentryNativeJava extends SentryNativeChannel {
?..releasedBy(arena);
if (nativeOptions == null) return;

final nativeUser = native.User.fromMap(
_dartToJMap(user.toJson(), arena), nativeOptions)
final jMap = _dartToJMap(user.toJson());
final nativeUser = native.User.fromMap(jMap, nativeOptions)
?..releasedBy(arena);
// release jMap directly after use
jMap.release();
if (nativeUser == null) return;

native.Sentry.setUser(nativeUser);
Expand All @@ -237,7 +239,7 @@ class SentryNativeJava extends SentryNativeChannel {
run: (iScope) {
using((arena) {
final jKey = key.toJString()..releasedBy(arena);
final jVal = _dartToJObject(value, arena);
final jVal = _dartToJObject(value)?..releasedBy(arena);

if (jVal == null) return;

Expand Down Expand Up @@ -306,8 +308,8 @@ class SentryNativeJava extends SentryNativeChannel {
SentryId captureReplay() {
final id = tryCatchSync<SentryId>('captureReplay', () {
return using((arena) {
_nativeReplay ??= native.SentryFlutterPlugin.Companion
.privateSentryGetReplayIntegration();
_nativeReplay ??=
native.SentryFlutterPlugin.privateSentryGetReplayIntegration();
// The passed parameter is `isTerminating`
_nativeReplay?.captureReplay(false.toJBoolean()..releasedBy(arena));

Expand Down Expand Up @@ -380,46 +382,45 @@ class SentryNativeJava extends SentryNativeChannel {
0, // bitRate is currently not used
);

_nativeReplay ??= native.SentryFlutterPlugin.Companion
.privateSentryGetReplayIntegration();
_nativeReplay ??=
native.SentryFlutterPlugin.privateSentryGetReplayIntegration();
_nativeReplay?.onConfigurationChanged(replayConfig);

replayConfig.release();
});
}

JObject? _dartToJObject(Object? value, Arena arena) => switch (value) {
JObject? _dartToJObject(Object? value) => switch (value) {
null => null,
String s => s.toJString()..releasedBy(arena),
bool b => b.toJBoolean()..releasedBy(arena),
int i => i.toJLong()..releasedBy(arena),
double d => d.toJDouble()..releasedBy(arena),
List<dynamic> l => _dartToJList(l, arena),
Map<String, dynamic> m => _dartToJMap(m, arena),
String s => s.toJString(),
bool b => b.toJBoolean(),
int i => i.toJLong(),
double d => d.toJDouble(),
List<dynamic> l => _dartToJList(l),
Map<String, dynamic> m => _dartToJMap(m),
_ => null
};

JList<JObject?> _dartToJList(List<dynamic> values, Arena arena) {
final jlist = JList.array(JObject.nullableType)..releasedBy(arena);

for (final value in values) {
final jObj = _dartToJObject(value, arena);
jlist.add(jObj);
JList<JObject?> _dartToJList(List<dynamic> values) {
final jList = JList.array(JObject.nullableType);
for (final v in values) {
final j = _dartToJObject(v);
jList.add(j);
j?.release();
}

return jlist;
return jList;
}

JMap<JString, JObject?> _dartToJMap(Map<String, dynamic> json, Arena arena) {
final jmap = JMap.hash(JString.type, JObject.nullableType)..releasedBy(arena);

JMap<JString, JObject?> _dartToJMap(Map<String, dynamic> json) {
final jMap = JMap.hash(JString.type, JObject.nullableType);
for (final entry in json.entries) {
final key = entry.key.toJString()..releasedBy(arena);
final value = _dartToJObject(entry.value, arena);
jmap[key] = value;
final jk = entry.key.toJString();
final jv = _dartToJObject(entry.value);
jMap[jk] = jv;
jk.release();
jv?.release();
}

return jmap;
return jMap;
}

const _videoBlockSize = 16;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ void initSentryAndroid({
);

replayCallbacks.use((cb) {
native.SentryFlutterPlugin.Companion
.setupReplay(androidOptions, cb);
native.SentryFlutterPlugin.setupReplay(androidOptions, cb);
});
},
),
Expand Down Expand Up @@ -126,7 +125,9 @@ native.SentryOptions$BeforeSendReplayCallback createBeforeSendReplayCallback(
return shouldRemove;
});

payload?.addAll(_dartToJMap(options.privacy.toJson(), arena));
final jMap = _dartToJMap(options.privacy.toJson());
payload?.addAll(jMap);
jMap.release();
}
});
return sentryReplayEvent;
Expand All @@ -151,8 +152,8 @@ native.ReplayRecorderCallbacks? createReplayRecorderCallbacks({
SentryId.fromId(replayIdString.toDartString(releaseOriginal: true));

owner._replayId = replayId;
owner._nativeReplay = native.SentryFlutterPlugin.Companion
.privateSentryGetReplayIntegration();
owner._nativeReplay =
native.SentryFlutterPlugin.privateSentryGetReplayIntegration();
owner._replayRecorder = AndroidReplayRecorder.factory(options);
await owner._replayRecorder!.start();
hub.configureScope((s) {
Expand Down
Loading