@@ -125,7 +125,14 @@ class JSCodeGen()(using genCtx: Context) {
125125 /** Implicitly materializes the current local name generator. */
126126 implicit def implicitLocalNames : LocalNameGenerator = localNames.get
127127
128- private def currentClassType = encodeClassType(currentClassSym)
128+ def currentThisType : jstpe.Type = {
129+ encodeClassType(currentClassSym) match {
130+ case tpe @ jstpe.ClassType (cls) =>
131+ jstpe.BoxedClassToPrimType .getOrElse(cls, tpe)
132+ case tpe =>
133+ tpe
134+ }
135+ }
129136
130137 /** Returns a new fresh local identifier. */
131138 private def freshLocalIdent ()(implicit pos : Position ): js.LocalIdent =
@@ -1023,7 +1030,7 @@ class JSCodeGen()(using genCtx: Context) {
10231030 // Constructor of a non-native JS class ------------------------------------
10241031
10251032 def genJSClassCapturesAndConstructor (constructorTrees : List [DefDef ])(
1026- implicit pos : SourcePosition ): (List [js.ParamDef ], js.JSMethodDef ) = {
1033+ implicit pos : SourcePosition ): (List [js.ParamDef ], js.JSConstructorDef ) = {
10271034 /* We need to merge all Scala constructors into a single one because the
10281035 * IR, like JavaScript, only allows a single one.
10291036 *
@@ -1095,20 +1102,21 @@ class JSCodeGen()(using genCtx: Context) {
10951102 (exports.result(), jsClassCaptures.result())
10961103 }
10971104
1105+ // The name 'constructor' is used for error reporting here
10981106 val (formalArgs, restParam, overloadDispatchBody) =
10991107 jsExportsGen.genOverloadDispatch(JSName .Literal (" constructor" ), exports, jstpe.IntType )
11001108
11011109 val overloadVar = js.VarDef (freshLocalIdent(" overload" ), NoOriginalName ,
11021110 jstpe.IntType , mutable = false , overloadDispatchBody)
11031111
1104- val ctorStats = genJSClassCtorStats(overloadVar.ref, ctorTree)
1105-
1106- val constructorBody = js.Block (
1107- paramVarDefs ::: List (overloadVar, ctorStats, js.Undefined ()))
1112+ val constructorBody = wrapJSCtorBody(
1113+ paramVarDefs :+ overloadVar,
1114+ genJSClassCtorBody(overloadVar.ref, ctorTree),
1115+ js.Undefined () :: Nil
1116+ )
11081117
1109- val constructorDef = js.JSMethodDef (
1110- js.MemberFlags .empty,
1111- js.StringLiteral (" constructor" ),
1118+ val constructorDef = js.JSConstructorDef (
1119+ js.MemberFlags .empty.withNamespace(js.MemberNamespace .Constructor ),
11121120 formalArgs, restParam, constructorBody)(OptimizerHints .empty, None )
11131121
11141122 (jsClassCaptures, constructorDef)
@@ -1150,7 +1158,8 @@ class JSCodeGen()(using genCtx: Context) {
11501158 assert(jsSuperCall.isDefined,
11511159 s " Did not find Super call in primary JS construtor at ${dd.sourcePos}" )
11521160
1153- new PrimaryJSCtor (sym, genParamsAndInfo(sym, dd.paramss), jsSuperCall.get :: jsStats.result())
1161+ new PrimaryJSCtor (sym, genParamsAndInfo(sym, dd.paramss),
1162+ js.JSConstructorBody (Nil , jsSuperCall.get, jsStats.result())(dd.span))
11541163 }
11551164
11561165 private def genSecondaryJSClassCtor (dd : DefDef ): SplitSecondaryJSCtor = {
@@ -1251,9 +1260,9 @@ class JSCodeGen()(using genCtx: Context) {
12511260 (jsExport, jsClassCaptures)
12521261 }
12531262
1254- /** generates a sequence of JS constructor statements based on a constructor tree. */
1255- private def genJSClassCtorStats (overloadVar : js.VarRef ,
1256- ctorTree : ConstructorTree [PrimaryJSCtor ])(implicit pos : Position ): js.Tree = {
1263+ /** Generates a JS constructor body based on a constructor tree. */
1264+ private def genJSClassCtorBody (overloadVar : js.VarRef ,
1265+ ctorTree : ConstructorTree [PrimaryJSCtor ])(implicit pos : Position ): js.JSConstructorBody = {
12571266
12581267 /* generates a statement that conditionally executes body iff the chosen
12591268 * overload is any of the descendants of `tree` (including itself).
@@ -1348,21 +1357,27 @@ class JSCodeGen()(using genCtx: Context) {
13481357 val primaryCtor = ctorTree.ctor
13491358 val secondaryCtorTrees = ctorTree.subCtors
13501359
1351- js. Block (
1352- secondaryCtorTrees.map(preStats(_, primaryCtor.paramsAndInfo)) ++
1353- primaryCtor.body ++
1360+ wrapJSCtorBody (
1361+ secondaryCtorTrees.map(preStats(_, primaryCtor.paramsAndInfo)),
1362+ primaryCtor.body,
13541363 secondaryCtorTrees.map(postStats(_))
13551364 )
13561365 }
13571366
1367+ private def wrapJSCtorBody (before : List [js.Tree ], body : js.JSConstructorBody ,
1368+ after : List [js.Tree ]): js.JSConstructorBody = {
1369+ js.JSConstructorBody (before ::: body.beforeSuper, body.superCall,
1370+ body.afterSuper ::: after)(body.pos)
1371+ }
1372+
13581373 private sealed trait JSCtor {
13591374 val sym : Symbol
13601375 val paramsAndInfo : List [(Symbol , JSParamInfo )]
13611376 }
13621377
13631378 private class PrimaryJSCtor (val sym : Symbol ,
13641379 val paramsAndInfo : List [(Symbol , JSParamInfo )],
1365- val body : List [ js.Tree ] ) extends JSCtor
1380+ val body : js.JSConstructorBody ) extends JSCtor
13661381
13671382 private class SplitSecondaryJSCtor (val sym : Symbol ,
13681383 val paramsAndInfo : List [(Symbol , JSParamInfo )],
@@ -1945,9 +1960,9 @@ class JSCodeGen()(using genCtx: Context) {
19451960 }*/
19461961
19471962 thisLocalVarIdent.fold[js.Tree ] {
1948- js.This ()(currentClassType )
1963+ js.This ()(currentThisType )
19491964 } { thisLocalIdent =>
1950- js.VarRef (thisLocalIdent)(currentClassType )
1965+ js.VarRef (thisLocalIdent)(currentThisType )
19511966 }
19521967 }
19531968
@@ -2014,9 +2029,7 @@ class JSCodeGen()(using genCtx: Context) {
20142029
20152030 val (exceptValDef, exceptVar) = if (mightCatchJavaScriptException) {
20162031 val valDef = js.VarDef (freshLocalIdent(" e" ), NoOriginalName ,
2017- encodeClassType(defn.ThrowableClass ), mutable = false , {
2018- genModuleApplyMethod(jsdefn.Runtime_wrapJavaScriptException , origExceptVar :: Nil )
2019- })
2032+ encodeClassType(defn.ThrowableClass ), mutable = false , js.WrapAsThrowable (origExceptVar))
20202033 (valDef, valDef.ref)
20212034 } else {
20222035 (js.Skip (), origExceptVar)
@@ -2307,7 +2320,7 @@ class JSCodeGen()(using genCtx: Context) {
23072320 val privateFieldDefs = mutable.ListBuffer .empty[js.FieldDef ]
23082321 val classDefMembers = mutable.ListBuffer .empty[js.MemberDef ]
23092322 val instanceMembers = mutable.ListBuffer .empty[js.MemberDef ]
2310- var constructor : Option [js.JSMethodDef ] = None
2323+ var constructor : Option [js.JSConstructorDef ] = None
23112324
23122325 originalClassDef.memberDefs.foreach {
23132326 case fdef : js.FieldDef =>
@@ -2321,17 +2334,13 @@ class JSCodeGen()(using genCtx: Context) {
23212334 " Non-static, unexported method in non-native JS class" )
23222335 classDefMembers += mdef
23232336
2324- case mdef : js.JSMethodDef =>
2325- mdef.name match {
2326- case js.StringLiteral (" constructor" ) =>
2327- assert(! mdef.flags.namespace.isStatic, " Exported static method" )
2328- assert(constructor.isEmpty, " two ctors in class" )
2329- constructor = Some (mdef)
2337+ case cdef : js.JSConstructorDef =>
2338+ assert(constructor.isEmpty, " two ctors in class" )
2339+ constructor = Some (cdef)
23302340
2331- case _ =>
2332- assert(! mdef.flags.namespace.isStatic, " Exported static method" )
2333- instanceMembers += mdef
2334- }
2341+ case mdef : js.JSMethodDef =>
2342+ assert(! mdef.flags.namespace.isStatic, " Exported static method" )
2343+ instanceMembers += mdef
23352344
23362345 case property : js.JSPropertyDef =>
23372346 instanceMembers += property
@@ -2361,7 +2370,7 @@ class JSCodeGen()(using genCtx: Context) {
23612370 val jsClassCaptures = originalClassDef.jsClassCaptures.getOrElse {
23622371 throw new AssertionError (s " no class captures for anonymous JS class at $pos" )
23632372 }
2364- val js .JSMethodDef (_, _, ctorParams, ctorRestParam, ctorBody) = constructor.getOrElse {
2373+ val js .JSConstructorDef ( _, ctorParams, ctorRestParam, ctorBody) = constructor.getOrElse {
23652374 throw new AssertionError (" No ctor found" )
23662375 }
23672376 assert(ctorParams.isEmpty && ctorRestParam.isEmpty,
@@ -2396,6 +2405,9 @@ class JSCodeGen()(using genCtx: Context) {
23962405 case mdef : js.MethodDef =>
23972406 throw new AssertionError (" unexpected MethodDef" )
23982407
2408+ case cdef : js.JSConstructorDef =>
2409+ throw new AssertionError (" unexpected JSConstructorDef" )
2410+
23992411 case mdef : js.JSMethodDef =>
24002412 implicit val pos = mdef.pos
24012413 val impl = memberLambda(mdef.args, mdef.restParam, mdef.body)
@@ -2468,36 +2480,43 @@ class JSCodeGen()(using genCtx: Context) {
24682480 }
24692481
24702482 // Transform the constructor body.
2471- val inlinedCtorStats = new ir.Transformers .Transformer {
2472- override def transform (tree : js.Tree , isStat : Boolean ): js.Tree = tree match {
2473- // The super constructor call. Transform this into a simple new call.
2474- case js.JSSuperConstructorCall (args) =>
2475- implicit val pos = tree.pos
2476-
2477- val newTree = {
2478- val ident = originalClassDef.superClass.getOrElse(throw new FatalError (" No superclass" ))
2479- if (args.isEmpty && ident.name == JSObjectClassName )
2480- js.JSObjectConstr (Nil )
2481- else
2482- js.JSNew (jsSuperClassRef, args)
2483- }
2484-
2485- js.Block (
2486- js.VarDef (selfName, thisOriginalName, jstpe.AnyType , mutable = false , newTree) ::
2487- memberDefinitions)
2483+ val inlinedCtorStats : List [js.Tree ] = {
2484+ val beforeSuper = ctorBody.beforeSuper
24882485
2489- case js.This () =>
2490- selfRef(tree.pos)
2486+ val superCall = {
2487+ implicit val pos = ctorBody.superCall.pos
2488+ val js .JSSuperConstructorCall (args) = ctorBody.superCall
24912489
2492- // Don't traverse closure boundaries
2493- case closure : js.Closure =>
2494- val newCaptureValues = closure.captureValues.map(transformExpr)
2495- closure.copy(captureValues = newCaptureValues)(closure.pos)
2490+ val newTree = {
2491+ val ident = originalClassDef.superClass.getOrElse(throw new FatalError (" No superclass" ))
2492+ if (args.isEmpty && ident.name == JSObjectClassName )
2493+ js.JSObjectConstr (Nil )
2494+ else
2495+ js.JSNew (jsSuperClassRef, args)
2496+ }
24962497
2497- case tree =>
2498- super .transform(tree, isStat)
2498+ val selfVarDef = js. VarDef (selfName, thisOriginalName, jstpe. AnyType , mutable = false , newTree)
2499+ selfVarDef :: memberDefinitions
24992500 }
2500- }.transform(ctorBody, isStat = true )
2501+
2502+ // After the super call, substitute `selfRef` for `This()`
2503+ val afterSuper = new ir.Transformers .Transformer {
2504+ override def transform (tree : js.Tree , isStat : Boolean ): js.Tree = tree match {
2505+ case js.This () =>
2506+ selfRef(tree.pos)
2507+
2508+ // Don't traverse closure boundaries
2509+ case closure : js.Closure =>
2510+ val newCaptureValues = closure.captureValues.map(transformExpr)
2511+ closure.copy(captureValues = newCaptureValues)(closure.pos)
2512+
2513+ case tree =>
2514+ super .transform(tree, isStat)
2515+ }
2516+ }.transformStats(ctorBody.afterSuper)
2517+
2518+ beforeSuper ::: superCall ::: afterSuper
2519+ }
25012520
25022521 val closure = js.Closure (arrow = true , jsClassCaptures, Nil , None ,
25032522 js.Block (inlinedCtorStats, selfRef), jsSuperClassValue :: args)
@@ -2989,14 +3008,12 @@ class JSCodeGen()(using genCtx: Context) {
29893008 implicit val pos : SourcePosition = tree.sourcePos
29903009 val exception = args.head
29913010 val genException = genExpr(exception)
2992- js.Throw {
2993- if (exception.tpe.typeSymbol.derivesFrom(jsdefn.JavaScriptExceptionClass )) {
2994- genModuleApplyMethod(
2995- jsdefn.Runtime_unwrapJavaScriptException ,
2996- List (genException))
2997- } else {
2998- genException
2999- }
3011+ genException match {
3012+ case js.New (cls, _, _) if cls != JavaScriptExceptionClassName =>
3013+ // Common case where ex is neither null nor a js.JavaScriptException
3014+ js.Throw (genException)
3015+ case _ =>
3016+ js.Throw (js.UnwrapFromThrowable (genException))
30003017 }
30013018 }
30023019
@@ -3982,6 +3999,53 @@ class JSCodeGen()(using genCtx: Context) {
39823999 js.JSFunctionApply (fVarDef.ref, List (keyVarRef))
39834000 }))
39844001
4002+ case JS_THROW =>
4003+ // js.special.throw(arg)
4004+ js.Throw (genArgs1)
4005+
4006+ case JS_TRY_CATCH =>
4007+ /* js.special.tryCatch(arg1, arg2)
4008+ *
4009+ * We must generate:
4010+ *
4011+ * val body = arg1
4012+ * val handler = arg2
4013+ * try {
4014+ * body()
4015+ * } catch (e) {
4016+ * handler(e)
4017+ * }
4018+ *
4019+ * with temporary vals, because `arg2` must be evaluated before
4020+ * `body` executes. Moreover, exceptions thrown while evaluating
4021+ * the function values `arg1` and `arg2` must not be caught.
4022+ */
4023+ val (arg1, arg2) = genArgs2
4024+ val bodyVarDef = js.VarDef (freshLocalIdent(" body" ), NoOriginalName ,
4025+ jstpe.AnyType , mutable = false , arg1)
4026+ val handlerVarDef = js.VarDef (freshLocalIdent(" handler" ), NoOriginalName ,
4027+ jstpe.AnyType , mutable = false , arg2)
4028+ val exceptionVarIdent = freshLocalIdent(" e" )
4029+ val exceptionVarRef = js.VarRef (exceptionVarIdent)(jstpe.AnyType )
4030+ js.Block (
4031+ bodyVarDef,
4032+ handlerVarDef,
4033+ js.TryCatch (
4034+ js.JSFunctionApply (bodyVarDef.ref, Nil ),
4035+ exceptionVarIdent,
4036+ NoOriginalName ,
4037+ js.JSFunctionApply (handlerVarDef.ref, List (exceptionVarRef))
4038+ )(jstpe.AnyType )
4039+ )
4040+
4041+ case WRAP_AS_THROWABLE =>
4042+ // js.special.wrapAsThrowable(arg)
4043+ js.WrapAsThrowable (genArgs1)
4044+
4045+ case UNWRAP_FROM_THROWABLE =>
4046+ // js.special.unwrapFromThrowable(arg)
4047+ js.UnwrapFromThrowable (genArgs1)
4048+
39854049 case UNION_FROM | UNION_FROM_TYPE_CONSTRUCTOR =>
39864050 /* js.|.from and js.|.fromTypeConstructor
39874051 * We should not have to deal with those. They have a perfectly valid
@@ -4764,6 +4828,7 @@ object JSCodeGen {
47644828
47654829 private val NullPointerExceptionClass = ClassName (" java.lang.NullPointerException" )
47664830 private val JSObjectClassName = ClassName (" scala.scalajs.js.Object" )
4831+ private val JavaScriptExceptionClassName = ClassName (" scala.scalajs.js.JavaScriptException" )
47674832
47684833 private val ObjectClassRef = jstpe.ClassRef (ir.Names .ObjectClass )
47694834
0 commit comments