@@ -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
}
@@ -1300,7 +1354,7 @@ private class FunctionEmitter private (
1300
1354
*/
1301
1355
genNPE()
1302
1356
} else {
1303
- genCheckNonNull( )
1357
+ genCheckNonNullFor(qualifier )
1304
1358
fb += wa.StructGet (
1305
1359
genTypeID.forClass(className),
1306
1360
genFieldID.forClassInstanceField(fieldName)
@@ -2056,9 +2110,15 @@ private class FunctionEmitter private (
2056
2110
case _ =>
2057
2111
targetWasmType match {
2058
2112
case watpe.RefType (true , watpe.HeapType .Any ) =>
2059
- () // nothing to do
2113
+ if (nullabilityLevelOf(expr) >= 1 )
2114
+ () // nothing to do
2115
+ else
2116
+ fb += wa.RefAsNonNull
2060
2117
case targetWasmType : watpe.RefType =>
2061
- fb += wa.RefCast (targetWasmType)
2118
+ if (nullabilityLevelOf(expr) >= 2 )
2119
+ fb += wa.RefCast (targetWasmType)
2120
+ else
2121
+ fb += wa.RefCast (targetWasmType.toNonNullable)
2062
2122
case _ =>
2063
2123
throw new AssertionError (s " Unexpected type in AsInstanceOf: $targetTpe" )
2064
2124
}
@@ -2082,7 +2142,11 @@ private class FunctionEmitter private (
2082
2142
fb += wa.GlobalGet (genGlobalID.undef)
2083
2143
2084
2144
case StringType =>
2085
- genAsNonNullOrNPE()
2145
+ val sig = watpe.FunctionType (List (watpe.RefType .anyref), List (watpe.RefType .any))
2146
+ fb.block(sig) { nonNullLabel =>
2147
+ fb += wa.BrOnNonNull (nonNullLabel)
2148
+ fb += wa.GlobalGet (genGlobalID.emptyString)
2149
+ }
2086
2150
2087
2151
case targetTpe : PrimTypeWithRef =>
2088
2152
targetTpe match {
@@ -2139,13 +2203,13 @@ private class FunctionEmitter private (
2139
2203
2140
2204
genTreeAuto(expr)
2141
2205
markPosition(tree)
2142
- genCheckNonNull( )
2206
+ genCheckNonNullFor(expr )
2143
2207
fb += wa.StructGet (genTypeID.ObjectStruct , genFieldID.objStruct.vtable)
2144
2208
fb += wa.Call (genFunctionID.getClassOf)
2145
2209
} else {
2146
2210
genTree(expr, AnyType )
2147
2211
markPosition(tree)
2148
- genAsNonNullOrNPE( )
2212
+ genAsNonNullOrNPEFor(expr )
2149
2213
fb += wa.Call (genFunctionID.anyGetClass)
2150
2214
}
2151
2215
@@ -2497,7 +2561,7 @@ private class FunctionEmitter private (
2497
2561
2498
2562
markPosition(tree)
2499
2563
2500
- genAsNonNullOrNPE( )
2564
+ genAsNonNullOrNPEFor(expr )
2501
2565
2502
2566
// if !expr.isInstanceOf[js.JavaScriptException], then br $done
2503
2567
fb += wa.BrOnCastFail (
@@ -2736,7 +2800,7 @@ private class FunctionEmitter private (
2736
2800
array.tpe match {
2737
2801
case ArrayType (arrayTypeRef) =>
2738
2802
// Get the underlying array
2739
- genCheckNonNull( )
2803
+ genCheckNonNullFor(array )
2740
2804
fb += wa.StructGet (
2741
2805
genTypeID.forArrayClass(arrayTypeRef),
2742
2806
genFieldID.objStruct.arrayUnderlying
@@ -2840,7 +2904,7 @@ private class FunctionEmitter private (
2840
2904
2841
2905
array.tpe match {
2842
2906
case ArrayType (arrayTypeRef) =>
2843
- genCheckNonNull( )
2907
+ genCheckNonNullFor(array )
2844
2908
2845
2909
if (semantics.arrayIndexOutOfBounds == CheckedBehavior .Unchecked ) {
2846
2910
// Get the underlying array
@@ -2998,7 +3062,7 @@ private class FunctionEmitter private (
2998
3062
2999
3063
markPosition(tree)
3000
3064
3001
- genAsNonNullOrNPE( )
3065
+ genAsNonNullOrNPEFor(expr )
3002
3066
fb += wa.LocalTee (exprLocal)
3003
3067
3004
3068
fb += wa.LocalGet (exprLocal)
@@ -3198,7 +3262,7 @@ private class FunctionEmitter private (
3198
3262
case tpe : PrimType =>
3199
3263
tpe
3200
3264
case tpe =>
3201
- genCheckNonNull( )
3265
+ genAsNonNullOrNPEFor(expr )
3202
3266
tpe
3203
3267
}
3204
3268
@@ -3215,7 +3279,7 @@ private class FunctionEmitter private (
3215
3279
case Transients .ObjectClassName (obj) =>
3216
3280
genTree(obj, AnyType )
3217
3281
markPosition(tree)
3218
- genAsNonNullOrNPE( )
3282
+ genAsNonNullOrNPEFor(obj )
3219
3283
fb += wa.Call (genFunctionID.anyGetClassName)
3220
3284
StringType
3221
3285
0 commit comments