Dotty Documentation

0.13.0-bin-SNAPSHOT

class JSCodeGen
extends Object

Main codegen for Scala.js IR.

[[GenSJSIR]] creates one instance of JSCodeGen per compilation unit. The run() method processes the whole compilation unit and generates .sjsir files for it.

There are 4 main levels of translation:

  • genCompilationUnit() iterates through all the type definitions in the compilation unit. Each generated js.ClassDef is serialized to an .sjsir file.
  • genScalaClass() and other similar methods generate the skeleton of classes.
  • genMethod() and similar methods generate the declarations of methods.
  • genStatOrExpr() and everything else generate the bodies of methods.

[-] Constructors

JSCodeGen ( )
JSCodeGen ( implicit ctx: Context )

[-] Members

[+] private object JSBinaryOpMethodName
[+] private object JSUnaryOpMethodName
[+] private object MaybeAsInstanceOf
[+] private object WrapArray
[+] private implicit val ctx : Context
[+] private val currentClassSym : ScopedVar [ Symbol ]
[+] private val currentMethodSym : ScopedVar [ Symbol ]
[+] private val desugared : IdentityHashMap [ Type, Select ]
[+] private lazy val externalEquals : Symbol
[+] private lazy val externalEqualsNumChar : Symbol
[+] private lazy val externalEqualsNumNum : Symbol
[+] private lazy val externalEqualsNumObject : Symbol
[+] protected lazy val isHijackedClass : Set [ Symbol ]
[+] private var isModuleInitialized : Boolean
[+] private val jsdefn : JSDefinitions
[+] private val localNames : ScopedVar [ LocalNameGenerator ]
[+] private val positionConversions : JSPositions
[+] private val primitives : JSPrimitives
[+] private val thisLocalVarIdent : ScopedVar [ Option [ Ident ] ]
[+] private val undefinedDefaultParams : ScopedVar [ Set [ Symbol ] ]
[+] private def adaptPrimitive ( value: Tree , to: Type ) ( implicit pos: Position ) : Tree
[+] private def box ( expr: Tree , tpeEnteringElimErasedValueType: Type ) ( implicit pos: Position ) : Tree

Boxes a value of the given type before elimErasedValueType.

This should be used when sending values to a JavaScript context, which is erased/boxed at the IR level, although it is not erased at the dotty/JVM level.

[+] private def currentClassType : Type
[+] def desugarIdent ( i: Ident ) : Option [ Select ]
[+] private def exprToStat ( tree: Tree ) : Tree

Turn a JavaScript expression of type Unit into a statement

[+] private def extractFirstArg ( args: List [ TreeOrJSSpread ] ) : ( Tree, List [ TreeOrJSSpread ] )

Extract the first argument in a list of actual arguments.

This is nothing else than decomposing into head and tail, except that we assert that the first element is not a JSSpread.

[+] private def freshLocalIdent ( ) ( implicit pos: Position ) : Ident

Returns a new fresh local identifier.

[+] private def freshLocalIdent ( base: String ) ( implicit pos: Position ) : Ident

Returns a new fresh local identifier.

[+] private def genActualArgs ( sym: Symbol , args: List [ Tree ] ) ( implicit pos: Position ) : List [ Tree ]

Gen actual actual arguments to Scala method call. Returns a list of the transformed arguments.

This tries to optimize repeated arguments (varargs) by turning them into js.WrappedArray instead of Scala wrapped arrays.

[+] private def genActualJSArgs ( sym: Symbol , args: List [ Tree ] ) ( implicit pos: Position ) : List [ TreeOrJSSpread ]

Gen actual actual arguments to a JS method call. Returns a list of the transformed arguments.

  • TODO Repeated arguments (varargs) are expanded
  • Default arguments are omitted or replaced by undefined
  • All arguments are boxed

Repeated arguments that cannot be expanded at compile time (i.e., if a Seq is passed to a varargs parameter with the syntax seq: _*) will be wrapped in a [[js.JSSpread]] node to be expanded at runtime.

[+] private def genApply ( tree: Apply , isStat: Boolean ) : Tree

Gen JS code for an Apply node (method call)

There's a whole bunch of varieties of Apply nodes: regular method calls, super calls, constructor calls, isInstanceOf/asInstanceOf, primitives, JS calls, etc. They are further dispatched in here.

[+] private def genApplyJSClassMethod ( receiver: Tree , method: Symbol , arguments: List [ Tree ] ) ( implicit pos: Position ) : Tree

Gen a call to a non-exposed method of a non-native JS class.

