V8: Fatal error: CHECK(V8::ArrayBufferAllocator() != NULL) failed - javascript

I get this error when I try to run some JS code (this + this) with V8 (tried master from two weaks ago, 3.23.17, 3.24.40, 3.25.5; 3.23.0 doesn't work anymore because of API changes):
#
# Fatal error in ..\..\src\runtime.cc, line 785
# CHECK(V8::ArrayBufferAllocator() != NULL) failed
#
A lot of other JS code has worked already, so I wonder what the problem is.
It's on Win8 with a x64 build. V8 has been build just as described in the official docs (using gyp + MSVC 2012). I don't think that there was a problem because it worked fine already with most other JS code.
I think that there might be an issue with V8 itself, but not sure...
I also asked on the mailing-list here.
Some C++ code, but I don't think that there is a problem with it because it worked fine with other JS code:
#include <string>
#include <assert.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <boost/noncopyable.hpp>
#include <v8.h>
// Create a new isolate (completely isolated JS VM).
struct V8Isolate : boost::noncopyable {
v8::Isolate* isolate;
V8Isolate() : isolate(v8::Isolate::New()) {}
~V8Isolate() { isolate->Dispose(); }
operator v8::Isolate*() { return isolate; }
v8::Isolate* operator->() { return isolate; }
};
struct ReturnType {
std::string err; // non-empty if there is an error
ReturnType(bool success) {
assert(success);
}
ReturnType(const std::string& _err) : err(_err) {
assert(!err.empty());
}
ReturnType(const char* _err) : err(_err) {
assert(!err.empty());
}
operator bool() const { return err.empty(); }
};
#define CHECK_RETURN(cmd) { ReturnType ret = (cmd); if(!ret) return ret; }
using namespace std;
using namespace v8;
ReturnType readFile(const std::string& filename, std::string& res) {
res = "";
FILE* f = fopen(filename.c_str(), "r");
if(!f) return "File '" + filename + "' cannot be opened";
while(!feof(f) && !ferror(f)) {
char buffer[1024 * 8];
size_t s = fread(buffer, 1, sizeof(buffer), f);
if(s > 0)
res.append(buffer, buffer + s);
}
auto err = ferror(f);
fclose(f);
if(err)
return "Error while reading file '" + filename + "'";
return true;
}
ReturnType execJsFile(const std::string& jsSourceDir, const std::string& extfilename) {
v8::TryCatch try_catch;
std::string sourceStr;
CHECK_RETURN(readFile(jsSourceDir + "/" + extfilename, sourceStr));
Local<String> origin = String::NewFromUtf8(Isolate::GetCurrent(), &extfilename[0], String::kNormalString, (int)extfilename.size());
Local<String> source = String::NewFromUtf8(Isolate::GetCurrent(), &sourceStr[0], String::kNormalString, (int)sourceStr.size());
Local<v8::Script> script = Script::Compile(source, origin);
if(script.IsEmpty()) {
assert(try_catch.HasCaught());
return "JS compile failed: " + jsObjToString(try_catch.Exception());
}
// Run the script to setup its environment
Local<Value> result = script->Run();
if(result.IsEmpty()) {
assert(try_catch.HasCaught());
return "JS script execution failed: " + jsReportExceptionToString(Isolate::GetCurrent(), &try_catch);
}
return true;
}
ReturnType loadJsGit() {
V8Isolate isolate;
v8::Isolate::Scope isolateScope(isolate);
HandleScope handleScope(isolate);
Handle<Context> context = Context::New(isolate);
Context::Scope contextScope(context);
auto globalObj = context->Global();
CHECK_RETURN(execJsFile(".", "global.js"));
CHECK_RETURN(execJsFile(".", "jsgit.js"));
return true;
}
int main(int argc, char** argv) {
ReturnType ret = loadJsGit();
if(!ret) cout << "error: " << ret.err << endl;
}

You need to initialize array buffer allocator.
Use malloc, for example:
class MallocArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
virtual void* Allocate(size_t length) { return malloc(length); }
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
virtual void Free(void* data, size_t length) { free(data); }
};
Initialize:
v8::V8::SetArrayBufferAllocator(new MallocArrayBufferAllocator);

Related

CefSharp Javascript registration and execution is not working in Release 79.1.36

