Executing a function in a specific context in Nashorn - javascript

I have a custom Nashorn runtime that I set up with some global functions and objects - some of these are stateless and some of these are stateful. Against this runtime, I am running some custom scripts.
For each execution, I am planning on creating a new context that is backed by the global context:
myContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
engine.eval(myScript, myContext);
Based on what I read, any modifications to the global scope (from the perspective of the script) will be limited to the new context I created.
These scripts, when evaluated, expose some objects (with well-defined names and method-names). I can invoke a method on the object by casting engine to Invocable. But how do I know the context in which the function will run? Is that even an issue, or is the execution context of that function set up based on the context in which it was evaluated?
What behavior can I expect in a multithreaded situation where all threads share the same script-engine instance, and they all try to run the same script (which exposes a global object). When I then invoke the method on the object, in which context will the function run? How will it know which instance of the object to to use?
I was expecting to see an invoke method where I can specify the context, but this doesn't seem to be the case. Is there a way to do this, or am I going about this completely wrong?
I know that an easy way to get around this is to create a new script-engine instance per execution, but as I understand, I would lose optimizations (especially on the shared code). That being said, would pre-compiling help here?

I figured this out. The problem I was running into was that invokeFunction would throw a NoSuchMethodException because the functions exposed by the custom script didn't exist in the bindings from the engine's default scope:
ScriptContext context = new SimpleScriptContext();
context.setBindings(nashorn.createBindings(), ScriptContext.ENGINE_SCOPE);
engine.eval(customScriptSource, context);
((Invocable) engine).invokeFunction(name, args); //<- NoSuchMethodException thrown
So what I had to do was pull out the function from the context by name and call it explicitly like so:
JSObject function = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
function.call(null, args); //call to JSObject#isFunction omitted brevity
This will call the function that exists in your newly-created context. You can also invoke methods on objects this way:
JSObject object = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
JSObject method = (JSObject) object.getMember(name);
method.call(object, args);
call throws an unchecked exception (either Throwable wrapped in a RuntimeException or NashornException that has been initialized with JavaScript stackframe information) so you may have to explicitly handle that if you want to provide useful feedback.
This way threads can't step over each other because there is a separate context per thread. I was also able to share custom runtime-code between the threads and ensure that state changes to mutable-objects exposed by the custom-runtime were isolated by context.
To do this, I create a CompiledScript instance that contains a compiled representation of my custom runtime-library:
public class Runtime {
private ScriptEngine engine;
private CompiledScript compiledRuntime;
public Runtime() {
engine = new NashornScriptEngineFactory().getScriptEngine("-strict");
String source = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/runtime.js")
).useDelimiter("\\Z").next();
try {
compiledRuntime = ((Compilable) engine).compile(source);
} catch(ScriptException e) {
...
}
}
...
}
Then when I need to execute a script I evaluate the compiled source, and then evaluate the script against that context as well:
ScriptContext context = new SimpleScriptContext();
context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
//Exception handling omitted for brevity
//Evaluate the compiled runtime in our new context
compiledRuntime.eval(context);
//Evaluate the source in the same context
engine.eval(source, context);
//Call a function
JSObject jsObject = (JSObject) context.getAttribute(function, ScriptContext.ENGINE_SCOPE);
jsObject.call(null, args);
I tested this out with multiple threads and I was able to make sure that state changes were limited to the contexts that belong to individual threads. This is because the compiled representation is executed within a specific context, which means that instances of anything exposed by it are scoped to that context.
One small disadvantage here is that you may be needlessly reevaluating object definitions for objects that don't need to have thread-specific state. To get around this, evaluate them on the engine directly, which will add bindings for those objects to the engine's ENGINE_SCOPE:
public Runtime() {
...
String shared = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/shared.js")
).useDelimiter("\\Z").next();
try {
...
nashorn.eval(shared);
...
} catch(ScriptException e) {
...
}
}
Then later, you can populate the thread-specific context from the engine's ENGINE_SCOPE:
context.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE));
One thing you will need to do is make sure that any such objects that you expose, have been frozen. Otherwise it is possible to redefine or add properties to them.

Related

Firefox add-on: function not available after juggling scopes

