Spidermonkey : Send arguments from javascript to C++ - javascript

I am using Spidermonkey for Pacfile Parsing in iOS.I register a function with below code.
if (!JS_DefineFunction(cx, globalHandle, "dnsResolve", (JSNative) dns_resolve, 1, 1)) {
print_error("%s %s\n", error_prefix,
"Could not define dnsResolve in JS context.");
return 0; }
But when I call dnsResolve method with an argument I got garbage values.I use below code to parse arguments
static bool dns_resolve(JSContext *cx, unsigned argc, jsval *argv)
{
JS::CallArgs args = CallArgsFromVp(argc, argv);
JSString *temp = JS::ToString(cx, args[0]);
char* name = JS_EncodeString(cx, temp);
JS_ConvertArgumentsVA(cx, args, name, NULL);
return true;
}
While same code works on other platforms like Android,MAC,windows while gives garbage values in iOS.

Related

JINT performance is significantly worse when an application is run as a 64bit application

The application requires JS support (the program is written in C#) and JINT is being used as the interpreter. I have noticed how performance significantly degrades when the application is run as a 64bit application.
I have simplified this down to the following example which illustrates the issue:
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000; i++)
{
var engine = new Jint.Engine(cfg =>
{
cfg.Strict(true); // Good practive for javascript
cfg.AllowClr(); // Access to .net
cfg.LimitRecursion(16); // Help stop broken scripts taking down application
cfg.CatchClrExceptions(ex => ex is Exception);
});
try
{
engine.Execute(#"
function test()
{
throw 'Error';
};
test();
");
}
catch (Exception) { }
}
sw.Stop();
Debug.WriteLine(sw.Elapsed);
When this is compiled as a 32bit application it takes around 11 seconds. When this is compiled as a 64bit application is takes around 35 seconds. Please that the exceptions are thrown quite frequently in the real application.
Does anyone know why this is the case?
Note this only appears to be an issue when running under the debugger. Outside the debugger the performance seems to be similar.
Update #1
I have still been working on this and now have this example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 20000; i++)
{
DefinedDotNetApi();
}
sw.Stop();
MessageBox.Show(sw.Elapsed.ToString());
}
public static void DefinedDotNetApi()
{
var engine = new Jint.Engine();
engine.SetValue("demoJSApi", new DemoJavascriptApi());
var result = engine.Execute("demoJSApi.helloWorldFromDotNet('TestTest');demoJSApi.helloWorldFromDotNet('TestTest')").GetCompletionValue();
}
public class DemoJavascriptApi
{
public string helloWorldFromDotNet(string name)
{
return $"Hello {name} - this is executed in {typeof(Program).FullName}";
}
}
}
When runs a 32bit app this is 20-30% faster than when built as a 64bit app.
Even doing this:
public static void DefinedDotNetApi()
{
var engine = new Jint.Engine();
engine.SetValue("demoJSApi", new DemoJavascriptApi());
// var result = engine.Execute("demoJSApi.helloWorldFromDotNet('TestTest');demoJSApi.helloWorldFromDotNet('TestTest')").GetCompletionValue();
var result = engine.Execute("function test() { return 'test'; };test();").GetCompletionValue();
}
So without a .NET callback and it is still 10-20% slower in 64bit mode.
Does anyone know why this is?
Update #2
This example shows the difference in speed between a 32bit and 64bit process. I have used benchmarkdotnet.org to show the difference.
public class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<JintTest>();
}
}
[LegacyJitX86Job, LegacyJitX64Job]
[MinColumn, MaxColumn, MeanColumn, MedianColumn]
public class JintTest
{
public JintTest()
{
Test();
}
private const string ContextString = #"/*var test1;var LineupScheduleItem1 = undefined;var LineupScheduleItem2 = undefined;var LineupScheduleItem3 = undefined;var LineupScheduleItem4 = undefined;*/";
[Benchmark]
public void Test()
{
Jint.Engine engine = new Jint.Engine(cfg =>
{
cfg.Strict(true); // Good practive for javascript
cfg.AllowClr(); // Access to .net
cfg.LimitRecursion(16); // Help stop broken scripts taking down application
cfg.CatchClrExceptions(ex => ex is Exception);
});
JavaScriptParser parser = new Jint.Parser.JavaScriptParser(true);
int maxIterations = 500;
for (int i = 0; i < maxIterations; i++)
{
engine.Execute(parser.Parse(ContextString));
}
}
}
The script is all commented out and it is just being executed. The 64bit process is about 30% slower. It does not seem to make a difference if the script is parsed or not.
Does anyone know why this would be the case?

