microvium
Version:
A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.
159 lines • 11.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringifyAnalysis = void 0;
const utils_1 = require("../../utils");
const stringify_structured_1 = require("stringify-structured");
const sections = (...s) => (0, stringify_structured_1.list)('; ', s, { multiLineJoiner: '\n', skipEmpty: true });
const subsections = (...s) => (0, stringify_structured_1.list)('; ', s, { multiLineJoiner: '', skipEmpty: true });
const items = (content, render) => (0, stringify_structured_1.list)('; ', [...content].map(render), { multiLineJoiner: '', skipEmpty: true });
const context = (0, utils_1.defineContext)();
const newContext = () => ({
bindingIds: new Map(),
nextBindingId: 0,
});
const getBindingId = (binding) => (0, utils_1.mapEmplace)(context.value.bindingIds, binding, {
insert: () => `binding_${++context.value.nextBindingId}`
});
function stringifyAnalysis(analysis) {
return context.use(newContext(), () => (0, stringify_structured_1.stringify)(renderAnalysis(analysis), { wrapWidth: 60 }));
}
exports.stringifyAnalysis = stringifyAnalysis;
function renderAnalysis(analysis) {
return sections(subsections((0, stringify_structured_1.inline) `[this module slot] ${analysis.thisModuleSlot.name}`, items(analysis.freeVariables, v => (0, stringify_structured_1.inline) `[free var] ${v}`), items(analysis.moduleImports, ([s, v]) => (0, stringify_structured_1.inline) `[import slot] ${v?.name ?? '<unnamed>'} [from] ${s}`), items(analysis.exportedBindings, v => (0, stringify_structured_1.inline) `[export binding] ${v.name} [in slot] ${renderSlot(v.slot)}`), items(analysis.globalSlots, g => (0, stringify_structured_1.inline) `[global slot] ${g.name}`)), renderScope(analysis.moduleScope));
}
function renderScope(scope) {
switch (scope.type) {
case 'ModuleScope': return renderModuleScope(scope);
case 'FunctionScope': return renderFunctionScope(scope);
case 'ClassScope': return renderClassScope(scope);
case 'BlockScope': return renderBlockScope(scope);
default: return (0, utils_1.assertUnreachable)(scope);
}
}
function renderModuleScope(scope) {
return (0, stringify_structured_1.inline) `module with entry ${(0, stringify_structured_1.text) `${scope.functionIsClosure ? 'closure ' : ''}`}${scope.ilFunctionId} ${(0, stringify_structured_1.block) `{ ${renderFunctionLikeBody(scope)} }`}`;
}
function renderFunctionLikeBody(scope) {
return sections(subsections(scope.closureSlots
? (0, stringify_structured_1.inline) `[closure scope with ${(0, stringify_structured_1.text) `${(0, utils_1.notUndefined)(scope.closureSlots.length)}`} slots: ${(0, stringify_structured_1.list)(', ', scope.closureSlots.map(c => (0, stringify_structured_1.text) `${c.debugName}`))}]`
: (0, stringify_structured_1.text) `[no closure scope]`, (0, stringify_structured_1.inline) `[${scope.varDeclarations.length} var declarations]`), renderScopeBindings(scope), renderReferencesSection(scope.references), renderPrologue(scope.prologue), ...scope.children.map(c => renderScope(c)));
}
function renderReferencesSection(references) {
if (references.length) {
return (0, stringify_structured_1.block) `references { ${items(references, renderReference)} }`;
}
else {
return (0, stringify_structured_1.text) `No references`;
}
}
function renderReference(reference) {
let s;
switch (reference.resolvesTo.type) {
case 'Binding': {
s = (0, stringify_structured_1.inline) `@ ${(0, stringify_structured_1.renderKey)(getBindingId(reference.resolvesTo.binding))}`;
break;
}
case 'FreeVariable': {
s = (0, stringify_structured_1.inline) `@ free ${(0, stringify_structured_1.renderKey)(reference.resolvesTo.name)}`;
break;
}
case 'RootLevelThis': {
s = (0, stringify_structured_1.inline) `@ root-level \`this\``;
break;
}
default: (0, utils_1.assertUnreachable)(reference.resolvesTo);
}
if (reference.access.type === 'ClosureSlotAccess') {
s = (0, stringify_structured_1.inline) `${s} using relative slot index ${reference.access.relativeIndex}`;
}
s = (0, stringify_structured_1.inline) `${(0, stringify_structured_1.renderKey)(reference.name)} ${s}`;
return s;
}
function renderPrologue(prologue) {
return (0, stringify_structured_1.block) `prologue { ${(0, stringify_structured_1.list)('; ', prologue.map(renderPrologueStep), { multiLineJoiner: '' })} }`;
}
function renderEpilogue(epilogue) {
return (0, stringify_structured_1.block) `epilogue { ${(0, stringify_structured_1.list)('; ', epilogue.map(renderEpilogueStep), { multiLineJoiner: '' })} }`;
}
function renderPrologueStep(step) {
switch (step.type) {
case 'InitFunctionDeclaration':
return (0, stringify_structured_1.inline) `func ${step.functionId} -> ${renderSlotReference(step.slot)}${step.closureType !== 'none' ? (0, stringify_structured_1.text) ` [${step.closureType} closure]` : (0, stringify_structured_1.text) ``}`;
case 'InitVarDeclaration': return (0, stringify_structured_1.inline) `new var -> ${renderSlotReference(step.slot)}`;
case 'InitLexicalDeclaration': return (0, stringify_structured_1.inline) `new let -> ${renderSlotReference(step.slot)}`;
case 'InitParameter': return (0, stringify_structured_1.inline) `arg[${step.argIndex}] -> ${renderSlotReference(step.slot)}`;
case 'InitThis': return (0, stringify_structured_1.inline) `arg[0] as this -> ${renderSlotReference(step.slot)}`;
case 'InitCatchParam': return (0, stringify_structured_1.inline) `Pop exception -> ${renderSlotReference(step.slot)}`;
case 'DiscardCatchParam': return (0, stringify_structured_1.inline) `Pop exception`;
case 'ScopePush': return (0, stringify_structured_1.inline) `ScopePush(${step.slotCount})`;
case 'ScopeNew': return (0, stringify_structured_1.inline) `ScopeNew(${step.slotCount})`;
case 'AsyncStart': return (0, stringify_structured_1.inline) `AsyncStart(${step.slotCount}, ${step.captureParent})`;
case 'StartTry': return (0, stringify_structured_1.inline) `StartTry`;
case 'DummyPushException': return (0, stringify_structured_1.inline) `Stack has exception`;
default: return (0, utils_1.assertUnreachable)(step);
}
}
function renderEpilogueStep(step) {
const requiredFlag = (0, stringify_structured_1.text) `${step.requiredDuringReturn ? '!' : ''}`;
switch (step.type) {
case 'EndTry': return (0, stringify_structured_1.inline) `${requiredFlag}EndTry`;
case 'Pop': return (0, stringify_structured_1.inline) `${requiredFlag}Pop(${step.count})`;
case 'ScopePop': return (0, stringify_structured_1.inline) `${requiredFlag}ScopePop`;
case 'ScopeDiscard': return (0, stringify_structured_1.inline) `${requiredFlag}ScopDiscard`;
default: return (0, utils_1.assertUnreachable)(step);
}
}
function renderSlotReference(slot) {
switch (slot.type) {
case 'ClosureSlotAccess': return (0, stringify_structured_1.inline) `scoped[+${slot.relativeIndex}]`; // Plus means relative cascading index
case 'ClosureSlot': return (0, stringify_structured_1.inline) `scoped[!${slot.index}]`; // Exclamation means absolute local index
case 'LocalSlot': return (0, stringify_structured_1.inline) `local[${slot.index}]`;
case 'ArgumentSlot': return (0, stringify_structured_1.inline) `arg[${slot.argIndex}]`;
case 'GlobalSlot': return (0, stringify_structured_1.inline) `global[${slot.name}]`;
case 'ModuleImportExportSlot': return (0, stringify_structured_1.inline) `importExport[${(0, stringify_structured_1.renderKey)(slot.moduleNamespaceObjectSlot.name)}.${(0, stringify_structured_1.renderKey)(slot.propertyName)}]`;
case 'ConstUndefinedAccess': return (0, stringify_structured_1.inline) `literal[undefined]`;
default: return (0, utils_1.assertUnreachable)(slot);
}
}
function renderFunctionScope(scope) {
return (0, stringify_structured_1.inline) `${(0, stringify_structured_1.text) `${scope.embeddedInParentSlot ? 'embedded ' : ''}`}${(0, stringify_structured_1.text) `${scope.functionIsClosure ? 'closure ' : ''}`}function ${(0, stringify_structured_1.renderKey)(scope.funcName ?? '<anonymous>')} as ${scope.ilFunctionId} ${(0, stringify_structured_1.block) `{ ${renderFunctionLikeBody(scope)} }`}`;
}
function renderClassScope(scope) {
return (0, stringify_structured_1.inline) `class ${(0, stringify_structured_1.renderKey)(scope.className ?? '<anonymous>')} with ${(0, stringify_structured_1.block) `{ ${sections(renderScopeBindings(scope), renderPrologue(scope.prologue), renderEpilogue(scope.epilogue), renderReferencesSection(scope.references), ...scope.children.map(c => renderScope(c)))} }`}`;
}
function renderBlockScope(scope) {
return (0, stringify_structured_1.inline) `block ${(0, stringify_structured_1.block) `{ ${sections(subsections((0, stringify_structured_1.inline) `sameInstanceCountAsParent: ${scope.sameInstanceCountAsParent}`, ...(scope.varDeclarations.length
? [(0, stringify_structured_1.inline) `[${scope.varDeclarations.length} var declarations]`]
: []), scope.closureSlots
? (0, stringify_structured_1.inline) `[closure scope with ${(0, stringify_structured_1.text) `${(0, utils_1.notUndefined)(scope.closureSlots.length)}`} slots: ${(0, stringify_structured_1.list)(', ', scope.closureSlots.map(c => (0, stringify_structured_1.text) `${c.debugName}`))}]`
: (0, stringify_structured_1.text) `[no closure scope]`), renderScopeBindings(scope), renderPrologue(scope.prologue), renderEpilogue(scope.epilogue), renderReferencesSection(scope.references), ...scope.children.map(c => renderScope(c)))} }`}`;
}
function renderScopeBindings(scope) {
return (0, stringify_structured_1.block) `bindings { ${(0, stringify_structured_1.list)('; ', Object.values(scope.bindings).map(renderBinding))} }`;
}
function renderBinding(binding) {
let s = (0, stringify_structured_1.inline) `${(0, stringify_structured_1.text) `${binding.kind}`} ${binding.name} # ${(0, stringify_structured_1.text) `${getBindingId(binding)}`}`;
if (binding.isDeclaredReadonly)
s = (0, stringify_structured_1.inline) `readonly ${s}`;
if (binding.isWrittenTo)
s = (0, stringify_structured_1.inline) `writable ${s}`;
if (binding.isExported)
s = (0, stringify_structured_1.inline) `export ${s}`;
if (binding.slot) {
s = (0, stringify_structured_1.inline) `${s} @ ${renderSlotReference(binding.slot)}`;
}
return (0, stringify_structured_1.text) `${s}`;
}
function renderSlot(slot) {
if (!slot)
return (0, stringify_structured_1.text) `<no slot>`;
switch (slot.type) {
case 'ArgumentSlot': return (0, stringify_structured_1.inline) `[arg slot] ${slot.argIndex}`;
case 'ClosureSlot': return (0, stringify_structured_1.inline) `[closure slot] ${slot.index}`;
case 'GlobalSlot': return (0, stringify_structured_1.inline) `[global slot] ${slot.name}`;
case 'LocalSlot': return (0, stringify_structured_1.inline) `[local slot] ${slot.index}`;
case 'ModuleImportExportSlot': return (0, stringify_structured_1.inline) `[import/export slot] ${slot.propertyName} [in] ${renderSlot(slot.moduleNamespaceObjectSlot)}`;
default: return (0, utils_1.assertUnreachable)(slot);
}
}
//# sourceMappingURL=stringify-analysis.js.map