UNPKG

microvium

Version:

A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.

159 lines 11.7 kB
"use strict"; 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