Add backwards-stepping JS-Interpreter demo

This commit is contained in:
Neil Fraser
2022-03-01 11:11:39 -08:00
parent ff882e6d50
commit 03ef734721
5 changed files with 6995 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+321
View File
@@ -0,0 +1,321 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Blockly Demo: Backwards Stepping with JS-Interpreter</title>
<link href="style.css" rel="stylesheet" type="text/css">
<script src="acorn.js"></script>
<script src="interpreter.js"></script>
<script src="serialize.js"></script>
<script src="diff_match_patch.js"></script>
<script src="../../blockly_compressed.js"></script>
<script src="../../blocks_compressed.js"></script>
<script src="../../javascript_compressed.js"></script>
<script src="../../msg/js/en.js"></script>
<style>
body {
background-color: #fff;
font-family: sans-serif;
}
h1 {
font-weight: normal;
font-size: 140%;
}
</style>
</head>
<body>
<h1><a href="https://developers.google.com/blockly/">Blockly</a> &gt;
<a href="../index.html">Demos</a> &gt; Backwards Stepping with JS-Interpreter</h1>
<p>This is a demo of executing code step-by-step with a sandboxed JavaScript
interpreter -- both forwards and backwards.</p>
<p>Each step forwards saves the current state to a stack. Each state
backwards restores to the previous state on the stack. Compression is used
to keep only the deltas between stack frames. Note that because serialization
reaches deep beyond the public API for the JS-Interpreter, the uncompiled
acorn.js and interpreter.js must be used in place of acorn_interpreter.js.</p>
<p>&rarr; <a href="https://developers.google.com/blockly/guides/configure-blockly/web/running-javascript#js_interpreter">More info on running code with JS-Interpreter</a></p>
<p>
<button onclick="stepBackwards()" id="stepBackwardsButton" disabled="disabled">Step &larr;</button>
<button onclick="stepForwards()" id="stepForwardsButton">Step &rarr;</button>
</p>
<div style="width: 100%">
<div id="blocklyDiv"
style="display: inline-block; height: 480px; width: 58%"></div>
<textarea id="output" disabled="disabled"
style="display: inline-block; height: 480px; width: 38%;">
</textarea>
</div>
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
<category name="Logic" colour="%{BKY_LOGIC_HUE}">
<block type="controls_if"></block>
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_negate"></block>
<block type="logic_boolean"></block>
</category>
<category name="Loops" colour="%{BKY_LOOPS_HUE}">
<block type="controls_repeat_ext">
<value name="TIMES">
<block type="math_number">
<field name="NUM">10</field>
</block>
</value>
</block>
<block type="controls_whileUntil"></block>
</category>
<category name="Math" colour="%{BKY_MATH_HUE}">
<block type="math_number">
<field name="NUM">123</field>
</block>
<block type="math_arithmetic"></block>
<block type="math_single"></block>
</category>
<category name="Text" colour="%{BKY_TEXTS_HUE}">
<block type="text"></block>
<block type="text_length"></block>
<block type="text_print"></block>
<block type="text_prompt_ext">
<value name="TEXT">
<block type="text"></block>
</value>
</block>
</category>
<sep></sep>
<category name="Variables" custom="VARIABLE" colour="%{BKY_VARIABLES_HUE}">
</category>
<category name="Functions" custom="PROCEDURE" colour="%{BKY_PROCEDURES_HUE}">
</category>
</xml>
<xml xmlns="https://developers.google.com/blockly/xml" id="startBlocks" style="display: none">
<block type="variables_set" id="set_n_initial" inline="true" x="20" y="20">
<field name="VAR">n</field>
<value name="VALUE">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
<next>
<block type="controls_repeat_ext" id="repeat" inline="true">
<value name="TIMES">
<block type="math_number">
<field name="NUM">4</field>
</block>
</value>
<statement name="DO">
<block type="variables_set" id="set_n_update" inline="true">
<field name="VAR">n</field>
<value name="VALUE">
<block type="math_arithmetic" inline="true">
<field name="OP">MULTIPLY</field>
<value name="A">
<block type="variables_get">
<field name="VAR">n</field>
</block>
</value>
<value name="B">
<block type="math_number">
<field name="NUM">2</field>
</block>
</value>
</block>
</value>
<next>
<block type="text_print" id="print">
<value name="TEXT">
<block type="variables_get">
<field name="VAR">n</field>
</block>
</value>
</block>
</next>
</block>
</statement>
</block>
</next>
</block>
</xml>
<script>
var demoWorkspace = Blockly.inject('blocklyDiv',
{media: '../../media/',
toolbox: document.getElementById('toolbox')});
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
Blockly.JavaScript.addReservedWords('highlightBlock');
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
demoWorkspace);
var outputArea = document.getElementById('output');
var myInterpreter = null;
// Stack of serializations. Each object contains three properties:
// .json: A full serialization of the interpreter's state.
// .delta: Instead of a full serialization, a delta from the next sate.
// .highlight: The ID of the block highlighted in this state.
var serializationStack = [];
// Global variable to keep track of the currently highlighted block.
var highlightedBlockId = '';
// The forward step button disables for 2s after program completion, then
// re-enables. If the backwards step button is pressed during this time
// then cancel the scheduled re-enabling.
var disablePid = 0;
// Optionally, use the Diff Match Patch library to compress the stack.
var dmp;
if (typeof diff_match_patch === 'function') {
dmp = new diff_match_patch();
console.log('Using DMP for compression.');
} else {
console.warn('DMP not available, each step will cost ~300 KB.');
}
function initApi(interpreter, globalObject) {
// Add an API function for the alert() block, generated for "text_print" blocks.
var wrapper = function alert(text) {
text = arguments.length ? text : '';
outputArea.value += '\n' + text;
};
interpreter.setProperty(globalObject, 'alert',
interpreter.createNativeFunction(wrapper));
// Add an API function for the prompt() block.
var wrapper = function prompt(text) {
return window.prompt(text);
};
interpreter.setProperty(globalObject, 'prompt',
interpreter.createNativeFunction(wrapper));
// Add an API function for highlighting blocks.
var wrapper = function(id) {
id = String(id || '');
highlightedBlockId = id;
return highlightBlock(id);
};
interpreter.setProperty(globalObject, 'highlightBlock',
interpreter.createNativeFunction(wrapper));
}
var highlightPause = false;
function highlightBlock(id) {
demoWorkspace.highlightBlock(id);
highlightPause = true;
}
function resetStepUi(clearOutput) {
demoWorkspace.highlightBlock(null);
highlightPause = false;
if (clearOutput) {
outputArea.value = 'Program output:\n=================';
}
myInterpreter = null;
}
function stepBackwards() {
var serialization = serializationStack.pop();
if (!serializationStack.length) {
document.getElementById('stepBackwardsButton').disabled = 'disabled';
}
if (!serialization) {
return; // Should never happen.
}
highlightedBlockId = serialization.highlight;
highlightBlock(highlightedBlockId);
var json = serialization.json;
if (dmp && serializationStack.length) {
// Uncompress the previous serialization.
var previousStack = serializationStack[serializationStack.length - 1];
var previousDiff = dmp.diff_fromDelta(json, previousStack.delta);
previousStack.delta = null;
previousStack.json = dmp.diff_text2(previousDiff);
}
json = JSON.parse(json);
// Create a clean interpreter with the same initialization functions.
myInterpreter = new Interpreter('', initApi);
deserialize(json, myInterpreter);
if (disablePid) {
clearTimeout(disablePid);
disablePid = 0;
document.getElementById('stepForwardsButton').disabled = '';
}
}
function stepForwards() {
if (!myInterpreter) {
// First statement of this code.
// Clear the program output.
resetStepUi(true);
var latestCode = Blockly.JavaScript.workspaceToCode(demoWorkspace);
serializationStack.length = 0;
document.getElementById('stepBackwardsButton').disabled = 'disabled';
myInterpreter = new Interpreter(latestCode, initApi);
// And then show generated code in an alert.
// In a timeout to allow the outputArea.value to reset first.
setTimeout(function() {
alert('Ready to execute the following code\n' +
'===================================\n' + latestCode);
highlightPause = true;
stepForwards();
}, 1);
return;
}
highlightPause = false;
var json = JSON.stringify(serialize(myInterpreter));
if (dmp && serializationStack.length) {
// Compress the previous serialization as a delta of the current one.
var previousStack = serializationStack[serializationStack.length - 1];
var diffs = dmp.diff_main(json, previousStack.json);
dmp.diff_cleanupEfficiency(diffs);
previousStack.json = null;
previousStack.delta = dmp.diff_toDelta(diffs);
}
serializationStack.push({json: json, delta: null, highlight: highlightedBlockId});
document.getElementById('stepBackwardsButton').disabled = '';
do {
try {
var hasMoreCode = myInterpreter.step();
} finally {
if (!hasMoreCode) {
// Program complete, no more code to execute.
outputArea.value += '\n\n<< Program complete >>';
resetStepUi(false);
// Cool down, to discourage accidentally restarting the program.
document.getElementById('stepForwardsButton').disabled = 'disabled';
disablePid = setTimeout(function() {
document.getElementById('stepForwardsButton').disabled = '';
document.getElementById('stepBackwardsButton').disabled = 'disabled';
disablePid = 0;
}, 2000);
return;
}
}
// Keep executing until a highlight statement is reached,
// or the code completes or errors.
} while (hasMoreCode && !highlightPause);
}
demoWorkspace.addChangeListener(function(event) {
if (!event.isUiEvent) {
// Something changed. Interpreter needs to be reloaded.
resetStepUi(true);
}
});
</script>
</body>
</html>
+55
View File
@@ -0,0 +1,55 @@
var diff_match_patch=function(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=.5;this.Patch_Margin=4;this.Match_MaxBits=32},DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;diff_match_patch.Diff=function(a,b){this[0]=a;this[1]=b};diff_match_patch.Diff.prototype.length=2;diff_match_patch.Diff.prototype.toString=function(){return this[0]+","+this[1]};
diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[new diff_match_patch.Diff(DIFF_EQUAL,a)]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);f=this.diff_commonSuffix(a,b);var g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,
b.length-f);a=this.diff_compute_(a,b,e,d);c&&a.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));g&&a.push(new diff_match_patch.Diff(DIFF_EQUAL,g));this.diff_cleanupMerge(a);return a};
diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[new diff_match_patch.Diff(DIFF_INSERT,b)];if(!b)return[new diff_match_patch.Diff(DIFF_DELETE,a)];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[new diff_match_patch.Diff(DIFF_INSERT,e.substring(0,g)),new diff_match_patch.Diff(DIFF_EQUAL,f),new diff_match_patch.Diff(DIFF_INSERT,e.substring(g+f.length))],a.length>b.length&&(c[0][0]=c[2][0]=DIFF_DELETE),c):1==f.length?[new diff_match_patch.Diff(DIFF_DELETE,
a),new diff_match_patch.Diff(DIFF_INSERT,b)]:(e=this.diff_halfMatch_(a,b))?(b=e[1],f=e[3],a=e[4],e=this.diff_main(e[0],e[2],c,d),c=this.diff_main(b,f,c,d),e.concat([new diff_match_patch.Diff(DIFF_EQUAL,a)],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,d):this.diff_bisect_(a,b,d)};
diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push(new diff_match_patch.Diff(DIFF_EQUAL,""));for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case DIFF_INSERT:e++;g+=a[b][1];break;case DIFF_DELETE:d++;f+=a[b][1];break;case DIFF_EQUAL:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,
0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=2*f,h=Array(g),l=Array(g),k=0;k<g;k++)h[k]=-1,l[k]=-1;h[f+1]=0;l[f+1]=0;k=d-e;for(var m=0!=k%2,p=0,x=0,w=0,q=0,t=0;t<f&&!((new Date).getTime()>c);t++){for(var v=-t+p;v<=t-x;v+=2){var n=f+v;var r=v==-t||v!=t&&h[n-1]<h[n+1]?h[n+1]:h[n-1]+1;for(var y=r-v;r<d&&y<e&&a.charAt(r)==b.charAt(y);)r++,y++;h[n]=r;if(r>d)x+=2;else if(y>e)p+=2;else if(m&&(n=f+k-v,0<=n&&n<g&&-1!=l[n])){var u=d-l[n];if(r>=
u)return this.diff_bisectSplit_(a,b,r,y,c)}}for(v=-t+w;v<=t-q;v+=2){n=f+v;u=v==-t||v!=t&&l[n-1]<l[n+1]?l[n+1]:l[n-1]+1;for(r=u-v;u<d&&r<e&&a.charAt(d-u-1)==b.charAt(e-r-1);)u++,r++;l[n]=u;if(u>d)q+=2;else if(r>e)w+=2;else if(!m&&(n=f+k-v,0<=n&&n<g&&-1!=h[n]&&(r=h[n],y=f+r-n,u=d-u,r>=u)))return this.diff_bisectSplit_(a,b,r,y,c)}}return[new diff_match_patch.Diff(DIFF_DELETE,a),new diff_match_patch.Diff(DIFF_INSERT,b)]};
diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,g=-1,h=d.length;g<a.length-1;){g=a.indexOf("\n",c);-1==g&&(g=a.length-1);var l=a.substring(c,g+1);(e.hasOwnProperty?e.hasOwnProperty(l):void 0!==e[l])?b+=String.fromCharCode(e[l]):(h==f&&(l=a.substring(c),g=a.length),b+=String.fromCharCode(h),e[l]=h,d[h++]=l);c=g+1}return b}var d=[],e={};d[0]="";var f=4E4,g=c(a);f=65535;var h=c(b);return{chars1:g,chars2:h,lineArray:d}};
diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;d=0;for(var e=1;;){var f=a.substring(c-e);f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,k,l,m;-1!=(e=b.indexOf(d,e+1));){var p=f.diff_commonPrefix(a.substring(c),b.substring(e)),u=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<u+p&&(g=b.substring(e-u,e)+b.substring(e,e+p),h=a.substring(0,c-u),k=a.substring(c+p),l=b.substring(0,e-u),m=b.substring(e+p))}return 2*g.length>=a.length?[h,k,l,m,g]:null}if(0>=this.Diff_Timeout)return null;
var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4));d=c(d,e,Math.ceil(d.length/2));if(g||d)g=d?g?g[4].length>d[4].length?g:d:d:g;else return null;if(a.length>b.length){d=g[0];e=g[1];var h=g[2];var l=g[3]}else h=g[0],l=g[1],d=g[2],e=g[3];return[d,e,h,l,g[4]]};
diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,l=0,k=0;f<a.length;)a[f][0]==DIFF_EQUAL?(c[d++]=f,g=l,h=k,k=l=0,e=a[f][1]):(a[f][0]==DIFF_INSERT?l+=a[f][1].length:k+=a[f][1].length,e&&e.length<=Math.max(g,h)&&e.length<=Math.max(l,k)&&(a.splice(c[d-1],0,new diff_match_patch.Diff(DIFF_DELETE,e)),a[c[d-1]+1][0]=DIFF_INSERT,d--,d--,f=0<d?c[d-1]:-1,k=l=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<
a.length;){if(a[f-1][0]==DIFF_DELETE&&a[f][0]==DIFF_INSERT){b=a[f-1][1];c=a[f][1];d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,c.substring(0,d))),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,b.substring(0,e))),a[f-1][0]=DIFF_INSERT,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=DIFF_DELETE,
a[f+1][1]=b.substring(e),f++;f++}f++}};
diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_);c=g&&c.match(diff_match_patch.linebreakRegex_);d=h&&d.match(diff_match_patch.linebreakRegex_);var k=c&&a.match(diff_match_patch.blanklineEndRegex_),l=d&&b.match(diff_match_patch.blanklineStartRegex_);
return k||l?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(a[c-1][0]==DIFF_EQUAL&&a[c+1][0]==DIFF_EQUAL){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g){var h=e.substring(e.length-g);d=d.substring(0,d.length-g);e=h+e.substring(0,e.length-g);f=h+f}g=d;h=e;for(var l=f,k=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){d+=e.charAt(0);e=e.substring(1)+f.charAt(0);f=f.substring(1);var m=b(d,e)+b(e,f);m>=k&&(k=m,g=d,h=e,l=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-
1,1),c--),a[c][1]=h,l?a[c+1][1]=l:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,l=!1,k=!1;f<a.length;)a[f][0]==DIFF_EQUAL?(a[f][1].length<this.Diff_EditCost&&(l||k)?(c[d++]=f,g=l,h=k,e=a[f][1]):(d=0,e=null),l=k=!1):(a[f][0]==DIFF_DELETE?k=!0:l=!0,e&&(g&&h&&l&&k||e.length<this.Diff_EditCost/2&&3==g+h+l+k)&&(a.splice(c[d-1],0,new diff_match_patch.Diff(DIFF_DELETE,e)),a[c[d-1]+1][0]=DIFF_INSERT,d--,e=null,g&&h?(l=k=!0,d=0):(d--,f=0<d?c[d-1]:-1,l=k=!1),b=!0)),f++;b&&this.diff_cleanupMerge(a)};
diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push(new diff_match_patch.Diff(DIFF_EQUAL,""));for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case DIFF_INSERT:d++;f+=a[b][1];b++;break;case DIFF_DELETE:c++;e+=a[b][1];b++;break;case DIFF_EQUAL:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&a[b-c-d-1][0]==DIFF_EQUAL?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,new diff_match_patch.Diff(DIFF_EQUAL,f.substring(0,g))),b++),f=f.substring(g),e=e.substring(g)),
g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-g),e=e.substring(0,e.length-g))),b-=c+d,a.splice(b,c+d),e.length&&(a.splice(b,0,new diff_match_patch.Diff(DIFF_DELETE,e)),b++),f.length&&(a.splice(b,0,new diff_match_patch.Diff(DIFF_INSERT,f)),b++),b++):0!==b&&a[b-1][0]==DIFF_EQUAL?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)a[b-1][0]==DIFF_EQUAL&&a[b+1][0]==DIFF_EQUAL&&(a[b][1].substring(a[b][1].length-
a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};
diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){a[g][0]!==DIFF_INSERT&&(c+=a[g][1].length);a[g][0]!==DIFF_DELETE&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&a[g][0]===DIFF_DELETE?f:f+(b-e)};
diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],l=a[g][1].replace(c,"&amp;").replace(d,"&lt;").replace(e,"&gt;").replace(f,"&para;<br>");switch(h){case DIFF_INSERT:b[g]='<ins style="background:#e6ffe6;">'+l+"</ins>";break;case DIFF_DELETE:b[g]='<del style="background:#ffe6e6;">'+l+"</del>";break;case DIFF_EQUAL:b[g]="<span>"+l+"</span>"}}return b.join("")};
diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_INSERT&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_DELETE&&(b[c]=a[c][1]);return b.join("")};
diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][1];switch(a[e][0]){case DIFF_INSERT:c+=f.length;break;case DIFF_DELETE:d+=f.length;break;case DIFF_EQUAL:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case DIFF_INSERT:b[c]="+"+encodeURI(a[c][1]);break;case DIFF_DELETE:b[c]="-"+a[c][1].length;break;case DIFF_EQUAL:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=new diff_match_patch.Diff(DIFF_INSERT,decodeURI(h))}catch(k){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var l=parseInt(h,10);if(isNaN(l)||0>l)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=l);"="==f[g].charAt(0)?c[d++]=new diff_match_patch.Diff(DIFF_EQUAL,h):c[d++]=
new diff_match_patch.Diff(DIFF_DELETE,h);break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return f.Match_Distance?e+g/f.Match_Distance:g?1:e}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));var l=1<<b.length-1;h=-1;for(var k,m,p=b.length+a.length,x,w=0;w<b.length;w++){k=0;for(m=p;k<m;)d(w,
c+m)<=g?k=m:p=m,m=Math.floor((p-k)/2+k);p=m;k=Math.max(1,c-m+1);var q=Math.min(c+m,a.length)+b.length;m=Array(q+2);for(m[q+1]=(1<<w)-1;q>=k;q--){var t=e[a.charAt(q-1)];m[q]=0===w?(m[q+1]<<1|1)&t:(m[q+1]<<1|1)&t|(x[q+1]|x[q])<<1|1|x[q+1];if(m[q]&l&&(t=d(w,q-1),t<=g))if(g=t,h=q-1,h>c)k=Math.max(1,2*c-h);else break}if(d(w+1,c)>g)break;x=m}return h};
diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){if(null===a.start2)throw Error("patch not initialized");for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));(d=b.substring(a.start2+a.length1,
a.start2+a.length1+d))&&a.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,d));a.start1-=c.length;a.start2-=c.length;a.length1+=c.length+d.length;a.length2+=c.length+d.length}};
diff_match_patch.prototype.patch_make=function(a,b,c){if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c){var d=a;b=this.diff_main(d,b,!0);2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b))}else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,l=0;l<b.length;l++){var k=b[l][0],m=b[l][1];e||k===DIFF_EQUAL||(a.start1=f,a.start2=g);switch(k){case DIFF_INSERT:a.diffs[e++]=b[l];a.length2+=m.length;d=d.substring(0,g)+m+d.substring(g);break;case DIFF_DELETE:a.length1+=m.length;a.diffs[e++]=b[l];d=d.substring(0,g)+d.substring(g+m.length);break;case DIFF_EQUAL:m.length<=2*this.Patch_Margin&&e&&b.length!=l+1?(a.diffs[e++]=b[l],a.length1+=m.length,a.length2+=m.length):
m.length>=2*this.Patch_Margin&&e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}k!==DIFF_INSERT&&(f+=m.length);k!==DIFF_DELETE&&(g+=m.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};
diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=new diff_match_patch.Diff(d.diffs[f][0],d.diffs[f][1]);e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),l=-1;if(h.length>this.Match_MaxBits){var k=this.match_main(b,h.substring(0,this.Match_MaxBits),g);-1!=k&&(l=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==l||k>=l)&&(k=-1)}else k=this.match_main(b,h,
g);if(-1==k)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=k-g,g=-1==l?b.substring(k,k+h.length):b.substring(k,l+this.Match_MaxBits),h==g)b=b.substring(0,k)+this.diff_text2(a[f].diffs)+b.substring(k+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);h=0;var m;for(l=0;l<a[f].diffs.length;l++){var p=a[f].diffs[l];p[0]!==DIFF_EQUAL&&(m=this.diff_xIndex(g,h));p[0]===
DIFF_INSERT?b=b.substring(0,k+m)+p[1]+b.substring(k+m):p[0]===DIFF_DELETE&&(b=b.substring(0,k+m)+b.substring(k+this.diff_xIndex(g,h+p[1].length)));p[0]!==DIFF_DELETE&&(h+=p[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;d=a[0];var e=d.diffs;if(0==e.length||e[0][0]!=DIFF_EQUAL)e.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;
0==e.length||e[e.length-1][0]!=DIFF_EQUAL?(e.push(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,l=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,g)));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){g=d.diffs[0][0];var k=d.diffs[0][1];g===DIFF_INSERT?(h.length2+=
k.length,f+=k.length,h.diffs.push(d.diffs.shift()),l=!1):g===DIFF_DELETE&&1==h.diffs.length&&h.diffs[0][0]==DIFF_EQUAL&&k.length>2*b?(h.length1+=k.length,e+=k.length,l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),d.diffs.shift()):(k=k.substring(0,b-h.length1-this.Patch_Margin),h.length1+=k.length,e+=k.length,g===DIFF_EQUAL?(h.length2+=k.length,f+=k.length):l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),k==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(k.length))}g=this.diff_text2(h.diffs);
g=g.substring(g.length-this.Patch_Margin);k=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==k&&(h.length1+=k.length,h.length2+=k.length,0!==h.diffs.length&&h.diffs[h.diffs.length-1][0]===DIFF_EQUAL?h.diffs[h.diffs.length-1][1]+=k:h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,k)));l||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_DELETE,g));else if("+"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_INSERT,g));else if(" "==e)f.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,g));else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};
diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
diff_match_patch.patch_obj.prototype.toString=function(){for(var a=["@@ -"+(0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1)+" +"+(0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2)+" @@\n"],b,c=0;c<this.diffs.length;c++){switch(this.diffs[c][0]){case DIFF_INSERT:b="+";break;case DIFF_DELETE:b="-";break;case DIFF_EQUAL:b=" "}a[c+1]=b+encodeURI(this.diffs[c][1])+"\n"}return a.join("").replace(/%20/g," ")};
this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=DIFF_DELETE;this.DIFF_INSERT=DIFF_INSERT;this.DIFF_EQUAL=DIFF_EQUAL;
File diff suppressed because it is too large Load Diff
+411
View File
@@ -0,0 +1,411 @@
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Saving and restoring the state of a JS-Intepreter.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
// Constructors for objects within Acorn.
var NODE_CONSTRUCTOR;
var NODE_LOC_CONSTRUCTOR;
var LINE_LOC_CONSTRUCTOR;
function recordAcornConstructons(interpreter) {
// Constructors for objects within Acorn.
NODE_CONSTRUCTOR = interpreter.ast.constructor;
NODE_LOC_CONSTRUCTOR = interpreter.ast.loc &&
interpreter.ast.loc.constructor;
LINE_LOC_CONSTRUCTOR = interpreter.ast.loc &&
interpreter.ast.loc.end.constructor;
}
function deserialize(json, interpreter) {
function decodeValue(value) {
if (value && typeof value === 'object') {
var data;
if ((data = value['#'])) {
// Object reference: {'#': 42}
value = objectList[data];
if (!value) {
throw ReferenceError('Object reference not found: ' + data);
}
return value;
}
if ((data = value['Number'])) {
// Special number: {'Number': 'Infinity'}
return Number(data);
}
if ((data = value['Value'])) {
// Special value: {'Value': 'undefined'}
if (value['Value'] === 'undefined') {
return undefined;
}
}
}
return value;
}
var stack = interpreter.getStateStack();
if (!Array.isArray(json)) {
throw TypeError('Top-level JSON is not a list.');
}
if (!stack.length) {
// Require native functions to be present.
throw Error('Interpreter must be initialized prior to deserialization.');
}
recordAcornConstructons(interpreter);
var LOC_REGEX = /^(\d*):(\d*)-(\d*):(\d*) ?(.*)$/;
// Find all native functions in existing interpreter.
var objectList = [];
objectHunt_(stack, objectList);
var functionHash = Object.create(null);
for (var i = 0; i < objectList.length; i++) {
if (typeof objectList[i] === 'function') {
functionHash[objectList[i].id] = objectList[i];
}
}
// First pass: Create object stubs for every object.
objectList = [];
for (var i = 0; i < json.length; i++) {
var jsonObj = json[i];
var obj;
switch (jsonObj['type']) {
case 'Map':
obj = Object.create(null);
break;
case 'Object':
obj = {};
break;
case 'ScopeReference':
obj = Interpreter.SCOPE_REFERENCE;
break;
case 'Function':
obj = functionHash[jsonObj['id']];
if (!obj) {
throw RangeError('Function ID not found: ' + jsonObj['id']);
}
break;
case 'Array':
// Currently we assume that Arrays are not sparse.
obj = [];
break;
case 'Date':
obj = new Date(jsonObj['data']);
if (isNaN(obj)) {
throw TypeError('Invalid date: ' + jsonObj['data']);
}
break;
case 'RegExp':
obj = RegExp(jsonObj['source'], jsonObj['flags']);
break;
case 'PseudoObject':
obj = new Interpreter.Object(null);
break;
case 'Scope':
obj = new Interpreter.Scope(undefined, undefined, undefined);
break;
case 'State':
obj = new Interpreter.State(undefined, undefined);
break;
case 'Node':
obj = new NODE_CONSTRUCTOR();
delete obj.start;
delete obj.end;
var locText = jsonObj['loc'];
if (locText) {
var loc = new NODE_LOC_CONSTRUCTOR();
var m = locText.match(LOC_REGEX);
var locStart = null;
if (m[1] || m[2]) {
locStart = new LINE_LOC_CONSTRUCTOR();
locStart.line = Number(m[1]);
locStart.column = Number(m[2]);
}
loc.start = locStart;
var locEnd = null;
if (m[3] || m[4]) {
locEnd = new LINE_LOC_CONSTRUCTOR();
locEnd.line = Number(m[3]);
locEnd.column = Number(m[4]);
}
loc.end = locEnd;
if (m[5]) {
loc.source = decodeURI(m[5]);
} else {
delete loc.source;
}
obj.loc = loc;
}
break;
default:
throw TypeError('Unknown type: ' + jsonObj['type']);
}
objectList[i] = obj;
}
// Second pass: Populate properties for every object.
for (var i = 0; i < json.length; i++) {
var jsonObj = json[i];
var obj = objectList[i];
// Repopulate objects.
var props = jsonObj['props'];
if (props) {
var nonConfigurable = jsonObj['nonConfigurable'] || [];
var nonEnumerable = jsonObj['nonEnumerable'] || [];
var nonWritable = jsonObj['nonWritable'] || [];
var getter = jsonObj['getter'] || [];
var setter = jsonObj['setter'] || [];
var names = Object.getOwnPropertyNames(props);
for (var j = 0; j < names.length; j++) {
var name = names[j];
var descriptor = {
configurable: nonConfigurable.indexOf(name) === -1,
enumerable: nonEnumerable.indexOf(name) === -1
};
var hasGetter = getter.indexOf(name) !== -1;
var hasSetter = setter.indexOf(name) !== -1;
if (hasGetter || hasSetter) {
if (hasGetter) {
descriptor.get = interpreter.setProperty.placeholderGet_;
}
if (hasSetter) {
descriptor.set = interpreter.setProperty.placeholderSet_;
}
} else {
descriptor.value = decodeValue(props[name]);
descriptor.writable = nonWritable.indexOf(name) === -1;
}
Object.defineProperty(obj, name, descriptor);
}
}
// Repopulate arrays.
if (Array.isArray(obj)) {
var data = jsonObj['data'];
if (data) {
for (var j = 0; j < data.length; j++) {
obj.push(decodeValue(data[j]));
}
}
}
}
// First object is the interpreter.
var root = objectList[0];
for (var prop in root) {
interpreter[prop] = root[prop];
}
}
function serialize(interpreter) {
function encodeValue(value) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
var ref = objectList.indexOf(value);
if (ref === -1) {
throw RangeError('Object not found in table.');
}
return {'#': ref};
}
if (value === undefined) {
return {'Value': 'undefined'};
}
if (typeof value === 'number') {
if (value === Infinity) {
return {'Number': 'Infinity'};
} else if (value === -Infinity) {
return {'Number': '-Infinity'};
} else if (isNaN(value)) {
return {'Number': 'NaN'};
} else if (1 / value === -Infinity) {
return {'Number': '-0'};
}
}
return value;
}
// Shallow-copy all properties of interest onto a root object.
var properties = [
'OBJECT', 'OBJECT_PROTO',
'FUNCTION', 'FUNCTION_PROTO',
'ARRAY', 'ARRAY_PROTO',
'REGEXP', 'REGEXP_PROTO',
'BOOLEAN',
'DATE',
'NUMBER',
'STRING',
'ERROR',
'EVAL_ERROR',
'RANGE_ERROR',
'REFERENCE_ERROR',
'SYNTAX_ERROR',
'TYPE_ERROR',
'URI_ERROR',
'globalScope',
'globalObject',
'stateStack'
];
var root = Object.create(null);
for (var i = 0; i < properties.length; i++) {
root[properties[i]] = interpreter[properties[i]];
}
recordAcornConstructons(interpreter);
// Find all objects.
var objectList = [];
objectHunt_(root, objectList);
// Serialize every object.
var json = [];
for (var i = 0; i < objectList.length; i++) {
var jsonObj = Object.create(null);
json.push(jsonObj);
var obj = objectList[i];
// Uncomment this line for a debugging label.
//jsonObj['#'] = i;
switch (Object.getPrototypeOf(obj)) {
case null:
jsonObj['type'] = 'Map';
break;
case Object.prototype:
if (obj === Interpreter.SCOPE_REFERENCE) {
jsonObj['type'] = 'ScopeReference';
continue; // No need to index properties.
} else {
jsonObj['type'] = 'Object';
}
break;
case Function.prototype:
jsonObj['type'] = 'Function';
jsonObj['id'] = obj.id;
if (obj.id === undefined) {
throw Error('Native function has no ID: ' + obj);
}
continue; // No need to index properties.
case Array.prototype:
// Currently we assume that Arrays are not sparse.
jsonObj['type'] = 'Array';
if (obj.length) {
jsonObj['data'] = obj.map(encodeValue);
}
continue; // No need to index properties.
case Date.prototype:
jsonObj['type'] = 'Date';
jsonObj['data'] = obj.toJSON();
continue; // No need to index properties.
case RegExp.prototype:
jsonObj['type'] = 'RegExp';
jsonObj['source'] = obj.source;
jsonObj['flags'] = obj.flags;
continue; // No need to index properties.
case Interpreter.Object.prototype:
jsonObj['type'] = 'PseudoObject';
break;
case Interpreter.Scope.prototype:
jsonObj['type'] = 'Scope';
break;
case Interpreter.State.prototype:
jsonObj['type'] = 'State';
break;
case NODE_CONSTRUCTOR.prototype:
jsonObj['type'] = 'Node';
break;
default:
throw TypeError('Unknown type: ' + obj);
}
var props = Object.create(null);
var nonConfigurable = [];
var nonEnumerable = [];
var nonWritable = [];
var getter = [];
var setter = [];
var names = Object.getOwnPropertyNames(obj);
for (var j = 0; j < names.length; j++) {
var name = names[j];
if (jsonObj['type'] === 'Node' && name === 'loc') {
// Compactly serialize the location objects on a Node.
var loc = obj.loc;
var locText = '';
if (loc.start) {
locText += loc.start.line + ':' + loc.start.column;
} else {
locText += ':';
}
locText += '-';
if (loc.end) {
locText += loc.end.line + ':' + loc.end.column;
} else {
locText += ':';
}
if (loc.source !== undefined) {
locText += ' ' + encodeURI(loc.source);
}
jsonObj['loc'] = locText;
} else {
var descriptor = Object.getOwnPropertyDescriptor(obj, name);
props[name] = encodeValue(descriptor.value);
if (!descriptor.configurable) {
nonConfigurable.push(name);
}
if (!descriptor.enumerable) {
nonEnumerable.push(name);
}
if (!descriptor.writable) {
nonWritable.push(name);
}
if (descriptor.get) {
getter.push(name);
}
if (descriptor.set) {
setter.push(name);
}
}
}
if (names.length) {
jsonObj['props'] = props;
}
if (nonConfigurable.length) {
jsonObj['nonConfigurable'] = nonConfigurable;
}
if (nonEnumerable.length) {
jsonObj['nonEnumerable'] = nonEnumerable;
}
if (nonWritable.length) {
jsonObj['nonWritable'] = nonWritable;
}
if (getter.length) {
jsonObj['getter'] = getter;
}
if (setter.length) {
jsonObj['setter'] = setter;
}
}
return json;
}
// Recursively search the stack to find all non-primitives.
function objectHunt_(node, objectList) {
if (node && (typeof node === 'object' || typeof node === 'function')) {
if (objectList.indexOf(node) !== -1) {
return;
}
objectList.push(node);
if (typeof node === 'object') { // Recurse.
var isAcornNode = Object.getPrototypeOf(node) === NODE_CONSTRUCTOR.prototype;
var names = Object.getOwnPropertyNames(node);
for (var i = 0; i < names.length; i++) {
if (isAcornNode && names[i] === 'loc') {
continue; // Skip over node locations, they are specially handled.
}
try {
objectHunt_(node[names[i]], objectList);
} catch (e) {
// Accessing some properties may trigger a placeholder getter.
// Squelch this error, but re-throw any others.
if (e.message !== 'Placeholder getter') {
throw e;
}
}
}
}
}
}