I just started using JavaScript V8 and compiled a basic example from the Embedder's Guide. Now I want to bind C++ functions to the JavaScript context.
For now I only have a more or less blank class which should handle binding later on.
class Manager
{
public:
Manager()
{
context = Context::New();
}
void Bind(string Name, function<Handle<Value>(const Arguments&)> Function)
{
// bind the function to the given context
}
private:
Persistent<Context> context;
};
How can I bind a std::function object to the JavaScript V8 context?
What I used in my engine was a rather complicated approach but the very first I came up with. (Sadly I moved from V8 to LLVM such that I haven't been optimizing this.)
void ExposeFunctions(v8::Handle<v8::ObjectTemplate> engine) {
/* ... */
engine->Set(v8::String::New("Exit"), v8::FunctionTemplate::New(NativeExit));
engine->Set(v8::String::New("Log"), v8::FunctionTemplate::New(NativeLog));
/* ... */
}
int SpawnEngine() {
v8::Locker locker;
v8::HandleScope handleScope;
v8::TryCatch exceptionManager;
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
ExposeFunctions(global);
v8::Persistent<v8::Context> context = v8::Context::New(nullptr, global);
v8::Context::Scope scope(context);
/* ... */
context.Dispose();
return 0;
}
This should give you at least a possible solution to bind native functions to the interpreter which you could rework for your needs.
Considering your problem using a function object one could try (just guessing here) to pass it directly like I did with named functions, or nest it in a lambda expression passed to v8::FunctionTemplate::New. But its quite a while since I worked with this.
The parameter type InvocationCallback which is requested by the Set function is a simple typedef.
typedef Handle<Value> (*InvocationCallback)(Arguments const &);
Therefore I had to convert the std::function to a raw function pointer.
void Register(string Name, function<Handle<Value>(Arguments const &)> Function)
{
InvocationCallback* function = Function.target<InvocationCallback>();
globals->Set(String::New(Name.c_str()), FunctionTemplate::New(*function));
}
Related
I'm trying to develop a C++ library that will work with HTML elements. So, I need classes that will store references to Element, which I will get through document.getElementById("id"). Since I can't work with the DOM directly from WASM, I need to get a reference from the imported JS function somehow in C++ when I call that function. But I don't quite understand what it's supposed to look like.
In the documentation I found a class called WebAssembly.Table. As far as I understand, in this table I can just store references that I can access from WASM. But I don't really understand how to access this table from C++. I haven't yet compiled C++ into WASM, but for now I'm just learning how I can do it. As far as I understand, I can do this with Clang by setting the target platform as WASM.
So, are there any special types in Clang for transferring references from JS? Builtin functions to access WebAssembly.Table?
The pseudocode of what I mean:
Ref DOMGetElementById();
void DOMChangeText(Ref element);
class CMyElement
{
private:
Ref Element;
public:
CMyElement() :
Element{ DOMGetElementById() } { }
void ChangeText()
{
DOMChangeText(Element);
}
};
int main()
{
CMyElement myElement;
myElement.ChangeText();
return 0;
}
const wasmImports =
{
env:
{
DOMGetElementById: () =>
{
return document.getElementById("myElement");
},
DOMChangeText: (element) =>
{
element.textContent = "Hello world."
}
}
};
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule, wasmImports);
console.log(wasmInstance.exports.main());
I understand that the code above most likely cannot be implemented due to the fact that GC has to count references.
Okay, apparently this is why we have to use a class called WebAssembly.Table. But I can't figure out what I should call in C++ to get a value from this table and pass it to the called function.
I imagine that Clang should provide me with some functions or special types that I should use for such a task. But I could not find it.
So I need an explanation of how I can implement my idea.
I have a third-party library that exposes a public method that points to a private one.
const thirdPartyLibrary = function(){
function privateFunction() {
return superSecretFunction();
};
function superSecretFunction() {
return Math.random();
};
// library public api
return {
publicMethod:() => { return privateFunction() }
}
}();
I've simplified it a bit in the example, but it's something like an implementation of the Module Pattern.
I wonder if it is possible to somehow access these private methods to change their behavior or at least to expose them.
I have tried to access this but as expected the private methods are not listed.
thirdPartyLibrary.foo = function(){console.log(this)};
thirdPartyLibrary.foo() // private members not listed
Obviously, I cannot change the library's code :)
No, it is not possible. Closure-scoped variables really are private.
All you can do is access (and overwrite) the methods of the thirdPartyLibrary object, which is created by the "library public api" object literal. This means you can completely replace the whole library with your own version if you need to, but you cannot access its internals1.
1: Some libraries accidentally leak their internals, e.g. by calling callbacks with an internal as the this context, but that's a bug in the module implementation.
Probably many of you tried to achieve encapsulation in JavaScript. The two methods known to me are:
a bit more common I guess:
var myClass(){
var prv //and all private stuff here
//and we don't use protoype, everything is created inside scope
return {publicFunc:sth};
}
and second one:
var myClass2(){
var prv={private stuff here}
Object.defineProperty(this,'prv',{value:prv})
return {publicFunc:this.someFunc.bind(this)};
}
myClass2.prototype={
get prv(){throw 'class must be created using new keyword'},
someFunc:function(){
console.log(this.prv);
}
}
Object.freeze(myClass)
Object.freeze(myClass.prototype)
So, as second option is WAY more convenient to me (specifically in my case as it visually separates construction from workflow) the question is - are there any serious disadvantages / leaks in this case? I know it allows external code to access arguments of someFunc by
myClass.protoype.someFunc.arguments
but only in case of sloppily executed callbacks (synchronously inside caller chain). Calling them with setTimeout(cb,0) breaks chain and disallows to get arguments as well as just returning value synchronously. At least as far as i know.
Did I miss anything? It's kind of important as code will be used by external, untrusted user provided code.
I like to wrap my prototypes in a module which returns the object, this way you can use the module's scope for any private variables, protecting consumers of your object from accidentally messing with your private properties.
var MyObject = (function (dependency) {
// private (static) variables
var priv1, priv2;
// constructor
var module = function () {
// ...
};
// public interfaces
module.prototype.publicInterface1 = function () {
};
module.prototype.publicInterface2 = function () {
};
// return the object definition
return module;
})(dependency);
Then in some other file you can use it like normal:
obj = new MyObject();
Any more 'protecting' of your object is a little overkill for JavaScript imo. If someone wants to extend your object then they probably know what they're doing and you should let them!
As redbmk points out if you need private instance variables you could use a map with some unique identifier of the object as the key.
So, as second option is WAY more convenient to me (specifically in my case as it visually separates construction from workflow) the question is - are there any serious disadvantages / leaks in this case?
Hm, it doesn't really use the prototype. There's no reason to "encapsulate" anything here, as the prototype methods will only be able to use public properties - just like your untrusted code can access them. A simple
function myClass2(){
var prv = // private stuff here
Object.defineProperty(this, 'prv', {value:prv})
// optionally bind the publicFunc if you need to
}
myClass2.prototype.publicFunc = function(){
console.log(this.prv);
};
should suffice. Or you use the factory pattern, without any prototypes:
function myClass2(){
var prv = // private stuff here
return {
prv: prv,
publicFunc: function(){
console.log(this.prv); // or even just `prv`?
}
};
}
I know it allows external code to access arguments of someFunc by
myClass.protoype.someFunc.arguments
Simply use strict mode, this "feature" is disallowed there.
It's kind of important as code will be used by external, untrusted user provided code.
They will always get your secrets if the code is running in the same environment. Always. You might want to try WebWorkers instead, but notice that they're still CORS-privileged.
To enforcing encapsulation in a language that doesn't properly support private, protected and public class members I say "Meh."
I like the cleanliness of the Foo.prototype = { ... }; syntax. Making methods public also allows you to unit test all the methods in your "class". On top of that, I just simply don't trust JavaScript from a security standpoint. Always have security measures on the server protecting your system.
Go for "ease of programming and testing" and "cleanliness of code." Make it easy to write and maintain, so whichever you feel is easier to write and maintain is the answer.
I am starting with Qt and one of my projects is using QJSEngine to evaluate javascript and I want to provide an entire API to the script, with classes and global functions.
Right now my program provides only the ECMAScript default stuff (eval, encodeURI, parseInt, etc...), but I need to expose some custom classes to the code, like the browsers API (WebSocket class, Image class, document object). For example:
var obj = new CustomClass("", 0);
var ret = obj.customClassMethod("[...]!");
customFunction(ret);
I need to define the behavior of the classes in C++, it wouldn't help evaluate the classes definition and let the user code run.
In contrast to QScriptEngine, where you can add custom classes if they inherit from QObject by using the Q_SCRIPT_DECLARE_QMETAOBJECT macro, the QJSEngine does not directly provide this functionality.
You can still use the Qt Meta-object system to provide interfaces for Javascript, but you have to instantiate the object in C++ and add it to the Javascript context.
Then its slots, methods defined with Q_INVOKABLE, and properties defined with Q_PROPERTY are all accessible from within the Javascript runtime.
Now you can create a factory which creates instances of your custom class CustomClass for a given QJSEngine wrapped as Javascript objects:
class CustomClassFactory : public QObject
{
Q_OBJECT
public:
CustomClassFactory(QJSEngine* engine) : m_engine(engine) {}
Q_INVOKABLE QJSValue createInstance() {
// The engine takes ownership and destroys the object if no longer required.
return m_engine->newQObject(new CustomClass());
}
private:
QJSEngine* m_engine;
}
A factory instance needs to be constructed and added to the global object of the Javascript runtime:
QJSEngine engine;
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);
Now we can construct objects in Javascript with:
var obj = _customClassFactory.createInstance()
As we've come this far, lets additionally inject a constructor for the custom class into the Javascript runtime:
QJSEngine engine;
// Again, the QJSEngine will take ownership of the created object.
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);
engine.evaluate(
"function CustomClass() {"
" return _customClassFactory.createInstance()"
"}");
Et voilà, now you can construct C++ object in Javascript, like you would custom Javascript classes:
var obj = new CustomClass()
For the mentioned WebSocket API you could wrap QtWebSocket for that purpose – that was exactly what I required when I came up with the proposed approach.
Note that for the sake of simplicity I omitted parameters for the constructor, but they can simply be added as well.
PS: I would have added more links to the official documentation, but due to the lack of reputation I'm not allowed to.
In Qt 5.8 a new feature was added to QJSEngine: newQMetaObject
You simple add the static meta object e.g. &MyQObjectDerivedClass::staticMetaObject to the JSEngine using the above function.
You will then be able to new those objects in Javascript from within QML. I have found this a very neat solution.
As the documentation says you must mark you constructor Q_INVOKABLE or you won't be able to instantiate an object of your class.
The property system (setters/getters) works as expected as do slots.
https://doc.qt.io/qt-5/qjsengine.html#newQMetaObject
Here is my test code - first is the C++ part that adds the meta object
QQmlApplicationEngine engine;
QJSValue jsMetaObject = engine.newQMetaObject(&MyClassOfObject::staticMetaObject);
engine.globalObject().setProperty("MyClassOfObject", jsMetaObject);
I can now write JS that news an object of that type and use setters/getters etc. This code actually exists in a MouseArea onClicked handler for manual testing.
var bob = new MyClassOfObject();
print(bob.x);
bob.x = 918264;
print(bob.x);
print(bob.words);
And here is the class definition...
class MyClassOfObject : public QObject
{
Q_OBJECT
Q_PROPERTY(int x READ getX WRITE setX)
Q_PROPERTY(int y READ getY WRITE setX)
Q_PROPERTY(QStringList words READ getWords)
public:
Q_INVOKABLE explicit MyClassOfObject(QObject *parent = nullptr);
public slots:
int getX() const { return x; }
int getY() const { return y; }
void setX(int x) { this->x = x; }
void setY(int y) { this->y = y; }
QStringList getWords() const;
private:
int x = -113;
int y = 616;
QStringList stringList;
};
As of Qt5.5 QScriptEngine has been deprecated, so only QJsEngine should be used in the future.
https://wiki.qt.io/New_Features_in_Qt_5.5#Deprecated_Functionality
If you look up Documentation of QScriptEngine, or by searching "QScriptEngine examples" you can find some stuff about making Custom C++ Classes available to QScriptEngine.
Here is a good place to start:
link to example
QScriptEngine is very similiar to QJsEngine, so it shouldn't be a big problem for you.
Hope this helps :)
Awesomium easily allows for C++ code to call Javascript methods, but I haven't found a definite answer as to if it can do the opposite. This site seems to say that you can, but looking through the text and examples doesn't enlighten me.
So, I'm looking for a definite answer: can I call C++ variables/methods in my Javascript(Jquery), or not?
If you could include a simple example, that would be extremely appreciated as well.
Thank you!
You definitely can-- you'll just need to build an extra layer on top of WebView::setObjectCallback and WebViewListener::onCallback using delegates/function-pointers.
I wrote a quick JSDelegate.h class (view it here) that you can use to hookup "onCallback" events directly to C++ member functions.
The basic idea is to maintain a mapping of callback names to delegates:
typedef std::map<std::wstring, Awesomium::JSDelegate> DelegateMap;
DelegateMap _delegateMap;
And call the corresponding function from your WebViewListener::onCallback:
void MyListener::onCallback(Awesomium::WebView* caller, const std::wstring& objectName,
const std::wstring& callbackName, const Awesomium::JSArguments& args)
{
DelegateMap::iterator i = _delegateMap.find(callbackName);
if(i != _delegateMap.end())
i->second(caller, args);
}
And then, each time you wish to bind a specific C++ function, you would do it like so:
// Member function we wish to bind, must have this signature for JSDelegate
void MyClass::myFunction(Awesomium::WebView* caller, const Awesomium::JSArguments& args)
{
// handle args here
}
// Instantiate MyClass instance in C++
MyClass* myClass = new MyClass();
// Create corresponding 'MyClass' object in Javascript
webView->createObject(L"MyClass");
// Do the following for each member function:
// Bind MyClass::myFunction delegate to MyClass.myFunction in JS
_delegateMap[L"myFunction"] = Awesomium::JSDelegate(myClass, &MyClass::myFunction);
webView->setObjectCallback(L"MyClass", L"myFunction");
Then, you should be able to call MyClass::myFunction directly from Javascript like so:
MyClass.myFunction("foo", 1, 2 3)
Hope this helps! I haven't tested any of the code but I wrote it with Awesomium v1.6 RC4 SDK in mind.