[stdlib] Merge js-ir specific sources into common js sources
This commit is contained in:
committed by
Space Team
parent
f00d4022c4
commit
911fa3bbbb
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal typealias BitMask = IntArray
|
||||
|
||||
private fun bitMaskWith(activeBit: Int): BitMask {
|
||||
val numberIndex = activeBit shr 5
|
||||
val intArray = IntArray(numberIndex + 1)
|
||||
val positionInNumber = activeBit and 31
|
||||
val numberWithSettledBit = 1 shl positionInNumber
|
||||
intArray[numberIndex] = intArray[numberIndex] or numberWithSettledBit
|
||||
return intArray
|
||||
}
|
||||
|
||||
internal fun BitMask.isBitSet(possibleActiveBit: Int): Boolean {
|
||||
val numberIndex = possibleActiveBit shr 5
|
||||
if (numberIndex > size) return false
|
||||
val positionInNumber = possibleActiveBit and 31
|
||||
val numberWithSettledBit = 1 shl positionInNumber
|
||||
return get(numberIndex) and numberWithSettledBit != 0
|
||||
}
|
||||
|
||||
private fun compositeBitMask(capacity: Int, masks: Array<BitMask>): BitMask {
|
||||
return IntArray(capacity) { i ->
|
||||
var result = 0
|
||||
for (mask in masks) {
|
||||
if (i < mask.size) {
|
||||
result = result or mask[i]
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
internal fun implement(interfaces: Array<dynamic>): BitMask {
|
||||
var maxSize = 1
|
||||
val masks = js("[]")
|
||||
|
||||
for (i in interfaces) {
|
||||
var currentSize = maxSize
|
||||
val imask: BitMask? = i.prototype.`$imask$` ?: i.`$imask$`
|
||||
|
||||
if (imask != null) {
|
||||
masks.push(imask)
|
||||
currentSize = imask.size
|
||||
}
|
||||
|
||||
val iid: Int? = i.`$metadata$`.iid
|
||||
val iidImask: BitMask? = iid?.let { bitMaskWith(it) }
|
||||
|
||||
if (iidImask != null) {
|
||||
masks.push(iidImask)
|
||||
currentSize = JsMath.max(currentSize, iidImask.size)
|
||||
}
|
||||
|
||||
if (currentSize > maxSize) {
|
||||
maxSize = currentSize
|
||||
}
|
||||
}
|
||||
|
||||
return compositeBitMask(maxSize, masks)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal object DefaultConstructorMarker
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@JsName("Error")
|
||||
internal open external class JsError(message: String) : Throwable
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
import withType
|
||||
|
||||
@PublishedApi
|
||||
internal external fun <T> Array(size: Int): Array<T>
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T> fillArrayVal(array: Array<T>, initValue: T): Array<T> {
|
||||
for (i in 0..array.size - 1) {
|
||||
array[i] = initValue
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
internal inline fun <T> arrayWithFun(size: Int, init: (Int) -> T) = fillArrayFun(Array<T>(size), init)
|
||||
|
||||
internal inline fun <T> fillArrayFun(array: dynamic, init: (Int) -> T): Array<T> {
|
||||
val result = array.unsafeCast<Array<T>>()
|
||||
var i = 0
|
||||
while (i != result.size) {
|
||||
result[i] = init(i)
|
||||
++i
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun booleanArray(size: Int): BooleanArray = withType("BooleanArray", fillArrayVal(Array<Boolean>(size), false)).unsafeCast<BooleanArray>()
|
||||
|
||||
internal fun booleanArrayOf(arr: Array<Boolean>): BooleanArray = withType("BooleanArray", arr.asDynamic().slice()).unsafeCast<BooleanArray>()
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun charArray(size: Int): CharArray = withType("CharArray", js("new Uint16Array(size)")).unsafeCast<CharArray>()
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun charArrayOf(arr: Array<Char>): CharArray = withType("CharArray", js("new Uint16Array(arr)")).unsafeCast<CharArray>()
|
||||
|
||||
internal fun longArray(size: Int): LongArray = withType("LongArray", fillArrayVal(Array<Long>(size), 0L)).unsafeCast<LongArray>()
|
||||
|
||||
internal fun longArrayOf(arr: Array<Long>): LongArray = withType("LongArray", arr.asDynamic().slice()).unsafeCast<LongArray>()
|
||||
|
||||
internal fun <T> arrayIterator(array: Array<T>) = object : Iterator<T> {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun next() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
|
||||
internal fun booleanArrayIterator(array: BooleanArray) = object : BooleanIterator() {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun nextBoolean() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
|
||||
internal fun byteArrayIterator(array: ByteArray) = object : ByteIterator() {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun nextByte() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
|
||||
internal fun shortArrayIterator(array: ShortArray) = object : ShortIterator() {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun nextShort() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
|
||||
internal fun charArrayIterator(array: CharArray) = object : CharIterator() {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun nextChar() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
|
||||
internal fun intArrayIterator(array: IntArray) = object : IntIterator() {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun nextInt() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
|
||||
internal fun floatArrayIterator(array: FloatArray) = object : FloatIterator() {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun nextFloat() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
|
||||
internal fun doubleArrayIterator(array: DoubleArray) = object : DoubleIterator() {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun nextDouble() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
|
||||
internal fun longArrayIterator(array: LongArray) = object : LongIterator() {
|
||||
var index = 0
|
||||
override fun hasNext() = index != array.size
|
||||
override fun nextLong() = if (index != array.size) array[index++] else throw NoSuchElementException("$index")
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
// TODO use declarations from stdlib
|
||||
private external class ArrayBuffer(size: Int)
|
||||
private external class Float64Array(buffer: ArrayBuffer)
|
||||
private external class Float32Array(buffer: ArrayBuffer)
|
||||
private external class Int32Array(buffer: ArrayBuffer)
|
||||
|
||||
private val buf = ArrayBuffer(8)
|
||||
// TODO use one DataView instead of bunch of typed views.
|
||||
private val bufFloat64 = Float64Array(buf).unsafeCast<DoubleArray>()
|
||||
private val bufFloat32 = Float32Array(buf).unsafeCast<FloatArray>()
|
||||
private val bufInt32 = Int32Array(buf).unsafeCast<IntArray>()
|
||||
|
||||
private val lowIndex = run {
|
||||
bufFloat64[0] = -1.0 // bff00000_00000000
|
||||
if (bufInt32[0] != 0) 1 else 0
|
||||
}
|
||||
private val highIndex = 1 - lowIndex
|
||||
|
||||
internal fun doubleToRawBits(value: Double): Long {
|
||||
bufFloat64[0] = value
|
||||
return Long(bufInt32[lowIndex], bufInt32[highIndex])
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun doubleFromBits(value: Long): Double {
|
||||
bufInt32[lowIndex] = value.low
|
||||
bufInt32[highIndex] = value.high
|
||||
return bufFloat64[0]
|
||||
}
|
||||
|
||||
internal fun floatToRawBits(value: Float): Int {
|
||||
bufFloat32[0] = value
|
||||
return bufInt32[0]
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun floatFromBits(value: Int): Float {
|
||||
bufInt32[0] = value
|
||||
return bufFloat32[0]
|
||||
}
|
||||
|
||||
// returns zero value for number with positive sign bit and non-zero value for number with negative sign bit.
|
||||
internal fun doubleSignBit(value: Double): Int {
|
||||
bufFloat64[0] = value
|
||||
return bufInt32[highIndex] and Int.MIN_VALUE
|
||||
}
|
||||
|
||||
internal fun getNumberHashCode(obj: Double): Int {
|
||||
@Suppress("DEPRECATED_IDENTITY_EQUALS")
|
||||
if (jsBitwiseOr(obj, 0).unsafeCast<Double>() === obj) {
|
||||
return obj.toInt()
|
||||
}
|
||||
|
||||
bufFloat64[0] = obj
|
||||
return bufInt32[highIndex] * 31 + bufInt32[lowIndex]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
import JsError
|
||||
|
||||
@JsName("Boolean")
|
||||
internal external fun nativeBoolean(obj: Any?): Boolean
|
||||
|
||||
internal fun booleanInExternalLog(name: String, obj: dynamic) {
|
||||
if (jsTypeOf(obj) != "boolean") {
|
||||
console.asDynamic().error("Boolean expected for '$name', but actual:", obj)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun booleanInExternalException(name: String, obj: dynamic) {
|
||||
if (jsTypeOf(obj) != "boolean") {
|
||||
throw JsError("Boolean expected for '$name', but actual: $obj")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal annotation class DoNotIntrinsify
|
||||
|
||||
@PublishedApi
|
||||
@DoNotIntrinsify
|
||||
internal fun charSequenceGet(a: CharSequence, index: Int): Char {
|
||||
return if (isString(a)) {
|
||||
Char(a.asDynamic().charCodeAt(index).unsafeCast<Int>())
|
||||
} else {
|
||||
a[index]
|
||||
}
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@DoNotIntrinsify
|
||||
internal fun charSequenceLength(a: CharSequence): Int {
|
||||
return if (isString(a)) {
|
||||
a.asDynamic().length.unsafeCast<Int>()
|
||||
} else {
|
||||
a.length
|
||||
}
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@DoNotIntrinsify
|
||||
internal fun charSequenceSubSequence(a: CharSequence, startIndex: Int, endIndex: Int): CharSequence {
|
||||
return if (isString(a)) {
|
||||
a.asDynamic().substring(startIndex, endIndex).unsafeCast<String>()
|
||||
} else {
|
||||
a.subSequence(startIndex, endIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// Keeping this function as separate non-inline to intrincify `is` operator
|
||||
internal fun isString(a: CharSequence) = a is String
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.collections
|
||||
|
||||
import kotlin.js.*
|
||||
|
||||
internal fun arrayToString(array: Array<*>) = array.joinToString(", ", "[", "]") { toString(it) }
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
internal fun <T> Array<out T>?.contentDeepHashCodeInternal(): Int {
|
||||
if (this == null) return 0
|
||||
var result = 1
|
||||
for (element in this) {
|
||||
val elementHash = when {
|
||||
element == null -> 0
|
||||
isArrayish(element) -> (element.unsafeCast<Array<*>>()).contentDeepHashCodeInternal()
|
||||
|
||||
element is UByteArray -> element.contentHashCode()
|
||||
element is UShortArray -> element.contentHashCode()
|
||||
element is UIntArray -> element.contentHashCode()
|
||||
element is ULongArray -> element.contentHashCode()
|
||||
|
||||
else -> element.hashCode()
|
||||
}
|
||||
|
||||
result = 31 * result + elementHash
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun <T> T.contentEqualsInternal(other: T): Boolean {
|
||||
val a = this.asDynamic()
|
||||
val b = other.asDynamic()
|
||||
|
||||
if (a === b) return true
|
||||
|
||||
if (a == null || b == null || !isArrayish(b) || a.length != b.length) return false
|
||||
|
||||
for (i in 0 until a.length) {
|
||||
if (!equals(a[i], b[i])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun <T> T.contentHashCodeInternal(): Int {
|
||||
val a = this.asDynamic()
|
||||
if (a == null) return 0
|
||||
|
||||
var result = 1
|
||||
|
||||
for (i in 0 until a.length) {
|
||||
result = result * 31 + hashCode(a[i])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
|
||||
// Adopted from misc.js
|
||||
|
||||
internal fun compareTo(a: dynamic, b: dynamic): Int = when (jsTypeOf(a)) {
|
||||
"number" -> when {
|
||||
jsTypeOf(b) == "number" ->
|
||||
doubleCompareTo(a, b)
|
||||
b is Long ->
|
||||
doubleCompareTo(a, b.toDouble())
|
||||
else ->
|
||||
primitiveCompareTo(a, b)
|
||||
}
|
||||
|
||||
"string", "boolean" -> primitiveCompareTo(a, b)
|
||||
|
||||
else -> compareToDoNotIntrinsicify(a, b)
|
||||
}
|
||||
|
||||
@DoNotIntrinsify
|
||||
private fun <T : Comparable<T>> compareToDoNotIntrinsicify(a: Comparable<T>, b: T) =
|
||||
a.compareTo(b)
|
||||
|
||||
internal fun primitiveCompareTo(a: dynamic, b: dynamic): Int =
|
||||
when {
|
||||
a < b -> -1
|
||||
a > b -> 1
|
||||
else -> 0
|
||||
}
|
||||
|
||||
internal fun doubleCompareTo(a: dynamic, b: dynamic): Int =
|
||||
when {
|
||||
a < b -> -1
|
||||
a > b -> 1
|
||||
|
||||
a === b -> {
|
||||
if (a !== 0)
|
||||
0
|
||||
else {
|
||||
val ia = 1.asDynamic() / a
|
||||
if (ia === 1.asDynamic() / b) {
|
||||
0
|
||||
} else if (ia < 0) {
|
||||
-1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a !== a ->
|
||||
if (b !== b) 0 else 1
|
||||
|
||||
else -> -1
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
/**
|
||||
* @param CT is return type of calling constructor (uses in DCE)
|
||||
*/
|
||||
internal fun <CT> construct(constructorType: dynamic, resultType: dynamic, vararg args: Any?): Any {
|
||||
return js("Reflect").construct(constructorType, args, resultType)
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal fun equals(obj1: dynamic, obj2: dynamic): Boolean {
|
||||
if (obj1 == null) {
|
||||
return obj2 == null
|
||||
}
|
||||
if (obj2 == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (jsTypeOf(obj1) == "object" && jsTypeOf(obj1.equals) == "function") {
|
||||
return (obj1.equals)(obj2)
|
||||
}
|
||||
|
||||
if (obj1 !== obj1) {
|
||||
return obj2 !== obj2
|
||||
}
|
||||
|
||||
if (jsTypeOf(obj1) == "number" && jsTypeOf(obj2) == "number") {
|
||||
return obj1 === obj2 && (obj1 !== 0 || 1.asDynamic() / obj1 === 1.asDynamic() / obj2)
|
||||
}
|
||||
return obj1 === obj2
|
||||
}
|
||||
|
||||
internal fun toString(o: dynamic): String = when {
|
||||
o == null -> "null"
|
||||
isArrayish(o) -> "[...]"
|
||||
jsTypeOf(o.toString) != "function" -> anyToString(o)
|
||||
else -> (o.toString)().unsafeCast<String>()
|
||||
}
|
||||
|
||||
internal fun anyToString(o: dynamic): String = js("Object").prototype.toString.call(o)
|
||||
|
||||
internal fun hashCode(obj: dynamic): Int {
|
||||
if (obj == null) return 0
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
return when (val typeOf = jsTypeOf(obj)) {
|
||||
"object" -> if ("function" === jsTypeOf(obj.hashCode)) (obj.hashCode)() else getObjectHashCode(obj)
|
||||
"function" -> getObjectHashCode(obj)
|
||||
"number" -> getNumberHashCode(obj)
|
||||
"boolean" -> getBooleanHashCode(obj.unsafeCast<Boolean>())
|
||||
"string" -> getStringHashCode(js("String")(obj))
|
||||
"bigint" -> getBigIntHashCode(obj)
|
||||
"symbol" -> getSymbolHashCode(obj)
|
||||
else -> js("throw new Error('Unexpected typeof `' + typeOf + '`')")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getBooleanHashCode(value: Boolean): Int {
|
||||
return if (value) 1231 else 1237
|
||||
}
|
||||
|
||||
private fun getBigIntHashCode(value: dynamic): Int {
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val shiftNumber = js("BigInt(32)");
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val MASK = js("BigInt(0xffffffff)");
|
||||
|
||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||
var bigNumber = if (value < 0) -value else value
|
||||
var hashCode = 0
|
||||
val signum = if (value < 0) -1 else 1
|
||||
|
||||
while (bigNumber != 0) {
|
||||
val chunk = js("Number(bigNumber & MASK)").unsafeCast<Int>()
|
||||
hashCode = 31 * hashCode + chunk
|
||||
@Suppress("UNUSED_VALUE")
|
||||
bigNumber = js("bigNumber >> shiftNumber")
|
||||
}
|
||||
|
||||
return hashCode * signum
|
||||
}
|
||||
|
||||
@Suppress("MUST_BE_INITIALIZED")
|
||||
private var symbolWeakMap: dynamic
|
||||
|
||||
@Suppress("MUST_BE_INITIALIZED")
|
||||
private var symbolMap: dynamic
|
||||
|
||||
private fun getSymbolWeakMap(): dynamic {
|
||||
if (symbolWeakMap === VOID) {
|
||||
symbolWeakMap = js("new WeakMap()")
|
||||
}
|
||||
return symbolWeakMap
|
||||
}
|
||||
|
||||
private fun getSymbolMap(): dynamic {
|
||||
if (symbolMap === VOID) {
|
||||
symbolMap = js("new Map()")
|
||||
}
|
||||
return symbolMap
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun symbolIsSharable(symbol: dynamic) = js("Symbol.keyFor(symbol)") != VOID
|
||||
|
||||
private fun getSymbolHashCode(value: dynamic): Int {
|
||||
val hashCodeMap = if (symbolIsSharable(value)) getSymbolMap() else getSymbolWeakMap()
|
||||
val cachedHashCode = hashCodeMap.get(value)
|
||||
|
||||
if (cachedHashCode !== VOID) return cachedHashCode
|
||||
|
||||
val hash = calculateRandomHash()
|
||||
hashCodeMap.set(value, hash)
|
||||
return hash
|
||||
}
|
||||
|
||||
private const val POW_2_32 = 4294967296.0
|
||||
private const val OBJECT_HASH_CODE_PROPERTY_NAME = "kotlinHashCodeValue$"
|
||||
|
||||
private fun calculateRandomHash(): Int {
|
||||
return jsBitwiseOr(js("Math").random() * POW_2_32, 0) // Make 32-bit singed integer.
|
||||
}
|
||||
|
||||
internal fun getObjectHashCode(obj: dynamic): Int {
|
||||
if (!jsIn(OBJECT_HASH_CODE_PROPERTY_NAME, obj)) {
|
||||
var hash = calculateRandomHash()
|
||||
var descriptor = js("new Object()")
|
||||
descriptor.value = hash
|
||||
descriptor.enumerable = false
|
||||
js("Object").defineProperty(obj, OBJECT_HASH_CODE_PROPERTY_NAME, descriptor)
|
||||
}
|
||||
return obj[OBJECT_HASH_CODE_PROPERTY_NAME].unsafeCast<Int>();
|
||||
}
|
||||
|
||||
internal fun getStringHashCode(str: String): Int {
|
||||
var hash = 0
|
||||
val length: Int = str.length // TODO: Implement WString.length
|
||||
for (i in 0..length-1) {
|
||||
val code: Int = str.asDynamic().charCodeAt(i)
|
||||
hash = hash * 31 + code
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
internal fun identityHashCode(obj: Any?): Int = getObjectHashCode(obj)
|
||||
|
||||
internal fun captureStack(instance: Throwable, constructorFunction: Any) {
|
||||
if (js("Error").captureStackTrace != null) {
|
||||
js("Error").captureStackTrace(instance, constructorFunction)
|
||||
} else {
|
||||
instance.asDynamic().stack = js("new Error()").stack
|
||||
}
|
||||
}
|
||||
|
||||
internal fun newThrowable(message: String?, cause: Throwable?): Throwable {
|
||||
val throwable = js("new Error()")
|
||||
throwable.message = if (isUndefined(message)) {
|
||||
if (isUndefined(cause)) message else cause?.toString() ?: VOID
|
||||
} else message ?: VOID
|
||||
throwable.cause = cause
|
||||
throwable.name = "Throwable"
|
||||
return throwable.unsafeCast<Throwable>()
|
||||
}
|
||||
|
||||
internal fun extendThrowable(this_: dynamic, message: String?, cause: Throwable?) {
|
||||
js("Error").call(this_)
|
||||
setPropertiesToThrowableInstance(this_, message, cause)
|
||||
}
|
||||
|
||||
internal fun setPropertiesToThrowableInstance(this_: dynamic, message: String?, cause: Throwable?) {
|
||||
val errorInfo = calculateErrorInfo(JsObject.getPrototypeOf(this_))
|
||||
if ((errorInfo and 0x1) == 0) {
|
||||
@Suppress("IfThenToElvis")
|
||||
this_.message = if (message == null) {
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
if (message !== null) {
|
||||
// undefined
|
||||
cause?.toString() ?: VOID
|
||||
} else {
|
||||
// real null
|
||||
VOID
|
||||
}
|
||||
} else message
|
||||
}
|
||||
if ((errorInfo and 0x2) == 0) {
|
||||
this_.cause = cause
|
||||
}
|
||||
this_.name = JsObject.getPrototypeOf(this_).constructor.name
|
||||
}
|
||||
|
||||
@JsName("Object")
|
||||
internal external class JsObject {
|
||||
companion object {
|
||||
fun getPrototypeOf(obj: Any?): dynamic
|
||||
}
|
||||
}
|
||||
|
||||
// Note: once some error-compilation design happened consider to distinguish a special exception for error-code.
|
||||
internal fun errorCode(description: String): Nothing {
|
||||
throw IllegalStateException(description)
|
||||
}
|
||||
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
internal fun isUndefined(value: dynamic): Boolean = value === VOID
|
||||
|
||||
internal fun <T, R> boxIntrinsic(@Suppress("UNUSED_PARAMETER") x: T): R = error("Should be lowered")
|
||||
internal fun <T, R> unboxIntrinsic(@Suppress("UNUSED_PARAMETER") x: T): R = error("Should be lowered")
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun protoOf(constructor: Any) =
|
||||
js("constructor.prototype")
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun <T> objectCreate(proto: T?) =
|
||||
js("Object.create(proto)")
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun createThis(ctor: Ctor, box: dynamic): dynamic {
|
||||
val self = js("Object.create(ctor.prototype)")
|
||||
boxApply(self, box)
|
||||
return self
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun boxApply(self: dynamic, box: dynamic) {
|
||||
if (box !== VOID) js("Object.assign(self, box)")
|
||||
}
|
||||
|
||||
@OptIn(JsIntrinsic::class)
|
||||
@Suppress("UNUSED_PARAMETER", "UNUSED_VARIABLE", "REIFIED_TYPE_PARAMETER_NO_INLINE")
|
||||
internal fun <reified T : Any> createExternalThis(
|
||||
ctor: JsClass<T>,
|
||||
superExternalCtor: JsClass<T>,
|
||||
parameters: Array<Any?>,
|
||||
box: dynamic
|
||||
): T {
|
||||
val selfCtor = if (box === VOID) {
|
||||
ctor
|
||||
} else {
|
||||
val newCtor: dynamic = jsNewAnonymousClass(ctor)
|
||||
js("Object.assign(newCtor.prototype, box)")
|
||||
newCtor.constructor = ctor
|
||||
newCtor
|
||||
}
|
||||
return js("Reflect.construct(superExternalCtor, parameters, selfCtor)")
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun defineProp(obj: Any, name: String, getter: Any?, setter: Any?) =
|
||||
js("Object.defineProperty(obj, name, { configurable: true, get: getter, set: setter })")
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
import kotlin.coroutines.*
|
||||
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T> getContinuation(): Continuation<T> { throw Exception("Implemented as intrinsic") }
|
||||
// Do we really need this intrinsic in JS?
|
||||
|
||||
@PublishedApi
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal suspend fun <T> returnIfSuspended(argument: Any?): T {
|
||||
return argument as T
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T> interceptContinuationIfNeeded(
|
||||
context: CoroutineContext,
|
||||
continuation: Continuation<T>
|
||||
) = context[ContinuationInterceptor]?.interceptContinuation(continuation) ?: continuation
|
||||
|
||||
|
||||
@SinceKotlin("1.2")
|
||||
@PublishedApi
|
||||
internal inline suspend fun getCoroutineContext(): CoroutineContext = getContinuation<Any?>().context
|
||||
|
||||
// TODO: remove `JS` suffix oncec `NameGenerator` is implemented
|
||||
@PublishedApi
|
||||
internal inline suspend fun <T> suspendCoroutineUninterceptedOrReturnJS(block: (Continuation<T>) -> Any?): T =
|
||||
returnIfSuspended<T>(block(getContinuation<T>()))
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
import JsError
|
||||
|
||||
internal fun unreachableDeclarationLog() {
|
||||
console.asDynamic().trace("Unreachable declaration")
|
||||
}
|
||||
|
||||
internal fun unreachableDeclarationException() {
|
||||
throw JsError("Unreachable declaration")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
@JsPolyfill("""
|
||||
(function() {
|
||||
if (typeof globalThis === 'object') return;
|
||||
Object.defineProperty(Object.prototype, '__magic__', {
|
||||
get: function() {
|
||||
return this;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
__magic__.globalThis = __magic__;
|
||||
delete Object.prototype.__magic__;
|
||||
}());
|
||||
""")
|
||||
internal external val globalThis: dynamic
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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
|
||||
|
||||
@PublishedApi
|
||||
internal fun throwUninitializedPropertyAccessException(name: String): Nothing =
|
||||
throw UninitializedPropertyAccessException("lateinit property $name has not been initialized")
|
||||
|
||||
@PublishedApi
|
||||
internal fun throwKotlinNothingValueException(): Nothing =
|
||||
throw KotlinNothingValueException()
|
||||
|
||||
internal fun noWhenBranchMatchedException(): Nothing = throw NoWhenBranchMatchedException()
|
||||
|
||||
internal fun THROW_ISE(): Nothing {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
internal fun THROW_CCE(): Nothing {
|
||||
throw ClassCastException()
|
||||
}
|
||||
|
||||
internal fun THROW_NPE(): Nothing {
|
||||
throw NullPointerException()
|
||||
}
|
||||
|
||||
internal fun THROW_IAE(msg: String): Nothing {
|
||||
throw IllegalArgumentException(msg)
|
||||
}
|
||||
|
||||
internal fun <T:Any> ensureNotNull(v: T?): T = if (v == null) THROW_NPE() else v
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NON_MEMBER_FUNCTION_NO_BODY", "UNUSED_PARAMETER", "unused")
|
||||
|
||||
package kotlin.js
|
||||
|
||||
@RequiresOptIn(message = "Here be dragons! This is a compiler intrinsic, proceed with care!")
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
internal annotation class JsIntrinsic
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsEqeq(a: Any?, b: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsNotEq(a: Any?, b: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsUndefined(): Nothing?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsEqeqeq(a: Any?, b: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsNotEqeq(a: Any?, b: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsGt(a: Any?, b: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsGtEq(a: Any?, b: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsLt(a: Any?, b: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsLtEq(a: Any?, b: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsNot(a: Any?): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsUnaryPlus(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsUnaryMinus(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsPrefixInc(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsPostfixInc(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsPrefixDec(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsPostfixDec(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsPlus(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsMinus(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsMult(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsDiv(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsMod(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsPlusAssign(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsMinusAssign(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsMultAssign(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsDivAssign(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsModAssign(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsAnd(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsOr(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsBitAnd(a: Any?, b: Any?): Int
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsBitOr(a: Any?, b: Any?): Int
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsBitXor(a: Any?, b: Any?): Int
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsBitNot(a: Any?): Int
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsBitShiftR(a: Any?, b: Any?): Int
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsBitShiftRU(a: Any?, b: Any?): Int
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsBitShiftL(a: Any?, b: Any?): Int
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsInstanceOfIntrinsic(a: Any?, b: Any?): Boolean
|
||||
|
||||
// @JsIntrinsic
|
||||
// To prevent people to insert @OptIn every time
|
||||
public external fun jsTypeOf(a: Any?): String
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsNewTarget(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun emptyObject(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun openInitializerBox(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsArrayLength(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsArrayGet(a: Any?, b: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsArraySet(a: Any?, b: Any?, c: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun arrayLiteral(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun int8Array(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun int16Array(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun int32Array(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun float32Array(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun float64Array(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun int8ArrayOf(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun int16ArrayOf(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun int32ArrayOf(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun float32ArrayOf(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun float64ArrayOf(a: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun <T> sharedBoxCreate(v: T?): dynamic
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun <T> sharedBoxRead(box: dynamic): T?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun <T> sharedBoxWrite(box: dynamic, nv: T?)
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun <T> DefaultType(): T
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsBind(receiver: Any?, target: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsCall(receiver: Any?, target: Any?, vararg args: Any?): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun <A> slice(a: A): A
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun <T> jsArrayLike2Array(arrayLike: Any?): Array<T>
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun <T> jsSliceArrayLikeFromIndex(arrayLike: Any?, start: Int): Array<T>
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun <T> jsSliceArrayLikeFromIndexToIndex(arrayLike: Any?, start: Int, end: Int): Array<T>
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun unreachable(): Nothing
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsArguments(): Any?
|
||||
|
||||
@JsIntrinsic
|
||||
@Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE")
|
||||
internal fun <reified T : Any> jsNewAnonymousClass(superClass: JsClass<T>): JsClass<T>
|
||||
|
||||
@JsIntrinsic
|
||||
@Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE") // TODO: mark `inline` and skip in inliner
|
||||
internal fun <reified T : Any> jsClassIntrinsic(): JsClass<T>
|
||||
|
||||
// Returns true if the specified property is in the specified object or its prototype chain.
|
||||
@JsIntrinsic
|
||||
internal fun jsInIntrinsic(lhs: Any?, rhs: Any): Boolean
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsDelete(e: Any?)
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsContextfulRef(context: dynamic, fn: dynamic): dynamic
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsIsEs6(): Boolean
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
|
||||
// File is a copy of stdlib/js/src/kotlin/kotlin.kt
|
||||
// TODO: Compile arrayPlusCollection
|
||||
// TODO: implement a copy of jsIsType for both JS backends
|
||||
|
||||
@file:Suppress("UNUSED_PARAMETER", "NOTHING_TO_INLINE")
|
||||
|
||||
package kotlin
|
||||
|
||||
/**
|
||||
* Returns an empty array of the specified type [T].
|
||||
*/
|
||||
public inline fun <T> emptyArray(): Array<T> = js("[]")
|
||||
|
||||
/**
|
||||
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer].
|
||||
*/
|
||||
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)
|
||||
|
||||
/**
|
||||
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer].
|
||||
*
|
||||
* The [mode] parameter is ignored. */
|
||||
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)
|
||||
|
||||
/**
|
||||
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer].
|
||||
*
|
||||
* The [lock] parameter is ignored.
|
||||
*/
|
||||
@Deprecated("Synchronization on Any? object is not supported.", ReplaceWith("lazy(initializer)"))
|
||||
@DeprecatedSinceKotlin(warningSince = "1.9")
|
||||
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)
|
||||
|
||||
|
||||
internal fun fillFrom(src: dynamic, dst: dynamic): dynamic {
|
||||
val srcLen: Int = src.length
|
||||
val dstLen: Int = dst.length
|
||||
var index: Int = 0
|
||||
val arr = dst.unsafeCast<Array<Any?>>()
|
||||
while (index < srcLen && index < dstLen) arr[index] = src[index++]
|
||||
return dst
|
||||
}
|
||||
|
||||
|
||||
internal fun arrayCopyResize(source: dynamic, newSize: Int, defaultValue: Any?): dynamic {
|
||||
val result = source.slice(0, newSize).unsafeCast<Array<Any?>>()
|
||||
copyArrayType(source, result)
|
||||
var index: Int = source.length
|
||||
if (newSize > index) {
|
||||
result.asDynamic().length = newSize
|
||||
while (index < newSize) result[index++] = defaultValue
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun <T> arrayPlusCollection(array: dynamic, collection: Collection<T>): dynamic {
|
||||
val result = array.slice().unsafeCast<Array<T>>()
|
||||
result.asDynamic().length = result.size + collection.size
|
||||
copyArrayType(array, result)
|
||||
var index: Int = array.length
|
||||
for (element in collection) result[index++] = element
|
||||
return result
|
||||
}
|
||||
|
||||
internal inline fun copyArrayType(from: dynamic, to: dynamic) {
|
||||
if (from.`$type$` !== undefined) {
|
||||
to.`$type$` = from.`$type$`
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T : Enum<T>> enumValuesIntrinsic(): Array<T> =
|
||||
throw IllegalStateException("Should be replaced by compiler")
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T : Enum<T>> enumValueOfIntrinsic(@Suppress("UNUSED_PARAMETER") name: String): T =
|
||||
throw IllegalStateException("Should be replaced by compiler")
|
||||
|
||||
// These were necessary for legacy-property-access functionality in IR
|
||||
// But this functionality is not required anymore, so these methods are redundant
|
||||
// But they can't be removed because if old version of compiler will compile against new version of stdlib
|
||||
// (when these methods are removed) compiler will fail with Internal error
|
||||
@Deprecated("This is an intrinsic for removed functionality", level = DeprecationLevel.HIDDEN)
|
||||
internal fun safePropertyGet(self: dynamic, getterName: String, propName: String): dynamic {
|
||||
val getter = self[getterName]
|
||||
return if (getter != null) getter.call(self) else self[propName]
|
||||
}
|
||||
|
||||
@Deprecated("This is an intrinsic for removed functionality", level = DeprecationLevel.HIDDEN)
|
||||
internal fun safePropertySet(self: dynamic, setterName: String, propName: String, value: dynamic) {
|
||||
val setter = self[setterName]
|
||||
if (setter != null) setter.call(self, value) else self[propName] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements annotated function in JavaScript.
|
||||
* [code] string must contain JS expression that evaluates to JS function with signature that matches annotated kotlin function
|
||||
*
|
||||
* For example, a function that adds two Doubles:
|
||||
*
|
||||
* @JsFun("(x, y) => x + y")
|
||||
* fun jsAdd(x: Double, y: Double): Double =
|
||||
* error("...")
|
||||
*
|
||||
* Code gets inserted as is without syntax verification.
|
||||
*/
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
|
||||
internal annotation class JsFun(val code: String)
|
||||
|
||||
/**
|
||||
* The annotation is needed for annotating class declarations and type alias which are used inside exported declarations, but
|
||||
* doesn't contain @JsExport annotation
|
||||
* This information is used for generating special tagged types inside d.ts files, for more strict usage of implicitly exported entities
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
internal annotation class JsImplicitExport
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.toStringImpl(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()
|
||||
// Using rem.asDynamic() to break dependency on "kotlin.text" package
|
||||
return div.toStringImpl(radix) + rem.asDynamic().toString(radix).unsafeCast<String>()
|
||||
} else {
|
||||
return "-${negate().toStringImpl(radix)}"
|
||||
}
|
||||
}
|
||||
|
||||
// Do several digits each time through the loop, so as to
|
||||
// minimize the calls to the very expensive emulated div.
|
||||
val digitsPerTime = when {
|
||||
radix == 2 -> 31
|
||||
radix <= 10 -> 9
|
||||
radix <= 21 -> 7
|
||||
radix <= 35 -> 6
|
||||
else -> 5
|
||||
}
|
||||
val radixToPower = fromNumber(JsMath.pow(radix.toDouble(), digitsPerTime.toDouble()))
|
||||
|
||||
var rem = this
|
||||
var result = ""
|
||||
while (true) {
|
||||
val remDiv = rem.div(radixToPower)
|
||||
val intval = rem.subtract(remDiv.multiply(radixToPower)).toInt()
|
||||
var digits = intval.asDynamic().toString(radix).unsafeCast<String>()
|
||||
|
||||
rem = remDiv
|
||||
if (rem.isZero()) {
|
||||
return digits + result
|
||||
} else {
|
||||
while (digits.length < digitsPerTime) {
|
||||
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 = 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 + 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 approx2 = JsMath.max(1.0, JsMath.floor(approxDouble))
|
||||
|
||||
// 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 = JsMath.ceil(JsMath.log(approx2) / JsMath.LN2)
|
||||
val delta = if (log2 <= 48) 1.0 else JsMath.pow(2.0, log2 - 48)
|
||||
|
||||
// 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(approx2)
|
||||
var approxRem = approxRes.multiply(other)
|
||||
while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
|
||||
approx2 -= delta
|
||||
approxRes = fromNumber(approx2)
|
||||
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 {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
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 {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
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 {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
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)
|
||||
|
||||
/**
|
||||
* Converts this [Double] value to [Long].
|
||||
* The fractional part, if any, is rounded down towards zero.
|
||||
* Returns zero if this `Double` value is `NaN`, [Long.MIN_VALUE] if it's less than `Long.MIN_VALUE`,
|
||||
* [Long.MAX_VALUE] if it's bigger than `Long.MAX_VALUE`.
|
||||
*/
|
||||
internal fun fromNumber(value: Double): Long {
|
||||
if (value.isNaN()) {
|
||||
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(
|
||||
jsBitwiseOr(value.rem(twoPwr32), 0),
|
||||
jsBitwiseOr(value / twoPwr32, 0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val TWO_PWR_16_DBL_ = (1 shl 16).toDouble()
|
||||
|
||||
private const val TWO_PWR_24_DBL_ = (1 shl 24).toDouble()
|
||||
|
||||
//private val TWO_PWR_32_DBL_ = TWO_PWR_16_DBL_ * TWO_PWR_16_DBL_
|
||||
private const val TWO_PWR_32_DBL_ = (1 shl 16).toDouble() * (1 shl 16).toDouble()
|
||||
|
||||
//private val TWO_PWR_64_DBL_ = TWO_PWR_32_DBL_ * TWO_PWR_32_DBL_
|
||||
private const val TWO_PWR_64_DBL_ = ((1 shl 16).toDouble() * (1 shl 16).toDouble()) * ((1 shl 16).toDouble() * (1 shl 16).toDouble())
|
||||
|
||||
//private val TWO_PWR_63_DBL_ = TWO_PWR_64_DBL_ / 2
|
||||
private const val TWO_PWR_63_DBL_ = (((1 shl 16).toDouble() * (1 shl 16).toDouble()) * ((1 shl 16).toDouble() * (1 shl 16).toDouble())) / 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)
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@file:JsQualifier("Math")
|
||||
package kotlin.js
|
||||
|
||||
@JsPolyfill("""
|
||||
if (typeof Math.imul === "undefined") {
|
||||
Math.imul = function imul(a, b) {
|
||||
return ((a & 0xffff0000) * (b & 0xffff) + (a & 0xffff) * (b | 0)) | 0;
|
||||
}
|
||||
}
|
||||
""")
|
||||
internal external fun imul(a_local: Int, b_local: Int): Int
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal fun setMetadataFor(
|
||||
ctor: Ctor,
|
||||
name: String?,
|
||||
metadataConstructor: (name: String?, defaultConstructor: dynamic, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?) -> Metadata,
|
||||
parent: Ctor?,
|
||||
interfaces: Array<dynamic>?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?
|
||||
) {
|
||||
if (parent != null) {
|
||||
js("""
|
||||
ctor.prototype = Object.create(parent.prototype)
|
||||
ctor.prototype.constructor = ctor;
|
||||
""")
|
||||
}
|
||||
|
||||
val metadata = metadataConstructor(name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity ?: js("[]"))
|
||||
ctor.`$metadata$` = metadata
|
||||
|
||||
if (interfaces != null) {
|
||||
val receiver = if (metadata.iid != null) ctor else ctor.prototype
|
||||
receiver.`$imask$` = implement(interfaces)
|
||||
}
|
||||
}
|
||||
|
||||
// There was a problem with per-module compilation (KT-55758) when the top-level state (iid) was reinitialized during stdlib module initialization
|
||||
// As a result we miss already incremented iid and had the same iids in two different modules
|
||||
// So, to keep the state consistent it was moved into the variable without initializer and function
|
||||
@Suppress("MUST_BE_INITIALIZED")
|
||||
private var iid: dynamic
|
||||
|
||||
private fun generateInterfaceId(): Int {
|
||||
if (iid === VOID) {
|
||||
iid = 0
|
||||
}
|
||||
iid = iid.unsafeCast<Int>() + 1
|
||||
return iid.unsafeCast<Int>()
|
||||
}
|
||||
|
||||
|
||||
internal fun interfaceMeta(
|
||||
name: String?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?
|
||||
): Metadata {
|
||||
return createMetadata("interface", name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, generateInterfaceId())
|
||||
}
|
||||
|
||||
internal fun objectMeta(
|
||||
name: String?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?
|
||||
): Metadata {
|
||||
return createMetadata("object", name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, null)
|
||||
}
|
||||
|
||||
internal fun classMeta(
|
||||
name: String?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?
|
||||
): Metadata {
|
||||
return createMetadata("class", name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, null)
|
||||
}
|
||||
|
||||
// Seems like we need to disable this check if variables are used inside js annotation
|
||||
@Suppress("UNUSED_PARAMETER", "UNUSED_VARIABLE")
|
||||
private fun createMetadata(
|
||||
kind: String,
|
||||
name: String?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?,
|
||||
iid: Int?
|
||||
): Metadata {
|
||||
val undef = VOID
|
||||
return js("""({
|
||||
kind: kind,
|
||||
simpleName: name,
|
||||
associatedObjectKey: associatedObjectKey,
|
||||
associatedObjects: associatedObjects,
|
||||
suspendArity: suspendArity,
|
||||
${'$'}kClass$: undef,
|
||||
defaultConstructor: defaultConstructor,
|
||||
iid: iid
|
||||
})""")
|
||||
}
|
||||
|
||||
internal external interface Metadata {
|
||||
val kind: String
|
||||
// This field gives fast access to the prototype of metadata owner (Object.getPrototypeOf())
|
||||
// Can be pre-initialized or lazy initialized and then should be immutable
|
||||
val simpleName: String?
|
||||
val associatedObjectKey: Number?
|
||||
val associatedObjects: dynamic
|
||||
val suspendArity: Array<Int>?
|
||||
val iid: Int?
|
||||
|
||||
var `$kClass$`: dynamic
|
||||
val defaultConstructor: dynamic
|
||||
|
||||
var errorInfo: Int? // Bits set for overridden properties: "message" => 0x1, "cause" => 0x2
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
/** Concat regular Array's and TypedArray's into an Array.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun <T> arrayConcat(vararg args: T): T {
|
||||
val len = args.size
|
||||
val typed = js("Array(len)").unsafeCast<Array<T>>()
|
||||
for (i in 0 .. (len - 1)) {
|
||||
val arr = args[i]
|
||||
if (arr !is Array<*>) {
|
||||
typed[i] = js("[]").slice.call(arr)
|
||||
} else {
|
||||
typed[i] = arr
|
||||
}
|
||||
}
|
||||
return js("[]").concat.apply(js("[]"), typed);
|
||||
}
|
||||
|
||||
/** Concat primitive arrays. Main use: prepare vararg arguments.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun <T> primitiveArrayConcat(vararg args: T): T {
|
||||
var size_local = 0
|
||||
for (i in 0 .. (args.size - 1)) {
|
||||
size_local += args[i].unsafeCast<Array<Any?>>().size
|
||||
}
|
||||
val a = args[0]
|
||||
val result = js("new a.constructor(size_local)").unsafeCast<Array<Any?>>()
|
||||
if (a.asDynamic().`$type$` != null) {
|
||||
withType(a.asDynamic().`$type$`, result)
|
||||
}
|
||||
|
||||
size_local = 0
|
||||
for (i in 0 .. (args.size - 1)) {
|
||||
val arr = args[i].unsafeCast<Array<Any?>>()
|
||||
for (j in 0 .. (arr.size - 1)) {
|
||||
result[size_local++] = arr[j]
|
||||
}
|
||||
}
|
||||
return result.unsafeCast<T>()
|
||||
}
|
||||
|
||||
internal fun <T> taggedArrayCopy(array: dynamic): T {
|
||||
val res = array.slice()
|
||||
res.`$type$` = array.`$type$`
|
||||
return res.unsafeCast<T>()
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
internal inline fun withType(type: String, array: dynamic): dynamic {
|
||||
array.`$type$` = type
|
||||
return array
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal fun numberToByte(a: dynamic): Byte = toByte(numberToInt(a))
|
||||
|
||||
internal fun numberToDouble(@Suppress("UNUSED_PARAMETER") a: dynamic): Double = js("+a").unsafeCast<Double>()
|
||||
|
||||
internal fun numberToInt(a: dynamic): Int = if (a is Long) a.toInt() else doubleToInt(a)
|
||||
|
||||
internal fun numberToShort(a: dynamic): Short = toShort(numberToInt(a))
|
||||
|
||||
// << and >> shifts are used to preserve sign of the number
|
||||
internal fun toByte(@Suppress("UNUSED_PARAMETER") a: dynamic): Byte = js("a << 24 >> 24").unsafeCast<Byte>()
|
||||
internal fun toShort(@Suppress("UNUSED_PARAMETER") a: dynamic): Short = js("a << 16 >> 16").unsafeCast<Short>()
|
||||
|
||||
internal fun numberToLong(a: dynamic): Long = if (a is Long) a else fromNumber(a)
|
||||
|
||||
internal fun toLong(a: dynamic): Long = fromInt(a)
|
||||
|
||||
internal fun doubleToInt(a: Double): Int = when {
|
||||
a > 2147483647 -> 2147483647
|
||||
a < -2147483648 -> -2147483648
|
||||
else -> jsBitwiseOr(a, 0)
|
||||
}
|
||||
|
||||
internal fun numberToChar(a: dynamic) = Char(numberToInt(a).toUShort())
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
// Creates IntRange for {Byte, Short, Int}.rangeTo(x: {Byte, Short, Int})
|
||||
internal fun numberRangeToNumber(start: dynamic, endInclusive: dynamic) =
|
||||
IntRange(start, endInclusive)
|
||||
|
||||
// Create LongRange for {Byte, Short, Int}.rangeTo(x: Long)
|
||||
// Long.rangeTo(x: *) should be implemented in Long class
|
||||
internal fun numberRangeToLong(start: dynamic, endInclusive: dynamic) =
|
||||
LongRange(numberToLong(start), endInclusive)
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
internal fun getPropertyCallableRef(
|
||||
name: String,
|
||||
paramCount: Int,
|
||||
superType: dynamic,
|
||||
getter: dynamic,
|
||||
setter: dynamic
|
||||
): KProperty<*> {
|
||||
getter.get = getter
|
||||
getter.set = setter
|
||||
getter.callableName = name
|
||||
return getPropertyRefClass(
|
||||
getter,
|
||||
getKPropMetadata(paramCount, setter),
|
||||
getInterfaceMaskFor(getter, superType)
|
||||
).unsafeCast<KProperty<*>>()
|
||||
}
|
||||
|
||||
internal fun getLocalDelegateReference(name: String, superType: dynamic, mutable: Boolean, lambda: dynamic): KProperty<*> {
|
||||
return getPropertyCallableRef(name, 0, superType, lambda, if (mutable) lambda else null)
|
||||
}
|
||||
|
||||
private fun getPropertyRefClass(obj: Ctor, metadata: Metadata, imask: BitMask): dynamic {
|
||||
obj.`$metadata$` = metadata
|
||||
obj.constructor = obj
|
||||
obj.`$imask$` = imask
|
||||
return obj;
|
||||
}
|
||||
|
||||
private fun getInterfaceMaskFor(obj: Ctor, superType: dynamic): BitMask =
|
||||
obj.`$imask$` ?: implement(arrayOf(superType))
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun getKPropMetadata(paramCount: Int, setter: Any?): dynamic {
|
||||
return propertyRefClassMetadataCache[paramCount][if (setter == null) 0 else 1]
|
||||
}
|
||||
|
||||
private fun metadataObject(): Metadata {
|
||||
return classMeta(VOID, VOID, VOID, VOID, VOID)
|
||||
}
|
||||
|
||||
private val propertyRefClassMetadataCache: Array<Array<dynamic>> = arrayOf<Array<dynamic>>(
|
||||
// immutable , mutable
|
||||
arrayOf<dynamic>(metadataObject(), metadataObject()), // 0
|
||||
arrayOf<dynamic>(metadataObject(), metadataObject()), // 1
|
||||
arrayOf<dynamic>(metadataObject(), metadataObject()) // 2
|
||||
)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
// Inlined intrinsics for backward compatibility with already implemented functions in stdlib for old JS backend
|
||||
@OptIn(JsIntrinsic::class)
|
||||
@kotlin.internal.InlineOnly
|
||||
internal inline fun jsDeleteProperty(obj: dynamic, property: Any) = jsDelete(obj[property])
|
||||
|
||||
@OptIn(JsIntrinsic::class)
|
||||
@kotlin.internal.InlineOnly
|
||||
internal inline fun jsBitwiseOr(lhs: Any?, rhs: Any?): Int = jsBitOr(lhs, rhs)
|
||||
|
||||
@OptIn(JsIntrinsic::class)
|
||||
@kotlin.internal.InlineOnly
|
||||
internal inline fun jsBitwiseAnd(lhs: Any?, rhs: Any?): Int = jsBitAnd(lhs, rhs)
|
||||
|
||||
@OptIn(JsIntrinsic::class)
|
||||
@kotlin.internal.InlineOnly
|
||||
internal inline fun jsInstanceOf(obj: Any?, jsClass: Any?): Boolean = jsInstanceOfIntrinsic(obj, jsClass)
|
||||
|
||||
@OptIn(JsIntrinsic::class)
|
||||
@kotlin.internal.InlineOnly
|
||||
internal inline fun jsIn(lhs: Any?, rhs: Any): Boolean = jsInIntrinsic(lhs, rhs)
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal external interface Ctor {
|
||||
var `$imask$`: BitMask?
|
||||
var `$metadata$`: Metadata
|
||||
var constructor: Ctor?
|
||||
val prototype: dynamic
|
||||
}
|
||||
|
||||
private fun hasProp(proto: dynamic, propName: String): Boolean = proto.hasOwnProperty(propName)
|
||||
|
||||
internal fun calculateErrorInfo(proto: dynamic): Int {
|
||||
val metadata: Metadata? = proto.constructor?.`$metadata$`
|
||||
|
||||
metadata?.errorInfo?.let { return it } // cached
|
||||
|
||||
var result = 0
|
||||
if (hasProp(proto, "message")) result = result or 0x1
|
||||
if (hasProp(proto, "cause")) result = result or 0x2
|
||||
|
||||
if (result != 0x3) { //
|
||||
val parentProto = getPrototypeOf(proto)
|
||||
if (parentProto != js("Error").prototype) {
|
||||
result = result or calculateErrorInfo(parentProto)
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata != null) {
|
||||
metadata.errorInfo = result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getPrototypeOf(obj: dynamic) = JsObject.getPrototypeOf(obj)
|
||||
|
||||
private fun isInterfaceImpl(obj: dynamic, iface: Int): Boolean {
|
||||
val mask: BitMask = obj.`$imask$`.unsafeCast<BitMask?>() ?: return false
|
||||
return mask.isBitSet(iface)
|
||||
}
|
||||
|
||||
internal fun isInterface(obj: dynamic, iface: dynamic): Boolean {
|
||||
return isInterfaceImpl(obj, iface.`$metadata$`.iid)
|
||||
}
|
||||
|
||||
internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean {
|
||||
val objTypeOf = jsTypeOf(obj)
|
||||
|
||||
if (objTypeOf == "function") {
|
||||
@Suppress("DEPRECATED_IDENTITY_EQUALS")
|
||||
return obj.`$arity`.unsafeCast<Int>() === arity
|
||||
}
|
||||
|
||||
val suspendArity = obj?.constructor.unsafeCast<Ctor?>()?.`$metadata$`?.suspendArity ?: return false
|
||||
|
||||
@Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS")
|
||||
var result = false
|
||||
for (item in suspendArity) {
|
||||
if (arity == item) {
|
||||
result = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isJsArray(obj: Any): Boolean {
|
||||
return js("Array").isArray(obj).unsafeCast<Boolean>()
|
||||
}
|
||||
|
||||
internal fun isArray(obj: Any): Boolean {
|
||||
return isJsArray(obj) && !(obj.asDynamic().`$type$`)
|
||||
}
|
||||
|
||||
// TODO: Remove after the next bootstrap
|
||||
internal fun isObject(o: dynamic): Boolean = o != null
|
||||
|
||||
internal fun isArrayish(o: dynamic) = isJsArray(o) || arrayBufferIsView(o)
|
||||
|
||||
internal fun isChar(@Suppress("UNUSED_PARAMETER") c: Any): Boolean {
|
||||
error("isChar is not implemented")
|
||||
}
|
||||
|
||||
// TODO: Distinguish Boolean/Byte and Short/Char
|
||||
internal fun isBooleanArray(a: dynamic): Boolean = isJsArray(a) && a.`$type$` === "BooleanArray"
|
||||
internal fun isByteArray(a: dynamic): Boolean = jsInstanceOf(a, js("Int8Array"))
|
||||
internal fun isShortArray(a: dynamic): Boolean = jsInstanceOf(a, js("Int16Array"))
|
||||
internal fun isCharArray(a: dynamic): Boolean = jsInstanceOf(a, js("Uint16Array")) && a.`$type$` === "CharArray"
|
||||
internal fun isIntArray(a: dynamic): Boolean = jsInstanceOf(a, js("Int32Array"))
|
||||
internal fun isFloatArray(a: dynamic): Boolean = jsInstanceOf(a, js("Float32Array"))
|
||||
internal fun isDoubleArray(a: dynamic): Boolean = jsInstanceOf(a, js("Float64Array"))
|
||||
internal fun isLongArray(a: dynamic): Boolean = isJsArray(a) && a.`$type$` === "LongArray"
|
||||
|
||||
internal fun jsGetPrototypeOf(jsClass: dynamic) = js("Object").getPrototypeOf(jsClass)
|
||||
|
||||
internal fun jsIsType(obj: dynamic, jsClass: dynamic): Boolean {
|
||||
if (jsClass === js("Object")) {
|
||||
return obj != null
|
||||
}
|
||||
|
||||
val objType = jsTypeOf(obj)
|
||||
val jsClassType = jsTypeOf(jsClass)
|
||||
|
||||
if (obj == null || jsClass == null || (objType != "object" && objType != "function")) {
|
||||
return false
|
||||
}
|
||||
|
||||
// In WebKit (JavaScriptCore) for some interfaces from DOM typeof returns "object", nevertheless they can be used in RHS of instanceof
|
||||
val constructor = if (jsClassType == "object") jsGetPrototypeOf(jsClass) else jsClass
|
||||
val klassMetadata = constructor.`$metadata$`
|
||||
|
||||
if (klassMetadata?.kind === "interface") {
|
||||
val iid = klassMetadata.iid.unsafeCast<Int?>() ?: return false
|
||||
return isInterfaceImpl(obj, iid)
|
||||
}
|
||||
|
||||
return jsInstanceOf(obj, constructor)
|
||||
}
|
||||
|
||||
internal fun isNumber(a: dynamic) = jsTypeOf(a) == "number" || a is Long
|
||||
|
||||
@OptIn(JsIntrinsic::class)
|
||||
internal fun isComparable(value: dynamic): Boolean {
|
||||
val type = jsTypeOf(value)
|
||||
|
||||
return type == "string" ||
|
||||
type == "boolean" ||
|
||||
isNumber(value) ||
|
||||
isInterface(value, jsClassIntrinsic<Comparable<*>>())
|
||||
}
|
||||
|
||||
@OptIn(JsIntrinsic::class)
|
||||
internal fun isCharSequence(value: dynamic): Boolean =
|
||||
jsTypeOf(value) == "string" || isInterface(value, jsClassIntrinsic<CharSequence>())
|
||||
|
||||
|
||||
@OptIn(JsIntrinsic::class)
|
||||
internal fun isExternalObject(value: dynamic, ktExternalObject: dynamic) =
|
||||
jsEqeqeq(value, ktExternalObject) || (jsTypeOf(ktExternalObject) == "function" && jsInstanceOf(value, ktExternalObject))
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal class IrLinkageError(message: String?) : Error(message)
|
||||
|
||||
internal fun throwLinkageError(message: String?): Nothing {
|
||||
throw IrLinkageError(message)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.js
|
||||
|
||||
internal val VOID: Nothing? = js("void 0")
|
||||
Reference in New Issue
Block a user