As of Firefox 36, Function.__exposedProps__ was made unavailable. Instead if one wanted to expose a chrome JS object to be used in content scripts, they have to use Components.utils.cloneInto with the target scope as browser.contentWindow.wrappedJSObject.
If one does not turn on the cloneFunctions flag, only those attributes are cloned that are not functions. Turning the flag does clone functions too, but not those functions that are defined via the Function.prototype path. For those functions one has to export them via Components.utils.exportTo with the target scope as your exposed object.
Coming to the issue I'm facing. (As I am unable to put it in words, I am adding a MWE).
Chrome end JS:
function Foo(){
this._nFunc = "something";
this._func = function(){/*do something*/};
}
Foo.prototype.Bar = function(){
this._func();
}
Foo.prototype.FooBar = function(){
this._nFunc = "somthing else";
}
var myFoo = new Foo();
var targetScope = browser.contentWindow.wrappedJSObject;
targetScope.myExposedObject = Components.utils.cloneInto(myFoo, targetScope, {cloneFunctions:true});
Components.utils.exportFunction(myFoo.Bar, targetScope.myExposedObject , {defineAs:"Bar"});
Components.utils.exportFunction(myFoo.FooBar, targetScope.myExposedObject , {defineAs:"FooBar"});
Content end JS:
window.myExposedObject.FooBar(); // works
window.myExposedObject._func(); // works
window.myExposedObject.Bar() // error this._func is undefined
Upon logging the this scope received by the function Bar(), we get _func:(void 0), while _nFunc is logged correctly.
Questions:
Is there something I'm missing, or is this a limitation in Firefox? If it is a limitation, please suggest possible ways to workaround the limitation.
Initially I thought that Bar() was somehow unable to access the scope of the calling object, and I tried to supply it the scope as parameters, i.e., Foo.prototype.Bar = function(scope){ scope._func();} and window.myExposedObject.Bar(window.myExposedObject);. Interestingly upon logging, the scope object also turned out to be (void 0). Why is that? I am sure that I am missing something here. What I expected was that the exposed object would map to the original object and upon sending the exposed object as parameters the chrome end JS would be able to get the original object.
While what you're trying to do might be possible with the right combination of cloneInto/exportFunction and waiving of xrays i would suggest you simply load the unprivileged part of your class hierarchy directly into the target context with the subscript loader and only hook the minimal amount of privileged functions into the prototype once it has been created.
This should reduce the attack surface and also avoid headaches with inheritance.
Additionally, these may prove useful:
https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
https://developer.mozilla.org/en-US/docs/Components.utils.makeObjectPropsNormal

Extending nw.gui functionality in Node.js Module

So I'm building a node module for use with node-webkit that creates a new object and exports it. Standard fare. But since Node has no access to the nw-gui module of node-webkit, I'm just passing it in as a parameter to the constructor. Something like this:
function Example(gui) {
this.gui = gui; //Save for later
}
Example.prototype.createExampleMenu = function() {
return new this.gui.Menu();
}
exports.example = Example;
Works great. But I'm trying to modify .prototype methods of node-webkit's inner modules, like Menu and MenuItem. Is the only way to modify those methods (or add new ones) in the constructor itself? If I try to add new prototype methods outside, it (obviously) fails since this.gui hasn't been set. Basically, I'm trying to make it nicer to add new prototype methods to node-webkit modules without doing it in the constructor. Anyone?
I'm in no way an expert but from what I understand of the implementation of node-webkit from reading its source code, I doubt you can modify any of the objects defined in nw.gui.
If you look at the implementation of Node's standard require function in a running node-webkit instance, you'll find:
function (name) {
if (name == 'nw.gui')
return nwDispatcher.requireNwGui();
return global.require(name);
}
which means that requires of nw.gui are very special indeed.
Rather than requiring JavaScript code, this returns an internal binary object that only appears to be a required library.
Looking a little deeper, we find the nwDispatcher.nwGui.Menu is defined as:
function Menu(option) {
if (typeof option != 'object')
option = { type: 'contextmenu' };
if (option.type != 'contextmenu' && option.type != 'menubar')
throw new String('Invalid menu type: ' + option.type);
this.type = option.type;
v8_util.setHiddenValue(this, 'items', []);
nw.allocateObject(this, option);
}
which calls methods of the nw object, which is an object that is not available outside of this function, (i.e. the function acts as a closure over it.)
Further inspection of the various prototype methods of nw.gui.Menu shows that each call refers (internally) to this nw object to handle method dispatch to internally defined functions (written in C++).
So, rather than a group of standard JavaScript prototypical objects, the nw.gui module calls internal binary functions within the node-webkit runtime which are not exposed via its defined API.
UPDATE
From the node-webkit wiki:
Do not change UI types' prototype.

