Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #15701: Implement js.dynamicImport for dynamic module loading. #15720

Merged
merged 4 commits into from
Jul 23, 2022

Conversation

sjrd
Copy link
Member

@sjrd sjrd commented Jul 21, 2022

No description provided.

sjrd added 3 commits July 22, 2022 10:33
In dotc, `private val`s do not generate a getter method, so always
relying on the getter to generate the `JSNativeMemberDef` and
reading it is not correct.

We now generate `JSNativeMemberDef`s both for `ValDef`s and for
`DefDef`s that are not Accessors. For reading, we support both
`Select`s of fields and `Apply`s of methods.
This ensures that the module-only features are tested.
* that can be dynamically loaded. Moving members across that boundary changes
* the dynamic and static dependencies between ES modules, which is forbidden.
*/
ctx.settings.scalajs.value && encClass.isSubClass(jsdefn.DynamicImportThunkClass)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this enough to guarantee that no member is ever moved across a DynamicImportThunk class boundary?

Scala 2 did not move things around like that, so this is new code compared to the commit we're porting.

Comment on lines 299 to 301
val applySym = newSymbol(cls, nme.apply, Method, MethodType(Nil, Nil, defn.AnyType), coord = span).entered
val newBody = transform(body).changeOwnerAfter(currentOwner, applySym, thisPhase)
val applyDefDef = DefDef(applySym, newBody)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the correct way to completely move a tree, and everything that it contains, inside the new def apply(): Any method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, looks correct to me.

Comment on lines 295 to 297
val cls = newNormalizedClassSymbol(currentOwner, tpnme.ANON_CLASS, Synthetic | Final,
List(jsdefn.DynamicImportThunkType), coord = span)
val constr = newConstructor(cls, Synthetic, Nil, Nil).entered
Copy link
Member Author

@sjrd sjrd Jul 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of wanted to use tpe.AnonClass here, but it only creates forwarder methods, whereas I need a new method (apply) that actually contains the tree body. Is there a better way to do this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not aware of a more suitable method. But maybe we can factor out the useful parts of tpd.AnonClass and call them from both AnonClass and here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, there was some opportunity for refactoring. In fact, it extended to two other places in the codebase which followed the same pattern. I have added a commit that factors out the common pattern from those 4 places. Does it look good to you?

@sjrd
Copy link
Member Author

sjrd commented Jul 22, 2022

@odersky May I ask you to review this one? This is mostly a port, but I have pointed out a few places where I'm not sure what I'm doing. The most critical is the change in lambdalift's Dependencies, which git blame traces to you.

The most important tests for these features are in https://github.com/scala-js/scala-js/blob/v1.10.1/test-suite/js/src/test/require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTest.scala and are brought in by the last commit of this PR. Without the change in lambdalift, the test localDefDynamic() fails, for example, because the def apply gets lifted in SJSDynamicImportTest instead of being kept in the anonymous DynamicImportThunk class.

@sjrd sjrd requested a review from odersky July 22, 2022 10:03
Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only reviewed the changes to the non-js compiler code base. These look all good to me.

Comment on lines 295 to 297
val cls = newNormalizedClassSymbol(currentOwner, tpnme.ANON_CLASS, Synthetic | Final,
List(jsdefn.DynamicImportThunkType), coord = span)
val constr = newConstructor(cls, Synthetic, Nil, Nil).entered
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not aware of a more suitable method. But maybe we can factor out the useful parts of tpd.AnonClass and call them from both AnonClass and here?

Comment on lines 299 to 301
val applySym = newSymbol(cls, nme.apply, Method, MethodType(Nil, Nil, defn.AnyType), coord = span).entered
val newBody = transform(body).changeOwnerAfter(currentOwner, applySym, thisPhase)
val applyDefDef = DefDef(applySym, newBody)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, looks correct to me.

@odersky odersky assigned sjrd and unassigned odersky Jul 22, 2022
@sjrd sjrd assigned odersky and unassigned sjrd Jul 23, 2022
There are several places where we create typed anonymous classes.
All followed the same pattern, which we now factored out in a new
`tpe.AnonClass` overload.
Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactoring was clearly worth it. It's a nice simplification!

val parents1 =
if (parents.head.classSymbol.is(Trait)) {
val head = parents.head.parents.head
if (head.isRef(defn.AnyClass)) defn.AnyRefType :: parents else head :: parents
}
else parents
val cls = newNormalizedClassSymbol(owner, tpnme.ANON_CLASS, Synthetic | Final, parents1,
coord = fns.map(_.span).reduceLeft(_ union _))
val cls = newNormalizedClassSymbol(owner, tpnme.ANON_CLASS, Synthetic | Final, parents1, coord = coord)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: If it's the same name, consider just writing it once. I.e. coord instead of coord = coord.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't. There are other parameters with default values in the middle, namely selfInfo and privateWithin. So we need coord =.

@odersky odersky assigned sjrd and unassigned odersky Jul 23, 2022
@sjrd sjrd merged commit 837b1cc into scala:main Jul 23, 2022
@sjrd sjrd deleted the sjs-dynamic-import branch July 23, 2022 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants