Giter Site home page Giter Site logo

liquidplayer / liquidcore Goto Github PK

View Code? Open in Web Editor NEW
1.0K 24.0 127.0 459.28 MB

Node.js virtual machine for Android and iOS

License: MIT License

Ruby 2.11% JavaScript 8.97% C++ 48.66% Java 29.57% Objective-C 4.12% C 0.71% Python 0.62% Shell 0.30% CMake 4.93%
android node-virtual-machine ios javascript-framework nodejs node node-js javascriptcore liquidcore android-library

liquidcore's Introduction

The LiquidCore Project

Download

NPM

LiquidCore enables Node.js virtual machines to run inside Android and iOS apps. It provides a complete runtime environment, including a virtual file system.

LiquidCore also provides a convenient way for Android developers to execute raw JavaScript inside of their apps, as iOS developers can already do natively with JavaScriptCore.

Installation

Step 1: Make sure your project is configured for use with npm

In the root directory of your project, you must have a package.json file. If you do not already have one, you can create it by running:

$ npm init

and following the steps in the wizard.

Step 2: Install LiquidCore and configure package.json

$ npm i liquidcore
$ npx liquidcore init

The init step will add some utility scripts and the liquidcore object to your package.json file. It will also create an example service called example.js, which will get packaged into your app. You can change / add files to be packaged by editing the liquidcore.entry property in your package.json.

Step 3: Configure your mobile app project

Android

iOS

$ npx liquidcore gradle-config --module=<app>

where <app> is the name of your application module (the default in Android Studio is 'app').

$ npx liquidcore pod-config --target=<target> --podfile=<podfile>
$ npx liquidcore bundler --platform=ios
$ pod install

where <target> is your XCode project target, and <podfile> is the path of your application's Podfile

Notes on iOS

LiquidCore requires the use of Cocoapods, so make sure you've set up your project to use a Podfile first. Important: Your Podfile must contain the use_frameworks! directive. If you are not yet using Cocoapods, you can create a simple one like this:

platform :ios, '11.0'
use_frameworks!

target '<TARGET>' do
end

where <TARGET> is the name of your xcodeproj (without the .xcodeproj extension).

Also, any time you add a new liquidcore.entry point in package.json, you must first run the bundler and then run pod install again. This is a quirk of how Cocoapods works with finding files. Those files must exist at installation time, or they will not be available in the pod, even if they are created later. So after adding a new entry, just do this part again:

$ npx liquidcore bundler --platform=ios
$ pod install

Automatic Bundling

One of the newest features in 0.7.0+ is the ability to automatically bundle JavaScript files in the application build process. This is configured in the gradle-config and/or pod-config steps above. The bundling options are stored in the local package.json file in the liquidcore property. A typical file liquidcore object may look something like this:

  "liquidcore": {
    "entry": [
      "example.js",
      "index.js"
    ],
    "gradle_options": {
      "module": "app"
    },
    "bundler_output": {
      "android": "app/src/main/res/raw",
      "ios": ".liquidcore/ios_bundle"
    },
    "bundler_options": {
      "minify": false
    },
    "pod_options": {
      "dev": true,
      "target": "TestApp"
    }
  }

To include a new bundle, simply put the entry point JavaScript file in the entry array property. LiquidCore will generate one bundle for each entry during the build process.

If you have a non-standard configuration for your app, you may have to change some of these values. For example, bundler_output for Android assumes that your resources directory is at <app-module>/src/main/res. This is the Android Studio default. If you have changed this to something else, you will need to update this property.

Bundling is a convenient way to test and package your JavaScript projects. The bundler uses Metro to package up all of your required node modules into a single file that can be packaged as a resource in your app. If you are running on the Android Emulator or iOS Simulator, you can run a local server on your development machine and hot-edit your JavaScript code by npx liquidcore server in your project root. If you are using the Bundle API (described below), and your app is built in debug mode, it will first attempt to get the bundles from the server. If the server is not available, it will use the automated bundle packaged at build time. In release mode, it will always use the packaged bundle.

Usage

The MicroService API

A micro service is nothing more than an independent Node.js instance whose startup code is referenced by a URI. For example:

Android Kotlin

iOS Swift

val uri = MicroService.Bundle(androidContext, "example")
val service = MicroService(androidContext, uri)
service.start()
import LiquidCore
...
let url = LCMicroService.bundle("example")
let service = LCMicroService(url: url)
service?.start()

The service URI can either refer to a server URL or a local resource. For services that are automatically bundled with your app by LiquidCore, you can use the MicroService.Bundle() or LCMicroService.bundle() methods to generate the correct URI. Any javascript entry files referenced in your package.json in liquidcore.entry will get bundled automatically with each build. By default, the initialization script creates and packages example.js, but you can easily change this.

A micro service can communicate with the host app once the Node.js environment is set up. This can be determined by adding a start listener in the constructor:

Android Kotlin

iOS Swift

val uri = MicroService.Bundle(androidContext, "example")
val startListener = MicroService.ServiceStartListener {
    // .. The environment is live, but the startup
    // JS code (from the URI) has not been executed yet.
}
val service = MicroService(androidContext, uri,
    startListener)
service.start()

Conform to LCMicroServiceDelegate

let service = LCMicroService(url:url,
                        delegate:self)
service?.start()
...
func onStart(_ service: LCMicroService) {
    // .. The environment is live, but the
    // startup JS code (from the URI) has
    // not been executed yet.
}

A micro service communicates with the host through a simple EventEmitter interface, eponymously called LiquidCore. For example, in your JavaScript startup code:

LiquidCore.emit('my_event', {foo: "hello, world", bar: 5, l337 : ['a', 'b'] })

On the app side, the host app can listen for events:

Android Kotlin

iOS Swift

val listener = MicroService.EventListener {
    service, event, payload ->
    android.util.Log.i("Event:" + event,
        payload.getString("foo"))
    // logs: I/Event:my_event: hello, world
}
service.addEventListener("my_event", listener)

Conform to LCMicroServiceEventListener

service.addEventListener("my_event", listener:self)
...
func onEvent(_ service: LCMicroService, event: String,
               payload: Any?) {
    var p = (payload as! Dictionary<String,AnyObject>)
    NSLog(format:"Event: %@: %@", args:event, p["foo"]);
    // logs: Event:my_event: hello, world
}

Similarly, the micro service can listen for events from the host:

Android Kotlin

iOS Swift

val payload = JSONObject()
payload.put("hallo", "die Weld")
service.emit("host_event", payload)
var payload = ["hallo" : "die Weld"]
service.emitObject("host_event", object:payload)

Then, in Javascript:

LiquidCore.on('host_event', function(msg) {
   console.log('Hallo, ' + msg.hallo)
})

LiquidCore creates a convenient virtual file system so that instances of micro services do not unintentionally or maliciously interfere with each other or the rest of the Android/iOS filesystem. The file system is described in detail here.

Playing with example.js

When you follow the directions above, LiquidCore will automatically bundle a file called example.js, which looks like this:

const {LiquidCore} = require('liquidcore')

// A micro service will exit when it has nothing left to do.  So to
// avoid a premature exit, set an indefinite timer.  When we
// exit() later, the timer will get invalidated.
setInterval(()=>{}, 1000)

console.log('Hello, World!')

// Listen for a request from the host for the 'ping' event
LiquidCore.on( 'ping', () => {
    // When we get the ping from the host, respond with "Hello, World!"
    // and then exit.
    LiquidCore.emit( 'pong', { message: 'Hello, World from LiquidCore!' } )
    process.exit(0)
})

// Ok, we are all set up.  Let the host know we are ready to talk
LiquidCore.emit( 'ready' )

Below is an example of how to interact with this JavaScript code from the app. Note that hello_text on Android and textBox on iOS are UI elements whose setup is not shown here.

Android Kotlin

iOS Swift

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import org.liquidplayer.service.MicroService

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState:
                        Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val hello = findViewById<TextView>(
        R.id.hello_text)

    val readyListener = MicroService.EventListener {
      service, _, _ -> service.emit("ping")
    }
    val pongListener = MicroService.EventListener {
      _, _, jsonObject ->
      val message = jsonObject.getString("message")
      runOnUiThread { hello.text = message }
    }
    val startListener =
        MicroService.ServiceStartListener{
      service ->
      service.addEventListener("ready", readyListener)
      service.addEventListener("pong", pongListener)
    }
    val uri = MicroService.Bundle(this, "example")
    val service = MicroService(this, uri,
                               startListener)
    service.start()
  }
}
import UIKit
import LiquidCore

class ViewController: UIViewController,
  LCMicroServiceDelegate,
  LCMicroServiceEventListener {

  @IBOutlet weak var textBox: UITextField!

  override func viewDidLoad() {
    super.viewDidLoad()

    let url = LCMicroService.bundle("example")
    let service = LCMicroService(url: url,
                                delegate: self)
    service?.start()
  }

  func onStart(_ service: LCMicroService) {
    service.addEventListener("ready",listener: self)
    service.addEventListener("pong", listener: self)
  }

  func onEvent(_ service: LCMicroService,
                   event: String,
                 payload: Any?) {
    if event == "ready" {
      service.emit("ping")
    } else if event == "pong" {
      let p = (payload as! Dictionary<String,AnyObject>)
      let message = p["message"] as! String
      DispatchQueue.main.async {
        self.textBox.text = message
      }
    }
  }
}

You can use this as a guide for how to create your own services. You can use npm install to install most JS-only (non-native) modules and require them as normal. The bundler will package all of the code into a single file.

API Documentation

Android Javadocs (liquidcore-Nodejs)

Android Javadocs (liquidcore-V8)

iOS Objective-C/Swift

License

Copyright (c) 2014 - 2020 LiquidPlayer

Distributed under the MIT License. See LICENSE.md for terms and conditions.

liquidcore's People

Contributors

and42 avatar daetal-us avatar dsemenovsky avatar ericwlange avatar j0j00 avatar juliocbcotta avatar neusoft-technology-solutions avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

liquidcore's Issues

Allow usage of LiquidCoreAndroid with minSdkVersion = 16

Hi there,
and thank you for the great work you published here!

I am currently tinkering around with the LiquidCore for a projekt and it seems very promising.

While testing on a bunch of devices i run into a libc error which where fixed with the last release.

Since the project targets devices with sdkVersion >= 16 i am not able to update to the current version.

In the change log i can't find a change in LiquidCoreAndroid that requires minSdkVersion 17:
78edf66

Any chance to decrement the minSdkVersion for LiquidCoreAndroid only?

Best regards
Martin

Debug Logging from JS?

Is there an existing of planned interface to pass logging information from javascript back to the android log so we can see it in logcat?

Its difficult to trace errors that cross the javascript/java divide without some sort of shared logging.

Class not found exception when using LiquidCoreAndroid with proguard

Hi there,

When using the LiquidCore lib while proguard is activated will fail.

To solve one just need to add a simple proguard keep rule:
-keep class org.liquidplayer.javascript.** { *; }

May this could be part of the Library itself

Sample stacktrace for one running into the same issue:
03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] JNI DETECTED ERROR IN APPLICATION: JNI GetMethodID called with pending exception java.lang.ClassNotFoundException: Didn't find class "org.liquidplayer.javascript.JSValue$JNIReturnObject" on path: DexPathList[[zip file "/data/app/packagename.of.the.app-2/base.apk"],nativeLibraryDirectories=[/data/app/packagename.of.the.app-2/lib/x86, /data/app/packagename.of.the.app-2/base.apk!/lib/x86, /system/lib, /vendor/lib]] 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:56) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:380) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at org.liquidplayer.javascript.JSValue$b org.liquidplayer.javascript.JSObject.setProperty(long, long, java.lang.String, long, int) (JSObject.java:-2) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void org.liquidplayer.javascript.JSObject$5.run() (JSObject.java:337) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void org.liquidplayer.javascript.JSContext.a(java.lang.Runnable) (JSContext.java:66) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void org.liquidplayer.javascript.JSObject.a(java.lang.String, java.lang.Object, int) (JSObject.java:345) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void org.liquidplayer.javascript.JSObject.a(java.lang.String, java.lang.Object) (JSObject.java:360) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void packagename.of.the.app.wrapper.a.c.<init>(android.content.Context) (RiveScriptWrapper.java:51) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void packagename.of.the.app.wrapper.d.<init>(android.content.Context) (Wrapper.java:20) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at java.lang.Integer packagename.of.the.app.wrapper.d.b.d$a.a(java.net.URL[]) (SampleClass.java:384) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at java.lang.Object packagename.of.the.app..b.d$a.doInBackground(java.lang.Object[]) (AFragment.java:375) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at java.lang.Object android.os.AsyncTask$2.call() (AsyncTask.java:305) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void java.util.concurrent.FutureTask.run() (FutureTask.java:237) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void android.os.AsyncTask$SerialExecutor$1.run() (AsyncTask.java:243) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void java.util.concurrent.ThreadPoolExecutor.runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) (ThreadPoolExecutor.java:1133) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void java.util.concurrent.ThreadPoolExecutor$Worker.run() (ThreadPoolExecutor.java:607) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427] at void java.lang.Thread.run() (Thread.java:761) 03-29 11:13:15.861 6806-6828/packagename.of.the.app A/art: art/runtime/runtime.cc:427]

Simple send-receive event

Hi,

I'm very new to LiquidPlayer, and to get started, I want to achieve a simple thing:

  • make a small Android app which has a button, on which click an event is send to the JS side, which then returns the current DateTime to the app, and a simple TextView instance prints the value

As simple as that. This should proof that a simple communication between Java and Javascript is possible and continuous.

I've encountered some issues though:

  1. If a pass an instance of MicroService.ServiceErrorListener to the MicroService constructor, it's onExit callback gets called with an exitCode of 0. Why is that, and does that mean that the MicroService instance is exited just after its been created?
  2. I register my events in the onStart callback of the MicroService.ServiceStartListener instance. Can I register more events instead of just one?
  3. Do you have any working Android example project?
  4. Should I install anything from node in this section: https://github.com/LiquidPlayer/LiquidCore#the-micro-service?

Additionaly, sending and receiving events doesn't seem to work well. Here is the code

// register the event, which will be sent from the JS side
mMainService.addEventListener("event_receive_timestamp", new MicroService.EventListener() {
	@Override
	public void onEvent(MicroService service, String event, JSONObject payload) {
		Log.d(TAG, "onEvent: โ€” " + event + " โ€” " + payload);
		// update textview etc
	}
});

private void sendEvent() {
	JSONObject payload = new JSONObject();
	try {
		payload.put("hallo", "die Weld");
	} catch (JSONException e) {
		e.printStackTrace();
	}
	mMainService.emit("event_request_timestamp", payload);
}

// JS SIDE
LiquidCore.on('event_request_timestamp', function(msg) {
   LiquidCore.emit('event_receive_timestamp', {timestamp: new Date().toLocaleString()})
})

// the callback for "event_receive_timestamp" never gets called!

Random crash when running tests (sporadic)

Example dump:

12-07 08:53:03.001 12436-12453/org.liquidplayer.node.test A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x500000038 in tid 12453 (roidJUnitRunner)
12-07 08:53:03.105 7173-7173/? I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
12-07 08:53:03.106 7173-7173/? I/DEBUG: Build fingerprint: 'Lenovo/YT3_10_row_lte/YT3:5.1/LMY47I/YT3-X90L_USR_S100139_1601281130_:user/release-keys'
12-07 08:53:03.106 7173-7173/? I/DEBUG: Revision: '0'
12-07 08:53:03.106 7173-7173/? I/DEBUG: ABI: 'x86_64'
12-07 08:53:03.106 7173-7173/? I/DEBUG: pid: 12436, tid: 12453, name: roidJUnitRunner  >>> org.liquidplayer.node.test <<<
12-07 08:53:03.106 7173-7173/? I/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x500000038
12-07 08:53:03.129 7173-7173/? I/DEBUG:     rax 0000000500000000  rbx 00007f2545fe1000  rcx 0000000000000002  rdx 00007f2536ff3030
12-07 08:53:03.130 7173-7173/? I/DEBUG:     rsi 0000000500000000  rdi 000012e44941c881
12-07 08:53:03.130 7173-7173/? I/DEBUG:     r8  0000000000000001  r9  0000000000000000  r10 000000000000002a  r11 00007f2536ff3030
12-07 08:53:03.130 7173-7173/? I/DEBUG:     r12 00007f2545fe1000  r13 00007f2536ff3010  r14 00007f2536ff3018  r15 00007f2536ff3008
12-07 08:53:03.131 7173-7173/? I/DEBUG:     cs  0000000000000033  ss  000000000000002b
12-07 08:53:03.131 7173-7173/? I/DEBUG:     rip 00007f25378d7aca  rbp 00007f25478696a0  rsp 00007f2547869670  eflags 0000000000010206
12-07 08:53:03.131 7173-7173/? I/DEBUG: backtrace:
12-07 08:53:03.132 7173-7173/? I/DEBUG:     #00 pc 00000000006e5aca  /data/app/org.liquidplayer.node.test-2/lib/x86_64/libnode.so (v8::UnboundScript::BindToCurrentContext()+106)
12-07 08:53:03.133 7173-7173/? I/DEBUG:     #01 pc 0000000000705d23  /data/app/org.liquidplayer.node.test-2/lib/x86_64/libnode.so (v8::ScriptCompiler::Compile(v8::Local<v8::Context>, v8::ScriptCompiler::Source*, v8::ScriptCompiler::CompileOptions)+67)
12-07 08:53:03.134 7173-7173/? I/DEBUG:     #02 pc 0000000000705dc1  /data/app/org.liquidplayer.node.test-2/lib/x86_64/libnode.so (v8::Script::Compile(v8::Local<v8::Context>, v8::Local<v8::String>, v8::ScriptOrigin*)+81)
12-07 08:53:03.134 7173-7173/? I/DEBUG:     #03 pc 0000000000034ccd  /data/app/org.liquidplayer.node.test-2/lib/x86_64/libnodedroid.so (Java_org_liquidplayer_javascript_JSObject_makeFunction+877)
12-07 08:53:03.134 7173-7173/? I/DEBUG:     #04 pc 000000000031d07c  /data/dalvik-cache/x86_64/data@[email protected]@[email protected]
12-07 08:53:03.292 7173-7173/? I/DEBUG: Tombstone written to: /data/tombstones/tombstone_06
12-07 08:53:03.293 7680-12521/? W/ActivityManager: Error in app org.liquidplayer.node.test running instrumentation ComponentInfo{org.liquidplayer.node.test/android.support.test.runner.AndroidJUnitRunner}:
12-07 08:53:03.293 7680-12521/? W/ActivityManager:   Native crash
12-07 08:53:03.293 7680-12521/? W/ActivityManager:   Native crash: Segmentation fault
12-07 08:53:03.294 12424-12424/? D/AndroidRuntime: Shutting down VM