Why does electron gives access violation trying to access a new Isolate?

I wrote a Node.js addon to be used in Electron framework
The main entry of the addon calls another C++ library that makes a long running operation so I put callbacks in to have reports of the operation progress.
So the C++ library calls a callback in my addon but the Isolate is null so I tried to create a new one
The isolate is created well but when I try to use it to have new Local i had this error:
Exception thrown at 0x0000000000000000 in electron.exe: 0xC0000005: Access violation executing location 0x0000000000000000.
Here is an excerpt of my code (the progress_cb variable takes value in the main entry of the addon casting the function passed in Javascript call):
Local<Function> progress_cb;
class ArrayBufferAllocator : public ArrayBuffer::Allocator {
public:
virtual void* Allocate(size_t length) {
void* data = AllocateUninitialized(length);
return data == NULL ? data : memset(data, 0, length);
}
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
virtual void Free(void* data, size_t) { free(data); }
};
void progress_callback(int read, int write, int total)
{
V8::Initialize();
ArrayBufferAllocator allocator;
Isolate::CreateParams params;
params.array_buffer_allocator = &allocator;
Isolate* isolate = Isolate::GetCurrent();
if (!isolate) {
isolate = Isolate::New(params);
isolate->Enter();
}
const unsigned argc = 3;
Local<Integer> r = Int32::New(isolate, (int32_t)read);
Local<Integer> w = Int32::New(isolate, (int32_t)write);
Local<Value> t = Int32::New(isolate, (int32_t)total);
Local<Value> argv[argc] = { r, w, t };
progress_cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
}
Any suggestion?

C++ static archive library with multiple definition conflicts in debug

I'm writing a mobile app with c++ using marmalade middleware software, my plugin I'm making has a component that relies on a compiled python library.
my application works with this 2.6 version:
https://github.com/marmalade/python
switched to 2.7 with socket support:
https://github.com/guyburton/python-loves-marmalade
My first step is I compile my library for the right platform, it is then linked into my application> this creates a file libpython.a ( debug libpython_d.a )
My application has some c-python functions so I also link the required python.h and pyrun.h headers.
none of my headers make reference to python.h or python.c And wmApplication.cpp doesnt make reference to python at all, it does call my plugin though.
1> ARM Compiling(GCC) c:\marmalade\7.3\web\wmapplication\wmApplication.cpp ...
1> ARM Compiling(GCC) c:\TestMove\fluidSec\pyRun.cpp ...
1> ARM Linking(GCC) ...
1> c:/TestMove/python-master/lib/arm\libpython.a(python.obj): In function `main':
1> python.c:(.text.startup.main+0x0): multiple definition of `main'
1> :Release_fluidSec_vc12x_gcc_arm/wmApplication.obj(wmApplication.cpp) : first defined here (col (.text.startup.main+0x0))
I had my plugin working when I was on python version 2.6 then I switched to a different implementation of python 2.7
Isn't
" multiple definition of `main' "only an issue when you have two main functions with the same function signature??? meaning they both have the same program structure? As I understand this can happen regardless of the actual function name being main, the compiler switches it to main as compile time.
I don't have any other instances of python.c ( I searched )
Also: if i compile with the debug version of the library, the error appears to keep the old path in which the library was built ( on a network drive Z:.. even after I disconnect ), if I open the library in a hex editor the path is embedded in the debug info... any change this could be an issue?
1> ARM Compiling(GCC) c:\marmalade\7.3\web\wmapplication\wmApplication.cpp ...
1> ARM Compiling(GCC) c:\TestMove\fluidSec\pyRun.cpp ...
1> ARM Linking(GCC) ...
1> c:/TestMove/python-master/lib/arm\libpython_d.a(python.obj): In function `main':
1> z:/_ProjectFolder/python-master/modified/Modules/python.c:11: multiple definition of `main'
1> :Debug_fluidSec_vc12x_gcc_arm/wmApplication.obj(wmApplication.cpp) : first defined here (col (.text.main+0x0))
Any ideas? I'm clearly missing something!
( definitely cleaned all temp folders and restarted, hoping for a cache issue )
edit:
here is python.c
/* Minimal main program -- everything is loaded from the library */
#include "Python.h"
#ifdef __FreeBSD__
#include <floatingpoint.h>
#endif
int
main(int argc, char **argv)
{
/* 754 requires that FP exceptions run in "no stop" mode by default,
* and until C vendors implement C99's ways to control FP exceptions,
* Python requires non-stop mode. Alas, some platforms enable FP
* exceptions by default. Here we disable them.
*/
#ifdef __FreeBSD__
fp_except_t m;
m = fpgetmask();
fpsetmask(m & ~FP_X_OFL);
#endif
return Py_Main(argc, argv);
}
wmApplication.c
/*
* (C) 2001-2012 Marmalade. All Rights Reserved.
*
* This document is protected by copyright, and contains information
* proprietary to Marmalade.
*
* This file consists of source code released by Marmalade under
* the terms of the accompanying End User License Agreement (EULA).
* Please do not use this program/source code before you have read the
* EULA and have agreed to be bound by its terms.
*/
#include <string>
#include "s3eKeyboard.h"
#include "s3eConfig.h"
#include "s3eWebView.h"
#include "IwDebug.h"
#include "IwGx.h"
#include "../IwWMDispatcher.h"
#include "FallbackPage.h"
#include "../IwWMRegisterModules.h"
#define WEB_DEFAULT_ROOT_DIRECTORY "webassets"
#define WEB_DEFAULT_URL "index.html"
//TOOD Wide char support (relevant for the next line)??
#define ALLOCATION_SIZE_FOR_CONFIG_STRINGS sizeof(char) * S3E_CONFIG_STRING_MAX
#define WEB_DEFAULT_SCHEME LOCAL_ROM_URL
#define URL_HAS_SCHEME(x) strstr(x, "://")
typedef enum CurrentURLType
{
STARTUP_URL_USER_DEFINED,
STARTUP_URL_DEFAULT,
STARTUP_URL_EMBEDDED
} CurrentURLType;
//TODO Implement these globals in a better way (there's no point yet as we may wildly change
//where all out defaults are coming from)
// Tells us which url to try and navigate to (no ownership)
static const char* const * g_WebStartupURL = NULL;
// Scheme to locate local files (might one day not be const)
static const char* const g_LocalFileURLScheme = WEB_DEFAULT_SCHEME;
// Root folder of all web app assets
static char* g_WebAssetsRootDirectory = NULL;
// User defined start URL from icf
static char* g_WebUserDefinedURL = NULL;
// Default start URL if user didn't specify one
static char* g_WebDefaultURL = NULL;
// State representing which stage of fallbacks we're in
static CurrentURLType g_CurrentURLType = STARTUP_URL_USER_DEFINED;
// Name of temp file to create and load as our final fallback url
static const char* g_WebInternalFallbackURL = LOCAL_RAM_URL "error.html";
// Flag to indicate whether we need to cleanup a tempfile
static bool g_DeleteTempURL = false;
static bool g_UsingWinSim = false;
// The main webview
static s3eWebView* g_WebView = NULL;
// The Javascript Dispatcher
static IwWebMarmalade::CDispatcher* g_Dispatcher = NULL;
// Fwd delcarations
bool CreateFallbackPage(const char* url, const char* content);
void DeleteFallbackPage(const char* url);
//Assumes that str is long enough to take the prefix
static void strprepend(const char* prefix, char* str)
{
const int originalLen = strlen(str);
const int prefixLen = strlen(prefix);
//Shift the string along
memmove(str+prefixLen, str, originalLen);
//Can use memcpy for the prefix
memcpy(str, prefix, prefixLen);
//NULL terminate
str[originalLen+prefixLen] = '\0';
}
static bool InitGlobals()
{
if (!g_WebAssetsRootDirectory)
{
if (!(g_WebAssetsRootDirectory = (char*)s3eMalloc(ALLOCATION_SIZE_FOR_CONFIG_STRINGS + 1))) // + 1 for possible extra "/"
return false; //Like it's going to happen at this stage!
if (s3eConfigGetString("Web", "WebRootDirectory", g_WebAssetsRootDirectory) != S3E_RESULT_SUCCESS)
{
strcpy(g_WebAssetsRootDirectory, WEB_DEFAULT_ROOT_DIRECTORY);
}
int length = strlen(g_WebAssetsRootDirectory);
if (g_WebAssetsRootDirectory[length - 1] != '/' || g_WebAssetsRootDirectory[length - 1] != '\\') //TODO Better way?
{
g_WebAssetsRootDirectory[length] = '/';
g_WebAssetsRootDirectory[length + 1] = 0;
}
//Root directory mustn't contain a scheme
if (URL_HAS_SCHEME(g_WebAssetsRootDirectory))
{
IwTrace(WEBMARMALADE, ("WebRootDirectory must not contain a scheme"));
return false;
}
}
if (!g_WebUserDefinedURL)
{
const int totalCapacity = ALLOCATION_SIZE_FOR_CONFIG_STRINGS +
strlen(g_WebAssetsRootDirectory) + strlen(g_LocalFileURLScheme);
if (!(g_WebUserDefinedURL = (char*)s3eMalloc(totalCapacity)))
return false; //Like it's going to happen at this stage!
if (s3eConfigGetString("Web", "WebStartURL", g_WebUserDefinedURL) != S3E_RESULT_SUCCESS)
{
//No user defined URL
g_CurrentURLType = STARTUP_URL_DEFAULT;
g_WebUserDefinedURL[0] = 0;
}
//TODO This means if the user specifies e.g. www.madewithmarmalade.com then this fails.
//We should possibly check for things like "www." as well.
else if (!URL_HAS_SCHEME(g_WebUserDefinedURL))
{
//The user defined url is "relative" then add the scheme and the default file location
strprepend(g_WebAssetsRootDirectory, g_WebUserDefinedURL);
strprepend(g_LocalFileURLScheme, g_WebUserDefinedURL);
}
}
if (!g_WebDefaultURL)
{
const int totalCapacity = strlen(WEB_DEFAULT_URL) +
strlen(g_WebAssetsRootDirectory) + strlen(g_LocalFileURLScheme);
if (!(g_WebDefaultURL = (char*)s3eMalloc(totalCapacity)))
return false; //Like it's going to happen at this stage!
strcpy(g_WebDefaultURL, WEB_DEFAULT_URL);
if (!URL_HAS_SCHEME(g_WebDefaultURL))
{
//The default url is "relative" then add the scheme and the default file location
strprepend(g_WebAssetsRootDirectory, g_WebDefaultURL);
strprepend(g_LocalFileURLScheme, g_WebDefaultURL);
}
}
if (g_CurrentURLType == STARTUP_URL_USER_DEFINED)
g_WebStartupURL = &g_WebUserDefinedURL;
else if (g_CurrentURLType == STARTUP_URL_DEFAULT)
g_WebStartupURL = &g_WebDefaultURL;
g_UsingWinSim = s3eDeviceGetInt(S3E_DEVICE_OS) == S3E_OS_ID_WINDOWS;
IwTrace(WMAPP, ("xxxxxxxxxxxxxxxxxxxxxx: (userdefined) %s", g_WebUserDefinedURL));
return true;
}
static bool CheckQuit()
{
bool rtn = s3eDeviceCheckQuitRequest()
|| (s3eKeyboardGetState(s3eKeyEsc) & S3E_KEY_STATE_PRESSED)
|| (s3eKeyboardGetState(s3eKeyAbsBSK) & S3E_KEY_STATE_PRESSED);
if (rtn)
IwTrace(WEBMARMALADE, ("Quitting Web Marmalade App"));
return rtn;
}
static int32 handleReset(void* systemData, void* userData)
{
s3eWebViewNavigate(g_WebView, *g_WebStartupURL);
return 1;
}
static void Terminate()
{
s3eDeviceUnRegister(S3E_DEVICE_SIMULATOR_RESTART, handleReset);
if (g_Dispatcher)
{
delete g_Dispatcher;
g_Dispatcher = NULL;
}
if (g_WebView)
{
s3eWebViewDestroy(g_WebView);
g_WebView = NULL;
//Free the strings
free(g_WebAssetsRootDirectory);
free(g_WebDefaultURL);
free(g_WebUserDefinedURL);
g_WebAssetsRootDirectory = NULL;
g_WebDefaultURL = NULL;
g_WebUserDefinedURL = NULL;
if (g_DeleteTempURL)
DeleteFallbackPage(g_WebInternalFallbackURL);
}
}
void DoNavigate()
{
IwTrace(WEBMARMALADE, ("loading url = %s", *g_WebStartupURL));
s3eWebViewNavigate(g_WebView, *g_WebStartupURL);
}
static int32 screenResizePending(void* systemData, void* userData)
{
IwTrace(WEBMARMALADE, ("screenResize Pending, calling s3eSurfaceShow()"));
// First call to surface show resizes the surface and calls the S3E_SURFACE_SCREENSIZE
// callback. No surface is actually displayed
s3eSurfaceShow();
// On ios the first call only sets the state to resize pending. The second call actually performs it
s3eSurfaceShow();
return 0;
}
static int32 screenSizeChanged(void* systemData, void* userData)
{
IwTrace(WEBMARMALADE, ("screenSizeChanged callback fired. Resizing webview"));
s3eWebViewResize(g_WebView, 0, 0, s3eSurfaceGetInt(S3E_SURFACE_WIDTH), s3eSurfaceGetInt(S3E_SURFACE_HEIGHT));
// This is annoying. If we don't do this on android then the OS locks the surface and
// we the screen doesn't even rotate natively.
s3eSurfaceShow();
return 0;
}
static int32 pageNotFound(s3eWebView *instance, void *systemData, void *userData)
{
switch (g_CurrentURLType)
{
case STARTUP_URL_USER_DEFINED:
g_CurrentURLType = STARTUP_URL_DEFAULT;
IwTrace(WEBMARMALADE, ("pageNotFound: trying default url = %s", g_WebDefaultURL));
g_WebStartupURL = &g_WebDefaultURL;
break;
case STARTUP_URL_DEFAULT:
g_CurrentURLType = STARTUP_URL_EMBEDDED;
IwTrace(WEBMARMALADE, ("pageNotFound: creating and loading fallback url = %s", g_WebInternalFallbackURL));
if (!(g_DeleteTempURL = CreateFallbackPage(g_WebInternalFallbackURL, g_WebInteralFallbackContent)))
{
IwTrace(WEBMARMALADE, ("failed to create fallback url: quitting"));
s3eDeviceRequestQuit();
return 0;
}
else
{
g_WebStartupURL = &g_WebInternalFallbackURL;
}
break;
default:
IwTrace(WEBMARMALADE, ("pageNotFound: can't find loadable page, quitting"));
s3eDeviceRequestQuit();
return 0;
}
DoNavigate();
return 0;
}
bool Init()
{
// Clear the debug screen from behind the app (you can see it on iOS when you drag
// the browser).
s3eSurfaceClear(0, 0, 0);
s3eSurfaceShow();
if (!s3eWebViewAvailable())
{
IwError(("Webview not available"));
return false;
}
InitGlobals();
if (!(g_WebView = s3eWebViewCreate()))
{
IwTrace(WEBMARMALADE, ("Failed to create webview"));
return false;
}
g_Dispatcher = new IwWebMarmalade::CDispatcher(g_WebView);
if (!g_Dispatcher)
{
IwTrace(WEBMARMALADE, ("Failed to create dispatcher"));
return false;
}
s3eWebViewRegister(S3E_WEBVIEW_FAILED_LOADING, pageNotFound, 0, g_WebView);
s3eSurfaceRegister(S3E_SURFACE_SCREENSIZE, screenSizeChanged, NULL);
s3eDeviceRegister(S3E_DEVICE_SIMULATOR_RESTART, handleReset, NULL);
s3eSurfaceRegister((s3eSurfaceCallback)2, screenResizePending, NULL);
s3eWebViewShow(g_WebView, 0, 0, s3eSurfaceGetInt(S3E_SURFACE_WIDTH), s3eSurfaceGetInt(S3E_SURFACE_HEIGHT));
// For some iOS devices we need to call this on init otherwise the webview is not displayed
s3eSurfaceShow();
DoNavigate();
return true;
}
bool Update()
{
// Work-around since socket events do not unyield the app in windows
if (!g_UsingWinSim)
s3eDeviceYieldUntilEvent();
else
s3eDeviceYield(0);
s3eKeyboardUpdate();
return true;
}
int main()
{
IwTrace(WEBMARMALADE, ("Started Web Marmalade App"));
if (!Init())
return 0;
//Main loop
while (!CheckQuit())
{
if (!Update())
break;
}
Terminate();
return 0;
}
I figured out why it was working before and now it wasn't,
The marmalade MKB file ( which sets up visual studio for cross compiling) had python.c referenced in it to include when it wasn't necessary.
for those wanting to do the same, python in web marmalade, i'll be linking my framework on github as i've made numerous changes to the python implementations, just a few minor issues ( that took me way too long to solve)

How to pass a const void * to node.js?

i'm extending the libspotify wrapper of node-libspotify to support album cover images.
so far, i have the following, working c-binding code:
static Handle<Value> Album_Cover(const Arguments& args) {
HandleScope scope;
// test arguments sanity
assert(args.Length() == 2);
assert(args[0]->IsObject()); // sp_session
assert(args[1]->IsObject()); // sp_album
ObjectHandle<sp_session> *session = ObjectHandle<sp_session>::Unwrap(args[0]);
ObjectHandle<sp_album> *album = ObjectHandle<sp_album>::Unwrap(args[1]);
const byte *image_id = sp_album_cover(album->pointer, SP_IMAGE_SIZE_LARGE);
size_t image_size;
const void *image_data;
if(image_id) {
sp_image *image = sp_image_create(session->pointer, image_id);
image_data = sp_image_data(image, &image_size);
sp_image_release(image);
}
return scope.Close(image_data);
}
i struggle on the last line: how can i pass the raw image data over to node.js when running scope.Close(...)?
thanks for any suggestsions.
You should wrap it in a v8::Value as v8::HandleScope::Close expects a handle to one as an argument.
I guess v8::String should do it - v8::String Class Reference
scope.Close(String::New((const char*)image_data, image_size));
A v8::Array might be useful too - it all depends on how you are going to use the returned value afterwards.
I hope this helps.

Send byte array from javascript to silverlight

Can this be done ?
Below are snippets of what is not working, failing with exception:
function SendBytesJS() {
var control1 = document.getElementById('sl1');
bytes = new Array(1, 2, 3);
control1.Content.MainPage.SendBytesSL(bytes);
}
and
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
System.Windows.Browser.HtmlPage.RegisterScriptableObject("MainPage", this);
}
[System.Windows.Browser.ScriptableMember]
public void SendBytesSL(byte[] bytes)
{
// never gets here
}
}
The HTML bridge does not support byte arrays and Javascript only understands integer and float (actually double).
An array is passed as object[] and numbers are always passed as double. Hence your code needs to look more like the following:-
// Warning untested code
[ScriptableMember]
public void SendBytesSL(object[] arrayIn)
{
byte[] bytes = arrayIn.Select(o => Convert.ToByte(o)).ToArray();
}

Categories