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

org.scalajs.linker.backend.emitter.GlobalRefUtils.scala Maven / Gradle / Ivy

The newest version!
/*
 * 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.emitter

/** Utilities related to global refs mentioned in the program.
 *
 *  When a global ref is mentioned somewhere in the program, bad things happen.
 *
 *  On the one hand, in the local method where it is mentioned, no local
 *  variable can use the same name, as it would shadow the reference to the
 *  global variable. That is however a local property, and is handled by the
 *  name management of [[FunctionEmitter]].
 *
 *  On the other hand, there are so-called "dangerous" global refs, which are
 *  global refs that could collide with the emitter's own internal encoding,
 *  such as `$doubleToInt` or `$c_O`. When a dangerous global ref is mentioned
 *  somewhere in the program, the *entire* emitted program is affected, because
 *  the internal name must be changed.
 *
 *  Dangerous global refs are those that start with `$`, with the exception of
 *  the string `"$"` itself (because jQuery ...).
 *
 *  Hopefully, in a reasonable program, there is actually no dangerous global
 *  ref mentioned anywhere, and the [[Emitter]] can do its job in one pass.
 *  That is why we eagerly filter out non-dangerous global refs in individual
 *  caches, and why we have most paths optimized for empty sets.
 */
private[emitter] object GlobalRefUtils {
  /** Semantically equivalent to `a ++ b`, but optimized for empty sets.
   *
   *  Using this method over `a ++ b` is meaningful is there is a strong
   *  likelihood that one or both parameters are empty, which is the case for
   *  sets of mentioned global refs.
   */
  def unionPreserveEmpty(a: Set[String], b: Set[String]): Set[String] = {
    if (a.isEmpty) b
    else if (b.isEmpty) a
    else a ++ b
  }

  /** Tests whether a global ref is dangerous. */
  def isDangerousGlobalRef(globalRef: String): Boolean = {
    // Note that this intentionally filters out `$` itself
    globalRef.length > 1 && globalRef.charAt(0) == '$'
  }

  /** Filters a set to keep only the dangerous global refs. */
  def keepOnlyDangerousGlobalRefs(allGlobalRefs: Set[String]): Set[String] = {
    if (allGlobalRefs.isEmpty) {
      // Fast path, make sure to return a Set.EmptySet
      Set.empty
    } else {
      // Slow path in a different `def` to keep it out of the JIT's way
      def slowPath(): Set[String] = {
        val r = allGlobalRefs.filter(isDangerousGlobalRef(_))
        /* In the likely empty case, make sure to return a Set.EmptySet,
         * whose `contains()` method is super efficient and can potentially
         * be JIT'ed away.
         */
        if (r.isEmpty)
          Set.empty
        else
          r
      }
      slowPath()
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy