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

org.scalajs.linker.backend.wasmemitter.StringPool.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.backend.wasmemitter

import scala.collection.mutable

import org.scalajs.ir.OriginalName

import org.scalajs.linker.backend.webassembly.Instructions._
import org.scalajs.linker.backend.webassembly.Modules._
import org.scalajs.linker.backend.webassembly.Types._

import VarGen._

private[wasmemitter] final class StringPool {
  import StringPool._

  private val registeredStrings = new mutable.AnyRefMap[String, StringData]
  private val rawData = new mutable.ArrayBuffer[Byte]()
  private var nextIndex: Int = 0

  // Set to true by `genPool()`. When true, registering strings is illegal.
  private var poolWasGenerated: Boolean = false

  /** Registers the given constant string and returns its allocated data. */
  private def register(str: String): StringData = {
    if (poolWasGenerated)
      throw new IllegalStateException("The string pool was already generated")

    registeredStrings.getOrElseUpdate(str, {
      // Compute the new entry before changing the state
      val data = StringData(nextIndex, offset = rawData.size)

      // Write the actual raw data and update the next index
      rawData ++= str.toCharArray.flatMap { char =>
        Array((char & 0xFF).toByte, (char >> 8).toByte)
      }
      nextIndex += 1

      data
    })
  }

  /** Returns the list of instructions that load the given constant string.
   *
   *  The resulting list is *not* a Wasm constant expression, since it includes
   *  a `call` to the helper function `stringLiteral`.
   */
  def getConstantStringInstr(str: String): List[Instr] =
    getConstantStringDataInstr(str) :+ Call(genFunctionID.stringLiteral)

  /** Returns the list of 3 constant integers that must be passed to `stringLiteral`.
   *
   *  The resulting list is a Wasm constant expression, and hence can be used
   *  in the initializer of globals.
   */
  def getConstantStringDataInstr(str: String): List[I32Const] = {
    val data = register(str)
    List(
      I32Const(data.offset),
      I32Const(str.length()),
      I32Const(data.constantStringIndex)
    )
  }

  def genPool()(implicit ctx: WasmContext): Unit = {
    poolWasGenerated = true

    ctx.moduleBuilder.addData(
      Data(
        genDataID.string,
        OriginalName("stringPool"),
        rawData.toArray,
        Data.Mode.Passive
      )
    )

    ctx.addGlobal(
      Global(
        genGlobalID.stringLiteralCache,
        OriginalName("stringLiteralCache"),
        isMutable = false,
        RefType(genTypeID.externrefArray),
        Expr(
          List(
            I32Const(nextIndex), // number of entries in the pool
            ArrayNewDefault(genTypeID.externrefArray)
          )
        )
      )
    )
  }
}

private[wasmemitter] object StringPool {
  private final case class StringData(constantStringIndex: Int, offset: Int)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy