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 :)
Related
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.
I have been using this snippet for creating js listener in my javafx browser:
engine.getLoadWorker().stateProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
System.out.println("changed event detected");
JSObject win = (JSObject) engine.executeScript("window");
win.setMember("java", new JavascriptListener());
}
}
);
But I would like to be able to call my functions without prefixing them with "java".
Is that possible?
Generally speaking, no.
There are no (working) java/javascript functions to do this on a native scale. I tried the javascript Object.assign function, but it didn't work for variables or methods. Other, similar things wouldn't work properly on Java objects either
If you are willing to expose yourself to the horrors of Java reflection, though, be my guest:
public class JavaVariableAssigner {
//javascript-stored objects do not appear to the
private static ArrayList<Object> gcLock = new ArrayList(); //garbadge collector so we need to make sure
//they don't get deleted by always keeping
//a reference to them from this list
public static void addAllMembers(WebEngine browser, Object obj) {
gcLock.add(obj);
((JSObject) browser.executeScript("window")).setMember("java", obj);
for(Method m : obj.getClass().getDeclaredMethods()) {
ArrayList<String> parameterNames = new ArrayList();
for(Parameter p : m.getParameters()) parameterNames.add(p.getName());
String params = String.join(",", parameterNames.toArray(new String[0]));
browser.executeScript("function "+m.getName()+"("+params+"){ return java."+m.getName()+"("+params+"); };");
}
for(Field f : obj.getClass().getDeclaredFields()) {
browser.executeScript("var "+f.getName()+" = java."+f.getName()+";");
}
}
}
All your code using the 'java' prefix will continue to work, but every method can now (hopefully) also just be accessed directly by typing its name.
Like I (sort of) said, though, this method relies entirely on just stitching together javascript and evaling it so I would not recommend it.
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));
}
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.
I've decided to get used to using Javascript as my server sided (I'm using Node.js) language to setup a webserver, create server deamons and a lot more. This is a rather big project, which means that I have to get used to the language and get myself an optimal setup before actually starting to avoid overhead and unneeded hassle.
I have been looking for sources that'd explain the basics of functional programming in big projects. Unfortunately, most sources talk only about basic Javascript meant for simple tricks in a browser.
Two helpful links that explained how object creation works in Javascript were http://howtonode.org/object-graphs and http://howtonode.org/object-graphs-2.
In the end, it seems most wise to create an object like:
function MyObject(constructorMemberOne, constructorMemberTwo) {
this.constructorMemberOne = constructorMemberOne;
this.constructorMemberTwo = constructorMembertwo;
this.doSomething = function doSomething() {
//
}
}
Now, I'm looking for a complete Javascript language reference. So far, https://developer.mozilla.org/en/JavaScript/Reference seems to be most complete.
Q1: is this the recommended ECMAScript language reference? I'm asking mostly because it's sourced by a company that's mostly working in the browser industry, yet Javascript is not just there for browsers -- maybe there are sources that I'm unaware of.
Secondly, I'm used to creating a new file for every class I create where the file name represents the name of the class. Q2: Is this recommended practice in Javascript (V8, Node.js) too? How would one "import" this class?
This "importing" leads me to my confusingness about Node.js's "require". I know it's not the same. Require basically loads another file which then has it's own namespace, meaning that it's variables are out of the scope of the file that's requireing this file. For my classes however, I want to have methods that are available to the class that is "importing" (quotes as I am not sure whether this is even possible) this class. Eg.:
var utils = require("utils/MainUtils.js");
utils.doSomething();
As far as I know, this doSomething() method is only available if it was set like:
function MainUtils() {
exports.doSomething = function doSomething() {
//
}
}
Q3: Is that correct? Doesn't that seem quite abnormal?
Q4: Are there any other blogs or resources that are helpful in getting my setup working, like howtonode.org?
Finally, Q5: have there been any efforts into making all this inheritance, object creation, project structure, namespacing etc. easier for big projects? Any libraries or something for this purpose?
Hopefully my questions are clear. Any help is appreciated. Thanks.
is this the recommended ECMAScript language reference?
Well the official ECMAScript language reference is the ECMA-262 itself. But unfortunately this is completely unreadable even by the standards of standards documents.
ECMA do not produce any materials aimed at end-programmers and there's no one tutorial considered “best”. The MDC link looks decent, at least. (Most JavaScript tutorials are absolutely horrible and full of errors. Which is partly JavaScript's fault for having so many... quirky... features, but still.)
In the end, it seems most wise to create an object like:
There is unfortunately no widely-accepted-‘best’ way to implement a class/instance system in JavaScript. A lot of frameworks have their own systems, or you can brew your own. Your example code creates a new instance of each method for each object instance, which you might consider suboptimal compared to JS's native prototyping. (Normally this approach is used with a var that= this in the closure to avoid this-binding problems in callback code.) You would also need to exercise care in how you create subclasses and initialise them in the constructors.
See this question for a discussion on approaches to class/instance in JS.
I'm used to creating a new file for every class I create
Yeah, that's a Java wart you shouldn't bring to JS.
Keep your source files in manageable chunks of related classes and functions. Sometimes that will be one public class per file, more often it won't be.
How would one "import" this class?
JS itself has no native import functionality. In browsers you do it by inserting <script> tags or evaling code fetched by XMLHttpRequest, and have to take care of keeping variables in separate namespaces manually. In Node.js and other places where you're working with CommonJS, you use modules and require().
Is that correct?
Yes.
Doesn't that seem quite abnormal?
I don't think so. It's similar to other scripting languages, where it proves very useful. It's only really Java that forces you to wrap up a compilation unit into a single class.
I came up with the following after reading a book called Pro Javascript Design Patterns. However, I've been told that it's not good to think like this (private, public, static, etc.) in Javascript:
var SomeClass = (function() {
this.prototype.publicStaticMember = "this is a public static variable",
this.prototype.publicStaticMethod = function() {
return "this is a public method: cannot access private members/methods, but can access privileged and public members/methods";
}
var privateStaticMember = "this is a private static variable";
function privateStaticMethod() {
return "this is a private static method: can only access private members/methods";
}
//instance part
return function() {
this.publicInstanceMember = "this is a public instance variable";
var privateInstanceMember = "this is a private instance variable";
this.publicInstanceMethod = function() {
return "this is a privileged method: can access both public and private members/methods";
}
var privateInstanceMethod = function() {
return "this is a private method: can access both public and private members/methods but is private";
}
}
})();
It'd be instantiated like this:
var someInstance = new SomeClass().("param1", "param2");
Any comments? Should I read an other book?
If you haven't already, checkout the videos by Douglas Crockford. He has a bunch of videos that talk about the prototypal aspects of JavaScript.
Google for 'Douglas Crockford Java Programming Language Video'. Here is a link to the first part in the series: http://video.yahoo.com/watch/111593/1710507
The defineClass npm package provides simple yet powerful OOP for JavaScript with support for traits (mixins) and nested classes. Here is an example of usage:
var defineClass = require("defineClass").defineClass;
var Person = defineClass({
cash: 0,
constructor: function (firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
},
greet: function (name) {
console.log("Hello " + name + ". My name is " + this.firstName);
},
earn: function (amount) {
this.cash += amount;
}
});
var Developer = defineClass({
_super: Person,
// override a field default value
cash: 100,
// override the constructor
constructor: function (firstName, lastName, language) {
// you may have code before the base constructor call
// call the base constructor
this._super(firstName, lastName);
this.language = language;
}
// override a method
greet: function (name) {
console.log("Hey, " + name + ". I'm " + this.firstName)
},
// override a method and call its base method
earn: function (amount) {
return this._super(amount * 1.2);
}
});
Read more in the readme: https://github.com/nodirt/defineClass
Installation:
$ npm install defineClass