I tried to upgrade CefSharp from Version 69.0.0.0 to 79.1.36.
I could not get the Javascript interaction working.
The registration changed from
this.Browser.RegisterJsObject
to
this.Browser.JavascriptObjectRepository.Register
according to https://github.com/cefsharp/CefSharp/issues/2990.
When I execute EvaluateScriptAsync, I get a response back with Status Canceled.
Trying to understand how to implement it correctly, I examined the CefSharp.WpfExample and noticed that the Javascript functionality in the example WPF application does not work either.
The Execute Javascript (asynchronously) does not do anything when clicking the Run button.
The Evaluate Javascript (Async) returns:
Uncaught ReferenceError: bound is not defined # about:blank:1:0
Did the Javascript functionality break in the latest release?
Update
Here is how it is used in our code.
This is the registration
public void RegisterJavaScriptHandler(string name, object handler)
{
try
{
CefSharpSettings.LegacyJavascriptBindingEnabled = true;
this.Browser.JavascriptObjectRepository.Register(name, handler, false, new BindingOptions() { CamelCaseJavascriptNames = false });
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
This is the EvaluateScriptAsync part
public void InitializeLayers()
{
try
{
int count = _mapLogic.Layers.Count();
foreach (WMSLayer layer in _mapLogic.Layers)
{
if (!_loadedLayers.Contains(layer))
{
var script = string.Format("addWMSLayer('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}')",
layer.ProviderCode.Url, layer.AttributionText, layer.AttributionHref,
layer.Layer, layer.FormatCode.Format, layer.ServerType, layer.Res1, layer.Res2, layer.Res3, layer.Res4);
var response = this.ECBBrowser.Browser.EvaluateScriptAsync(script, new TimeSpan(0, 0, 1));
response.ContinueWith(t =>
{
count--;
if (count == 0) this.initializeMap();
});
_loadedLayers.Add(layer);
}
else
{
count--;
if(count == 0) this.initializeMap();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
Update II
I now believe something has changed with the resource loading.
This is what I have (unimportant parts are left out).
public class ECBSchemeHandler : IResourceHandler
{
private string _mimeType;
private Stream _stream;
public bool Open(IRequest request, out bool handleRequest, ICallback callback)
{
var result = open(request, callback);
handleRequest = result;
return result;
}
public bool Read(Stream dataOut, out int bytesRead, IResourceReadCallback callback)
{
return read(dataOut, out bytesRead, callback);
}
public bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
{
return read(dataOut, out bytesRead, callback);
}
private bool open(IRequest request, ICallback callback)
{
var u = new Uri(request.Url);
var file = u.Authority + u.AbsolutePath;
var ass = Assembly.GetExecutingAssembly();
var resourcePath = ECBConfiguration.DEFAULT_ASSEMBLY_NAMESPACE + "." + file.Replace("/", ".");
if (ass.GetManifestResourceInfo(resourcePath) != null)
{
Task.Run(() =>
{
using (callback)
{
_stream = ass.GetManifestResourceStream(resourcePath);
var fileExtension = Path.GetExtension(file);
_mimeType = ResourceHandler.GetMimeType(fileExtension);
callback.Continue();
}
});
return true;
}
else
{
callback.Dispose();
}
return false;
}
private bool read(Stream dataOut, out int bytesRead, IDisposable callback)
{
callback.Dispose();
if (_stream == null)
{
bytesRead = 0;
return false;
}
//Data out represents an underlying buffer (typically 32kb in size).
var buffer = new byte[dataOut.Length];
bytesRead = _stream.Read(buffer, 0, buffer.Length);
dataOut.Write(buffer, 0, buffer.Length);
return bytesRead > 0;
}
}
}
Using the built-in ResourceHandlers as pointed out by #amaitland solved the problem with the Javascript registration.

Chrome addon is randomly sending trash to my c++ native host application

I'm trying to write a simple parental control app for my university project, but I'm a newbie in browser addons. I want to use Chrome addon to send hosts viewed by the user in real-time to Qt app, which will analyze the user behavior. The problem is, that sometimes chrome's sending a correct host, another time it's sending trash with an empty string or enormously long message, which my Qt app filter. But these 'wrong' messages are sent in an endless loop, and to make it working again, I have to restart extension or chrome or even whole PC.
Chrome addon manifest:
{
"name": "AM Chrome addon",
"version": "0.7",
"description": "Get your activity supervised!",
"background": {
"scripts": [
"background.js"
],
"persistent": false
},
"permissions": [
"tabs",
"nativeMessaging",
"background"
],
"manifest_version": 2
}
Addon background.js file:
var current = undefined;
var port = null;
tryConnectagain();
function tryConnectagain() {
port = chrome.runtime.connectNative('<Native host app-name>');
port.onDisconnect.addListener(onDisconnect);
}
function onDisconnect() {
port = null;
console.log("last error: ", chrome.runtime.lastError.message);
setTimeout(tryConnectagain, 1000);
}
function sendMessageToNativeApp(message) {
if (port != null) port.postMessage({ message: message });
}
function newUrl(u) {
if (u != undefined && !u.includes(current) && !u.includes("chrome-extension://") && u.includes('.')) {
var u = new URL(u);
var domain = u.hostname.replace("www.", "");
if (domain != current) {
current = domain;
sendMessageToNativeApp(current);
console.log(current);
}
}
else if (current != "NotURL") {
current = "NotURL";
sendMessageToNativeApp(current);
console.log(current);
}
}
// Here I'm trying to intercept all URL change situations
chrome.tabs.onActivated.addListener(function (activeInfo) {
chrome.tabs.get(activeInfo.tabId, function (tab) {
if (tab.active && tab.highlighted) newUrl(tab.url);
});
});
chrome.tabs.onAttached.addListener(function (tabId, attachInfo) {
chrome.tabs.get(tabId, function (tab) {
if (tab.active && tab.highlighted) newUrl(tab.url);
});
});
chrome.tabs.onReplaced.addListener(function (addedTabId, removedTabId) {
chrome.tabs.get(addedTabId, function (tab) {
if (tab.active && tab.highlighted) newUrl(tab.url);
});
});
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.url && tab.active && tab.highlighted) newUrl(changeInfo.url);
});
chrome.windows.onFocusChanged.addListener(function (windowId) {
if (windowId > -1) {
var getInfo = { populate: true, windowTypes: ['normal'] };
chrome.windows.getLastFocused(getInfo, function (window) {
for (var t = 0; t < window.tabs.length; t++) {
if (window.tabs[t].active && window.tabs[t].highlighted) {
newUrl(window.tabs[t].url);
break;
}
}
})
}
});
Native host app manifest:
{
"name": "<Native host app-name>",
"description": "Hostname Identifier",
"path": "<Hostname app Path>",
"type": "stdio",
"allowed_origins": [
"chrome-extension://<extension-ID>/"
]
}
And a fragment of c++ qt code that receive data from addon:
addonmessagereceiver.h:
#ifndef ADDONMESSAGERECEIVER_H
#define ADDONMESSAGERECEIVER_H
#include <qthread.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <iostream>
#include <string>
class AddonMessageReceiver : public QThread
{
Q_OBJECT
public:
void run();
signals:
void UpdateMessage(const QString &);
};
#endif // ADDONMESSAGERECEIVER_H
addonmessagereceiver.cpp:
#include "addonmessagereceiver.h"
#include <qdebug.h>
using namespace std;
void AddonMessageReceiver::run()
{
do{
char nextMessageLen[4];
cin.read(nextMessageLen, 4);
unsigned long int messageLength = *reinterpret_cast<unsigned long int *>(nextMessageLen);
qDebug() << messageLength << static_cast<int>(nextMessageLen[0]) << static_cast<int>(nextMessageLen[1]) << static_cast<int>(nextMessageLen[2]) << static_cast<int>(nextMessageLen[3]);
if(messageLength<1024 && messageLength>1)
{
char *incomingMessage = new char[messageLength+1];
memset(incomingMessage,'\0',messageLength+1);
cin.read(incomingMessage, messageLength);
QString message = QString::fromLatin1(incomingMessage);
delete[] incomingMessage;
qDebug() << messageLength << message;
if(message.length()>5)
{
QJsonDocument json = QJsonDocument::fromJson(message.toLatin1());
QJsonObject obj = json.object();
QString host = obj.value("message").toString();
emit UpdateMessage(host);
}
}
QThread::msleep(100);
}while(true);
}
Example of qDebug wrong nextMessageLen in loop:
And an example of good input that turns into wrong in a loop:
Can you please tell me what is going on with that extension or chrome, or what I f... up with native app? Thank you for your answer.
Based on this answer I have built a class that monitors "stdin", get the text and decode it using QFile:
nativemessenger.h
#ifndef NATIVEMESSENGER_H
#define NATIVEMESSENGER_H
#include <QObject>
#include <QFile>
class NativeMessenger : public QObject
{
Q_OBJECT
public:
explicit NativeMessenger(QObject *parent = nullptr);
public Q_SLOTS:
void sendMessage(const QByteArray & message);
Q_SIGNALS:
void messageChanged(const QByteArray & message);
private Q_SLOTS:
void readyRead();
private:
QFile m_qin;
QFile m_qout;
};
#endif // NATIVEMESSENGER_H
nativemessenger.cpp
#include "nativemessenger.h"
#include <QCoreApplication>
#ifdef Q_OS_WIN
#include <QWinEventNotifier>
#include <windows.h>
#else
#include <QSocketNotifier>
#endif
NativeMessenger::NativeMessenger(QObject *parent) : QObject(parent)
{
#ifdef Q_OS_WIN
// https://developer.chrome.com/apps/nativeMessaging#native-messaging-debugging
_setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY);
#endif
m_qin.open(stdin, QIODevice::ReadOnly | QIODevice::Unbuffered);
m_qout.open(stdout, QIODevice::WriteOnly);
#ifdef Q_OS_WIN
QWinEventNotifier *m_notifier = new QWinEventNotifier(GetStdHandle(STD_INPUT_HANDLE));
connect(m_notifier, &QWinEventNotifier::activated, this, &NativeMessenger::readyRead);
#else
QSocketNotifier *m_notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this, &NativeMessenger::readyRead);
#endif
}
void NativeMessenger::sendMessage(const QByteArray &message){
quint32 len = message.length();
m_qout.write(reinterpret_cast<char *>(&len), sizeof(len));
m_qout.write(message);
m_qout.flush();
}
void NativeMessenger::readyRead(){
m_qin.startTransaction();
quint32 length = 0;
qint64 rc = m_qin.read(reinterpret_cast<char *>(&length), sizeof(quint32));
if (rc == -1) {
m_qin.rollbackTransaction();
return;
}
QByteArray message = m_qin.read(length);
if (message.length() != int(length)) {
m_qin.rollbackTransaction();
return;
}
if (message.isEmpty()) {
m_qin.rollbackTransaction();
return;
}
m_qin.commitTransaction();
Q_EMIT messageChanged(message);
}
main.cpp
#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>
#include "nativemessenger.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
NativeMessenger listener;
QObject::connect(&listener, &NativeMessenger::messageChanged,
[&listener]
(const QByteArray & message){
// decode message
QJsonParseError error;
QJsonDocument json = QJsonDocument::fromJson(message, &error);
if(error.error != QJsonParseError::NoError){
qDebug() << error.errorString();
}
// build response
QJsonObject object
{
{"data", json.object()},
{"name", QCoreApplication::applicationName()}
};
QByteArray response = QJsonDocument(object).toJson(QJsonDocument::Compact);
// send response
listener.sendMessage(response);
});
return a.exec();
}
A complete example can be found here.

