Skip to content

Commit 6b83864

Browse files
authored
Merge pull request #5177 from sjrd/rt-long-static-methods
Use static methods as entry points for RuntimeLong operator methods.
2 parents e293a4c + f19a183 commit 6b83864

File tree

12 files changed

+251
-267
lines changed

12 files changed

+251
-267
lines changed

linker-private-library/src/main/scala/org/scalajs/linker/runtime/RuntimeLong.scala

Lines changed: 86 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -40,74 +40,81 @@ import scala.annotation.tailrec
4040
/** Emulates a Long on the JavaScript platform. */
4141
@inline
4242
final class RuntimeLong(val lo: Int, val hi: Int) {
43-
a =>
44-
4543
import RuntimeLong._
4644

47-
// Universal equality
45+
// java.lang.Object
4846

4947
@inline
5048
override def equals(that: Any): Boolean = that match {
51-
case b: RuntimeLong => inline_equals(b)
52-
case _ => false
49+
case that: RuntimeLong => RuntimeLong.equals(this, that)
50+
case _ => false
5351
}
5452

5553
@inline override def hashCode(): Int = lo ^ hi
5654

57-
// String operations
58-
5955
@inline override def toString(): String =
60-
RuntimeLong.toString(lo, hi)
61-
62-
// Conversions
63-
64-
@inline def toByte: Byte = lo.toByte
65-
@inline def toShort: Short = lo.toShort
66-
@inline def toChar: Char = lo.toChar
67-
@inline def toInt: Int = lo
68-
@inline def toLong: Long = this.asInstanceOf[Long]
69-
@inline def toFloat: Float = RuntimeLong.toFloat(lo, hi)
70-
@inline def toDouble: Double = RuntimeLong.toDouble(lo, hi)
56+
RuntimeLong.toString(this)
7157

7258
// java.lang.Number
7359

74-
@inline def byteValue(): Byte = toByte
75-
@inline def shortValue(): Short = toShort
76-
@inline def intValue(): Int = toInt
77-
@inline def longValue(): Long = toLong
78-
@inline def floatValue(): Float = toFloat
79-
@inline def doubleValue(): Double = toDouble
60+
@inline def byteValue(): Byte = lo.toByte
61+
@inline def shortValue(): Short = lo.toShort
62+
@inline def intValue(): Int = lo
63+
@inline def longValue(): Long = this.asInstanceOf[Long]
64+
@inline def floatValue(): Float = RuntimeLong.toFloat(this)
65+
@inline def doubleValue(): Double = RuntimeLong.toDouble(this)
8066

8167
// java.lang.Comparable, including bridges
8268

8369
@inline
8470
def compareTo(that: Object): Int =
85-
compareTo(that.asInstanceOf[RuntimeLong])
71+
RuntimeLong.compare(this, that.asInstanceOf[RuntimeLong])
8672

8773
@inline
8874
def compareTo(that: java.lang.Long): Int =
89-
compareTo(that.asInstanceOf[RuntimeLong])
75+
RuntimeLong.compare(this, that.asInstanceOf[RuntimeLong])
76+
77+
// A few operator-friendly methods used by the division algorithms
78+
79+
@inline private def <<(b: Int): RuntimeLong = RuntimeLong.shl(this, b)
80+
@inline private def >>>(b: Int): RuntimeLong = RuntimeLong.shr(this, b)
81+
@inline private def +(b: RuntimeLong): RuntimeLong = RuntimeLong.add(this, b)
82+
@inline private def -(b: RuntimeLong): RuntimeLong = RuntimeLong.sub(this, b)
83+
}
84+
85+
object RuntimeLong {
86+
private final val TwoPow32 = 4294967296.0
87+
private final val TwoPow63 = 9223372036854775808.0
88+
89+
/** The magical mask that allows to test whether an unsigned long is a safe
90+
* double.
91+
* @see isUnsignedSafeDouble
92+
*/
93+
private final val UnsignedSafeDoubleHiMask = 0xffe00000
94+
95+
private final val AskQuotient = 0
96+
private final val AskRemainder = 1
97+
private final val AskToString = 2
98+
99+
/** The hi part of a (lo, hi) return value. */
100+
private[this] var hiReturn: Int = _
90101

91102
// Comparisons
92103

93104
@inline
94-
def compareTo(b: RuntimeLong): Int =
105+
def compare(a: RuntimeLong, b: RuntimeLong): Int =
95106
RuntimeLong.compare(a.lo, a.hi, b.lo, b.hi)
96107

97108
@inline
98-
private def inline_equals(b: RuntimeLong): Boolean =
109+
def equals(a: RuntimeLong, b: RuntimeLong): Boolean =
99110
a.lo == b.lo && a.hi == b.hi
100111

101112
@inline
102-
def equals(b: RuntimeLong): Boolean =
103-
inline_equals(b)
104-
105-
@inline
106-
def notEquals(b: RuntimeLong): Boolean =
107-
!inline_equals(b)
113+
def notEquals(a: RuntimeLong, b: RuntimeLong): Boolean =
114+
!equals(a, b)
108115

109116
@inline
110-
def <(b: RuntimeLong): Boolean = {
117+
def lt(a: RuntimeLong, b: RuntimeLong): Boolean = {
111118
/* We should use `inlineUnsignedInt_<(a.lo, b.lo)`, but that first extracts
112119
* a.lo and b.lo into local variables, which cause the if/else not to be
113120
* a valid JavaScript expression anymore. This causes useless explosion of
@@ -121,7 +128,7 @@ final class RuntimeLong(val lo: Int, val hi: Int) {
121128
}
122129

123130
@inline
124-
def <=(b: RuntimeLong): Boolean = {
131+
def le(a: RuntimeLong, b: RuntimeLong): Boolean = {
125132
/* Manually inline `inlineUnsignedInt_<=(a.lo, b.lo)`.
126133
* See the comment in `<` for the rationale.
127134
*/
@@ -132,7 +139,7 @@ final class RuntimeLong(val lo: Int, val hi: Int) {
132139
}
133140

134141
@inline
135-
def >(b: RuntimeLong): Boolean = {
142+
def gt(a: RuntimeLong, b: RuntimeLong): Boolean = {
136143
/* Manually inline `inlineUnsignedInt_>a.lo, b.lo)`.
137144
* See the comment in `<` for the rationale.
138145
*/
@@ -143,7 +150,7 @@ final class RuntimeLong(val lo: Int, val hi: Int) {
143150
}
144151

145152
@inline
146-
def >=(b: RuntimeLong): Boolean = {
153+
def ge(a: RuntimeLong, b: RuntimeLong): Boolean = {
147154
/* Manually inline `inlineUnsignedInt_>=(a.lo, b.lo)`.
148155
* See the comment in `<` for the rationale.
149156
*/
@@ -156,26 +163,26 @@ final class RuntimeLong(val lo: Int, val hi: Int) {
156163
// Bitwise operations
157164

158165
@inline
159-
def unary_~ : RuntimeLong = // scalastyle:ignore
160-
new RuntimeLong(~lo, ~hi)
166+
def not(a: RuntimeLong): RuntimeLong =
167+
new RuntimeLong(~a.lo, ~a.hi)
161168

162169
@inline
163-
def |(b: RuntimeLong): RuntimeLong =
170+
def or(a: RuntimeLong, b: RuntimeLong): RuntimeLong =
164171
new RuntimeLong(a.lo | b.lo, a.hi | b.hi)
165172

166173
@inline
167-
def &(b: RuntimeLong): RuntimeLong =
174+
def and(a: RuntimeLong, b: RuntimeLong): RuntimeLong =
168175
new RuntimeLong(a.lo & b.lo, a.hi & b.hi)
169176

170177
@inline
171-
def ^(b: RuntimeLong): RuntimeLong =
178+
def xor(a: RuntimeLong, b: RuntimeLong): RuntimeLong =
172179
new RuntimeLong(a.lo ^ b.lo, a.hi ^ b.hi)
173180

174181
// Shifts
175182

176183
/** Shift left */
177184
@inline
178-
def <<(n: Int): RuntimeLong = {
185+
def shl(a: RuntimeLong, n: Int): RuntimeLong = {
179186
/* This should *reasonably* be:
180187
* val n1 = n & 63
181188
* if (n1 < 32)
@@ -241,43 +248,43 @@ final class RuntimeLong(val lo: Int, val hi: Int) {
241248
*
242249
* Finally we have:
243250
*/
244-
val lo = this.lo
251+
val lo = a.lo
245252
new RuntimeLong(
246253
if ((n & 32) == 0) lo << n else 0,
247-
if ((n & 32) == 0) (lo >>> 1 >>> (31-n)) | (hi << n) else lo << n)
254+
if ((n & 32) == 0) (lo >>> 1 >>> (31-n)) | (a.hi << n) else lo << n)
248255
}
249256

250257
/** Logical shift right */
251258
@inline
252-
def >>>(n: Int): RuntimeLong = {
259+
def shr(a: RuntimeLong, n: Int): RuntimeLong = {
253260
// This derives in a similar way as in <<
254-
val hi = this.hi
261+
val hi = a.hi
255262
new RuntimeLong(
256-
if ((n & 32) == 0) (lo >>> n) | (hi << 1 << (31-n)) else hi >>> n,
263+
if ((n & 32) == 0) (a.lo >>> n) | (hi << 1 << (31-n)) else hi >>> n,
257264
if ((n & 32) == 0) hi >>> n else 0)
258265
}
259266

260267
/** Arithmetic shift right */
261268
@inline
262-
def >>(n: Int): RuntimeLong = {
269+
def sar(a: RuntimeLong, n: Int): RuntimeLong = {
263270
// This derives in a similar way as in <<
264-
val hi = this.hi
271+
val hi = a.hi
265272
new RuntimeLong(
266-
if ((n & 32) == 0) (lo >>> n) | (hi << 1 << (31-n)) else hi >> n,
273+
if ((n & 32) == 0) (a.lo >>> n) | (hi << 1 << (31-n)) else hi >> n,
267274
if ((n & 32) == 0) hi >> n else hi >> 31)
268275
}
269276

270277
// Arithmetic operations
271278

272279
@inline
273-
def unary_- : RuntimeLong = { // scalastyle:ignore
274-
val lo = this.lo
275-
val hi = this.hi
280+
def neg(a: RuntimeLong): RuntimeLong = {
281+
val lo = a.lo
282+
val hi = a.hi
276283
new RuntimeLong(inline_lo_unary_-(lo), inline_hi_unary_-(lo, hi))
277284
}
278285

279286
@inline
280-
def +(b: RuntimeLong): RuntimeLong = {
287+
def add(a: RuntimeLong, b: RuntimeLong): RuntimeLong = {
281288
val alo = a.lo
282289
val ahi = a.hi
283290
val bhi = b.hi
@@ -287,7 +294,7 @@ final class RuntimeLong(val lo: Int, val hi: Int) {
287294
}
288295

289296
@inline
290-
def -(b: RuntimeLong): RuntimeLong = {
297+
def sub(a: RuntimeLong, b: RuntimeLong): RuntimeLong = {
291298
val alo = a.lo
292299
val ahi = a.hi
293300
val bhi = b.hi
@@ -297,7 +304,7 @@ final class RuntimeLong(val lo: Int, val hi: Int) {
297304
}
298305

299306
@inline
300-
def *(b: RuntimeLong): RuntimeLong = {
307+
def mul(a: RuntimeLong, b: RuntimeLong): RuntimeLong = {
301308
/* The following algorithm is based on the decomposition in 32-bit and then
302309
* 16-bit subproducts of the unsigned interpretation of operands.
303310
*
@@ -523,54 +530,23 @@ final class RuntimeLong(val lo: Int, val hi: Int) {
523530
new RuntimeLong(lo, hi)
524531
}
525532

526-
@inline
527-
def /(b: RuntimeLong): RuntimeLong =
528-
RuntimeLong.divide(a, b)
529-
530-
/** `java.lang.Long.divideUnsigned(a, b)` */
531-
@inline
532-
def divideUnsigned(b: RuntimeLong): RuntimeLong =
533-
RuntimeLong.divideUnsigned(a, b)
534-
535-
@inline
536-
def %(b: RuntimeLong): RuntimeLong =
537-
RuntimeLong.remainder(a, b)
538-
539-
/** `java.lang.Long.remainderUnsigned(a, b)` */
540-
@inline
541-
def remainderUnsigned(b: RuntimeLong): RuntimeLong =
542-
RuntimeLong.remainderUnsigned(a, b)
543-
544-
/** Computes `longBitsToDouble(this)`.
533+
/** Computes `longBitsToDouble(a)`.
545534
*
546535
* `fpBitsDataView` must be a scratch `js.typedarray.DataView` whose
547536
* underlying buffer is at least 8 bytes long.
548537
*/
549538
@inline
550-
def bitsToDouble(fpBitsDataView: scala.scalajs.js.typedarray.DataView): Double = {
551-
fpBitsDataView.setInt32(0, lo, littleEndian = true)
552-
fpBitsDataView.setInt32(4, hi, littleEndian = true)
539+
def bitsToDouble(a: RuntimeLong,
540+
fpBitsDataView: scala.scalajs.js.typedarray.DataView): Double = {
541+
542+
fpBitsDataView.setInt32(0, a.lo, littleEndian = true)
543+
fpBitsDataView.setInt32(4, a.hi, littleEndian = true)
553544
fpBitsDataView.getFloat64(0, littleEndian = true)
554545
}
555546

556-
}
557-
558-
object RuntimeLong {
559-
private final val TwoPow32 = 4294967296.0
560-
private final val TwoPow63 = 9223372036854775808.0
561-
562-
/** The magical mask that allows to test whether an unsigned long is a safe
563-
* double.
564-
* @see isUnsignedSafeDouble
565-
*/
566-
private final val UnsignedSafeDoubleHiMask = 0xffe00000
567-
568-
private final val AskQuotient = 0
569-
private final val AskRemainder = 1
570-
private final val AskToString = 2
571-
572-
/** The hi part of a (lo, hi) return value. */
573-
private[this] var hiReturn: Int = _
547+
@inline
548+
def toString(a: RuntimeLong): String =
549+
toString(a.lo, a.hi)
574550

575551
private def toString(lo: Int, hi: Int): String = {
576552
if (isInt32(lo, hi)) {
@@ -608,6 +584,14 @@ object RuntimeLong {
608584
}
609585
}
610586

587+
@inline
588+
def toInt(a: RuntimeLong): Int =
589+
a.lo
590+
591+
@inline
592+
def toDouble(a: RuntimeLong): Double =
593+
toDouble(a.lo, a.hi)
594+
611595
private def toDouble(lo: Int, hi: Int): Double = {
612596
if (hi < 0) {
613597
// We do asUint() on the hi part specifically for MinValue
@@ -618,6 +602,10 @@ object RuntimeLong {
618602
}
619603
}
620604

605+
@inline
606+
def toFloat(a: RuntimeLong): Float =
607+
toFloat(a.lo, a.hi)
608+
621609
private def toFloat(lo: Int, hi: Int): Float = {
622610
/* This implementation is based on the property that, *if* the conversion
623611
* `x.toDouble` is lossless, then the result of `x.toFloat` is equivalent

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,7 @@ private[emitter] object CoreJSLib {
10201020
Return(Apply(genIdentBracketSelect(fpBitsDataView, "getBigInt64"), List(0, bool(true))))
10211021
)
10221022
} else {
1023-
Return(genLongModuleApply(LongImpl.fromDoubleBits, x, fpBitsDataView))
1023+
Return(genLongApplyStatic(LongImpl.fromDoubleBits, x, fpBitsDataView))
10241024
}
10251025
} :::
10261026
defineFloatingPointBitsFunctionOrPolyfill(VarField.doubleFromBits, doubleFromBits) { (x, fpBitsDataView) =>
@@ -1030,7 +1030,7 @@ private[emitter] object CoreJSLib {
10301030
Return(Apply(genIdentBracketSelect(fpBitsDataView, "getFloat64"), List(0, bool(true))))
10311031
)
10321032
} else {
1033-
Return(genApply(x, LongImpl.bitsToDouble, fpBitsDataView))
1033+
Return(genLongApplyStatic(LongImpl.bitsToDouble, x, fpBitsDataView))
10341034
}
10351035
}
10361036
}

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,8 +1437,8 @@ object Emitter {
14371437
multiple(
14381438
instanceTests(LongImpl.RuntimeLongClass),
14391439
instantiateClass(LongImpl.RuntimeLongClass, LongImpl.AllConstructors.toList),
1440-
callMethods(LongImpl.RuntimeLongClass, LongImpl.AllMethods.toList),
1441-
callOnModule(LongImpl.RuntimeLongModuleClass, LongImpl.AllModuleMethods.toList)
1440+
callMethods(LongImpl.RuntimeLongClass, LongImpl.BoxedLongMethods.toList),
1441+
callStaticMethods(LongImpl.RuntimeLongClass, LongImpl.OperatorMethods.toList)
14421442
)
14431443
},
14441444

0 commit comments

Comments
 (0)