All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalajs.linker.interface.Report.scala Maven / Gradle / Ivy

/*
 * Scala.js (https://www.scala-js.org/)
 *
 * Copyright EPFL.
 *
 * Licensed under Apache License 2.0
 * (https://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package org.scalajs.linker.interface

import scala.collection.immutable

import java.io._

import org.scalajs.ir.ScalaJSVersions

import org.scalajs.linker.interface.unstable.ReportImpl

/** Information about a linker run. */
abstract class Report private[interface] () {
  /** Public modules resulting from linking.
   *
   *  An incremental linker must return all public modules here, even if some
   *  were not re-written during this linking run.
   */
  def publicModules: immutable.Iterable[Report.Module]

  override def toString(): String = {
    s"""Report(
       |  publicModules = [
       |    ${publicModules.mkString(",\n")}
       |  ],
       |)""".stripMargin
  }
}

object Report {
  /** Information about a module produced by the linker. */
  abstract class Module private[interface] () {
    /** The module ID of this module. */
    def moduleID: String

    /** The name of the JS file in the linker's [[OutputDirectory]]. */
    def jsFileName: String

    /** The name of the source map (if one was generated) in the linker's
     *  [[OutputDirectory]].
     */
    def sourceMapName: Option[String]

    /** The [[ModuleKind]] of this module. */
    def moduleKind: ModuleKind

    override def toString(): String = {
      s"""Module(
         |  moduleID      = $moduleID,
         |  jsFileName    = $jsFileName,
         |  sourceMapName = $sourceMapName,
         |  moduleKind    = $moduleKind,
         |)""".stripMargin
    }
  }

  /** Serializes this [[Report]] to a byte array.
   *
   *  A report serialized with the exact same linker version is guaranteed to be
   *  deserializable using [[deserialize]]. If the linker version is different,
   *  no guarantee is given.
   */
  def serialize(report: Report): Array[Byte] = {
    val bytes = new ByteArrayOutputStream
    val out = new DataOutputStream(bytes)

    new Serializer(out).writeReport(report)

    out.close()
    bytes.toByteArray
  }

  /** Tries to deserialize the given bytes as a [[Report]].
   *
   *  @return `None` If the provided bytes are not compatible with the current
   *      linker version, `Some()` otherwise.
   *  @see [[serialize]] about when this is guaranteed to return `Some()`.
   */
  def deserialize(bytes: Array[Byte]): Option[Report] = {
    val in = new DataInputStream(new ByteArrayInputStream(bytes))
    new Deserializer(in).readReport()
  }

  private final class Serializer(out: DataOutputStream) {
    import out._

    def writeReport(report: Report): Unit = {
      writeUTF(ScalaJSVersions.current)
      writeInt(report.publicModules.size)
      report.publicModules.foreach(writeModule(_))
    }

    private def writeModule(module: Module): Unit = {
      writeUTF(module.moduleID)
      writeUTF(module.jsFileName)
      writeBoolean(module.sourceMapName.isDefined)
      module.sourceMapName.foreach(writeUTF(_))
      writeModuleKind(module.moduleKind)
    }

    private def writeModuleKind(kind: ModuleKind): Unit = {
      val i = kind match {
        case ModuleKind.NoModule       => 0
        case ModuleKind.ESModule       => 1
        case ModuleKind.CommonJSModule => 2
      }
      writeByte(i)
    }
  }

  private final class Deserializer(in: DataInputStream) {
    import in._

    def readReport(): Option[Report] = {
      val v = in.readUTF()
      if (v != ScalaJSVersions.current) {
        None
      } else {
        val publicModules = List.fill(in.readInt())(readModule())
        Some(new ReportImpl(publicModules))
      }
    }

    private def readModule(): Module = {
      new ReportImpl.ModuleImpl (
          moduleID = readUTF(),
          jsFileName = readUTF(),
          sourceMapName = readOptString(),
          moduleKind = readModuleKind()
      )
    }

    private def readOptString(): Option[String] =
      if (readBoolean()) Some(readUTF())
      else None

    private def readModuleKind(): ModuleKind = {
      readByte() match {
        case 0 => ModuleKind.NoModule
        case 1 => ModuleKind.ESModule
        case 2 => ModuleKind.CommonJSModule
        case v => throw new IllegalArgumentException(s"unknown module byte: $v")
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy