From 54160f5a44c1512f8e6c6f32bab31898c5558fbe Mon Sep 17 00:00:00 2001 From: soywiz Date: Thu, 4 Feb 2021 09:07:27 +0100 Subject: [PATCH] Kotlin 1.4.30 + min/max JS IR performance circumvention + Deprecate KDynamic in favour of new .dyn --- gradle.properties | 12 +- .../korio/dynamic/DynamicInternalAndroid.kt | 94 ----- .../com/soywiz/korio/file/std/VfsAndroid.kt | 7 +- .../com/soywiz/korio/async/ChannelExt.kt | 4 +- .../korio/compression/deflate/Deflate.kt | 4 +- .../soywiz/korio/compression/lzma/SevenZip.kt | 18 +- .../korio/compression/util/BitReader.kt | 4 +- .../com/soywiz/korio/compression/zip/Zip.kt | 6 +- .../kotlin/com/soywiz/korio/dynamic/Dyn.kt | 353 ++++++++++++++++++ .../kotlin/com/soywiz/korio/dynamic/DynApi.kt | 24 ++ .../com/soywiz/korio/dynamic/KDynamic.kt | 167 ++------- .../kotlin/com/soywiz/korio/file/PathInfo.kt | 3 +- .../kotlin/com/soywiz/korio/file/Vfs.kt | 4 +- .../korio/file/std/MapLikeStorageVfs.kt | 8 +- .../com/soywiz/korio/internal/internal.kt | 13 + .../kotlin/com/soywiz/korio/lang/Charset.kt | 3 +- .../kotlin/com/soywiz/korio/lang/StringExt.kt | 6 +- .../com/soywiz/korio/net/http/HttpPortable.kt | 4 +- .../com/soywiz/korio/stream/AsyncStream.kt | 14 +- .../com/soywiz/korio/stream/SyncStream.kt | 8 +- .../kotlin/com/soywiz/korio/util/StrReader.kt | 10 +- .../com/soywiz/korio/dynamic/DynCommonTest.kt | 45 +++ .../com/soywiz/korio/KorioNativeJsNodeJs.kt | 2 +- .../com/soywiz/korio/dynamic/DynApiJs.kt | 17 + .../soywiz/korio/dynamic/DynamicInternal.kt | 17 - .../com/soywiz/korio/dynamic/DynApiJvm.kt | 101 +++++ .../soywiz/korio/dynamic/DynamicInternal.kt | 94 ----- .../com/soywiz/korio/file/std/LocalVfsJvm.kt | 5 +- .../soywiz/korio/net/http/HttpClientJvm.kt | 5 +- .../korio/serialization/binary/Struct.kt | 4 +- .../com/soywiz/korio/dynamic/DynTest.kt | 55 +++ .../com/soywiz/korio/dynamic/DynApiNative.kt | 7 + .../soywiz/korio/dynamic/KDynamicNative.kt | 10 - .../soywiz/korio/file/std/LocalVfsNative.kt | 5 +- 34 files changed, 734 insertions(+), 399 deletions(-) delete mode 100644 korio/src/androidMain/kotlin/com/soywiz/korio/dynamic/DynamicInternalAndroid.kt create mode 100644 korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/Dyn.kt create mode 100644 korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/DynApi.kt create mode 100644 korio/src/commonMain/kotlin/com/soywiz/korio/internal/internal.kt create mode 100644 korio/src/commonTest/kotlin/com/soywiz/korio/dynamic/DynCommonTest.kt create mode 100644 korio/src/jsMain/kotlin/com/soywiz/korio/dynamic/DynApiJs.kt delete mode 100644 korio/src/jsMain/kotlin/com/soywiz/korio/dynamic/DynamicInternal.kt create mode 100644 korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/dynamic/DynApiJvm.kt delete mode 100644 korio/src/jvmMain/kotlin/com/soywiz/korio/dynamic/DynamicInternal.kt create mode 100644 korio/src/jvmTest/kotlin/com/soywiz/korio/dynamic/DynTest.kt create mode 100644 korio/src/nativeCommonMain/kotlin/com/soywiz/korio/dynamic/DynApiNative.kt delete mode 100644 korio/src/nativeCommonMain/kotlin/com/soywiz/korio/dynamic/KDynamicNative.kt diff --git a/gradle.properties b/gradle.properties index 65134eee..293dc331 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ # sytleguide kotlin.code.style=official -# Kotlin 1.4.30-RC: https://github.com/korlibs/easy-kotlin-mpp-gradle-plugin -easyPluginVersion=0.12.5 +# Kotlin 1.4.30: https://github.com/korlibs/easy-kotlin-mpp-gradle-plugin +easyPluginVersion=0.13.0 # version group=com.soywiz.korlibs.korio @@ -12,10 +12,10 @@ version=2.0.0-SNAPSHOT coroutinesVersion=1.4.2 # korlibs -klockVersion=2.0.5 -kdsVersion=2.0.5 -kmemVersion=2.0.5 -kryptoVersion=2.0.5 +klockVersion=2.0.6 +kdsVersion=2.0.6 +kmemVersion=2.0.6 +kryptoVersion=2.0.6 # bintray location project.bintray.org=korlibs diff --git a/korio/src/androidMain/kotlin/com/soywiz/korio/dynamic/DynamicInternalAndroid.kt b/korio/src/androidMain/kotlin/com/soywiz/korio/dynamic/DynamicInternalAndroid.kt deleted file mode 100644 index 9dbdb546..00000000 --- a/korio/src/androidMain/kotlin/com/soywiz/korio/dynamic/DynamicInternalAndroid.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.soywiz.korio.dynamic - -import com.soywiz.korio.util.* -import java.lang.reflect.* - -internal actual object DynamicInternal { - class JavaPackage(val name: String) - - actual val global: Any? = JavaPackage("") - - private fun tryGetField(clazz: Class<*>, name: String): Field? { - val field = runCatching { clazz.getDeclaredField(name) }.getOrNull() - return when { - field != null -> field.apply { isAccessible = true } - clazz.superclass != null -> return tryGetField(clazz.superclass, name) - else -> null - } - } - - private fun tryGetMethod(clazz: Class<*>, name: String, args: Array?): Method? { - val methods = clazz.allDeclaredMethods.filter { it.name == name } - val method = when (methods.size) { - 0 -> null - 1 -> methods.first() - else -> { - if (args != null) { - val methodsSameArity = methods.filter { it.parameterTypes.size == args.size } - val argTypes = args.map { it!!::class.javaObjectType } - methodsSameArity.firstOrNull { - it.parameterTypes.toList().zip(argTypes).all { - it.first.kotlin.javaObjectType.isAssignableFrom(it.second) - } - } - } else { - methods.first() - } - } - } - return when { - method != null -> method.apply { isAccessible = true } - clazz.superclass != null -> return tryGetMethod(clazz.superclass, name, args) - else -> null - } - } - - actual fun get(instance: Any?, key: String): Any? { - if (instance == null) return null - - val static = instance is Class<*> - val clazz: Class<*> = if (static) instance as Class<*> else instance.javaClass - - if (instance is JavaPackage) { - val path = "${instance.name}.$key".trim('.') - return try { - java.lang.Class.forName(path) - } catch (e: ClassNotFoundException) { - JavaPackage(path) - } - } - val method = tryGetMethod(clazz, "get${key.capitalize()}", null) - if (method != null) { - return method.invoke(if (static) null else instance) - } - val field = tryGetField(clazz, key) - if (field != null) { - return field.get(if (static) null else instance) - } - return null - } - - actual fun set(instance: Any?, key: String, value: Any?) { - if (instance == null) return - - val static = instance is Class<*> - val clazz: Class<*> = if (static) instance as Class<*> else instance.javaClass - - val method = tryGetMethod(clazz, "set${key.capitalize()}", null) - if (method != null) { - method.invoke(if (static) null else instance, value) - return - } - val field = tryGetField(clazz, key) - if (field != null) { - field.set(if (static) null else instance, value) - return - } - } - - actual fun invoke(instance: Any?, key: String, args: Array): Any? { - if (instance == null) return null - val method = tryGetMethod(if (instance is Class<*>) instance else instance.javaClass, key, args) - return method?.invoke(if (instance is Class<*>) null else instance, *args) - } -} diff --git a/korio/src/androidMain/kotlin/com/soywiz/korio/file/std/VfsAndroid.kt b/korio/src/androidMain/kotlin/com/soywiz/korio/file/std/VfsAndroid.kt index 9f15681f..545c8016 100644 --- a/korio/src/androidMain/kotlin/com/soywiz/korio/file/std/VfsAndroid.kt +++ b/korio/src/androidMain/kotlin/com/soywiz/korio/file/std/VfsAndroid.kt @@ -4,6 +4,7 @@ import android.content.Context import com.soywiz.korio.android.androidContext import com.soywiz.klock.* import com.soywiz.korio.file.* +import com.soywiz.korio.internal.* import com.soywiz.korio.lang.Closeable import com.soywiz.korio.stream.* import com.soywiz.korio.util.* @@ -93,7 +94,7 @@ class AndroidResourcesVfs(var context: Context?) : Vfs() { val temp = ByteArray(16 * 1024) var available = (range.endExclusiveClamped - range.start) while (available >= 0) { - val read = fs.read(temp, 0, Math.min(temp.size.toLong(), available).toInt()) + val read = fs.read(temp, 0, min2(temp.size.toLong(), available).toInt()) if (read <= 0) break out.write(temp, 0, read) available -= read @@ -181,8 +182,8 @@ private class LocalVfsJvm : LocalVfsV2() { override suspend fun readRange(path: String, range: LongRange): ByteArray = executeIo { RandomAccessFile(resolveFile(path), "r").use { raf -> val fileLength = raf.length() - val start = kotlin.math.min(range.start, fileLength) - val end = kotlin.math.min(range.endInclusive, fileLength - 1) + 1 + val start = min2(range.start, fileLength) + val end = min2(range.endInclusive, fileLength - 1) + 1 val totalRead = (end - start).toInt() val out = ByteArray(totalRead) raf.seek(start) diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/async/ChannelExt.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/async/ChannelExt.kt index 34f2f288..63bcb981 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/async/ChannelExt.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/async/ChannelExt.kt @@ -1,6 +1,8 @@ package com.soywiz.korio.async import com.soywiz.kmem.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.min2 import com.soywiz.korio.stream.* import kotlinx.coroutines.* import kotlinx.coroutines.channels.* @@ -47,7 +49,7 @@ fun ReceiveChannel.toAsyncInputStream(): AsyncInputStream { currentPos = 0 } - val toRead = min(currentAvailable, len) + val toRead = min2(currentAvailable, len) arraycopy(currentData, currentPos, buffer, offset, toRead) currentPos += toRead return toRead diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/compression/deflate/Deflate.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/compression/deflate/Deflate.kt index 132431d6..efa5eb62 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/compression/deflate/Deflate.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/compression/deflate/Deflate.kt @@ -4,6 +4,8 @@ import com.soywiz.kmem.* import com.soywiz.korio.compression.* import com.soywiz.korio.compression.util.* import com.soywiz.korio.experimental.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.min2 import com.soywiz.korio.stream.* import kotlin.math.* @@ -20,7 +22,7 @@ open class DeflatePortable(val windowBits: Int) : CompressionMethod { ) { while (i.hasAvailable()) { val available = i.getAvailable() - val chunkSize = min(available, 0xFFFFL).toInt() + val chunkSize = min2(available, 0xFFFFL).toInt() o.write8(if (chunkSize >= available) 1 else 0) o.write16LE(chunkSize) o.write16LE(chunkSize.inv()) diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/compression/lzma/SevenZip.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/compression/lzma/SevenZip.kt index 27ba412e..b8771304 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/compression/lzma/SevenZip.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/compression/lzma/SevenZip.kt @@ -3,6 +3,8 @@ package com.soywiz.korio.compression.lzma import com.soywiz.korio.experimental.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.max2 import com.soywiz.korio.stream.* import com.soywiz.korio.util.checksum.* import kotlin.math.* @@ -533,8 +535,8 @@ object SevenZip { if (dictionarySize < 0) return false if (m_DictionarySize != dictionarySize) { m_DictionarySize = dictionarySize - m_DictionarySizeCheck = max(m_DictionarySize, 1) - m_OutWindow.create(max(m_DictionarySizeCheck, 1 shl 12)) + m_DictionarySizeCheck = max2(m_DictionarySize, 1) + m_OutWindow.create(max2(m_DictionarySizeCheck, 1 shl 12)) } return true } @@ -1414,7 +1416,7 @@ object SevenZip { } var numAvailableBytesFull = _matchFinder!!.getNumAvailableBytes() + 1 - numAvailableBytesFull = min(kNumOpts - 1 - cur, numAvailableBytesFull) + numAvailableBytesFull = min2(kNumOpts - 1 - cur, numAvailableBytesFull) numAvailableBytes = numAvailableBytesFull if (numAvailableBytes < 2) @@ -1423,7 +1425,7 @@ object SevenZip { numAvailableBytes = _numFastBytes if (!nextIsChar && matchByte != currentByte) { // try Literal + rep0 - val t = min(numAvailableBytesFull - 1, _numFastBytes) + val t = min2(numAvailableBytesFull - 1, _numFastBytes) val lenTest2 = _matchFinder!!.getMatchLen(0, reps[0], t) if (lenTest2 >= 2) { val state2 = LzmaBase.stateUpdateChar(state) @@ -1483,7 +1485,7 @@ object SevenZip { // if (_maxMode) if (lenTest < numAvailableBytesFull) { - val t = min(numAvailableBytesFull - 1 - lenTest, _numFastBytes) + val t = min2(numAvailableBytesFull - 1 - lenTest, _numFastBytes) val lenTest2 = _matchFinder!!.getMatchLen(lenTest, reps[repIndex], t) if (lenTest2 >= 2) { var state2 = @@ -1572,7 +1574,7 @@ object SevenZip { if (lenTest == _matchDistances[offs]) { if (lenTest < numAvailableBytesFull) { - val t = min(numAvailableBytesFull - 1 - lenTest, _numFastBytes) + val t = min2(numAvailableBytesFull - 1 - lenTest, _numFastBytes) val lenTest2 = _matchFinder!!.getMatchLen(lenTest, curBack, t) if (lenTest2 >= 2) { var state2 = @@ -2202,7 +2204,7 @@ object SevenZip { _cyclicBufferPos - delta + _cyclicBufferSize) shl 1 val pby1 = _bufferOffset + curMatch - var len = min(len0, len1) + var len = min2(len0, len1) if (_bufferBase!![pby1 + len] == _bufferBase!![cur + len]) { while (++len != lenLimit) if (_bufferBase!![pby1 + len] != _bufferBase!![cur + len]) @@ -2291,7 +2293,7 @@ object SevenZip { _cyclicBufferPos - delta + _cyclicBufferSize) shl 1 val pby1 = _bufferOffset + curMatch - var len = min(len0, len1) + var len = min2(len0, len1) if (_bufferBase!![pby1 + len] == _bufferBase!![cur + len]) { while (++len != lenLimit) if (_bufferBase!![pby1 + len] != _bufferBase!![cur + len]) diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/compression/util/BitReader.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/compression/util/BitReader.kt index 3da6e969..40046458 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/compression/util/BitReader.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/compression/util/BitReader.kt @@ -3,6 +3,8 @@ package com.soywiz.korio.compression.util import com.soywiz.kds.* import com.soywiz.kmem.* import com.soywiz.korio.experimental.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.min2 import com.soywiz.korio.lang.* import com.soywiz.korio.stream.* import kotlin.math.* @@ -38,7 +40,7 @@ open class BitReader(val s: AsyncInputStreamWithLength) : AsyncInputStreamWithLe private val tempBA = ByteArray(BIG_CHUNK_SIZE) suspend fun prepareBytesUpTo(expectedBytes: Int): BitReader { while (sbuffers.availableRead < expectedBytes) { - val read = s.read(tempBA, 0, min(tempBA.size, expectedBytes)) + val read = s.read(tempBA, 0, min2(tempBA.size, expectedBytes)) if (read <= 0) break // No more data sbuffers.write(tempBA, 0, read) } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/compression/zip/Zip.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/compression/zip/Zip.kt index 626bfcd8..09574e9e 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/compression/zip/Zip.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/compression/zip/Zip.kt @@ -5,7 +5,9 @@ import com.soywiz.kmem.extract import com.soywiz.kmem.toIntClamp import com.soywiz.kmem.unsigned import com.soywiz.korio.file.* +import com.soywiz.korio.internal.* import com.soywiz.korio.internal.indexOf +import com.soywiz.korio.internal.max2 import com.soywiz.korio.stream.* import com.soywiz.krypto.encoding.* import kotlin.math.max @@ -37,9 +39,9 @@ class ZipFile private constructor( val fileLength = s.getLength() for (chunkSize in listOf(0x16, 0x100, 0x1000, 0x10000)) { - val pos = max(0L, fileLength - chunkSize) + val pos = max2(0L, fileLength - chunkSize) s.setPosition(pos) - val bytesLen = max(chunkSize, s.getAvailable().toIntClamp()) + val bytesLen = max2(chunkSize, s.getAvailable().toIntClamp()) val ebytes = s.readBytesExact(bytesLen) endBytes = ebytes pk_endIndex = endBytes.indexOf(PK_END) diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/Dyn.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/Dyn.kt new file mode 100644 index 00000000..2a4287b1 --- /dev/null +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/Dyn.kt @@ -0,0 +1,353 @@ +package com.soywiz.korio.dynamic + +import com.soywiz.kds.* +import com.soywiz.korio.util.* +import kotlin.math.* + +val Any?.dyn: Dyn get() = Dyn(this) + +@Suppress("DEPRECATION") +inline class Dyn(val value: Any?) : Comparable { + val dyn get() = this + val isNull get() = value == null + val isNotNull get() = value != null + + @Suppress("UNCHECKED_CAST") + fun toComparable(): Comparable = when (value) { + null -> 0 as Comparable + is Comparable<*> -> value as Comparable + else -> value.toString() as Comparable + } + + //fun unop(op: String): Dyn = unop(this, op) + //fun binop(op: String, r: Dyn): Dyn = binop(this, r, op) + + operator fun unaryMinus(): Dyn = (-toDouble()).dyn + operator fun unaryPlus(): Dyn = this + fun inv(): Dyn = toInt().inv().dyn + fun not(): Dyn = (!toBool()).dyn + + operator fun plus(r: Dyn): Dyn { + val l = this + val out: Any? = when (l.value) { + is String -> l.toString() + r.toString() + is Iterable<*> -> l.toIterableAny() + r.toIterableAny() + else -> l.toDouble() + r.toDouble() + } + return out.dyn + } + operator fun minus(r: Dyn): Dyn = (this.toDouble() - r.toDouble()).dyn + operator fun times(r: Dyn): Dyn = (this.toDouble() * r.toDouble()).dyn + operator fun div(r: Dyn): Dyn = (this.toDouble() / r.toDouble()).dyn + operator fun rem(r: Dyn): Dyn = (this.toDouble() % r.toDouble()).dyn + infix fun pow(r: Dyn): Dyn = (this.toDouble().pow(r.toDouble())).dyn + infix fun bitAnd(r: Dyn): Dyn = (this.toInt() and r.toInt()).dyn + infix fun bitOr(r: Dyn): Dyn = (this.toInt() or r.toInt()).dyn + infix fun bitXor(r: Dyn): Dyn = (this.toInt() xor r.toInt()).dyn + /** Logical AND */ + infix fun and(r: Dyn): Boolean = (this.toBool() && r.toBool()) + /** Logical OR */ + infix fun or(r: Dyn): Boolean = (this.toBool() || r.toBool()) + + /** Equal */ + infix fun eq(r: Dyn): Boolean = when { + this.value is Number && r.value is Number -> this.toDouble() == r.toDouble() + this.value is String || r.value is String -> this.toString() == r.toString() + else -> this.value == r.value + } + /** Not Equal */ + infix fun ne(r: Dyn): Boolean = when { + this.value is Number && r.value is Number -> this.toDouble() != r.toDouble() + this.value is String || r.value is String -> this.toString() != r.toString() + else -> this.value != r.value + } + /** Strict EQual */ + infix fun seq(r: Dyn): Boolean = this.value === r.value + /** Strict Not Equal */ + infix fun sne(r: Dyn): Boolean = this.value !== r.value + /** Less Than */ + infix fun lt(r: Dyn): Boolean = compare(this, r) < 0 + /** Less or Equal */ + infix fun le(r: Dyn): Boolean = compare(this, r) <= 0 + /** Greater Than */ + infix fun gt(r: Dyn): Boolean = compare(this, r) > 0 + /** Greater or Equal */ + infix fun ge(r: Dyn): Boolean = compare(this, r) >= 0 + operator fun contains(r: Dyn): Boolean { + val collection = this + val element = r + if (collection.value == element.value) return true + return when (collection.value) { + is String -> collection.value.contains(element.value.toString()) + is Set<*> -> element.value in collection.value + else -> element.value in collection.toListAny() + } + } + fun coalesce(default: Dyn): Dyn = if (this.isNotNull) this else default + + override fun compareTo(other: Dyn): Int { + val l = this + val r = other + if (l.value is Number && r.value is Number) { + return l.value.toDouble().compareTo(r.value.toDouble()) + } + val lc = l.toComparable() + val rc = r.toComparable() + return if (lc::class.isInstance(rc)) lc.compareTo(rc) else -1 + } + + override fun toString(): String = value.toString() + + companion object { + val global get() = dynApi.global.dyn + + fun compare(l: Dyn, r: Dyn): Int = l.compareTo(r) + fun contains(collection: Dyn, element: Dyn): Boolean = element in collection + + /* + fun unop(r: Dyn, op: String): Dyn = when (op) { + "+" -> +r + "-" -> -r + "~" -> r.inv() + "!" -> r.not() + else -> error("Not implemented unary operator '$op'") + } + + fun binop(l: Dyn, r: Dyn, op: String): Dyn { + return when (op) { + "+" -> (l + r) + "-" -> (l - r) + "*" -> (l * r) + "/" -> (l / r) + "%" -> (l % r) + "**" -> (l pow r) + "&" -> (l bitAnd r) + "|" -> (l bitOr r) + "^" -> (l bitXor r) + "&&" -> (l and r).dyn + "and" -> (l and r).dyn + "||" -> (l or r).dyn + "or" -> (l or r).dyn + "==" -> (l eq r).dyn + "!=" -> (l ne r).dyn + "===" -> (l seq r).dyn + "!==" -> (l sne r).dyn + "<" -> (l lt r).dyn + "<=" -> (l le r).dyn + ">" -> (l gt r).dyn + ">=" -> (l ge r).dyn + "in" -> r.contains(l).dyn + "contains" -> l.contains(r).dyn + "?:" -> l.coalesce(r) + else -> error("Not implemented binary operator '$op'") + } + } + */ + } + + fun toList(): List = toList().map { it.dyn } + fun toIterable(): Iterable = toIterableAny().map { it.dyn } + + fun toListAny(): List<*> = toIterableAny().toList() + + fun toIterableAny(): Iterable<*> = when (value) { + null -> listOf() + //is Dynamic2Iterable -> it.dynamic2Iterate() + is Iterable<*> -> value + is CharSequence -> value.toList() + is Map<*, *> -> value.toList() + else -> listOf() + } + + interface Invokable { + fun invoke(name: String, args: Array): Any? + } + + interface SuspendInvokable { + suspend fun invoke(name: String, args: Array): Any? + } + + fun dynamicInvoke(name: String, vararg args: Any?): Dyn = when (value) { + null -> null.dyn + is Invokable -> value.invoke(name, args).dyn + else -> dynApi.invoke(value, name, args).dyn + } + + suspend fun suspendDynamicInvoke(name: String, vararg args: Any?): Dyn = when (value) { + null -> null.dyn + is Invokable -> value.invoke(name, args).dyn + is SuspendInvokable -> value.invoke(name, args).dyn + else -> dynApi.suspendInvoke(value, name, args).dyn + } + + operator fun set(key: Dyn, value: Dyn) = set(key.value, value.value) + operator fun set(key: Any?, value: Dyn) = set(key, value.value) + operator fun set(key: Any?, value: Any?) { + when (value) { + is MutableMap<*, *> -> (this.value as MutableMap)[key] = value + is MutableList<*> -> (this.value as MutableList)[key.dyn.toInt()] = value + else -> dynApi.set(this.value, key.toString(), value) + } + } + + operator fun get(key: Dyn): Dyn = get(key.value) + operator fun get(key: Any?): Dyn = when (value) { + null -> null.dyn + is Map<*, *> -> (value as Map)[key].dyn + is List<*> -> value[key.dyn.toInt()].dyn + else -> dynApi.get(value, key.toString()).dyn + } + + suspend fun suspendSet(key: Dyn, value: Dyn) = suspendSet(key.value, value.value) + suspend fun suspendSet(key: Any?, value: Dyn) = suspendSet(key, value.value) + suspend fun suspendSet(key: Any?, value: Any?) { + when (value) { + is MutableMap<*, *> -> (this.value as MutableMap)[key] = value + is MutableList<*> -> (this.value as MutableList)[key.dyn.toInt()] = value + else -> dynApi.suspendSet(this.value, key.toString(), value) + } + } + + suspend fun suspendGet(key: Dyn): Dyn = suspendGet(key.value) + suspend fun suspendGet(key: Any?): Dyn = when (value) { + null -> null.dyn + is Map<*, *> -> (value as Map)[key].dyn + is List<*> -> value[key.dyn.toInt()].dyn + else -> dynApi.suspendGet(value, key.toString()).dyn + } + + val mapAny: Map get() = if (value is Map<*, *>) value as Map else LinkedHashMap() + val listAny: List get() = if (value == null) listOf() else if (value is List<*>) value else if (value is Iterable<*>) value.toList() else listOf(value) + val keysAny: List get() = if (value is Map<*, *>) keys.toList() else listOf() + + val map: Map get() = mapAny.map { it.key.dyn to it.value.dyn }.toMap() + val list: List get() = listAny.map { it.dyn } + val keys: List get() = keysAny.map { it.dyn } + + fun String.toNumber(): Number = (this.toIntOrNull() as? Number?) ?: this.toDoubleOrNull() ?: Double.NaN + + fun toBool(extraStrings: Boolean = true): Boolean = when (value) { + null -> false + is Boolean -> value + else -> toBoolOrNull(extraStrings) ?: true + } + + fun toBoolOrNull(extraStrings: Boolean = true): Boolean? = when (value) { + null -> null + is Boolean -> value + is Number -> toDouble() != 0.0 + is String -> { + if (extraStrings) { + when (value.toLowerCase()) { + "", "0", "false", "NaN", "null", "undefined", "ko", "no" -> false + else -> true + } + } else { + value.isNotEmpty() && value != "0" && value != "false" + } + } + else -> null + } + + fun toNumber(): Number = when (value) { + null -> 0 + is Number -> value + is Boolean -> if (value) 1 else 0 + is String -> value.toIntSafe() ?: value.toDoubleSafe() ?: 0 + //else -> it.toString().toNumber() + else -> value.toString().toNumber() + } + + fun toString(value: Any?): String = when (value) { + null -> "" + is String -> value + is Double -> { + if (value == value.toInt().toDouble()) { + value.toInt().toString() + } else { + value.toString() + } + } + is Iterable<*> -> "[" + value.joinToString(", ") { toString(it) } + "]" + is Map<*, *> -> "{" + value.map { toString(it.key).quote() + ": " + toString(it.value) }.joinToString(", ") + "}" + else -> value.toString() + } + + fun toByte(): Byte = toNumber().toByte() + fun toChar(): Char = when { + value is Char -> value + value is String && (value.length == 1) -> value.first() + else -> toNumber().toChar() + } + + fun toShort(): Short = toNumber().toShort() + fun toInt(): Int = toNumber().toInt() + fun toLong(): Long = toNumber().toLong() + fun toFloat(): Float = toNumber().toFloat() + fun toDouble(): Double = toNumber().toDouble() + + fun toBoolOrNull(): Boolean? = when (value) { + is Boolean -> value + is String -> value == "1" || value == "true" || value == "on" + is Number -> toInt() != 0 + else -> null + } + + fun toIntOrNull(): Int? = when (value) { + is Number -> toInt() + is String -> value.toIntSafe() + else -> null + } + + fun toLongOrNull(): Long? = when (value) { + is Number -> toLong() + is String -> value.toLongSafe() + else -> null + } + + fun toDoubleOrNull(): Double? = when (value) { + is Number -> toDouble() + is String -> value.toDoubleSafe() + else -> null + } + + fun toIntDefault(default: Int = 0): Int = when (value) { + is Number -> toInt() + is String -> value.toIntSafe(10) ?: default + else -> default + } + + fun toLongDefault(default: Long = 0L): Long = when (value) { + is Number -> toLong() + is String -> value.toLongSafe(10) ?: default + else -> default + } + + fun toFloatDefault(default: Float = 0f): Float = when (value) { + is Number -> toFloat() + is String -> toFloat() + else -> default + } + + fun toDoubleDefault(default: Double = 0.0): Double = when (value) { + is Number -> toDouble() + is String -> toDouble() + else -> default + } + + val str: String get() = toString() + val int: Int get() = toIntDefault() + val bool: Boolean get() = toBoolOrNull() ?: false + val float: Float get() = toFloatDefault() + val double: Double get() = toDoubleDefault() + val long: Long get() = toLongDefault() + + val intArray: IntArray get() = value as? IntArray ?: (value as? IntArrayList)?.toIntArray() ?: list.map { it.dyn.int }.toIntArray() + val floatArray: FloatArray get() = value as? FloatArray ?: (value as? FloatArrayList)?.toFloatArray() ?: list.map { it.dyn.float }.toFloatArray() + val doubleArray: DoubleArray get() = value as? DoubleArray ?: (value as? DoubleArrayList)?.toDoubleArray() ?: list.map { it.dyn.double }.toDoubleArray() + val longArray: LongArray get() = value as? LongArray ?: list.map { it.dyn.long }.toLongArray() +} + +private fun String.toIntSafe(radix: Int = 10) = this.toIntOrNull(radix) +private fun String.toDoubleSafe() = this.toDoubleOrNull() +private fun String.toLongSafe(radix: Int = 10) = this.toLongOrNull(radix) diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/DynApi.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/DynApi.kt new file mode 100644 index 00000000..be47384b --- /dev/null +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/DynApi.kt @@ -0,0 +1,24 @@ +package com.soywiz.korio.dynamic + +import kotlin.native.concurrent.* + +interface DynApi { + val global: Any? get() = null + + fun get(instance: Any?, key: String): Any? + fun set(instance: Any?, key: String, value: Any?) + fun invoke(instance: Any?, key: String, args: Array): Any? + + suspend fun suspendGet(instance: Any?, key: String): Any? = get(instance, key) + suspend fun suspendSet(instance: Any?, key: String, value: Any?): Unit = set(instance, key, value) + suspend fun suspendInvoke(instance: Any?, key: String, args: Array): Any? = invoke(instance, key, args) +} + +@ThreadLocal +var defaultDynApi: DynApi = DynamicInternal + +// @TODO: We should be able to plug-in a kotlinx-serialization version for this +@ThreadLocal +var dynApi: DynApi = DynamicInternal + +internal expect object DynamicInternal : DynApi diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/KDynamic.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/KDynamic.kt index 77fa9d7a..9be3b219 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/KDynamic.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/dynamic/KDynamic.kt @@ -1,143 +1,52 @@ package com.soywiz.korio.dynamic -import com.soywiz.kds.* - -internal expect object DynamicInternal { - val global: Any? - fun get(instance: Any?, key: String): Any? - fun set(instance: Any?, key: String, value: Any?) - fun invoke(instance: Any?, key: String, args: Array): Any? -} - -private fun String.toIntSafe(radix: Int = 10) = this.toIntOrNull(radix) -private fun String.toDoubleSafe() = this.toDoubleOrNull() -private fun String.toLongSafe(radix: Int = 10) = this.toLongOrNull(radix) - +@Suppress("DEPRECATION") +@Deprecated("Use Dyn instead") open class KDynamic { companion object : KDynamic() { inline operator fun invoke(callback: KDynamic.() -> T): T = callback(KDynamic) inline operator fun invoke(value: T, callback: KDynamic.(T) -> R): R = callback(KDynamic, value) } - val global get() = DynamicInternal.global + val global get() = Dyn.global.value interface Invokable { fun invoke(name: String, args: Array): Any? } - fun Any?.dynamicInvoke(name: String, vararg args: Any?): Any? = when (this) { - null -> null - is Invokable -> invoke(name, args) - else -> DynamicInternal.invoke(this, name, args) - } - - operator fun Any?.set(key: Any?, value: Any?) { - when (this) { - is MutableMap<*, *> -> (this as MutableMap)[key] = value - is MutableList<*> -> (this as MutableList)[key.toInt()] = value - else -> DynamicInternal.set(this, key.toString(), value) - } - } - - operator fun Any?.get(key: Any?): Any? = when (this) { - null -> null - is Map<*, *> -> (this as Map)[key] - is List<*> -> this[key.toInt()] - else -> DynamicInternal.get(this, key.toString()) - } - - val Any?.map: Map get() = if (this is Map<*, *>) this as Map else LinkedHashMap() - val Any?.list: List get() = if (this == null) listOf() else if (this is List<*>) this else if (this is Iterable<*>) this.toList() else listOf(this) - val Any?.keys: List get() = if (this is Map<*, *>) keys.toList() else listOf() - - fun Any?.toNumber(): Number = when (this) { - null -> 0 - is Boolean -> if (this) 1 else 0 - is Number -> this - is String -> this.toIntSafe() ?: this.toDoubleSafe() ?: 0 - else -> 0 - } - - fun Any?.toBool(): Boolean = when (this) { - is Boolean -> this - is String -> when (this.toLowerCase()) { - "", "0", "false", "NaN", "null", "undefined", "ko", "no" -> false - else -> true - } - else -> toInt() != 0 - } - - fun Any?.toByte(): Byte = toNumber().toByte() - fun Any?.toChar(): Char = when { - this is Char -> this - this is String && (this.length == 1) -> this.first() - else -> toNumber().toChar() - } - - fun Any?.toShort(): Short = toNumber().toShort() - fun Any?.toInt(): Int = toNumber().toInt() - fun Any?.toLong(): Long = toNumber().toLong() - fun Any?.toFloat(): Float = toNumber().toFloat() - fun Any?.toDouble(): Double = toNumber().toDouble() - - fun Any?.toBoolOrNull(): Boolean? = when (this) { - is Boolean -> this - is String -> this == "1" || this == "true" || this == "on" - is Number -> toInt() != 0 - else -> null - } - - fun Any?.toIntOrNull(): Int? = when (this) { - is Number -> toInt() - is String -> toIntSafe() - else -> null - } - - fun Any?.toLongOrNull(): Long? = when (this) { - is Number -> toLong() - is String -> toLongSafe() - else -> null - } - - fun Any?.toDoubleOrNull(): Double? = when (this) { - is Number -> toDouble() - is String -> toDoubleSafe() - else -> null - } - - fun Any?.toIntDefault(default: Int = 0): Int = when (this) { - is Number -> toInt() - is String -> toIntSafe(10) ?: default - else -> default - } - - fun Any?.toLongDefault(default: Long = 0L): Long = when (this) { - is Number -> toLong() - is String -> toLongSafe(10) ?: default - else -> default - } - - fun Any?.toFloatDefault(default: Float = 0f): Float = when (this) { - is Number -> toFloat() - is String -> this.toFloat() - else -> default - } - - fun Any?.toDoubleDefault(default: Double = 0.0): Double = when (this) { - is Number -> toDouble() - is String -> this.toDouble() - else -> default - } - - val Any?.str: String get() = toString() - val Any?.int: Int get() = toIntDefault() - val Any?.bool: Boolean get() = toBoolOrNull() ?: false - val Any?.float: Float get() = toFloatDefault() - val Any?.double: Double get() = toDoubleDefault() - val Any?.long: Long get() = toLongDefault() - - val Any?.intArray: IntArray get() = this as? IntArray ?: (this as? IntArrayList)?.toIntArray() ?: list.map { it.int }.toIntArray() - val Any?.floatArray: FloatArray get() = this as? FloatArray ?: (this as? FloatArrayList)?.toFloatArray() ?: list.map { it.float }.toFloatArray() - val Any?.doubleArray: DoubleArray get() = this as? DoubleArray ?: (this as? DoubleArrayList)?.toDoubleArray() ?: list.map { it.double }.toDoubleArray() - val Any?.longArray: LongArray get() = this as? LongArray ?: list.map { it.long }.toLongArray() + fun Any?.dynamicInvoke(name: String, vararg args: Any?): Any? = this.dyn.dynamicInvoke(name, *args).value + operator fun Any?.set(key: Any?, value: Any?) = this.dyn.set(key, value) + operator fun Any?.get(key: Any?): Any? = this.dyn.get(key).value + val Any?.map: Map get() = this.dyn.mapAny + val Any?.list: List get() = this.dyn.listAny + val Any?.keys: List get() = this.dyn.keysAny + fun Any?.toNumber(): Number = this.dyn.toNumber() + fun Any?.toBool(): Boolean = this.dyn.toBool() + fun Any?.toByte(): Byte = this.dyn.toByte() + fun Any?.toChar(): Char = this.dyn.toChar() + fun Any?.toShort(): Short = this.dyn.toShort() + fun Any?.toInt(): Int = this.dyn.toInt() + fun Any?.toLong(): Long = this.dyn.toLong() + fun Any?.toFloat(): Float = this.dyn.toFloat() + fun Any?.toDouble(): Double = this.dyn.toDouble() + fun Any?.toBoolOrNull(): Boolean? = this.dyn.toBoolOrNull() + fun Any?.toIntOrNull(): Int? = this.dyn.toIntOrNull() + fun Any?.toLongOrNull(): Long? = this.dyn.toLongOrNull() + fun Any?.toDoubleOrNull(): Double? = this.dyn.toDoubleOrNull() + fun Any?.toIntDefault(default: Int = 0): Int = this.dyn.toIntDefault(default) + fun Any?.toLongDefault(default: Long = 0L): Long = this.dyn.toLongDefault(default) + fun Any?.toFloatDefault(default: Float = 0f): Float = this.dyn.toFloatDefault(default) + fun Any?.toDoubleDefault(default: Double = 0.0): Double = this.dyn.toDoubleDefault(default) + + val Any?.str: String get() = this.dyn.str + val Any?.int: Int get() = this.dyn.int + val Any?.bool: Boolean get() = this.dyn.bool + val Any?.float: Float get() = this.dyn.float + val Any?.double: Double get() = this.dyn.double + val Any?.long: Long get() = this.dyn.long + + val Any?.intArray: IntArray get() = this.dyn.intArray + val Any?.floatArray: FloatArray get() = this.dyn.floatArray + val Any?.doubleArray: DoubleArray get() = this.dyn.doubleArray + val Any?.longArray: LongArray get() = this.dyn.longArray } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/file/PathInfo.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/file/PathInfo.kt index 3420088a..9b96bcb5 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/file/PathInfo.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/file/PathInfo.kt @@ -3,6 +3,7 @@ package com.soywiz.korio.file import com.soywiz.kds.* import com.soywiz.kds.iterators.* import com.soywiz.korio.* +import com.soywiz.korio.internal.* import com.soywiz.korio.lang.* import com.soywiz.korio.net.* @@ -14,7 +15,7 @@ inline class PathInfo(val fullPath: String) fun PathInfo.relativePathTo(relative: PathInfo): String? { val thisParts = this.parts().toMutableList() val relativeParts = relative.parts().toMutableList() - val maxNumParts = kotlin.math.min(thisParts.size, relativeParts.size) + val maxNumParts = min2(thisParts.size, relativeParts.size) val outputParts = arrayListOf() val commonCount = count { it < maxNumParts && thisParts[it] == relativeParts[it] } while (relativeParts.size > commonCount) { diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt index 69725b6d..6926a230 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt @@ -5,6 +5,8 @@ package com.soywiz.korio.file import com.soywiz.klock.* import com.soywiz.korio.async.* import com.soywiz.korio.file.std.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.min2 import com.soywiz.korio.lang.* import com.soywiz.korio.stream.* import kotlinx.coroutines.channels.* @@ -69,7 +71,7 @@ abstract class Vfs : AsyncCloseable { val s = open(path, VfsOpenMode.READ) try { s.position = range.start - val readCount = min(Int.MAX_VALUE.toLong() - 1, (range.endInclusive - range.start)).toInt() + 1 + val readCount = min2(Int.MAX_VALUE.toLong() - 1, (range.endInclusive - range.start)).toInt() + 1 return s.readBytesUpTo(readCount) } finally { s.close() diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/file/std/MapLikeStorageVfs.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/file/std/MapLikeStorageVfs.kt index cf38c34c..1354c545 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/file/std/MapLikeStorageVfs.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/file/std/MapLikeStorageVfs.kt @@ -5,6 +5,8 @@ import com.soywiz.klock.* import com.soywiz.kmem.* import com.soywiz.korio.async.* import com.soywiz.korio.file.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.max2 import com.soywiz.korio.lang.* import com.soywiz.korio.serialization.json.* import com.soywiz.korio.stream.* @@ -121,7 +123,7 @@ class MapLikeStorageVfs(val storage: SimpleStorage) : VfsV2() { override suspend fun write(position: Long, buffer: ByteArray, offset: Int, len: Int) { files.writeData(npath, position, buffer, offset, len) - updateInfo(info.copy(size = max(info.size, position + len))) + updateInfo(info.copy(size = max2(info.size, position + len))) } override suspend fun setLength(value: Long) { @@ -239,7 +241,7 @@ private class StorageFiles(val storage: SimpleStorage) { val inChunk = (apos % CHUNK_SIZE).toInt() val c = getFileChunk(fileName, chunk) ?: byteArrayOf() val available = CHUNK_SIZE - inChunk - val written = min(available, pending) + val written = min2(available, pending) if (written <= 0) invalidOp("Unexpected written") val cc = c.copyOf(inChunk + written) arraycopy(buffer, aoffset, cc, inChunk, written) @@ -257,7 +259,7 @@ private class StorageFiles(val storage: SimpleStorage) { val inChunk = (position % CHUNK_SIZE).toInt() val c = getFileChunk(fileName, chunk) ?: return 0 val available = c.size - inChunk - val read = min(available, len) + val read = min2(available, len) arraycopy(c, inChunk, buffer, offset, read) return read } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/internal/internal.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/internal/internal.kt new file mode 100644 index 00000000..bbde80b5 --- /dev/null +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/internal/internal.kt @@ -0,0 +1,13 @@ +package com.soywiz.korio.internal + +@PublishedApi internal fun min2(a: Int, b: Int) = if (a < b) a else b +@PublishedApi internal fun max2(a: Int, b: Int) = if (a > b) a else b + +@PublishedApi internal fun min2(a: Float, b: Float) = if (a < b) a else b +@PublishedApi internal fun max2(a: Float, b: Float) = if (a > b) a else b + +@PublishedApi internal fun min2(a: Double, b: Double) = if (a < b) a else b +@PublishedApi internal fun max2(a: Double, b: Double) = if (a > b) a else b + +@PublishedApi internal fun min2(a: Long, b: Long) = if (a < b) a else b +@PublishedApi internal fun max2(a: Long, b: Long) = if (a > b) a else b diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/lang/Charset.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/lang/Charset.kt index 8991f467..4cddfedf 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/lang/Charset.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/lang/Charset.kt @@ -2,6 +2,7 @@ package com.soywiz.korio.lang import com.soywiz.kds.* import com.soywiz.kmem.* +import com.soywiz.korio.internal.* import kotlin.native.concurrent.SharedImmutable abstract class Charset(val name: String) { @@ -194,7 +195,7 @@ fun ByteArray.toString(charset: Charset): String { fun ByteArray.readStringz(o: Int, size: Int, charset: Charset = UTF8): String { var idx = o - val stop = kotlin.math.min(this.size, o + size) + val stop = min2(this.size, o + size) while (idx < stop) { if (this[idx] == 0.toByte()) break idx++ diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/lang/StringExt.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/lang/StringExt.kt index 577c883a..6a333a13 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/lang/StringExt.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/lang/StringExt.kt @@ -1,6 +1,8 @@ package com.soywiz.korio.lang import com.soywiz.kmem.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.min2 import com.soywiz.korio.util.* operator fun String.Companion.invoke(arrays: IntArray, offset: Int = 0, size: Int = arrays.size - offset): String { @@ -95,7 +97,7 @@ fun String.splitInChunks(size: Int): List { val out = arrayListOf() var pos = 0 while (pos < this.length) { - out += this.substring(pos, kotlin.math.min(this.length, pos + size)) + out += this.substring(pos, min2(this.length, pos + size)) pos += size } return out @@ -128,4 +130,4 @@ fun String.parseInt(): Int = when { val String.quoted: String get() = this.quote() -fun String.toCharArray() = CharArray(length) { this@toCharArray[it] } \ No newline at end of file +fun String.toCharArray() = CharArray(length) { this@toCharArray[it] } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/net/http/HttpPortable.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/net/http/HttpPortable.kt index 01fbd621..5a18a1f1 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/net/http/HttpPortable.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/net/http/HttpPortable.kt @@ -1,6 +1,8 @@ package com.soywiz.korio.net.http import com.soywiz.korio.async.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.min2 import com.soywiz.korio.lang.* import com.soywiz.korio.net.* import com.soywiz.korio.stream.* @@ -150,7 +152,7 @@ internal object HttpPortable { if (contentLength != null) { var remaining = contentLength while (remaining > 0) { - val toRead = min(BodyChunkSize.toLong(), remaining).toInt() + val toRead = min2(BodyChunkSize.toLong(), remaining).toInt() val read = cb.readBytesUpToFirst(toRead) bodyHandler(read) remaining -= read.size diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/stream/AsyncStream.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/stream/AsyncStream.kt index c1805c57..df3c6507 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/stream/AsyncStream.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/stream/AsyncStream.kt @@ -282,7 +282,7 @@ class BufferedStreamBase(val base: AsyncStreamBase, val blockSize: Int = 2048, v } val soffset = (position % bsize).toInt() val available = cachedData.size - soffset - val toRead = min(available, len) + val toRead = min2(available, len) arraycopy(cachedData, soffset, buffer, offset, toRead) return toRead } @@ -480,13 +480,13 @@ suspend fun AsyncInputStream.readBytesUpTo(len: Int): ByteArray { val BYTES_TEMP_SIZE = 0x1000 if (len <= BYTES_TEMP_SIZE) return readBytesUpToCopy(ByteArray(len)) - if (this is AsyncPositionLengthStream) return readBytesUpToCopy(ByteArray(min(len, this.getAvailable().toIntClamp()))) + if (this is AsyncPositionLengthStream) return readBytesUpToCopy(ByteArray(min2(len, this.getAvailable().toIntClamp()))) var pending = len val temp = ByteArray(BYTES_TEMP_SIZE) val bout = ByteArrayBuilder() while (pending > 0) { - val read = this.read(temp, 0, min(temp.size, pending)) + val read = this.read(temp, 0, min2(temp.size, pending)) if (read <= 0) break bout.append(temp, 0, read) pending -= read @@ -552,7 +552,7 @@ suspend fun AsyncInputStream.skip(count: Int) { val temp = ByteArray(0x1000) var remaining = count while (remaining > 0) { - val toRead = min(remaining, count) + val toRead = min2(remaining, count) readTempExact(toRead, temp) remaining -= toRead } @@ -790,15 +790,15 @@ class MemoryAsyncStreamBase(var data: com.soywiz.kmem.ByteArrayBuilder) : AsyncS override suspend fun read(position: Long, buffer: ByteArray, offset: Int, len: Int): Int { checkPosition(position) if (position !in 0 until ilength) return 0 - val end = min(this.ilength.toLong(), position + len) - val actualLen = max((end - position).toInt(), 0) + val end = min2(this.ilength.toLong(), position + len) + val actualLen = max2((end - position).toInt(), 0) arraycopy(this.data.data, position.toInt(), buffer, offset, actualLen) return actualLen } override suspend fun write(position: Long, buffer: ByteArray, offset: Int, len: Int) { checkPosition(position) - data.size = max(data.size, (position + len).toInt()) + data.size = max2(data.size, (position + len).toInt()) arraycopy(buffer, offset, this.data.data, position.toInt(), len) } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/stream/SyncStream.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/stream/SyncStream.kt index 41c1cc53..dd3e19e3 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/stream/SyncStream.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/stream/SyncStream.kt @@ -129,7 +129,7 @@ class SliceSyncStreamBase(internal val base: SyncStreamBase, internal val baseSt class FillSyncStreamBase(val fill: Byte, override var length: Long) : SyncStreamBase() { override fun read(position: Long, buffer: ByteArray, offset: Int, len: Int): Int { - val end = min(length, position + len) + val end = min2(length, position + len) val actualLen = (end - position).toIntSafe() buffer.fill(fill, offset, offset + actualLen) return actualLen @@ -214,15 +214,15 @@ class MemorySyncStreamBase(var data: ByteArrayBuilder) : SyncStreamBase() { val ipos = position.toInt() //if (position !in 0 until ilength) return -1 if (position !in 0 until ilength) return 0 - val end = min(this.ilength, ipos + len) - val actualLen = max((end - ipos), 0) + val end = min2(this.ilength, ipos + len) + val actualLen = max2((end - ipos), 0) arraycopy(this.data.data, ipos, buffer, offset, actualLen) return actualLen } override fun write(position: Long, buffer: ByteArray, offset: Int, len: Int) { checkPosition(position) - data.size = max(data.size, (position + len).toInt()) + data.size = max2(data.size, (position + len).toInt()) arraycopy(buffer, offset, this.data.data, position.toInt(), len) } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt index 22111a85..e597d94e 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt @@ -5,6 +5,8 @@ import com.soywiz.korio.lang.* import kotlin.collections.* import kotlin.math.* import com.soywiz.kds.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.internal.max2 class StrReader(val str: String, val file: String = "file", var pos: Int = 0) { companion object { @@ -88,7 +90,7 @@ class StrReader(val str: String, val file: String = "file", var pos: Int = 0) { fun expect(expected: Char) = readExpect("$expected") fun skip(count: Int = 1) = this.apply { this.pos += count; } private fun substr(pos: Int, length: Int): String { - return this.str.substring(min(pos, this.length), min(pos + length, this.length)) + return this.str.substring(min2(pos, this.length), min2(pos + length, this.length)) } fun tryLit(lit: String): String? { @@ -194,7 +196,7 @@ class StrReader(val str: String, val file: String = "file", var pos: Int = 0) { class TRange(val min: Int, val max: Int, val reader: StrReader) { companion object { fun combine(a: TRange, b: TRange): TRange { - return TRange(min(a.min, b.min), max(a.max, b.max), a.reader) + return TRange(min2(a.min, b.min), max2(a.max, b.max), a.reader) } fun combineList(list: List): TRange? { @@ -203,8 +205,8 @@ class StrReader(val str: String, val file: String = "file", var pos: Int = 0) { var min = first.min var max = first.max list.fastForEach { i -> - min = min(min, i.min) - max = max(max, i.max) + min = min2(min, i.min) + max = max2(max, i.max) } return TRange(min, max, first.reader) } diff --git a/korio/src/commonTest/kotlin/com/soywiz/korio/dynamic/DynCommonTest.kt b/korio/src/commonTest/kotlin/com/soywiz/korio/dynamic/DynCommonTest.kt new file mode 100644 index 00000000..b1b6bbe9 --- /dev/null +++ b/korio/src/commonTest/kotlin/com/soywiz/korio/dynamic/DynCommonTest.kt @@ -0,0 +1,45 @@ +package com.soywiz.korio.dynamic + +import kotlin.test.* + +class DynCommonTest { + val data1 = mapOf("a" to mapOf("b" to 10)).dyn + + @Test + fun test() { + assertEquals(10, data1["a"]["b"].int) + } + + @Test + fun testBinop() { + assertEquals(11, (10.0.dyn + 1.dyn).int) + assertEquals(9, (10.0.dyn - 1.dyn).int) + assertEquals(20, (10.0.dyn * 2.dyn).int) + assertEquals(5, (10.0.dyn / 2.0.dyn).int) + assertEquals(0, (10.0.dyn % 2.0.dyn).int) + assertEquals(100, (10.0.dyn pow 2.0.dyn).int) + assertEquals(2, (3.dyn bitAnd 6.dyn).int) + assertEquals(7, (3.dyn bitOr 6.dyn).int) + assertEquals(5, (3.dyn bitXor 6.dyn).int) + assertEquals(true, (3.dyn and 6.dyn)) + assertEquals(true, (3.dyn or 6.dyn)) + } + + @Test + fun testEq() { + assertEquals(true, (3.dyn seq 3.dyn)) + assertEquals(false, (3.dyn seq 4.dyn)) + + assertEquals(false, (3.dyn sne 3.dyn)) + assertEquals(true, (3.dyn sne 4.dyn)) + + assertEquals(false, (3.dyn seq "3".dyn)) + assertEquals(false, (3.dyn seq "4".dyn)) + + assertEquals(true, (3.dyn eq "3".dyn)) + assertEquals(false, (3.dyn eq "4".dyn)) + + assertEquals(false, (3.dyn ne "3".dyn)) + assertEquals(true, (3.dyn ne "4".dyn)) + } +} diff --git a/korio/src/jsMain/kotlin/com/soywiz/korio/KorioNativeJsNodeJs.kt b/korio/src/jsMain/kotlin/com/soywiz/korio/KorioNativeJsNodeJs.kt index 51f7dd21..fa136e7a 100644 --- a/korio/src/jsMain/kotlin/com/soywiz/korio/KorioNativeJsNodeJs.kt +++ b/korio/src/jsMain/kotlin/com/soywiz/korio/KorioNativeJsNodeJs.kt @@ -372,7 +372,7 @@ class NodeFDStream(val file: VfsFile, val fs: dynamic, var fd: NodeJsLocalVfs.FD if (err != null) { c.resumeWithException(IOException("Error reading from $file :: err=$err")) } else { - //println("NODE READ[$file] read: ${bytesRead} : ${buffer.sliceArray(0 until min(buffer.size, 5)).contentToString()}") + //println("NODE READ[$file] read: ${bytesRead} : ${buffer.sliceArray(0 until min2(buffer.size, 5)).contentToString()}") c.resume(bytesRead) } Unit diff --git a/korio/src/jsMain/kotlin/com/soywiz/korio/dynamic/DynApiJs.kt b/korio/src/jsMain/kotlin/com/soywiz/korio/dynamic/DynApiJs.kt new file mode 100644 index 00000000..5141df0f --- /dev/null +++ b/korio/src/jsMain/kotlin/com/soywiz/korio/dynamic/DynApiJs.kt @@ -0,0 +1,17 @@ +package com.soywiz.korio.dynamic + +internal actual object DynamicInternal : DynApi { + override val global: Any? = js("(typeof global !== 'undefined') ? global : window") + + override fun get(instance: Any?, key: String): Any? { + return (instance.asDynamic())[key] + } + + override fun set(instance: Any?, key: String, value: Any?) { + (instance.asDynamic())[key] = value + } + + override fun invoke(instance: Any?, key: String, args: Array): Any? { + return (instance.asDynamic())[key].apply(instance, args) + } +} diff --git a/korio/src/jsMain/kotlin/com/soywiz/korio/dynamic/DynamicInternal.kt b/korio/src/jsMain/kotlin/com/soywiz/korio/dynamic/DynamicInternal.kt deleted file mode 100644 index b41f37d6..00000000 --- a/korio/src/jsMain/kotlin/com/soywiz/korio/dynamic/DynamicInternal.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.soywiz.korio.dynamic - -internal actual object DynamicInternal { - actual val global: Any? = js("(typeof global !== 'undefined') ? global : window") - - actual fun get(instance: Any?, key: String): Any? { - return (instance.asDynamic())[key] - } - - actual fun set(instance: Any?, key: String, value: Any?) { - (instance.asDynamic())[key] = value - } - - actual fun invoke(instance: Any?, key: String, args: Array): Any? { - return (instance.asDynamic())[key].apply(instance, args) - } -} diff --git a/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/dynamic/DynApiJvm.kt b/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/dynamic/DynApiJvm.kt new file mode 100644 index 00000000..fb146555 --- /dev/null +++ b/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/dynamic/DynApiJvm.kt @@ -0,0 +1,101 @@ +package com.soywiz.korio.dynamic + +import java.lang.reflect.* + +internal actual object DynamicInternal : DynApi { + class JavaPackage(val name: String) + + override val global: Any? = JavaPackage("") + + private fun tryGetField(clazz: Class<*>, name: String): Field? { + val field = runCatching { clazz.getDeclaredField(name) }.getOrNull() + return when { + field != null -> field.apply { isAccessible = true } + clazz.superclass != null -> return tryGetField(clazz.superclass, name) + else -> null + } + } + + private fun tryGetMethod(clazz: Class<*>, name: String, args: Array?): Method? { + val methods = clazz.allDeclaredMethods.filter { it.name == name } + val method = when (methods.size) { + 0 -> null + 1 -> methods.first() + else -> { + if (args != null) { + val methodsSameArity = methods.filter { it.parameterTypes.size == args.size } + val argTypes = args.map { it!!::class.javaObjectType } + methodsSameArity.firstOrNull { + it.parameterTypes.toList().zip(argTypes).all { + it.first.kotlin.javaObjectType.isAssignableFrom(it.second) + } + } + } else { + methods.first() + } + } + } + return when { + method != null -> method.apply { isAccessible = true } + clazz.superclass != null -> return tryGetMethod(clazz.superclass, name, args) + else -> null + } + } + + override fun get(instance: Any?, key: String): Any? { + if (instance == null) return null + + val static = instance is Class<*> + val clazz: Class<*> = if (static) instance as Class<*> else instance.javaClass + + if (instance is JavaPackage) { + val path = "${instance.name}.$key".trim('.') + return try { + java.lang.Class.forName(path) + } catch (e: ClassNotFoundException) { + JavaPackage(path) + } + } + val method = tryGetMethod(clazz, "get${key.capitalize()}", null) + if (method != null) { + return method.invoke(if (static) null else instance) + } + val field = tryGetField(clazz, key) + if (field != null) { + return field.get(if (static) null else instance) + } + return null + } + + override fun set(instance: Any?, key: String, value: Any?) { + if (instance == null) return + + val static = instance is Class<*> + val clazz: Class<*> = if (static) instance as Class<*> else instance.javaClass + + val method = tryGetMethod(clazz, "set${key.capitalize()}", null) + if (method != null) { + method.invoke(if (static) null else instance, value) + return + } + val field = tryGetField(clazz, key) + if (field != null) { + field.set(if (static) null else instance, value) + return + } + } + + override fun invoke(instance: Any?, key: String, args: Array): Any? { + if (instance == null) return null + val method = tryGetMethod(if (instance is Class<*>) instance else instance.javaClass, key, args) + return method?.invoke(if (instance is Class<*>) null else instance, *args) + } +} + +private val Class<*>.allDeclaredFields: List + get() = this.declaredFields.toList() + (this.superclass?.allDeclaredFields?.toList() ?: listOf()) + +private fun Class<*>.isSubtypeOf(that: Class<*>) = that.isAssignableFrom(this) + +private val Class<*>.allDeclaredMethods: List + get() = this.declaredMethods.toList() + (this.superclass?.allDeclaredMethods?.toList() ?: listOf()) diff --git a/korio/src/jvmMain/kotlin/com/soywiz/korio/dynamic/DynamicInternal.kt b/korio/src/jvmMain/kotlin/com/soywiz/korio/dynamic/DynamicInternal.kt deleted file mode 100644 index 9dbdb546..00000000 --- a/korio/src/jvmMain/kotlin/com/soywiz/korio/dynamic/DynamicInternal.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.soywiz.korio.dynamic - -import com.soywiz.korio.util.* -import java.lang.reflect.* - -internal actual object DynamicInternal { - class JavaPackage(val name: String) - - actual val global: Any? = JavaPackage("") - - private fun tryGetField(clazz: Class<*>, name: String): Field? { - val field = runCatching { clazz.getDeclaredField(name) }.getOrNull() - return when { - field != null -> field.apply { isAccessible = true } - clazz.superclass != null -> return tryGetField(clazz.superclass, name) - else -> null - } - } - - private fun tryGetMethod(clazz: Class<*>, name: String, args: Array?): Method? { - val methods = clazz.allDeclaredMethods.filter { it.name == name } - val method = when (methods.size) { - 0 -> null - 1 -> methods.first() - else -> { - if (args != null) { - val methodsSameArity = methods.filter { it.parameterTypes.size == args.size } - val argTypes = args.map { it!!::class.javaObjectType } - methodsSameArity.firstOrNull { - it.parameterTypes.toList().zip(argTypes).all { - it.first.kotlin.javaObjectType.isAssignableFrom(it.second) - } - } - } else { - methods.first() - } - } - } - return when { - method != null -> method.apply { isAccessible = true } - clazz.superclass != null -> return tryGetMethod(clazz.superclass, name, args) - else -> null - } - } - - actual fun get(instance: Any?, key: String): Any? { - if (instance == null) return null - - val static = instance is Class<*> - val clazz: Class<*> = if (static) instance as Class<*> else instance.javaClass - - if (instance is JavaPackage) { - val path = "${instance.name}.$key".trim('.') - return try { - java.lang.Class.forName(path) - } catch (e: ClassNotFoundException) { - JavaPackage(path) - } - } - val method = tryGetMethod(clazz, "get${key.capitalize()}", null) - if (method != null) { - return method.invoke(if (static) null else instance) - } - val field = tryGetField(clazz, key) - if (field != null) { - return field.get(if (static) null else instance) - } - return null - } - - actual fun set(instance: Any?, key: String, value: Any?) { - if (instance == null) return - - val static = instance is Class<*> - val clazz: Class<*> = if (static) instance as Class<*> else instance.javaClass - - val method = tryGetMethod(clazz, "set${key.capitalize()}", null) - if (method != null) { - method.invoke(if (static) null else instance, value) - return - } - val field = tryGetField(clazz, key) - if (field != null) { - field.set(if (static) null else instance, value) - return - } - } - - actual fun invoke(instance: Any?, key: String, args: Array): Any? { - if (instance == null) return null - val method = tryGetMethod(if (instance is Class<*>) instance else instance.javaClass, key, args) - return method?.invoke(if (instance is Class<*>) null else instance, *args) - } -} diff --git a/korio/src/jvmMain/kotlin/com/soywiz/korio/file/std/LocalVfsJvm.kt b/korio/src/jvmMain/kotlin/com/soywiz/korio/file/std/LocalVfsJvm.kt index 6453b43c..f79140f1 100644 --- a/korio/src/jvmMain/kotlin/com/soywiz/korio/file/std/LocalVfsJvm.kt +++ b/korio/src/jvmMain/kotlin/com/soywiz/korio/file/std/LocalVfsJvm.kt @@ -4,6 +4,7 @@ import com.soywiz.klock.* import com.soywiz.korio.async.* import com.soywiz.korio.dynamic.* import com.soywiz.korio.file.* +import com.soywiz.korio.internal.* import com.soywiz.korio.lang.* import com.soywiz.korio.lang.Closeable import com.soywiz.korio.stream.* @@ -271,8 +272,8 @@ private class LocalVfsJvm : LocalVfsV2() { override suspend fun readRange(path: String, range: LongRange): ByteArray = executeIo { RandomAccessFile(resolveFile(path), "r").use { raf -> val fileLength = raf.length() - val start = kotlin.math.min(range.start, fileLength) - val end = kotlin.math.min(range.endInclusive, fileLength - 1) + 1 + val start = min2(range.start, fileLength) + val end = min2(range.endInclusive, fileLength - 1) + 1 val totalRead = (end - start).toInt() val out = ByteArray(totalRead) raf.seek(start) diff --git a/korio/src/jvmMain/kotlin/com/soywiz/korio/net/http/HttpClientJvm.kt b/korio/src/jvmMain/kotlin/com/soywiz/korio/net/http/HttpClientJvm.kt index 23992cb5..9f7ca1b1 100644 --- a/korio/src/jvmMain/kotlin/com/soywiz/korio/net/http/HttpClientJvm.kt +++ b/korio/src/jvmMain/kotlin/com/soywiz/korio/net/http/HttpClientJvm.kt @@ -3,6 +3,7 @@ package com.soywiz.korio.net.http import com.soywiz.kmem.* import com.soywiz.korio.async.* import com.soywiz.korio.concurrent.atomic.* +import com.soywiz.korio.internal.* import com.soywiz.korio.lang.* import com.soywiz.korio.stream.* import kotlinx.coroutines.* @@ -83,7 +84,7 @@ class HttpClientJvm : HttpClient() { val os = con.outputStream while (left > 0) { - val read = ccontent.read(temp, 0, Math.min(temp.size, left.toUintClamp())) + val read = ccontent.read(temp, 0, min2(temp.size, left.toUintClamp())) if (read <= 0) invalidOp("Problem reading") left -= read os.write(temp, 0, read) @@ -150,4 +151,4 @@ class HttpClientJvm : HttpClient() { } return result } -} \ No newline at end of file +} diff --git a/korio/src/jvmMain/kotlin/com/soywiz/korio/serialization/binary/Struct.kt b/korio/src/jvmMain/kotlin/com/soywiz/korio/serialization/binary/Struct.kt index dc04faa2..44fd2457 100644 --- a/korio/src/jvmMain/kotlin/com/soywiz/korio/serialization/binary/Struct.kt +++ b/korio/src/jvmMain/kotlin/com/soywiz/korio/serialization/binary/Struct.kt @@ -125,8 +125,8 @@ class StructReflect(val clazz: Class) { .sortedBy { it.offset } val specifiedSize = clazz.getAnnotation(Size::class.java)?.size - val calculatedSize = fieldsWithAnnotation.map { it.offset + it.type.size }.max() - val size = specifiedSize ?: calculatedSize ?: fieldsWithAnnotation.map { it.offset + it.type.size }.max() + val calculatedSize = fieldsWithAnnotation.map { it.offset + it.type.size }.maxOrNull() + val size = specifiedSize ?: calculatedSize ?: fieldsWithAnnotation.map { it.offset + it.type.size }.maxOrNull() ?: throw IllegalArgumentException("Empty struct $clazz or without @Offset") @Suppress("UNCHECKED_CAST") diff --git a/korio/src/jvmTest/kotlin/com/soywiz/korio/dynamic/DynTest.kt b/korio/src/jvmTest/kotlin/com/soywiz/korio/dynamic/DynTest.kt new file mode 100644 index 00000000..47cadd41 --- /dev/null +++ b/korio/src/jvmTest/kotlin/com/soywiz/korio/dynamic/DynTest.kt @@ -0,0 +1,55 @@ +package com.soywiz.korio.dynamic + +import org.junit.* +import org.junit.Test +import kotlin.test.* + +class DynTest { + @Test + fun test() { + assertEquals("10", Dyn.global["java.lang.String"].dynamicInvoke("valueOf", 10).value) + assertEquals("10", Dyn.global["java"]["lang.String"].dynamicInvoke("valueOf", 10).value) + assertEquals("HELLO", "hello".dyn.dynamicInvoke("toUpperCase").value) + assertEquals("a", Demo().dyn.dynamicInvoke("demo").value) + assertEquals("b", Demo().dyn.dynamicInvoke("demo", "1").value) + assertEquals("c", Demo().dyn.dynamicInvoke("demo", 1).value) + } + + @Test + fun test2() { + assertEquals("c", Demo().dyn.dynamicInvoke("demo", 1).value) + } + + @Suppress("unused", "UNUSED_PARAMETER") + class Demo { + fun demo(): String = "a" + fun demo(a: String): String = "b" + fun demo(a: Int): String = "c" + } + + @Test + fun test3() { + assertEquals(10, mapOf("a" to 10).dyn["a"].int) + } + + @Suppress("unused") + class Demo2 { + var z = 3 + fun setA(value: Int) { + z = value * 2 + } + fun getA() = z * 3 + } + + @Test + fun test4() { + assertEquals(20, Demo2().also { it.dyn["a"] = 10 }.z) + assertEquals(9, Demo2().dyn["a"].value ) + assertEquals(3, Demo2().dyn["z"].value ) + } + + @Test + fun test5() { + assertEquals(Integer.TYPE, Dyn.global["java.lang.Integer"]["TYPE"].value) + } +} diff --git a/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/dynamic/DynApiNative.kt b/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/dynamic/DynApiNative.kt new file mode 100644 index 00000000..037cf226 --- /dev/null +++ b/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/dynamic/DynApiNative.kt @@ -0,0 +1,7 @@ +package com.soywiz.korio.dynamic + +internal actual object DynamicInternal : DynApi { + override fun get(instance: Any?, key: String): Any? = throw UnsupportedOperationException("DynamicInternal.get") + override fun set(instance: Any?, key: String, value: Any?): Unit = throw UnsupportedOperationException("DynamicInternal.set") + override fun invoke(instance: Any?, key: String, args: Array): Any? = throw UnsupportedOperationException("DynamicInternal.invoke") +} diff --git a/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/dynamic/KDynamicNative.kt b/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/dynamic/KDynamicNative.kt deleted file mode 100644 index 13079ee9..00000000 --- a/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/dynamic/KDynamicNative.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.soywiz.korio.dynamic - -import com.soywiz.korio.lang.* - -internal actual object DynamicInternal { - actual val global: Any? = null - actual fun get(instance: Any?, key: String): Any? = unsupported("DynamicInternal.get") - actual fun set(instance: Any?, key: String, value: Any?): Unit = unsupported("DynamicInternal.set") - actual fun invoke(instance: Any?, key: String, args: Array): Any? = unsupported("DynamicInternal.invoke") -} diff --git a/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/file/std/LocalVfsNative.kt b/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/file/std/LocalVfsNative.kt index 7a5e6a85..523dbbae 100644 --- a/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/file/std/LocalVfsNative.kt +++ b/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/file/std/LocalVfsNative.kt @@ -10,6 +10,7 @@ import com.soywiz.korio.file.* import com.soywiz.korio.file.std.* import com.soywiz.korio.file.* import com.soywiz.korio.file.std.* +import com.soywiz.korio.internal.* import com.soywiz.korio.lang.* import com.soywiz.korio.net.* import com.soywiz.korio.net.http.* @@ -146,8 +147,8 @@ class LocalVfsNative : LocalVfsV2() { //val length = ftell(fd).toLong() // @TODO: Kotlin native bug? val length: Long = ftell(fd).convert() - val start = kotlin.math.min(range.start, length) - val end = kotlin.math.min(range.endInclusive, length - 1) + 1 + val start = min2(range.start, length) + val end = min2(range.endInclusive, length - 1) + 1 val totalRead = (end - start).toInt() //println("range=$range")