threaded.module.min.js
Version:
JavaScript with threads !
1 lines • 20.7 kB
JavaScript
class ThreadedTools{}ThreadedTools.acorn=acorn,ThreadedTools.walk=acorn.walk,ThreadedTools.escodegen=escodegen;class Thread{constructor(e,r,t){if(void 0===Thread.count&&(Thread.count=0),Thread.count++,this.__generatorFunc__=ThreadExecutor.__generatorFunc__(e),this.id=void 0===t?"anonymous thread n-"+Thread.count:t+" (thread n-"+Thread.count+")",void 0===ThreadExecutor.queue&&(ThreadExecutor.queue=[]),this.__sleepingpriority__=!1,this.__resumingpriority__=!1,this.__stepscount__=0,void 0!==r){if("number"!=typeof r)throw new ThreadError("thread priority level must be a valid number",this);if(!Number.isInteger(r))throw new ThreadError("thread priority level must be an integer number",this);if(r<0)throw new ThreadError("thread priority level must be a positive number",this);this.prioritylevel=r}else this.prioritylevel=(r??1)|1;this.__args__=[],this.__result__=null,this.__errorSilently__=!1,this.done=!1,this.started=!1,this.running=!1,this.stopped=!1,this.paused=!1}static innerThreadFor(e,r,t,n){void 0===Thread.__sharedinnerthreads__&&(Thread.__sharedinnerthreads__=new Map),Thread.__sharedinnerthreads__.has(e)||Thread.__sharedinnerthreads__.set(e,new Map),Thread.__sharedinnerthreads__.get(e).has(r)||Thread.__sharedinnerthreads__.get(e).set(r,new Thread(r,t,n));let a=Thread.__sharedinnerthreads__.get(e).get(r);return void 0!==t&&a.setPriorityLevel(t),void 0!==n&&a.setId(n),a}setId(e){return this.id=null==e?"anonymous thread n-"+Thread.count:e+" (thread n-"+Thread.count+")",this}setArgs(...e){return this.__args__=e,this}errorSilently(e){return null==e&&(this.__errorSilently__=!1),this.__errorSilently__=e,this}toString(){return this.id}stepsCount(){return this.__stepscount__}setPriorityLevel(e){if("number"!=typeof e)throw new ThreadError("thread priority level must be a valid number",this);if(!Number.isInteger(e))throw new ThreadError("thread priority level must be an integer number",this);if(e<0)throw new ThreadError("thread priority level must be a positive number",this);return this.prioritylevel=e,ThreadExecutor.notifyForPriority(),this}start(){return this.__generator__=void 0,this.__stepscount__=0,this.__sleepingpriority__=!1,this.__resumingpriority__=!1,this.__result__=null,this.done=!1,this.started=!0,this.paused=!1,this.stopped=!1,this.running=!0,ThreadExecutor.__timeSpentOutsideThreads__=void 0,ThreadExecutor.queue.push(this),ThreadExecutor.isHandling()||ThreadExecutor.handleThreadQueue(),this}startAfter(e){return this.paused||this.pause(),setTimeout(()=>this.start(),e),this}interrupt(){return this.stop()}static interrupt(){let e=ThreadExecutor.currentThread;if(null==e)throw new ThreadError("Thread.interrupt called outside a thread environment");e.interrupt()}interruptAfter(e){return this.paused||this.pause(),setTimeout(()=>this.interrupt(),e),this}static interruptAfter(e){let r=ThreadExecutor.currentThread;if(null==r)throw new ThreadError("Thread.interruptAfter called outside a thread environment");r.interruptAfter(e)}stop(){if(!this.started)return console.error("Thread not started yet"),this;if(this.stopped)return console.error("Thread already stopped"),this;this.stopped=!0,this.running=!1;let e=ThreadExecutor.queue.indexOf(this);if(-1===e)throw new ThreadError('Cannot stop thread "'+sleptThread.id+", the thread is not in the execution queue");return ThreadExecutor.queue.splice(e,1),this}static stop(){let e=ThreadExecutor.currentThread;if(null==e)throw new ThreadError("Thread.stop called outside a thread environment");e.stop()}stopAfter(e){return setTimeout(()=>this.stop(),e),this}static stopAfter(e){let r=ThreadExecutor.currentThread;if(null==r)throw new ThreadError("Thread.stopAfter called outside a thread environment");r.stopAfter(e)}pause(){if(this.paused){console.error("Thread already paused");return}return this.paused=!0,this}static pause(){let e=ThreadExecutor.currentThread;if(null==e)throw new ThreadError("Thread.pause called outside a thread environment");e.pause()}pauseAfter(e){return setTimeout(()=>this.pause(),e),this}static pauseAfter(e){let r=ThreadExecutor.currentThread;if(null==r)throw new ThreadError("Thread.pauseAfter called outside a thread environment");r.pauseAfter(e)}resume(){if(!this.paused){console.error("Thread is not paused");return}let e=ThreadExecutor.queue.indexOf(this);if(-1===e)throw new ThreadError('Cannot resume thread "'+sleptThread.id+", the thread is not in the execution queue");return this.paused=!1,this.__resumingpriority__=!0,ThreadExecutor.notifyForResuming(this,e),this}static resume(){let e=ThreadExecutor.currentThread;if(null==e)throw new ThreadError("Thread.resume called outside a thread environment");e.resume()}resumeAfter(e){return setTimeout(()=>this.resume(),e),this}static resumeAfter(e){let r=ThreadExecutor.currentThread;if(null==r)throw new ThreadError("Thread.resumeAfter called outside a thread environment");r.resumeAfter(e)}sleep(e){if(!this.started)throw new ThreadError('Cannot sleep thread "'+this.id+", the thread is not started yet");if(this.stopped)throw new ThreadError('Cannot sleep thread "'+this.id+", the thread is stopped");if(!this.running)throw new ThreadError('Cannot sleep thread "'+this.id+", the thread is not running");if(this.paused)throw new ThreadError('Cannot sleep thread "'+this.id+", the thread is paused");let r=ThreadExecutor.queue.indexOf(this);if(-1===r)throw new ThreadError('Cannot sleep thread "'+this.id+", the thread is not in the execution queue");return this.sleeping=!0,this.__sleepingpriority__=!0,setTimeout(()=>ThreadExecutor.notifyForSleeping(this,r),e),this}static sleep(e){let r=ThreadExecutor.currentThread;if(null==r)throw new ThreadError("Thread.sleep called outside a thread environment");r.sleep(e)}sleepAfter(e){return setTimeout(()=>this.sleep(),e),this}static sleepAfter(e){let r=ThreadExecutor.currentThread;if(null==r)throw new ThreadError("Thread.sleepAfter called outside a thread environment");r.sleepAfter(e)}catch(e){return this.__exceptionFunc__=e,this}result(){return this.__result__}}Thread.LOW_PRIORITY_LEVEL=1,Thread.MID_PRIORITY_LEVEL=2,Thread.HIGH_PRIORITY_LEVEL=3,Thread.innerfunctionsisolation=!1;class ThreadExecutor{static handleThreadQueue(){void 0!==ThreadExecutor.queue&&0!==ThreadExecutor.queue.length&&(ThreadExecutor.__isLooping__=!0,ThreadExecutor.__isHandling__=!1,ThreadExecutor.__timeSpentOutsideThreads__=void 0,ThreadExecutor.queueIndex=0,ThreadExecutor.handlerLoop(!1))}static handlerLoop(e){if(void 0!==ThreadExecutor.__timeSpentOutsideThreads__?ThreadExecutor.__timeSpentOutsideThreads__=Date.now()-ThreadExecutor.__timeSpentOutsideThreads__:ThreadExecutor.__timeSpentOutsideThreads__=10,ThreadExecutor.__timeSpentOutsideThreads__=Math.min(ThreadExecutor.__timeSpentOutsideThreads__,20),!e&&ThreadExecutor.__isHandling__)return;let r=ThreadExecutor.queue[ThreadExecutor.queueIndex];if(null!=r){if(r.sleeping||!r.started||r.stopped||!r.running||r.paused)ThreadExecutor.queueIndex++,ThreadExecutor.queueIndex=ThreadExecutor.queueIndex%ThreadExecutor.queue.length;else{ThreadExecutor.__isHandling__=!0,ThreadExecutor.currentThread=r,r.__sleepingpriority__=!1,r.__resumingpriority__=!1,void 0===r.__generator__&&(r.__generator__=r.__generatorFunc__(...r.__args__));let t=r.__generator__.next();r.__stepscount__="number"==typeof t.value?t.value:r.__stepscount__,t instanceof Error||!0!==t.done?(ThreadExecutor.queueIndex++,ThreadExecutor.queueIndex=ThreadExecutor.queueIndex%ThreadExecutor.queue.length):(ThreadExecutor.queue.splice(ThreadExecutor.queueIndex,1),r.__result__=t.value,r.done=!0,r.stopped=!0,r.running=!1,r.__sleepingpriority__=!1,r.__resumingpriority__=!1,ThreadExecutor.queueIndex>=ThreadExecutor.queue.length&&(ThreadExecutor.queueIndex=0)),ThreadExecutor.__isHandling__=!1}}0!==ThreadExecutor.queue.length?(setTimeout(()=>ThreadExecutor.handlerLoop(!1),void 0===ThreadExecutor.__loopingbeattime__||ThreadExecutor.__loopingbeattime__===ThreadExecutor.ADAPTIVE?ThreadExecutor.__timeSpentOutsideThreads__:ThreadExecutor.__loopingbeattime__),ThreadExecutor.__timeSpentOutsideThreads__=Date.now()):ThreadExecutor.__isLooping__=!1}static isHandling(){return void 0!==ThreadExecutor.__isHandling__&&ThreadExecutor.__isHandling__}static isLooping(){return void 0!==ThreadExecutor.__isLooping__&&ThreadExecutor.__isLooping__}static setBeatTime(e){ThreadExecutor.__loopingbeattime__=e}static notifyForSleeping(e,r){for(let t=0;t<ThreadExecutor.queue.length;t++){let n=ThreadExecutor.queue[t];if(!n.__sleepingpriority__){ThreadExecutor.queue.splice(r,1),ThreadExecutor.queue.splice(t,0,e);break}}e.sleeping=!1,ThreadExecutor.__timeSpentOutsideThreads__=void 0,ThreadExecutor.isLooping()?ThreadExecutor.handlerLoop(!0):ThreadExecutor.handleThreadQueue()}static notifyForResuming(e,r){for(let t=0;t<ThreadExecutor.queue.length;t++){let n=ThreadExecutor.queue[t];if(!n.__sleepingpriority__&&!n.__resumingpriority__){ThreadExecutor.queue.splice(r,1),ThreadExecutor.queue.splice(t,0,e);break}}ThreadExecutor.__timeSpentOutsideThreads__=void 0,ThreadExecutor.isLooping()?ThreadExecutor.handlerLoop(!0):ThreadExecutor.handleThreadQueue()}static notifyForPriority(){let e=ThreadExecutor.queue.length;for(let r=0;r<ThreadExecutor.queue.length;r++){let t=ThreadExecutor.queue[r];if(!t.__sleepingpriority__&&!t.__resumingpriority__){e=r;break}}ThreadExecutor.queue.length-e!=0&&(ThreadExecutor.queue=ThreadExecutor.queue.slice(0,e).concat(ThreadExecutor.queue.slice(e,ThreadExecutor.queue.length).sort((e,r)=>((e.prioritylevel??1)|1)-((r.prioritylevel??1)|1))),ThreadExecutor.__timeSpentOutsideThreads__=void 0,ThreadExecutor.isLooping()?ThreadExecutor.handlerLoop(!0):ThreadExecutor.handleThreadQueue())}static catch(e){ThreadExecutor.__globalExceptionFunc__=e}static __threadExceptionOccurred__(e){if(e instanceof TypeError&&"Generator is already running"===e.message)return;e=new ThreadError(e,ThreadExecutor.currentThread);let r=!1;void 0!==ThreadExecutor.__globalExceptionFunc__&&null!==ThreadExecutor.__globalExceptionFunc__?ThreadExecutor.currentThread.__errorSilently__||ThreadExecutor.__globalExceptionFunc__(e,ThreadExecutor.currentThread):r=!0,void 0!==ThreadExecutor.currentThread&&null!==ThreadExecutor.currentThread&&(void 0===ThreadExecutor.currentThread.__threadgroup__||null===ThreadExecutor.currentThread.__threadgroup__||void 0===ThreadExecutor.currentThread.__threadgroup__.__exceptionFunc__||null===ThreadExecutor.currentThread.__threadgroup__.__exceptionFunc__||(r=!1,ThreadExecutor.currentThread.__errorSilently__||ThreadExecutor.currentThread.__threadgroup__.__exceptionFunc__(e,ThreadExecutor.currentThread)),void 0!==ThreadExecutor.currentThread.__exceptionFunc__&&null!==ThreadExecutor.currentThread.__exceptionFunc__&&(r=!1,ThreadExecutor.currentThread.__exceptionFunc__(e)),r&&!ThreadExecutor.currentThread.__errorSilently__&&console.error(e))}static __generatorFunc__(func){let functionSource=`(${func.toString()})`;if(null===functionSource)throw new ThreadError("Failed to retrieve the function source code");if(ThreadExecutor.__isNativeFunction__(func))throw new ThreadError("Can't execute native function \""+functionSource+'", the thread function has to be a normal function, try to wrap the function into a normal function instead...');let ast=ThreadedTools.acorn.parse(functionSource,{ecmaVersion:2024}),alreadyAGeneratorFunction;"function"==typeof func&&func.constructor&&"GeneratorFunction"===func.constructor.name||ThreadedTools.walk.full(ast,node=>{let functionId=0;if("BlockStatement"===node.type){let newBody=[];for(let i=0;i<node.body.length;i++){let stmt=node.body[i];if(!0===Thread.innerfunctionsisolation&&("VariableDeclaration"===stmt.type||"ExpressionStatement"===stmt.type)){let originalVarName="VariableDeclaration"===stmt.type?stmt.declarations[0].id.name:null,callExpr="VariableDeclaration"===stmt.type?stmt.declarations[0].init:stmt.expression,functionCallee=null,isNativeOrThreadFunction=!1,dontIsolateToThread=!1;if("ArrowFunctionExpression"===callExpr.type){for(let declarator of stmt.declarations)if("VariableDeclarator"===declarator.type&&"Identifier"===declarator.id.type){let varName=declarator.id.name;functionCallee={type:"Identifier",name:varName}}isNativeOrThreadFunction=ThreadExecutor.__isNativeOrThreadFunction__(eval("("+escodegen.generate(callExpr)+")".replaceAll("this","Thread"))),dontIsolateToThread=!0}else{if(void 0!==(functionCallee=callExpr.callee)&&null!==functionCallee)try{isNativeOrThreadFunction=ThreadExecutor.__isNativeOrThreadFunction__(eval(escodegen.generate(functionCallee).replaceAll("this","Thread")))}catch(ex){if(!(ex instanceof ReferenceError))throw ex}dontIsolateToThread="CallExpression"!==callExpr.type}if(isNativeOrThreadFunction||dontIsolateToThread){newBody.push(stmt),newBody.push({type:"ExpressionStatement",expression:{type:"YieldExpression",argument:{type:"UpdateExpression",operator:"++",argument:{type:"Identifier",name:"__thefunctionstepscount__"},prefix:!1}}});continue}if(!dontIsolateToThread){let threadVar=`__innerfunctionexecutor${++functionId}__`,tempVarName=`__innerfunctionexecutionresult${functionId}__`;newBody.push({type:"VariableDeclaration",kind:"const",declarations:[{type:"VariableDeclarator",id:{type:"Identifier",name:threadVar},init:{type:"CallExpression",callee:{type:"MemberExpression",object:{type:"CallExpression",callee:{type:"MemberExpression",object:{type:"CallExpression",callee:{type:"MemberExpression",object:{type:"CallExpression",callee:{type:"MemberExpression",object:{type:"CallExpression",callee:{type:"MemberExpression",object:{type:"Identifier",name:"Thread"},property:{type:"Identifier",name:"innerThreadFor"},computed:!1,optional:!1},arguments:[{type:"ThisExpression"},functionCallee]},property:{type:"Identifier",name:"setId"},computed:!1,optional:!1},arguments:[{type:"BinaryExpression",operator:"+",left:{type:"MemberExpression",object:{type:"ThisExpression"},property:{type:"Identifier",name:"id"},computed:!1,optional:!1},right:{type:"Literal",value:"'s inner thread",raw:"'s inner thread"}}]},property:{type:"Identifier",name:"setArgs"},computed:!1,optional:!1},arguments:callExpr.arguments},property:{type:"Identifier",name:"errorSilently"},computed:!1,optional:!1},arguments:[{type:"Literal",value:!0,raw:"true"}]},property:{type:"Identifier",name:"start"},computed:!1,optional:!1},arguments:[]}}]},{type:"WhileStatement",test:{type:"MemberExpression",object:{type:"Identifier",name:threadVar},property:{type:"Identifier",name:"running"}},body:{type:"BlockStatement",body:[{type:"ExpressionStatement",expression:{type:"YieldExpression",argument:{type:"Identifier",name:"__thefunctionstepscount__"}}}]}},{type:"VariableDeclaration",kind:"let",declarations:[{type:"VariableDeclarator",id:{type:"Identifier",name:null!==originalVarName?originalVarName:tempVarName},init:{type:"CallExpression",callee:{type:"MemberExpression",object:{type:"Identifier",name:threadVar},property:{type:"Identifier",name:"result"}},arguments:[]}}]},{type:"ExpressionStatement",expression:{type:"YieldExpression",argument:{type:"UpdateExpression",operator:"++",argument:{type:"Identifier",name:"__thefunctionstepscount__"},prefix:!1}}},{type:"IfStatement",test:{type:"BinaryExpression",operator:"instanceof",left:{type:"Identifier",name:null!==originalVarName?originalVarName:tempVarName},right:{type:"Identifier",name:"Error"}},consequent:{type:"BlockStatement",body:[{type:"ThrowStatement",argument:{type:"NewExpression",callee:{type:"Identifier",name:"ThreadError"},arguments:[{type:"Identifier",name:null!==originalVarName?originalVarName:tempVarName},{type:"VariableDeclarator",id:{type:"Identifier",name:threadVar}}]}}]},alternate:null})}}else newBody.push(stmt),"ReturnStatement"!==stmt.type&&"ContinueStatement"!==stmt.type&&"BreakStatement"!==stmt.type&&"ThrowStatement"!==stmt.type&&newBody.push({type:"ExpressionStatement",expression:{type:"YieldExpression",argument:{type:"UpdateExpression",operator:"++",argument:{type:"Identifier",name:"__thefunctionstepscount__"},prefix:!1}}})}return node.body=newBody,node}}),ThreadedTools.walk.simple(ast,{FunctionDeclaration(e){ThreadExecutor.__wrapFunctionBodyInTryCatch__(e,!e.generator),e.generator=!0},FunctionExpression(e){ThreadExecutor.__wrapFunctionBodyInTryCatch__(e,!e.generator),e.generator=!0},ArrowFunctionExpression(e){"BlockStatement"!==e.body.type&&ThreadExecutor.__wrapArrowFunctionInBlockStatement__(e),ThreadExecutor.__wrapFunctionBodyInTryCatch__(e,!e.generator),e.type="FunctionExpression",e.generator=!0,e.expression=!1}});let newCode,generatorFunc;return eval(ThreadedTools.escodegen.generate(ast))}static __isNativeFunction__(e){return void 0!==e&&"function"==typeof e&&/\{\s*\[native code\]\s*\}/.test(e.toString())}static __isNativeOrThreadFunction__(e){return void 0!==e&&(ThreadExecutor.__isNativeFunction__(e)||Thread.prototype.hasOwnProperty(e.name))}static __wrapArrowFunctionInBlockStatement__(e){e.body,e.expression&&(e.body={type:"BlockStatement",body:[{type:"ReturnStatement",argument:e.body}]},e.expression=!1)}static __wrapFunctionBodyInTryCatch__(e,r){let t=e.body.body;e.body.body=[{type:"TryStatement",block:{type:"BlockStatement",body:[!0===r?{type:"VariableDeclaration",declarations:[{type:"VariableDeclarator",id:{type:"Identifier",name:"__thefunctionstepscount__"},init:{type:"Literal",value:0}}],kind:"let"}:{type:"EmptyStatement"},...t]},handler:{type:"CatchClause",param:{type:"Identifier",name:"ex"},body:{type:"BlockStatement",body:[{type:"ExpressionStatement",expression:{type:"CallExpression",callee:{type:"MemberExpression",object:{type:"Identifier",name:"ThreadExecutor"},property:{type:"Identifier",name:"__threadExceptionOccurred__"}},arguments:[{type:"Identifier",name:"ex"}]}},{type:"ReturnStatement",argument:{type:"Identifier",name:"ex"}}]}},finalizer:null}]}}ThreadExecutor.ADAPTIVE=-1;class ThreadGroup{constructor(...e){for(let r of(void 0===ThreadGroup.count&&(ThreadGroup.count=0),ThreadGroup.count++,this.id="anonymous threadgroup n-"+ThreadGroup.count,null==e?this.threads=[]:this.threads=e,this.threads))null!=r&&(r.__threadgroup__=this)}setId(e){return this.id=null==e?"anonymous threadgroup n-"+ThreadGroup.count:e+" (threadgroup n-"+ThreadGroup.count+")",this}add(e){if(null==e)throw new ThreadError("Given thread in threadgroup add function is undefined or null",this);return this.threads.push(e),e.__threadgroup__=this,this}remove(e){if(null==e)return;let r=this.threads.indexOf(e);if(-1===r)throw new ThreadError('Cannot remove thread "'+e.id+" from threadgroup, the thread is not added to that threadgroup");return this.threads.splice(r,1),e.__threadgroup__=void 0,this}stepsCount(){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);let e=0;for(let r of this.threads)null!=r&&(e+=r.stepsCount());return e}start(){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);for(let e of this.threads)null!=e&&e.start();return this}startAfter(e){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);return this.pause(),setTimeout(()=>this.start(),e),this}interrupt(){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);return this.stop()}interruptAfter(e){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);return setTimeout(()=>this.interrupt(),e),this}stop(){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);for(let e of this.threads)null!=e&&e.stop();return this}stopAfter(e){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);return setTimeout(()=>this.stop(),e),this}pause(){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);for(let e of this.threads)null==e||e.paused||e.pause();return this}pauseAfter(e){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);return setTimeout(()=>this.pause(),e),this}resume(){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);for(let e of this.threads)null!=e&&e.resume();return this}resumeAfter(e){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);return setTimeout(()=>this.resume(),e),this}sleep(e){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);for(let r of this.threads)null!=r&&r.sleep(e);return this}sleepAfter(e){if(0==this.threads.length)throw new ThreadError("threadgroup has no threads",this);return setTimeout(()=>this.sleep(),e),this}catch(e){return this.__exceptionFunc__=e,this}}class ThreadError extends Error{constructor(e,r){super((r instanceof Thread?"error in thread ":"error in threadgroup ")+'"'+r.id+'", details : '+e,e instanceof Error?e:void 0)}}try{module.exports={Thread,ThreadGroup,ThreadExecutor,ThreadError}}catch(ex){}