diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureAstTransformer.scala b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureAstTransformer.scala index fa759a4858..79ad4562ec 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureAstTransformer.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureAstTransformer.scala @@ -43,7 +43,8 @@ private class ClosureAstTransformer(featureSet: FeatureSet, def transformScript(topLevelTrees: List[Tree]): Node = { val script = setNodePosition(new Node(Token.SCRIPT), NoPosition) - transformBlockStats(topLevelTrees)(NoPosition).foreach(script.addChildToBack(_)) + for (stat <- topLevelTrees) + script.addChildToBack(transformStat(stat)(NoPosition)) script.putProp(Node.FEATURE_SET, featureSet) script } @@ -55,6 +56,20 @@ private class ClosureAstTransformer(featureSet: FeatureSet, implicit val pos = pos_in wrapTransform(tree) { + case JSDocConstructor(tree) => + val node = transformStat(tree) + // The @constructor must be propagated through an ExprResult node + val trg = + if (node.isExprResult()) node.getChildAtIndex(0) + else node + val ctorDoc = { + val b = JSDocInfo.builder() + b.recordConstructor() + b.build() + } + trg.setJSDocInfo(ctorDoc) + node + case VarDef(ident, optRhs) => val node = transformName(ident) optRhs.foreach(rhs => node.addChildToFront(transformExpr(rhs))) @@ -448,45 +463,11 @@ private class ClosureAstTransformer(featureSet: FeatureSet, def transformBlock(stats: List[Tree], blockPos: Position): Node = { val block = new Node(Token.BLOCK) - for (node <- transformBlockStats(stats)(blockPos)) - block.addChildToBack(node) + for (stat <- stats) + block.addChildToBack(transformStat(stat)(blockPos)) block } - def transformBlockStats(stats: List[Tree])( - implicit parentPos: Position): List[Node] = { - - @inline def ctorDoc(): JSDocInfo = { - val b = JSDocInfo.builder() - b.recordConstructor() - b.build() - } - - // The Rhino IR attaches DocComments to the following nodes (rather than - // having individual nodes). We preprocess these here. - @tailrec - def loop(ts: List[Tree], nextIsCtor: Boolean, acc: List[Node]): List[Node] = ts match { - case DocComment(text) :: tss => - loop(tss, nextIsCtor = text.startsWith("@constructor"), acc) - - case t :: tss => - val node = transformStat(t) - if (nextIsCtor) { - // The @constructor must be propagated through an ExprResult node - val trg = - if (node.isExprResult()) node.getChildAtIndex(0) - else node - trg.setJSDocInfo(ctorDoc()) - } - loop(tss, nextIsCtor = false, node :: acc) - - case Nil => - acc.reverse - } - - loop(stats, nextIsCtor = false, Nil) - } - @inline private def wrapTransform(tree: Tree)(body: Tree => Node)( implicit pos: Position): Node = { diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala index 144672a471..fc7054c1e6 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala @@ -214,14 +214,14 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { } yield { ( // Real constructor - js.DocComment("@constructor") :: - realCtorDef ::: + js.JSDocConstructor(realCtorDef.head) :: + realCtorDef.tail ::: chainProto ::: (genIdentBracketSelect(ctorVar.prototype, "constructor") := ctorVar) :: // Inheritable constructor - js.DocComment("@constructor") :: - inheritableCtorDef ::: + js.JSDocConstructor(inheritableCtorDef.head) :: + inheritableCtorDef.tail ::: (globalVar(VarField.h, className).prototype := ctorVar.prototype) :: Nil ) } @@ -251,8 +251,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val ctorVar = fileLevelVar(VarField.b, genName(className)) - js.DocComment("@constructor") :: - (ctorVar := ctorFun) :: + js.JSDocConstructor(ctorVar := ctorFun) :: chainPrototypeWithLocalCtor(className, ctorVar, superCtor) ::: (genIdentBracketSelect(ctorVar.prototype, "constructor") := ctorVar) :: Nil } @@ -344,8 +343,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) { val dummyCtor = fileLevelVar(VarField.hh, genName(className)) List( - js.DocComment("@constructor"), - genConst(dummyCtor.ident, js.Function(false, Nil, None, js.Skip())), + js.JSDocConstructor(genConst(dummyCtor.ident, js.Function(false, Nil, None, js.Skip()))), dummyCtor.prototype := superCtor.prototype, ctorVar.prototype := js.New(dummyCtor, Nil) ) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Printers.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Printers.scala index 9690946e69..4d675d4dec 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Printers.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Printers.scala @@ -139,27 +139,11 @@ object Printers { } tree match { - // Comments - - case DocComment(text) => - val lines = text.split("\n").toList - if (lines.tail.isEmpty) { - print("/** ") - print(lines.head) - print(" */") - } else { - print("/** ") - print(lines.head) - println(); printIndent() - var rest = lines.tail - while (rest.nonEmpty) { - print(" * ") - print(rest.head) - println(); printIndent() - rest = rest.tail - } - print(" */") - } + case JSDocConstructor(tree) => + print("/** @constructor */") + println(); printIndent() + // not printStat: we must not print the trailing newline. + printTree(tree, isStat = true) // Definitions diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala index 27da8e50c1..efcf98e609 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala @@ -39,9 +39,9 @@ object Trees { } } - // Comments + // Constructor comment / annotation. - sealed case class DocComment(text: String)(implicit val pos: Position) + sealed case class JSDocConstructor(tree: Tree)(implicit val pos: Position) extends Tree // Identifiers and properties diff --git a/linker/shared/src/test/scala/org/scalajs/linker/backend/javascript/PrintersTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/backend/javascript/PrintersTest.scala index 316f2c3907..86c9215f02 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/backend/javascript/PrintersTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/backend/javascript/PrintersTest.scala @@ -89,12 +89,14 @@ class PrintersTest { ) } - @Test def printDocComment(): Unit = { + @Test def printJSDocConstructor(): Unit = { assertPrintEquals( """ - | /** test */ + |/** @constructor */ + |ctor = (function() { + |}); """, - DocComment("test") + JSDocConstructor(Assign(VarRef("ctor"), Function(false, Nil, None, Skip()))) ) }