Native methods are not callable when using factory pattern and private class
NCenerar opened this issue · comments
Environment
% tns info
✔ Getting NativeScript components versions information...
✔ Component nativescript has 8.1.5 version and is up to date.
✔ Component @nativescript/core has 8.1.5 version and is up to date.
✖ Component @nativescript/ios is not installed.
✔ Component @nativescript/android has 8.1.1 version and is up to date.
"dependencies": {
"@nativescript/core": "~8.1.1",
"@nativescript/theme": "~3.0.1",
"nativescript-vue": "~2.9.0"
},
"devDependencies": {
"@nativescript/android": "8.1.1",
"@nativescript/types": "~8.1.1",
"@nativescript/webpack": "~5.0.0",
"@types/node": "~14.6.2",
"nativescript-vue-template-compiler": "~2.9.0",
"typescript": "~4.3.5",
"vue": "~2.6.12"
}
Describe the bug
When running my project, I get an TypeError: theProblem.run is not a function
but it exists.
The problem seems to occur because the underlying native code uses a factory pattern and the returned instance is built from a package protected class.
App_Resources/Android/src/main/java/mypackage/PublicClass.java
package mypackage;
import android.util.Log;
class PackageProtectedClass implements Runnable {
@Override
public void run() {
Log.d("JS", String.format("Run: I am %s %s", this.getClass(), this));
}
}
public class PublicClass {
public PackageProtectedClass getIt() {
return new PackageProtectedClass();
}
}
The getIt()
method returns an object that implements the Runnable
interface but the run()
method cannot be called from NativeScript.
To Reproduce
Clone my project:
git clone https://github.com/NCenerar/nativescript-issue-01.git
Enter the project and run it on android:
tns debug android
Expected behavior
The run()
method should be found and called.
Sample project
You can find a project reproducing the problem here: https://github.com/NCenerar/nativescript-issue-01.git
Additional context
./types/references.d.ts
/// <reference path="../node_modules/@nativescript/types/index.d.ts" />
declare namespace mypackage {
export class PackageProtectedClass extends java.lang.Object implements java.lang.Runnable {
public constructor(): PackageProtectedClass;
public run(): void;
}
export class PublicClass extends java.lang.Object {
public constructor(): PublicClass;
public getIt(): mypackage.PackageProtectedClass;
}
}
Script part of ./app/Components/Home.vue
import Vue from "nativescript-vue";
function logObject(name: string, obj: any, indent: number = 0) {
const prefix = ''.padStart(indent, ' ');
const proto = Object.getPrototypeOf(obj);
if (name) console.log(prefix, name, obj);
console.log(prefix, " >", Object.entries(obj));
console.log(prefix, " >", Object.getOwnPropertyNames(obj));
if (obj && proto && obj !== Object.prototype) {
logObject('', proto, indent + 2);
}
}
export default Vue.extend({
computed: {
message() {
return "Blank {N}-Vue app";
}
},
mounted() {
logObject("mypackage", mypackage);
logObject("PublicClass", mypackage.PublicClass);
logObject("PackageProtectedClass", mypackage.PackageProtectedClass);
const theFactory = new mypackage.PublicClass();
const theProblem = theFactory.getIt();
logObject("theFactory", theFactory);
logObject("theProblem", theProblem);
console.log("theProblem.run :", theProblem.run);
const theClass = theProblem.getClass();
const theInterfaces = theClass.getInterfaces();
const theDeclaredMethods = theClass.getDeclaredMethods()
console.log("theProblem.getClass() :", theClass);
console.log("theClass.getInterfaces() :", theInterfaces);
for (let i=0; i<theInterfaces.length; i++) {
console.log(" > ", i, theInterfaces[i]);
}
console.log("theClass.getDeclaredMethods() :", theDeclaredMethods);
for (let i=0; i<theDeclaredMethods.length; i++) {
console.log(" > ", i, theDeclaredMethods[i]);
}
try {
theProblem.run();
} catch (e) {
console.error(e, (e as Error).stack);
}
}
});
The output:
JS: mypackage {}
JS: > [[PublicClass, function () { [native code] }], [PackageProtectedClass, function () { [native code] }]]
JS: > [PublicClass, PackageProtectedClass]
JS: > []
JS: > [constructor, __defineGetter__, __defineSetter__, hasOwnProperty, __lookupGetter__, __lookupSetter__, isPrototypeOf, propertyIsEnumerable, toString, valueOf, __proto__, toLocaleString]
JS: PublicClass function () { [native code] }
JS: > [[extend, function () { [native code] }], [null, function () { [native code] }], [class, class mypackage.PublicClass]]
JS: > [length, name, arguments, caller, prototype, extend, null, class, valueOf]
JS: > [[extend, function () { [native code] }], [null, function () { [native code] }], [class, class java.lang.Object]]
JS: > [length, name, arguments, caller, prototype, extend, null, class, valueOf]
JS: > []
JS: > [length, name, arguments, caller, constructor, apply, bind, call, toString]
JS: > []
JS: > [constructor, __defineGetter__, __defineSetter__, hasOwnProperty, __lookupGetter__, __lookupSetter__, isPrototypeOf, propertyIsEnumerable, toString, valueOf, __proto__, toLocaleString]
JS: PackageProtectedClass function () { [native code] }
JS: > [[extend, function () { [native code] }], [null, function () { [native code] }], [class, class mypackage.PackageProtectedClass]]
JS: > [length, name, arguments, caller, prototype, extend, null, class, valueOf]
JS: > [[extend, function () { [native code] }], [null, function () { [native code] }], [class, class java.lang.Object], [valueOf, function () { [native code] }]]
JS: > [length, name, arguments, caller, prototype, extend, null, class, valueOf]
JS: > []
JS: > [length, name, arguments, caller, constructor, apply, bind, call, toString]
JS: > []
JS: > [constructor, __defineGetter__, __defineSetter__, hasOwnProperty, __lookupGetter__, __lookupSetter__, isPrototypeOf, propertyIsEnumerable, toString, valueOf, __proto__, toLocaleString]
JS: theFactory mypackage.PublicClass@b67070e
JS: > []
JS: > []
JS: > [[<init>, function <init>() { [native code] }], [getIt, function getIt() { [native code] }]]
JS: > [<init>, getIt, constructor]
JS: > [[<init>, function <init>() { [native code] }], [clone, function clone() { [native code] }], [equals, function equals() { [native code] }], [finalize, function finalize() { [native code] }], [getClass, function getClass() { [native code] }], [hashCode, function hashCode() { [native code] }], [notify, function notify() { [native code] }], [notifyAll, function notifyAll() { [native code] }], [toString, function toString() { [native code] }], [wait, function wait() { [native code] }]]
JS: > [<init>, clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, constructor]
JS: > []
JS: > [constructor, __defineGetter__, __defineSetter__, hasOwnProperty, __lookupGetter__, __lookupSetter__, isPrototypeOf, propertyIsEnumerable, toString, valueOf, __proto__, toLocaleString]
JS: theProblem mypackage.PackageProtectedClass@1d8752f
JS: > [[constructor, function () { [native code] }]]
JS: > [constructor]
JS: > []
JS: > [constructor]
JS: > [[<init>, function <init>() { [native code] }], [clone, function clone() { [native code] }], [equals, function equals() { [native code] }], [finalize, function finalize() { [native code] }], [getClass, function getClass() { [native code] }], [hashCode, function hashCode() { [native code] }], [notify, function notify() { [native code] }], [notifyAll, function notifyAll() { [native code] }], [toString, function toString() { [native code] }], [wait, function wait() { [native code] }]]
JS: > [<init>, clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, constructor]
JS: > []
JS: > [constructor, __defineGetter__, __defineSetter__, hasOwnProperty, __lookupGetter__, __lookupSetter__, isPrototypeOf, propertyIsEnumerable, toString, valueOf, __proto__, toLocaleString]
JS: theProblem.run : undefined
JS: theProblem.getClass() : class mypackage.PackageProtectedClass
JS: theClass.getInterfaces() : [Ljava.lang.Class;@e38673c
JS: > 0 interface java.lang.Runnable
JS: theClass.getDeclaredMethods() : [Ljava.lang.reflect.Method;@1e06cc5
JS: > 0 public void mypackage.PackageProtectedClass.run()
JS: TypeError: theProblem.run is not a function TypeError: theProblem.run is not a function
JS: at VueComponent.mounted (file: app/webpack:/issue-native-android/app/components/Home.vue?3a7c:60:17)
JS: at invokeWithErrorHandling (file: app/webpack:/issue-native-android/node_modules/nativescript-vue/dist/index.js:1862:0)
JS: at callHook$1 (file: app/webpack:/issue-native-android/node_modules/nativescript-vue/dist/index.js:5097:0)
JS: at Object.insert (file: app/webpack:/issue-native-android/node_modules/nativescript-vue/dist/index.js:4019:0)
JS: at invokeInsertHook (file: app/webpack:/issue-native-android/node_modules/nativescript-vue/dist/index.js:5680:0)
JS: at Vue.patch [as __patch__] (file: app/webpack:/issue-native-android/node_modules/nativescript-vue/dist/index.js:5899:0)
JS: at Vue._update (file: app/webpack:/issue-native-android/node_modules/nativescript-vue/dist/index.js:4823:0)
JS: at Vue.updateComponent (file: app/webpack:/issue-native-android/node_modules/nativescript-vue/dist/index.js:4944:0)
JS: at Watcher.get (file:///data/data/org.nativescript.issuen...
@NCenerar can you try this again with the latest android alpha runtime