From 7060821e1335eaee896180eb6ff659775132a00f Mon Sep 17 00:00:00 2001 From: jungkumseok Date: Wed, 13 Jun 2018 02:19:02 -0700 Subject: [PATCH] - Users can define custom metadata via comments (must be valid yaml) - Added js-yaml dependency --- lib/core/CodeV3.js | 45 +++++++++++++++++++++++++++++--------- package.json | 1 + samples/motion_detector.js | 2 +- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/lib/core/CodeV3.js b/lib/core/CodeV3.js index 5d0d9e9..e436353 100644 --- a/lib/core/CodeV3.js +++ b/lib/core/CodeV3.js @@ -6,6 +6,7 @@ var EventEmitter = require('events').EventEmitter; var esprima = require('esprima'); var escodegen = require('escodegen'); var pidusage = require('pidusage'); +var yaml = require('js-yaml'); var jsBeautify = require('js-beautify').js_beautify; var chalk = require('chalk'); var Pubsub = require('./Pubsub.js'); @@ -580,19 +581,34 @@ Code.prototype.kill = function(){ }) } +Code._extractMeta = function(comments){ + var meta = {}; + for (var i=0; i < comments.length; i++){ + if (comments[i].type === 'Block' && comments[i].value.indexOf('things.meta') === 0){ + var body = comments[i].value.split('\n').slice(1,-1).join('\n'); + meta = yaml.safeLoad(body); + break; + } + } + return meta; +} + /** * Return instrumented code string * @param {string} raw_code */ Code.instrument = function(pubsub, code_name, raw_code){ // var started = Date.now(); - var ast = esprima.parse(raw_code); + var ast = esprima.parse(raw_code, { comment: true }); // Comment may contain meta info var root_scope = new StaticScope('root'); // Create root scope (static scope) var hook = new AstHook(root_scope, ast); // Process the AST + // Process metadata about the program + var meta = JSON.stringify(Code._extractMeta(ast.comments)); + // Prepare the things-js template - var template_str = `(function(${SCOPE_PREFIX}){})(require('things-js').bootstrap('${pubsub.url}', '${code_name}'));`; + var template_str = `(function(${SCOPE_PREFIX}){})(require('things-js').bootstrap('${pubsub.url}', '${code_name}', ${meta}));`; var template = esprima.parse(template_str); // WARNING: the following depends on esprima output, and might break if esprima changes its format template.body[0].expression.callee.body.body = ast.body; @@ -643,7 +659,9 @@ Code.fromSnapshot = function(snapshot, dummy_pubsub){ content += Timer.generateCode(snapshot.timers[id]); }); - code.source = `(function(${SCOPE_PREFIX}){ ${content} })(require('things-js').bootstrap('${code.pubsub.url}', '${snapshot.meta.code_name}/${snapshot.meta.instance_id}'));`; + var meta = JSON.stringify(snapshot.meta.extra); + + code.source = `(function(${SCOPE_PREFIX}){ ${content} })(require('things-js').bootstrap('${code.pubsub.url}', '${snapshot.meta.code_name}/${snapshot.meta.instance_id}', ${meta}));`; // console.log(jsBeautify(code.source)); // code.source = jsBeautify(code.source); @@ -1209,11 +1227,13 @@ ImmediateTimer.prototype.resume = ImmediateTimer.prototype.start; var MasterIPCHandler = { 'init': function(proc, payload){ proc.id = payload.id; + proc.meta = payload.meta; proc.status = Process.Status.RUNNING; proc.emit('started', proc.id); proc.code.pubsub.publish(PROGRAM_MONITOR_NAMESPACE, { code_name: proc.code.name, instance_id: proc.id, + meta: proc.meta, status: proc.status, source: proc.code.source }); @@ -1257,6 +1277,7 @@ function Process(code, options, history){ var started, ended, elapsed; self.code = code; self.obj = child_process.fork(path.join(__dirname, 'vm.js')); + self.meta = {}; self.status = Process.Status.READY; self.snapshots = []; @@ -1541,8 +1562,8 @@ ProxyPubsub.prototype.publish = function(topic, message){ process.send({ ctrl: 'publish', payload: { topic: topic, message: message } }); } -function RootScope(pubsub_url, code_uri){ - if (!(this instanceof RootScope)) return new RootScope(pubsub_url, code_uri); +function RootScope(pubsub_url, code_uri, user_meta){ + if (!(this instanceof RootScope)) return new RootScope(pubsub_url, code_uri, user_meta); Scope.call(this, null, null, null, null, SCOPE_PREFIX); var self = this; var identity = code_uri.split('/'); @@ -1550,7 +1571,8 @@ function RootScope(pubsub_url, code_uri){ this.meta = { pubsub: (process.connected ? new ProxyPubsub(pubsub_url) : new Pubsub(pubsub_url) ), code_name: identity[0], - instance_id: (identity[1] || helpers.randKey()) + instance_id: (identity[1] || helpers.randKey()), + extra: user_meta }; this.timers = {}; // this.root = undefined; @@ -1559,7 +1581,7 @@ function RootScope(pubsub_url, code_uri){ this.console = new ProxyConsole(this); if (process.connected){ - process.send({ ctrl: 'init', payload: { id: this.meta.instance_id } }); + process.send({ ctrl: 'init', payload: { id: this.meta.instance_id, meta: user_meta } }); process.on('message', function(message){ if (message.ctrl in RuntimeHandler){ var result = RuntimeHandler[message.ctrl](self, message.kwargs); @@ -1593,15 +1615,18 @@ RootScope.prototype.snapshot = function(){ meta : { pubsub: this.meta.pubsub.url, code_name: this.meta.code_name, - instance_id: this.meta.instance_id + instance_id: this.meta.instance_id, + extra: this.meta.extra }, timers: {}, tree: undefined } + // Capture scope tree var scope_tree = this.extractor(); snapshot.tree = scope_tree; + // Capture pending timer events Object.values(this.timers).forEach(function(timer){ snapshot.timers[timer.id] = timer.getSerializable(); }); @@ -1699,8 +1724,8 @@ RootScope.prototype.clearInterval = RootScope.prototype.clearTimer; RootScope.prototype.clearTimeout = RootScope.prototype.clearTimer; RootScope.prototype.clearImmediate = RootScope.prototype.clearTimer; -Code.bootstrap = function(pubsub_url, code_uri){ - return new RootScope(pubsub_url, code_uri); +Code.bootstrap = function(pubsub_url, code_uri, user_meta){ + return new RootScope(pubsub_url, code_uri, user_meta); } module.exports = Code; \ No newline at end of file diff --git a/package.json b/package.json index cc06873..5fcb295 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "esprima": "^4.0.0", "express": "^4.16.2", "js-beautify": "^1.7.4", + "js-yaml": "^3.12.0", "jsonschema": "^1.2.0", "mongodb": "^2.2.33", "mongoose": "^5.0.17", diff --git a/samples/motion_detector.js b/samples/motion_detector.js index 741104a..0c7dbe6 100644 --- a/samples/motion_detector.js +++ b/samples/motion_detector.js @@ -20,7 +20,7 @@ var alarm_channel = 'things-videostream/alarm'; var threshold = 0.10; /* end of configurable variables */ -var pubsub = new things.Pubsub('motion-worker-1', pubsub_url); +var pubsub = new things.Pubsub(pubsub_url); function formatTime(ms){ return ('00'+Math.floor((ms/1000) / 60).toString()).slice(-2)+":"+('00'+(Math.floor(ms/1000) % 60).toString()).slice(-2)+"."+('000'+(ms%1000)).slice(-3)