@@ -33,6 +33,7 @@ object HelperFunctions {
33
33
genNewArrayOfThisClass()
34
34
genAnyGetClass()
35
35
genNewArrayObject()
36
+ genIdentityHashCode()
36
37
}
37
38
38
39
private def genStringLiteral ()(implicit ctx : WasmContext ): Unit = {
@@ -1383,6 +1384,137 @@ object HelperFunctions {
1383
1384
fctx.buildAndAddToContext()
1384
1385
}
1385
1386
1387
+ /** `identityHashCode`: `anyref -> i32`.
1388
+ *
1389
+ * This is the implementation of `IdentityHashCode`. It is also used to compute the `hashCode()`
1390
+ * of primitive values when dispatch is required (i.e., when the receiver type is not known to be
1391
+ * a specific primitive or hijacked class), so it must be consistent with the implementations of
1392
+ * `hashCode()` in hijacked classes.
1393
+ *
1394
+ * For `String` and `Double`, we actually call the hijacked class methods, as they are a bit
1395
+ * involved. For `Boolean` and `Void`, we hard-code a copy here.
1396
+ */
1397
+ def genIdentityHashCode ()(implicit ctx : WasmContext ): Unit = {
1398
+ import IRTrees .MemberNamespace .Public
1399
+ import SpecialNames .hashCodeMethodName
1400
+ import WasmTypeName ._
1401
+ import WasmFieldIdx .typeData ._
1402
+
1403
+ // A global exclusively used by this function
1404
+ ctx.addGlobal(
1405
+ WasmGlobal (
1406
+ WasmGlobalName .lastIDHashCode,
1407
+ WasmInt32 ,
1408
+ WasmExpr (List (I32_CONST (0 ))),
1409
+ isMutable = true
1410
+ )
1411
+ )
1412
+
1413
+ val fctx = WasmFunctionContext (
1414
+ WasmFunctionName .identityHashCode,
1415
+ List (" obj" -> WasmRefType .anyref),
1416
+ List (WasmInt32 )
1417
+ )
1418
+
1419
+ val List (objParam) = fctx.paramIndices
1420
+
1421
+ import fctx .instrs
1422
+
1423
+ val objNonNullLocal = fctx.addLocal(" objNonNull" , WasmRefType .any)
1424
+ val resultLocal = fctx.addLocal(" result" , WasmInt32 )
1425
+
1426
+ // If `obj` is `null`, return 0 (by spec)
1427
+ fctx.block(WasmRefType .any) { nonNullLabel =>
1428
+ instrs += LOCAL_GET (objParam)
1429
+ instrs += BR_ON_NON_NULL (nonNullLabel)
1430
+ instrs += I32_CONST (0 )
1431
+ instrs += RETURN
1432
+ }
1433
+ instrs += LOCAL_TEE (objNonNullLocal)
1434
+
1435
+ // If `obj` is one of our objects, skip all the jsValueType tests
1436
+ instrs += REF_TEST (WasmRefType (WasmHeapType .ObjectType ))
1437
+ instrs += I32_EQZ
1438
+ fctx.ifThen() {
1439
+ fctx.switch() { () =>
1440
+ instrs += LOCAL_GET (objNonNullLocal)
1441
+ instrs += CALL (WasmFunctionName .jsValueType)
1442
+ }(
1443
+ List (JSValueTypeFalse ) -> { () =>
1444
+ instrs += I32_CONST (1237 ) // specified by jl.Boolean.hashCode()
1445
+ instrs += RETURN
1446
+ },
1447
+ List (JSValueTypeTrue ) -> { () =>
1448
+ instrs += I32_CONST (1231 ) // specified by jl.Boolean.hashCode()
1449
+ instrs += RETURN
1450
+ },
1451
+ List (JSValueTypeString ) -> { () =>
1452
+ instrs += LOCAL_GET (objNonNullLocal)
1453
+ instrs += CALL (WasmFunctionName (Public , IRNames .BoxedStringClass , hashCodeMethodName))
1454
+ instrs += RETURN
1455
+ },
1456
+ List (JSValueTypeNumber ) -> { () =>
1457
+ instrs += LOCAL_GET (objNonNullLocal)
1458
+ instrs += CALL (WasmFunctionName .unbox(IRTypes .DoubleRef ))
1459
+ instrs += CALL (WasmFunctionName (Public , IRNames .BoxedDoubleClass , hashCodeMethodName))
1460
+ instrs += RETURN
1461
+ },
1462
+ List (JSValueTypeUndefined ) -> { () =>
1463
+ instrs += I32_CONST (0 ) // specified by jl.Void.hashCode(), Scala.js only
1464
+ instrs += RETURN
1465
+ },
1466
+ List (JSValueTypeBigInt ) -> { () =>
1467
+ instrs += LOCAL_GET (objNonNullLocal)
1468
+ instrs += CALL (WasmFunctionName .bigintHashCode)
1469
+ instrs += RETURN
1470
+ },
1471
+ List (JSValueTypeSymbol ) -> { () =>
1472
+ fctx.block() { descriptionIsNullLabel =>
1473
+ instrs += LOCAL_GET (objNonNullLocal)
1474
+ instrs += CALL (WasmFunctionName .symbolDescription)
1475
+ instrs += BR_ON_NULL (descriptionIsNullLabel)
1476
+ instrs += CALL (WasmFunctionName (Public , IRNames .BoxedStringClass , hashCodeMethodName))
1477
+ instrs += RETURN
1478
+ }
1479
+ instrs += I32_CONST (0 )
1480
+ instrs += RETURN
1481
+ }
1482
+ ) { () =>
1483
+ // JSValueTypeOther -- fall through to using idHashCodeMap
1484
+ ()
1485
+ }
1486
+ }
1487
+
1488
+ // If we get here, use the idHashCodeMap
1489
+
1490
+ // Read the existing idHashCode, if one exists
1491
+ instrs += GLOBAL_GET (WasmGlobalName .idHashCodeMap)
1492
+ instrs += LOCAL_GET (objNonNullLocal)
1493
+ instrs += CALL (WasmFunctionName .idHashCodeGet)
1494
+ instrs += LOCAL_TEE (resultLocal)
1495
+
1496
+ // If it is 0, there was no recorded idHashCode yet; allocate a new one
1497
+ instrs += I32_EQZ
1498
+ fctx.ifThen() {
1499
+ // Allocate a new idHashCode
1500
+ instrs += GLOBAL_GET (WasmGlobalName .lastIDHashCode)
1501
+ instrs += I32_CONST (1 )
1502
+ instrs += I32_ADD
1503
+ instrs += LOCAL_TEE (resultLocal)
1504
+ instrs += GLOBAL_SET (WasmGlobalName .lastIDHashCode)
1505
+
1506
+ // Store it for next time
1507
+ instrs += GLOBAL_GET (WasmGlobalName .idHashCodeMap)
1508
+ instrs += LOCAL_GET (objNonNullLocal)
1509
+ instrs += LOCAL_GET (resultLocal)
1510
+ instrs += CALL (WasmFunctionName .idHashCodeSet)
1511
+ }
1512
+
1513
+ instrs += LOCAL_GET (resultLocal)
1514
+
1515
+ fctx.buildAndAddToContext()
1516
+ }
1517
+
1386
1518
/** Generate type inclusion test for interfaces.
1387
1519
*
1388
1520
* The expression `isInstanceOf[<interface>]` will be compiled to a CALL to the function
0 commit comments