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

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

import scala.concurrent.{ExecutionContext, Future}

import org.scalajs.ir.ClassKind._
import org.scalajs.ir.Names._
import org.scalajs.ir.OriginalName
import org.scalajs.ir.OriginalName.NoOriginalName
import org.scalajs.ir.Position
import org.scalajs.ir.Trees._
import org.scalajs.ir.Types._
import org.scalajs.ir.{EntryPointsInfo, Version}

import org.scalajs.linker.interface.IRFile
import org.scalajs.linker.interface.unstable.IRFileImpl

import org.scalajs.linker.standard.LinkedClass

import SpecialNames._

/** Derives `CharacterBox` and `LongBox` from `jl.Character` and `jl.Long`. */
object DerivedClasses {
  def deriveClasses(classes: List[LinkedClass]): List[LinkedClass] = {
    classes.collect {
      case clazz if clazz.className == BoxedCharacterClass || clazz.className == BoxedLongClass =>
        deriveBoxClass(clazz)
    }
  }

  /** Generates the accompanying Box class of `Character` or `Long`.
   *
   *  These box classes will be used as the generic representation of `char`s and `long`s when they
   *  are upcast to `java.lang.Character`/`java.lang.Long` or any of their supertypes.
   *
   *  The generated Box classes mimic the public structure of the corresponding hijacked classes.
   *  Whereas the hijacked classes instances *are* the primitives (conceptually), the box classes
   *  contain an explicit `value` field of the primitive type. They delegate all their instance
   *  methods to the corresponding methods of the hijacked class, applied on the `value` primitive.
   *
   *  For example, given the hijacked class
   *
   *  {{{
   *  hijacked class Long extends java.lang.Number with Comparable {
   *    def longValue;J(): long = this.asInstanceOf[long]
   *    def toString;T(): string = Long$.toString(this.longValue;J())
   *    def compareTo;jlLong;Z(that: java.lang.Long): boolean =
   *      Long$.compare(this.longValue;J(), that.longValue;J())
   *  }
   *  }}}
   *
   *  we generate
   *
   *  {{{
   *  class LongBox extends java.lang.Number with Comparable {
   *    val value: long
   *    def (value: long) = { this.value = value }
   *    def longValue;J(): long = this.value.longValue;J()
   *    def toString;T(): string = this.value.toString;J()
   *    def compareTo;jlLong;Z(that: jlLong): boolean =
   *      this.value.compareTo;jlLong;Z(that)
   *  }
   *  }}}
   */
  private def deriveBoxClass(clazz: LinkedClass): LinkedClass = {
    implicit val pos: Position = clazz.pos

    val EAF = ApplyFlags.empty
    val EMF = MemberFlags.empty
    val EOH = OptimizerHints.empty
    val NON = NoOriginalName
    val NOV = Version.Unversioned

    val className = clazz.className
    val derivedClassName = className.withSuffix("Box")
    val primType = BoxedClassToPrimType(className).asInstanceOf[PrimTypeWithRef]
    val derivedThisType = ClassType(derivedClassName, nullable = false)

    val fieldName = FieldName(derivedClassName, valueFieldSimpleName)
    val fieldIdent = FieldIdent(fieldName)

    val derivedFields: List[FieldDef] = List(
      FieldDef(EMF, fieldIdent, NON, primType)
    )

    val selectField = Select(This()(derivedThisType), fieldIdent)(primType)

    val ctorParamDef =
      ParamDef(LocalIdent(fieldName.simpleName.toLocalName), NON, primType, mutable = false)
    val derivedCtor = MethodDef(
      EMF.withNamespace(MemberNamespace.Constructor),
      MethodIdent(MethodName.constructor(List(primType.primRef))),
      NON,
      List(ctorParamDef),
      NoType,
      Some(Assign(selectField, ctorParamDef.ref))
    )(EOH, NOV)

    val derivedMethods: List[MethodDef] = for {
      method <- clazz.methods if method.flags.namespace == MemberNamespace.Public
    } yield {
      MethodDef(
        method.flags,
        method.name,
        method.originalName,
        method.args,
        method.resultType,
        Some(ApplyStatically(EAF, selectField, className, method.name,
            method.args.map(_.ref))(method.resultType))
      )(method.optimizerHints, method.version)
    }

    new LinkedClass(
      ClassIdent(derivedClassName),
      Class,
      jsClassCaptures = None,
      clazz.superClass,
      clazz.interfaces,
      jsSuperClass = None,
      jsNativeLoadSpec = None,
      derivedFields,
      derivedCtor :: derivedMethods,
      jsConstructorDef = None,
      exportedMembers = Nil,
      jsNativeMembers = Nil,
      EOH,
      pos,
      ancestors = derivedClassName :: clazz.ancestors.tail,
      hasInstances = true,
      hasDirectInstances = true,
      hasInstanceTests = true,
      hasRuntimeTypeInfo = true,
      fieldsRead = Set(fieldName),
      staticFieldsRead = Set.empty,
      staticDependencies = Set.empty,
      externalDependencies = Set.empty,
      dynamicDependencies = Set.empty,
      clazz.version
    )
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy