From 5815dd601a61642f901beeb399cd7006dc92b1f2 Mon Sep 17 00:00:00 2001 From: hochang <“hochang@ebaysf.com”> Date: Wed, 24 Oct 2012 14:31:06 -0700 Subject: [PATCH] visualization --- modules/compiler/lib/compiler.js | 31 +--- modules/compiler/pegs/ql.peg | 5 +- modules/compiler/test/scope-test.js | 2 +- modules/console/public/scripts/console.js | 60 ++------ modules/engine/lib/engine.js | 57 ++++++-- modules/engine/lib/engine/event-types.js | 2 + modules/engine/lib/engine/trycatch.js | 16 +-- modules/engine/lib/engine/visualization.js | 160 +++++++++++++++++++++ modules/engine/test/scope-test.js | 2 +- 9 files changed, 238 insertions(+), 97 deletions(-) create mode 100644 modules/engine/lib/engine/visualization.js diff --git a/modules/compiler/lib/compiler.js b/modules/compiler/lib/compiler.js index be3b3904..8565490f 100644 --- a/modules/compiler/lib/compiler.js +++ b/modules/compiler/lib/compiler.js @@ -75,12 +75,12 @@ function plan(compiled) { creates[line.id.toString()] = line; } else if (line.type === 'if') { - divescope(line.if); - divescope(line.else); + divescope(line.if, line); + divescope(line.else, line); } else if (line.type === 'try') { //dependsOn are the lines in try clause - divescope(line.dependsOn); + divescope(line.dependsOn, line); _.each(line.catchClause, function(k, mycatch){ divescope(mycatch, line); }); @@ -194,7 +194,6 @@ function plan(compiled) { } if(node.scope) { used.push(node.scope.id); - //addListener(node, node.scope); } } used.push(ret.rhs.id); @@ -244,17 +243,9 @@ function walk(line, symbols) { break; case 'define': introspectObject(line.object, symbols, line.dependsOn, line); - if(line.fallback) { - walk(line.fallback, symbols); - } break; case 'return': walk(line.rhs, symbols); - - if(line.fallback) { - walk(line.fallback, symbols); - } - // Route if(line.route) { introspectString(line.route.path, symbols, line.dependsOn); @@ -269,9 +260,6 @@ function walk(line, symbols) { case 'delete': introspectFrom(line, [line.source], symbols); line = introspectWhere(line, symbols); - if(line.fallback) { - walk(line.fallback, symbols); - } break; case 'insert': introspectFrom(line, [line.source], symbols); @@ -284,9 +272,6 @@ function walk(line, symbols) { if(line.jsonObj) { introspectString(line.jsonObj.value, symbols, line.dependsOn, line); } - if(line.fallback) { - walk(line.fallback, symbols); - } break; case 'update': introspectFrom(line, [line.source], symbols); @@ -299,9 +284,6 @@ function walk(line, symbols) { introspectFrom(line.joiner, line.joiner.fromClause, symbols, line); introspectWhere(line.joiner, symbols, line); } - if(line.fallback) { - walk(line.fallback, symbols); - } break; case 'if': addDep(line, line.dependsOn, line.condition, symbols); @@ -321,10 +303,6 @@ function walk(line, symbols) { addDep(line, line.dependsOn, dependency, symbols); walk(dependency, symbols); }); - if(line.fallback) { - walk(line.fallback, symbols); - } - break; case 'try': _.each(line.dependsOn, function(tryline){ @@ -344,6 +322,9 @@ function walk(line, symbols) { } break; } + if(line.fallback) { + walk(line.fallback, symbols); + } } function addListener(node, listener) { diff --git a/modules/compiler/pegs/ql.peg b/modules/compiler/pegs/ql.peg index 69623311..9902a783 100755 --- a/modules/compiler/pegs/ql.peg +++ b/modules/compiler/pegs/ql.peg @@ -475,7 +475,6 @@ TryClause = 'try' insig '{' insig tryClause:TryCrlf+ insig '}' catchClause:Catch type : 'try', dependsOn : tryClause, catchClause : catchClause, - exceptions : [], finallyClause : finallyClause || undefined } } @@ -515,9 +514,7 @@ LogicParen = '(' insig p:LogicPhrase insig ')' insig { LogicPhrase = OrPhrase / AndPhrase / NotPhrase / NormalPhrase; OrPhrase = s1:(AndPhrase / NotPhrase / NormalPhrase) insig sn:OrTail{ - if(sn){ - s1.fallback = sn; - } + s1.fallback = sn; return s1; } OrTail = '||' insig s:LogicStatement insig{ diff --git a/modules/compiler/test/scope-test.js b/modules/compiler/test/scope-test.js index 18cfbe44..bbcf17ca 100644 --- a/modules/compiler/test/scope-test.js +++ b/modules/compiler/test/scope-test.js @@ -17,7 +17,7 @@ "use strict"; var compiler = require('../lib/compiler'); - +var _ = require('underscore') module.exports = { 'try catch': function(test) { var q = "try {select * from aaa;\n\ diff --git a/modules/console/public/scripts/console.js b/modules/console/public/scripts/console.js index 48e2e7bc..d246e411 100644 --- a/modules/console/public/scripts/console.js +++ b/modules/console/public/scripts/console.js @@ -84,7 +84,6 @@ $(document).ready(function() { $('#step').hide(); emitterID = 0; runQuery(statement, escaped, compiled); - pastePic(); }); $('#debug').unbind(); @@ -96,7 +95,6 @@ $(document).ready(function() { step = 1; runQuery(statement, escaped, compiled, true); $('#step').show(); - pastePic(compiled); }); $('#step').unbind(); @@ -319,7 +317,7 @@ $(document).ready(function() { // Tell the server what notifications to receive function subscribe(socket, uri) { var events = ['ack', 'compile-error', 'statement-error', 'statement-in-flight', - 'statement-success', 'statement-request', 'statement-response', 'script-done', 'ql.io-debug']; + 'statement-success', 'statement-request', 'statement-response', 'script-done', 'ql.io-debug', 'ql.io-visualization']; var packet = { type: 'events', data: JSON.stringify(events) @@ -421,6 +419,10 @@ $(document).ready(function() { opacity: 1.0 }); }); + emitter.on('ql.io-visualization', function(data){ + $('#dependencyMap').attr('src', data); + $('#dependencyMap').show(); + }) emitter.on('script-done', function (data) { markers.push(editor.setMarker(data.line - 1, data.elapsed + ' ms', 'green')); }); @@ -435,52 +437,14 @@ $(document).ready(function() { id : emitterID }; socket.send(JSON.stringify(packet)); + delPic(); } - function pastePic(forest) { - function getDepHelper(tree) { - sofar = ""; + function delPic(){ + $('#dependencyMap').hide() + } +}); + + - if (tree.hasOwnProperty('rhs')) { - sofar += getDepHelper(tree.rhs); - } - var line_num = tree.line ? tree.line : 'return'; - if (tree.hasOwnProperty('fallback') && tree.fallback.dependsOn.length > 0) { - var fallback = tree.fallback; - for (var j = 0; j < fallback.dependsOn.length; j++) { - var addition = '%5B'+line_num+'%5D->%5Bnote:'+fallback.dependsOn[j].line+'%5D,'; - sofar += addition; - sofar += getDepHelper(fallback.dependsOn[j]); - } - } - if (tree.dependsOn && tree.dependsOn.length > 0) { - for (var j = 0; j < tree.dependsOn.length; j++) { - var dep = tree.dependsOn[j]; - var addition = '%5B'+line_num+'%5D-%3E%5B'+dep.line+'%5D,' - sofar += addition; - sofar += getDepHelper(dep); - } - } - return sofar; - } - var deptxt = ''; - if (forest) { - for (var k = 0; k < forest.length; k++) { - deptxt += getDepHelper(forest[k]); - } - /*$.ajax({ - type: 'POST', - url: 'http://yuml.me/diagram/plain/class/', - body: {dsl_text: '[a]'}, - success: function(){ - alert('good') - }, - error: function(data){console.log(data)} - }) */ - //$.get('http://yuml.me/diagram/plain/class/[a]', function(){alert('nice')}) - deptxt = 'http://ql-2:3026/diagram/scruffy;/class/'+deptxt.substring(0, deptxt.length-1)+'.png'; - } - $('#dependencyMap').attr('src', deptxt); - } -}); \ No newline at end of file diff --git a/modules/engine/lib/engine.js b/modules/engine/lib/engine.js index bea04aa1..0599c1da 100644 --- a/modules/engine/lib/engine.js +++ b/modules/engine/lib/engine.js @@ -41,6 +41,7 @@ var configLoader = require('./engine/config.js'), LogEmitter = require('./engine/log-emitter.js'), winston = require('winston'), compiler = require('ql.io-compiler'), + visualization = require('./engine/visualization'), _ = require('underscore'), EventEmitter = require('events').EventEmitter, util = require('util'), @@ -285,6 +286,9 @@ Engine.prototype.execute = function() { timeoutID = setTimeout( function () { emitter.emit(eventTypes.KILL); }, 1800000);// clear unused session in 30 minutes. + //try get visualization picture. + visualization.getPic(plan, emitter); + } // Initialize the exec state @@ -306,6 +310,7 @@ Engine.prototype.execute = function() { init(line); }); _.each(statement.catchClause, function(currentcatch){ + init(currentcatch.condition); _.each(currentcatch.lines, function(line){ init(line); }); @@ -342,14 +347,45 @@ Engine.prototype.execute = function() { /* Skip assign statement. Make it undefined, and trigger listener */ function skipVar(statement){ - context[statement.assign] = undefined; + if (execState[statement.id].state == eventTypes.STATEMENT_SUCCESS){ + return; + } execState[statement.id].state = eventTypes.STATEMENT_SUCCESS; - _.each(statement.listeners, function(listener) { - execState[listener.id].count--; - if (!(listener.fbhold)){ - sweep(listener); - } - }); + switch(statement.type){ + case 'try': + _.each(statement.dependsOn, function(tryline){//these are just lines within try{} + skipVar(tryline); + }); + _.each(statement.catchClause, function(mycatch,k){ + _.each(mycatch.lines, function(catchline){ + skipVar(catchline); + }); + }); + _.each(statement.finallyClause, function(finallyline){ + skipVar(finallyline); + }); + break; + case 'if': + _.each(statement.if, function(st) { + skipVar(st); + }); + _.each(statement.else, function(st) { + skipVar(st); + }); + break; + default: + if(!statement.assign){ + return; + } + context[statement.assign] = null; + _.each(statement.listeners, function(listener) { + execState[listener.id].count--; + if (!(listener.fbhold)){ + sweep(listener); + } + }); + } + } // scope is complex to execute, handle separately @@ -388,15 +424,16 @@ Engine.prototype.execute = function() { if(skip) { return; } + if(statement.scope && execState[statement.scope.id].state === eventTypes.STATEMENT_WAITING) { + sweep(statement.scope); + return; + } _.each(statement.dependsOn, function(dependency) { if(execState[dependency.id].state === eventTypes.STATEMENT_WAITING) { // Exec the dependency sweep(dependency); } }); - if(statement.scope && execState[statement.scope.id].state === eventTypes.STATEMENT_WAITING) { - sweep(statement.scope); - } if(statement.rhs) { _.each(statement.rhs.dependsOn, function(dependency) { if(execState[dependency.id].state === eventTypes.STATEMENT_WAITING) { diff --git a/modules/engine/lib/engine/event-types.js b/modules/engine/lib/engine/event-types.js index c6170036..3b72ff3d 100644 --- a/modules/engine/lib/engine/event-types.js +++ b/modules/engine/lib/engine/event-types.js @@ -111,4 +111,6 @@ exports.DEBUG = 'ql.io-debug'; exports.DEBUG_STEP = 'ql.io-debug-step'; +exports.VISUALIZATION = 'ql.io-visualization' + exports.KILL = 'ql.io-kill'; \ No newline at end of file diff --git a/modules/engine/lib/engine/trycatch.js b/modules/engine/lib/engine/trycatch.js index 7b40b8bb..d2deeb76 100644 --- a/modules/engine/lib/engine/trycatch.js +++ b/modules/engine/lib/engine/trycatch.js @@ -48,14 +48,14 @@ exports.throw = function(opts, statement, parentEvent, cb) { assert.ok(statement, 'Argument cb can not be undefined'); var tables = opts.tables, - throwTx = opts.logEmitter.beginEvent({ - parent: parentEvent, - type: 'throw', - message: { - line: statement.line - }, - cb: cb}) - if(!opts.context[statement.err]){// this is simple field check, no logic.exec required + throwTx = opts.logEmitter.beginEvent({ + parent: parentEvent, + type: 'throw', + message: { + line: statement.line + }, + cb: cb}) + if(!opts.context.hasOwnProperty(statement.err)){// this is simple field check, no logic.exec required opts.context[statement.err] = statement.err; } throwTx.cb(null, statement.err); diff --git a/modules/engine/lib/engine/visualization.js b/modules/engine/lib/engine/visualization.js new file mode 100644 index 00000000..f41982ea --- /dev/null +++ b/modules/engine/lib/engine/visualization.js @@ -0,0 +1,160 @@ +/* + * Copyright 2012 eBay Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; +var http = require('http'), + querystring = require('querystring'), + eventTypes = require('./event-types'), + _ = require('underscore'); +exports.getPic = function(compiled, emitter){ + var post_domain = 'ql-2'; + var post_port = 3026; + var post_path = '/diagram/scruffy/class/'; + var diagramtxt = composeDiagram(compiled); + if(!diagramtxt){ + return; + } + + var post_data = querystring.stringify({dsl_text: diagramtxt}); + + var post_options = { + host: post_domain, + port: post_port, + path: post_path, + method: 'POST', + headers: { + 'content-length': post_data.length + } + }; + var visualurl = ['http://', post_domain, ':', post_port, post_path].join(''); + var post_req = http.request(post_options, function(res) { + res.setEncoding('utf8'); + res.on('data', function (chunk) { + emitter.emit(eventTypes.VISUALIZATION, visualurl.concat(chunk)); + }); + }); + +// write parameters to post body + post_req.write(post_data); + post_req.end(); +} + + +function composeDiagram(forest){ + // scope involves reverse visit, keep track of visited statement to avoid infinite loop + var visited = [], + colors = ['red','yellow','blue','green','purple','brown'], + colorIdx = 0; + function getColor(){ + var color = ['{bg:', colors[colorIdx++],'}'].join(''); + if (colorIdx == colors.length){ + colorIdx = 0; + } + return color; + } + function getDepHelper(tree) { + if(visited.indexOf(tree.id) != -1){ + return ''; + } + visited.push(tree.id); + var sofar = '', addition; + + if (tree.hasOwnProperty('rhs')) { + sofar += getDepHelper(tree.rhs); + } + if (tree.hasOwnProperty('return')){ + sofar += getDepHelper(tree.return); + } + var line_num = tree.line ? tree.line : 'return'; + if (tree.hasOwnProperty('fallback')) { + var fallback = tree.fallback; + if (fallback.dependsOn && fallback.dependsOn.length > 0){ + addition = _.reduce(fallback.dependsOn, function(newgraph,fallbackDepend){ + var tmp = '['+line_num+']->[note:'+fallbackDepend.line+'],'; + newgraph = newgraph.concat(tmp); + return newgraph.concat(getDepHelper(fallbackDepend)); + },''); + sofar = sofar.concat(addition); + /*for (var j = 0; j < fallback.dependsOn.length; j++) { + addition = '['+line_num+']->[note:'+fallback.dependsOn[j].line+'],'; + sofar += addition; + sofar += getDepHelper(fallback.dependsOn[j]); + } */ + } + } + if (tree.type != 'try' && tree.dependsOn && tree.dependsOn.length > 0) { + for (var j = 0; j < tree.dependsOn.length; j++) { + var dep = tree.dependsOn[j]; + addition = '['+line_num+']->['+dep.line+'],' + sofar += addition; + sofar += getDepHelper(dep); + } + } + if(tree.scope){ + sofar += getDepHelper(tree.scope); + } + //special scope relationships + if(tree.type === 'if'){ + var mycolor = getColor(); + _.each(tree.if, function(ifline){ + addition = ['[', ifline.line, mycolor, ']-if>[', line_num, mycolor, '],'].join(''); + sofar += addition; + sofar += getDepHelper(ifline); + }) + _.each(tree.else, function(elseline){ + addition = ['[', elseline.line, mycolor, ']-else>[', line_num, mycolor, '],'].join(''); + sofar += addition; + sofar += getDepHelper(elseline); + }) + }else if(tree.type === 'try'){ + var mycolor = getColor(); + _.each(tree.dependsOn, function(tryline){ + addition = ['[',tryline.line, mycolor, ']-try>[',line_num, mycolor, '],'].join(''); + sofar += addition; + sofar += getDepHelper(tryline); + }) + _.each(tree.catchClause, function(mycatch){ + var cond = mycatch.condition; + addition = ['[', cond.line, mycolor, ']-catch_cond>[', line_num, mycolor, '],'].join(''); + sofar += addition; + sofar += getDepHelper(cond); + _.each(mycatch.lines, function(catchline){ + addition = ['[',catchline.line, mycolor, ']-catch>[', cond.line, mycolor, '],'].join(''); + sofar += addition; + sofar += getDepHelper(catchline); + }) + }) + + _.each(tree.finally, function(finallyline){ + addition = ['[',finallyline.line, mycolor, ']-finally>[', line_num, mycolor, '],'].join(''); + sofar += addition; + sofar += getDepHelper(finallyline); + }) + } + + return sofar; + } + if (!forest){ + return ''; + } + else { + //var diagramtxt = _.reduce(forest, function(tree, deptxt){ + var diagramtxt = getDepHelper(forest); + //}, ''); + return diagramtxt; + } + +} \ No newline at end of file diff --git a/modules/engine/test/scope-test.js b/modules/engine/test/scope-test.js index 3c041657..d30cbdbf 100644 --- a/modules/engine/test/scope-test.js +++ b/modules/engine/test/scope-test.js @@ -186,4 +186,4 @@ module.exports['trycatch-nested'] = function(test) { test.done(); } }}); -}; \ No newline at end of file +};