[+] private def genApplyJSMethodGeneric ( tree: Tree , sym: Symbol , receiver: Tree , args: List [ TreeOrJSSpread ] , isStat: Boolean , jsSuperClassValue: Option [ Tree ] ) ( implicit pos: Position ) : Tree

Gen JS code for a call to a JS method (of a subclass of js.Any).

Basically it boils down to calling the method as a JSBracketSelect, without name mangling. But other aspects come into play:

  • Operator methods are translated to JS operators (not method calls)
  • apply is translated as a function call, i.e., o() instead of o.apply()
  • Scala varargs are turned into JS varargs (see genPrimitiveJSArgs())
  • Getters and parameterless methods are translated as JSBracketSelect
  • Setters are translated to Assign to JSBracketSelect
[+] private def genApplyJSMethodGeneric$default$6 : None.type

Gen JS code for a call to a JS method (of a subclass of js.Any).

Basically it boils down to calling the method as a JSBracketSelect, without name mangling. But other aspects come into play:

  • Operator methods are translated to JS operators (not method calls)
  • apply is translated as a function call, i.e., o() instead of o.apply()
  • Scala varargs are turned into JS varargs (see genPrimitiveJSArgs())
  • Getters and parameterless methods are translated as JSBracketSelect
  • Setters are translated to Assign to JSBracketSelect
[+] private def genApplyMethod ( receiver: Tree , methodSym: Symbol , arguments: List [ Tree ] ) ( implicit pos: Position ) : Tree

Gen a dynamically linked call to a Scala method.

[+] private def genApplyMethodStatically ( receiver: Tree , method: Symbol , arguments: List [ Tree ] ) ( implicit pos: Position ) : Tree

Gen a statically linked call to an instance method.

[+] private def genApplyNew ( tree: Apply ) : Tree

Gen JS code for a constructor call (new). Further refined into: * new String(...) * new of a hijacked boxed class * new of an anonymous function class that was recorded as JS function * new of a raw JS class * new Array * regular new

[+] private def genApplyStatic ( method: Symbol , arguments: List [ Tree ] ) ( implicit pos: Position ) : Tree

Gen a call to a static method.

[+] private def genArrayOp ( tree: Tree , code: Int ) : Tree

Gen JS code for an array operation (get, set or length)

[+] private def genAsInstanceOf ( value: Tree , to: Type ) ( implicit pos: Position ) : Tree

Gen JS code for an asInstanceOf cast (for reference types only)

[+] private def genClassConstant ( tpe: Type ) ( implicit pos: Position ) : Tree

Generate a Class[_] value (e.g. coming from classOf[T])

[+] private def genClassFields ( td: TypeDef ) : List [ FieldDef ]

Gen definitions for the fields of a class.

[+] private def genClassInterfaces ( sym: ClassSymbol ) ( implicit pos: Position ) : List [ Ident ]
[+] private def genClosure ( tree: Closure ) : Tree

Gen JS code for a closure.

Input: a Closure tree of the form {{{ Closure(env, call, functionalInterface) }}} representing the pseudo-syntax {{{ { (p1, ..., pm) => call(env1, ..., envn, p1, ..., pm) }: functionInterface }}} where envi are identifiers in the local scope. The qualifier of call is also implicitly captured.

Output: a js.Closure tree of the form {{{ js.Closure(formalCaptures, formalParams, body, actualCaptures) }}} representing the pseudo-syntax {{{ lambda<formalCapture1 = actualCapture1, ..., formalCaptureN = actualCaptureN>( formalParam1, ..., formalParamM) = body }}} where the actualCaptures and body are, in general, arbitrary expressions. But in this case, actualCaptures will be identifiers from env, and the body will be of the form {{{ call(formalCapture1.ref, ..., formalCaptureN.ref, formalParam1.ref, ...formalParamM.ref) }}}

When the js.Closure node is evaluated, i.e., when the closure value is created, the expressions of the actualCaptures are evaluated, and the results of those evaluations is "stored" in the environment of the closure as the corresponding formalCapture.

When we later call the closure, the formalCaptures already have their values from the environment, and they are available in the body. The formalParams of the created closure receive their values from the actual arguments at the call-site of the closure, and they are also available in the body.

[+] private def genCoercion ( tree: Apply , receiver: Tree , code: Int ) : Tree

Gen JS code for a coercion

[+] private def genCompilationUnit ( cunit: CompilationUnit ) : Unit

Generates the Scala.js IR for a compilation unit This method iterates over all the class and interface definitions found in the compilation unit and emits their IR (.sjsir).

Some classes are never actually emitted: - Classes representing primitive types - The scala.Array class

