@@ -409,24 +409,78 @@ private class FunctionEmitter private (
409
409
}
410
410
}
411
411
412
- /** Emits a `ref_as_non_null`, or an NPE check if required. */
413
- private def genAsNonNullOrNPE (): Unit = {
414
- if (semantics.nullPointers == CheckedBehavior .Unchecked )
415
- fb += wa.RefAsNonNull
416
- else
417
- fb += wa.BrOnNull (getNPELabel())
412
+ /** Emits a `ref_as_non_null` or an NPE check if required for the given `Tree`.
413
+ *
414
+ * This method does not emit `tree`. It only uses it to determine whether
415
+ * a check is required.
416
+ */
417
+ private def genAsNonNullOrNPEFor (tree : Tree ): Unit = {
418
+ val nullabilityLevel = nullabilityLevelOf(tree)
419
+ if (nullabilityLevel >= 1 ) {
420
+ if (semantics.nullPointers != CheckedBehavior .Unchecked && nullabilityLevel >= 2 )
421
+ fb += wa.BrOnNull (getNPELabel())
422
+ else
423
+ fb += wa.RefAsNonNull
424
+ }
418
425
}
419
426
420
- /** Emits an NPE check if required, otherwise nothing.
427
+ /** Emits an NPE check if required for the given `Tree`, otherwise nothing.
428
+ *
429
+ * This method does not emit `tree`. It only uses it to determine whether
430
+ * a check is required.
421
431
*
422
432
* Unlike `genAsNonNullOrNPE`, after this codegen the value on the stack is
423
433
* still statically typed as nullable at the Wasm level.
424
434
*/
425
- private def genCheckNonNull ( ): Unit = {
426
- if (semantics.nullPointers != CheckedBehavior .Unchecked )
435
+ private def genCheckNonNullFor ( tree : Tree ): Unit = {
436
+ if (semantics.nullPointers != CheckedBehavior .Unchecked && nullabilityLevelOf(tree) >= 2 )
427
437
fb += wa.BrOnNull (getNPELabel())
428
438
}
429
439
440
+ /** Analyzes the nullability level of `tree`.
441
+ *
442
+ * - `0` if `tree` is statically known to generate a non-nullable Wasm value.
443
+ * - `1` if `tree` is statically known to generate a non-nullable value,
444
+ * but maybe typed as nullable at the Wasm level.
445
+ * - `2` if `tree` can be `null`.
446
+ *
447
+ * See also: `isNotNull` in `emitter.FunctionEmitter`.
448
+ */
449
+ private def nullabilityLevelOf (tree : Tree ): Int = {
450
+ // !!! Similar code in emitter.FunctionEmitter.isNotNull
451
+ // !!! Similar code in OptimizerCore.isNotNull
452
+
453
+ def isNullableType (tpe : Type ): Boolean = tpe match {
454
+ case NullType => true
455
+ case _ : PrimType => false
456
+ case _ => true
457
+ }
458
+
459
+ def shapeNullabilityLevel (tree : Tree ): Int = tree match {
460
+ case Transient (Transients .CheckNotNull (_)) =>
461
+ 0
462
+ case Transient (Transients .AssumeNotNull (expr)) =>
463
+ Math .min(1 , shapeNullabilityLevel(expr))
464
+ case Transient (Transients .Cast (expr, _)) =>
465
+ shapeNullabilityLevel(expr)
466
+ case _ : This =>
467
+ if (tree.tpe != AnyType ) 0
468
+ else 2
469
+ case _:New | _:NewArray | _:ArrayValue | _:Clone | _:ClassOf =>
470
+ 0
471
+ case _ : LoadModule =>
472
+ if (semantics.moduleInit == CheckedBehavior .Compliant ) 2
473
+ else 0
474
+ case _ =>
475
+ 2
476
+ }
477
+
478
+ if (! isNullableType(tree.tpe))
479
+ 0
480
+ else
481
+ shapeNullabilityLevel(tree)
482
+ }
483
+
430
484
/** Emits an unconditional NPE. */
431
485
private def genNPE (): Unit = {
432
486
if (semantics.nullPointers == CheckedBehavior .Unchecked )
@@ -652,7 +706,7 @@ private class FunctionEmitter private (
652
706
markPosition(tree)
653
707
genNPE()
654
708
} else {
655
- genAsNonNullOrNPE( )
709
+ genAsNonNullOrNPEFor(qualifier )
656
710
genTree(rhs, lhs.tpe)
657
711
markPosition(tree)
658
712
fb += wa.StructSet (
@@ -686,7 +740,7 @@ private class FunctionEmitter private (
686
740
case _ => false
687
741
}
688
742
689
- genCheckNonNull( )
743
+ genCheckNonNullFor(array )
690
744
691
745
if (semantics.arrayIndexOutOfBounds == CheckedBehavior .Unchecked &&
692
746
(semantics.arrayStores == CheckedBehavior .Unchecked || isPrimArray)) {
@@ -839,7 +893,7 @@ private class FunctionEmitter private (
839
893
840
894
// Load receiver and arguments
841
895
genTree(receiver, AnyType )
842
- genAsNonNullOrNPE( )
896
+ genAsNonNullOrNPEFor(receiver )
843
897
fb += wa.LocalTee (receiverLocalForDispatch)
844
898
genArgs(args, methodName)
845
899
@@ -901,7 +955,7 @@ private class FunctionEmitter private (
901
955
*/
902
956
def genReceiverNotNull (): Unit = {
903
957
genTreeAuto(receiver)
904
- genAsNonNullOrNPE( )
958
+ genAsNonNullOrNPEFor(receiver )
905
959
}
906
960
907
961
/* Generates a resolved call to a method of a hijacked class.
@@ -1180,14 +1234,14 @@ private class FunctionEmitter private (
1180
1234
BoxedClassToPrimType .get(targetClassName) match {
1181
1235
case None =>
1182
1236
genTree(receiver, ClassType (targetClassName))
1183
- genAsNonNullOrNPE( )
1237
+ genAsNonNullOrNPEFor(receiver )
1184
1238
1185
1239
case Some (primReceiverType) =>
1186
1240
if (receiver.tpe == primReceiverType) {
1187
1241
genTreeAuto(receiver)
1188
1242
} else {
1189
1243
genTree(receiver, AnyType )
1190
- genAsNonNullOrNPE( )
1244
+ genAsNonNullOrNPEFor(receiver )
1191
1245
genUnbox(primReceiverType)
1192
1246
}
1193
1247
}
@@ -1285,7 +1339,7 @@ private class FunctionEmitter private (
1285
1339
*/
1286
1340
genNPE()
1287
1341
} else {
1288
- genCheckNonNull( )
1342
+ genCheckNonNullFor(qualifier )
1289
1343
fb += wa.StructGet (
1290
1344
genTypeID.forClass(className),
1291
1345
genFieldID.forClassInstanceField(fieldName)
@@ -2005,7 +2059,10 @@ private class FunctionEmitter private (
2005
2059
case watpe.RefType (true , watpe.HeapType .Any ) =>
2006
2060
() // nothing to do
2007
2061
case targetWasmType : watpe.RefType =>
2008
- fb += wa.RefCast (targetWasmType)
2062
+ if (nullabilityLevelOf(expr) >= 2 )
2063
+ fb += wa.RefCast (targetWasmType)
2064
+ else
2065
+ fb += wa.RefCast (targetWasmType.toNonNullable)
2009
2066
case _ =>
2010
2067
throw new AssertionError (s " Unexpected type in AsInstanceOf: $targetTpe" )
2011
2068
}
@@ -2029,7 +2086,11 @@ private class FunctionEmitter private (
2029
2086
fb += wa.GlobalGet (genGlobalID.undef)
2030
2087
2031
2088
case StringType =>
2032
- genAsNonNullOrNPE()
2089
+ val sig = watpe.FunctionType (List (watpe.RefType .anyref), List (watpe.RefType .any))
2090
+ fb.block(sig) { nonNullLabel =>
2091
+ fb += wa.BrOnNonNull (nonNullLabel)
2092
+ fb += wa.GlobalGet (genGlobalID.emptyString)
2093
+ }
2033
2094
2034
2095
case CharType | LongType =>
2035
2096
// Extract the `value` field (the only field) out of the box class.
@@ -2084,13 +2145,13 @@ private class FunctionEmitter private (
2084
2145
2085
2146
genTreeAuto(expr)
2086
2147
markPosition(tree)
2087
- genCheckNonNull( )
2148
+ genCheckNonNullFor(expr )
2088
2149
fb += wa.StructGet (genTypeID.ObjectStruct , genFieldID.objStruct.vtable)
2089
2150
fb += wa.Call (genFunctionID.getClassOf)
2090
2151
} else {
2091
2152
genTree(expr, AnyType )
2092
2153
markPosition(tree)
2093
- genAsNonNullOrNPE( )
2154
+ genAsNonNullOrNPEFor(expr )
2094
2155
fb += wa.Call (genFunctionID.anyGetClass)
2095
2156
}
2096
2157
@@ -2442,7 +2503,7 @@ private class FunctionEmitter private (
2442
2503
2443
2504
markPosition(tree)
2444
2505
2445
- genAsNonNullOrNPE( )
2506
+ genAsNonNullOrNPEFor(expr )
2446
2507
2447
2508
// if !expr.isInstanceOf[js.JavaScriptException], then br $done
2448
2509
fb += wa.BrOnCastFail (
@@ -2681,7 +2742,7 @@ private class FunctionEmitter private (
2681
2742
array.tpe match {
2682
2743
case ArrayType (arrayTypeRef) =>
2683
2744
// Get the underlying array
2684
- genCheckNonNull( )
2745
+ genCheckNonNullFor(array )
2685
2746
fb += wa.StructGet (
2686
2747
genTypeID.forArrayClass(arrayTypeRef),
2687
2748
genFieldID.objStruct.arrayUnderlying
@@ -2776,7 +2837,7 @@ private class FunctionEmitter private (
2776
2837
2777
2838
array.tpe match {
2778
2839
case ArrayType (arrayTypeRef) =>
2779
- genCheckNonNull( )
2840
+ genCheckNonNullFor(array )
2780
2841
2781
2842
if (semantics.arrayIndexOutOfBounds == CheckedBehavior .Unchecked ) {
2782
2843
// Get the underlying array
@@ -2932,7 +2993,7 @@ private class FunctionEmitter private (
2932
2993
2933
2994
markPosition(tree)
2934
2995
2935
- genAsNonNullOrNPE( )
2996
+ genAsNonNullOrNPEFor(expr )
2936
2997
fb += wa.LocalTee (exprLocal)
2937
2998
2938
2999
fb += wa.LocalGet (exprLocal)
@@ -3134,7 +3195,7 @@ private class FunctionEmitter private (
3134
3195
case tpe : PrimType =>
3135
3196
tpe
3136
3197
case tpe =>
3137
- genCheckNonNull( )
3198
+ genAsNonNullOrNPEFor(expr )
3138
3199
tpe
3139
3200
}
3140
3201
@@ -3151,7 +3212,7 @@ private class FunctionEmitter private (
3151
3212
case Transients .ObjectClassName (obj) =>
3152
3213
genTree(obj, AnyType )
3153
3214
markPosition(tree)
3154
- genAsNonNullOrNPE( )
3215
+ genAsNonNullOrNPEFor(obj )
3155
3216
fb += wa.Call (genFunctionID.anyGetClassName)
3156
3217
StringType
3157
3218
0 commit comments