var handlers = []; var parsedTree; (function(check_pred){ var tracer_items = []; var tracer_length = 0; var tracer_pos = 0; $("#source_code").val(source_file); function stepTo(step) { tracer_pos = step; updateSelection(); } // Take a predicate in form of a JavaScript code (string) and returns either true or an error message (string). function goToPred(pred) { function check(i){ var item = datalog[i]; var jsheap = jsheap_of_heap(item.heap); var obj = jsenv_of_env(jsheap, item.env); var objX = {}; if (item.ctx !== undefined){ objX = jsenv_of_env(jsheap, item.ctx); } objX.line = item.line; objX.type = item.type; objX.heap = jsheap; obj.X = objX; // If we want to change the “X” identifier, just change this line. try { if (check_pred(pred, obj)){ stepTo(i); return true; } } catch(e){ error++; } return false; } var error = 0; if (datalog.length === 0) return false; for (var i = (tracer_pos + 1) % datalog.length, current = tracer_pos; i !== current; i++, i %= datalog.length) if (check(i)) return true; if (check(tracer_pos)) return true; if (error === datalog.length) return "There was an execution error at every execution of your condition: are you sure that this is a valid JavaScript code?"; return "Not found"; } function button_reach_handler() { var pred = $("#text_condition").val(); var res = goToPred(pred); if (res !== true){ $("#action_output").html(res); var timeoutID = window.setTimeout(function() { $("#action_output").html(""); }, 3000); } }; $('#text_condition').keypress(function(e){ var keycode = (e.keyCode ? e.keyCode : e.which); if (keycode == '13') { button_reach_handler(); } }); $("#button_reach").click(button_reach_handler); $("#navigation_step").change(function(e) { var n = + $("#navigation_step").val(); n = Math.max(0, Math.min(tracer_length - 1, n)); stepTo(n); }); $("#button_run").click(function() { try { var code = source.getValue(); //console.log(code); parsedTree = esprima.parse(code, {loc:true}); // console.log(parsedTree); program = esprimaToAST(parsedTree); // console.log(program); run(); $("#action_output").html("Run successful!"); } catch(_){ $("#action_output").html("Error during the run."); }; var timeoutID = window.setTimeout(function() { $("#run_output").html(""); }, 1000); }); $("#button_reset").click(function() { stepTo(0); }); $("#button_prev").click(function() { stepTo(Math.max(0, tracer_pos-1)); }); $("#button_next").click(function() { stepTo(Math.min(tracer_length-1, tracer_pos+1)); }); // Assumes tracer_files to be an array of objects with two field: // - file, containing the name of the file, // - contents, a string containing its source code // Assumes tracer_items to be an array with items, e.g.: // { type: 'enter', file: 'foo.ml', start_line: 4, start_col: 0, end_line: 5, end_col: 10 }, // { type: 'exit', file: 'foo.ml', start_line: 4, start_col: 0, end_line: 5, end_col: 10 }, // { type: 'other_event', file: 'foo.ml', start_line: 4, start_col: 0, end_line: 5, end_col: 10 }, function tracer_valid_pos(i) { return (i >= 0 && i < tracer_length); } // dir is -1 or +1 function shared_step(dir) { var i = tracer_pos; i += dir; if (! tracer_valid_pos(i)) return; // not found, we don’t update the tracer position. tracer_pos = i; } // dir is -1 or +1, // target (= target depth) is 0 for (next/prev) or -1 (finish) function shared_next(dir, target) { var i = tracer_pos; var depth = 0; var ty = tracer_items[i].type; if (dir === +1 && ty === 'exit') { depth = 1; } else if (dir === -1 && ty === 'enter') { depth = -1; } while (true) { if (! tracer_valid_pos(i)) { tracer_pos = i - dir; // just before out of range return; // not found } if (i !== tracer_pos && depth === target) { tracer_pos = i; return; } var ty = tracer_items[i].type; if (ty === 'enter') { depth++; } else if (ty === 'exit') { depth--; } i += dir; } } function restart() { tracer_pos = 0; } function step() { shared_step(+1); } function backstep() { shared_step(-1); } function next() { shared_next(+1, 0); } function previous() { shared_next(-1, 0); } function finish() { shared_next(+1, -1); } var curfile = ''; var docs = {}; for (var i = 0; i < tracer_files.length; i++) { var file = tracer_files[i].file; var txt = tracer_files[i].contents; docs[file] = CodeMirror.Doc(txt, 'js'); } var editor = null; var source = null; function viewFile(file) { if (curfile !== file) { curfile = file; editor.swapDoc(docs[curfile]); editor.focus(); updateFileList(); } } function updateFileList() { var s = ''; for (var i = 0; i < tracer_files.length; i++) { var file = tracer_files[i].file; s += "<span class=\"file_item" + ((curfile == file) ? '_current' : '') + "\" onclick=\"viewFile('" + file + "')\">" + file + "</span> "; } $('#file_list').html(s); } function text_of_cst(c) { switch (c.tag) { case "cst_bool": return c.bool + ""; case "cst_number": return c.number + ""; default: return "unrecognized cst"; } } var next_fresh_id = 0; function fresh_id() { return "fresh_id_" + (next_fresh_id++); } function show_value(heap, v, target, depth) { var contents_init = $("#" + target).html(); var s = ""; switch (v.tag) { case "val_cst": s = text_of_cst(v.cst); break; case "val_loc": var contents_rest = "<span class='heap_link'><a onclick=\"handlers['" + target + "']()\"><Object>(" + v.loc + ")</a></span>"; var contents_default = contents_init + contents_rest; function handler_close() { handlers[target] = handler_open; $("#" + target).html(contents_default); editor.focus(); } function handler_open() { handlers[target] = handler_close; var obj = heap.get(v.loc).asReadOnlyArray(); // type object var count = 0; for (var x in obj) { if (obj[x] === undefined) continue; // LATER remove! count++; var targetsub = fresh_id(); $("#" + target).append("<div style='margin-left:1em' id='" + targetsub + "'></div>"); $("#" + targetsub).html(x + ": "); show_value(heap, obj[x], targetsub, depth-1); } if (count === 0) $("#" + target).append("<div style='margin-left:1em'>(empty object)</div>"); editor.focus(); }; handlers[target] = handler_open; $("#" + target).html(contents_default); if (depth > 0) handler_open(); return; case "val_abs": s = "<Closure>"; break; default: s = "<pre style='margin:0; padding: 0; margin-left:1em'>" + JSON.stringify(v, null, 2) + "</pre>"; break; } $("#" + target).append(s); } function updateContext(targetid, heap, env) { $(targetid).html(""); if (env === undefined) return; array_of_env(env).map(function(env){ var target = fresh_id(); $(targetid).append("<div id='" + target + "'></div>"); $("#" + target).html(env.name + ": "); var depth = 1; show_value(heap, env.val, target, depth); }); } var source_select = undefined; function updateSourceSelection() { if (source_select === undefined) { return; } // TODO: rename column into col var anchor = {line: source_select.start.line-1 , ch: source_select.start.column }; var head = {line: source_select.end.line-1, ch: source_select.end.column }; source.setSelection(anchor, head); /* deprecated: var anchor = {line: source_select-1, ch: 0 }; var head = {line: source_select-1, ch: 100 } */; } function updateSelection() { var item = tracer_items[tracer_pos]; source.setSelection({line: 0, ch:0}, {line: 0, ch:0}); // TODO: fct reset if (item !== undefined) { // console.log(item); // $("#disp_infos").html(); if (item.line === undefined) alert("missing line in log event"); // source panel source_select = item.source_select; // console.log(source_select); updateSourceSelection(); // ctx panel updateContext("#disp_ctx", item.heap, item.ctx); // file panel viewFile(item.file); //console.log("pos: " + tracer_pos); // var color = (item.type === 'enter') ? '#F3F781' : '#CCCCCC'; var color = '#F3F781'; $('.CodeMirror-selected').css({ background: color }); $('.CodeMirror-focused .CodeMirror-selected').css({ background: color }); var anchor = {line: item.start_line-1 , ch: item.start_col }; var head = {line: item.end_line-1, ch: item.end_col }; editor.setSelection(anchor, head); // env panel updateContext("#disp_env", item.heap, item.env); // navig panel $("#navigation_step").val(tracer_pos); } updateFileList(); editor.focus(); } source = CodeMirror.fromTextArea(document.getElementById('source_code'), { mode: 'js', lineNumbers: true, lineWrapping: true, }); source.setSize(300, 150); editor = CodeMirror.fromTextArea(document.getElementById('interpreter_code'), { mode: 'js', lineNumbers: true, lineWrapping: true, readOnly: true, extraKeys: { 'R': function(cm) { restart(); updateSelection(); }, 'S': function(cm) { step(); updateSelection(); }, 'B': function(cm) { backstep(); updateSelection(); }, 'N': function(cm) { next(); updateSelection(); }, 'P': function(cm) { previous(); updateSelection(); }, 'F': function(cm) { finish(); updateSelection(); }, }, }); editor.setSize(600,250); /* ==> try in new version of codemirror*/ try { $(editor.getWrapperElement()).resizable({ resize: function() { editor.setSize($(this).width(), $(this).height()); } }); } catch(e) { } editor.on('dblclick', function() { var line = editor.getCursor().line; var txt = editor.getLine(line); var prefix = "#sec-"; var pos_start = txt.indexOf(prefix); if (pos_start === -1) return; var pos_end = txt.indexOf("*", pos_start); if (pos_end === -1) return; var sec = txt.substring(pos_start, pos_end); var url = "http://www.ecma-international.org/ecma-262/5.1/" + sec; window.open(url, '_blank'); }); editor.focus(); // used to ensure that most events have an associated term function completeTermsInDatalog(items) { var last = undefined; for (var k = 0; k < datalog.length; k++) { var item = datalog[k]; item.source_select = last; if (item.ctx !== undefined) { var ctx_as_array = array_of_env(item.ctx); if (ctx_as_array.length > 0 && ctx_as_array[0].name === "t") { var t = ctx_as_array[0].val; if (! (t === undefined || t.start === undefined || t.end === undefined)) { item.source_select = { start: t.start, end: t.end }; // TODO: avoir un t.location last = t; } } } } } function run() { run_program(program); completeTermsInDatalog(datalog); tracer_items = datalog; tracer_length = tracer_items.length $("#navigation_total").html(tracer_length - 1); stepTo(0); // calls updateSelection(); } // Note: for the demo // run(); // stepTo(78); /* demo var j = jsheap_of_heap(heap); for (var k = 0; k < datalog.length; k++) { var item = datalog[k]; var jsheap = jsheap_of_heap(item.heap); item.heap = jsheap; item.env = jsenv_of_env(jsheap, item.env); if (item.ctx !== undefined) { item.ctx = jsenv_of_env(jsheap, item.ctx); } } */ }(function check_pred(p, obj) { with (obj){ return eval(p) } }));