@@ -682,16 +682,304 @@ abstract class GenJSCode extends plugins.PluginComponent
682
682
" repeated parameters." )
683
683
}
684
684
685
- // Implementation restriction
686
- for (tree <- secondaryCtorTrees) {
687
- reporter.error(tree.pos,
688
- " Implementation restriction: Scala.js-defined JS classes cannot " +
689
- " have secondary constructors" )
685
+ withNewLocalNameScope {
686
+ val infoBuilder = new MethodInfoBuilder ()
687
+ infoBuilder.setEncodedName(" constructor" )
688
+ infoBuilder.setIsExported(true )
689
+ infoBuilder.setIsStatic(false )
690
+
691
+ val infoBuilderOpt = Some (infoBuilder)
692
+
693
+ val primaryCtor =
694
+ genMethodWithInfoBuilder(primaryCtorTree, infoBuilderOpt).get._1
695
+ val secondaryCtors = secondaryCtorTrees.map { tree =>
696
+ genMethodWithInfoBuilder(tree, infoBuilderOpt).get._1
697
+ }
698
+
699
+ val dispatch =
700
+ genJSConstructorExport(constructorTrees.map(_.symbol), infoBuilder)
701
+
702
+ val isConstructor =
703
+ secondaryCtors.map(_.name.name).toSet + primaryCtor.name.name
704
+
705
+ val constructorGraph =
706
+ mkConstructorGraph(primaryCtor, secondaryCtors, isConstructor)
707
+
708
+ val overloadIdent = js.Ident (" overload" )
709
+
710
+ val js .MethodDef (_, dispatchName, dispatchArgs, dispatchResultType,
711
+ dispatchResolution) = dispatch
712
+
713
+ val overloadSelection = mkOverloadSelection(constructorGraph,
714
+ overloadIdent, dispatchResolution, isConstructor)
715
+
716
+ val preSuperCall =
717
+ constructorGraph.mkPreSuperCall(overloadIdent, dispatch, isConstructor)
718
+
719
+ val superCall = primaryCtor.body
720
+
721
+ val postSupperCall =
722
+ constructorGraph.mkPostSuperCall(overloadIdent, isConstructor)
723
+
724
+ val newBody = js.Block (overloadSelection :::
725
+ preSuperCall :: superCall :: postSupperCall :: Nil )
726
+
727
+ val newConstructor = js.MethodDef (static = false , dispatchName, dispatchArgs,
728
+ jstpe.NoType , newBody)(dispatch.optimizerHints, dispatch.hash)
729
+
730
+ // Boldly get rid of everything in infoBuilder, because it's broken
731
+ currentClassInfoBuilder.addMethod(
732
+ ir.Infos .generateMethodInfo(newConstructor))
733
+
734
+ newConstructor
735
+ }
736
+ }
737
+
738
+ private class ConstructorGraph (overrideNum : Int , method : js.MethodDef ,
739
+ subConstructors : List [ConstructorGraph ])(implicit pos : Position ) {
740
+
741
+ def methodName : String = method.name.name
742
+
743
+ def hasSubConstructors : Boolean = subConstructors.nonEmpty
744
+
745
+ lazy val overrideNumBounds : (Int , Int ) =
746
+ if (subConstructors.isEmpty) (overrideNum, overrideNum)
747
+ else (subConstructors.last.overrideNumBounds._1, overrideNum)
748
+
749
+ def get (methodName : String ): Option [ConstructorGraph ] = {
750
+ if (methodName == this .methodName) {
751
+ Some (this )
752
+ } else {
753
+ subConstructors.iterator.map(_.get(methodName)).collectFirst {
754
+ case Some (node) => node
755
+ }
756
+ }
690
757
}
691
758
692
- genMethod(primaryCtorTree).get
759
+ def getOverrideNum (methodName : String ): Int =
760
+ get(methodName).fold(- 1 )(_.getOverrideNum)
761
+
762
+ private [ConstructorGraph ] def getOverrideNum : Int = overrideNum
763
+
764
+ def getParamRefs (methodName : String ): List [js.VarRef ] =
765
+ get(methodName).fold(List .empty[js.VarRef ])(_.getParamRefs)
766
+
767
+ private [ConstructorGraph ] def getParamRefs : List [js.VarRef ] =
768
+ method.args.map(_.ref)
769
+
770
+ def mkPreSuperCall (overrideNumIdent : js.Ident , dispatch : js.MethodDef ,
771
+ isConstructor : Set [String ])(implicit pos : Position ): js.Tree = {
772
+ val overrideNumRef = js.VarRef (overrideNumIdent)(jstpe.IntType )
773
+ mkSubPreCalls(overrideNumRef, isConstructor)
774
+ }
775
+
776
+ def mkPostSuperCall (overrideNumIdent : js.Ident ,
777
+ isConstructor : Set [String ])(implicit pos : Position ): js.Tree = {
778
+ val overrideNumRef = js.VarRef (overrideNumIdent)(jstpe.IntType )
779
+ js.Block (mkSubPostCalls(overrideNumRef, isConstructor))
780
+ }
781
+
782
+ def getAllParamDefsAsVars : List [js.VarDef ] = {
783
+ val localDefs = method.args.map { pDef =>
784
+ js.VarDef (pDef.name, pDef.ptpe, true , jstpe.zeroOf(pDef.ptpe))
785
+ }
786
+ localDefs ++ subConstructors.flatMap(_.getAllParamDefsAsVars)
787
+ }
788
+
789
+ private [ConstructorGraph ] def mkSubPreCalls (overrideNumRef : js.VarRef ,
790
+ isConstructor : Set [String ])(implicit pos : Position ) = {
791
+ val overrideNumss = subConstructors.map(_.overrideNumBounds)
792
+ val paramRefs = getParamRefs
793
+ val bodies = subConstructors.map { cg =>
794
+ cg.mkPreSuperCallOnSndCtr(overrideNumRef, paramRefs, isConstructor)
795
+ }
796
+ overrideNumss.zip(bodies).foldRight[js.Tree ](js.Skip ()) {
797
+ case ((numBounds, body), acc) =>
798
+ val cond = mkOverrideNumsCond(overrideNumRef, numBounds)
799
+ js.If (cond, body, acc)(jstpe.BooleanType )
800
+ }
801
+ }
802
+
803
+ private [ConstructorGraph ] def mkPreSuperCallOnSndCtr (
804
+ overrideNumRef : js.VarRef , outputParams : List [js.VarRef ],
805
+ isConstructor : Set [String ])(implicit pos : Position ): js.Tree = {
806
+ val subCalls =
807
+ mkSubPreCalls(overrideNumRef, isConstructor)
808
+
809
+ val preSuperCall = {
810
+ method.body match {
811
+ case js.Block (stats) =>
812
+ val beforeSuperCall = stats.takeWhile {
813
+ case js.ApplyStatic (_, method, _) if isConstructor(method.name) => false
814
+
815
+ case _ => true
816
+ }
817
+ val superCallParams = stats.collectFirst {
818
+ case js.ApplyStatic (cls, method, args) if isConstructor(methodName) =>
819
+ zipMap(outputParams, args.tail) { (ref, tree) =>
820
+ js.Assign (ref, tree)
821
+ }
822
+ }.getOrElse(Nil )
823
+
824
+ beforeSuperCall ::: superCallParams
825
+
826
+ case js.ApplyStatic (cls, method, args) =>
827
+ zipMap(outputParams, args.tail) { (ref, tree) =>
828
+ js.Assign (ref, tree)
829
+ }
830
+
831
+ case _ => Nil
832
+ }
833
+ }
834
+
835
+ js.Block (subCalls :: preSuperCall)
836
+ }
837
+
838
+ private [ConstructorGraph ] def mkSubPostCalls (overrideNumRef : js.VarRef ,
839
+ isConstructor : Set [String ])(implicit pos : Position ): js.Tree = {
840
+ val overrideNumss = subConstructors.map(_.overrideNumBounds)
841
+ val bodies = subConstructors.map { cg =>
842
+ cg.mkPostSuperCallOnSndCtr(overrideNumRef, isConstructor)
843
+ }
844
+ overrideNumss.zip(bodies).foldRight[js.Tree ](js.Skip ()) {
845
+ case ((numBounds, js.Skip ()), acc) => acc
846
+
847
+ case ((numBounds, body), acc) =>
848
+ val cond = mkOverrideNumsCond(overrideNumRef, numBounds)
849
+ js.If (cond, body, acc)(jstpe.BooleanType )
850
+ }
851
+ }
852
+
853
+ private [ConstructorGraph ] def mkPostSuperCallOnSndCtr (overrideNumRef : js.VarRef ,
854
+ isConstructor : Set [String ])(implicit pos : Position ): js.Tree = {
855
+ val postSuperCall = {
856
+ method.body match {
857
+ case js.Block (stats) =>
858
+ stats.dropWhile {
859
+ case js.ApplyStatic (_, mtd, _) if isConstructor(mtd.name) => false
860
+ case _ => true
861
+ }.tail
862
+
863
+ case _ => Nil
864
+ }
865
+ }
866
+ js.Block (postSuperCall :+ mkSubPostCalls(overrideNumRef, isConstructor))
867
+ }
868
+
869
+ private def mkOverrideNumsCond (numRef : js.VarRef ,
870
+ numBounds : (Int , Int )) = numBounds match {
871
+ case (lo, hi) if lo == hi =>
872
+ js.BinaryOp (js.BinaryOp .=== , js.IntLiteral (lo), numRef)
873
+
874
+ case (lo, hi) if lo == hi - 1 =>
875
+ val lhs = js.BinaryOp (js.BinaryOp .=== , numRef, js.IntLiteral (lo))
876
+ val rhs = js.BinaryOp (js.BinaryOp .=== , numRef, js.IntLiteral (hi))
877
+ js.If (lhs, js.BooleanLiteral (true ), rhs)(jstpe.BooleanType )
878
+
879
+ case (lo, hi) =>
880
+ val lhs = js.BinaryOp (js.BinaryOp .Num_<= , js.IntLiteral (lo), numRef)
881
+ val rhs = js.BinaryOp (js.BinaryOp .Num_<= , numRef, js.IntLiteral (hi))
882
+ js.BinaryOp (js.BinaryOp .Boolean_& , lhs, rhs)
883
+ js.If (lhs, rhs, js.BooleanLiteral (false ))(jstpe.BooleanType )
884
+ }
885
+ }
886
+
887
+ private def zipMap [T , U , V ](xs : List [T ], ys : List [U ])(
888
+ f : (T , U ) => V ): List [V ] = {
889
+ if (xs.nonEmpty && ys.nonEmpty)
890
+ f(xs.head, ys.head) :: zipMap(xs.tail, ys.tail)(f)
891
+ else
892
+ Nil
693
893
}
694
894
895
+ private def mkOverloadSelection (constructorGraph : ConstructorGraph ,
896
+ overloadIdent : js.Ident , dispatchResolution : js.Tree ,
897
+ isConstructor : Set [String ])(implicit pos : Position ): List [js.Tree ] = {
898
+ if (! constructorGraph.hasSubConstructors) {
899
+ dispatchResolution match {
900
+ case js.Block (stats) =>
901
+ val js .ApplyStatic (_, method, _) = stats.last
902
+ val refs = constructorGraph.getParamRefs(method.name)
903
+ val rhss = stats.collect { case js.VarDef (_, _, _, rhs) => rhs }
904
+ zipMap(refs, rhss) { (ref, rhs) =>
905
+ js.VarDef (ref.ident, ref.tpe, false , rhs)
906
+ }
907
+
908
+ case js.ApplyStatic (cls, method, args) if isConstructor(method.name) =>
909
+ // Calls the constructor with no arguments
910
+ Nil
911
+
912
+ case tree => List (tree)
913
+ }
914
+ } else {
915
+ val overloadRef = js.VarRef (overloadIdent)(jstpe.IntType )
916
+ def assignOverloadNumbers (tree : js.Tree ): js.Tree = tree match {
917
+ case js.Block (stats) =>
918
+ val js .ApplyStatic (_, method, _) = stats.last
919
+ val num = constructorGraph.getOverrideNum(method.name)
920
+ val refs = overloadRef :: constructorGraph.getParamRefs(method.name)
921
+ val rhss = js.IntLiteral (num) ::
922
+ stats.collect { case js.VarDef (_, _, _, rhs) => rhs }
923
+ val newStats = zipMap(refs, rhss)((ref, rhs) => js.Assign (ref, rhs))
924
+ js.Block (newStats)
925
+
926
+ case js.ApplyStatic (cls, method, args) if isConstructor(method.name) =>
927
+ // Calls the constructor with no arguments
928
+ js.Assign (overloadRef,
929
+ js.IntLiteral (constructorGraph.getOverrideNum(method.name)))
930
+
931
+ case js.Match (selector, cases, default) =>
932
+ val newCases = cases.map {
933
+ case (literals, body) => (literals, assignOverloadNumbers(body))
934
+ }
935
+ js.Match (selector, newCases, default)(tree.tpe)
936
+
937
+ case js.If (cond, thenp, elsep) =>
938
+ js.If (cond, assignOverloadNumbers(thenp),
939
+ assignOverloadNumbers(elsep))(tree.tpe)
940
+
941
+ case _ => tree
942
+ }
943
+
944
+ val newDispatchResolution = assignOverloadNumbers(dispatchResolution)
945
+ val allParamDefsAsVars = constructorGraph.getAllParamDefsAsVars
946
+ val overrideNumDef =
947
+ js.VarDef (overloadIdent, jstpe.IntType , true , js.IntLiteral (0 ))
948
+
949
+ overrideNumDef :: allParamDefsAsVars ::: newDispatchResolution :: Nil
950
+ }
951
+ }
952
+
953
+ private def mkConstructorGraph (primaryCtor : js.MethodDef ,
954
+ secondaryCtors : List [js.MethodDef ], isConstructor : Set [String ])(
955
+ implicit pos : Position ): ConstructorGraph = {
956
+ def superCall (tree : js.Tree ): Option [String ] = tree match {
957
+ case js.Block (stats) =>
958
+ stats.map(superCall).collectFirst {
959
+ case Some (name) => name
960
+ }
961
+
962
+ case js.ApplyStatic (cls, method, args) if isConstructor(method.name) =>
963
+ Some (method.name)
964
+
965
+ case _ => None
966
+ }
967
+
968
+ var overrideNum = - 1
969
+ def mkConstructorGraph (method : js.MethodDef ): ConstructorGraph = {
970
+ val methodName = method.name.name
971
+ val subCtrGraphs = secondaryCtors.collect {
972
+ case subCtr if superCall(subCtr.body).exists(_ == methodName) =>
973
+ mkConstructorGraph(subCtr)
974
+ }
975
+ overrideNum += 1
976
+ new ConstructorGraph (overrideNum, method, subCtrGraphs)
977
+ }
978
+
979
+ mkConstructorGraph(primaryCtor)
980
+ }
981
+
982
+
695
983
// Generate a method -------------------------------------------------------
696
984
697
985
def genMethod (dd : DefDef ): Option [js.MethodDef ] = withNewLocalNameScope {
@@ -720,8 +1008,14 @@ abstract class GenJSCode extends plugins.PluginComponent
720
1008
*
721
1009
* Other (normal) methods are emitted with `genMethodBody()`.
722
1010
*/
1011
+
723
1012
def genMethodWithInfoBuilder (
724
1013
dd : DefDef ): Option [(js.MethodDef , MethodInfoBuilder )] = {
1014
+ genMethodWithInfoBuilder(dd, None )
1015
+ }
1016
+
1017
+ def genMethodWithInfoBuilder (dd : DefDef ,
1018
+ infoBuilderOpt : Option [MethodInfoBuilder ]): Option [(js.MethodDef , MethodInfoBuilder )] = {
725
1019
726
1020
implicit val pos = dd.pos
727
1021
val DefDef (mods, name, _, vparamss, _, rhs) = dd
@@ -742,14 +1036,16 @@ abstract class GenJSCode extends plugins.PluginComponent
742
1036
val isJSClassConstructor =
743
1037
sym.isClassConstructor && isScalaJSDefinedJSClass(currentClassSym)
744
1038
745
- val methodName : js.PropertyName =
746
- if (isJSClassConstructor) js.StringLiteral (" constructor" )
747
- else encodeMethodSym(sym)
1039
+ val methodName : js.PropertyName = encodeMethodSym(sym)
748
1040
749
- def createInfoBuilder () = {
750
- new MethodInfoBuilder ()
751
- .setEncodedName(methodName.name)
752
- .setIsStatic(sym.owner.isImplClass)
1041
+ def getInfoBuilder () = {
1042
+ if (infoBuilderOpt.isDefined) {
1043
+ infoBuilderOpt.get
1044
+ } else {
1045
+ new MethodInfoBuilder ()
1046
+ .setEncodedName(methodName.name)
1047
+ .setIsStatic(sym.owner.isImplClass)
1048
+ }
753
1049
}
754
1050
755
1051
def jsParams = for (param <- params) yield {
@@ -761,7 +1057,7 @@ abstract class GenJSCode extends plugins.PluginComponent
761
1057
if (scalaPrimitives.isPrimitive(sym)) {
762
1058
None
763
1059
} else if (sym.isDeferred || sym.owner.isInterface) {
764
- val infoBuilder = createInfoBuilder ().setIsAbstract(true )
1060
+ val infoBuilder = getInfoBuilder ().setIsAbstract(true )
765
1061
Some ((
766
1062
js.MethodDef (static = false , methodName,
767
1063
jsParams, currentClassType, js.EmptyTree )(
@@ -775,7 +1071,7 @@ abstract class GenJSCode extends plugins.PluginComponent
775
1071
None
776
1072
} else {
777
1073
withScopedVars(
778
- currentMethodInfoBuilder := createInfoBuilder (),
1074
+ currentMethodInfoBuilder := getInfoBuilder (),
779
1075
mutableLocalVars := mutable.Set .empty,
780
1076
mutatedLocalVars := mutable.Set .empty
781
1077
) {
@@ -803,14 +1099,12 @@ abstract class GenJSCode extends plugins.PluginComponent
803
1099
804
1100
val methodDef = {
805
1101
if (isJSClassConstructor) {
806
- currentMethodInfoBuilder.setIsExported(true )
807
1102
val body0 = genStat(rhs)
808
- val body1 = moveAllStatementsAfterSuperConstructorCall(body0)
809
- val (patchedParams, patchedBody) =
810
- patchFunBodyWithBoxes(sym, jsParams, body1 )
1103
6CCD
td>+ val body1 =
1104
+ if ( ! sym.isPrimaryConstructor) body0
1105
+ else moveAllStatementsAfterSuperConstructorCall(body0 )
811
1106
js.MethodDef (static = false , methodName,
812
- patchedParams, jstpe.NoType , patchedBody)(
813
- optimizerHints, None )
1107
+ jsParams, jstpe.NoType , body1)(optimizerHints, None )
814
1108
} else if (sym.isClassConstructor) {
815
1109
js.MethodDef (static = false , methodName,
816
1110
jsParams, currentClassType,
0 commit comments