[feat] basic js run in character

This commit is contained in:
kwaroran
2023-11-08 14:46:15 +09:00
parent 23e0d5f6bc
commit e7ca31ff39
9 changed files with 272 additions and 16 deletions

View File

@@ -0,0 +1,102 @@
import { get } from 'svelte/store'
import type { ScriptMode } from '../process/scripts'
import myWorkerUrl from './embedworker?worker&url'
import { DataBase } from '../storage/database'
import { selectedCharID } from '../stores'
import { cloneDeep } from 'lodash'
let worker = new Worker(new URL(myWorkerUrl), {type: 'module'})
let results:{
id: string,
result: any
}[] = []
let workerFunctions: {
[key:string]: (...args:any[])=> Promise<any>
} = {}
worker.onmessage = ({data}) => {
if(data.type === 'api'){
workerFunctions[data.name](...data.args).then((result)=>{
worker.postMessage({
type: 'result',
id: data.id,
result
})
})
}
else{
results.push(data)
}
}
function addWorkerFunction(name:string, func: (...args:any[])=> Promise<any>){
workerFunctions[name] = func
worker.postMessage({
type: 'api',
name
})
}
function runVirtualJS(code:string){
const id = `id${Math.random()}`.replace('.','')
worker.postMessage({
id,code
})
let startTime = performance.now()
return new Promise((resolve,reject)=>{
const interval = setInterval(()=>{
const result = results.find(r=>r.id === id)
console.log(performance.now() - startTime )
if(result){
clearInterval(interval)
resolve(result.result)
}
else if(performance.now() - startTime > 400){
clearInterval(interval)
//restart worker
worker.terminate()
worker = new Worker(new URL('./worker.ts', import.meta.url), {type: 'module'})
reject('timeout')
}
},100)
})
}
addWorkerFunction('getCharacter', async () => {
const db = get(DataBase)
const selectedChar = get(selectedCharID)
return cloneDeep(db.characters[selectedChar])
})
export async function runCharacterJS(arg:{
code: string,
mode: ScriptMode
data: string
}):Promise<string>{
try {
const codes = {
"editinput": 'editInput',
"editoutput": 'editOutput',
"editprocess": 'editProcess',
"editdisplay": 'editDisplay',
} as const
const runCode = codes[arg.mode]
const result = await runVirtualJS(`${arg.code}\n${runCode}(${JSON.stringify(arg.data)})`)
if(!result){
return arg.data
}
return result.toString()
} catch (error) {
if(arg.mode !== 'editprocess'){
return `Error: ${error}`
}
return arg.data
}
}

View File

@@ -0,0 +1,142 @@
let globaly = globalThis
const whitelist = [
"Array",
"ArrayBuffer",
"BigInt",
"BigInt64Array",
"BigUint64Array",
"Boolean",
"DataView",
"Date",
"Error",
"EvalError",
"Float32Array",
"Float64Array",
"Function",
"Infinity",
"Int16Array",
"Int32Array",
"Int8Array",
"JSON",
"Map",
"Math",
"NaN",
"Number",
"Object",
"Promise",
"Proxy",
"RangeError",
"ReferenceError",
"Reflect",
"RegExp",
"Set",
"SharedArrayBuffer",
"String",
"Symbol",
"SyntaxError",
"TypeError",
"URIError",
"Uint16Array",
"Uint32Array",
"Uint8Array",
"Uint8ClampedArray",
"WeakMap",
"WeakSet",
"WebAssembly",
"console",
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent",
"escape",
"globalThis",
"isFinite",
"isNaN",
"null",
"parseFloat",
"parseInt",
"undefined",
"unescape",
"queueMicrotask",
"setTimeout",
"clearTimeout",
"setInterval",
"clearInterval",
"setImmediate",
"clearImmediate",
"atob",
"btoa",
"Headers",
"Request",
"Response",
"Blob",
"postMessage"
]
const evaluation = global.eval
Object.getOwnPropertyNames( global ).forEach( function( prop ) {
if( !whitelist.includes(prop) ) {
Object.defineProperty( global, prop, {
get : function() {
throw "Security Exception: cannot access "+prop;
return 1;
},
configurable : false
});
}
});
let workerResults:{
id: string,
result: any
}[] = []
self.onmessage = async (event) => {
const da = event.data
if(da.type === 'result'){
workerResults.push(da)
return
}
if(da.type === 'api'){
//add api
Object.defineProperty( global, da.name, {
get : function() {
return function (...args:any[]) {
return new Promise((resolve)=>{
const functionCallID = `id${Math.random()}`.replace('.','')
self.postMessage({
type: 'api',
name: da.name,
id: functionCallID,
args
})
const interval = setInterval(()=>{
const result = workerResults.find(r=>r.id === functionCallID)
if(result){
clearInterval(interval)
resolve(result.result)
}
},100)
})
}
}
});
return
}
try{
const d = await evaluation(da.code)
self.postMessage({
id: da.id,
result: d
})
}
catch(e){
console.error(e)
self.postMessage({
id: da.id,
result: e
})
}
}