Skip to content

Commit dff0fd3

Browse files
authored
Merge pull request #1164 from NativeScript/trifonov/fix-NoClassDefFoundError
Fixing NoClassDefFoundError when using older APIs
2 parents f416921 + 1fa1b6e commit dff0fd3

File tree

2 files changed

+104
-39
lines changed

2 files changed

+104
-39
lines changed
Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
exports.run = function(cntxt)
22
{
33
describe("Tests with context ", function () {
4-
4+
55
var context = cntxt;
66
var myCustomEquality = function(first, second) {
77
return first == second;
88
};
9-
9+
1010
beforeEach(function() {
1111
jasmine.addCustomEqualityTester(myCustomEquality);
1212
});
13-
13+
1414
it("TestConstructorOverrideForBuiltinType", function () {
15-
15+
1616
__log("TEST: TestConstructorOverrideForBuiltinType");
17-
17+
1818
var ctorCalled = false;
1919
var isConstructor = false;
2020

@@ -24,48 +24,48 @@ exports.run = function(cntxt)
2424
isConstructor = arguments[arguments.length - 1];
2525
}
2626
});
27-
27+
2828
var btn = new MyButton(context);
29-
29+
3030
expect(ctorCalled).toEqual(true);
3131
expect(isConstructor).toEqual(true);
3232
});
33-
33+
3434
it("TestConstructorOverrideForBuiltinTypeWithInitMethod", function () {
35-
35+
3636
__log("TEST: TestConstructorOverrideForBuiltinTypeWithInitMethod");
37-
37+
3838
var initInvocationCount = 0;
3939

4040
var MyDatePicker = android.widget.DatePicker.extend({
4141
init: function() {
4242
++initInvocationCount;
4343
}
4444
});
45-
45+
4646
var datePicker = new MyDatePicker(context);
47-
47+
4848
__log("datePicker=" + datePicker);
49-
49+
5050
var count1 = initInvocationCount;
51-
51+
5252
expect(count1).toBeGreaterThan(0);
53-
53+
5454
datePicker.init(2014, 3, 25, null);
55-
55+
5656
var count2 = initInvocationCount;
57-
57+
5858
expect(count2).toBeGreaterThan(count1);
5959
});
60-
60+
6161
it("TestBuiltinNestedClassCreation", function () {
62-
62+
6363
__log("TEST: TestBuiltinNestedClassCreation");
64-
64+
6565
var loader = new android.content.Loader(context);
6666

6767
var observer = new android.content.Loader.ForceLoadContentObserver(loader);
68-
68+
6969
expect(observer).not.toEqual(null);
7070
});
7171

@@ -82,35 +82,47 @@ exports.run = function(cntxt)
8282

8383
expect(exceptionCaught).toBe(true);
8484
});
85-
85+
8686
it("TestPublicWindowManagerImplWithoutMetadata", function () {
87-
87+
8888
__log("TEST: TestPublicWindowManagerImplWithoutMetadata");
89-
89+
9090
var windowManagerImpl = context.getSystemService(android.content.Context.WINDOW_SERVICE);
91-
91+
9292
var display = windowManagerImpl.getDefaultDisplay();
93-
93+
9494
//__log("display.isValid=" + display.isValid());
95-
95+
9696
var displayInfo = display.toString();
97-
97+
9898
expect(displayInfo.length).toBeGreaterThan(0);
9999
});
100-
100+
101101
it("TestCanPassCharSequenceArray", function () {
102-
102+
103103
__log("TEST: TestCanPassCharSequenceArray");
104-
104+
105105
var alert = new android.app.AlertDialog.Builder(context);
106-
106+
107107
var builder = alert.setItems(["One", "Two" ], new android.content.DialogInterface.OnClickListener({
108108
onClick: function (dialog, which) {
109109
//
110110
}
111111
}));
112-
112+
113113
expect(builder).not.toEqual(null);
114114
});
115+
116+
it("TestOldAPIForGettingMethodsListForMethodsWithParametersFromMissingType", function () {
117+
__log("TEST: TestOldAPIForGettingMethodsListForMethodsWithParametersFromMissingType");
118+
119+
var til = new android.support.design.widget.TextInputLayout(context);
120+
var editText = new android.widget.EditText(context);
121+
var relativeLayout = new android.widget.RelativeLayout(context);
122+
var relativeLayoutParams = new android.widget.RelativeLayout.LayoutParams(android.widget.RelativeLayout.LayoutParams.MATCH_PARENT, android.widget.RelativeLayout.LayoutParams.MATCH_PARENT);
123+
relativeLayout.setLayoutParams(relativeLayoutParams);
124+
editText.setHint("TEST");
125+
til.addView(editText);
126+
});
115127
});
116128
};

test-app/runtime/src/main/java/com/tns/MethodResolver.java

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,19 @@ static String resolveMethodOverload(Class<?> clazz, String methodName, Object[]
112112
methodOverloadsForClass.put(c, finder);
113113
}
114114

115-
ArrayList<Method> matchingMethods = finder.getMatchingMethods(methodName);
116-
tryFindMatches(methodName, candidates, args, argLength, matchingMethods);
117-
if (candidates.size() > iterationIndex && candidates.get(iterationIndex).y == 0) {
118-
// direct matching (distance 0) found
119-
break;
115+
if(!finder.errorGettingMethods()) {
116+
ArrayList<Method> matchingMethods = finder.getMatchingMethods(methodName);
117+
tryFindMatches(methodName, candidates, args, argLength, matchingMethods);
118+
if (candidates.size() > iterationIndex && candidates.get(iterationIndex).y == 0) {
119+
// direct matching (distance 0) found
120+
break;
121+
}
122+
} else {
123+
Method method = finder.getMatchingMethodWithArguments(methodName, args);
124+
if(method != null) {
125+
candidates.add(new Tuple<>(method, 0));
126+
break;
127+
}
120128
}
121129

122130
c = c.getSuperclass();
@@ -479,11 +487,42 @@ private static boolean convertPrimitiveArg(Class<?> primitiveType, Object[] args
479487
static class MethodFinder {
480488
private Method[] declaredMethods;
481489
private HashMap<String, ArrayList<Method>> matchingMethods = new HashMap<String, ArrayList<Method>>();
490+
private final Class<?> clazz;
491+
private final boolean couldNotGetMethods;
492+
482493
public MethodFinder(Class<?> clazz) {
483-
this.declaredMethods = clazz.getDeclaredMethods();
494+
this.clazz = clazz;
495+
boolean errorGettingMethods = false;
496+
try {
497+
this.declaredMethods = clazz.getDeclaredMethods();
498+
} catch (NoClassDefFoundError error) {
499+
// get at least the public methods as it shouldn't fail with NoClassDefFoundError
500+
// it is not a good practice to catch Errors in Java, but we have the following case:
501+
// when using support library > 26.0.0 and android API < 23
502+
// if we try to create android.support.design.widget.TextInputLayout and call its addView method
503+
// such error is thrown for android.view.ViewStructure as it is not present in older APIs
504+
// so in that case we are going to get only the public methods using getMethods which may or may not throw the same error
505+
// depends on the java Class implementation, on some of the cases it calls getDeclaredMethods() internally and
506+
try {
507+
this.declaredMethods = clazz.getMethods();
508+
} catch (NoClassDefFoundError err) {
509+
// if an error is thrown here we would set the declared methods to an empty array
510+
// then when searching for a method we will try to find the exact method instead of looking in the declaredMethods list
511+
this.declaredMethods = new Method[]{};
512+
errorGettingMethods = true;
513+
}
514+
}
515+
this.couldNotGetMethods = errorGettingMethods;
516+
}
517+
518+
public boolean errorGettingMethods() {
519+
return couldNotGetMethods;
484520
}
485521

486522
public ArrayList<Method> getMatchingMethods(String methodName) {
523+
if(this.errorGettingMethods()) {
524+
return null;
525+
}
487526
ArrayList<Method> matches = this.matchingMethods.get(methodName);
488527
if (matches == null) {
489528
matches = new ArrayList<Method>();
@@ -505,5 +544,19 @@ public ArrayList<Method> getMatchingMethods(String methodName) {
505544

506545
return matches;
507546
}
547+
548+
public Method getMatchingMethodWithArguments(String methodName, Object[] args) {
549+
// fallback mechanism to try to find the exact method by name and arguments
550+
// this method is not so useful as the arguments need to match the method types directly, but still it can find a method in some cases
551+
Class<?>[] types = new Class<?>[args.length];
552+
for (int i = 0; i < args.length; i++) {
553+
types[i] = args[i].getClass();
554+
}
555+
try {
556+
return this.clazz.getDeclaredMethod(methodName, types);
557+
} catch (NoSuchMethodException ex) {
558+
return null;
559+
}
560+
}
508561
}
509562
}

0 commit comments

Comments
 (0)