"Fossies" - the Fresh Open Source Software Archive

Member "scala-js-1.3.1/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala" (14 Nov 2020, 12843 Bytes) of package /linux/www/scala-js-1.3.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Scala source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "Analysis.scala": 1.2.0_vs_1.3.0.

    1 /*
    2  * Scala.js (https://www.scala-js.org/)
    3  *
    4  * Copyright EPFL.
    5  *
    6  * Licensed under Apache License 2.0
    7  * (https://www.apache.org/licenses/LICENSE-2.0).
    8  *
    9  * See the NOTICE file distributed with this work for
   10  * additional information regarding copyright ownership.
   11  */
   12 
   13 package org.scalajs.linker.analyzer
   14 
   15 import scala.annotation.tailrec
   16 
   17 import scala.collection.mutable
   18 
   19 import org.scalajs.logging._
   20 
   21 import org.scalajs.linker.standard.ModuleSet.ModuleID
   22 
   23 import org.scalajs.ir.ClassKind
   24 import org.scalajs.ir.Names._
   25 import org.scalajs.ir.Trees.MemberNamespace
   26 import org.scalajs.ir.Types._
   27 
   28 /** Reachability graph produced by the [[Analyzer]].
   29  *
   30  *  Warning: this trait is not meant to be extended by third-party libraries
   31  *  and applications. Methods and/or fields can be added in subsequent
   32  *  versions, possibly causing `LinkageError`s if you extend it.
   33  */
   34 trait Analysis {
   35   import Analysis._
   36 
   37   def classInfos: scala.collection.Map[ClassName, ClassInfo]
   38   def topLevelExportInfos: Map[(ModuleID, String), TopLevelExportInfo]
   39 
   40   def errors: scala.collection.Seq[Error]
   41 }
   42 
   43 object Analysis {
   44 
   45   /** Class node in a reachability graph produced by the [[Analyzer]].
   46    *
   47    *  Warning: this trait is not meant to be extended by third-party libraries
   48    *  and applications. Methods and/or fields can be added in subsequent
   49    *  versions, possibly causing `LinkageError`s if you extend it.
   50    */
   51   trait ClassInfo {
   52     def className: ClassName
   53     def kind: ClassKind
   54     def superClass: Option[ClassInfo]
   55     def interfaces: scala.collection.Seq[ClassInfo]
   56     def ancestors: scala.collection.Seq[ClassInfo]
   57     def nonExistent: Boolean
   58     /** For a Scala class, it is instantiated with a `New`; for a JS class,
   59      *  its constructor is accessed with a `JSLoadConstructor` or because it
   60      *  is needed for a subclass.
   61      */
   62     def isInstantiated: Boolean
   63     def isAnySubclassInstantiated: Boolean
   64     def isModuleAccessed: Boolean
   65     def areInstanceTestsUsed: Boolean
   66     def isDataAccessed: Boolean
   67     def isAnyStaticFieldUsed: Boolean
   68     def isAnyPrivateJSFieldUsed: Boolean
   69     def jsNativeMembersUsed: scala.collection.Set[MethodName]
   70 
   71     def staticDependencies: scala.collection.Set[ClassName]
   72     def externalDependencies: scala.collection.Set[String]
   73 
   74     def linkedFrom: scala.collection.Seq[From]
   75     def instantiatedFrom: scala.collection.Seq[From]
   76     def methodInfos(
   77         namespace: MemberNamespace): scala.collection.Map[MethodName, MethodInfo]
   78 
   79     def displayName: String = className.nameString
   80   }
   81 
   82   /** Method node in a reachability graph produced by the [[Analyzer]].
   83    *
   84    *  Warning: this trait is not meant to be extended by third-party libraries
   85    *  and applications. Methods and/or fields can be added in subsequent
   86    *  versions, possibly causing `LinkageError`s if you extend it.
   87    */
   88   trait MethodInfo {
   89     def owner: ClassInfo
   90     def methodName: MethodName
   91     def namespace: MemberNamespace
   92     def isAbstractReachable: Boolean
   93     def isReachable: Boolean
   94     def calledFrom: scala.collection.Seq[From]
   95     def instantiatedSubclasses: scala.collection.Seq[ClassInfo]
   96     def nonExistent: Boolean
   97     def syntheticKind: MethodSyntheticKind
   98 
   99     def displayName: String = methodName.displayName
  100 
  101     def fullDisplayName: String =
  102       this.namespace.prefixString + owner.displayName + "." + displayName
  103   }
  104 
  105   sealed trait MethodSyntheticKind
  106 
  107   object MethodSyntheticKind {
  108     /** Not a synthetic method. */
  109     final case object None extends MethodSyntheticKind
  110 
  111     /** A reflective proxy bridge to the appropriate target method.
  112      *
  113      *  A reflective proxy `method__xyz__` dynamically calls some `target`
  114      *  method `method__xyz__R` on `this`. `R` is boxed according to JVM boxing
  115      *  semantics, i.e.,
  116      *
  117      *  - `Char` is boxed in `java.lang.Character`
  118      *  - `void` is followed by a reified `()`, i.e., `undefined`
  119      *  - All other types are left as is
  120      *
  121      *  The basic shape is:
  122      *
  123      *  {{{
  124      *  def method__xyz__(p1: T1, ..., pn: TN): any = {
  125      *    this.method__xyz__R(p1, ..., pn)
  126      *  }
  127      *  }}}
  128      */
  129     final case class ReflectiveProxy(target: MethodName)
  130         extends MethodSyntheticKind
  131 
  132     /** Bridge to a default method.
  133      *
  134      *  After the linker, default methods are not inherited anymore. Bridges
  135      *  are generated where appropriate to statically call the corresponding
  136      *  default method in the target interface.
  137      *
  138      *  The shape of default bridges is
  139      *
  140      *  {{{
  141      *  def method__xyz(p1: T1, ..., pn: TN): R = {
  142      *    this.TargetInterface::method__xyz(p1, ..., pn)
  143      *  }
  144      *  }}}
  145      */
  146     final case class DefaultBridge(targetInterface: ClassName)
  147         extends MethodSyntheticKind
  148   }
  149 
  150   trait TopLevelExportInfo {
  151     def moduleID: ModuleID
  152     def exportName: String
  153     def owningClass: ClassName
  154     def staticDependencies: scala.collection.Set[ClassName]
  155     def externalDependencies: scala.collection.Set[String]
  156   }
  157 
  158   sealed trait Error {
  159     def from: From
  160   }
  161 
  162   final case class MissingJavaLangObjectClass(from: From) extends Error
  163   final case class InvalidJavaLangObjectClass(from: From) extends Error
  164   final case class CycleInInheritanceChain(encodedClassNames: List[ClassName], from: From) extends Error
  165   final case class MissingClass(info: ClassInfo, from: From) extends Error
  166 
  167   final case class MissingSuperClass(subClassInfo: ClassInfo, from: From)
  168       extends Error
  169 
  170   final case class InvalidSuperClass(superClassInfo: ClassInfo,
  171       subClassInfo: ClassInfo, from: From)
  172       extends Error
  173 
  174   final case class InvalidImplementedInterface(superIntfInfo: ClassInfo,
  175       subClassInfo: ClassInfo, from: From)
  176       extends Error
  177 
  178   final case class MissingJSNativeLoadSpec(info: ClassInfo, from: From) extends Error
  179 
  180   final case class NotAModule(info: ClassInfo, from: From) extends Error
  181   final case class MissingMethod(info: MethodInfo, from: From) extends Error
  182   final case class MissingJSNativeMember(info: ClassInfo, name: MethodName, from: From) extends Error
  183   final case class ConflictingDefaultMethods(infos: List[MethodInfo], from: From) extends Error
  184 
  185   final case class InvalidTopLevelExportInScript(info: TopLevelExportInfo) extends Error {
  186     def from: From = FromExports
  187   }
  188 
  189   final case class ConflictingTopLevelExport(moduleID: ModuleID, exportName: String,
  190       infos: List[TopLevelExportInfo]) extends Error {
  191     def from: From = FromExports
  192   }
  193 
  194   final case class ImportWithoutModuleSupport(module: String, info: ClassInfo,
  195       jsNativeMember: Option[MethodName], from: From) extends Error
  196 
  197   final case class MultiplePublicModulesWithoutModuleSupport(
  198       moduleIDs: List[ModuleID]) extends Error {
  199     def from: From = FromExports
  200   }
  201 
  202   sealed trait From
  203   final case class FromMethod(methodInfo: MethodInfo) extends From
  204   final case class FromClass(classInfo: ClassInfo) extends From
  205   final case class FromCore(moduleName: String) extends From
  206   case object FromExports extends From
  207 
  208   def logError(error: Error, logger: Logger, level: Level): Unit = {
  209     val headMsg = error match {
  210       case MissingJavaLangObjectClass(_) =>
  211         "Fatal error: java.lang.Object is missing"
  212       case InvalidJavaLangObjectClass(_) =>
  213         "Fatal error: java.lang.Object is invalid (it must be a Scala class " +
  214         "without superclass nor any implemented interface)"
  215       case CycleInInheritanceChain(encodedClassNames, _) =>
  216         ("Fatal error: cycle in inheritance chain involving " +
  217             encodedClassNames.map(_.nameString).mkString(", "))
  218       case MissingClass(info, _) =>
  219         s"Referring to non-existent class ${info.displayName}"
  220       case MissingSuperClass(subClassInfo, _) =>
  221         s"${subClassInfo.displayName} (of kind ${subClassInfo.kind}) is " +
  222         "missing a super class"
  223       case InvalidSuperClass(superClassInfo, subClassInfo, _) =>
  224         s"${superClassInfo.displayName} (of kind ${superClassInfo.kind}) is " +
  225         s"not a valid super class of ${subClassInfo.displayName} (of kind " +
  226         s"${subClassInfo.kind})"
  227       case InvalidImplementedInterface(superIntfInfo, subClassInfo, _) =>
  228         s"${superIntfInfo.displayName} (of kind ${superIntfInfo.kind}) is " +
  229         s"not a valid interface implemented by ${subClassInfo.displayName} " +
  230         s"(of kind ${subClassInfo.kind})"
  231       case MissingJSNativeLoadSpec(info, _) =>
  232         s"${info.displayName} is a native class but does not have a JSNativeLoadSpec"
  233       case NotAModule(info, _) =>
  234         s"Cannot access module for non-module ${info.displayName}"
  235       case MissingMethod(info, _) =>
  236         s"Referring to non-existent method ${info.fullDisplayName}"
  237       case MissingJSNativeMember(info, name, _) =>
  238         s"Referring to non-existent js native member ${info.displayName}.${name.displayName}"
  239       case ConflictingDefaultMethods(infos, _) =>
  240         s"Conflicting default methods: ${infos.map(_.fullDisplayName).mkString(" ")}"
  241       case InvalidTopLevelExportInScript(info) =>
  242         s"Invalid top level export for name '${info.exportName}' in class " +
  243         s"${info.owningClass.nameString} when emitting a Script (NoModule) because it " +
  244         "is not a valid JavaScript identifier " +
  245         "(did you want to emit a module instead?)"
  246       case ConflictingTopLevelExport(moduleID, exportName, infos) =>
  247         s"Conflicting top level exports for module $moduleID, name $exportName "
  248         "involving " + infos.map(_.owningClass.nameString).mkString(", ")
  249       case ImportWithoutModuleSupport(module, info, None, _) =>
  250         s"${info.displayName} needs to be imported from module " +
  251         s"'$module' but module support is disabled"
  252       case ImportWithoutModuleSupport(module, info, Some(jsNativeMember), _) =>
  253         s"${info.displayName}.${jsNativeMember.displayName} " +
  254         s"needs to be imported from module '$module' but " +
  255         "module support is disabled"
  256       case MultiplePublicModulesWithoutModuleSupport(moduleIDs) =>
  257         "Found multiple public modules but module support is disabled: " +
  258         moduleIDs.map(_.id).mkString("[", ", ", "]")
  259     }
  260 
  261     logger.log(level, headMsg)
  262     val csl = new CallStackLogger(logger)
  263     csl.logCallStack(error.from, level)
  264   }
  265 
  266   private class CallStackLogger(logger: Logger) {
  267     private[this] val seenInfos = mutable.Set.empty[AnyRef]
  268     private[this] var indentation: String = ""
  269 
  270     def logCallStack(from: From, level: Level): Unit = {
  271       logCallStackImpl(level, Some(from))
  272       seenInfos.clear()
  273     }
  274 
  275     private def log(level: Level, msg: String) =
  276       logger.log(level, indentation+msg)
  277 
  278     private def indented[A](body: => A): A = {
  279       indentation += "  "
  280       try body
  281       finally indentation = indentation.substring(2)
  282     }
  283 
  284     private def logCallStackImpl(level: Level, optFrom: Option[From],
  285         verb: String = "called"): Unit = {
  286       val involvedClasses = new mutable.ListBuffer[ClassInfo]
  287 
  288       def onlyOnce(level: Level, info: AnyRef): Boolean = {
  289         if (seenInfos.add(info)) {
  290           true
  291         } else {
  292           log(level, "  (already seen, not repeating call stack)")
  293           false
  294         }
  295       }
  296 
  297       @tailrec
  298       def loopTrace(optFrom: Option[From], verb: String = "called"): Unit = {
  299         optFrom match {
  300           case None =>
  301             log(level, s"$verb from ... er ... nowhere!? (this is a bug in dce)")
  302           case Some(from) =>
  303             from match {
  304               case FromMethod(methodInfo) =>
  305                 log(level, s"$verb from ${methodInfo.fullDisplayName}")
  306                 if (onlyOnce(level, methodInfo)) {
  307                   involvedClasses ++= methodInfo.instantiatedSubclasses
  308                   loopTrace(methodInfo.calledFrom.lastOption)
  309                 }
  310               case FromClass(classInfo) =>
  311                 log(level, s"$verb from ${classInfo.displayName}")
  312                 loopTrace(classInfo.linkedFrom.lastOption)
  313               case FromCore(moduleName) =>
  314                 log(level, s"$verb from core module $moduleName")
  315               case FromExports =>
  316                 log(level, "exported to JavaScript with @JSExport")
  317             }
  318         }
  319       }
  320 
  321       indented {
  322         loopTrace(optFrom, verb = verb)
  323       }
  324 
  325       if (involvedClasses.nonEmpty) {
  326         log(level, "involving instantiated classes:")
  327         indented {
  328           for (classInfo <- involvedClasses.result().distinct) {
  329             log(level, classInfo.displayName)
  330 
  331             // recurse with Debug log level not to overwhelm the user
  332             if (onlyOnce(Level.Debug, classInfo)) {
  333               logCallStackImpl(Level.Debug,
  334                   classInfo.instantiatedFrom.lastOption, verb = "instantiated")
  335             }
  336           }
  337         }
  338       }
  339     }
  340   }
  341 
  342 }