NativeScript / android

Android runtime for NativeScript (based on V8)

Home Page:https://docs.nativescript.org/guide/android-marshalling

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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