microvium
Version:
A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.
191 lines • 8.35 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValueWrapper = exports.NativeVMFriendly = void 0;
const lib_1 = require("../lib");
const utils_1 = require("./utils");
const NativeVM = __importStar(require("./native-vm"));
const runtime_types_1 = require("./runtime-types");
const snapshot_1 = require("./snapshot");
class NativeVMFriendly {
constructor(snapshot, hostImportMap = lib_1.defaultHostEnvironment) {
let hostImportFunction;
if (typeof hostImportMap !== 'function') {
hostImportFunction = (hostFunctionID) => {
if (!hostImportMap.hasOwnProperty(hostFunctionID)) {
return (0, utils_1.invalidOperation)('Unresolved import: ' + hostFunctionID);
}
return hostImportMap[hostFunctionID];
};
}
else {
hostImportFunction = hostImportMap;
}
this.vm = new NativeVM.NativeVM(snapshot.data, hostFunctionID => {
const inner = hostImportFunction(hostFunctionID);
return this.hostFunctionToVM(inner);
});
}
getMemoryStats() {
return this.vm.getMemoryStats();
}
resolveExport(exportID) {
return vmValueToHost(this.vm, this.vm.resolveExport(exportID));
}
garbageCollect(squeeze = false) {
this.vm.runGC(squeeze);
}
createSnapshot() {
return new snapshot_1.SnapshotClass(this.vm.createSnapshot());
}
asyncStart() {
return vmValueToHost(this.vm, this.vm.asyncStart());
}
hostFunctionToVM(hostFunction) {
return (args) => {
const result = hostFunction.apply(undefined, args.map(a => vmValueToHost(this.vm, a)));
return hostValueToVM(this.vm, result);
};
}
}
exports.NativeVMFriendly = NativeVMFriendly;
function vmValueToHost(vm, value) {
switch (value.type) {
case runtime_types_1.mvm_TeType.VM_T_UNDEFINED: return undefined;
case runtime_types_1.mvm_TeType.VM_T_NULL: return null;
case runtime_types_1.mvm_TeType.VM_T_BOOLEAN: return value.toBoolean();
case runtime_types_1.mvm_TeType.VM_T_NUMBER: return value.toNumber();
case runtime_types_1.mvm_TeType.VM_T_STRING: return value.toString();
case runtime_types_1.mvm_TeType.VM_T_FUNCTION: {
return new Proxy(dummyFunctionTarget, new ValueWrapper(vm, value));
}
case runtime_types_1.mvm_TeType.VM_T_OBJECT: {
return new Proxy(dummyObject, new ValueWrapper(vm, value));
}
case runtime_types_1.mvm_TeType.VM_T_ARRAY: return (0, utils_1.notImplemented)();
case runtime_types_1.mvm_TeType.VM_T_UINT8_ARRAY: return (0, utils_1.notImplemented)();
case runtime_types_1.mvm_TeType.VM_T_CLASS: return (0, utils_1.notImplemented)();
case runtime_types_1.mvm_TeType.VM_T_SYMBOL: return (0, utils_1.reserved)();
case runtime_types_1.mvm_TeType.VM_T_BIG_INT: return (0, utils_1.notImplemented)();
case runtime_types_1.mvm_TeType.VM_T_END: return (0, utils_1.unexpected)();
default: return (0, utils_1.assertUnreachable)(value.type);
}
}
function hostValueToVM(vm, value) {
switch (typeof value) {
case 'undefined': return vm.undefined;
case 'boolean': return vm.newBoolean(value);
case 'number': return vm.newNumber(value);
case 'string': return vm.newString(value);
case 'function': {
if (ValueWrapper.isWrapped(vm, value)) {
return ValueWrapper.unwrap(vm, value);
}
else {
return (0, utils_1.notImplemented)('Ephemeral in native VM');
// return vm.ephemeralFunction(hostFunctionToVM(vm, value), nameHint || value.name);
}
}
case 'object': {
if (value === null) {
return (0, utils_1.notImplemented)();
// return vm.null;
}
if (ValueWrapper.isWrapped(vm, value)) {
return ValueWrapper.unwrap(vm, value);
}
else {
return (0, utils_1.notImplemented)('Ephemeral object in native VM');
}
}
default: return (0, utils_1.notImplemented)();
}
}
// Used as a target for function proxies, so that `typeof` and `call` work as expected
const dummyFunctionTarget = () => { };
const dummyObject = {};
const vmValueSymbol = Symbol('vmValue');
const vmSymbol = Symbol('vm');
class ValueWrapper {
constructor(vm, vmValue) {
this.vm = vm;
this.vmValue = vmValue;
this.toString = this.toString.bind(this);
}
static isWrapped(vm, value) {
return (typeof value === 'function' || typeof value === 'object') &&
value !== null &&
value[vmValueSymbol] &&
value[vmSymbol] == vm; // It needs to be a wrapped value in the context of the particular VM in question
}
static unwrap(vm, value) {
(0, utils_1.hardAssert)(ValueWrapper.isWrapped(vm, value));
return value[vmValueSymbol];
}
toString() {
return `<Microvium native ${this.getTypeDescription()} 0x${this.vmValue.raw.toString(16).padStart(4, '0')} "${this.vmValue.toString()}">`;
}
getTypeDescription() {
switch (this.vmValue.type) {
case runtime_types_1.mvm_TeType.VM_T_UNDEFINED: return 'undefined';
case runtime_types_1.mvm_TeType.VM_T_NULL: return 'null';
case runtime_types_1.mvm_TeType.VM_T_BOOLEAN: return 'boolean';
case runtime_types_1.mvm_TeType.VM_T_NUMBER: return 'number';
case runtime_types_1.mvm_TeType.VM_T_STRING: return 'string';
case runtime_types_1.mvm_TeType.VM_T_FUNCTION: return 'function';
case runtime_types_1.mvm_TeType.VM_T_OBJECT: return 'object';
case runtime_types_1.mvm_TeType.VM_T_ARRAY: return 'array';
case runtime_types_1.mvm_TeType.VM_T_UINT8_ARRAY: return 'uint8 array';
case runtime_types_1.mvm_TeType.VM_T_CLASS: return 'class';
default: return 'unknown';
}
}
get(_target, p, receiver) {
if (p === vmValueSymbol)
return this.vmValue;
if (p === vmSymbol)
return this.vm;
if (p === Symbol.toPrimitive)
return this.toString;
if (p === Symbol.toStringTag)
return this.toString;
if (p === 'toString')
return this.toString;
return undefined;
}
set(_target, p, value, receiver) {
return (0, utils_1.notImplemented)();
}
apply(_target, _thisArg, argArray = []) {
const args = argArray.map(a => hostValueToVM(this.vm, a));
const func = this.vmValue;
if (func.type !== runtime_types_1.mvm_TeType.VM_T_FUNCTION)
return (0, utils_1.invalidOperation)('Target is not callable');
const result = this.vm.call(func, args);
return vmValueToHost(this.vm, result);
}
}
exports.ValueWrapper = ValueWrapper;
//# sourceMappingURL=native-vm-friendly.js.map