@@ -18,8 +18,10 @@ object HelperFunctions {
18
18
genCreateStringFromData()
19
19
genTypeDataName()
20
20
genCreateClassOf()
21
+ genGetClassOf()
21
22
genArrayTypeData()
22
23
genGetComponentType()
24
+ genAnyGetClass()
23
25
}
24
26
25
27
/** `createStringFromData: (ref array u16) -> (ref any)` (representing a `string`). */
@@ -338,6 +340,43 @@ object HelperFunctions {
338
340
fctx.buildAndAddToContext()
339
341
}
340
342
343
+ /** `getClassOf: (ref typeData) -> (ref jlClass)`.
344
+ *
345
+ * Initializes the `java.lang.Class` instance associated with the given
346
+ * `typeData` if not already done, and returns it.
347
+ *
348
+ * This includes the fast-path and the slow-path to `createClassOf`, for
349
+ * call sites that are not performance-sensitive.
350
+ */
351
+ private def genGetClassOf ()(implicit ctx : WasmContext ): Unit = {
352
+ import WasmImmediate ._
353
+ import WasmTypeName .WasmStructTypeName
354
+
355
+ val typeDataType = WasmRefType (WasmHeapType .Type (WasmStructType .typeData.name))
356
+
357
+ val fctx = WasmFunctionContext (
358
+ WasmFunctionName .getClassOf,
359
+ List (" typeData" -> typeDataType),
360
+ List (WasmRefType (WasmHeapType .ClassType ))
361
+ )
362
+
363
+ val List (typeDataParam) = fctx.paramIndices
364
+
365
+ import fctx .instrs
366
+
367
+ fctx.block(WasmRefType (WasmHeapType .ClassType )) { alreadyInitializedLabel =>
368
+ // fast path
369
+ instrs += LOCAL_GET (typeDataParam)
370
+ instrs += STRUCT_GET (TypeIdx (WasmStructTypeName .typeData), WasmFieldName .typeData.classOfIdx)
371
+ instrs += BR_ON_NON_NULL (alreadyInitializedLabel)
372
+ // slow path
373
+ instrs += LOCAL_GET (typeDataParam)
374
+ instrs += CALL (FuncIdx (WasmFunctionName .createClassOf))
375
+ } // end bock alreadyInitializedLabel
376
+
377
+ fctx.buildAndAddToContext()
378
+ }
379
+
341
380
/** `arrayTypeData: (ref typeData), i32 -> (ref typeData)`.
342
381
*
343
382
* Returns the typeData of an array with `dims` dimensions over the given
@@ -434,24 +473,186 @@ object HelperFunctions {
434
473
val componentTypeDataLocal = fctx.addLocal(" componentTypeData" , typeDataType)
435
474
436
475
fctx.block() { nullResultLabel =>
437
- fctx.block(Types .WasmRefType (Types .WasmHeapType .ClassType )) { nonNullClassOfLabel =>
438
- // Try and extract non-null component type data
439
- instrs += LOCAL_GET (typeDataParam)
440
- instrs += STRUCT_GET (TypeIdx (WasmStructTypeName .typeData), WasmFieldName .typeData.componentTypeIdx)
441
- instrs += BR_ON_NULL (nullResultLabel)
442
- // fast path
443
- instrs += LOCAL_TEE (componentTypeDataLocal)
444
- instrs += STRUCT_GET (TypeIdx (WasmStructTypeName .typeData), WasmFieldName .typeData.classOfIdx)
445
- instrs += BR_ON_NON_NULL (nonNullClassOfLabel)
446
- // slow path
447
- instrs += LOCAL_GET (componentTypeDataLocal)
448
- instrs += CALL (FuncIdx (WasmFunctionName .createClassOf))
449
- } // end bock nonNullClassOfLabel
476
+ // Try and extract non-null component type data
477
+ instrs += LOCAL_GET (typeDataParam)
478
+ instrs += STRUCT_GET (TypeIdx (WasmStructTypeName .typeData), WasmFieldName .typeData.componentTypeIdx)
479
+ instrs += BR_ON_NULL (nullResultLabel)
480
+ // Get the corresponding classOf
481
+ instrs += CALL (FuncIdx (WasmFunctionName .getClassOf))
450
482
instrs += RETURN
451
483
} // end block nullResultLabel
452
484
instrs += REF_NULL (HeapType (WasmHeapType .ClassType ))
453
485
454
486
fctx.buildAndAddToContext()
455
487
}
456
488
489
+ /** `anyGetClass: (ref any) -> (ref null jlClass)`.
490
+ *
491
+ * This is the implementation of `value.getClass()` when `value` can be an
492
+ * instance of a hijacked class, i.e., a primitive.
493
+ *
494
+ * For `number`s, the result is based on the actual value, as specified by
495
+ * [[https://www.scala-js.org/doc/semantics.html#getclass ]].
496
+ */
497
+ private def genAnyGetClass ()(implicit ctx : WasmContext ): Unit = {
498
+ import WasmImmediate ._
499
+ import WasmTypeName .WasmStructTypeName
500
+
501
+ val typeDataType = WasmRefType (WasmHeapType .Type (WasmStructType .typeData.name))
502
+
503
+ val fctx = WasmFunctionContext (
504
+ WasmFunctionName .anyGetClass,
505
+ List (" value" -> WasmRefType .any),
506
+ List (WasmRefNullType (WasmHeapType .ClassType ))
507
+ )
508
+
509
+ val List (valueParam) = fctx.paramIndices
510
+
511
+ import fctx .instrs
512
+
513
+ val objectTypeIdx = TypeIdx (WasmStructTypeName (IRNames .ObjectClass ))
514
+ val typeDataLocal = fctx.addLocal(" typeData" , typeDataType)
515
+ val doubleValueLocal = fctx.addLocal(" doubleValue" , WasmFloat64 )
516
+ val intValueLocal = fctx.addLocal(" intValue" , WasmInt32 )
517
+
518
+ def getHijackedClassTypeDataInstr (className : IRNames .ClassName ): WasmInstr =
519
+ GLOBAL_GET (GlobalIdx (WasmGlobalName .WasmGlobalVTableName (IRTypes .ClassRef (className))))
520
+
521
+ fctx.block(WasmRefNullType (WasmHeapType .ClassType )) { nonNullClassOfLabel =>
522
+ fctx.block(typeDataType) { gotTypeDataLabel =>
523
+ fctx.block(WasmRefType (WasmHeapType .ObjectType )) { ourObjectLabel =>
524
+ // if value is our object, jump to $ourObject
525
+ instrs += LOCAL_GET (valueParam)
526
+ instrs += BR_ON_CAST (
527
+ CastFlags (false , false ),
528
+ ourObjectLabel,
529
+ WasmImmediate .HeapType (WasmHeapType .Simple .Any ),
530
+ WasmImmediate .HeapType (WasmHeapType .ObjectType )
531
+ )
532
+
533
+ // switch(jsValueType(value)) { ... }
534
+ fctx.block() { typeOtherLabel =>
535
+ fctx.block() { typeUndefinedLabel =>
536
+ fctx.block() { typeNumberLabel =>
537
+ fctx.block() { typeStringLabel =>
538
+ fctx.block() { typeBooleanLabel =>
539
+ instrs += LOCAL_GET (valueParam)
540
+ instrs += CALL (FuncIdx (WasmFunctionName .jsValueType))
541
+ instrs += BR_TABLE (LabelIdxVector (List (
542
+ typeBooleanLabel, // 0
543
+ typeBooleanLabel, // 1
544
+ typeStringLabel, // 2
545
+ typeNumberLabel, // 3
546
+ typeUndefinedLabel, // 4
547
+ )), typeOtherLabel)
548
+ }
549
+
550
+ // typeBoolean:
551
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedBooleanClass )
552
+ instrs += BR (gotTypeDataLabel)
553
+ }
554
+
555
+ // typeString:
556
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedStringClass )
557
+ instrs += BR (gotTypeDataLabel)
558
+ }
559
+
560
+ /* typeNumber:
561
+ * For `number`s, the result is based on the actual value, as specified by
562
+ * [[https://www.scala-js.org/doc/semantics.html#getclass]].
563
+ */
564
+
565
+ // doubleValue := unboxDouble(value)
566
+ instrs += LOCAL_GET (valueParam)
567
+ instrs += CALL (FuncIdx (WasmFunctionName .unbox(IRTypes .DoubleRef )))
568
+ instrs += LOCAL_TEE (doubleValueLocal)
569
+
570
+ // intValue := doubleValue.toInt
571
+ instrs += I32_TRUNC_SAT_F64_S
572
+ instrs += LOCAL_TEE (intValueLocal)
573
+
574
+ // if same(intValue.toDouble, doubleValue) -- same bit pattern to avoid +0.0 == -0.0
575
+ instrs += F64_CONVERT_I32_S
576
+ instrs += I64_REINTERPRET_F64
577
+ instrs += LOCAL_GET (doubleValueLocal)
578
+ instrs += I64_REINTERPRET_F64
579
+ instrs += I64_EQ
580
+ fctx.ifThenElse() {
581
+ // then it is a Byte, a Short, or an Integer
582
+
583
+ // if intValue.toByte.toInt == intValue
584
+ instrs += LOCAL_GET (intValueLocal)
585
+ instrs += I32_EXTEND8_S
586
+ instrs += LOCAL_GET (intValueLocal)
587
+ instrs += I32_EQ
588
+ fctx.ifThenElse() {
589
+ // then it is a Byte
590
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedByteClass )
591
+ instrs += BR (gotTypeDataLabel)
592
+ } {
593
+ // else, if intValue.toShort.toInt == intValue
594
+ instrs += LOCAL_GET (intValueLocal)
595
+ instrs += I32_EXTEND16_S
596
+ instrs += LOCAL_GET (intValueLocal)
597
+ instrs += I32_EQ
598
+ fctx.ifThenElse() {
599
+ // then it is a Short
600
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedShortClass )
601
+ instrs += BR (gotTypeDataLabel)
602
+ } {
603
+ // else, it is an Integer
604
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedIntegerClass )
605
+ instrs += BR (gotTypeDataLabel)
606
+ }
607
+ }
608
+ } {
609
+ // else, it is a Float or a Double
610
+
611
+ // if doubleValue.toFloat.toDouble == doubleValue
612
+ instrs += LOCAL_GET (doubleValueLocal)
613
+ instrs += F32_DEMOTE_F64
614
+ instrs += F64_PROMOTE_F32
615
+ instrs += LOCAL_GET (doubleValueLocal)
616
+ instrs += F64_EQ
617
+ fctx.ifThenElse() {
618
+ // then it is a Float
619
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedFloatClass )
620
+ instrs += BR (gotTypeDataLabel)
621
+ } {
622
+ // else, if it is NaN
623
+ instrs += LOCAL_GET (doubleValueLocal)
624
+ instrs += LOCAL_GET (doubleValueLocal)
625
+ instrs += F64_NE
626
+ fctx.ifThenElse() {
627
+ // then it is a Float
628
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedFloatClass )
629
+ instrs += BR (gotTypeDataLabel)
630
+ } {
631
+ // else, it is a Double
632
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedDoubleClass )
633
+ instrs += BR (gotTypeDataLabel)
634
+ }
635
+ }
636
+ }
637
+ }
638
+
639
+ // typeUndefined:
640
+ instrs += getHijackedClassTypeDataInstr(IRNames .BoxedUnitClass )
641
+ instrs += BR (gotTypeDataLabel)
642
+ }
643
+
644
+ // typeOther:
645
+ instrs += REF_NULL (HeapType (WasmHeapType .ClassType ))
646
+ instrs += RETURN
647
+ }
648
+
649
+ instrs += STRUCT_GET (objectTypeIdx, StructFieldIdx (0 ))
650
+ }
651
+
652
+ instrs += CALL (FuncIdx (WasmFunctionName .getClassOf))
653
+ }
654
+
655
+ fctx.buildAndAddToContext()
656
+ }
657
+
457
658
}
0 commit comments