Skip to content

Commit

Permalink
Avoid stack overlow exception by limiting the maximum recursion depth (
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach committed Apr 26, 2019
1 parent 1786d0c commit 9a5f19e
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 12 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Expand Up @@ -5,7 +5,7 @@ buildscript {
maven { url 'https://jitpack.io' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.android.tools.build:gradle:3.4.0'
classpath 'com.github.JakeWharton:sdk-manager-plugin:0ce4cdf08009d79223850a59959d9d6e774d0f77'
classpath 'de.mobilej.unmock:UnMockPlugin:0.6.4'
}
Expand Down
Expand Up @@ -44,10 +44,13 @@
@TargetApi(18)
public class UiAutomationElement extends UiElement<AccessibilityNodeInfo, UiAutomationElement> {
private final static String ROOT_NODE_NAME = "hierarchy";
// https://github.com/appium/appium/issues/12545
private final static int MAX_DEPTH = 70;

private final static Map<AccessibilityNodeInfo, UiAutomationElement> cache = new WeakHashMap<>();
private final Map<Attribute, Object> attributes;
private final List<UiAutomationElement> children;
private int depth = 0;

/**
* A snapshot of all attributes is taken at construction. The attributes of a
Expand Down Expand Up @@ -100,6 +103,14 @@ protected UiAutomationElement(String hierarchyClassName, AccessibilityNodeInfo c
this.children = mutableChildren;
}

private int getDepth() {
return this.depth;
}

private void setDepth(int depth) {
this.depth = depth;
}

public static UiAutomationElement rebuildForNewRoot(AccessibilityNodeInfo rawElement, @Nullable List<CharSequence> toastMSGs) {
cache.clear();
UiAutomationElement root = new UiAutomationElement(ROOT_NODE_NAME, rawElement, 0);
Expand All @@ -121,10 +132,11 @@ public static UiAutomationElement getCachedElement(AccessibilityNodeInfo rawElem
return cache.get(rawElement);
}

private static UiAutomationElement getOrCreateElement(AccessibilityNodeInfo rawElement, int index) {
private static UiAutomationElement getOrCreateElement(AccessibilityNodeInfo rawElement, int index, int depth) {
UiAutomationElement element = cache.get(rawElement);
if (element == null) {
element = new UiAutomationElement(rawElement, index);
element.setDepth(depth);
cache.put(rawElement, element);
}
return element;
Expand All @@ -147,22 +159,25 @@ private void addToastMsgToRoot(CharSequence tokenMSG) {
}

private List<UiAutomationElement> buildChildren(AccessibilityNodeInfo node) {
int childCount = node.getChildCount();
if (childCount == 0) {
final int childCount = node.getChildCount();
if (childCount == 0 || getDepth() >= MAX_DEPTH) {
if (getDepth() >= MAX_DEPTH) {
Logger.warn(String.format("Skipping building children of '%s' because the maximum " +
"recursion depth (%s) has been reached", node, MAX_DEPTH));
}
return Collections.emptyList();
}

List<UiAutomationElement> children = new ArrayList<>(childCount);
Object allowInvisibleElements = AppiumUIA2Driver
boolean areInvisibleElementsAllowed = AppiumUIA2Driver
.getInstance()
.getSessionOrThrow()
.getCapability(ALLOW_INVISIBLE_ELEMENTS.toString());
boolean isAllowInvisibleElements = allowInvisibleElements != null && (boolean) allowInvisibleElements;
.getCapability(ALLOW_INVISIBLE_ELEMENTS.toString(), false);
for (int i = 0; i < childCount; i++) {
AccessibilityNodeInfo child = node.getChild(i);
//Ignore if element is not visible on the screen
if (child != null && (child.isVisibleToUser() || isAllowInvisibleElements)) {
children.add(getOrCreateElement(child, i));
if (child != null && (child.isVisibleToUser() || areInvisibleElementsAllowed)) {
children.add(getOrCreateElement(child, i, getDepth() + 1));
}
}
return children;
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/io/appium/uiautomator2/utils/Logger.java
Expand Up @@ -49,6 +49,15 @@ public static void error(String message, Throwable throwable) {
}
}

/**
* Logger warning
*/
public static void warn(Object... messages) {
if (android.util.Log.isLoggable(TAG, android.util.Log.WARN)) {
android.util.Log.w(TAG, getString(messages));
}
}

/**
* Logger info
*/
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -10,7 +10,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.android.tools.build:gradle:3.4.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Tue Jan 22 17:19:02 PST 2019
#Thu Apr 25 19:49:32 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

0 comments on commit 9a5f19e

Please sign in to comment.