Skip to content

Commit 5220e9d

Browse files
committed
Scala.js: Implement @js.native vals and defs in the back-end.
This is a forward port of the back-end changes in the upstream commit scala-js/scala-js@a1a27f2
1 parent dab903f commit 5220e9d

File tree

1 file changed

+62
-14
lines changed

1 file changed

+62
-14
lines changed

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import StdNames._
2626
import TypeErasure.ErasedValueType
2727

2828
import dotty.tools.dotc.transform.{Erasure, ValueClasses}
29+
import dotty.tools.dotc.transform.SymUtils._
2930
import dotty.tools.dotc.util.SourcePosition
3031
import dotty.tools.dotc.util.Spans.Span
3132
import dotty.tools.dotc.report
@@ -283,7 +284,7 @@ class JSCodeGen()(using genCtx: Context) {
283284

284285
// Generate members (constructor + methods)
285286

286-
val generatedMethods = new mutable.ListBuffer[js.MethodDef]
287+
val generatedNonFieldMembers = new mutable.ListBuffer[js.MemberDef]
287288
val exportedSymbols = new mutable.ListBuffer[Symbol]
288289

289290
val tpl = td.rhs.asInstanceOf[Template]
@@ -298,13 +299,11 @@ class JSCodeGen()(using genCtx: Context) {
298299
val sym = dd.symbol
299300

300301
val isExport = false //jsInterop.isExport(sym)
301-
val isNamedExport = false /*isExport && sym.annotations.exists(
302-
_.symbol == JSExportNamedAnnotation)*/
303302

304-
/*if (isNamedExport)
305-
generatedMethods += genNamedExporterDef(dd)
306-
else*/
307-
generatedMethods ++= genMethod(dd)
303+
if (sym.hasAnnotation(jsdefn.JSNativeAnnot))
304+
generatedNonFieldMembers += genJSNativeMemberDef(dd)
305+
else
306+
generatedNonFieldMembers ++= genMethod(dd)
308307

309308
if (isExport) {
310309
// We add symbols that we have to export here. This way we also
@@ -318,7 +317,7 @@ class JSCodeGen()(using genCtx: Context) {
318317
}
319318

320319
// Generate fields and add to methods + ctors
321-
val generatedMembers = genClassFields(td) ++ generatedMethods.toList
320+
val generatedMembers = genClassFields(td) ++ generatedNonFieldMembers.toList
322321

323322
// Generate the exported members, constructors and accessors
324323
val exports = {
@@ -530,7 +529,6 @@ class JSCodeGen()(using genCtx: Context) {
530529

531530
private def genClassInterfaces(sym: ClassSymbol)(
532531
implicit pos: Position): List[js.ClassIdent] = {
533-
import dotty.tools.dotc.transform.SymUtils._
534532
for {
535533
intf <- sym.directlyInheritedTraits
536534
} yield {
@@ -678,7 +676,10 @@ class JSCodeGen()(using genCtx: Context) {
678676
"genClassFields called with a ClassDef other than the current one")
679677

680678
// Term members that are neither methods nor modules are fields
681-
classSym.info.decls.filter(f => !f.isOneOf(Method | Module) && f.isTerm).map({ f =>
679+
classSym.info.decls.filter { f =>
680+
!f.isOneOf(Method | Module) && f.isTerm
681+
&& !f.hasAnnotation(jsdefn.JSNativeAnnot)
682+
}.map({ f =>
682683
implicit val pos = f.span
683684

684685
val name =
@@ -816,6 +817,17 @@ class JSCodeGen()(using genCtx: Context) {
816817

817818
// Generate a method -------------------------------------------------------
818819

820+
/** Generates the JSNativeMemberDef. */
821+
def genJSNativeMemberDef(tree: DefDef): js.JSNativeMemberDef = {
822+
implicit val pos = tree.span
823+
824+
val sym = tree.symbol
825+
val flags = js.MemberFlags.empty.withNamespace(js.MemberNamespace.PublicStatic)
826+
val methodName = encodeMethodSym(sym)
827+
val jsNativeLoadSpec = computeJSNativeLoadSpecOfValDef(sym)
828+
js.JSNativeMemberDef(flags, methodName, jsNativeLoadSpec)
829+
}
830+
819831
private def genMethod(dd: DefDef): Option[js.MethodDef] = {
820832
withScopedVars(
821833
localNames := new LocalNameGenerator
@@ -1228,7 +1240,7 @@ class JSCodeGen()(using genCtx: Context) {
12281240
val sym = lhs0.symbol
12291241
if (sym.is(JavaStaticTerm))
12301242
throw new FatalError(s"Assignment to static member ${sym.fullName} not supported")
1231-
val genRhs = genExpr(rhs)
1243+
def genRhs = genExpr(rhs)
12321244
val lhs = lhs0 match {
12331245
case lhs: Ident => desugarIdent(lhs).getOrElse(lhs)
12341246
case lhs => lhs
@@ -1248,7 +1260,15 @@ class JSCodeGen()(using genCtx: Context) {
12481260

12491261
val genQual = genExpr(qualifier)
12501262

1251-
/*if (isScalaJSDefinedJSClass(sym.owner)) {
1263+
if (sym.hasAnnotation(jsdefn.JSNativeAnnot)) {
1264+
/* This is an assignment to a @js.native field. Since we reject
1265+
* `@js.native var`s as compile errors, this can only happen in
1266+
* the constructor of the enclosing object.
1267+
* We simply ignore the assignment, since the field will not be
1268+
* emitted at all.
1269+
*/
1270+
js.Skip()
1271+
} else /*if (isScalaJSDefinedJSClass(sym.owner)) {
12521272
val genLhs = if (isExposed(sym))
12531273
js.JSBracketSelect(genQual, js.StringLiteral(jsNameOf(sym)))
12541274
else
@@ -1257,12 +1277,12 @@ class JSCodeGen()(using genCtx: Context) {
12571277
ensureBoxed(genRhs,
12581278
enteringPhase(currentRun.posterasurePhase)(rhs.tpe))
12591279
js.Assign(genLhs, boxedRhs)
1260-
} else {*/
1280+
} else*/ {
12611281
js.Assign(
12621282
js.Select(genQual, encodeClassName(sym.owner),
12631283
encodeFieldSym(sym))(toIRType(sym.info)),
12641284
genRhs)
1265-
//}
1285+
}
12661286
case _ =>
12671287
js.Assign(
12681288
js.VarRef(encodeLocalSym(sym))(toIRType(sym.info)),
@@ -2120,6 +2140,8 @@ class JSCodeGen()(using genCtx: Context) {
21202140
genApplyJSMethodGeneric(sym, genExprOrGlobalScope(receiver), genActualJSArgs(sym, args), isStat)(tree.sourcePos)
21212141
/*else
21222142
genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/
2143+
} else if (sym.hasAnnotation(jsdefn.JSNativeAnnot)) {
2144+
genJSNativeMemberCall(tree, isStat)
21232145
} else {
21242146
genApplyMethodMaybeStatically(genExpr(receiver), sym, genActualArgs(sym, args))
21252147
}
@@ -2287,6 +2309,26 @@ class JSCodeGen()(using genCtx: Context) {
22872309
(firstArg.asInstanceOf[js.Tree], args.tail)
22882310
}
22892311

2312+
/** Gen JS code for a call to a native JS def or val. */
2313+
private def genJSNativeMemberCall(tree: Apply, isStat: Boolean): js.Tree = {
2314+
val sym = tree.symbol
2315+
val Apply(_, args) = tree
2316+
2317+
implicit val pos = tree.span
2318+
2319+
val jsNativeMemberValue =
2320+
js.SelectJSNativeMember(encodeClassName(sym.owner), encodeMethodSym(sym))
2321+
2322+
val boxedResult =
2323+
if (sym.isJSGetter) jsNativeMemberValue
2324+
else js.JSFunctionApply(jsNativeMemberValue, genActualJSArgs(sym, args))
2325+
2326+
unbox(boxedResult, atPhase(elimErasedValueTypePhase) {
2327+
sym.info.resultType
2328+
})
2329+
}
2330+
2331+
22902332
/** Gen JS code for a call to a polymorphic method.
22912333
*
22922334
* The only methods that reach the back-end as polymorphic are
@@ -3498,6 +3540,12 @@ class JSCodeGen()(using genCtx: Context) {
34983540
}
34993541
}
35003542

3543+
private def computeJSNativeLoadSpecOfValDef(sym: Symbol): js.JSNativeLoadSpec = {
3544+
atPhase(picklerPhase.next) {
3545+
computeJSNativeLoadSpecOfInPhase(sym)
3546+
}
3547+
}
3548+
35013549
private def computeJSNativeLoadSpecOfClass(sym: Symbol): Option[js.JSNativeLoadSpec] = {
35023550
if (sym.is(Trait) || sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) {
35033551
None

0 commit comments

Comments
 (0)