Toggle a boolean within a struct in zig, possible? - zig

To toggle* a boolean I normally use boolean = !boolean like this:
var boolean: bool = true;
boolean = !boolean;
std.debug.print("My bool is: {}\n", .{boolean}); //My bool is: false
But how do I achieve this for booleans within a struct? Or is it not possible?
const std = #import("std");
pub fn main() void {
//Struct with default values:
const animal = struct {
tail: bool = true,
wings: bool = false,
horns: bool = false,
paws: bool = true,
};
//Struct instances:
var has = animal{};
//This works alright:
//var hasno = animal{.tail = false, .wings = true, .horns = true, .paws = false};
//FAILS: error: expected type 'bool', found '#TypeOf(.enum_literal)'
var hasno = animal{ .tail = !.tail }; //, .wings = !.wings, .horns = !.horns, .paws = !.paws };
//Debug printing:
std.debug.print("Animal has tail: {}, wings: {}, horns: {}, paws: {}\n", .{ has.tail, has.wings, has.horns, has.paws });
std.debug.print("Animal has no tail: {}, wings: {}, horns: {}, paws: {}\n", .{ hasno.tail, hasno.wings, hasno.horns, hasno.paws });
}
Test code for yourself online in zig playground:
https://zig-play.dev
*give it the opposite value of what it was, without knowing what it was.
Like if (boolean == true) boolean = false; else boolean = true; But I'm wondering if it is possible with the (bang) operator for booleans within struct.

const std = #import("std");
const Animal = struct {
tail: bool,
};
pub fn main() void {
var animal = Animal { .tail = true };
std.debug.print("{}\n", .{ animal });
animal.tail = !animal.tail;
std.debug.print("{}\n", .{ animal });
}
Prints:
main.Animal{ .tail = true }
main.Animal{ .tail = false }

Related

Jetpack compose Lazy Column exception: View=androidx.compose.ui.window.PopupLayout not attached to window manager

Sometimes the app crashes with exception "java.lang.IllegalArgumentException: View=androidx.compose.ui.window.PopupLayout{...} not attached to window manager".
I'm only able to reproduce it with testcase and it happens only sometimes.
My test case: list with 3 items, let's call them A, B, C; and test case has basically 3 steps:
Update item A text and remove item B
Update item A text and remove item C
Try to add item C back to list (and sometimes this causes a crash). (The problem only happens when adding item with the same key back that removed one was)
My hypothesis why it happens:
It seems to be happen only when update text A causes to go A text to more lines than it was before. So the recomposition redrew the item A and it will have more height than before
If I run fetchSemanticsNode before step3, then it seems it crashes only when item C is still cached (the node exists but it's not displayed)
Seems like these both conditions needs to be true to error to happen. So I was wondering am I using LazyColumn somehow wrongly or there seems to be bug in Jetpack Compose code?
Below is the full exception and my code and test case to reproduce it.
App is crashing with following exception:
java.lang.IllegalArgumentException: View=androidx.compose.ui.window.PopupLayout{7744d0c V.E...... ......ID 0,0-62,75 #1020002 android:id/content} not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:544)
at android.view.WindowManagerGlobal.updateViewLayout(WindowManagerGlobal.java:433)
at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:116)
at androidx.compose.ui.window.PopupLayoutHelperImpl.updateViewLayout(AndroidPopup.android.kt:776)
at androidx.compose.ui.window.PopupLayout.updatePosition(AndroidPopup.android.kt:659)
at androidx.compose.ui.window.PopupLayout.updateParentBounds$ui_release(AndroidPopup.android.kt:626)
at androidx.compose.ui.window.PopupLayout.updateParentLayoutCoordinates(AndroidPopup.android.kt:581)
at androidx.compose.ui.window.AndroidPopup_androidKt$Popup$7.invoke(AndroidPopup.android.kt:316)
at androidx.compose.ui.window.AndroidPopup_androidKt$Popup$7.invoke(AndroidPopup.android.kt:310)
at androidx.compose.ui.layout.OnGloballyPositionedModifierImpl.onGloballyPositioned(OnGloballyPositionedModifier.kt:59)
at androidx.compose.ui.node.LayoutNode.dispatchOnPositionedCallbacks$ui_release(LayoutNode.kt:1149)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:51)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:55)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:55)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:55)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:55)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:55)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:55)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:55)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatchHierarchy(OnPositionedDispatcher.kt:55)
at androidx.compose.ui.node.OnPositionedDispatcher.dispatch(OnPositionedDispatcher.kt:44)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.dispatchOnPositionedCallbacks(MeasureAndLayoutDelegate.kt:348)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.dispatchOnPositionedCallbacks$default(MeasureAndLayoutDelegate.kt:344)
at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:761)
at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.kt:196)
at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:954)
at android.view.View.draw(View.java:22353)
at android.view.View.updateDisplayListIfDirty(View.java:21226)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4500)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4473)
at android.view.View.updateDisplayListIfDirty(View.java:21186)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4500)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4473)
at android.view.View.updateDisplayListIfDirty(View.java:21186)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4500)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4473)
at android.view.View.updateDisplayListIfDirty(View.java:21186)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4500)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4473)
at android.view.View.updateDisplayListIfDirty(View.java:21186)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4500)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4473)
at android.view.View.updateDisplayListIfDirty(View.java:21186)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4500)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4473)
at android.view.View.updateDisplayListIfDirty(View.java:21186)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:559)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:565)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:642)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:4101)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3828)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3099)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:731)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
UI
const val LAZY_COLUMN_TEST_TAG = "lazy_column_tag"
const val UNDO_TEST_TAG = "undo_tag"
const val ITEM_FIELD_TEST_TAG = "item_field_tag"
#Composable
fun ScreenInitWrapper(viewModel: MyViewModel = hiltViewModel()) {
viewModel.initModel()
if (viewModel.isLoaded) {
Screen()
}
}
#Composable
fun Screen(viewModel: MyViewModel = hiltViewModel()) {
val items = viewModel.items
val scrollState = rememberLazyListState()
LazyColumn(
state = scrollState,
modifier = Modifier.testTag(LAZY_COLUMN_TEST_TAG)
) {
item {
UndoButton(viewModel)
}
items(items = items, key = { item -> "ITEM_${item.id}" }) { item ->
ItemText(item, viewModel)
}
}
}
#Composable
fun UndoButton(viewModel: MyViewModel) {
IconButton(
onClick = { viewModel.undo() }, enabled = true,
modifier = Modifier.testTag(UNDO_TEST_TAG)
) {
Icon(
imageVector = Icons.Filled.Undo, contentDescription = "undo",
modifier = Modifier.padding(start = 16.dp, end = 16.dp)
)
}
}
#Composable
fun ItemText(item: MyItem, viewModel: MyViewModel) {
var textFieldValueState by remember { mutableStateOf(TextFieldValue(text = item.text, selection = item.selection)) }
val textFieldValue = textFieldValueState.copy(text = item.text, selection = item.selection)
TextField(
value = textFieldValue,
onValueChange = {
textFieldValueState = it
if (item.selection != it.selection) {
item.selection = it.selection
}
if (item.text != it.text) {
item.text = it.text
}
},
modifier = Modifier
.onPreviewKeyEvent {
if (isBackspaceClickedAndCursorIsBeginningOfLine(it, textFieldValue)) {
if (item.id != 1) {
viewModel.removeItemAndUpdatePreviousItemText(item)
}
true
} else {
false
}
}
.testTag("${ITEM_FIELD_TEST_TAG}_${item.id}"),
)
}
#OptIn(ExperimentalComposeUiApi::class)
private fun isBackspaceClickedAndCursorIsBeginningOfLine(it: KeyEvent, textFieldValue: TextFieldValue): Boolean {
if (it.key == Key.Backspace && it.type == KeyEventType.KeyDown) {
val currentPosition = textFieldValue.selection.end
if (currentPosition == 0) {
return true
}
}
return false
}
ViewModel
data class MyItem (val id: Int, var text: String, var selection: TextRange = TextRange.Zero)
#HiltViewModel
class MyViewModel : ViewModel() {
var isLoaded: Boolean by mutableStateOf(false)
private set
private var _items = emptyList<MyItem>().toMutableStateList()
var items: List<MyItem> = _items
private set
private val undoStack = ArrayDeque<MyItem>()
fun initModel() {
if (!isLoaded) {
// The names matter
val itemsTemp = listOf(
MyItem(id = 1, text = "some text long enough for item 1"),
MyItem(id = 2, text = "item 2"),
MyItem(id = 3, text = "item 3"),
).toMutableList()
_items.addAll(itemsTemp)
isLoaded = true
}
}
fun removeItemAndUpdatePreviousItemText(deletedItem: MyItem) {
// Update previous item text
val index = items.indexOfFirst { it.id == deletedItem.id }
val previousItemText = items[index - 1]
// I was able to get this exception only when the amount of lines of the previous text changed
previousItemText.text = previousItemText.text + "Some new text to make it more lines"
// Undo
undoStack.addLast(deletedItem)
// Remove value from list
val index = items.indexOfFirst { it.id == item.id }
_items.removeAt(index)
}
fun undo() = viewModelScope.launch {
val deletedItem = undoStack.removeLastOrNull()
if (deletedItem != null) {
_items.add(deletedItem)
}
}
}
InstrumentedTest
#RunWith(AndroidJUnit4::class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
#LargeTest
#HiltAndroidTest
class UndoRemoveItemInstrumentedTest {
#get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
#get:Rule(order = 1)
val composeRule = createAndroidComposeRule<MainActivity>()
#Test
undoRemoveItemTest() {
removeItem(2)
removeItem(3)
// First undo causes sometimes failure
composeRule.onNodeWithTag(UNDO_TEST_TAG).performClick()
composeRule.onNodeWithTag(UNDO_TEST_TAG).performClick()
}
#OptIn(ExperimentalTestApi::class)
private fun removeItem(id: Int) {
composeRule.onNodeWithTag(LAZY_COLUMN_TEST_TAG)
.performScrollToKey("ITEM_${id}")
composeRule.onNodeWithTag("${ITEM_FIELD_TEST_TAG}_${id}").performClick()
composeRule.onNodeWithTag("${ITEM_FIELD_TEST_TAG}_${id}")
.performTextInputSelection(TextRange(0))
performBackspaceKeyPress(id)
}
private fun performBackspaceKeyPress(itemId: Int) {
val backspaceDown = android.view.KeyEvent(NativeKeyEvent.ACTION_DOWN, NativeKeyEvent.KEYCODE_DEL)
val backspaceUp = android.view.KeyEvent(NativeKeyEvent.ACTION_UP, NativeKeyEvent.KEYCODE_DEL)
composeRule.onNodeWithTag("${ITEM_FIELD_TEST_TAG}_${itemId}").performKeyPress(KeyEvent(backspaceUp))
composeRule.onNodeWithTag("${ITEM_FIELD_TEST_TAG}_${itemId}").performKeyPress(KeyEvent(backspaceDown))
}
}
Seems to be fixed in compose version 1.3.0. At least I'm not able to reproduce it in this version