TODO Some classes representing anonymous functions are not actually emitted. Instead, a temporary representation of their apply method is built and recorded, so that it can be inlined as a JavaScript anonymous function in the method that instantiates it.

Other ClassDefs are emitted according to their nature: * Scala.js-defined JS class -> genScalaJSDefinedJSClass() * Other raw JS type (<: js.Any) -> genRawJSClassData() * Interface -> genInterface() * Normal class -> genClass()

[+] private def genConversion ( from: Type , to: Type , value: Tree ) ( implicit pos: Position ) : Tree
[+] private def genEqEqPrimitive ( ltpe: Type , rtpe: Type , lsrc: Tree , rsrc: Tree ) ( implicit pos: Position ) : Tree

Gen JS code for a call to Any.==

[+] private def genExpr ( tree: Tree ) : Tree

Gen JS code for a tree in expression position (in the IR).

[+] private def genIRFile ( cunit: CompilationUnit , sym: Symbol , tree: ClassDef ) : Unit
[+] private def genInterface ( td: TypeDef ) : ClassDef

Gen the IR ClassDef for an interface definition.

[+] private def genIsInstanceOf ( tree: Tree , value: Tree , to: Type ) : Tree

Gen JS code for an isInstanceOf test (for reference types only)

[+] private def genJSRepeatedParam ( arg: Tree ) : List [ TreeOrJSSpread ]

Gen JS code for a repeated param of a JS method.

In this case arg has type Seq[T] for some T, but the result should be an expanded list of the elements in the sequence. So this method takes care of the conversion.

It is specialized for the shapes of tree generated by the desugaring of repeated params in Scala, so that these are actually expanded at compile-time.

Otherwise, it returns a JSSpread with the Seq converted to a js.Array.

[+] private def genJavaSeqLiteral ( tree: JavaSeqLiteral ) : Tree

Gen JS code for a Java Seq literal.

[+] private def genLoadJSConstructor ( sym: Symbol ) ( implicit pos: Position ) : Tree

Gen JS code representing the constructor of a JS class.

[+] private def genLoadJSGlobal ( ) ( implicit pos: Position ) : Tree

Gen JS code to load the JavaScript global scope.

[+] private def genLoadModule ( sym0: Symbol ) ( implicit pos: Position ) : Tree

Gen JS code for loading a module.

Can be given either the module symbol, or its module class symbol.

[+] private def genLoadNativeJSModule ( sym: Symbol ) ( implicit pos: Position ) : Tree

Gen JS code representing a native JS module.

[+] private def genLoadStaticField ( sym: Symbol ) ( implicit pos: Position ) : Tree

Gen JS code for loading a Java static field.

[+] private def genMethod ( dd: DefDef ) : Option [ MethodDef ]
[+] private def genMethodDef ( static: Boolean , methodName: PropertyName , paramsSyms: List [ Symbol ] , resultIRType: Type , tree: Tree , optimizerHints: OptimizerHints ) : MethodDef

Generates the MethodDef of a (non-constructor) method

Most normal methods are emitted straightforwardly. If the result type is Unit, then the body is emitted as a statement. Otherwise, it is emitted as an expression.

Methods Scala.js-defined JS classes are compiled as static methods taking an explicit parameter for their this value.

[+] private def genMethodWithCurrentLocalNameScope ( dd: DefDef ) : Option [ MethodDef ]

Gen JS code for a method definition in a class or in an impl class. On the JS side, method names are mangled to encode the full signature of the Scala method, as described in JSEncoding, to support overloading.

Some methods are not emitted at all: - Primitives, since they are never actually called - Constructors of hijacked classes

Constructors are emitted by generating their body as a statement.

Other (normal) methods are emitted with genMethodBody().

[+] private def genModuleApplyMethod ( methodSym: Symbol , arguments: List [ Tree ] ) ( implicit pos: Position ) : Tree

Gen a call to a method of a Scala top-level module.

[+] private def genNewHijackedClass ( clazz: Symbol , ctor: Symbol , args: List [ Tree ] ) ( implicit pos: Position ) : Tree

Gen JS code for a call to a constructor of a hijacked class. Reroute them to the new method with the same signature in the companion object.

[+] private def genNormalApply ( tree: Apply , isStat: Boolean ) : Tree

Gen a "normal" apply (to a true method).

But even these are further refined into: * Methods of java.lang.String, which are redirected to the RuntimeString trait implementation. * Calls to methods of raw JS types (Scala.js -> JS interop) * Calls to methods in impl classes of Scala2 traits. * Regular method call

[+] private def genPrimitiveOp ( tree: Apply , isStat: Boolean ) : Tree

Gen JS code for a primitive method call.

