@@ -204,8 +204,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
204
204
}
205
205
206
206
// For anonymous methods
207
- // These have a default, since we always read them.
208
- private val tryingToGenMethodAsJSFunction = new ScopedVar [Boolean ](false )
207
+ // It has a default, since we always read it.
209
208
private val paramAccessorLocals = new ScopedVar (Map .empty[Symbol , js.ParamDef ])
210
209
211
210
/* Contextual JS class value for some operations of nested JS classes that
@@ -223,11 +222,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
223
222
}
224
223
}
225
224
226
- private class CancelGenMethodAsJSFunction (message : String )
227
- extends scala.util.control.ControlThrowable {
228
- override def getMessage (): String = message
229
- }
230
-
231
225
// Rewriting of anonymous function classes ---------------------------------
232
226
233
227
/** Start nested generation of a class.
@@ -248,7 +242,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
248
242
undefinedDefaultParams := null ,
249
243
mutableLocalVars := null ,
250
244
mutatedLocalVars := null ,
251
- tryingToGenMethodAsJSFunction := false ,
252
245
paramAccessorLocals := Map .empty
253
246
)(withNewLocalNameScope(body))
254
247
}
@@ -387,21 +380,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
387
380
private val generatedStaticForwarderClasses = ListBuffer .empty[(Symbol , js.ClassDef )]
388
381
389
382
private def consumeLazilyGeneratedAnonClass (sym : Symbol ): ClassDef = {
390
- /* If we are trying to generate an method as JSFunction, we cannot
391
- * actually consume the symbol, since we might fail trying and retry.
392
- * We will then see the same tree again and not find the symbol anymore.
393
- *
394
- * If we are sure this is the only generation, we remove the symbol to
395
- * make sure we don't generate the same class twice.
396
- */
397
- val optDef = {
398
- if (tryingToGenMethodAsJSFunction)
399
- lazilyGeneratedAnonClasses.get(sym)
400
- else
401
- lazilyGeneratedAnonClasses.remove(sym)
402
- }
403
-
404
- optDef.getOrElse {
383
+ lazilyGeneratedAnonClasses.remove(sym).getOrElse {
405
384
abort(" Couldn't find tree for lazily generated anonymous class " +
406
385
s " ${sym.fullName} at ${sym.pos}" )
407
386
}
@@ -450,25 +429,22 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
450
429
}
451
430
val allClassDefs = collectClassDefs(cunit.body)
452
431
453
- /* There are three types of anonymous classes we want to generate
454
- * only once we need them so we can inline them at construction site:
432
+ /* There are two types of anonymous classes we want to generate only
433
+ * once we need them, so we can inline them at construction site:
455
434
*
456
- * - anonymous class that are JS types, which includes:
457
- * - lambdas for js.FunctionN and js.ThisFunctionN (SAMs). (We may
458
- * not generate actual Scala classes for these).
459
- * - anonymous (non-lambda) JS classes. These classes may not have
460
- * their own prototype. Therefore, their constructor *must* be
461
- * inlined.
462
- * - lambdas for scala.FunctionN. This is only an optimization and may
463
- * fail. In the case of failure, we fall back to generating a
464
- * fully-fledged Scala class.
435
+ * - Lambdas for `js.Function` SAMs, including `js.FunctionN`,
436
+ * `js.ThisFunctionN` and custom JS function SAMs. We must generate
437
+ * JS functions for these, instead of actual classes.
438
+ * - Anonymous (non-lambda) JS classes. These classes may not have
439
+ * their own prototype. Therefore, their constructor *must* be
440
+ * inlined.
465
441
*
466
442
* Since for all these, we don't know how they inter-depend, we just
467
443
* store them in a map at this point.
468
444
*/
469
445
val (lazyAnons, fullClassDefs) = allClassDefs.partition { cd =>
470
446
val sym = cd.symbol
471
- isAnonymousJSClass(sym) || isJSFunctionDef(sym) || sym.isAnonymousFunction
447
+ isAnonymousJSClass(sym) || isJSFunctionDef(sym)
472
448
}
473
449
474
450
lazilyGeneratedAnonClasses ++= lazyAnons.map(cd => cd.symbol -> cd)
@@ -2824,9 +2800,10 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
2824
2800
*/
2825
2801
private def genThis ()(implicit pos : Position ): js.Tree = {
2826
2802
thisLocalVarName.fold[js.Tree ] {
2827
- if (tryingToGenMethodAsJSFunction) {
2828
- throw new CancelGenMethodAsJSFunction (
2829
- " Trying to generate `this` inside the body" )
2803
+ if (isJSFunctionDef(currentClassSym)) {
2804
+ abort(
2805
+ " Unexpected `this` reference inside the body of a JS function class: " +
2806
+ currentClassSym.fullName)
2830
2807
}
2831
2808
js.This ()(currentThisType)
2832
2809
} { thisLocalName =>
@@ -3359,10 +3336,10 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
3359
3336
}
3360
3337
3361
3338
/** Gen JS code for a constructor call (new).
3339
+ *
3362
3340
* Further refined into:
3363
- * * new String(...)
3364
3341
* * new of a hijacked boxed class
3365
- * * new of an anonymous function class that was recorded as JS function
3342
+ * * new of a JS function class
3366
3343
* * new of a JS class
3367
3344
* * new Array
3368
3345
* * regular new
@@ -3382,13 +3359,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
3382
3359
} else if (isJSFunctionDef(clsSym)) {
3383
3360
val classDef = consumeLazilyGeneratedAnonClass(clsSym)
3384
3361
genJSFunction(classDef, args.map(genExpr))
3385
- } else if (clsSym.isAnonymousFunction) {
3386
- val classDef = consumeLazilyGeneratedAnonClass(clsSym)
3387
- tryGenAnonFunctionClass(classDef, args.map(genExpr)).getOrElse {
3388
- // Cannot optimize anonymous function class. Generate full class.
3389
- generatedClasses += nestedGenerateClass(clsSym)(genClass(classDef)) -> clsSym.pos
3390
- genNew(clsSym, ctor, genActualArgs(ctor, args))
3391
- }
3392
3362
} else if (isJSType(clsSym)) {
3393
3363
genPrimitiveJSNew(tree)
3394
3364
} else {
@@ -6057,69 +6027,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
6057
6027
6058
6028
// Synthesizers for JS functions -------------------------------------------
6059
6029
6060
- /** Try and generate JS code for an anonymous function class.
6061
- *
6062
- * Returns Some(<js code>) if the class could be rewritten that way, None
6063
- * otherwise.
6064
- *
6065
- * We make the following assumptions on the form of such classes:
6066
- * - It is an anonymous function
6067
- * - Includes being anonymous, final, and having exactly one constructor
6068
- * - It is not a PartialFunction
6069
- * - It has no field other than param accessors
6070
- * - It has exactly one constructor
6071
- * - It has exactly one non-bridge method apply if it is not specialized,
6072
- * or a method apply$...$sp and a forwarder apply if it is specialized.
6073
- * - As a precaution: it is synthetic
6074
- *
6075
- * From a class looking like this:
6076
- *
6077
- * final class <anon>(outer, capture1, ..., captureM) extends AbstractionFunctionN[...] {
6078
- * def apply(param1, ..., paramN) = {
6079
- * <body>
6080
- * }
6081
- * }
6082
- * new <anon>(o, c1, ..., cM)
6083
- *
6084
- * we generate a function:
6085
- *
6086
- * arrow-lambda<o = outer, c1 = capture1, ..., cM = captureM>(param1, ..., paramN) {
6087
- * <body>
6088
- * }
6089
- *
6090
- * so that, at instantiation point, we can write:
6091
- *
6092
- * new AnonFunctionN(function)
6093
- *
6094
- * the latter tree is returned in case of success.
6095
- *
6096
- * Trickier things apply when the function is specialized.
6097
- */
6098
- private def tryGenAnonFunctionClass (cd : ClassDef ,
6099
- capturedArgs : List [js.Tree ]): Option [js.Tree ] = {
6100
- // scalastyle:off return
6101
- implicit val pos = cd.pos
6102
- val sym = cd.symbol
6103
- assert(sym.isAnonymousFunction,
6104
- s " tryGenAndRecordAnonFunctionClass called with non-anonymous function $cd" )
6105
-
6106
- if (! sym.superClass.fullName.startsWith(" scala.runtime.AbstractFunction" )) {
6107
- /* This is an anonymous class for a non-LMF capable SAM in 2.12.
6108
- * We must not rewrite it, as it would then not inherit from the
6109
- * appropriate parent class and/or interface.
6110
- */
6111
- None
6112
- } else {
6113
- nestedGenerateClass(sym) {
6114
- val (functionBase, arity) =
6115
- tryGenAnonFunctionClassGeneric(cd, capturedArgs)(_ => return None )
6116
-
6117
- Some (genJSFunctionToScala(functionBase, arity))
6118
- }
6119
- }
6120
- // scalastyle:on return
6121
- }
6122
-
6123
6030
/** Gen a conversion from a JavaScript function into a Scala function. */
6124
6031
private def genJSFunctionToScala (jsFunction : js.Tree , arity : Int )(
E377
6125
6032
implicit pos : Position ): js.Tree = {
@@ -6136,11 +6043,9 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
6136
6043
* functions are not classes, we deconstruct the ClassDef, then
6137
6044
* reconstruct it to be a genuine Closure.
6138
6045
*
6139
- * Compared to `tryGenAnonFunctionClass()`, this function must
6140
- * always succeed, because we really cannot afford keeping them as
6141
- * anonymous classes. The good news is that it can do so, because the
6142
- * body of SAM lambdas is hoisted in the enclosing class. Hence, the
6143
- * apply() method is just a forwarder to calling that hoisted method.
6046
+ * We can always do so, because the body of SAM lambdas is hoisted in the
6047
+ * enclosing class. Hence, the apply() method is just a forwarder to
6048
+ * calling that hoisted method.
6144
6049
*
6145
6050
* From a class looking like this:
6146
6051
*
@@ -6163,26 +6068,18 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
6163
6068
s " genAndRecordJSFunctionClass called with non-JS function $cd" )
6164
6069
6165
6070
nestedGenerateClass(sym) {
6166
- val (function, _) = tryGenAnonFunctionClassGeneric(cd, captures)(msg =>
6167
- abort(s " Could not generate function for JS function: $msg" ))
6168
-
6169
- function
6071
+ genJSFunctionInner(cd, captures)
6170
6072
}
6171
6073
}
6172
6074
6173
- /** Code common to tryGenAndRecordAnonFunctionClass and
6174
- * genAndRecordJSFunctionClass.
6175
- */
6176
- private def tryGenAnonFunctionClassGeneric (cd : ClassDef ,
6177
- initialCapturedArgs : List [js.Tree ])(
6178
- fail : (=> String ) => Nothing ): (js.Tree , Int ) = {
6075
+ /** The code of `genJSFunction` that is inside the `nestedGenerateClass` wrapper. */
6076
+ private def genJSFunctionInner (cd : ClassDef ,
6077
+ initialCapturedArgs : List [js.Tree ]): js.Closure = {
6179
6078
implicit val pos = cd.pos
6180
6079
val sym = cd.symbol
6181
6080
6182
- // First checks
6183
-
6184
- if (sym.isSubClass(PartialFunctionClass ))
6185
- fail(s " Cannot rewrite PartialFunction $cd" )
6081
+ def fail (reason : String ): Nothing =
6082
+ abort(s " Could not generate function for JS function: $reason" )
6186
6083
6187
6084
// First step: find the apply method def, and collect param accessors
6188
6085
@@ -6210,10 +6107,12 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
6210
6107
if (! ddsym.isPrimaryConstructor)
6211
6108
fail(s " Non-primary constructor $ddsym in anon function $cd" )
6212
6109
} else {
6213
- val name = dd.name.toString
6214
- if (name == " apply" || (ddsym.isSpecialized && name.startsWith(" apply$" ))) {
6215
- if ((applyDef eq null ) || ddsym.isSpecialized)
6110
+ if (dd.name == nme.apply) {
6111
+ if (! ddsym.isBridge) {
6112
+ if (applyDef ne null )
6113
+ fail(s " Found duplicate apply method $ddsym in $cd" )
6216
6114
applyDef = dd
6115
+ }
6217
6116
} else if (ddsym.hasAnnotation(JSOptionalAnnotation )) {
6218
6117
// Ignore (this is useful for default parameters in custom JS function types)
6219
6118
} else {
@@ -6253,24 +6152,15 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
6253
6152
// Third step: emit the body of the apply method def
6254
6153
6255
6154
val applyMethod = withScopedVars(
6256
- paramAccessorLocals := (paramAccessors zip ctorParamDefs).toMap,
6257
- tryingToGenMethodAsJSFunction := true
6155
+ paramAccessorLocals := (paramAccessors zip ctorParamDefs).toMap
6258
6156
) {
6259
- try {
6260
- genMethodWithCurrentLocalNameScope(applyDef)
6261
- } catch {
6262
- case e : CancelGenMethodAsJSFunction =>
6263
- fail(e.getMessage)
6264
- }
6157
+ genMethodWithCurrentLocalNameScope(applyDef)
6265
6158
}
6266
6159
6267
6160
// Fourth step: patch the body to unbox parameters and box result
6268
6161
6269
- val hasRepeatedParam = {
6270
- sym.superClass == JSFunctionClass && // Scala functions are known not to have repeated params
6271
- enteringUncurry {
6272
- applyDef.symbol.paramss.flatten.lastOption.exists(isRepeated(_))
6273
- }
6162
+ val hasRepeatedParam = enteringUncurry {
6163
+ applyDef.symbol.paramss.flatten.lastOption.exists(isRepeated(_))
6274
6164
}
6275
6165
6276
6166
val js .MethodDef (_, _, _, params, _, body) = applyMethod
@@ -6303,8 +6193,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
6303
6193
val ok = patchedParams.nonEmpty
6304
6194
if (! ok) {
6305
6195
reporter.error(pos,
6306
- " The SAM or apply method for a js.ThisFunction must have a " +
6307
- " leading non-varargs parameter" )
6196
+ " The apply method for a js.ThisFunction must have a leading non-varargs parameter" )
6308
6197
}
6309
6198
ok
6310
6199
}
@@ -6335,9 +6224,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
6335
6224
}
6336
6225
}
6337
6226
6338
- val arity = params.size
6339
-
6340
- (closure, arity)
6227
+ closure
6341
6228
}
6342
6229
}
6343
6230
0 commit comments