8000 Fix #1795: Essentials of Scala.js-defined JS classes. by sjrd · Pull Request #1809 · scala-js/scala-js · GitHub
[go: up one dir, main page]

Skip to content

Fix #1795: Essentials of Scala.js-defined JS classes. #1809

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

Merged
merged 9 commits into from
Aug 24, 2015

Conversation

sjrd
Copy link
Member
@sjrd sjrd commented Jul 19, 2015

This PR implements most of the specification for Scala.js-defined JavaScript classes, as discussed in #1795.

The following features are not yet supported, and are reported as implementation restrictions:

This PR does not implement the deprecations that would lead to nicer definitions either. These aspects will come as follow ups, since they are not critical to the general specification.

@sjrd
Copy link
Member Author
sjrd commented Jul 19, 2015

CI only for now.

Or for the curious, you can dive in. One interesting thing is the tests :)

@sjrd
Copy link
Member Author
sjrd commented Jul 19, 2015

Also, suggestions for more pos tests are welcome :)

val genInitialThis = genThis() // outside of withScopedVars
val thisSym = thisDef.symbol
withScopedVars(
thisLocalVarIdent := Some(encodeLocalSym(thisSym))
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider making a val out of encodeLocalSyms result.

Copy link
Contributor

Choose a reason for hiding this comment

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

(will allow you to not write .get.get below).

Copy link
Member Author

Choose a reason for hiding this comment

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

OK.

@gzm0
Copy link
Contributor
gzm0 commented Jul 20, 2015

What about overload of normal methods? I didn't see any tests for it.

@sjrd
Copy link
Member Author
sjrd commented Jul 20, 2015

What about overload of normal methods? I didn't see any tests for it.

https://github.com/scala-js/scala-js/pull/1809/files#diff-6e8c360252085a4ec6f673609bd75631R395 ("simple overloaded methods" + "renamed overloaded methods")

I sort of trust the tests in ExportsTest for all the weird combinations or param count/types/repeated/default, since the same code is used in both cases.

@sjrd sjrd force-pushed the sjs-defined-js-classes branch 3 times, most recently from ffd799a to 34470e0 Compare July 20, 2015 17:07
@sjrd
Copy link
Member Author
sjrd commented Jul 20, 2015

And there we go! :D
Review by @gzm0, please :-p

More pos tests still welcome, I have this feeling that I must have missed some.

@sjrd sjrd changed the title Essentials of Scala.js-defined JS classes Fix #1795: Essentials of Scala.js-defined JS classes. Jul 20, 2015
@sjrd
Copy link
Member Author
sjrd commented Jul 20, 2015

Self-review: I forgot to forbid overloading of private methods.

@sjrd
Copy link
Member Author
sjrd commented Jul 20, 2015

The compiler test ScalaJSDefinedTest.noSuperConstructorCallWithSpread has different error messages in 2.10 (wrt. positions).

@sjrd sjrd force-pushed the sjs-defined-js-classes branch from 34470e0 to 96a6911 Compare July 21, 2015 08:42
@sjrd
Copy link
Member Author
sjrd commented Jul 21, 2015

Updated with the error messages to prevent overloading of private methods. And also to prevent over_riding_ of private methods, actually.

@sjrd sjrd force-pushed the sjs-defined-js-classes branch 2 times, most recently from f9822c7 to 552d972 Compare July 22, 2015 10:57
if (methodTailJumpThisSym.get != NoSymbol) {
js.VarRef(encodeLocalSym(methodTailJumpThisSym))(currentClassType)
if (thisLocalVarIdent.get.isDefined) {
js.VarRef(thisLocalVarIdent.get.get)(currentClassType)
Copy link
Contributor

Choose a reason for hiding this comment

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

Still use fold?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point.

@gzm0
Copy link
Contributor
gzm0 commented Jul 22, 2015

LGTM unti bca957a

@gzm0
Copy link
Contributor
gzm0 commented Jul 22, 2015

That's all until 5dc9d6b

val result = body
enclosingOwner = oldEnclosingOwner
allEnclosingOwners = oldAllEnclosingOwners
result
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmm.. maybe we should protect this by a finally block.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok

@gzm0
Copy link
Contributor
gzm0 commented Jul 22, 2015

Would you mind separating the state cleanup in PrepJSInterop into a separate commit? It is very hard to see which changes come from the Scala.js defined classes implementation and which come from the refactor.

for (methodInfo <- methodInfos.values) {
if (methodInfo.isExported)
methodInfo.reach(this)(FromExports)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this not done in the same place than for normal classes? IIUC, the export reachability for normal classes is actually too aggressive. It reaches even if the class is not instantiated...

Copy link
Member Author

Choose a reason for hiding this comment

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

Exports on normal classes take overriding into account. With:

class Foo {
  @JSExport def foobar(): Int = 42
}
class Bar extends Foo {
  @JSExport override def foobar(): Int = 3
}
new Bar

you'll have that Foo["foobar"] can be dce'ed away, because Foo is not instantiated. Only Bar["foobar"] is reachable. This is implemented through callMethod which actually delays the actual call until the class is instantiated(). We are allowed to do this because the semantics of Scala classes and exports do not guarantee anything about what will be present in the prototypes.

For JS classes, however, it is necessary to expose everything as soon as the constructor is reachable, because the classes could be extended by JS classes, or otherwise reflected upon using the JS prototype.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, makes sense. I was confused by callMethod and delayedCallMethod...

@gzm0
Copy link
Contributor
gzm0 commented Aug 22, 2015

That's all until eda1c38

| def foo(x: Int): Int = x + 1
| ^
"""
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Please also add a neg test for private members in traits

Copy link
Member Author

Choose a reason for hiding this comment

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

ok

Copy link
Contributor

Choose a reason for hiding this comment

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

Did you address this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, yes, I saw it. The error message is a bit convoluted, but that's fine. No need to complicate compiler code. Who puts private methods in traits you cannot put implementations in anyways...

@gzm0
Copy link
Contributor
gzm0 commented Aug 22, 2015

That's all.

sjrd added 3 commits August 22, 2015 21:04
This commit implements most of the specification for
Scala.js-defined JavaScript classes, as discussed in scala-js#1795.

The following features are not yet supported, and are reported
as implementation restrictions:

* Secondary constructors
* Exporting a Scala.js-defined class
* Calling the super constructor with :_* to a varargs param.

This commit does not implement the deprecations that would lead
to nicer definitions either. These aspects will come as follow
ups, since they are not critical to the general specification.
A JS trait can be `@ScalaJSDefined` if it does not contain any
concrete term member, and does not extend a native JS trait.
Such a Scala.js-defined JS trait can be implemented by a
Scala.js-defined JS class.

Note that a Scala.js-defined JS trait can very well extend a
(native or not) JS class. A Scala.js-defined JS class that mixes
in this trait will necessarily inherit from the parent JS class,
and therefore it already has all its members.

The top-level trait `js.Any` is marked as `@ScalaJSDefined`, so
that Scala.js-defined JS traits can directly extend it, without
binding themselves to `js.Object` or another JS class.
With Scala.js-defined JS classes, it makes sense to have
abstract members in native JS classes and traits, to force
a Scala.js-defined JS class extending them to implement
those members.
@sjrd sjrd force-pushed the sjs-defined-js-classes branch from 6166131 to 29314b6 Compare August 22, 2015 19:16
@sjrd
Copy link
Member Author
sjrd commented Aug 22, 2015

Updated. I should have addressed all the comments, except the fixes to super constructor call with spread. What would like me to do: fix directly within "Essentials of ..."; fix as an additional commit referencing #1813; or fix in a separate PR for #1813?

@sjrd
Copy link
Member Author
sjrd commented Aug 22, 2015

What would like me to do: fix directly within "Essentials of ..."; fix as an additional commit referencing #1813; or fix in a separate PR for #1813?

Well, actually, we lack the infrastructure in desugar to emit spread calls, at the moment. So fixing it requires substantial changes which should definitely go in a different PR anyway.

@scala-jenkins
Copy link

Refer to this link for build results (access rights to CI server needed):

https://scala-webapps.epfl.ch/jenkins/job/scalajs-pr/2120/
https://scala-webapps.epfl.ch/jenkins/job/scalajs-matrix-build/2741/
Test PASSed.

@gzm0
Copy link
Contributor
gzm0 commented Aug 24, 2015

Well, actually, we lack the infrastructure in desugar to emit spread calls, at the moment. So fixing it requires substantial changes which should definitely go in a different PR anyway.

Completely fine with that.

@gzm0
Copy link
Contributor
gzm0 commented Aug 24, 2015

LGTM

gzm0 added a commit that referenced this pull request Aug 24, 2015
Fix #1795: Essentials of Scala.js-defined JS classes.
@gzm0 gzm0 merged commit e8cf4a8 into scala-js:master Aug 24, 2015
@sjrd
Copy link
Member Author
sjrd commented Aug 24, 2015

Yeepee! :-)
Thanks a lot for that review.

@sjrd sjrd deleted the sjs-defined-js-classes branch August 24, 2015 05:55
@gzm0
Copy link
Contributor
gzm0 commented Aug 24, 2015

Brace Yourself - The followup PRs are coming

@sjrd
Copy link
Member Author
sjrd commented Aug 24, 2015

You have no idea :-p
But unlike the introduction of non-boxing after -- what was it? -- 0.4.4, I haven't them ready yet ;-)

@gzm0
Copy link
Contributor
gzm0 commented Aug 24, 2015

I don't know. But it was terrible :)

After 2000 LOC PR:
Me: "LGTM, phew done"
You: "Ohhhhhhh, btw, I had these nice 5 1000 LOC PRs each depending on the one we just merged"
Me: 😭

sjrd added a commit to dotty-staging/dotty that referenced this pull request Sep 18, 2020
This commit implements the entire specification of non-native JS
classes, including nested ones, except the behavior of *anonymous*
JS classes. Anonymous JS classes are not supposed to have their own
class/prototype, but currently they still do.

The most important PRs to scala-js/scala-js that define what is
implemented here were:

1. scala-js/scala-js#1809
   Essentials of Scala.js-defined classes (former name of
   non-native JS classes)
2. scala-js/scala-js#1982
   Support for secondary constructors
3. scala-js/scala-js#2186
   Support for default parameters in constructors
4. scala-js/scala-js#2659
   Support for `js.Symbol`s in `@JSName` annotations
5. scala-js/scala-js#3161
   Nested JS classes

However, this commit was written more based on the state of things
at Scala.js v1.2.0 rather than as a port of the abovementioned PRs.

The support is spread over 4 components:

* The `ExplicitJSClasses` phase, which reifies all nested JS
  classes, and has extensive documentation in the code.
* The `JSExportsGen` component of the back-end, which creates
  dispatchers for run-time overloading, default parameters and
  variadic arguments (equivalent to `GenJSExports` in Scala 2).
* The `JSConstructorGen` component, which massages the constructors
  of JS classes and their dispatcher into a unique JS constructor.
* Bits and pieces in `JSCodeGen`, notably to generate the
  `js.ClassDef`s for non-native JS classes, orchestrate the two
  other back-end components, and to adapt calls to the members of
  non-native JS classes.

`JSConstructorGen` in particular is copied quasi-verbatim from
pieces of `GenJSCode` in Scala 2, since it works on `js.IRNode`s,
without knowledge of scalac/dotc data structures.
sjrd added a commit to dotty-staging/dotty that referenced this pull request Sep 18, 2020
This commit implements the entire specification of non-native JS
classes, including nested ones, except the behavior of *anonymous*
JS classes. Anonymous JS classes are not supposed to have their own
class/prototype, but currently they still do.

The most important PRs to scala-js/scala-js that define what is
implemented here were:

1. scala-js/scala-js#1809
   Essentials of Scala.js-defined classes (former name of
   non-native JS classes)
2. scala-js/scala-js#1982
   Support for secondary constructors
3. scala-js/scala-js#2186
   Support for default parameters in constructors
4. scala-js/scala-js#2659
   Support for `js.Symbol`s in `@JSName` annotations
5. scala-js/scala-js#3161
   Nested JS classes

However, this commit was written more based on the state of things
at Scala.js v1.2.0 rather than as a port of the abovementioned PRs.

The support is spread over 4 components:

* The `ExplicitJSClasses` phase, which reifies all nested JS
  classes, and has extensive documentation in the code.
* The `JSExportsGen` component of the back-end, which creates
  dispatchers for run-time overloading, default parameters and
  variadic arguments (equivalent to `GenJSExports` in Scala 2).
* The `JSConstructorGen` component, which massages the constructors
  of JS classes and their dispatcher into a unique JS constructor.
* Bits and pieces in `JSCodeGen`, notably to generate the
  `js.ClassDef`s for non-native JS classes, orchestrate the two
  other back-end components, and to adapt calls to the members of
  non-native JS classes.

`JSConstructorGen` in particular is copied quasi-verbatim from
pieces of `GenJSCode` in Scala 2, since it works on `js.IRNode`s,
without knowledge of scalac/dotc data structures.
sjrd added a commit to dotty-staging/dotty that referenced this pull request Sep 24, 2020
This commit implements the entire specification of non-native JS
classes, including nested ones, except the behavior of *anonymous*
JS classes. Anonymous JS classes are not supposed to have their own
class/prototype, but currently they still do.

The most important PRs to scala-js/scala-js that define what is
implemented here were:

1. scala-js/scala-js#1809
   Essentials of Scala.js-defined classes (former name of
   non-native JS classes)
2. scala-js/scala-js#1982
   Support for secondary constructors
3. scala-js/scala-js#2186
   Support for default parameters in constructors
4. scala-js/scala-js#2659
   Support for `js.Symbol`s in `@JSName` annotations
5. scala-js/scala-js#3161
   Nested JS classes

However, this commit was written more based on the state of things
at Scala.js v1.2.0 rather than as a port of the abovementioned PRs.

The support is spread over 4 components:

* The `ExplicitJSClasses` phase, which reifies all nested JS
  classes, and has extensive documentation in the code.
* The `JSExportsGen` component of the back-end, which creates
  dispatchers for run-time overloading, default parameters and
  variadic arguments (equivalent to `GenJSExports` in Scala 2).
* The `JSConstructorGen` component, which massages the constructors
  of JS classes and their dispatcher into a unique JS constructor.
* Bits and pieces in `JSCodeGen`, notably to generate the
  `js.ClassDef`s for non-native JS classes, orchestrate the two
  other back-end components, and to adapt calls to the members of
  non-native JS classes.

`JSConstructorGen` in particular is copied quasi-verbatim from
pieces of `GenJSCode` in Scala 2, since it works on `js.IRNode`s,
without knowledge of scalac/dotc data structures.
sjrd added a commit to dotty-staging/dotty that referenced this pull request Oct 2, 2020
This commit implements the entire specification of non-native JS
classes, including nested ones, except the behavior of *anonymous*
JS classes. Anonymous JS classes are not supposed to have their own
class/prototype, but currently they still do.

The most important PRs to scala-js/scala-js that define what is
implemented here were:

1. scala-js/scala-js#1809
   Essentials of Scala.js-defined classes (former name of
   non-native JS classes)
2. scala-js/scala-js#1982
   Support for secondary constructors
3. scala-js/scala-js#2186
   Support for default parameters in constructors
4. scala-js/scala-js#2659
   Support for `js.Symbol`s in `@JSName` annotations
5. scala-js/scala-js#3161
   Nested JS classes

However, this commit was written more based on the state of things
at Scala.js v1.2.0 rather than as a port of the abovementioned PRs.

The support is spread over 4 components:

* The `ExplicitJSClasses` phase, which reifies all nested JS
  classes, and has extensive documentation in the code.
* The `JSExportsGen` component of the back-end, which creates
  dispatchers for run-time overloading, default parameters and
  variadic arguments (equivalent to `GenJSExports` in Scala 2).
* The `JSConstructorGen` component, which massages the constructors
  of JS classes and their dispatcher into a unique JS constructor.
* Bits and pieces in `JSCodeGen`, notably to generate the
  `js.ClassDef`s for non-native JS classes, orchestrate the two
  other back-end components, and to adapt calls to the members of
  non-native JS classes.

`JSConstructorGen` in particular is copied quasi-verbatim from
pieces of `GenJSCode` in Scala 2, since it works on `js.IRNode`s,
without knowledge of scalac/dotc data structures.
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.

3 participants
0