simple Constructor Pattern

I have worked with oop style scripting before and trying to get some kind of system with javascript. I wanted to try the most basic pattern, Constructor Pattern.
So I setup one js file called ImageView with a constructor matching the name of the js file.
function ImageView(){
alert( 'this is working');
}
Then I set up another js file called Main.js which will be the main instantiation class.
$(document).ready(function(){
var imageViewer = new ImageView();
//ImageView();
});
Now what I don't get is I can call this object ImageView without even the new constructor call. For example ImageView(). From what I gather this is just another global function and not a encapsulated class. I'm trying to get away from global crap and separate my methods and properties to their own class. What am I missing her.
Others have already answered what the difference is between using new and not using it, so I'll answer your entirely separate question: how do I avoid globals in JS?
The answer is that you can't entirely. You will always have at least one, in which you can stuff your other stuff. So for example if you wanted a "namespace" of xyz, you would do:
// global:
var xyz = {}; // or, window.xyz = {} if you are in a browser and want to be more explicit.
// "encapsulated" within the xyz "namespace":
xyz.ImageView = function () { alert("This is working"); };
There is a better solution: use the emerging concept of JavaScript modules. These are not language features (at least not in the current version of JavaScript), so they are really just hacks introduced by very clever libraries that overwrite a couple of global variables to let you avoid creating any more than the ones provided by those libraries. A good example is RequireJS, where you could do something like the following:
// In xyz.js, define the xyz module (name automatically derived from filename).
// Whatever is returned from the function you pass to define is "the xyz module"
define(function () {
return {
ImageView: function () { alert("This is working"); }
};
});
// In other code, in a different file, you can say "I require the xyz module
// to do my work," and pass require a function saying "once you've got the xyz module
// for me, here's the work I will do with it".
require(["xyz"], function (xyz) { // dependency array maps to callback arguments
// I got the xyz module, including the ImageView function it exported. Use it!
var imageViewer = new xyz.ImageView();
});
Here the clever globals RequireJS introduces are the functions define and require, but if you use them right, you can avoid ever introducing any further globals beside those two.
Inside of ImageView, the value of this will be different if you call it with new. Without, it's just another function. With new it will create a new ImageView instance and bind it to the variable this.
First off JavaScript doesn't have built in namespaces. It can only be simulated. You must also include each javascript file you plan on using.
Your right about just calling ImageView() that basically invokes the constructor on this which is next level of scope.
Using new ImageView() creates a new Object of constructor ImageView and this points to the new instance.
JavaScript is a prototype language with loose typing.

JavaScript scopes and object-orientation

What's wrong with this—how come the variable foo isn't defined from within onModified() of a Document object?
function Document() {
var foo = "dfsadf";
this.onModified = function() {
alert(foo);
};
}
// Does not alert; "foo" doesn't resolve
new Document().onModified();
I'd like to have public methods on Document that reference variables that are somehow private to Document.
Your Document function is clashing with the Document constructor from the DOM.
document instanceof Document; // true
As with any host-object its behavior completely depends on the host environment, and they often can give you unexpected results.
As far I've tested, on Firefox you are not able to replace its value, therefore I would recommend you to either, rename your function, or, declare it on other scope.

Properties attaching to wrong object

I've adapted the Crockford object() function so that I can pass in some parameters and autorun an init function in the new object:
function object(o) {
function F() {}
F.prototype = o;
var params = Array.prototype.slice.call(arguments,1);
var obj = new F();
if(params.length) {
obj.init.apply(obj,params);
}
return obj;
}
This works fine most of the time, but within one object I have functions defined as follows:
MY.Object = function() {
function init(element, generator) {
build(element);
// more code after
}
function build(element) {
this._property = "example";
}
return {
init: init;
}
}();
If I then run
My.Object2 = object(MY.Object, "test param");
For some reason _property gets added to the Windows object. This stops if I make build a public method and call it using this.build().
Can anyone explain why this happens?
build, although you've defined it within your class, has no context when you're calling it. So, no context means that this references your window object (within a browser, at least). But, even though you don't have the proper this context, you can still access your variables you've declared within the "private" scope of your class.
Try using build.call(this, element) (function.call is similar to function.apply).
Just know that JavaScript doesn't quite behave the same way as other OO languages you may have used, and that classes, and the notion of private and public (among other language features) are a bit of a hack.

Categories