@@ -54,23 +54,19 @@ object PrepJSExports {
54
54
// Scala classes are never exported: Their constructors are.
55
55
val isScalaClass = sym.isClass && ! sym.isOneOf(Trait | Module ) && ! isJSAny(sym)
56
56
57
+ // Filter constructors of module classes: The module classes themselves will be exported.
58
+ val isModuleClassCtor = sym.isConstructor && sym.owner.is(ModuleClass )
59
+
57
60
val exports =
58
- if (isScalaClass) Nil
61
+ if (isScalaClass || isModuleClassCtor ) Nil
59
62
else exportsOf(sym)
60
63
61
64
assert(exports.isEmpty || ! sym.is(Bridge ),
62
65
s " found exports for bridge symbol $sym. exports: $exports" )
63
66
64
- if (sym.isClass || sym.isConstructor) {
65
- /* we can generate constructors, classes and modules entirely in the backend,
66
- * since they do not need inheritance and such.
67
- */
68
- Nil
69
- } else {
70
- // For normal exports, generate exporter methods.
71
- val normalExports = exports.filter(_.destination == ExportDestination .Normal )
72
- normalExports.flatMap(exp => genExportDefs(sym, exp.jsName, exp.pos.span))
73
- }
67
+ // For normal exports, generate exporter methods.
68
+ val normalExports = exports.filter(_.destination == ExportDestination .Normal )
69
+ normalExports.flatMap(exp => genExportDefs(sym, exp.jsName, exp.pos.span))
74
70
}
75
71
76
72
/** Computes the ExportInfos for sym from its annotations. */
@@ -83,6 +79,10 @@ object PrepJSExports {
83
79
else sym
84
80
}
85
81
82
+ val symOwner =
83
+ if (sym.isConstructor) sym.owner.owner
84
+ else sym.owner
85
+
86
86
val JSExportAnnot = jsdefn.JSExportAnnot
87
87
val JSExportTopLevelAnnot = jsdefn.JSExportTopLevelAnnot
88
88
val JSExportStaticAnnot = jsdefn.JSExportStaticAnnot
@@ -92,13 +92,22 @@ object PrepJSExports {
92
92
val directMemberAnnots = Set [Symbol ](JSExportAnnot , JSExportTopLevelAnnot , JSExportStaticAnnot )
93
93
val directAnnots = trgSym.annotations.filter(annot => directMemberAnnots.contains(annot.symbol))
94
94
95
- // Is this a member export (i.e. not a class or module export)?
96
- val isMember = ! sym.isClass && ! sym.isConstructor
97
-
98
- // Annotations for this member on the whole unit
95
+ /* Annotations for this member on the whole unit
96
+ *
97
+ * Note that for top-level classes / modules this is always empty, because
98
+ * packages cannot have annotations.
99
+ */
99
100
val unitAnnots = {
100
- if (isMember && sym.isPublic && ! sym.is(Synthetic ))
101
- sym.owner.annotations.filter(_.symbol == JSExportAllAnnot )
101
+ val useExportAll = {
102
+ sym.isPublic &&
103
+ ! sym.is(Synthetic ) &&
104
+ ! sym.isConstructor &&
105
+ ! sym.is(Trait ) &&
106
+ (! sym.isClass || sym.is(ModuleClass ))
107
+ }
108
+
109
+ if (useExportAll)
110
+ symOwner.annotations.filter(_.symbol == JSExportAllAnnot )
102
111
else
103
112
Nil
104
113
}
@@ -139,7 +148,13 @@ object PrepJSExports {
139
148
" dummy"
140
149
}
141
150
} else {
142
- sym.defaultJSName
151
+ val name = (if (sym.isConstructor) sym.owner else sym).defaultJSName
152
+ if (name.endsWith(str.SETTER_SUFFIX ) && ! sym.isJSSetter) {
153
+ report.error(
154
+ " You must set an explicit name when exporting a non-setter with a name ending in _=" ,
155
+ exportPos)
156
+ }
157
+ name
143
158
}
144
159
}
145
160
@@ -166,20 +181,12 @@ object PrepJSExports {
166
181
if (! isTopLevelExport && name.contains(" __" ))
167
182
report.error(" An exported name may not contain a double underscore (`__`)" , exportPos)
168
183
169
- val symOwner =
170
- if (sym.isConstructor) sym.owner.owner
171
- else sym.owner
172
-
173
184
// Destination-specific restrictions
174
185
destination match {
175
186
case ExportDestination .Normal =>
176
- // Disallow @JSExport at the top-level, as well as on objects and classes
187
+ // Disallow @JSExport on top-level definitions.
177
188
if (symOwner.is(Package ) || symOwner.isPackageObject) {
178
189
report.error(" @JSExport is forbidden on top-level definitions. Use @JSExportTopLevel instead." , exportPos)
179
- } else if (! isMember && ! sym.is(Trait )) {
180
- report.error(
181
- " @JSExport is forbidden on objects and classes. Use @JSExport'ed factory methods instead." ,
182
- exportPos)
183
190
}
184
191
185
192
// Make sure we do not override the default export of toString
@@ -243,19 +250,19 @@ object PrepJSExports {
243
250
exportPos)
244
251
}
245
252
246
- if (isMember) {
247
- if (sym.is(Lazy ))
248
- report.error(" You may not export a lazy val as static" , exportPos)
253
+ if (sym.is(Lazy ))
254
+ report.error(" You may not export a lazy val as static" , exportPos)
249
255
250
- // Illegal function application export
251
- if (! hasExplicitName && sym.name == nme.apply) {
252
- report.error(
253
- " A member cannot be exported to function application as " +
254
- " static. Use @JSExportStatic(\" apply\" ) to export it under " +
255
- " the name 'apply'." ,
256
- exportPos)
257
- }
258
- } else {
256
+ // Illegal function application export
257
+ if (! hasExplicitName && sym.name == nme.apply) {
258
+ report.error(
259
+ " A member cannot be exported to function application as " +
260
+ " static. Use @JSExportStatic(\" apply\" ) to export it under " +
261
+ " the name 'apply'." ,
262
+ exportPos)
263
+ }
264
+
265
+ if (sym.isClass || sym.isConstructor) {
259
266
report.error(" Implementation restriction: cannot export a class or object as static" , exportPos)
260
267
}
261
268
}
@@ -375,31 +382,41 @@ object PrepJSExports {
375
382
}
376
383
377
384
/** Generates an exporter for a DefDef including default parameter methods. */
378
- private def genExportDefs (defSym : Symbol , jsName : String , span : Span )(using Context ): List [Tree ] = {
379
- val clsSym = defSym.owner.asClass
385
+ private def genExportDefs (sym : Symbol , jsName : String , span : Span )(using Context ): List [Tree ] = {
386
+ val siblingSym =
387
+ if (sym.isConstructor) sym.owner
388
+ else sym
389
+
390
+ val clsSym = siblingSym.owner.asClass
391
+
392
+ val isProperty = sym.is(ModuleClass ) || isJSAny(sym) || sym.isJSProperty
393
+
394
+ val copiedFlags0 = (siblingSym.flags & (Protected | Final )).toTermFlags
395
+ val copiedFlags =
396
+ if (siblingSym.is(HasDefaultParams )) copiedFlags0 | HasDefaultParams // term flag only
397
+ else copiedFlags0
380
398
381
399
// Create symbol for new method
382
- val name = makeExportName(jsName, ! defSym.is(Method ) || defSym.isJSProperty)
383
- val flags = (defSym.flags | Method | Synthetic )
384
- &~ (Deferred | Accessor | ParamAccessor | CaseAccessor | Mutable | Lazy | Override )
400
+ val scalaName = makeExportName(jsName, ! sym.is(Method ) || sym.isJSProperty)
401
+ val flags = Method | Synthetic | copiedFlags
385
402
val info =
386
- if (defSym .isConstructor) defSym .info
387
- else if (defSym .is(Method )) finalResultTypeToAny(defSym .info)
403
+ if (sym .isConstructor) sym .info
404
+ else if (sym .is(Method )) finalResultTypeToAny(sym .info)
388
405
else ExprType (defn.AnyType )
389
- val expSym = newSymbol(clsSym, name , flags, info, defSym .privateWithin, span).entered
406
+ val expSym = newSymbol(clsSym, scalaName , flags, info, sym .privateWithin, span).entered
390
407
391
408
// Construct exporter DefDef tree
392
- val exporter = genProxyDefDef(clsSym, defSym , expSym, span)
409
+ val exporter = genProxyDefDef(clsSym, sym , expSym, span)
393
410
394
411
// Construct exporters for default getters
395
- val defaultGetters = if (! defSym .hasDefaultParams) {
412
+ val defaultGetters = if (! sym .hasDefaultParams) {
396
413
Nil
397
414
} else {
398
415
for {
399
- (param, i) <- defSym .paramSymss.flatten.zipWithIndex
416
+ (param, i) <- sym .paramSymss.flatten.zipWithIndex
400
417
if param.is(HasDefault )
401
418
} yield {
402
- genExportDefaultGetter(clsSym, defSym , expSym, i, span)
419
+ genExportDefaultGetter(clsSym, sym , expSym, i, span)
403
420
}
404
421
}
405
422
@@ -435,7 +452,27 @@ object PrepJSExports {
435
452
proxySym : TermSymbol , span : Span )(using Context ): Tree = {
436
453
437
454
DefDef (proxySym, { argss =>
438
- This (clsSym).select(trgSym).appliedToArgss(argss)
455
+ if (trgSym.isConstructor) {
456
+ val tycon = trgSym.owner.typeRef
457
+ New (tycon).select(TermRef (tycon, trgSym)).appliedToArgss(argss)
458
+ } else if (trgSym.is(ModuleClass )) {
459
+ assert(argss.isEmpty,
460
+ s " got a module export with non-empty paramss. target: $trgSym, proxy: $proxySym at $span" )
461
+ ref(trgSym.sourceModule)
462
+ } else if (trgSym.isClass) {
463
+ assert(isJSAny(trgSym), s " got a class export for a non-JS class ( $trgSym) at $span" )
464
+ val tpe = argss match {
465
+ case Nil =>
466
+ trgSym.typeRef
467
+ case (targs @ (first :: _)) :: Nil if first.isType =>
468
+ trgSym.typeRef.appliedTo(targs.map(_.tpe))
469
+ case _ =>
470
+ throw AssertionError (s " got a class export with unexpected paramss. target: $trgSym, proxy: $proxySym at $span" )
471
+ }
472
+ ref(jsdefn.JSPackage_constructorOf ).appliedToType(tpe)
473
+ } else {
474
+ This (clsSym).select(trgSym).appliedToArgss(argss)
475
+ }
439
476
}).withSpan(span)
440
477
}
441
478
0 commit comments