From 42118ac3e84bc4bcd17097a32def8b0a08177aec Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Fri, 2 Jun 2023 18:54:00 +0200 Subject: [PATCH] Can no longer import Test.Logical_Export.Impl --- .../org/enso/compiler/PackageRepository.scala | 23 ++- .../main/scala/org/enso/compiler/Passes.scala | 1 + .../pass/analyse/ImportApiAnalysis.scala | 150 ++++++++++++++++++ 3 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/ImportApiAnalysis.scala diff --git a/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala b/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala index 99ea89f033eb..a9306cd6210d 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala @@ -337,6 +337,12 @@ object PackageRepository { .unzip regularModules.foreach(registerModule) + regularModules.foreach(module => { + val fqn = module.getName.toString + val prefix = libraryName.qualifiedName + val moduleName = fqn.substring(prefix.length() + 1) + pkg.registerModule(moduleName, module) + }) syntheticModulesMetadata.flatten .groupMap(_._1)(v => (v._2, v._3)) @@ -344,15 +350,20 @@ object PackageRepository { val source = modulesWithSources .map(_._2) .foldLeft("")(_ ++ "\n" ++ _) + val module = Module.synthetic( + qName, + pkg, + Rope(source), + context + ) registerSyntheticModule( - Module.synthetic( - qName, - pkg, - Rope(source), - context - ), + module, modulesWithSources.map(_._1) ) + val fqn = module.getName.toString + val prefix = libraryName.qualifiedName + val moduleName = fqn.substring(prefix.length() + 1) + pkg.registerModule(moduleName, module) } if (isLibrary) { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala b/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala index 54b4cf181a4e..66b79d617464 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala @@ -47,6 +47,7 @@ class Passes( OperatorToFunction, LambdaShorthandToLambda, ImportSymbolAnalysis, + ImportApiAnalysis, ShadowedPatternFields, UnreachableMatchBranches, NestedPatternMatch, diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/ImportApiAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/ImportApiAnalysis.scala new file mode 100644 index 000000000000..34022a50c9ee --- /dev/null +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/ImportApiAnalysis.scala @@ -0,0 +1,150 @@ +package org.enso.compiler.pass.analyse + +import org.enso.interpreter.runtime.Module +import org.enso.compiler.context.{InlineContext, ModuleContext} +import org.enso.compiler.core.IR +import org.enso.compiler.data.BindingsMap +import org.enso.compiler.pass.IRPass +import org.enso.compiler.pass.desugar.{ + ComplexType, + FunctionBinding, + GenerateMethodBodies +} +import org.enso.compiler.pass.resolve.{MethodDefinitions, Patterns} + +import scala.annotation.unused + +/** Recognizes all defined bindings in the current module and constructs + * a mapping data structure that can later be used for symbol resolution. + */ +case object ImportApiAnalysis extends IRPass { + + override type Metadata = BindingsMap + + /** The type of configuration for the pass. */ + override type Config = IRPass.Configuration.Default + + /** The passes that this pass depends _directly_ on to run. */ + override val precursorPasses: Seq[IRPass] = + Seq(ComplexType, FunctionBinding, GenerateMethodBodies) + + /** The passes that are invalidated by running this pass. */ + override val invalidatedPasses: Seq[IRPass] = + Seq(MethodDefinitions, Patterns) + + /** Executes the pass on the provided `ir`, and returns a possibly transformed + * or annotated version of `ir`. + * + * @param ir the Enso IR to process + * @param moduleContext a context object that contains the information needed + * to process a module + * @return `ir`, possibly having made transformations or annotations to that + * IR. + */ + override def runModule( + ir: IR.Module, + moduleContext: ModuleContext + ): IR.Module = { + val map: BindingsMap = + ir.unsafeGetMetadata(BindingAnalysis, "Should exist.") + val forbiddenImports = new java.util.HashMap[String, String] + + map.resolvedImports.map { imp: BindingsMap.ResolvedImport => + val maybeModule = imp.target match { + case rm: BindingsMap.ResolvedModule => + None + rm.module match { + case c: BindingsMap.ModuleReference.Concrete => Some(c.module) + case _ => None + } + case _ => None + } + + maybeModule.map(m => { + val pkg = m.getPackage() + if (moduleContext.module.getPackage() != pkg) { + if (pkg.mainFile != null) { + val found = pkg.findModule("Main") + if (found != null) { + // if the package has Main file, then imports has to go thru the main module + val mainModule = found.asInstanceOf[Module] + if (m != mainModule) { + // if different that Main module is requested, do a check + if (mainModule.getIr == null) { + // if main module IR isn't loaded, then certainly the import didn't go thru Main + forbiddenImports.put( + m.getName().toString(), + mainModule.getName().toString() + ) + } else { + val mainMap: BindingsMap = mainModule.getIr + .unsafeGetMetadata(BindingAnalysis, "Should exist.") + val checks = for { + // go thru all re-exported modules in main + (name, modules) <- mainMap.exportedSymbols + module <- modules + } yield { + module match { + case BindingsMap.ResolvedModule( + BindingsMap.ModuleReference.Concrete(allowed) + ) => { + // check if one of the exported modules matches the imported one + allowed == m + } + case _ => false + } + } + if (!checks.exists(b => b)) { + // if no re-exported module patches m, report an error + forbiddenImports.put( + m.getName().toString(), + mainModule.getName().toString() + ) + } + } + } + } + } + } + }) + } + val replace = ir.imports.map(imp => + imp match { + case m: IR.Module.Scope.Import.Module => + val name: String = m.name.name + val pkg = forbiddenImports.get(name) + if (pkg != null) { + IR.Error.ImportExport( + imp, + IR.Error.ImportExport + .SymbolDoesNotExist(name.split("\\.").last, pkg) + ) + } else { + m + } + case i => i + } + ) + ir.copy(imports = replace) + } + + /** Executes the pass on the provided `ir`, and returns a possibly transformed + * or annotated version of `ir` in an inline context. + * + * @param ir the Enso IR to process + * @param inlineContext a context object that contains the information needed + * for inline evaluation + * @return `ir`, possibly having made transformations or annotations to that + * IR. + */ + override def runExpression( + ir: IR.Expression, + inlineContext: InlineContext + ): IR.Expression = ir + + /** @inheritdoc */ + override def updateMetadataInDuplicate[T <: IR]( + @unused sourceIr: T, + copyOfIr: T + ): T = copyOfIr +}