UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

128 lines 6.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.defaultAnalysisOptions = void 0; const lodash_1 = require("lodash"); const assert_1 = require("../../utils/assert"); const helpers_1 = require("../../utils/ast/helpers"); const typeGuards_1 = require("../../utils/ast/typeGuards"); const dict_1 = require("../../utils/dict"); const errors_1 = require("../errors"); const utils_1 = require("../utils"); exports.defaultAnalysisOptions = { allowUndefinedImports: false, throwOnDuplicateNames: true }; /** * Import and Export analyzer: * - Checks for undefined imports * - Checks for different imports being given the same local name */ function analyzeImportsAndExports(programs, entrypointFilePath, topoOrder, { nativeStorage: { loadedModules } }, options = {}) { const declaredNames = new dict_1.default(); const moduleDocs = Object.fromEntries(Object.entries(loadedModules).map(([name, obj]) => [name, new Set(Object.keys(obj))])); for (const sourceModule of [...topoOrder, entrypointFilePath]) { const program = programs[sourceModule]; moduleDocs[sourceModule] = new Set(); for (const node of program.body) { if (node.type === 'ExportDefaultDeclaration') { if (!options.allowUndefinedImports) { (0, assert_1.default)(!moduleDocs[sourceModule].has('default'), "Multiple default exports should've been caught by the parser"); moduleDocs[sourceModule].add('default'); } continue; } if (node.type === 'ExportNamedDeclaration') { if (node.declaration) { if (!options.allowUndefinedImports) { const ids = (0, helpers_1.getIdsFromDeclaration)(node.declaration); ids.forEach(id => { moduleDocs[sourceModule].add(id.name); }); } continue; } for (const spec of node.specifiers) { moduleDocs[sourceModule].add(spec.exported.name); } if (!node.source) continue; } else if (!(0, typeGuards_1.isModuleDeclaration)(node)) { continue; } const dstModule = (0, helpers_1.getModuleDeclarationSource)(node); const dstModuleDocs = moduleDocs[dstModule]; if (node.type === 'ExportAllDeclaration') { if (!options.allowUndefinedImports) { if (dstModuleDocs.size === 0) throw new errors_1.UndefinedNamespaceImportError(dstModule, node); if (node.exported) { moduleDocs[sourceModule].add(node.exported.name); } else { for (const each of dstModuleDocs) { if (each === 'default') { // ExportAllDeclarations do not implicitly reexport default exports continue; } moduleDocs[sourceModule].add(each); } } } continue; } for (const spec of node.specifiers) { if (options.throwOnDuplicateNames && spec.type !== 'ExportSpecifier' && (0, utils_1.isSourceModule)(dstModule)) { const declaredName = spec.local.name; declaredNames.setdefault(declaredName, new dict_1.ArrayMap()).add(dstModule, spec); } if (options.allowUndefinedImports) continue; if (spec.type === 'ImportNamespaceSpecifier') { if (dstModuleDocs.size === 0) throw new errors_1.UndefinedNamespaceImportError(dstModule, spec); continue; } const importedName = (0, helpers_1.getImportedName)(spec); if (!dstModuleDocs.has(importedName)) { if (importedName === 'default') throw new errors_1.UndefinedDefaultImportError(dstModule, spec); throw new errors_1.UndefinedImportError(importedName, dstModule, spec); } } } } if (!options.throwOnDuplicateNames) return; // Because of the way the preprocessor works, different imports with the same declared name // will cause errors // There are two conditions we need to check: // 1. Two different symbols from the same module are declared with the same name: // import { a as x } from 'one_module'; AND import { b as x } from 'one_module'; // 2. Two different symbols from different modules are declared with the same name: // import { a } from 'one_module'; AND import { b as a } from 'another_module'; for (const [localName, moduleToSpecifierMap] of declaredNames) { if (moduleToSpecifierMap.size > 1) { // This means that two imports from different modules have the same // declared name const nodes = moduleToSpecifierMap.flatMap((_, v) => v); throw new errors_1.DuplicateImportNameError(localName, nodes); } const [[, specifiers]] = moduleToSpecifierMap; const [namespaceSpecifiers, regularSpecifiers] = (0, lodash_1.partition)(specifiers, typeGuards_1.isNamespaceSpecifier); // For the given local name, it can only represent one imported name from // the module. Collect specifiers referring to the same export. const importedNames = new Set(regularSpecifiers.map(helpers_1.getImportedName)); if ((namespaceSpecifiers.length > 0 && regularSpecifiers.length > 0) || importedNames.size > 1) { // This means that there is more than one unique export being given the same // local name const specs = [...regularSpecifiers, ...namespaceSpecifiers]; throw new errors_1.DuplicateImportNameError(localName, specs); } } } exports.default = analyzeImportsAndExports; //# sourceMappingURL=analyzer.js.map