UNPKG

microvium

Version:

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

85 lines 4.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.pass3_computeSlotAccessors = void 0; const utils_1 = require("../../utils"); function pass3_computeSlotAccessors(state) { /* The instructions LoadScoped and StoreScoped take a _relative_ index, that in some sense "overflows" from the current scope into the parent scope. This function computes the relative indices. */ const { model: { references }, } = state; for (const reference of references.values()) { reference.access = getAccessForReference(reference); } function getAccessForReference(reference) { (0, utils_1.hardAssert)(!reference.access); const resolvesTo = reference.resolvesTo; if (resolvesTo.type === 'FreeVariable') { return { type: 'GlobalSlot', name: resolvesTo.name, }; } if (resolvesTo.type === 'RootLevelThis') { // A `this` reference that hits the root scope without finding a binding must just be represented as `undefined` return { type: 'ConstUndefinedAccess' }; } // Otherwise, the reference resolves to a binding const binding = resolvesTo.binding; (0, utils_1.hardAssert)(binding); // All bindings must have slots if they are used, and we're in this function // because the binding is used (it has a reference to it). Note that the // definition of "used" here is quite conservative. In the current // implementation, all bindings with a self-reference are considered to be // "used" since they have a reference to themselves. The only bindings that // can be "unused" are the parameters const slot = binding.slot; if (!slot) (0, utils_1.unexpected)(); switch (slot.type) { case 'LocalSlot': return slot; case 'GlobalSlot': return slot; case 'ArgumentSlot': return slot; case 'ModuleImportExportSlot': return slot; case 'ClosureSlot': { // Start at the nearest scope and work backwards let scope = reference.nearestScope; const targetScope = binding.scope; let relativeIndex = 0; // While we're not in the scope containing the variable, move to the parent scope while (scope !== targetScope) { if (scope.closureSlots) { // I don't think there's any case where a scope has closure slots // but has the same instance count as the parent (0, utils_1.hardAssert)(!scope.sameInstanceCountAsParent); relativeIndex += scope.closureSlots.length; } // If we're stepping from a function scope to its parent scope, and // the function closure is not embedded in the parent, then we need // to step over the 2-slot closure allocation itself to get to the // parent. if (scope.type === 'FunctionScope' && !scope.embeddedInParentSlot) { relativeIndex += 2; } // In order for us to hop from the child to the parent function, // we'll need to have a reference to the parent scope at runtime, // which means the function we're hopping from must itself be a // closure. (0, utils_1.hardAssert)(scope.type !== 'FunctionScope' || scope.functionIsClosure); scope = scope.parent || (0, utils_1.unexpected)(); } relativeIndex += slot.index; return { type: 'ClosureSlotAccess', relativeIndex }; } default: (0, utils_1.assertUnreachable)(slot); } } } exports.pass3_computeSlotAccessors = pass3_computeSlotAccessors; //# sourceMappingURL=pass-3-compute-accessors.js.map