UNPKG

@duzc2-openblock/openblock-runtime

Version:

openblock runtime for browser and nodejs

1,491 lines (1,457 loc) 199 kB
/** * @license * Copyright 2021 Du Tian Wei * SPDX-License-Identifier: Apache-2.0 */ "use strict" import * as util from './util.mjs' //const util = require('./util.mjs'); class NativeUtil { static fieldSetter(target, fieldName, register) { return (builder, args) => { let getter = builder[register](args[1] & 0xfff); builder.PushAction((st, f, local, pos) => { let v = getter(st, f, local); target[fieldName] = v; return 1 + pos; }); }; } static fieldGetter(target, fieldName, register) { return (builder, args) => { builder[register](args[0] & 0xfff, (st, f, local) => { return target[fieldName]; }); }; } static objFieldSetter(fieldName, fieldRegister) { return (builder, args) => { let obj = builder.NObjectRegister(args[1] & 0xfff); let setingV = builder[fieldRegister](args[2] & 0xfff); builder.PushAction((st, f, local, pos) => { let o = obj(st, f, local); if (o) { let v = setingV(st, f, local); o[fieldName] = v; } return 1 + pos; }); }; } static objFieldGetter(fieldName, fieldRegister, defaultValue) { return (builder, args) => { let obj = builder.NObjectRegister(args[1] & 0xfff); builder[fieldRegister](args[0] & 0xfff, (st, f, local) => { let o = obj(st, f, local); if (o) { return o[fieldName]; } else { return defaultValue; } }); }; } static objMethodInvoke(type, methodName, valueRegister, argtype, defaultValue, addVMarg) { argtype.unshift('NObjectRegister'); return (builder, args) => { let ret = args.shift() & 0xfff; let argGetters = argtype.map((rtype, idx) => { let idx1 = args[idx] & 0xFFF; let v = builder[rtype](idx1); if (typeof (v) != 'function') { debugger } return (st, f, local) => { return v(st, f, local); }; }); if (valueRegister) { builder[valueRegister]((ret), (st, f, local) => { let argVals = argGetters.map(g => g(st, f, local)); if (addVMarg) { argVals.push(st); argVals.push(f); argVals.push(local); } let o = argVals.shift(); if (o instanceof type) { return o[methodName].apply(o, argVals); } else { return defaultValue; } }); } else { builder.PushAction((st, f, local, pos) => { let argVals = argGetters.map(g => g(st, f, local)); if (addVMarg) { argVals.push(st); argVals.push(f); argVals.push(local); argVals.push(pos); } let o = argVals.shift(); if (o instanceof type) { o[methodName].apply(argVals); } return pos + 1; }); } }; } /** * * @param {Function} func * @param {Number[]} argtype * @param {Boolean} addVMarg * @returns */ static closureVoid(func, argtype, addVMarg) { /** * * @param {OBFunctionBuilder} builder * @param {Number[]} args * @returns */ let f = (builder, args) => { args = args.slice(1); let argGetters = argtype.map((rtype, idx) => { let idx1 = args[idx] & 0xFFF; let v = builder[rtype](idx1); if (typeof (v) != 'function') { debugger } return (st, f, local) => { return v(st, f, local); }; }); builder.PushAction((st, f, local, pos) => { let argVals = argGetters.map(g => g(st, f, local)); if (addVMarg) { argVals.push(st); argVals.push(f); argVals.push(local); argVals.push(pos); } func.apply(null, argVals); return pos + 1; }); }; return f; } /** * * @param {Function} func * @param {Number[]} argtype * @returns */ static closureReturnValue(func, retRegisterType, argtype, addVMarg) { /** * * @param {OBFunctionBuilder} builder * @param {Number[]} args * @returns */ let f = (builder, args) => { let retRegIdx = args[0]; let retType = (retRegIdx & 0xF000) >> 12; retRegIdx = retRegIdx & 0xFFF; args = args.slice(1); let argGetters = argtype.map((rtype, idx) => { let idx1 = args[idx] & 0xFFF; let v = builder[rtype](idx1); return (st, f, local) => { return v(st, f, local); }; }); builder[retRegisterType](retRegIdx, (st, f, local) => { let argVals = argGetters.map(g => g(st, f, local)); if (addVMarg) { argVals.push(st); argVals.push(f); argVals.push(local); } return func.apply(null, argVals); }); }; return f; } } class OBScript { constructor() { this.NativeLibHash = {}; // libname->hash this.InstalledLibs = {}; this.NativeUtil = NativeUtil; /** * @type {Object.<string,StructData>} */ this.StructData = {}; //typename->StructData /** * @type {Object.<string,OBStructDef>} */ this.StructDef = {}; // typename-> def this.loadedFunctions; //= {};//function sign->function this.FullNameFSMData = {}; //FullName->OBFSM } /** * @callback FuncInstaller * @param {OBFunctionBuilder} funcBuilder * @param {number[]} registersConfig */ /** * 安装本地库 * @param {string} libName * @param {string} jsmd5 md5 of js generated config * @param {FuncInstaller[]} funcInstallers array of funcInstaller */ InstallLib(libName, jsmd5, funcInstallers) { if (this.InstalledLibs[libName]) { throw Error("重复导入 " + libName); } this.InstalledLibs[libName] = funcInstallers; this.NativeLibHash[libName] = jsmd5; } /** * * @param {string} libname * @param {number} funcIdx */ getNativeFunc(libname, funcIdx) { if (funcIdx < 0) { throw Error("funcIdx:" + funcIdx); } // Action < UFunctionBuilder, int[] > [] lib; let lib = this.InstalledLibs[libname]; if (lib) { if (funcIdx < lib.length) { return lib[funcIdx]; } else { throw Error("funcIdx:" + funcIdx + " of lib " + libname + " out of range " + lib.length); } } else { throw Error("Native lib " + libname + " not found"); } } } class OBStructDef { constructor() { this.Name; //string this.StructCnt; // int this.StringCnt; // int this.IntegerCnt; // int this.FloatCnt; // int this.NobjectCnt; // int this.StructFields; // OBStructDef[] } /** * * @param {Uint8Array} uint8arr * @returns OBStructValue */ decode(uint8arr) { let s = new OBStructValue(this); let dataView = new DataView(uint8arr.buffer); let read = uint8arr.byteOffset; // let int32arr = new Int32Array(uint8arr.buffer, uint8arr.byteOffset, uint8arr.byteLength); for (let i = 0; i < this.IntegerCnt; i++) { s.registers.LongRegister[i] = dataView.getInt32(read + i * 4); } read += this.IntegerCnt * 4; // let f32arr = new Float32Array(uint8arr.buffer, uint8arr.byteOffset + this.IntegerCnt * 4, uint8arr.byteLength - this.IntegerCnt * 4); for (let i = 0; i < this.FloatCnt; i++) { s.registers.DoubleRegister[i] = dataView.getFloat32(read + i * 4); } let decoder = new util.TextDecoder(); read += this.FloatCnt * 4; for (let i = 0; i < this.StringCnt; i++) { let byteLength = dataView.getUint32(read); read += 4; let stru8 = uint8arr.subarray(read - uint8arr.byteOffset, read + byteLength - uint8arr.byteOffset); read += byteLength; let str = decoder.decode(stru8); s.registers.StringRegister[i] = str; } for (let i = 0; i < this.StructCnt; i++) { let byteLength = dataView.getUint32(read); read += 4; let stru8 = uint8arr.subarray(read - uint8arr.byteOffset, read + byteLength - uint8arr.byteOffset); read += byteLength; let structData = this.StructFields.decode(stru8); s.registers.StructRegister[i] = structData; } return s; } /** * * @param {OBStructValue} s * @returns Uint8Array */ encode(s) { let int32arr = new ArrayBuffer(this.IntegerCnt * 4); let int32DataView = new DataView(int32arr); for (let i = 0; i < this.IntegerCnt; i++) { int32DataView.setInt32(i * 4, s.registers.LongRegister[i]); } let f32arr = new ArrayBuffer(this.FloatCnt * 4); let f2DataView = new DataView(f32arr); for (let i = 0; i < this.FloatCnt; i++) { f2DataView.setFloat32(i * 4, s.registers.DoubleRegister[i]); } let strbuf_length = 0; let strbufs = []; let encoder = new util.TextEncoder(); for (let i = 0; i < this.StringCnt; i++) { strbufs[i] = encoder.encode(s.registers.StringRegister[i]); strbuf_length += strbufs[i].byteLength; } let struct_buf_length = 0; let struct_bufs = []; for (let i = 0; i < this.StructCnt; i++) { struct_bufs[i] = this.StructFields.encode(s.registers.StructRegister[i]); struct_buf_length += struct_bufs[i].byteLength; } let buffer = new ArrayBuffer( this.IntegerCnt * 4 + this.FloatCnt * 4 + strbuf_length + this.StringCnt * 4 + struct_buf_length + this.StructCnt * 4); let dataView = new DataView(buffer); let uint8arr = new Uint8Array(buffer); uint8arr.set(new Uint8Array(int32arr)); let written = this.IntegerCnt * 4; uint8arr.set(new Uint8Array(f32arr), written); written += this.FloatCnt * 4; strbufs.forEach(buf => { dataView.setUint32(written, buf.byteLength); written += 4; uint8arr.set(buf, written); written += buf.byteLength; }); struct_bufs.forEach(buf => { dataView.setUint32(written, buf.byteLength); written += 4; uint8arr.set(buf, written); written += buf.byteLength; }); return uint8arr; } } class OBStructValueData { // /** // * @type {OBArrayBufferReader} // */ // Data; //arraybuffer // FullName; // Offset; // Length; // StructCount; } class OBVariableInfo { // typeIdx; // count; constructor(typeIdx, count) { this.typeIdx = typeIdx; this.count = count; } } class OBState { // /** // * @type {OBVariableInfo[]} // */ // Variables; // /** // * @type {string} // */ // Name; // MessageHandlers; // EventHandlers; } class OBCodeSegment { // name; // functions; // fsms; } class OBFunction { // /** // * @type {OBVariableInfo[]} // */ // Variables; // instructions; // /** // * @type {String} // */ // Signure; // Statements; } class OBFSM { // /** // * @type {string} // */ // Name; // /** // * @type {Object.<string,OBState>} // */ // States; //string->state // /** // * @type {OBState} // */ // Entry; //state // /** // * @type {OBVariableInfo[]} // */ // Variables; // /** // * @type {string} // */ // FullName; // /** // * @type {string} // */ // ModuleName; } class OBMessageHandler { // Name; // Func; // ArgTypeName; } class OBEventHandler { // Name; // Func; } class OBInstruction { // Position; /** * * @param {number} code * @param {OBFunctionBuilder} builder * @param {OBInstruction[]} instructions * @param {number} i */ init(code, builder, instructions, i) { } /** * * @param {OBFunctionBuilder} funcbuilder * @param {OBInstruction[]} instructions * @param {number} i */ link(funcbuilder, instructions, i) { } getRegisterByRegTypeId(typeId, builder) { switch (typeId) { case 0: return builder.LongRegister; case 1: return builder.DoubleRegister; case 2: return builder.StringRegister; case 3: return builder.StructRegister; case 4: return builder.NObjectRegister; default: throw Error("不支持类型:" + this.ArgTypeId); } } } class OBByteCodes { static createInstruction(cmd) { switch (cmd) { //case 0: // break; case 1: return new PRT(); case 2: return new ARITHI(); case 3: return new ARITHF(); case 4: return new LDSTR(); case 5: return new LDI(); case 6: return new LDF(); case 7: return new RET(); case 8: throw Error("Unknown byte code command:" + cmd); case 9: throw Error("Unknown byte code command:" + cmd); case 10: return new CHSTT(); case 11: return new STVG(); case 12: return new FSMVS(); case 13: return new FSMVG(); case 14: return new STVS(); case 15: return new MethodCall(); case 16: return new ExtInfo(); case 17: return new CreateFSM(); case 18: return new FSMSendMsg(); case 19: return new ReceivedMessage(); case 20: return new GetStructField(); case 21: return new SetStructField(); case 22: return new GZ0(); case 23: return new BRIF(); case 24: return new DEC(); case 25: return new BR(); case 26: return new Reg2Var(); case 27: return new Var2Reg(); case 28: return new NOP(); case 29: return new BRIFN(); case 30: return new I2F(); case 31: return new StructFieldDesc(); case 32: return new EQ(); case 33: return new NEQ(); case 34: return new LT(); case 35: return new LTE(); case 36: return new GT(); case 37: return new GTE(); case 38: return new SLF(); case 39: return new NativeMethodCall(); case 40: return new DestroyFSM(); case 41: return new FSMBroadcastMsg(); case 42: return new SGLF(); case 43: return new RAND(); case 44: return new F2I(); case 45: return new FSMSendMsgWait_Data(); case 46: return new FSMSendMsgWait(); case 47: return new FSMBroadcastMsgWait(); case 48: return new TextJoin(); case 49: return new ToString(); case 50: return new Sender(); case 51: return new VOM(); case 52: return new SHL(); case 53: return new AND(); case 54: return new FIX(); case 55: return new LAND(); case 56: return new LOR(); case 57: return new LNOT(); case 58: return new COND(); case 59: return new NEW(); case 60: return new CSTR(); case 61: return new LIST(); case 62: return new IMAP(); case 63: return new SMAP(); case 64: return new STKV(); case 65: return new RKOM(); case 66: return new SOM(); case 67: return new VOIM(); case 68: return new STIKV(); case 69: return new RKOIM(); case 70: return new VAT(); case 71: return new SVAT(); case 72: return new IVAT(); case 73: return new RVAT(); case 74: return new VAD(); case 75: return new PUSTT(); case 76: return new POPSTT(); case 77: return new DBI(); case 78: return new DBE(); case 79: return new SME(); case 80: return new IME(); case 81: return new IMA(); case 82: return new SMA(); default: throw Error("Unknown byte code command:" + cmd); } // return new OBInstruction(cmd); } } class PositionUpdatePair { // targetOffset; // callback; } class OBFunctionBuilder { // loader; //ScriptLoader // StatementLength; //integer // BuildingFunc; //OBFunction // PositionUpdatePairList; // currentInstructPosition; //integer // RootStatementContext = new OBStatementContext(); //[StatementContext] // /** // * @callback LongRegister // * @param {OBVMState} // * @param {OBFunctionBuilder} // * @param {OBInstruction[]} // * @returns {number} // */ // /** // * @type LongRegister[] // */ // LongRegister; // /** // * @callback DoubleRegister // * @param {OBVMState} // * @param {OBFunctionBuilder} // * @param {OBInstruction[]} // * @returns {number} // */ // /** // * @type DoubleRegister[] // */ // DoubleRegister; // /** // * @callback StringRegister // * @param {OBVMState} // * @param {OBFunctionBuilder} // * @param {OBInstruction[]} // * @returns {string} // */ // /** // * @type StringRegister[] // */ // StringRegister; // /*@ // * @callback StringRegister // * @param {OBVMState} // * @param {OBFunctionBuilder} // * @param {OBInstruction[]} // * @returns {OBStructValue} // */ // /** // * @type {Array} StructRegister[] // */ // StructRegister; // /*@ // * @callback StringRegister // * @param {OBVMState} // * @param {OBFunctionBuilder} // * @param {OBInstruction[]} // * @returns {object} // */ // /** // * @type {Array} NObjectRegister[] // */ // NObjectRegister; constructor(loader) { this.loader = loader; this.RootStatementContext = new OBStatementContext(); //[StatementContext] this.options = loader.options; } loadFunctionHeader(reader) { let data = this.loader.data; let BuildingFunc = new OBFunction(); this.BuildingFunc = BuildingFunc; let header = reader.ReadUInt32(); let pos = reader.pos; // this.StatementLength = header * 4 - pos; reader.pos = header * 4; let nameIdx = reader.ReadUInt32(); BuildingFunc.Signure = data.GetString(nameIdx); this._LongRegister = []; this._LongRegister.length = reader.ReadUInt32(); this._DoubleRegister = []; this._DoubleRegister.length = reader.ReadUInt32(); this._StringRegister = []; this._StringRegister.length = reader.ReadUInt32(); this._StructRegister = []; this._StructRegister.length = reader.ReadUInt32(); this._NObjectRegister = []; this._NObjectRegister.length = reader.ReadUInt32(); let varInfo = []; for (let i = 0; i < 5; i++) { let info = new OBVariableInfo(); info.typeIdx = i; info.count = reader.ReadUInt32(); varInfo[i] = info; } this.StatementLength = reader.ReadUInt32(); BuildingFunc.Variables = varInfo; reader.pos = pos; } LongRegister(idx, callback) { if (callback) { this._LongRegister[idx] = callback; } let r = this._LongRegister[idx]; if (!r) { debugger } return r; } DoubleRegister(idx, callback) { if (callback) { this._DoubleRegister[idx] = callback; } let r = this._DoubleRegister[idx]; if (!r) { debugger } return r; } StringRegister(idx, callback) { if (callback) { this._StringRegister[idx] = callback; } let r = this._StringRegister[idx]; if (!r) { debugger } return r; } StructRegister(idx, callback) { if (callback) { this._StructRegister[idx] = callback; } let r = this._StructRegister[idx]; if (!r) { debugger } return r; } NObjectRegister(idx, callback) { if (callback) { this._NObjectRegister[idx] = callback; } let r = this._NObjectRegister[idx]; if (!r) { debugger } return r; } loadStatement(reader) { let length = this.StatementLength; this.BuildingFunc.instructions = []; this.PositionUpdatePairList = []; //[PositionUpdatePair] for (let i = 0; i < length; i++) { let instPos = reader.pos; let code = reader.ReadUInt32(); let cmd = (code >> 24); let inst = OBByteCodes.createInstruction(cmd); inst.Position = instPos; inst.init(code, this, this.BuildingFunc.instructions, i); this.BuildingFunc.instructions[i] = inst; } } link() { let instructions = this.BuildingFunc.instructions; for (let i = 0; i < instructions.length; i++) { let inst = instructions[i]; this.currentInstructPosition = inst.Position; inst.link(this, instructions, i); } this.PositionUpdatePairList = null; this.BuildingFunc.Statements = this.RootStatementContext; } build() { return this.BuildingFunc; } PositionUpdate(targetOffset, callback) { if (this.PositionUpdatePairList == null) { throw Error("异常状态"); } let p = new PositionUpdatePair(); p.targetOffset = targetOffset; p.callback = callback; this.PositionUpdatePairList.push(p); } PushAction(Instruction) { let stmt = this.RootStatementContext; let newPos = stmt.Actions.length; if (this.options.instructionWrap) { Instruction = this.options.instructionWrap(Instruction); } if (this.options.debugger) { Instruction = this.options.debugger.instructionWrap(Instruction); } stmt.PushAction(Instruction); this.PositionUpdatePairList.forEach((p) => { if (p.targetOffset === this.currentInstructPosition) { p.callback(newPos); } }); } } class OBBuildInFunctions { /** * * @param {OBScript} script */ static install(script) { script.InstallLib("", "", [ OBBuildInFunctions.FSM_FindFsmByTypeInstaller, OBBuildInFunctions.Struct_countInDatasetInstaller, OBBuildInFunctions.Structs_LoadStructFromDatasetInstaller, // OBBuildInFunctions.FSM_TargetInstaller, script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_Length, 'LongRegister', ['StringRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_IsEmpty, 'LongRegister', ['StringRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_IndexOf, 'LongRegister', ['StringRegister', 'StringRegister', 'LongRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_CharAt, 'StringRegister', ['StringRegister', 'LongRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_GetSubstring, 'StringRegister', ['StringRegister', 'LongRegister', 'LongRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_ToUpperCase, 'StringRegister', ['StringRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_ToLowerCase, 'StringRegister', ['StringRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_ToTitleCase, 'StringRegister', ['StringRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_Count, 'LongRegister', ['StringRegister', 'StringRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_Replace, 'StringRegister', ['StringRegister', 'StringRegister', 'StringRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.Text_Reverse, 'StringRegister', ['StringRegister']), script.NativeUtil.closureVoid(OBBuildInFunctions.SYS_LOG, ['StringRegister', 'LongRegister'], true), script.NativeUtil.closureReturnValue(OBBuildInFunctions.math_text_to_integer, 'LongRegister', ['StringRegister']), script.NativeUtil.closureReturnValue(OBBuildInFunctions.math_text_to_number, 'DoubleRegister', ['StringRegister']), ]); } static math_text_to_integer(text) { let v = parseInt(text); if (Number.isInteger(v)) { return v; } else { return 0; } } static math_text_to_number(text) { let v = parseFloat(text); if (Number.isNaN(v)) { return 0; } else { return v; } } static SYS_LOG(msg, level, st, ofunc) { if (st.fsm.VM.logLevel <= level) { st.fsm.VM.Log(msg, 'usr', level, st, ofunc); } } /** * * @param {String} str * @returns */ static Text_Reverse(str) { if (str) { return str.split('').reverse().join(''); } else { return ""; } } static Text_Replace(haystack, needle, replacement) { needle = needle.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, "\\$1") .replace(/\x08/g, "\\x08"); return haystack.replace(new RegExp(needle, 'g'), replacement); } static Text_Count(haystack, needle) { if (needle.length === 0) { return haystack.length + 1; } else { return haystack.split(needle).length - 1; } } /** * * @param {String} str * @returns */ static Text_ToTitleCase(str) { return str.replace( /\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); } ); } /** * * @param {String} str * @returns */ static Text_ToLowerCase(str) { if (str) { return str.toLowerCase(); } return ""; } /** * * @param {String} str * @returns */ static Text_ToUpperCase(str) { if (str) { return str.toUpperCase(); } return ""; } static Text_GetSubstring(str, from, to) { let start = from; if (start < 0) { start = str.length + start; } else { start -= 1; } let end = to; if (end < 0) { end = str.length + to; } else { end -= 1; } if (start > end) { let t = start; start = end; end = t; } return str.substring(start, end + 1); } static Text_CharAt(str, index) { if (index === 0) { return ""; } if (index > 0) { return str[index - 1] || ""; } if (index < 0) { return str[str.length + index]; } } static Text_IndexOf(str, sub, forward) { if (forward === 1) { return str.indexOf(sub) + 1; } else { return str.lastIndexOf(sub) + 1; } } static Text_IsEmpty(str) { return str.length === 0 ? 1 : 0; } static Text_Length(str) { return str.length; } static FSM_FindFsmByTypeInstaller(builder, args) { let returnRegisterIdx = args[0] & 0xFFF; let argIdx = args[1] & 0xFFF; let a1 = builder.StringRegister(argIdx); builder.StructRegister(returnRegisterIdx, (state, func, locals) => { let r = state.fsm.VM.FindRunningFSMByType(a1(state, func, locals)); return r; }); } static Struct_countInDatasetInstaller(builder, args) { let returnRegisterIdx = args[0] & 0xFFF; let typeIdx = args[1] & 0xFFF; let a2 = builder.StringRegister(typeIdx); let StructData = builder.loader.script.StructData; builder.LongRegister(returnRegisterIdx, (state, func, locals) => { let typename = a2(state, func, locals); let r = StructData.Count(typename); return r; }); } static Structs_LoadStructFromDatasetInstaller(builder, args) { let returnRegisterIdx = args[0] & 0xFFF; let idIdx = args[2] & 0xFFF; let a1 = builder.LongRegister(idIdx); let typeIdx = args[1] & 0xFFF; let a2 = builder.StringRegister(typeIdx); let StructData = builder.loader.script.StructData; builder.StructRegister(returnRegisterIdx, (state, func, locals) => { let typename = a2(state, func, locals); let id = a1(state, func, locals); let r = StructData.Get(typename, id); if (!r) { throw VM.makeError(`Can't find ${typename} which id is ${id}`, state, func, locals); } return r; }); } static FSM_TargetInstaller(builder, args) { let returnRegisterIdx = args[0] & 0xFFF; builder.NObjectRegister(returnRegisterIdx, (state, func, locals) => { return state.fsm.Target; }); } } class OBStructValue { // /** // * @type {OBStructDef} // */ // Def; // /** // * @type {OBTypedVariableGroup} // */ // registers; constructor(Def) { this.Def = Def; let registers = new OBTypedVariableGroup(null); if (Def.IntegerCnt > 0) { registers.LongRegister = []; registers.LongRegister.length = Def.IntegerCnt; registers.LongRegister.fill(0, 0, Def.IntegerCnt); } if (Def.FloatCnt > 0) { registers.DoubleRegister = []; registers.DoubleRegister.length = Def.FloatCnt; registers.DoubleRegister.fill(0, 0, Def.FloatCnt); } if (Def.StringCnt > 0) { registers.StringRegister = []; registers.StringRegister.length = Def.StringCnt; registers.StringRegister.fill('', 0, Def.StringCnt); } if (Def.StructCnt > 0) { registers.StructRegister = []; registers.StructRegister.length = Def.StructCnt; } if (Def.NobjectCnt > 0) { registers.NObjectRegister = []; registers.NObjectRegister.length = Def.NobjectCnt; } this.registers = registers; } toString() { return "Struct." + this.Def.Name; } copyFrom(another) { this.registers.copyFrom(another.registers); } } class StructData { // /** // * @type {Object.<string,OBStructDef>} // */ // StructDef; // /** // * @type {Object.<string,OBStructValueData>} // */ // Groups; // /** // * @type {OBArrayBufferReader} // */ // DataSegment; constructor(structDataGroups, data) { this.Groups = structDataGroups; this.DataSegment = data; } Count(type) { if (type.startsWith("S") && type.endsWith(";")) { type = type.substr(1, type.length - 2); } let group = this.Groups[type]; if (!group) { throw `There is no preset data of the type ${type}`; } return group.StructCount; } /** * * @param {string} type fullname of type * @1param {integer} id id of data * @param {?Object.<string,OBStructValue>} * @returns {OBStructValue} */ Get(type, id, loading) { if (type.startsWith("S") && type.endsWith(";")) { type = type.substr(1, type.length - 2); } if (loading == null) { loading = {}; } else { let loaded = loading[id + "@" + type]; if (loaded) { return loaded; } } let def = this.StructDef[type]; let group = this.Groups[type]; if (!group) { throw `There is no preset data of the type ${type}`; } let reader = group.Data; let itemStart = 0; for (let i = 0; i < group.StructCount; i++) { reader.pos = itemStart; let length = reader.ReadInt32(); let itemid = reader.ReadUInt32(); if (itemid === id) { reader.pos -= 4; let s = new OBStructValue(def); loading[id + "@" + type] = s; for (let j = 0; j < def.IntegerCnt; j++) { s.registers.LongRegister[j] = reader.ReadInt32(); //VariableValueSet(j, reader.ReadInt32()); } for (let j = 0; j < def.StringCnt; j++) { let idx = reader.ReadUInt32(); let str = this.DataSegment.GetString(idx); s.registers.StringRegister[j] = str; //VariableValueSet(j, str); } for (let j = 0; j < def.FloatCnt; j++) { s.registers.DoubleRegister[j] = reader.ReadSingle(); //VariableValueSet(j, reader.ReadSingle()); } for (let j = 0; j < def.StructCnt; j++) { let fieldDef = def.StructFields[j]; if (fieldDef.startsWith("S")) { let subId = reader.ReadUInt32(); let subStruct = this.Get(def.StructFields[j], subId, loading); s.registers.StructRegister[j] = subStruct; //VariableValueSet(j, subStruct); } else if (fieldDef.startsWith("I")) { // TODO } else if (fieldDef.startsWith("N")) { let elementTypeName = fieldDef.substr(1); let structCnt = reader.ReadUInt32(); let map = {}; for (let k = 0; k < structCnt; k++) { let keyIdx = reader.ReadUInt32(); let keyStr = this.DataSegment.GetString(keyIdx); let structId = reader.ReadInt32(); let st = this.Get(elementTypeName, structId, loading); map[keyStr] = st; } s.registers.StructRegister[j] = map; } } return s; } else { itemStart += length * 4 + 4; } } throw Error("找不到 ID为" + id + "的" + type); } } class OBStructDataReader { /** * * @param {OBArrayBufferReader} reader * @returns {StructData} */ readStream(reader) { let dataLength = reader.ReadUInt32(); let data = reader.readSub(dataLength); return this.readStructData(reader, data); } /** * * @param {OBArrayBufferReader} reader * @param {OBArrayBufferReader} data * @returns {StructData} */ readStructData(reader, data) { let _length = reader.ReadInt32(); let structs = {}; let groupCnt = reader.ReadUInt32(); for (let i = 0; i < groupCnt; i++) { let offset = reader.pos; let strIdx = reader.ReadUInt32(); let FullName = data.GetString(strIdx); let structCnt = reader.ReadInt32(); let length = reader.ReadInt32(); // arraybuffer let bin = reader.readSub(length * 4); let info = new OBStructValueData(); // info.Data = bin; info.FullName = FullName; info.Offset = offset; info.Length = length; info.StructCount = structCnt; structs[FullName] = info; } return new StructData(structs, data); } } class Relocation { constructor() { /** * @type {Object.<string,{idx:Number,inited:bool}>} */ this.string = {}; /** * @type {Object.<string,{idx:Number,inited:bool}>} */ this.integer = {}; /** * @type {Object.<string,{idx:Number,inited:bool}>} */ this.float = {}; /** * @type {Object.<string,{idx:Number,inited:bool}>} */ this.bin = {}; /** * @type {Object.<string,{idx:Number,inited:bool}>} */ this.structFieldIndex = {}; } addRelocationString(str) { if ((typeof str) !== 'string') { throw Error('不是字符串'); } if (!this.string.hasOwnProperty(str)) { this.string[str] = { idx: 0, inited: false } } } } class ScriptLoader { constructor() { // /** // * @type {OBArrayBufferReader} // */ // reader; //OBArrayBufferReader // /** // * @type {OBArrayBufferReader} // */ // data; //OBArrayBufferReader this.loadingFunctions = {}; //[OBFunctionBuilder] this.Linkings = []; //Linkable } /** * @callback NativeLibInstaller * @param {OBScript} script */ /** * * @param {ArrayBuffer} arraybuffer of byte code * @param {NativeLibInstaller} nativeLibs * @param {Object} options * @returns */ static loadScript(arraybuffer, nativeLibs, options) { let script = new OBScript(); OBBuildInFunctions.install(script); let l = new ScriptLoader(); l.options = options || {}; // if (!l.options.debugger) { // l.options.debugger = new Debugger(); // } // let nativeLibs = OBNative.functions; if (nativeLibs) { if (Array.isArray(nativeLibs)) { nativeLibs.forEach(installer => { installer(script); }); } else { nativeLibs(script); } } l.load(script, arraybuffer); return script; } load(script, buf) { this.script = script; this.reader = new OBArrayBufferReader(buf); this.readXE(); } readXE() { let MAG = this.reader.ReadInt32(); //'\u007fUEX'; if (MAG != 0x5845557F) { throw Error("Unknown MAG:" + MAG); } let version = this.reader.ReadUInt32(); if (version != 1) { throw Error("Unsupported version." + version); } let SegmentCnt = this.reader.ReadUInt32(); let headerEnd = this.reader.pos + SegmentCnt * 2 * 4; let codes = []; for (let i = 0; i < SegmentCnt; i++) { let type = this.reader.ReadUInt32(); let startIn4Bytes = this.reader.ReadUInt32(); let start = headerEnd + startIn4Bytes * 4; let pos = this.reader.pos; switch (type) { case 0: this.reader.seek(start); this.data = this.loadDataSegment(); break; case 1: this.reader.seek(start); let code = this.loadCodeSegment(); // data应该是第一个段,所以此时data已经存在 codes.push(code); break; case 2: this.reader.seek(start); this.script.StructData = this.loadStructDataSegment(); this.script.StructData.StructDef = this.script.StructDef; break; case 3: this.reader.seek(start); this.script.StructDef = this.loadStructDefDataSegment(); break; case 4: this.reader.seek(start); this.loadPackageInfo(); break; default: throw Error("Unknown Segment type:" + type); } this.reader.seek(pos); } this.script.loadedFunctions = this.loadingFunctions; this.Linkings.forEach(l => { l.link(); }); codes.forEach(codeSeg => { codeSeg.fsms.forEach(fsm => { fsm.FullName = codeSeg.name + "." + fsm.Name; this.script.FullNameFSMData[fsm.FullName] = fsm; }); }); } loadCodeSegment() { let reader = this.reader; let data = this.data; let start = reader.pos; let SegmentReader = reader.getSub(start); let length = SegmentReader.ReadUInt32(); let header = SegmentReader.ReadUInt32() * 4; SegmentReader.pos = header; let segment = new OBCodeSegment(); let nameStringIdx = SegmentReader.ReadInt32(); let name = data.GetString(nameStringIdx); let ufunctions = this.readFunctions(SegmentReader); let fsms = this.readFSMs(SegmentReader, name); // 字段赋值 segment.name = name; segment.functions = ufunctions; segment.fsms = fsms; return segment; } readFSMs(reader, moduleName) { let cnt = reader.ReadInt32(); let f = []; // [OBFSM] for (let i = 0; i < cnt; i++) { let s = reader.ReadUInt32() * 4; let pos = reader.pos; reader.pos = s; let fsm = this.readFSM(reader); fsm.ModuleName = moduleName; fsm.FullName = moduleName + "." + fsm.Name; reader.pos = pos; f[i] = fsm; } return f; } readFSM(reader) { let data = this.data; let nameIdx = reader.ReadUInt32(); let name = data.GetString(nameIdx); let variables = this.readVariables(reader); let fucCnt = reader.ReadUInt32(); // TODO let states = this.readStates(reader); let entryStateNameIdx = reader.ReadUInt32(); let entryStateName = data.GetString(entryStateNameIdx); let entryState = null; // Dictionary<string, UState> stateDict = new Dictionary<string, UState>(); let stateDict = {}; for (let i = 0; i < states.length; i++) { let s = states[i]; stateDict[s.Name] = s; if (entryStateName === s.Name) { entryState = s; } } if (entryState == null) { throw Error("Can't find state named " + entryStateName + " FSM " + name); } let fsm = new OBFSM(); fsm.Name = name; fsm.States = stateDict; fsm.Entry = entryState; fsm.Variables = variables; return fsm; } readStates(reader) { let cnt = reader.ReadInt32(); let r = []; //[OBState] for (let i = 0; i < cnt; i++) { let s = reader.ReadUInt32() * 4; let p = reader.pos; reader.pos = s; r.push(this.readState(reader)); reader.pos = p; } return r; } readState(reader) { let data = this.data; let nameIdx = reader.ReadUInt32(); let name = data.GetString(nameIdx); let variables = this.readVariables(reader); // 读取函数 this.readFunctions(reader); // UMessageHandler[] let handlers = this.readHandlers(reader); // UEventHandler[] let ehandlers = this.readEHandlers(reader); // Dictionary<string, List<UMessageHandler>> Mh = new Dictionary<string, List<UMessageHandler>>(); let Mh = {}; for (let i = 0; i < handlers.length; i++) { let h = handlers[i]; let hl = Mh[h.Name]; if (hl) { } else { hl = []; //new List<UMessageHandler>(); Mh[h.Name] = hl; } hl.push(h); } // Dictionary<strin