JS IR: Long

This commit is contained in:
Anton Bannykh
2018-07-04 15:15:06 +03:00
parent 4872c97929
commit 65846d783d
10 changed files with 787 additions and 32 deletions
@@ -14,7 +14,9 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
import org.jetbrains.kotlin.js.resolve.JsPlatform.builtIns
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.findSingleFunction
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.Variance
@@ -96,8 +98,10 @@ class JsIntrinsics(
val jsNumberToDouble = getInternalFunction("numberToDouble")
val jsNumberToInt = getInternalFunction("numberToInt")
val jsNumberToShort = getInternalFunction("numberToShort")
val jsNumberToLong = getInternalFunction("numberToLong")
val jsToByte = getInternalFunction("toByte")
val jsToShort = getInternalFunction("toShort")
val jsToLong = getInternalFunction("toLong")
// Other:
@@ -112,6 +116,18 @@ class JsIntrinsics(
val jsEquals = getInternalFunction("equals")
val longConstructor =
context.symbolTable.referenceConstructor(context.getClass(FqName("kotlin.Long")).constructors.single())
val longToDouble = context.symbolTable.referenceSimpleFunction(
context.getClass(FqName("kotlin.Long")).unsubstitutedMemberScope.findSingleFunction(
Name.identifier("toDouble")
)
)
val longToFloat = context.symbolTable.referenceSimpleFunction(
context.getClass(FqName("kotlin.Long")).unsubstitutedMemberScope.findSingleFunction(
Name.identifier("toFloat")
)
)
// Helpers:
@@ -17,9 +17,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.isFakeOverriddenFromAny
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
@@ -52,13 +50,6 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
for (type in primitiveNumbers) {
op(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus)
op(type, OperatorNames.UNARY_MINUS, intrinsics.jsUnaryMinus)
op(type, OperatorNames.ADD, intrinsics.jsPlus)
op(type, OperatorNames.SUB, intrinsics.jsMinus)
op(type, OperatorNames.MUL, intrinsics.jsMult)
op(type, OperatorNames.DIV, intrinsics.jsDiv)
op(type, OperatorNames.MOD, intrinsics.jsMod)
op(type, OperatorNames.REM, intrinsics.jsMod)
}
irBuiltIns.stringType.let {
@@ -83,7 +74,7 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
}
// Conversion rules are ported from NumberAndCharConversionFIF
// TODO: Add Char, Long and Number conversions
// TODO: Add Char and Number conversions
irBuiltIns.byteType.let {
op(it, ConversionNames.TO_BYTE, intrinsics.jsAsIs)
@@ -91,6 +82,7 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs)
op(it, ConversionNames.TO_INT, intrinsics.jsAsIs)
op(it, ConversionNames.TO_SHORT, intrinsics.jsAsIs)
op(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
}
for (type in listOf(irBuiltIns.floatType, irBuiltIns.doubleType)) {
@@ -99,6 +91,7 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
op(type, ConversionNames.TO_FLOAT, intrinsics.jsAsIs)
op(type, ConversionNames.TO_INT, intrinsics.jsNumberToInt)
op(type, ConversionNames.TO_SHORT, intrinsics.jsNumberToShort)
op(type, ConversionNames.TO_LONG, intrinsics.jsNumberToLong)
}
irBuiltIns.intType.let {
@@ -107,6 +100,7 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs)
op(it, ConversionNames.TO_INT, intrinsics.jsAsIs)
op(it, ConversionNames.TO_SHORT, intrinsics.jsToShort)
op(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
}
irBuiltIns.shortType.let {
@@ -115,13 +109,14 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs)
op(it, ConversionNames.TO_INT, intrinsics.jsAsIs)
op(it, ConversionNames.TO_SHORT, intrinsics.jsAsIs)
op(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
}
}
symbolToIrFunction.run {
add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq)
// TODO: implement it a right way
add(irBuiltIns.eqeqSymbol, intrinsics.jsEqeq)
add(irBuiltIns.eqeqSymbol, intrinsics.jsEquals.owner)
// TODO: implement it a right way
add(irBuiltIns.ieee754equalsFunByOperandType, intrinsics.jsEqeqeq)
@@ -147,24 +142,39 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
}
}
}
for (type in primitiveNumbers) {
op(type, OperatorNames.ADD, withLongCoercion(intrinsics.jsPlus))
op(type, OperatorNames.SUB, withLongCoercion(intrinsics.jsMinus))
op(type, OperatorNames.MUL, withLongCoercion(intrinsics.jsMult))
op(type, OperatorNames.DIV, withLongCoercion(intrinsics.jsDiv))
op(type, OperatorNames.MOD, withLongCoercion(intrinsics.jsMod))
op(type, OperatorNames.REM, withLongCoercion(intrinsics.jsMod))
}
}
nameToIrTransformer.run {
addWithPredicate(
Name.special(Namer.KCALLABLE_GET_NAME),
{ call -> call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kCallableClass) } ?: false },
{ call ->
call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kCallableClass) } ?: false
},
{ call -> irCall(call, context.intrinsics.jsName.symbol, dispatchReceiverAsFirstArgument = true) })
addWithPredicate(
Name.identifier(Namer.KPROPERTY_GET),
{ call -> call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false },
{ call -> irCall(call, context.intrinsics.jsPropertyGet.symbol, dispatchReceiverAsFirstArgument = true)}
{ call ->
call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false
},
{ call -> irCall(call, context.intrinsics.jsPropertyGet.symbol, dispatchReceiverAsFirstArgument = true) }
)
addWithPredicate(
Name.identifier(Namer.KPROPERTY_SET),
{ call -> call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false},
{ call -> irCall(call, context.intrinsics.jsPropertySet.symbol, dispatchReceiverAsFirstArgument = true)}
{ call ->
call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false
},
{ call -> irCall(call, context.intrinsics.jsPropertySet.symbol, dispatchReceiverAsFirstArgument = true) }
)
addWithPredicate(
@@ -189,12 +199,44 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
override fun lower(irFile: IrFile) {
irFile.transform(object : IrElementTransformerVoid() {
// TODO should this be a separate lowering?
override fun <T> visitConst(expression: IrConst<T>): IrExpression {
if (expression.kind is IrConstKind.Long) {
val value = IrConstKind.Long.valueOf(expression)
val high = (value shr 32).toInt()
val low = value.toInt()
return IrCallImpl(
expression.startOffset,
expression.endOffset,
context.intrinsics.longConstructor.owner.returnType,
context.intrinsics.longConstructor
).apply {
putValueArgument(0, JsIrBuilder.buildInt(context.irBuiltIns.intType, low))
putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, high))
}
}
return super.visitConst(expression)
}
override fun visitCall(expression: IrCall): IrExpression {
val call = super.visitCall(expression)
if (call is IrCall) {
val symbol = call.symbol
// TODO create another map for this? Lower == to equals beforehand?
if (symbol == irBuiltIns.eqeqSymbol) {
val lhs = call.getValueArgument(0)!!
val rhs = call.getValueArgument(1)!!
return when (translateEquals(lhs.type, rhs.type)) {
is IdentityOperator -> irCall(call, intrinsics.jsEqeqeq.symbol)
is EqualityOperator -> irCall(call, intrinsics.jsEqeq.symbol)
else -> irCall(call, intrinsics.jsEquals)
}
}
symbolToIrFunction[symbol]?.let {
return irCall(call, it.symbol)
}
@@ -229,6 +271,58 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
}, null)
}
private fun withLongCoercion(intrinsic: IrSimpleFunction): (IrCall) -> IrExpression = { call ->
assert(call.valueArgumentsCount == 1)
val arg = call.getValueArgument(0)!!
val receiverType = call.dispatchReceiver!!.type
if (arg.type.isLong()) {
when {
// Double OP Long => Double OP Long.toDouble()
receiverType.isDouble() -> {
call.putValueArgument(0, IrCallImpl(
call.startOffset,
call.endOffset,
context.intrinsics.longToDouble.owner.returnType,
context.intrinsics.longToDouble
).apply {
dispatchReceiver = arg
})
}
// Float OP Long => Float OP Long.toFloat()
receiverType.isFloat() -> {
call.putValueArgument(0, IrCallImpl(
call.startOffset,
call.endOffset,
context.intrinsics.longToFloat.owner.returnType,
context.intrinsics.longToFloat
).apply {
dispatchReceiver = arg
})
}
// {Byte, Short, Int} OP Long => {Byte, Sort, Int}.toLong() OP Long
!receiverType.isLong() -> {
call.dispatchReceiver = IrCallImpl(
call.startOffset,
call.endOffset,
intrinsics.jsNumberToLong.owner.returnType,
intrinsics.jsNumberToLong
).apply {
putValueArgument(0, call.dispatchReceiver)
}
}
}
}
if (receiverType.isLong()) {
// LHS is Long => use as is
call
} else {
irCall(call, intrinsic.symbol, dispatchReceiverAsFirstArgument = true)
}
}
private fun transformEquals(call: IrCall): IrExpression {
if (call.superQualifier != null) return call
val symbol = call.symbol
@@ -434,11 +528,16 @@ private fun <V> MutableMap<IrFunctionSymbol, V>.add(from: IrFunctionSymbol, to:
put(from, to)
}
private fun <K> MutableMap<K, (IrCall) -> IrExpression>.addWithPredicate(from: K, predicate: (IrCall) -> Boolean, action: (IrCall) -> IrExpression) {
private fun <K> MutableMap<K, (IrCall) -> IrExpression>.addWithPredicate(
from: K,
predicate: (IrCall) -> Boolean,
action: (IrCall) -> IrExpression
) {
put(from) { call: IrCall -> select({ predicate(call) }, { action(call) }, { call }) }
}
private inline fun <T> select(crossinline predicate: () -> Boolean, crossinline ifTrue: () -> T, crossinline ifFalse: () -> T): T = if (predicate()) ifTrue() else ifFalse()
private inline fun <T> select(crossinline predicate: () -> Boolean, crossinline ifTrue: () -> T, crossinline ifFalse: () -> T): T =
if (predicate()) ifTrue() else ifFalse()
private class SimpleMemberKey(val klass: IrType, val name: Name) {
// TODO drop custom equals and hashCode when IrTypes will have right equals
@@ -82,8 +82,8 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
is IrConstKind.Byte -> JsIntLiteral(kind.valueOf(expression).toInt())
is IrConstKind.Short -> JsIntLiteral(kind.valueOf(expression).toInt())
is IrConstKind.Int -> JsIntLiteral(kind.valueOf(expression))
is IrConstKind.Long,
is IrConstKind.Char -> super.visitConst(expression, context)
is IrConstKind.Long -> throw IllegalStateException("Long const should have been lowered at this point")
is IrConstKind.Char -> TODO("Char const")
is IrConstKind.Float -> JsDoubleLiteral(kind.valueOf(expression).toDouble())
is IrConstKind.Double -> JsDoubleLiteral(kind.valueOf(expression))
}
@@ -26,7 +26,7 @@ object OperatorNames {
val SHL = Name.identifier("shl")
val SHR = Name.identifier("shr")
val SHRU = Name.identifier("shru")
val SHRU = Name.identifier("ushr")
val NOT = OperatorNameConventions.NOT
@@ -46,13 +46,17 @@ abstract class BasicIrBoxTest(
val runtime = listOf(
"libraries/stdlib/js/src/kotlin/core.kt",
"libraries/stdlib/js/irRuntime/core.kt",
"libraries/stdlib/js/irRuntime/long.kt",
"libraries/stdlib/js/irRuntime/longjs.kt",
"libraries/stdlib/js/irRuntime/numberConversion.kt",
"libraries/stdlib/js/irRuntime/compareTo.kt",
"libraries/stdlib/js/irRuntime/annotations.kt",
"libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt",
"libraries/stdlib/js/irRuntime/exceptions.kt",
"libraries/stdlib/js/irRuntime/internalAnnotations.kt",
"libraries/stdlib/js/irRuntime/typeCheckUtils.kt"
"libraries/stdlib/js/irRuntime/typeCheckUtils.kt",
"core/builtins/native/kotlin/Number.kt",
"core/builtins/native/kotlin/Comparable.kt"
).map { createPsiFile(it) }
val filesToIgnore = listOf(
+7 -8
View File
@@ -6,17 +6,16 @@
package kotlin.js
fun equals(obj1: dynamic, obj2: dynamic): Boolean {
if (obj1 == null) {
return obj2 == null
if (js("obj1 == null").unsafeCast<Boolean>()) {
return js("obj2 == null").unsafeCast<Boolean>();
}
if (obj2 == null) {
return false
if (js("obj2 == null").unsafeCast<Boolean>()) {
return false;
}
return js("""
if (typeof obj1 === "object" && typeof obj1.equals === "function") {
return obj1.equals(obj2);
if (typeof obj1 === "object" && typeof obj1.equals_Any_ === "function") {
return obj1.equals_Any_(obj2);
}
if (obj1 !== obj1) {
@@ -31,7 +30,7 @@ fun equals(obj1: dynamic, obj2: dynamic): Boolean {
}
fun toString(o: dynamic): String = when {
o == null -> "null"
js("o == null").unsafeCast<Boolean>() -> "null"
isArrayish(o) -> "[...]"
else -> js("o.toString()").unsafeCast<String>()
}
+253
View File
@@ -0,0 +1,253 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin
/**
* Represents a 64-bit signed integer.
*/
public class Long internal constructor(
internal val low: Int,
internal val high: Int
) : Number(), Comparable<Long> {
companion object {
/**
* A constant holding the minimum value an instance of Long can have.
*/
public const val MIN_VALUE: Long = -9223372036854775807L - 1L
/**
* A constant holding the maximum value an instance of Long can have.
*/
public const val MAX_VALUE: Long = 9223372036854775807L
}
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public inline operator fun compareTo(other: Byte): Int = compareTo(other.toLong())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public inline operator fun compareTo(other: Short): Int = compareTo(other.toLong())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public inline operator fun compareTo(other: Int): Int = compareTo(other.toLong())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public override operator fun compareTo(other: Long): Int = compare(other)
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public inline operator fun compareTo(other: Float): Int = toFloat().compareTo(other)
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
* or a positive number if it's greater than other.
*/
public inline operator fun compareTo(other: Double): Int = toDouble().compareTo(other)
/** Adds the other value to this value. */
public inline operator fun plus(other: Byte): Long = plus(other.toLong())
/** Adds the other value to this value. */
public inline operator fun plus(other: Short): Long = plus(other.toLong())
/** Adds the other value to this value. */
public inline operator fun plus(other: Int): Long = plus(other.toLong())
/** Adds the other value to this value. */
public operator fun plus(other: Long): Long = add(other)
/** Adds the other value to this value. */
public inline operator fun plus(other: Float): Float = toFloat() + other
/** Adds the other value to this value. */
public inline operator fun plus(other: Double): Double = toDouble() + other
/** Subtracts the other value from this value. */
public inline operator fun minus(other: Byte): Long = minus(other.toLong())
/** Subtracts the other value from this value. */
public inline operator fun minus(other: Short): Long = minus(other.toLong())
/** Subtracts the other value from this value. */
public inline operator fun minus(other: Int): Long = minus(other.toLong())
/** Subtracts the other value from this value. */
public operator fun minus(other: Long): Long = subtract(other)
/** Subtracts the other value from this value. */
public inline operator fun minus(other: Float): Float = toFloat() - other
/** Subtracts the other value from this value. */
public inline operator fun minus(other: Double): Double = toDouble() - other
/** Multiplies this value by the other value. */
public inline operator fun times(other: Byte): Long = times(other.toLong())
/** Multiplies this value by the other value. */
public inline operator fun times(other: Short): Long = times(other.toLong())
/** Multiplies this value by the other value. */
public inline operator fun times(other: Int): Long = times(other.toLong())
/** Multiplies this value by the other value. */
public operator fun times(other: Long): Long = multiply(other)
/** Multiplies this value by the other value. */
public inline operator fun times(other: Float): Float = toFloat() * other
/** Multiplies this value by the other value. */
public inline operator fun times(other: Double): Double = toDouble() * other
/** Divides this value by the other value. */
public inline operator fun div(other: Byte): Long = div(other.toLong())
/** Divides this value by the other value. */
public inline operator fun div(other: Short): Long = div(other.toLong())
/** Divides this value by the other value. */
public inline operator fun div(other: Int): Long = div(other.toLong())
/** Divides this value by the other value. */
public operator fun div(other: Long): Long = divide(other)
/** Divides this value by the other value. */
public inline operator fun div(other: Float): Float = toFloat() / other
/** Divides this value by the other value. */
public inline operator fun div(other: Double): Double = toDouble() / other
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public inline operator fun mod(other: Byte): Long = rem(other)
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public inline operator fun mod(other: Short): Long = rem(other)
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public inline operator fun mod(other: Int): Long = rem(other)
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public inline operator fun mod(other: Long): Long = rem(other)
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public inline operator fun mod(other: Float): Float = rem(other)
/** Calculates the remainder of dividing this value by the other value. */
@Deprecated("Use rem(other) instead", ReplaceWith("rem(other)"), DeprecationLevel.WARNING)
public inline operator fun mod(other: Double): Double = rem(other)
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public inline operator fun rem(other: Byte): Long = rem(other.toLong())
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public inline operator fun rem(other: Short): Long = rem(other.toLong())
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public inline operator fun rem(other: Int): Long = rem(other.toLong())
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public operator fun rem(other: Long): Long = modulo(other)
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public inline operator fun rem(other: Float): Float = toFloat() % other
/** Calculates the remainder of dividing this value by the other value. */
@SinceKotlin("1.1")
public inline operator fun rem(other: Double): Double = toDouble() % other
/** Increments this value. */
public operator fun inc(): Long = this + 1L
/** Decrements this value. */
public operator fun dec(): Long = this - 1L
/** Returns this value. */
public inline operator fun unaryPlus(): Long = this
/** Returns the negative of this value. */
public operator fun unaryMinus(): Long = inv() + 1L
/* TODO
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Byte): LongRange
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Short): LongRange
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Int): LongRange
/** Creates a range from this value to the specified [other] value. */
public operator fun rangeTo(other: Long): LongRange
*/
/** Shifts this value left by the [bitCount] number of bits. */
public infix fun shl(bitCount: Int): Long = shiftLeft(bitCount)
/** Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with copies of the sign bit. */
public infix fun shr(bitCount: Int): Long = shiftRight(bitCount)
/** Shifts this value right by the [bitCount] number of bits, filling the leftmost bits with zeros. */
public infix fun ushr(bitCount: Int): Long = shiftRightUnsigned(bitCount)
/** Performs a bitwise AND operation between the two values. */
public infix fun and(other: Long): Long = Long(low and other.low, high and other.high)
/** Performs a bitwise OR operation between the two values. */
public infix fun or(other: Long): Long = Long(low or other.low, high or other.high)
/** Performs a bitwise XOR operation between the two values. */
public infix fun xor(other: Long): Long = Long(low xor other.low, high xor other.high)
/** Inverts the bits in this value. */
public fun inv(): Long = Long(low.inv(), high.inv())
public override fun toByte(): Byte = low.toByte()
public override fun toChar(): Char = low.toChar()
public override fun toShort(): Short = low.toShort()
public override fun toInt(): Int = low
public override fun toLong(): Long = this
public override fun toFloat(): Float = toDouble().toFloat()
public override fun toDouble(): Double = toNumber()
// TODO: is it still needed?
private fun valueOf() = toDouble()
override fun equals(other: Any?): Boolean = other is Long && equalsLong(other)
override fun hashCode(): Int = hashCode(this)
override fun toString(): String = toString(10)
}
+380
View File
@@ -0,0 +1,380 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
package kotlin
internal fun Long.toNumber() = high * TWO_PWR_32_DBL_ + getLowBitsUnsigned()
internal fun Long.getLowBitsUnsigned() = if (low >= 0) low.toDouble() else TWO_PWR_32_DBL_ + low
internal fun hashCode(l: Long) = l.low xor l.high
internal fun Long.toString(radix: Int): String {
if (radix < 2 || 36 < radix) {
throw Exception("radix out of range: $radix")
}
if (isZero()) {
return "0"
}
if (isNegative()) {
if (equalsLong(MIN_VALUE)) {
// We need to change the Long value before it can be negated, so we remove
// the bottom-most digit in this base and then recurse to do the rest.
val radixLong = fromInt(radix)
val div = div(radixLong)
val rem = div.multiply(radixLong).subtract(this).toInt();
return js("div.toString(radix) + rem.toString(radix)")
} else {
return "-${negate().toString()}"
}
}
// Do several (6) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
val radixToPower = fromNumber(js("Math.pow(radix, 6)").unsafeCast<Double>())
var rem = this
var result = ""
while (true) {
val remDiv = rem.div(radixToPower)
val intval = rem.subtract(remDiv.multiply(radixToPower)).toInt()
var digits = js("intval.toString(radix)").unsafeCast<String>()
rem = remDiv
if (rem.isZero()) {
return digits + result
} else {
while (digits.length < 6) {
digits = "0" + digits
}
result = digits + result
}
}
}
internal fun Long.negate() = unaryMinus()
internal fun Long.isZero() = high == 0 && low == 0
internal fun Long.isNegative() = high < 0
internal fun Long.isOdd() = low and 1 == 1
internal fun Long.equalsLong(other: Long) = high == other.high && low == other.low
internal fun Long.lessThan(other: Long) = compare(other) < 0
internal fun Long.lessThanOrEqual(other: Long) = compare(other) <= 0
internal fun Long.greaterThan(other: Long) = compare(other) > 0
internal fun Long.greaterThanOrEqual(other: Long) = compare(other) >= 0
internal fun Long.compare(other: Long): Int {
if (equalsLong(other)) {
return 0;
}
val thisNeg = isNegative();
val otherNeg = other.isNegative();
return when {
thisNeg && !otherNeg -> -1
!thisNeg && otherNeg -> 1
// at this point, the signs are the same, so subtraction will not overflow
subtract(other).isNegative() -> -1
else -> 1
}
}
internal fun Long.add(other: Long): Long {
// Divide each number into 4 chunks of 16 bits, and then sum the chunks.
val a48 = high ushr 16
val a32 = high and 0xFFFF
val a16 = high ushr 16
val a00 = low and 0xFFFF
val b48 = other.high ushr 16
val b32 = other.high and 0xFFFF
val b16 = other.low ushr 16
val b00 = other.low and 0xFFFF
var c48 = 0
var c32 = 0
var c16 = 0
var c00 = 0
c00 += a00 + b00
c16 += c00 ushr 16
c00 = c00 and 0xFFFF
c16 += a16 + b16
c32 += c16 ushr 16
c16 = c16 and 0xFFFF
c32 += a32 + b32
c48 += c32 ushr 16
c32 = c32 and 0xFFFF
c48 += a48 + b48
c48 = c48 and 0xFFFF
return Long((c16 shl 16) or c00, (c48 shl 16) or c32)
}
internal fun Long.subtract(other: Long) = add(other.unaryMinus())
internal fun Long.multiply(other: Long): Long {
if (isZero()) {
return ZERO
} else if (other.isZero()) {
return ZERO
}
if (equalsLong(MIN_VALUE)) {
return if (other.isOdd()) MIN_VALUE else ZERO
} else if (other.equalsLong(MIN_VALUE)) {
return if (isOdd()) MIN_VALUE else ZERO
}
if (isNegative()) {
return if (other.isNegative()) {
negate().multiply(other.negate())
} else {
negate().multiply(other).negate()
}
} else if (other.isNegative()) {
return multiply(other.negate()).negate()
}
// If both longs are small, use float multiplication
if (lessThan(TWO_PWR_24_) && other.lessThan(TWO_PWR_24_)) {
return fromNumber(toNumber() * other.toNumber())
}
// Divide each long into 4 chunks of 16 bits, and then add up 4x4 products.
// We can skip products that would overflow.
val a48 = high ushr 16
val a32 = high and 0xFFFF
val a16 = low ushr 16
val a00 = low and 0xFFFF
val b48 = other.high ushr 16
val b32 = other.high and 0xFFFF
val b16 = other.low ushr 16
val b00 = other.low and 0xFFFF
var c48 = 0
var c32 = 0
var c16 = 0
var c00 = 0
c00 += a00 * b00
c16 += c00 ushr 16
c00 = c00 and 0xFFFF
c16 += a16 * b00
c32 += c16 ushr 16
c16 = c16 and 0xFFFF
c16 += a00 * b16
c32 += c16 ushr 16
c16 = c16 and 0xFFFF
c32 += a32 * b00
c48 += c32 ushr 16
c32 = c32 and 0xFFFF
c32 += a16 * b16
c48 += c32 ushr 16
c32 = c32 and 0xFFFF
c32 += a00 * b32
c48 += c32 ushr 16
c32 = c32 and 0xFFFF
c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48
c48 = c48 and 0xFFFF
return Long(c16 shl 16 or c00, c48 shl 16 or c32)
}
internal fun Long.divide(other: Long): Long {
if (other.isZero()) {
throw Exception("division by zero")
} else if (isZero()) {
return ZERO
}
if (equalsLong(MIN_VALUE)) {
if (other.equalsLong(ONE) || other.equalsLong(NEG_ONE)) {
return MIN_VALUE // recall that -MIN_VALUE == MIN_VALUE
} else if (other.equalsLong(MIN_VALUE)) {
return ONE
} else {
// At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
val halfThis = shiftRight(1)
val approx = halfThis.div(other).shiftLeft(1)
if (approx.equalsLong(ZERO)) {
return if (other.isNegative()) ONE else NEG_ONE
} else {
val rem = subtract(other.multiply(approx))
return approx.add(rem.div(other))
}
}
} else if (other.equalsLong(MIN_VALUE)) {
return ZERO
}
if (isNegative()) {
return if (other.isNegative()) {
negate().div(other.negate())
} else {
negate().div(other).negate()
}
} else if (other.isNegative()) {
return div(other.negate()).negate()
}
// Repeat the following until the remainder is less than other: find a
// floating-point that approximates remainder / other *from below*, add this
// into the result, and subtract it from the remainder. It is critical that
// the approximate value is less than or equal to the real value so that the
// remainder never becomes negative.
var res = ZERO
var rem = this
while (rem.greaterThanOrEqual(other)) {
// Approximate the result of division. This may be a little greater or
// smaller than the actual value.
val approxDouble = rem.toNumber() / other.toNumber()
var approx = js("Math.max(1, Math.floor(approxDouble))").unsafeCast<Double>()
// We will tweak the approximate result by changing it in the 48-th digit or
// the smallest non-fractional digit, whichever is larger.
val log2 = js("Math.ceil(Math.log(approx) / Math.LN2)").unsafeCast<Int>()
val delta = if (log2 <= 48) 1.0 else js("Math.pow(2, log2 - 48)").unsafeCast<Double>()
// Decrease the approximation until it is smaller than the remainder. Note
// that if it is too large, the product overflows and is negative.
var approxRes = fromNumber(approx)
var approxRem = approxRes.multiply(other)
while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
approx -= delta
approxRes = fromNumber(approx)
approxRem = approxRes.multiply(other)
}
// We know the answer can't be zero... and actually, zero would cause
// infinite recursion since we would make no progress.
if (approxRes.isZero()) {
approxRes = ONE
}
res = res.add(approxRes)
rem = rem.subtract(approxRem)
}
return res
}
internal fun Long.modulo(other: Long) = subtract(div(other).multiply(other))
internal fun Long.shiftLeft(numBits: Int): Long {
val numBits = numBits and 63
if (numBits == 0) {
return this
} else {
if (numBits < 32) {
return Long(low shl numBits, (high shl numBits) or (low ushr (32 - numBits)))
} else {
return Long(0, low shl (numBits - 32))
}
}
}
internal fun Long.shiftRight(numBits: Int): Long {
val numBits = numBits and 63
if (numBits == 0) {
return this
} else {
if (numBits < 32) {
return Long((low ushr numBits) or (high shl (32 - numBits)), high shr numBits)
} else {
return Long(high shr (numBits - 32), if (high >= 0) 0 else -1)
}
}
}
internal fun Long.shiftRightUnsigned(numBits: Int): Long {
val numBits = numBits and 63
if (numBits == 0) {
return this
} else {
if (numBits < 32) {
return Long((low ushr numBits) or (high shl (32 - numBits)), high ushr numBits)
} else return if (numBits == 32) {
Long(high, 0)
} else {
Long(high ushr (numBits - 32), 0)
}
}
}
/**
* Returns a Long representing the given (32-bit) integer value.
* @param {number} value The 32-bit integer in question.
* @return {!Kotlin.Long} The corresponding Long value.
*/
// TODO: cache
internal fun fromInt(value: Int) = Long(value, if (value < 0) -1 else 0)
/**
* Returns a Long representing the given value, provided that it is a finite
* number. Otherwise, zero is returned.
* @param {number} value The number in question.
* @return {!Kotlin.Long} The corresponding Long value.
*/
internal fun fromNumber(value: Double): Long {
if (js("isNaN(value)").unsafeCast<Boolean>() || !js("isFinite(value)").unsafeCast<Boolean>()) {
return ZERO;
} else if (value <= -TWO_PWR_63_DBL_) {
return MIN_VALUE;
} else if (value + 1 >= TWO_PWR_63_DBL_) {
return MAX_VALUE;
} else if (value < 0) {
return fromNumber(-value).negate();
} else {
val twoPwr32 = TWO_PWR_32_DBL_
return Long(
js("(value % twoPwr32) | 0").unsafeCast<Int>(),
js("(value / twoPwr32) | 0").unsafeCast<Int>()
)
}
}
private val TWO_PWR_16_DBL_ = (1 shl 16).toDouble()
private val TWO_PWR_24_DBL_ = (1 shl 24).toDouble()
private val TWO_PWR_32_DBL_ = TWO_PWR_16_DBL_ * TWO_PWR_16_DBL_
private val TWO_PWR_64_DBL_ = TWO_PWR_32_DBL_ * TWO_PWR_32_DBL_
private val TWO_PWR_63_DBL_ = TWO_PWR_64_DBL_ / 2
private val ZERO = fromInt(0)
private val ONE = fromInt(1)
private val NEG_ONE = fromInt(-1)
private val MAX_VALUE = Long(-1, -1 ushr 1)
private val MIN_VALUE = Long(0, 1 shl 31)
private val TWO_PWR_24_ = fromInt(1 shl 24)
@@ -19,6 +19,10 @@ fun numberToShort(a: dynamic): Short = toShort(numberToInt(a))
fun toByte(a: dynamic): Byte = js("a << 24 >> 24").unsafeCast<Byte>()
fun toShort(a: dynamic): Short = js("a << 16 >> 16").unsafeCast<Short>()
fun numberToLong(a: dynamic): Long = fromNumber(a)
fun toLong(a: dynamic): Long = fromInt(a)
fun doubleToInt(a: dynamic) = js("""
if (a > 2147483647) return 2147483647;
if (a < -2147483648) return -2147483648;
@@ -6,7 +6,7 @@
package kotlin.js
@kotlin.internal.DynamicExtension
public fun <T> dynamic.unsafeCast(): T = this
public inline fun <T> dynamic.unsafeCast(): T = this
private fun isInterfaceImpl(ctor: dynamic, iface: dynamic): Boolean {
if (ctor === iface) return true;