refactor infinite loop being triggered
This commit is contained in:
parent
6cecc6ae63
commit
f9d487ebe8
5 changed files with 47 additions and 13 deletions
|
@ -25,8 +25,18 @@ class Console extends React.Component {
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.isPlaying && !this.props.isPlaying) {
|
if (nextProps.isPlaying && !this.props.isPlaying) {
|
||||||
this.children = [];
|
this.children = [];
|
||||||
} else if (nextProps.isExpanded && nextProps.consoleEvent !== this.props.consoleEvent) {
|
} else if (nextProps.consoleEvent !== this.props.consoleEvent) {
|
||||||
const args = nextProps.consoleEvent.arguments;
|
const args = nextProps.consoleEvent.arguments;
|
||||||
|
Object.keys(args).forEach((key) => {
|
||||||
|
if (args[key].includes('Exiting potential infinite loop')) {
|
||||||
|
// stop sketch
|
||||||
|
// dispatch infinite loop found
|
||||||
|
this.props.stopSketch();
|
||||||
|
this.props.detectInfiniteLoops();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (nextProps.isExpanded) {
|
||||||
|
// const args = nextProps.consoleEvent.arguments;
|
||||||
const method = nextProps.consoleEvent.method;
|
const method = nextProps.consoleEvent.method;
|
||||||
const nextChild = (
|
const nextChild = (
|
||||||
<div key={this.children.length} className={`preview-console__${method}`}>
|
<div key={this.children.length} className={`preview-console__${method}`}>
|
||||||
|
@ -36,6 +46,7 @@ class Console extends React.Component {
|
||||||
this.children.push(nextChild);
|
this.children.push(nextChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
return (nextProps.consoleEvent !== this.props.consoleEvent)
|
return (nextProps.consoleEvent !== this.props.consoleEvent)
|
||||||
|
@ -79,7 +90,9 @@ Console.propTypes = {
|
||||||
isPlaying: PropTypes.bool.isRequired,
|
isPlaying: PropTypes.bool.isRequired,
|
||||||
isExpanded: PropTypes.bool.isRequired,
|
isExpanded: PropTypes.bool.isRequired,
|
||||||
collapseConsole: PropTypes.func.isRequired,
|
collapseConsole: PropTypes.func.isRequired,
|
||||||
expandConsole: PropTypes.func.isRequired
|
expandConsole: PropTypes.func.isRequired,
|
||||||
|
stopSketch: PropTypes.func.isRequired,
|
||||||
|
detectInfiniteLoops: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Console;
|
export default Console;
|
||||||
|
|
|
@ -123,6 +123,14 @@ class Editor extends React.Component {
|
||||||
if (this.props.theme !== prevProps.theme) {
|
if (this.props.theme !== prevProps.theme) {
|
||||||
this._cm.setOption('theme', `p5-${this.props.theme}`);
|
this._cm.setOption('theme', `p5-${this.props.theme}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.infiniteLoop && this.props.infiniteLoop !== prevProps.infiniteLoop) {
|
||||||
|
const msg = document.createElement('div');
|
||||||
|
const loopError = 'Loop is taking too long to run. This might be an infinite loop.';
|
||||||
|
msg.appendChild(document.createTextNode(loopError));
|
||||||
|
msg.className = 'lint-error';
|
||||||
|
this.widgets.push(this._cm.addLineWidget(1, msg, { coverGutter: false, noHScroll: true }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
|
|
@ -167,8 +167,6 @@ class PreviewFrame extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
injectLocalFiles() {
|
injectLocalFiles() {
|
||||||
loopProtect.alias = 'protect';
|
|
||||||
|
|
||||||
let htmlFile = this.props.htmlFile.content;
|
let htmlFile = this.props.htmlFile.content;
|
||||||
let scriptOffs = [];
|
let scriptOffs = [];
|
||||||
|
|
||||||
|
@ -196,7 +194,9 @@ class PreviewFrame extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
jsFiles.push(loopProtect(newJSFile));
|
newJSFile.content = loopProtect(newJSFile.content);
|
||||||
|
console.log(newJSFile.content);
|
||||||
|
jsFiles.push(newJSFile);
|
||||||
});
|
});
|
||||||
|
|
||||||
jsFiles.forEach(jsFile => {
|
jsFiles.forEach(jsFile => {
|
||||||
|
@ -212,6 +212,13 @@ class PreviewFrame extends React.Component {
|
||||||
htmlFile = htmlFile.replace(fileRegex, `<style>\n${cssFile.content}\n</style>`);
|
htmlFile = htmlFile.replace(fileRegex, `<style>\n${cssFile.content}\n</style>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const htmlHead = htmlFile.match(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi);
|
||||||
|
const headRegex = new RegExp('head', 'i');
|
||||||
|
let htmlHeadContents = htmlHead[0].split(headRegex)[1];
|
||||||
|
htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2);
|
||||||
|
htmlHeadContents += '<script type="text/javascript" src="/loop-protect.min.js"></script>\n';
|
||||||
|
htmlFile = htmlFile.replace(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi, `<head>\n${htmlHeadContents}\n</head>`);
|
||||||
|
|
||||||
if (this.props.textOutput || this.props.isTextOutputPlaying) {
|
if (this.props.textOutput || this.props.isTextOutputPlaying) {
|
||||||
const htmlHead = htmlFile.match(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi);
|
const htmlHead = htmlFile.match(/(?:<head.*?>)([\s\S]*?)(?:<\/head>)/gmi);
|
||||||
const headRegex = new RegExp('head', 'i');
|
const headRegex = new RegExp('head', 'i');
|
||||||
|
@ -227,11 +234,11 @@ class PreviewFrame extends React.Component {
|
||||||
scriptOffs = getAllScriptOffsets(htmlFile);
|
scriptOffs = getAllScriptOffsets(htmlFile);
|
||||||
htmlFile += hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
|
htmlFile += hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
|
||||||
|
|
||||||
|
|
||||||
return htmlFile;
|
return htmlFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSketch() {
|
renderSketch() {
|
||||||
|
this.props.resetInfiniteLoops();
|
||||||
const doc = ReactDOM.findDOMNode(this);
|
const doc = ReactDOM.findDOMNode(this);
|
||||||
if (this.props.isPlaying && !this.props.infiniteLoop) {
|
if (this.props.isPlaying && !this.props.infiniteLoop) {
|
||||||
srcDoc.set(doc, this.injectLocalFiles());
|
srcDoc.set(doc, this.injectLocalFiles());
|
||||||
|
@ -259,6 +266,7 @@ class PreviewFrame extends React.Component {
|
||||||
role="main"
|
role="main"
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
frameBorder="0"
|
frameBorder="0"
|
||||||
|
ref="iframe"
|
||||||
title="sketch output"
|
title="sketch output"
|
||||||
sandbox="allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-modals allow-forms"
|
sandbox="allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-modals allow-forms"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -292,6 +292,8 @@ class IDEView extends React.Component {
|
||||||
isExpanded={this.props.ide.consoleIsExpanded}
|
isExpanded={this.props.ide.consoleIsExpanded}
|
||||||
expandConsole={this.props.expandConsole}
|
expandConsole={this.props.expandConsole}
|
||||||
collapseConsole={this.props.collapseConsole}
|
collapseConsole={this.props.collapseConsole}
|
||||||
|
stopSketch={this.props.stopSketch}
|
||||||
|
detectInfiniteLoops={this.props.detectInfiniteLoops}
|
||||||
/>
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
<div>
|
<div>
|
||||||
|
@ -325,6 +327,7 @@ class IDEView extends React.Component {
|
||||||
autorefresh={this.props.preferences.autorefresh}
|
autorefresh={this.props.preferences.autorefresh}
|
||||||
previewIsRefreshing={this.props.ide.previewIsRefreshing}
|
previewIsRefreshing={this.props.ide.previewIsRefreshing}
|
||||||
endSketchRefresh={this.props.endSketchRefresh}
|
endSketchRefresh={this.props.endSketchRefresh}
|
||||||
|
stopSketch={this.props.stopSketch}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
|
|
2
static/loop-protect.min.js
vendored
Normal file
2
static/loop-protect.min.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/*! loop-protect | v1.0.1 | (c) 2016 Remy Sharp | http://jsbin.mit-license.org */
|
||||||
|
!function(a,b){"use strict";"object"==typeof exports&&"object"==typeof module?module.exports=b(a):"function"==typeof define&&define.amd?define(b(a)):"object"==typeof exports?module.exports=b(a):a.loopProtect=b(a)}(this,function(a){"use strict";function b(a,b){if(0===a)return!1;var c=a,d=1,e=-1,f=-1;do{if(e=b[c].indexOf("*/"),f=b[c].indexOf("/*"),e!==-1&&d++,e===b[c].length-2&&f!==-1&&d--,f!==-1&&(d--,0===d))return!0;c-=1}while(0!==c);return!1}function c(a,b){for(var c;--a>-1;){if(c=b.substr(a,1),'"'===c||"'"===c||"."===c)return!0;if(("/"===c||"*"===c)&&(--a,"/"===c))return!0}return!1}function d(a,b,c){h.lastIndex=0,i.lastIndex=0;var d=!1,e=c.slice(b).join("\n").substr(a).replace(i,"");return e.replace(h,function(a,b,c){var f=e.substr(0,c).replace(j,"").trim();0===f.length&&(d=!0)}),d}function e(a,e){function f(a,b,c){return b.slice(0,c)+"{;"+m+"({ line: "+a+", reset: true }); "+b.slice(c)}var h=[],j=a.split("\n"),l=!1,m=k.alias+".protect",n={},o={},p=null;return e||(e=0),j.forEach(function(a,k){if(g.lastIndex=0,i.lastIndex=0,!l){a.toLowerCase().indexOf("noprotect")!==-1&&(l=!0);var q=-1,r=-1,s=k,t=k-e+1,u="",v=!1,w=!1,x=!1,y=a.match(g)||[],z=y.length?y[0]:"",A=a.match(i)||[],B=0,C=0,D=!1;if(A.length&&(q=a.indexOf(A[1]),c(q,a)||b(k,j)||d(q,k,j)&&(p=k)),!n[k]){if(o[k])return void h.push(a);if(z&&1===y.length&&a.indexOf("jsbin")===-1){if(v="do"===z,r=q=a.indexOf(z),c(q,a))return void h.push(a);if(b(k,j))return void h.push(a);for(q=a.indexOf(z)+z.length,q===a.length&&q===a.length&&k<j.length-1&&(h.push(a),k++,a=j[k],n[k]=!0,q=0);q<a.length;){if(u=a.substr(q,1),"("===u&&B++,")"===u&&(B--,0===B&&x===!1&&(x=q)),"{"===u&&C++,"}"===u&&C--,0===B&&(";"===u||"{"===u)){if(";"===u)k!==s?(h[s]=h[s].substring(0,x+1)+"{\nif ("+m+"({ line: "+t+" })) break;\n"+h[s].substring(x+1),a+="\n}}\n"):a=a.substring(0,x+1)+"{\nif ("+m+"({ line: "+t+" })) break;\n"+a.substring(x+1)+"\n}}\n",D=!0;else if("{"===u){var E=";\nif ("+m+"({ line: "+t+" })) break;\n";a=a.substring(0,q+1)+E+a.substring(q+1),q+=E.length}if(k===s&&null===p?(a=f(t,a,r),q+=(";"+m+"({ line: "+k+", reset: true }); ").length):null===p?h[s]=f(t,h[s],r):(void 0===h[p]&&(p--,r=0),h[p]=f(t,h[p],r),p=null),v){for(w=!1;q<a.length;){if(u=a.substr(q,1),"{"===u&&C++,"}"===u&&C--,w=0===C,w&&a.indexOf("while")!==-1)return a+="}",h.push(a),void(n[k]=!0);q++,q===a.length&&k<j.length-1&&(h.push(a),n[k]=!0,k++,a=j[k],q=0)}return}if(D)return void h.push(a);for(;null!==a;){if(u=a.substr(q,1),"{"===u&&C++,"}"===u&&(C--,0===C))return a=a.substring(0,q+1)+"}"+a.substring(q+1),h.push(a),void(n[k]=!0);q++,q>=a.length&&(h.push(a),n[k]=!0,k++,a=j[k],q=0)}return}q++,q===a.length&&k<j.length-1&&(h.push(a),k++,a=j[k],n[k]=!0,q=0)}}else h.push(a)}}}),l?a:h.join("\n")}var f=null,g=/\b(for|while|do)\b/g,h=/\b(for|while|do)\b/,i=/\b(?!default:)([a-z_]{1}\w+:)/i,j=/(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm,k=e;return k.counters={},k.debug=function(a){f=a?function(){console.log.apply(console,[].slice.apply(arguments))}:function(){}},k.debug(!1),k.alias="loopProtect",k.protect=function(a){k.counters[a.line]=k.counters[a.line]||{};var b=k.counters[a.line],c=(new Date).getTime();return a.reset&&(b.time=c,b.hit=0,b.last=0),b.hit++,c-b.time>100?(k.hit(a.line),!0):(b.last++,!1)},k.hit=function(b){var c="Exiting potential infinite loop at line "+b+'. To disable loop protection: add "// noprotect" to your code';a.proxyConsole?a.proxyConsole.error(c):console.error(c)},k.reset=function(){k.counters={}},k});
|
Loading…
Reference in a new issue