⦿
CodeMash 2015
“In four years ... Node.js has experienced phenomenal growth. Node.js is the language of choice for high performance, low latency applications and has been powering everything from robots to API engines to cloud stacks to mobile web sites.”Node.js Advisory Board - October 23, 2014
document.addEventListener("DOMContentLoaded",
function(event) { //do work
});
if (!isFinite( someVar )) {
// to infinity and beyond!
}
setTimeout(function() {
// pomodoro complete!
}, 1,500,000);
var enc = "foo.asp?name=st%C3%A5le&car=saab";
var decoded = decodeURI(enc);
console.log("To be or not to be");
document.addEventListener("DOMContentLoaded",
function(event) { //do work
});
if (!isFinite( someVar )) {
// to infinity and beyond!
}
setTimeout(function() {
// pomodoro complete!
}, 1,500,000);
var enc = "foo.asp?name=st%C3%A5le&car=saab";
var decoded = decodeURI(enc);
console.log("To be or not to be");
document.addEventListener("DOMContentLoaded",
function(event) { //do work
});
if (!isFinite( someVar )) {
// to infinity and beyond!
}
setTimeout(function() {
// pomodoro complete!
}, 1,500,000);
var enc = "foo.asp?name=st%C3%A5le&car=saab";
var decoded = decodeURI(enc);
console.log("To be or not to be");
Asynchronous I/O & Event Loop
User Code
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
src/node.js(function(process) {
this.global = this;
function startup() {
var EventEmitter = NativeModule.require('events').EventEmitter;
process.__proto__ = Object.create(EventEmitter.prototype, {
constructor: {
value: process.constructor
}
});
EventEmitter.call(process);
process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated
// etc...
});
Node.js main function takes a process object
src/node.ccvoid SetupProcessObject(Environment* env,
int argc,
const char* const* argv,
int exec_argc,
const char* const* exec_argv) {
Local<Object> process = env->process_object();
// process.version
READONLY_PROPERTY(process,
"version",
FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION));
// process.binding
NODE_SET_METHOD(process, "binding", Binding);
}
The process object is created in C++ using the V8 API
Some native properties are bound to the object here
src/node.ccstatic void Binding(const FunctionCallbackInfo<Value>& args) {
HandleScope handle_scope(args.GetIsolate());
Environment* env = Environment::GetCurrent(args.GetIsolate());
Local<String> module = args[0]->ToString();
node::Utf8Value module_v(module);
Local<Object> cache = env->binding_cache_object();
Local<Object> exports;
if (cache->Has(module)) {
exports = cache->Get(module)->ToObject();
args.GetReturnValue().Set(exports);
return;
}
// etc.
}
Similar to a Javascript module lookup, but with native exports
src/node.ccvoid Load(Environment* env) {
// Compile, execute the src/node.js file. The node.js file returns a function 'f'
Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(), "node.js");
Local<Value> f_value = ExecuteString(env, MainSource(env), script_name);
Local<Function> f = Local<Function>::Cast(f_value);
// Now we call 'f' with the 'process' variable that we've built up with
// all our bindings. Inside node.js we'll take care of assigning things to
// their places.
Local<Object> global = env->context()->Global();
Local<Value> arg = env->process_object();
f->Call(global, 1, &arg);
}
Node.js V8 layer loads lib/node.js providing a process object
process.binding()lib/fs.jsvar binding = process.binding('fs');
fs.exists = function(path, callback) {
if (!nullCheck(path, cb)) return;
binding.stat(pathModule._makeLong(path), cb);
function cb(err, stats) {
if (callback) callback(err ? false : true);
}
};
The binding object does the heavy lifting
src/node_file.cpp
NODE_SET_METHOD(target, "stat", Stat);
static void Stat(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate());
HandleScope scope(env->isolate());
if (args.Length() < 1)
return TYPE_ERROR("path required");
if (!args[0]->IsString())
return TYPE_ERROR("path must be a string");
// etc..
}
Developed by project odd at Red Hat.
Apache 2.0 License
nodyn/bindings/os.js// Called by Node's os module.
// Node has no idea and does not care that a Java method
// is used to obtain and return the results.
exports.getTotalMem = function() {
return java.lang.Runtime.getRuntime().totalMemory();
};
Unlike V8, the JS runtimes on the JVM provide direct access to native objects
Asynchronous I/O & Event Loop
User Code - Unchanged
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
src/node.js - Unchanged(function(process) {
this.global = this;
function startup() {
var EventEmitter = NativeModule.require('events').EventEmitter;
process.__proto__ = Object.create(EventEmitter.prototype, {
constructor: {
value: process.constructor
}
});
EventEmitter.call(process);
process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated
// etc...
});
Node.js main function takes a JS/Java process object
DynJSRuntime.javaprotected static final String NODE_JS = "node.js";
protected static final String PROCESS = "nodyn/process.js";
public NodeProcess initialize() {
NodeProcess javaProcess = new NodeProcess(this);
getEventLoop().setProcess(javaProcess);
DynJS runtime = new DynJS(config);
JSObject global = runtime.getGlobalContext().getObject();
ExecutionContext context = runtime.getDefaultExecutionContext();
JSFunction processFunction = (JSFunction) runScript(PROCESS);
context.call(processFunction, global, javaProcess);
JSFunction nodeFunction = (JSFunction) runScript(NODE_JS);
context.call(nodeFunction, global, javaProcess);
return javaProcess;
}
NodeProcess.javapublic Object binding(String name) {
Object binding = this.bindings.get(name);
if (binding == null) {
binding = loadBinding(name);
this.bindings.put(name, binding);
}
return binding;
}
DynJSRuntime.java@Override
public Object loadBinding(String name) {
try {
this.runner.withSource("__native_require('nodyn/bindings/" + name + "');");
return runner.execute();
} catch(Throwable t) {
this.handleThrowable(t);
}
return null;
}
nodyn/bindings/fs.jsbinding.stat = function(path, callback) {
path = possiblyRelative(path);
function work() {
return buildStat(path, function(stat) { return posix.stat(path, stat); });
}
return executeWork(work.bind(this), callback, true);
};
A lightweight cross-platform POSIX emulation layer for Java, written in Java and is part of the JNR project
process.binding()lib/fs.js - Unchangedvar binding = process.binding('fs');
fs.exists = function(path, callback) {
if (!nullCheck(path, cb)) return;
// Now this node.js code is calling our Java/JS functions
binding.stat(pathModule._makeLong(path), cb);
function cb(err, stats) {
if (callback) callback(err ? false : true);
}
};
Now this node.js code calls Javascript functions backed by Java
Pure Node.js Javascript running on the JVM