How to read a Javascript file

I have a file a /path/to/my_js_functions.js that contains a Javascript function my_js_functions(), amongst other functions.
How can I go about reading the function myJsFunction as a String in C++?
I am looking for a way to get the whole function and only that function, and then contain it in a char *jsFunction.
function myJsFunction(stringParam) {
return stringParam // The function returns a stringParam from the parameter
}
function anotherJsFunction(stringParam) {
return stringParam // Another function
}
Thank you all in advance.
Using fstream, I would read line by line and check whether each line contains the sequence myJsFunction. If a line does contain this sequence, then you begin aggregating to a string until you reach the next function (stopping after you reach the next keyword "function" or something of that sort). Note that using } as a keyword may not work as functions are likely to have multiple closing braces.
Another possible solution could include identifying the end of the function by noticing that when a newline is immediately followed by non-whitespace a new function is beginning, assuming the code in your file is formatted where anything lower in scope is tabbed over correctly.
To do this you need to read your javascript code file and parse it. It is highly to use some parser library to do that like cashew,esprima-cpp. I never used that before I never used any of this before, So I can't comment on that.
But here is some quick code for parser. You can start with this build on this to make it more robust.
main.cpp
#include <fstream>
#include <iostream>
#include <streambuf>
#include <string>
#include <vector>
std::string getFunction(const std::string &fileData, const std::string &name) {
std::string ret;
std::size_t start = 0;
while (true) {
const auto fNameStart = fileData.find(name, start);
if (fNameStart != std::string::npos) {
auto fStart = fileData.find_last_not_of(" ",fNameStart-1);
if(fStart == std::string::npos){
ret = "No Function Defination";
break;
}
else {
fStart = fStart-7;
if(fileData.substr(fStart,8) == "function"){
int openBraceCount = 0, closeBraceCount = 0;
std::size_t fEnd = fNameStart + name.size();
fEnd = fileData.find_first_of("{}", fEnd);
while (fEnd != std::string::npos) {
if (fileData.at(fEnd) == '{') {
openBraceCount++;
} else {
closeBraceCount++;
}
if (openBraceCount == closeBraceCount) {
ret = fileData.substr(fStart, fEnd - fStart+1);
break;
}
fEnd++;
fEnd = fileData.find_first_of("{}", fEnd);
}
if(!ret.empty()){
break;
}
else if(openBraceCount != closeBraceCount){
ret = "Function Parse Error";
break;
}
}
else{
start = fNameStart + name.size();
}
}
} else {
ret = "No Function Defination";
break;
}
}
return ret;
}
int main(int argc, char **argv) {
const std::string jsPath = "module.js";
const std::vector<std::string> vecFuncNames{"funcA", "funcB", "funcC",
"funcD", "funcE"};
std::ifstream fs(jsPath);
if (fs.is_open()) {
std::string fileData((std::istreambuf_iterator<char>(fs)),
std::istreambuf_iterator<char>());
for (auto &name : vecFuncNames) {
std::cout << name << "\n" << getFunction(fileData, name) << std::endl;
}
}
return 0;
}
module.js
function funcA ( ){
funcC(); console.log(" Hello");funcB();
}function funcC(){funcB();console.log("Hello");funcA();}
function funcB(a, b, c){
funcA(); setTimeout(function(){ alert("Hello"); }, 3000);funcC();
}
funcD();
function funcE(){{{{}}}
You can simply doing this
For example /path/code.js is the path your code stored
Your code.js
function myJsFunction(stringParam) {
return stringParam // The function returns a stringParam from the parameter
}
function anotherJsFunction(stringParam) {
return stringParam // Another function
}
module.exports = {
myJsFunction,
anotherJsFunction
}
And this is the file that you use to read the function you write
index.js
const code = require('/path/code.js');
console.log(code), it will be showing your whole code in your code.js file
If you want to make it string, you can use syntax like this. Add this in code below on your index.js file and it will make string version of your code.
String(code.myJsFunction)

How to unpack JavaScript codes on Android?

I know a website(http://jsbeautifier.org/) can unpack JavaScript codes. But my question is how to unpack JavaScript codes on Android. Is there an API can do this?
origin codes:
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(11(){Z{4 2="0.1|17.8|16.8|15.8|18.c|19.c",2=2.14("|");4 a=1b.1a;6(a){4 b=a.1c(/([a-q-r-9\\-]+\\.[a-q-r-9\\-]+)[:\\/]/i);6(b!=Y){10(4 i=0;i<2.s;i++){6(b[1]==2[i]){13}12 6(i==2.s-1){1n 1p 1q("1s\'t 1l 1g")}}}}}1e(e){p.u.v="7://1i.1k.c/"+"?1j="+1t(p.u.v)}})();1h[\'1f\']({"1d":{"o":"7:\\/\\/x.w.z.h:f\\/d\\/g\\/n\\/A\\/l.k?j=m&y=&D=X&Q=0&P=3&R=O&U=5&T=V&W=S&M=-F&E=3&N=5&B=&C=&G=&H=&L=K","J":"I"},"1r":{"o":"7:\\/\\/x.w.z.h:f\\/d\\/g\\/n\\/A\\/l.k?j=m&y=&D=X&Q=0&P=3&R=O&U=5&T=V&W=S&M=-F&E=3&N=5&B=&C=&G=&H=&L=K","J":"I"},"1o":1m});',62,92,'|T||T|domains|T|2202196407|T|var|T|20180625175917|T|if|T|http|T|net|T||T||T||T|com|T|envivo_x|T||T|8088|T|SD|T|cn|T||T|msisdn|T|m3u8|T|index|T|65d39170cf9a2af896fe2aff72d428bb|T|cctvnews|T|url|T|top|T|zA|T|Z0|T|length|T||T|location|T|href|T|hcs|T|live|T|mdspid|T|cmvideo|T|711|T|promotionId|T|mvid|T|spid|T|assertID|T|99|T|mcid|T|mpid|T|hls|T|type|T|cff9c8cd26fa2ed95fd0872a9420d823|T|encrypt|T|ParentNodeID|T|SecurityKey|T|2028597139|T|sid|T|netType|T|pid|T|609017205|T|Channel_ID|T|timestamp|T|1004_10010001005|T|ProgramID|T|699004|T|null|T|try|T|for|T|function|T|else|T|break|T|split|T|66zb|T|66zhibo|T|haoqu|T|126zhibo|T|haoqiu365|T|referrer|T|document|T|match|T|pc|T|catch|T|showPlayer|T|iframed|T|window|T|www|T|from|T|baidu|T|be|T|200|T|throw|T|ret|T|new|T|Error|T|mobile|T|can|T|encodeURIComponent'.split('|'+String.fromCharCode(84)+'|'),0,{}))
expected codes:
(function() {
try {
var domains = "0.1|haoqu.net|66zhibo.net|66zb.net|126zhibo.com|haoqiu365.com",
domains = domains.split("|");
var a = document.referrer;
if (a) {
var b = a.match(/([a-zA-Z0-9\-]+\.[a-zA-Z0-9\-]+)[:\/]/i);
if (b != null) {
for (var i = 0; i < domains.length; i++) {
if (b[1] == domains[i]) {
break
} else if (i == domains.length - 1) {
throw new Error("can't be iframed")
}
}
}
}
} catch (e) {
top.location.href = "http://www.baidu.com/" + "?from=" + encodeURIComponent(top.location.href)
}
})();
window['showPlayer']({
"pc": {
"url": "http:\/\/live.hcs.cmvideo.cn:8088\/envivo_x\/SD\/cctvnews\/711\/index.m3u8?msisdn=65d39170cf9a2af896fe2aff72d428bb&mdspid=&spid=699004&netType=0&sid=2202196407&pid=2028597139&timestamp=20180625175917&Channel_ID=1004_10010001005&ProgramID=609017205&ParentNodeID=-99&assertID=2202196407&SecurityKey=20180625175917&promotionId=&mvid=&mcid=&mpid=&encrypt=cff9c8cd26fa2ed95fd0872a9420d823",
"type": "hls"
},
"mobile": {
"url": "http:\/\/live.hcs.cmvideo.cn:8088\/envivo_x\/SD\/cctvnews\/711\/index.m3u8?msisdn=65d39170cf9a2af896fe2aff72d428bb&mdspid=&spid=699004&netType=0&sid=2202196407&pid=2028597139&timestamp=20180625175917&Channel_ID=1004_10010001005&ProgramID=609017205&ParentNodeID=-99&assertID=2202196407&SecurityKey=20180625175917&promotionId=&mvid=&mcid=&mpid=&encrypt=cff9c8cd26fa2ed95fd0872a9420d823",
"type": "hls"
},
"ret": 200
});
Yeah, I have found the best answer. Use ScriptEngineer! However, javax.script.* is not available in Android, what a pity.
But I searched for this question to get the final answer.
Add implementation 'io.apisense:rhino-android:1.0' to dependencies, and the complete codes are below:
private String unpackJs(String jsPacked) {
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("rhino");
try {
engine.eval(jsPacked.replace("eval", "var _jsUnPacked = "));
} catch (Exception e) {
e.printStackTrace();
}
Object jsUnPacked = engine.get("_jsUnPacked");
return jsUnPacked.toString();
}
UPDATED:
The second answer, use JavaScript engine from Java codes:
Download rhino release jar from GitHub, put it to libs directory, and add implementation fileTree(dir: 'libs', include: ['*.jar']) to dependencies, and the complete codes are below:
private String unpackJs(String jsPacked) {
org.mozilla.javascript.Context ct = org.mozilla.javascript.Context.enter();
ct.setOptimizationLevel(-1); // https://stackoverflow.com/a/3859485/6482350
Scriptable scope = ct.initStandardObjects();
ct.evaluateString(scope, jsPacked.replace("eval", "var _jsUnPacked = "), null, 1, null);
Object jsUnpacked = scope.get("_jsUnPacked", scope);
return jsUnpacked.toString();
}

Java8 JS Nashorn convert array to Java array

How can I convert JS array to native array ?
In Rhino conversion looked like (Scala code):
val eng = (new javax.script.ScriptEngineManager).getEngineByName("JavaScript")
val obj = eng.eval("[1,2,3,4]")
val arr = obj.asInstanceOf[sun.org.mozilla.javascript.internal.NativeArray]
In Nashorn NativeArray absent, and I can't find any documentation on conversion.
From Java (and Scala), you can also invoke convert method on jdk.nashorn.api.scripting.ScriptUtils class. E.g. from Java:
import jdk.nashorn.api.scripting.ScriptUtils;
...
int[] iarr = (int[])ScriptUtils.convert(arr, int[].class)
my Scala is not too fluent, but I believe the equivalent is:
val iarr = ScriptUtils.convert(arr, Array[Int]).asInstanceOf(Array[Int])
Given a JavaScript array, you can convert it to a Java array using the Java.to() method in oracle nashorn engine which is available in jdk 8
Example
var data = [1,2,3,4,5,6];
var JavaArray = Java.to(data,"int[]");
print(JavaArray[0]+JavaArray[1]+JavaArray[2]);
I found a solution that works for Rhino and Nashorn.
First problem, the script writer must not deal with Java Objects!
That leads to a solution, that only uses Java.
Second it must work with Java 8 and previous versions!
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("JavaScript");
try {
Object result = convert(engine.eval("(function() {return ['a', 'b'];})()"));
log.debug("Result: {}", result);
result = convert(engine.eval("(function() {return [3, 7.75];})()"));
log.debug("Result: {}", result);
result = convert(engine.eval("(function() {return 'Test';})()"));
log.debug("Result: {}", result);
result = convert(engine.eval("(function() {return 7.75;})()"));
log.debug("Result: {}", result);
result = convert(engine.eval("(function() {return false;})()"));
log.debug("Result: {}", result);
} catch (final ScriptException e) {
e.printStackTrace();
}
private static Object convert(final Object obj) {
log.debug("JAVASCRIPT OBJECT: {}", obj.getClass());
if (obj instanceof Bindings) {
try {
final Class<?> cls = Class.forName("jdk.nashorn.api.scripting.ScriptObjectMirror");
log.debug("Nashorn detected");
if (cls.isAssignableFrom(obj.getClass())) {
final Method isArray = cls.getMethod("isArray");
final Object result = isArray.invoke(obj);
if (result != null && result.equals(true)) {
final Method values = cls.getMethod("values");
final Object vals = values.invoke(obj);
if (vals instanceof Collection<?>) {
final Collection<?> coll = (Collection<?>) vals;
return coll.toArray(new Object[0]);
}
}
}
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {}
}
if (obj instanceof List<?>) {
final List<?> list = (List<?>) obj;
return list.toArray(new Object[0]);
}
return obj;
}
Rhino delivers arrays as class sun.org.mozilla.javascript.internal.NativeArray that implement the java.util.List interface, that are easy to handle
13:48:42.400 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class sun.org.mozilla.javascript.internal.NativeArray
13:48:42.405 [main] DEBUG de.test.Tester - Result: [a, b]
13:48:42.407 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class sun.org.mozilla.javascript.internal.NativeArray
13:48:42.407 [main] DEBUG de.test.Tester - Result: [3.0, 7.75]
13:48:42.410 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class java.lang.String
13:48:42.410 [main] DEBUG de.test.Tester - Result: Test
13:48:42.412 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class java.lang.Double
13:48:42.412 [main] DEBUG de.test.Tester - Result: 7.75
13:48:42.414 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class java.lang.Boolean
13:48:42.415 [main] DEBUG de.test.Tester - Result: false
Nashorn returns a JavaScript array as jdk.nashorn.api.scripting.ScriptObjectMirror that unfortunately doesn't implement the List interface.
I solved it using reflection and I'm wondering why Oracle has made this big change.
13:51:02.488 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class jdk.nashorn.api.scripting.ScriptObjectMirror
13:51:02.495 [main] DEBUG de.test.Tester - Nashorn detected
13:51:02.497 [main] DEBUG de.test.Tester - Result: [a, b]
13:51:02.503 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class jdk.nashorn.api.scripting.ScriptObjectMirror
13:51:02.503 [main] DEBUG de.test.Tester - Nashorn detected
13:51:02.503 [main] DEBUG de.test.Tester - Result: [3.0, 7.75]
13:51:02.509 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class java.lang.String
13:51:02.509 [main] DEBUG de.test.Tester - Result: Test
13:51:02.513 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class java.lang.Double
13:51:02.513 [main] DEBUG de.test.Tester - Result: 7.75
13:51:02.520 [main] DEBUG de.test.Tester - JAVASCRIPT OBJECT: class java.lang.Boolean
13:51:02.520 [main] DEBUG de.test.Tester - Result: false
The solution is Java.to function to do conversion:
engine.eval("Java.to(" + script + ",'byte[]')").asInstanceOf[Array[Byte]]
engine.eval("Java.to(" + name + ",'java.lang.String[]')").asInstanceOf[Array[String]]
In case there is array of arrays, we have to process recursivelly:
public class Nashorn {
public static List<String> fromScript(final Object obj){
List<String> returnList = new ArrayList<>();
if(obj==null){
returnList.add("");
return returnList;
}
if (obj instanceof Bindings) {
flatArray(returnList, obj);
return returnList;
}
if (obj instanceof List<?>) {
final List<?> list = (List<?>) obj;
returnList.addAll(list.stream().map(String::valueOf).collect(Collectors.toList()));
return returnList;
}
if(obj.getClass().isArray()){
Object[] array = (Object[])obj;
for (Object anArray : array) {
returnList.add(String.valueOf(anArray));
}
return returnList;
}
returnList.add(String.valueOf(obj));
return returnList;
}
//if we have multiple levels of array, flat the structure
private static void flatArray(List<String> returnList, Object partialArray){
try {
final Class<?> cls = Class.forName("jdk.nashorn.api.scripting.ScriptObjectMirror");
if (cls.isAssignableFrom(partialArray.getClass())) {
final Method isArray = cls.getMethod("isArray");
final Object result = isArray.invoke(partialArray);
if (result != null && result.equals(true)) {
final Method values = cls.getMethod("values");
final Object vals = values.invoke(partialArray);
if (vals instanceof Collection<?>) {
final Collection<?> coll = (Collection<?>) vals;
for(Object el : coll) {
if (cls.isAssignableFrom(el.getClass())) {
flatArray(returnList, el);
}
else{
returnList.add(String.valueOf(el));
}
}
}
}
}
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException | InvocationTargetException ignored) {}
}
}
For multidimensional arrays:
You can use this code below to get an array with a dynamic dimension, depending on the object you want to convert. There is a usage example below.
public static Object[] toArray(ScriptObjectMirror scriptObjectMirror)
{
if (!scriptObjectMirror.isArray())
{
throw new IllegalArgumentException("ScriptObjectMirror is no array");
}
if (scriptObjectMirror.isEmpty())
{
return new Object[0];
}
Object[] array = new Object[scriptObjectMirror.size()];
int i = 0;
for (Map.Entry<String, Object> entry : scriptObjectMirror.entrySet())
{
Object result = entry.getValue();
System.err.println(result.getClass());
if (result instanceof ScriptObjectMirror && scriptObjectMirror.isArray())
{
array[i] = toArray((ScriptObjectMirror) result);
}
else
{
array[i] = result;
}
i++;
}
return array;
}
Now, use the method like this:
ScriptObjectMirror som = (ScriptObjectMirror) YOUR_NASHORN_ENGINE.eval("['this', ['tricky', ['method', ['works']], 'perfectly'], ':)']");
// create multi-dimensional array
Object[] obj = toArray(som);
And voila, you got it. Below is an example on how to iterate over such arrays:
public static void print(Object o)
{
if (o instanceof Object[])
{
for (Object ob : (Object[]) o)
{
print(ob);
}
}
else
{
System.out.println(o);
}
}
I success to retrieve java/scala array by using method like traditional netscape.javascript.JSObject in Java8/Scala2.12/Nashorn.
val arr = engine.eval(script) match {
case obj:ScriptObjectMirror =>
if(obj.isArray){
for(i <- 0 until obj.size()) yield obj.getSlot(i)
} else Seq.empty
case unexpected => Seq.empty
}
Using ScriptUtil() may retrieve scalar value such as String, but it seems to cause ClassCastException for Array.

Categories

Resources