Crash when encode mutable map

I got crash when try to encode to string a mutable map
internal class AsyncStorageHolder(
var manifest: MutableMap<String, String> = mutableMapOf(),
var hasCheatedStorageDirectory: Boolean = false,
var haveSetup: Boolean = false
)
private val asyncStorageHolder = IsolateState { AsyncStorageHolder() }
private fun writeManifest() {
val json = Json {
encodeDefaults = true
ignoreUnknownKeys = true
}
asyncStorageHolder.access {
val error: CPointer<ObjCObjectVar<NSError?>>? = null
val serialized = json.encodeToString(it.manifest) as NSString
serialized.writeToFile(
createStorageDirectoryPath(getManifestFilePath()),
true,
NSUTF8StringEncoding,
error
)
}
}
Log crash
DefaultAuthStateRepository.setState - setState error: kotlin.native.concurrent.FreezingException: freezing of Continuation # 147 has failed, first blocker is vn.momo.core.modules.storage.async.AsyncStorageModuleImpl.AsyncStorageHolder#2bc5be8

How to print called functions in android?

I'm using frida on an APK and I'm trying to print out which function is being called, and especially which parameters are sent to it. My environment is set correctly as I can print out classes, and perform various actions in accordance to the docs.
This is the closest i've found:
https://codeshare.frida.re/#razaina/get-a-stack-trace-in-your-hook/
But the code gives out errors (ThreadDef undefined and so on)
And the frida docs didn't help me get where I'm trying to.
Any guidance? suggestions? help?
The code from codeshare#razaina has bugs which can be easily fixed (threadef != ThreadDef)
var printBacktrace = function () {
Java.perform(function() {
var JLog = Java.use('android.util.Log'), JException = Java.use('java.lang.Exception');
// getting stacktrace by throwing an exception
console.warn(JLog.getStackTraceString(JException.$new()));
});
};
Just call printBacktrace() w/e you want to see who called your hooked function.
If you want to hook all the methods of Java class you can use this snippet;
var Color = {
RESET: "\x1b[39;49;00m", Black: "0;01", Blue: "4;01", Cyan: "6;01", Gray: "7;11", Green: "2;01", Purple: "5;01", Red: "1;01", Yellow: "3;01",
Light: {
Black: "0;11", Blue: "4;11", Cyan: "6;11", Gray: "7;01", Green: "2;11", Purple: "5;11", Red: "1;11", Yellow: "3;11"
}
};
/**
*
* #param input.
* If an object is passed it will print as json
* #param kwargs options map {
* -l level: string; log/warn/error
* -i indent: boolean; print JSON prettify
* -c color: #see ColorMap
* }
*/
var LOG = function (input, kwargs) {
kwargs = kwargs || {};
var logLevel = kwargs['l'] || 'log', colorPrefix = '\x1b[3', colorSuffix = 'm';
if (typeof input === 'object')
input = JSON.stringify(input, null, kwargs['i'] ? 2 : null);
if (kwargs['c'])
input = colorPrefix + kwargs['c'] + colorSuffix + input + Color.RESET;
console[logLevel](input);
};
var printBacktrace = function () {
Java.perform(function() {
var android_util_Log = Java.use('android.util.Log'), java_lang_Exception = Java.use('java.lang.Exception');
// getting stacktrace by throwing an exception
LOG(android_util_Log.getStackTraceString(java_lang_Exception.$new()), { c: Color.Gray });
});
};
function traceClass(targetClass) {
var hook;
try {
hook = Java.use(targetClass);
} catch (e) {
console.error("trace class failed", e);
return;
}
var methods = hook.class.getDeclaredMethods();
hook.$dispose();
var parsedMethods = [];
methods.forEach(function (method) {
var methodStr = method.toString();
var methodReplace = methodStr.replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1];
parsedMethods.push(methodReplace);
});
uniqBy(parsedMethods, JSON.stringify).forEach(function (targetMethod) {
traceMethod(targetClass + '.' + targetMethod);
});
}
function traceMethod(targetClassMethod) {
var delim = targetClassMethod.lastIndexOf('.');
if (delim === -1)
return;
var targetClass = targetClassMethod.slice(0, delim);
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length);
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
LOG({ tracing: targetClassMethod, overloaded: overloadCount }, { c: Color.Green });
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function () {
var log = { '#': targetClassMethod, args: [] };
for (var j = 0; j < arguments.length; j++) {
var arg = arguments[j];
// quick&dirty fix for java.io.StringWriter char[].toString() impl because frida prints [object Object]
if (j === 0 && arguments[j]) {
if (arguments[j].toString() === '[object Object]') {
var s = [];
for (var k = 0, l = arguments[j].length; k < l; k++) {
s.push(arguments[j][k]);
}
arg = s.join('');
}
}
log.args.push({ i: j, o: arg, s: arg ? arg.toString(): 'null'});
}
var retval;
try {
retval = this[targetMethod].apply(this, arguments); // might crash (Frida bug?)
log.returns = { val: retval, str: retval ? retval.toString() : null };
} catch (e) {
console.error(e);
}
LOG(log, { c: Color.Blue });
return retval;
}
}
}
// remove duplicates from array
function uniqBy(array, key) {
var seen = {};
return array.filter(function (item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
var Main = function() {
Java.perform(function () { // avoid java.lang.ClassNotFoundException
[
// "java.io.File",
'java.net.Socket',
'com.package.MyCustomClass'
].forEach(traceClass);
});
};
Java.perform(Main);

How to hook os_log by using Frida

As the title stated, how can I hook os_log by using Frida?
Tried below, not working.
Interceptor.attach(Module.findExportByName("libSystem.B.dylib", "os_log"), {
onEnter: function (args) {
console.log(args[0] + args[1]);
}
});
Enabling all logs
var m = 'libsystem_trace.dylib';
// bool os_log_type_enabled(os_log_t oslog, os_log_type_t type);
var isEnabledFunc = Module.findExportByName(m, 'os_log_type_enabled');
// _os_log_impl(void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, unsigned int size);
var logFunc = Module.findExportByName(m, '_os_log_impl');
Interceptor.attach(isEnabledFunc, {
onLeave: function (ret) {
// console.log('log_enabled', ret);
ret.replace(0x1);
}
});
Interceptor.attach(logFunc, {
onEnter: function (a) {
var type = a[2]; // https://github.com/darlinghq/darling/blob/master/src/libc/os/log.h#L105
var format = a[3];
if (type != 0x2) {
console.log(JSON.stringify({
type: type,
format: format.readCString(),
//buf: a[4].readPointer().readCString()
}, null, 2));
}
}
})

Regex TypeError: Cannot read property '1' of null

My datepicker regular expression is trying matches on a null aray. How do I fix it? Not sure what clazz should equal if the array is null. I'm thinking a simple if (matches[1]) { etc } but I'm not sure what to do if matches is null. Clazz is used elsewhere twice in the code. Do I just set clazz to null or zero?
var matches = exp.match(IS_REGEXP);
var clazz = scope.$eval(matches[1]);
Edit: Here's where they use clazz
if (data.lastActivated !== newActivated) {
if (data.lastActivated) {
$animate.removeClass(data.lastActivated.element, clazz);
}
if (newActivated) {
$animate.addClass(newActivated.element, clazz);
}
data.lastActivated = newActivated;
}
Here's IS_REGEXP
11111111 22222222
var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
Double Edit:
Here's the whole function
function addForExp(exp, scope) {
var matches = exp.match(IS_REGEXP);
var clazz = scope.$eval(matches[1]);
var compareWithExp = matches[2];
var data = expToData[exp];
if (!data) {
var watchFn = function(compareWithVal) {
var newActivated = null;
instances.some(function(instance) {
var thisVal = instance.scope.$eval(onExp);
if (thisVal === compareWithVal) {
newActivated = instance;
return true;
}
});
if (data.lastActivated !== newActivated) {
if (data.lastActivated) {
$animate.removeClass(data.lastActivated.element, clazz);
}
if (newActivated) {
$animate.addClass(newActivated.element, clazz);
}
data.lastActivated = newActivated;
}
};
expToData[exp] = data = {
lastActivated: null,
scope: scope,
watchFn: watchFn,
compareWithExp: compareWithExp,
watcher: scope.$watch(compareWithExp, watchFn)
};
}
data.watchFn(scope.$eval(compareWithExp));
}
Setting clazz to null or empty string shall do, if clazz is all your concern.
var clazz = matches ? scope.$eval(matches[1]) : '';
But with compareWithExp, it might be better to exit from the whole logic when there is no match:
if ( ! matches ) return;

Resources