[+] private def genRawJSClassData ( td: TypeDef ) : ClassDef

Gen the IR ClassDef for a raw JS class or trait.

[+] private def genScalaClass ( td: TypeDef ) : ClassDef

Gen the IR ClassDef for a Scala class definition (maybe a module class).

[+] private def genScalaHash ( tree: Apply , receiver: Tree ) : Tree

Gen JS code for a call to Any.##

[+] private def genScalaJSDefinedJSClass ( td: TypeDef ) : ClassDef

Gen the IR ClassDef for a Scala.js-defined JS class.

[+] private def genSimpleBinaryOp ( tree: Apply , lhs: Tree , rhs: Tree , code: Int ) : Tree

Gen JS code for a simple binary operation.

[+] private def genSimpleOp ( tree: Apply , args: List [ Tree ] , code: Int ) : Tree

Gen JS code for a simple operation (arithmetic, logical, or comparison)

[+] private def genSimpleUnaryOp ( tree: Apply , arg: Tree , code: Int ) : Tree

Gen JS code for a simple unary operation.

[+] private def genStat ( tree: Tree ) : Tree

Gen JS code for a tree in statement position (in the IR).

[+] private def genStatOrExpr ( tree: Tree , isStat: Boolean ) : Tree

Gen JS code for a tree in statement or expression position (in the IR).

This is the main transformation method. Each node of the Scala AST is transformed into an equivalent portion of the JS AST.

[+] private def genStringConcat ( tree: Apply , receiver: Tree , args: List [ Tree ] ) : Tree

Gen JS code for string concatenation.

[+] private def genSuperCall ( tree: Apply , isStat: Boolean ) : Tree

Gen JS code for a super call, of the form Class.super[mix].fun(args).

This does not include calls defined in mixin traits, as these are already desugared by the 'mixin' phase. Only calls to super classes remain.

Since a class has exactly one direct superclass, and calling a method two classes above the current one is invalid in Scala, the mix item is irrelevant.

[+] private def genSynchronized ( tree: Apply , isStat: Boolean ) : Tree

Gen JS code for a call to AnyRef.synchronized

[+] private def genThis ( ) ( implicit pos: Position ) : Tree

Gen JS this of the current class. Normally encoded straightforwardly as a JS this. But must be replaced by the thisLocalVarIdent local variable if there is one.

[+] private def genThrow ( tree: Apply , args: List [ Tree ] ) : Tree

Gen a call to the special throw method.

[+] private def genTraitImplApply ( method: Symbol , arguments: List [ Tree ] ) ( implicit pos: Position ) : Tree

Gen a call to a Scala2 impl class method.

[+] private def genTypeApply ( tree: TypeApply ) : Tree

Gen JS code for a call to a polymorphic method.

The only methods that reach the back-end as polymorphic are isInstanceOf and asInstanceOf.

(Well, in fact DottyRunTime.newRefArray too, but it is handled as a primitive instead.)

[+] private def genUniversalEqualityOp ( ltpe: Type , rtpe: Type , lhs: Tree , rhs: Tree , code: Int ) ( implicit pos: Position ) : Tree

Gen JS code for a universal equality test.

[+] private def getFileFor ( cunit: CompilationUnit , sym: Symbol , suffix: String ) : AbstractFile
[+] private implicit def implicitLocalNames : LocalNameGenerator

Implicitly materializes the current local name generator.

[+] private def isPrimitiveValueType ( tpe: Type ) : Boolean
[+] private def isStaticModule ( sym: Symbol ) : Boolean
[+] private def makePrimitiveBox ( expr: Tree , tpe: Type ) ( implicit pos: Position ) : Tree

Gen a boxing operation (tpe is the primitive type)

[+] private def makePrimitiveUnbox ( expr: Tree , tpe: Type ) ( implicit pos: Position ) : Tree

Gen an unboxing operation (tpe is the primitive type)

[+] private def qualifierOf ( fun: Tree ) : Tree
[+] def run ( ) : Unit
[+] private def tryGenRepeatedParamAsJSArray ( arg: Tree , handleNil: Boolean ) : Option [ List [ Tree ] ]

Try and expand an actual argument to a repeated param (xs: T*).

This method recognizes the shapes of tree generated by the desugaring of repeated params in Scala, and expands them. If arg does not have the shape of a generated repeated param, this method returns None.

[+] private def unbox ( expr: Tree , tpeEnteringElimErasedValueType: Type ) ( implicit pos: Position ) : Tree

Unboxes a value typed as Any to the given type before elimErasedValueType.

This should be used when receiving values from a JavaScript context, which is erased/boxed at the IR level, although it is not erased at the dotty/JVM level.