@@ -323,7 +323,6 @@ class JSCodeGen()(using genCtx: Context) {
323323 // Generate members (constructor + methods)
324324
325325 val generatedNonFieldMembers = new mutable.ListBuffer [js.MemberDef ]
326- val exportedSymbols = new mutable.ListBuffer [Symbol ]
327326
328327 val tpl = td.rhs.asInstanceOf [Template ]
329328 for (tree <- tpl.constr :: tpl.body) {
@@ -336,19 +335,11 @@ class JSCodeGen()(using genCtx: Context) {
336335 case dd : DefDef =>
337336 val sym = dd.symbol
338337
339- val isExport = false // jsInterop.isExport(sym)
340-
341338 if (sym.hasAnnotation(jsdefn.JSNativeAnnot ))
342339 generatedNonFieldMembers += genJSNativeMemberDef(dd)
343340 else
344341 generatedNonFieldMembers ++= genMethod(dd)
345342
346- if (isExport) {
347- // We add symbols that we have to export here. This way we also
348- // get inherited stuff that is implemented in this class.
349- exportedSymbols += sym
350- }
351-
352343 case _ =>
353344 throw new FatalError (" Illegal tree in body of genScalaClass(): " + tree)
354345 }
@@ -357,21 +348,11 @@ class JSCodeGen()(using genCtx: Context) {
357348 // Generate fields and add to methods + ctors
358349 val generatedMembers = genClassFields(td) ++ generatedNonFieldMembers.toList
359350
360- // Generate the exported members, constructors and accessors
361- val exports = {
362- /*
363- // Generate the exported members
364- val memberExports = genMemberExports(sym, exportedSymbols.toList)
351+ // Generate member exports
352+ val memberExports = jsExportsGen.genMemberExports(sym)
365353
366- // Generate exported constructors or accessors
367- val exportedConstructorsOrAccessors =
368- if (isStaticModule(sym)) genModuleAccessorExports(sym)
369- else genConstructorExports(sym)
370-
371- memberExports ++ exportedConstructorsOrAccessors
372- */
373- Nil
374- }
354+ // Generate top-level export definitions
355+ val topLevelExportDefs = jsExportsGen.genTopLevelExports(sym)
375356
376357 // Static initializer
377358 val optStaticInitializer = {
@@ -383,20 +364,27 @@ class JSCodeGen()(using genCtx: Context) {
383364 }
384365 }
385366 if (enableReflectiveInstantiation)
386- genRegisterReflectiveInstantiation(sym)
367+ genRegisterReflectiveInstantiation(sym).toList
387368 else
388- None
369+ Nil
389370 }
390371
391- val staticInitializerStats = reflectInit.toList
372+ // Initialization of the module because of field exports
373+ val needsStaticModuleInit =
374+ topLevelExportDefs.exists(_.isInstanceOf [js.TopLevelFieldExportDef ])
375+ val staticModuleInit =
376+ if (! needsStaticModuleInit) Nil
377+ else List (genLoadModule(sym))
378+
379+ val staticInitializerStats = reflectInit ::: staticModuleInit
392380 if (staticInitializerStats.nonEmpty)
393- List (genStaticInitializerWithStats( js.Block (staticInitializerStats)))
381+ List (genStaticConstructorWithStats(ir. Names . StaticInitializerName , js.Block (staticInitializerStats)))
394382 else
395383 Nil
396384 }
397385
398386 val allMemberDefsExceptStaticForwarders =
399- generatedMembers ::: exports ::: optStaticInitializer
387+ generatedMembers ::: memberExports ::: optStaticInitializer
400388
401389 // Add static forwarders
402390 val allMemberDefs = if (! isCandidateForForwarders(sym)) {
@@ -452,7 +440,7 @@ class JSCodeGen()(using genCtx: Context) {
452440 None ,
453441 None ,
454442 hashedDefs,
455- Nil )(
443+ topLevelExportDefs )(
456444 optimizerHints)
457445
458446 classDefinition
@@ -511,6 +499,28 @@ class JSCodeGen()(using genCtx: Context) {
511499 }
512500 }
513501
502+ // Static members (exported from the companion object)
503+ val staticMembers = {
504+ val module = sym.companionModule
505+ if (! module.exists) {
506+ Nil
507+ } else {
508+ val companionModuleClass = module.moduleClass
509+ val exports = withScopedVars(currentClassSym := companionModuleClass) {
510+ jsExportsGen.genStaticExports(companionModuleClass)
511+ }
512+ if (exports.exists(_.isInstanceOf [js.JSFieldDef ])) {
513+ val classInitializer =
514+ genStaticConstructorWithStats(ir.Names .ClassInitializerName , genLoadModule(companionModuleClass))
515+ exports :+ classInitializer
516+ } else {
517+ exports
518+ }
519+ }
520+ }
521+
522+ val topLevelExports = jsExportsGen.genTopLevelExports(sym)
523+
514524 val (jsClassCaptures, generatedConstructor) =
515525 genJSClassCapturesAndConstructor(sym, constructorTrees.toList)
516526
@@ -525,7 +535,8 @@ class JSCodeGen()(using genCtx: Context) {
525535 genClassFields(td) :::
526536 generatedConstructor ::
527537 jsExportsGen.genJSClassDispatchers(sym, dispatchMethodNames.result().distinct) :::
528- generatedMethods.toList
538+ generatedMethods.toList :::
539+ staticMembers
529540 }
530541
531542 // Hashed definitions of the class
@@ -546,7 +557,7 @@ class JSCodeGen()(using genCtx: Context) {
546557 jsSuperClass,
547558 None ,
548559 hashedMemberDefs,
549- Nil )(
560+ topLevelExports )(
550561 OptimizerHints .empty)
551562
552563 classDefinition
@@ -785,10 +796,15 @@ class JSCodeGen()(using genCtx: Context) {
785796 ! f.isOneOf(Method | Module ) && f.isTerm
786797 && ! f.hasAnnotation(jsdefn.JSNativeAnnot )
787798 && ! f.hasAnnotation(jsdefn.JSOptionalAnnot )
799+ && ! f.hasAnnotation(jsdefn.JSExportStaticAnnot )
788800 }.flatMap({ f =>
789801 implicit val pos = f.span
790802
791- val isStaticField = f.is(JavaStatic ).ensuring(isStatic => ! (isStatic && isJSClass))
803+ val isTopLevelExport = f.hasAnnotation(jsdefn.JSExportTopLevelAnnot )
804+ val isJavaStatic = f.is(JavaStatic )
805+ assert(! (isTopLevelExport && isJavaStatic),
806+ em " found ${f.fullName} which is both a top-level export and a Java static " )
807+ val isStaticField = isTopLevelExport || isJavaStatic
792808
793809 val namespace = if isStaticField then js.MemberNamespace .PublicStatic else js.MemberNamespace .Public
794810 val mutable = isStaticField || f.is(Mutable )
@@ -797,6 +813,7 @@ class JSCodeGen()(using genCtx: Context) {
797813
798814 val irTpe =
799815 if (isJSClass) genExposedFieldIRType(f)
816+ else if (isTopLevelExport) jstpe.AnyType
800817 else toIRType(f.info)
801818
802819 if (isJSClass && f.isJSExposed)
@@ -806,7 +823,7 @@ class JSCodeGen()(using genCtx: Context) {
806823 val originalName = originalNameOfField(f)
807824 val fieldDef = js.FieldDef (flags, fieldIdent, originalName, irTpe)
808825 val optionalStaticFieldGetter =
809- if isStaticField then
826+ if isJavaStatic then
810827 // Here we are generating a public static getter for the static field,
811828 // this is its API for other units. This is necessary for singleton
812829 // enum values, which are backed by static fields.
@@ -824,7 +841,7 @@ class JSCodeGen()(using genCtx: Context) {
824841 }).toList
825842 }
826843
827- private def genExposedFieldIRType (f : Symbol ): jstpe.Type = {
844+ def genExposedFieldIRType (f : Symbol ): jstpe.Type = {
828845 val tpeEnteringPosterasure = atPhase(elimErasedValueTypePhase)(f.info)
829846 tpeEnteringPosterasure match {
830847 case tpe : ErasedValueType =>
@@ -850,11 +867,11 @@ class JSCodeGen()(using genCtx: Context) {
850867
851868 // Static initializers -----------------------------------------------------
852869
853- private def genStaticInitializerWithStats ( stats : js.Tree )(
870+ private def genStaticConstructorWithStats ( name : MethodName , stats : js.Tree )(
854871 implicit pos : Position ): js.MethodDef = {
855872 js.MethodDef (
856873 js.MemberFlags .empty.withNamespace(js.MemberNamespace .StaticConstructor ),
857- js.MethodIdent (ir. Names . StaticInitializerName ),
874+ js.MethodIdent (name ),
858875 NoOriginalName ,
859876 Nil ,
860877 jstpe.NoType ,
@@ -3916,19 +3933,17 @@ class JSCodeGen()(using genCtx: Context) {
39163933 }
39173934
39183935 (f, true )
3919- } else /* if (jsInterop.topLevelExportsOf(sym).nonEmpty) {
3920- val f = js.SelectStatic(encodeClassName(sym.owner),
3921- encodeFieldSym(sym))(jstpe.AnyType)
3936+ } else if (sym.hasAnnotation(jsdefn.JSExportTopLevelAnnot )) {
3937+ val f = js.SelectStatic (encodeClassName(sym.owner), encodeFieldSym(sym))(jstpe.AnyType )
39223938 (f, true )
3923- } else if (jsInterop.staticExportsOf(sym).nonEmpty) {
3924- val exportInfo = jsInterop.staticExportsOf(sym).head
3925- val companionClass = patchedLinkedClassOfClass(sym.owner)
3926- val f = js.JSSelect(
3927- genLoadJSConstructor(companionClass),
3928- js.StringLiteral(exportInfo.jsName))
3929-
3939+ } else if (sym.hasAnnotation(jsdefn.JSExportStaticAnnot )) {
3940+ val jsName = sym.getAnnotation(jsdefn.JSExportStaticAnnot ).get.argumentConstantString(0 ).getOrElse {
3941+ sym.defaultJSName
3942+ }
3943+ val companionClass = sym.owner.linkedClass
3944+ val f = js.JSSelect (genLoadJSConstructor(companionClass), js.StringLiteral (jsName))
39303945 (f, true )
3931- } else*/ {
3946+ } else {
39323947 val f =
39333948 val className = encodeClassName(sym.owner)
39343949 val fieldIdent = encodeFieldSym(sym)
0 commit comments