Discussion about LiquidCore performances

Hi ๐Ÿ‘‹

I'm currently in the process of rolling out LiquidCore as a replacement for an old JS engine we're using (jv8). The problem I'm encountering is that LiquidCore is slower compared to the other JS engine. I've compared the two engines using the below script:

for (var i=0; i < 1024; i++) { nativeFunction(i + 0.0); }

on the native side nativeFunction is only keeping the sum of all the indexes. Below are my test results:

  • LiquidCore runs in 1153ms
  • V8Runner runs in 26ms

I've created a public GH repo with the test project I've built in order to run the tests https://github.com/capezzbr/JSEnginesPlayground/ (feel free to have a look).


I'm wondering if there is any way we could improve LiquidCore to be more performant. My first guess is that LiquidCore is slower since it's using reflection to obtain the native call JS wants to invoke. Ideas?

invoke js methods from .js file

I am trying to find a way to invoke functions written in javascript and located in .js file on the server. Is there a way LiquidCore can work directly with .js file and its methods?

Typo in readme's gradle section

dependencies {
    ...
    compile 'com.github.LiquidPlayer.LiquidCore:0.2.2'
}

should read like:


dependencies {
    ...
    compile 'com.github.liquidplayer.LiquidCore:0.2.2'
}

How to set property from js into java object?

From java to js works fine:

class Test extends JSObject implements TestImpl {
  @jsexport public Property<JSValue> x;

  Test() {
    x.set(new JSValue(getContext(), "123"));
  }
}

If now I input in js test.x = "test"; nothing will happen. x in java will be equal 123.

Using Javascript foreign libraries

Hello,

I am new to the android development world and am currently working on a pet project. The problem I currently face is that I need to include a Javascript library called BigNumber in my project. And since this does not have any supported jar files, the only thing I can think of is to go for library like LiquidCore that lets me access Javascript functionalities in android.

My question : Is it possible to load external api for JavaScrpit libraries like , and if so, how?

Fingers crossed!!!

JNI ERROR (app bug): global reference table overflow (max=51200)

My team is currently trying to develop Android App with nonWebView JS execution. We've decided to try out your library and all seems fine, aside one frequent error, that crush our app. Our project require of existing multiple JSContext's for a long period of time, so we hold it by executing simple JS:
setInterval(function() {}, 1000);
Here is stack trace for the crush:

art/runtime/indirect_reference_table.cc:128] JNI ERROR (app bug): global reference table overflow (max=51200)
art/runtime/indirect_reference_table.cc:128] global reference table dump:
art/runtime/indirect_reference_table.cc:128]   Last 10 entries (of 51200):
art/runtime/indirect_reference_table.cc:128]     51199: 0x1447d0f0 org.liquidplayer.javascript.JSValue$19
art/runtime/indirect_reference_table.cc:128]     51198: 0x12f9c060 org.liquidplayer.node.Process$ProcessContext
art/runtime/indirect_reference_table.cc:128]     51197: 0x1447d0b0 org.liquidplayer.javascript.JSValue$19
art/runtime/indirect_reference_table.cc:128]     51196: 0x12f9c060 org.liquidplayer.node.Process$ProcessContext
art/runtime/indirect_reference_table.cc:128]     51195: 0x1447d0a0 org.liquidplayer.javascript.JSValue$19
art/runtime/indirect_reference_table.cc:128]     51194: 0x131d7fb0 org.liquidplayer.node.Process$ProcessContext
art/runtime/indirect_reference_table.cc:128]     51193: 0x1447d060 org.liquidplayer.javascript.JSValue$19
art/runtime/indirect_reference_table.cc:128]     51192: 0x12c29510 org.liquidplayer.node.Process$ProcessContext
art/runtime/indirect_reference_table.cc:128]     51191: 0x1447d050 org.liquidplayer.javascript.JSValue$19
art/runtime/indirect_reference_table.cc:128]     51190: 0x12f9c060 org.liquidplayer.node.Process$ProcessContext
art/runtime/indirect_reference_table.cc:128]   Summary:
art/runtime/indirect_reference_table.cc:128]     25212 of org.liquidplayer.javascript.JSValue$19 (25212 unique instances)
art/runtime/indirect_reference_table.cc:128]     25215 of org.liquidplayer.node.Process$ProcessContext (3 unique instances)

Obviously, the proble is with the amount of JSValues and the fact that each JSValue contains a reference of JSContext, 3 other JSContext references is we create and hold with our code. Can you point us in the direction of the problem and how to solve it? Is there a way to gc this objects?

instanceof regression

Adding the --harmony-instanceof flag has caused a regression in testJSFunctionConstructors()

JNI ERROR (app bug): local reference table overflow (max=512)

  • Lib version: 0.4.4
  • Doesn't happen on version: 0.2.2
  • I'm currently invoking
context.evaluateScript(src, name, 0);

where src is the content of a javascript file I'm evaluating in order to boot my JS environment.


Exception details

JNI ERROR (app bug): local reference table overflow (max=512)
local reference table dump:
  Last 10 entries (of 512):
      511: 0x12c41d00 java.lang.String "org/liquidplayer... (38 chars)
      510: 0x12c9dd60 org.liquidplayer.javascript.JNIReturnObject
      509: 0x12cbca00 java.lang.Class<org.liquidplayer.javascript.JNIReturnObject>
      508: 0x12cfcbf0 java.lang.String "org/liquidplayer... (43 chars)
      507: 0x12cbdf00 java.lang.Class<org.liquidplayer.javascript.JNIObject>
      506: 0x12c41d60 java.lang.String "org/liquidplayer... (37 chars)
      505: 0x12c71a10 org.liquidplayer.javascript.JNIJSValue
      504: 0x12ca9820 java.lang.Class<org.liquidplayer.javascript.JNIJSValue>
      503: 0x12c3ce80 java.lang.String "org/liquidplayer... (38 chars)
      502: 0x12c71a20 org.liquidplayer.javascript.JNIJSValue
  Summary:
       26 of org.liquidplayer.javascript.JNIJSObject (13 unique instances)
      102 of org.liquidplayer.javascript.JNIJSValue (84 unique instances)
       36 of org.liquidplayer.javascript.JNIReturnObject (36 unique instances)
        1 of org.liquidplayer.javascript.JNIJSValue[] (2 elements)
      174 of java.lang.String (174 unique instances)
      172 of java.lang.Class (5 unique instances)
        1 of java.lang.String[] (3 elements)

Runtime aborting...
Aborting thread:

Any JavaScript execution crashes app

LiquidCore version: 0.1.0
Android version: 7.0
AppCompat version: 25.0.1

It appears that running any JavaScript will cause a fatal error. I'm trying to run the following code:

// jsContext is injected
jsContext.evaluateScript("function factorial(x) { let f = 1; for (; x > 1; x--) { f *=x; } return f; }");
JSFunction factorial = jsContext.property("factorial").toFunction();
Integer result = factorial.call(JSObject(), 10).toNumber().toInt();
System.out.println(result)

Which is effectively the same code as in the example, but every time it gets run the following error occurs:

