From 78ce1c81052fb15e64efb5af798d7bb43b119ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 15 Jan 2025 17:46:39 +0100 Subject: [PATCH] Fix #5115: Apply the StoreModule hack to JSConstructorDef from 1.8. Before 1.11, the body of `JSConstructorDef`s was a straight list of trees. We have a hack that extracts the three parts of a structured `JSConstructorBody`: `beforeSuper`, `superCall` and `afterSuper`. In 1.18, we introduced a separate hack to ensure that `afterSuper` starts with a `StoreModule` instruction. Unfortunately, we did not apply the latter hack on the result of the former. This means our deserializer incorrectly reads JS module classes from before 1.11. This commit makes sure to also apply the hack in that situation. Unfortunately, this does not come with automated tests. We do not have any non-native JS module class in our standard library, which is currently our only source of old IR for backward compatibility tests. --- .../scala/org/scalajs/ir/Serializers.scala | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala index bf50ef07fc..707dfc61d7 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala @@ -1543,7 +1543,7 @@ object Serializers { val (jsConstructor, jsMethodProps) = { if (hacks.use8 && kind.isJSClass) { assert(jsConstructorBuilder.result().isEmpty, "found JSConstructorDef in pre 1.8 IR") - jsConstructorHack(jsMethodPropsBuilder.result()) + jsConstructorHack(kind, jsMethodPropsBuilder.result()) } else { (jsConstructorBuilder.result(), jsMethodPropsBuilder.result()) } @@ -1803,7 +1803,7 @@ object Serializers { newInstanceRecMethod :: newMethods } - private def jsConstructorHack( + private def jsConstructorHack(ownerKind: ClassKind, jsMethodProps: List[JSMethodPropDef]): (Option[JSConstructorDef], List[JSMethodPropDef]) = { val jsConstructorBuilder = new OptionBuilder[JSConstructorDef] val jsMethodPropsBuilder = List.newBuilder[JSMethodPropDef] @@ -1817,8 +1817,9 @@ object Serializers { } bodyStats.span(!_.isInstanceOf[JSSuperConstructorCall]) match { - case (beforeSuper, (superCall: JSSuperConstructorCall) :: afterSuper) => + case (beforeSuper, (superCall: JSSuperConstructorCall) :: afterSuper0) => val newFlags = flags.withNamespace(MemberNamespace.Constructor) + val afterSuper = maybeHackJSConstructorDefAfterSuper(ownerKind, afterSuper0, superCall.pos) val newBody = JSConstructorBody(beforeSuper, superCall, afterSuper)(body.pos) val ctorDef = JSConstructorDef(newFlags, args, restParam, newBody)( methodDef.optimizerHints, Unversioned)(methodDef.pos) @@ -1966,19 +1967,22 @@ object Serializers { val beforeSuper = readTrees() val superCall = readTree().asInstanceOf[JSSuperConstructorCall] val afterSuper0 = readTrees() + val afterSuper = maybeHackJSConstructorDefAfterSuper(ownerKind, afterSuper0, superCall.pos) + val body = JSConstructorBody(beforeSuper, superCall, afterSuper)(bodyPos) + JSConstructorDef(flags, params, restParam, body)( + OptimizerHints.fromBits(readInt()), optHash) + } - val afterSuper = if (hacks.use17 && ownerKind == ClassKind.JSModuleClass) { + private def maybeHackJSConstructorDefAfterSuper(ownerKind: ClassKind, + afterSuper0: List[Tree], superCallPos: Position): List[Tree] = { + if (hacks.use17 && ownerKind == ClassKind.JSModuleClass) { afterSuper0 match { case StoreModule() :: _ => afterSuper0 - case _ => StoreModule()(superCall.pos) :: afterSuper0 + case _ => StoreModule()(superCallPos) :: afterSuper0 } } else { afterSuper0 } - - val body = JSConstructorBody(beforeSuper, superCall, afterSuper)(bodyPos) - JSConstructorDef(flags, params, restParam, body)( - OptimizerHints.fromBits(readInt()), optHash) } private def readJSMethodDef()(implicit pos: Position): JSMethodDef = {