From f9d487ebe8b74a5bbdbfeab90f32afec85b20b14 Mon Sep 17 00:00:00 2001 From: Cassie Tarakajian Date: Thu, 6 Oct 2016 13:01:48 -0400 Subject: [PATCH] refactor infinite loop being triggered --- client/modules/IDE/components/Console.js | 31 +++++++++++++------ client/modules/IDE/components/Editor.js | 8 +++++ client/modules/IDE/components/PreviewFrame.js | 16 +++++++--- client/modules/IDE/pages/IDEView.js | 3 ++ static/loop-protect.min.js | 2 ++ 5 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 static/loop-protect.min.js diff --git a/client/modules/IDE/components/Console.js b/client/modules/IDE/components/Console.js index 177f53cc..07cd4633 100644 --- a/client/modules/IDE/components/Console.js +++ b/client/modules/IDE/components/Console.js @@ -25,15 +25,26 @@ class Console extends React.Component { componentWillReceiveProps(nextProps) { if (nextProps.isPlaying && !this.props.isPlaying) { this.children = []; - } else if (nextProps.isExpanded && nextProps.consoleEvent !== this.props.consoleEvent) { + } else if (nextProps.consoleEvent !== this.props.consoleEvent) { const args = nextProps.consoleEvent.arguments; - const method = nextProps.consoleEvent.method; - const nextChild = ( -
- {Object.keys(args).map((key) => {args[key]})} -
- ); - this.children.push(nextChild); + 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 nextChild = ( +
+ {Object.keys(args).map((key) => {args[key]})} +
+ ); + this.children.push(nextChild); + } } } @@ -79,7 +90,9 @@ Console.propTypes = { isPlaying: PropTypes.bool.isRequired, isExpanded: PropTypes.bool.isRequired, collapseConsole: PropTypes.func.isRequired, - expandConsole: PropTypes.func.isRequired + expandConsole: PropTypes.func.isRequired, + stopSketch: PropTypes.func.isRequired, + detectInfiniteLoops: PropTypes.func.isRequired }; export default Console; diff --git a/client/modules/IDE/components/Editor.js b/client/modules/IDE/components/Editor.js index 1972aa15..0dc1b7ea 100644 --- a/client/modules/IDE/components/Editor.js +++ b/client/modules/IDE/components/Editor.js @@ -123,6 +123,14 @@ class Editor extends React.Component { if (this.props.theme !== prevProps.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() { diff --git a/client/modules/IDE/components/PreviewFrame.js b/client/modules/IDE/components/PreviewFrame.js index 9cf0fbf0..22f46fdd 100644 --- a/client/modules/IDE/components/PreviewFrame.js +++ b/client/modules/IDE/components/PreviewFrame.js @@ -167,8 +167,6 @@ class PreviewFrame extends React.Component { } injectLocalFiles() { - loopProtect.alias = 'protect'; - let htmlFile = this.props.htmlFile.content; 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 => { @@ -212,6 +212,13 @@ class PreviewFrame extends React.Component { htmlFile = htmlFile.replace(fileRegex, ``); }); + const htmlHead = htmlFile.match(/(?:)([\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 += '\n'; + htmlFile = htmlFile.replace(/(?:)([\s\S]*?)(?:<\/head>)/gmi, `\n${htmlHeadContents}\n`); + if (this.props.textOutput || this.props.isTextOutputPlaying) { const htmlHead = htmlFile.match(/(?:)([\s\S]*?)(?:<\/head>)/gmi); const headRegex = new RegExp('head', 'i'); @@ -227,11 +234,11 @@ class PreviewFrame extends React.Component { scriptOffs = getAllScriptOffsets(htmlFile); htmlFile += hijackConsoleErrorsScript(JSON.stringify(scriptOffs)); - return htmlFile; } renderSketch() { + this.props.resetInfiniteLoops(); const doc = ReactDOM.findDOMNode(this); if (this.props.isPlaying && !this.props.infiniteLoop) { srcDoc.set(doc, this.injectLocalFiles()); @@ -259,6 +266,7 @@ class PreviewFrame extends React.Component { role="main" tabIndex="0" frameBorder="0" + ref="iframe" title="sketch output" sandbox="allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-modals allow-forms" /> diff --git a/client/modules/IDE/pages/IDEView.js b/client/modules/IDE/pages/IDEView.js index 63253a79..299da237 100644 --- a/client/modules/IDE/pages/IDEView.js +++ b/client/modules/IDE/pages/IDEView.js @@ -292,6 +292,8 @@ class IDEView extends React.Component { isExpanded={this.props.ide.consoleIsExpanded} expandConsole={this.props.expandConsole} collapseConsole={this.props.collapseConsole} + stopSketch={this.props.stopSketch} + detectInfiniteLoops={this.props.detectInfiniteLoops} />
@@ -325,6 +327,7 @@ class IDEView extends React.Component { autorefresh={this.props.preferences.autorefresh} previewIsRefreshing={this.props.ide.previewIsRefreshing} endSketchRefresh={this.props.endSketchRefresh} + stopSketch={this.props.stopSketch} />
diff --git a/static/loop-protect.min.js b/static/loop-protect.min.js new file mode 100644 index 00000000..82dbef3b --- /dev/null +++ b/static/loop-protect.min.js @@ -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=a.length&&(h.push(a),n[k]=!0,k++,a=j[k],q=0)}return}q++,q===a.length&&k100?(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}); \ No newline at end of file