sails-js-parser
Version:
Sails-IDL parser for TypeScript
227 lines (224 loc) • 11.9 kB
JavaScript
import wasmParserBytes from './wasm-bytes.js';
import { EnumVariant, EnumDef, TypeDef, DefKind, StructField, StructDef, UserDefinedDef, PrimitiveDef, ResultDef, MapDef, FixedSizeArrayDef, VecDef, OptionalDef, Type } from './types.js';
import { CtorFunc, Ctor, Program } from './program.js';
import { FuncParam, ServiceEvent, ServiceFunc, Service } from './service.js';
const WASM_PAGE_SIZE = 0x1_00_00;
class SailsIdlParser {
_memory;
_instance;
_exports;
_encoder;
_program;
_memPtr;
_idlLen;
_numberOfGrownPages = 0;
constructor() {
this._encoder = new TextEncoder();
}
async _decompressWasm() {
const binaryStr = atob(wasmParserBytes);
const binaryBase64 = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
binaryBase64[i] = binaryStr.codePointAt(i);
}
const ds = new DecompressionStream('gzip');
const decompressed = new Response(binaryBase64).body.pipeThrough(ds);
const reader = decompressed.getReader();
let bytes = [];
while (true) {
const { value, done } = await reader.read();
if (done)
break;
bytes = [...bytes, ...value];
}
return new Uint8Array(bytes).buffer;
}
async init() {
const wasmBuf = await this._decompressWasm();
this._memory = new WebAssembly.Memory({ initial: 17 });
const source = await WebAssembly.instantiate(wasmBuf, {
env: {
memory: this._memory,
visit_type: (_, type_ptr) => {
const type = new Type(type_ptr, this._memory);
const id = this._program.addType(type);
this.handleAcceptError(this._instance.exports.accept_type(type_ptr, id));
},
visit_optional_type_decl: (ctx, optional_type_decl_ptr) => {
const type = this._program.getContext(ctx);
const def = new OptionalDef(optional_type_decl_ptr, this._memory);
type.setDef(new TypeDef(def, DefKind.Optional));
this._program.addContext(def.rawPtr, def);
this.handleAcceptError(this._exports.accept_type_decl(optional_type_decl_ptr, def.rawPtr));
},
visit_vector_type_decl: (ctx, vector_type_decl_ptr) => {
const type = this._program.getContext(ctx);
const def = new VecDef(vector_type_decl_ptr, this._memory);
type.setDef(new TypeDef(def, DefKind.Vec));
this._program.addContext(def.rawPtr, def);
this.handleAcceptError(this._exports.accept_type_decl(vector_type_decl_ptr, def.rawPtr));
},
visit_array_type_decl: (ctx, array_type_decl_ptr, len) => {
const type = this._program.getContext(ctx);
const def = new FixedSizeArrayDef(array_type_decl_ptr, len, this._memory);
type.setDef(new TypeDef(def, DefKind.FixedSizeArray));
this._program.addContext(def.rawPtr, def);
this.handleAcceptError(this._exports.accept_type_decl(array_type_decl_ptr, def.rawPtr));
},
visit_map_type_decl: (ctx, key_type_decl_ptr, value_type_decl_ptr) => {
const type = this._program.getContext(ctx);
const def = new MapDef(key_type_decl_ptr, value_type_decl_ptr, this._memory);
type.setDef(new TypeDef(def, DefKind.Map));
this._program.addContext(def.key.rawPtr, def.key);
this._program.addContext(def.value.rawPtr, def.value);
this.handleAcceptError(this._exports.accept_type_decl(key_type_decl_ptr, def.key.rawPtr));
this.handleAcceptError(this._exports.accept_type_decl(value_type_decl_ptr, def.value.rawPtr));
},
visit_result_type_decl: (ctx, ok_type_decl_ptr, err_type_decl_ptr) => {
const type = this._program.getContext(ctx);
const def = new ResultDef(ok_type_decl_ptr, err_type_decl_ptr, this._memory);
type.setDef(new TypeDef(def, DefKind.Result));
this._program.addContext(def.ok.rawPtr, def.ok);
this._program.addContext(def.err.rawPtr, def.err);
this.handleAcceptError(this._exports.accept_type_decl(ok_type_decl_ptr, def.ok.rawPtr));
this.handleAcceptError(this._exports.accept_type_decl(err_type_decl_ptr, def.err.rawPtr));
},
visit_primitive_type_id: (ctx, primitive_type_id) => {
const type = this._program.getContext(ctx);
const def = new PrimitiveDef(primitive_type_id);
type.setDef(new TypeDef(def, DefKind.Primitive));
},
visit_user_defined_type_id: (ctx, user_defined_type_id_ptr, user_defined_type_id_len) => {
const type = this._program.getContext(ctx);
const def = new UserDefinedDef(user_defined_type_id_ptr, user_defined_type_id_len, this._memory);
type.setDef(new TypeDef(def, DefKind.UserDefined));
},
visit_struct_def: (ctx, struct_def_ptr) => {
const type = this._program.getContext(ctx);
const def = new StructDef(struct_def_ptr, this._memory);
this._program.addContext(def.rawPtr, def);
type.setDef(new TypeDef(def, DefKind.Struct));
this.handleAcceptError(this._exports.accept_struct_def(struct_def_ptr, def.rawPtr));
},
visit_struct_field: (ctx, struct_field_ptr) => {
const def = this._program.getContext(ctx);
const field = new StructField(struct_field_ptr, this._memory);
const id = def.addField(field);
this._program.addContext(id, field);
this.handleAcceptError(this._exports.accept_struct_field(struct_field_ptr, id));
},
visit_enum_def: (ctx, enum_def_ptr) => {
const type = this._program.getType(ctx);
const def = new EnumDef(enum_def_ptr, this._memory);
this._program.addContext(def.rawPtr, def);
type.setDef(new TypeDef(def, DefKind.Enum));
this._exports.accept_enum_def(enum_def_ptr, def.rawPtr);
},
visit_enum_variant: (ctx, enum_variant_ptr) => {
const def = this._program.getContext(ctx);
const variant = new EnumVariant(enum_variant_ptr, this._memory);
const id = def.addVariant(variant);
this._program.addContext(id, variant);
this.handleAcceptError(this._exports.accept_enum_variant(enum_variant_ptr, id));
},
visit_ctor: (_, ctor_ptr) => {
this._program.addCtor(new Ctor(ctor_ptr, this._memory));
this.handleAcceptError(this._exports.accept_ctor(ctor_ptr, 0));
},
visit_ctor_func: (_, func_ptr) => {
const func = new CtorFunc(func_ptr, this._memory);
this._program.ctor.addFunc(func);
this._program.addContext(func.rawPtr, func);
this.handleAcceptError(this._exports.accept_ctor_func(func_ptr, func.rawPtr));
},
visit_service: (_, service_ptr) => {
const service = new Service(service_ptr, this._memory);
this._program.addContext(service.rawPtr, service);
this._program.addService(service);
this.handleAcceptError(this._exports.accept_service(service_ptr, service.rawPtr));
},
visit_service_func: (ctx, func_ptr) => {
const func = new ServiceFunc(func_ptr, this._memory);
const service = this._program.getContext(ctx);
service.addFunc(func);
this._program.addContext(func.rawPtr, func);
this.handleAcceptError(this._exports.accept_service_func(func_ptr, func.rawPtr));
},
visit_service_event: (ctx, event_ptr) => {
const event = new ServiceEvent(event_ptr, this._memory);
const service = this._program.getContext(ctx);
service.addEvent(event);
this._program.addContext(event.rawPtr, event);
this.handleAcceptError(this._exports.accept_service_event(event_ptr, event.rawPtr));
},
visit_func_param: (ctx, func_param_ptr) => {
const param = new FuncParam(func_param_ptr, this._memory);
const func = this._program.getContext(ctx);
func.addFuncParam(param.rawPtr, param);
this._program.addContext(param.rawPtr, param);
this.handleAcceptError(this._exports.accept_func_param(func_param_ptr, param.rawPtr));
},
visit_func_output: (ctx, func_output_ptr) => {
this.handleAcceptError(this._exports.accept_type_decl(func_output_ptr, ctx));
},
},
});
this._instance = source.instance;
this._exports = this._instance.exports;
}
static async new() {
const parser = new SailsIdlParser();
await parser.init();
return parser;
}
fillMemory(idl) {
const buf = this._encoder.encode(idl);
this._idlLen = buf.length;
const numberOfPages = Math.round(buf.length / WASM_PAGE_SIZE) + 1;
if (!this._memPtr || numberOfPages > this._numberOfGrownPages) {
this._memPtr = this._memory.grow(numberOfPages - this._numberOfGrownPages) * WASM_PAGE_SIZE;
this._numberOfGrownPages = numberOfPages;
}
for (const [i, element] of buf.entries()) {
new Uint8Array(this._memory.buffer)[i + this._memPtr] = element;
}
}
clearMemory() {
for (let i = 0; i < this._numberOfGrownPages * WASM_PAGE_SIZE; i++) {
new Uint8Array(this._memory.buffer)[i + this._memPtr] = 0;
}
this._idlLen = null;
}
readString = (ptr) => {
const view = new DataView(this._memory.buffer);
let len = 0;
while (view.getUint8(ptr + len) !== 0) {
len++;
}
const buf = new Uint8Array(this._memory.buffer, ptr, len);
return new TextDecoder().decode(buf);
};
parse(idl) {
this.fillMemory(idl);
const resultPtr = this._instance.exports.parse_idl(this._memPtr, this._idlLen);
const view = new DataView(this._memory.buffer);
const errorCode = view.getUint32(resultPtr + 4, true);
if (errorCode > 0) {
// Read ParseResult enum discriminant
const errorDetails = this.readString(view.getUint32(resultPtr + 8, true));
throw new Error(`Error code: ${errorCode}, Error details: ${errorDetails}`);
}
const programPtr = view.getUint32(resultPtr, true);
this._program = new Program();
this.handleAcceptError(this._instance.exports.accept_program(programPtr, 0));
this._exports.free_parse_result(resultPtr);
this.clearMemory();
return this._program;
}
handleAcceptError(errorCode) {
if (errorCode > 0) {
throw new Error(`Error code: this{errorCode}`);
}
}
}
export { SailsIdlParser };