E/WVMExtractor: Failed to open libwvm.so: dlopen failed: library "libwvm.so" not found
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdeadbeef in tid 3325 (t.android.debug)

        [ 02-09 13:59:05.510  1203: 1203 W/         ]
        debuggerd: handling request: pid=3325 uid=10071 gid=10071 tid=3325

JSObjectPropertiesMap - proper usage

It looks like this is an inner wrapper class to assist with maps. If I set a map in context as follows:

    Map<String, Object> pageContext =  new HashMap<String, Object>();
    JSObject oPageContext = new JSObject(this.jsContext, pageContext);
    jsContext.property("pageContext", oPageContext);

Is this the proper way to then reference that map in the context (after running evaluate script and associated call function):

    JSValue _pageContext = jsContext.property("pageContext");
    Map<String, Object> map = (Map<String, Object> _pageContext.toObject();

thx, appreciate the assist.

"Stack overflow" when calling a method

When calling a method using the following code:

List jsParameters = new ArrayList<>();
for (Object parameter : paramaters) {
JSValue jsParameter = parameter).toJSValue(jsContext);
jsParameters.add(jsParameter);
}
JSValue value = jsFunction.call(null, jsParameters.toArray());

I get what looks like a stack overflow error:
Maximum call stack size exceeded
Unknown Error
at org.liquidplayer.javascript.JSFunction.apply(JSFunction.java:469)
at org.liquidplayer.javascript.JSFunction.call(JSFunction.java:425)

We are not using any recursion in any of our code, so I don't understand why I am seeing a stack overflow error. (We are passing in large arrays though in other calls). It seems to me though that looking at how stack overflow messages are used in the code, it could be any number of issues that is causing this.

I am assuming that isolate->StackOverflow(); is generating these messages, and happens in a lot of places for many different reasons.

Could we get a bit more clarity on the stack overflow error messages? The current message is too generic to be of much use when identifying where the errors are coming from.

Can't run a node `Process` after invoking raw V8

Node requires some flags to be set prior to calling V8::Initialize(). If you start by running a node process and then subsequently create some contexts using raw v8, everything works fine. If you swap the order, the app will crash on node initialization.

App crashes due to too many open files

The last segment of log when app crashes.
Complete log of the app runtime.

Background of App:

  • Every 8 seconds from a Fragment, 5 requests are sent to a microservice (5 new threads are opened for this).
  • The received response from the 5 requests (5 threads) within a period of 8 seconds are collected and displayed in UI.
  • Apps runs fine for around 11 minutes, but then it crashes with the following error:
    06-24 07:33:04.513 24171-1946/org.loklak.android.wok E/art: ashmem_create_region failed for 'indirect ref table': Too many open files.

JSContext pointing to "null" object.

Hi,

Scenario:

  • I am creating a JSContext and trying to keep the reference of JSContext object inside a map.
  • Then I am calling a method which is executing inside a thread
  • Inside this method, i am trying to access the stored reference of JSContext and call a JSFunction
  • But its reference is pointing to "null" JSContext object.

This happens with JSFunction and JSObject references also.
Without any thread i am able to access the JSContext refence.

Thanks ,
Ayush

Is it possible to reduce file size

Hey,
me again, when building the app with the library included the download size (size of the packed apk) and the usage on device increase dramatically:

image

Since we have to deal with small devices (from a memory perspective) every megabyte counts. Is there a way to reduce the usage of the native libraries?
I am not in to native libraries yet but may we could have a "common" library instead of increasing the size by 4 when including the native libs for every system architecture.

If i can support in any way just let me know!

Best regards
Martin

JNI code should reuse JSC

There is a lot of cut-and-paste between JNI/ and JSC/. JSC has been debugged to conform to WebKit tests. Ideally use that.

Calling constructors from within the java script

Is it possible to call constructors from within the javascript. For Example if I have a PageDefinition which extends JSObject with the supporting JSContext constructor, should I be able to do the following in javascript: "var createPageDef = function() { var pageDefinition = PageDefinition("My Page Def"); return pageDefinition; }; ");

Thx, appreciate the assist

Promise doesn't work

I try to using Promise but nothing works out. The following code doesn't work:

var promise = new Promise(function(resolve, reject) {
    resolve(123);
});

promise.then(function(result) {
    console.log(result);
});

This code will work fine if I execute it in a browser.

avc: denied error on network calls

I'm running an emulator and have been trying to make network calls from Node using request and have also tried axios. So far I've run into these two issues with both:

W/dcorehelloworld: type=1400 audit(0.0:59): avc: denied { ioctl } for path="/sys/kernel/debug/tracing/trace_marker" dev="debugfs" ino=3080 ioctlcmd=5451 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:debugfs_trace_marker:s0 tclass=file permissive=0

W/dcorehelloworld: type=1400 audit(0.0:60): avc: denied { ioctl } for path="socket:[181434]" dev="sockfs" ino=181434 ioctlcmd=5451 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:r:surfaceflinger:s0 tclass=unix_stream_socket permissive=0

I've also tried this on a physical Nexus 6p and I'm not seeing the errors but none of my callbacks in Java are being called, so I'm assuming the request is not going through.

JavaScriptCore handles reference counting differently

The JSC bridge is written using the Retainer class in jni/common.h. That class expects the developer to clean up any references. When the subclass is created, it is created with a reference count of 1. Any subsequent holders must retain() the object and then release() it when done. Newly created or returned objects are assumed to have been retained and it is up to the caller to release.

The way the JavaScriptCore API is designed is slightly different. It allows lazy usage of references. For example, you can create a new JSValueRef, use it and then ignore it. It will get cleaned up when the garbage collector runs unless it has been explicitly retained. Only when an explicit JSValueRetain is used is a parallel JSValueRelease required.

Confusingly, though, it seems this is only the case with JSValueRef and JSObjectRef. JSClassRef, JSContextRef, JSContextGroupRef, JSPropertyNameArrayRef and JSStringRef all operate the same as is designed in Retainer.

Need to build a wrapper around JSValue<Value> that manages the value references the way JavaScriptCore expects, and stores them in a map which gets cleaned up upon garbage collection.

JNI NewObjectV called with pending exception java.lang.NoSuchMethodError

Details

  • Using: LiquidCore:0.4.4
  • Work on LiquidCore:0.2.2
  • Tested on physical device and simulator

Stack trace

Pending exception java.lang.NoSuchMethodError: no non-static method "Lorg/liquidplayer/javascript/JNIJSContextGroup;.<init>(J)V"
  at org.liquidplayer.javascript.JNIJSContextGroup org.liquidplayer.javascript.JNIJSContextGroup.create() (JNIJSContextGroup.java:-2)
  at void org.liquidplayer.javascript.JSContextGroup.<init>() (JSContextGroup.java:71)
  at void org.liquidplayer.javascript.JSContext.<init>() (JSContext.java:125)
  at void yapl.js.fusion.JavaScriptCore.JavascriptEngine.<init>() (JavascriptEngine.java:32)
  at yapl.js.fusion.FusionEngine yapl.js.fusion.FusionEngine.getJSEngine() (FusionEngine.java:15)

Memory issue on native objects

Hi Erik, I've a big problem with memory using liquid core.
I've an android app that have a timer that executes code.
If I attach profiler I can see that native memory consumption is always growing but there is no place where this memory is became free and this causes an app restart
Have you some tricks for this issue?

Improve Initialization Time

Hello,

Thanks for developing such an awesome library! I had one question around the initialization time, specifically the time it takes from calling start on a micro service to getting the onStart callback and then getting my "READY" callback after my js file has loaded. On average from the time I call start on the micro service to the time I get the onStart callback I see about a 500 ms overhead. Then from onStart to the time I get my "READY" callback at the end of my js file (which is fairly large at about 3mb) its about another 2 seconds. I was wondering if anyone else has experienced anything like this and found a way to reduce this overhead.

Thanks,
Tyler

"Maximum call stack size exceeded" in chrome debugger if launch without Microservice

I connected Stetho to LiquidCore. this and other code works fine in chrome debugger if I use Microservice. But if I use raw JSContext without running Microservice I periodically get JSException "Maximum call stack size exceeded" in chrome debugger. This problem disappears if I close debugger and open again, close and open again and again many times (random number of repetitions).
Is there idea what's wrong?

Crashes when running testJavaScriptCoreMiniDOM

Device: Nexus 5X with 7.1.1
Emulator: Nexus 5 API 24

A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 19475 (roidJUnitRunner)

Build fingerprint: 'google/bullhead/bullhead:7.1.1/N4F26O/3582057:user/release-keys'
02-15 15:10:32.694 19491-19491/? A/DEBUG: Revision: 'rev_1.0'
02-15 15:10:32.694 19491-19491/? A/DEBUG: ABI: 'arm64'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.