@duzc2-openblock/openblock-runtime
Version:
openblock runtime for browser and nodejs
1,491 lines (1,457 loc) • 199 kB
JavaScript
/**
* @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