Move everything under kotlin-native folder

I was forced to manually do update the following files, because otherwise
they would be ignored according .gitignore settings. Probably they
should be deleted from repo.

Interop/.idea/compiler.xml
Interop/.idea/gradle.xml
Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_3.xml
Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_3.xml
Interop/.idea/modules.xml
Interop/.idea/modules/Indexer/Indexer.iml
Interop/.idea/modules/Runtime/Runtime.iml
Interop/.idea/modules/StubGenerator/StubGenerator.iml
backend.native/backend.native.iml
backend.native/bc.frontend/bc.frontend.iml
backend.native/cli.bc/cli.bc.iml
backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt
backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt
backend.native/tests/link/lib/foo.kt
backend.native/tests/link/lib/foo2.kt
backend.native/tests/teamcity-test.property
This commit is contained in:
Stanislav Erokhin
2020-10-27 21:00:28 +03:00
parent 91e4162dad
commit f624800b84
2830 changed files with 0 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
# Disclaimer: this is a straw-man attempt to add navigation to Clion or similar tools.
# At the moment it can't be used for real building or debugging. Feel free to contribute
cmake_minimum_required(VERSION 3.14)
project(runtime)
set(CMAKE_CXX_STANDARD 14)
set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
set(GOOGLETEST_DIR ${CMAKE_SOURCE_DIR}/googletest)
include_directories(${SRC_DIR}/main/cpp)
include_directories(${SRC_DIR}/debug/headers)
include_directories(${CMAKE_SOURCE_DIR}/../common/src/hash/headers)
include_directories(${GOOGLETEST_DIR}/googletest/include)
include_directories(${GOOGLETEST_DIR}/googlemock/include)
add_executable(runtime
src/main/cpp/Arrays.cpp
src/main/cpp/Atomic.cpp
src/main/cpp/Boxing.cpp
src/main/cpp/Console.cpp
src/main/cpp/CyclicCollector.cpp
src/main/cpp/Exceptions.cpp
src/main/cpp/ExecFormat.cpp
src/main/cpp/Interop.cpp
src/main/cpp/JSInterop.cpp
src/main/cpp/KAssert.cpp
src/main/cpp/KString.cpp
src/main/cpp/KotlinMath.cpp
src/main/cpp/Memory.cpp
src/main/cpp/MemorySharedRefs.cpp
src/main/cpp/Natives.cpp
src/main/cpp/ObjCExceptions.cpp
src/main/cpp/Operator.cpp
src/main/cpp/Porting.cpp
src/main/cpp/Regex.cpp
src/main/cpp/ReturnSlot.cpp
src/main/cpp/Runtime.cpp
src/main/cpp/StdCppStubs.cpp
src/main/cpp/Time.cpp
src/main/cpp/ToString.cpp
src/main/cpp/TypeInfo.cpp
src/main/cpp/Types.cpp
src/main/cpp/Weak.cpp
src/main/cpp/Worker.cpp
src/main/cpp/dlmalloc/malloc.cpp
src/main/cpp/dtoa/cbigint.cpp
src/main/cpp/dtoa/dblparse.cpp
src/main/cpp/dtoa/fltparse.cpp
src/main/cpp/math/fmod.cpp
src/main/cpp/math/fmodf.cpp
src/main/cpp/math/scalbn.cpp
src/main/cpp/snprintf/snprintf.cpp
src/profile_runtime/cpp/ProfileRuntime.cpp
src/relaxed/cpp/MemoryImpl.cpp
src/release/cpp/SourceInfo.cpp
src/strict/cpp/MemoryImpl.cpp
src/opt_alloc/cpp/AllocImpl.cpp
src/std_alloc/cpp/AllocImpl.cpp
src/debug/cpp/KDebug.cpp
src/debug/cpp/SourceInfo.cpp
src/exceptions_support/cpp/ExceptionsSupport.cpp
src/launcher/cpp/androidLauncher.cpp
src/launcher/cpp/launcher.cpp
src/main/cpp/ObjCExport.mm
src/main/cpp/ObjCInterop.mm
src/main/cpp/ObjCInteropUtils.mm
src/main/cpp/ObjCExportCollectionUtils.mm
src/main/cpp/ObjCExportErrors.mm
src/main/cpp/ObjCExportExceptionDetails.mm
src/objc/cpp/ObjCExportNumbers.mm
src/objc/cpp/ObjCExportClasses.mm
src/objc/cpp/ObjCExportCollections.mm
src/objc/cpp/ObjCInteropUtilsClasses.mm
# Tests
src/test_support/cpp/CompilerGenerated.cpp
src/test_support/cpp/CompilerGeneratedObjC.mm
src/test_support/cpp/TestLauncher.cpp
src/main/cpp/ArraysTest.cpp
)
target_compile_definitions(runtime PUBLIC "-DKONAN_OSX=1")
target_compile_definitions(runtime PUBLIC "-DKONAN_MACOSX=1")
target_compile_definitions(runtime PUBLIC "-DKONAN_X64=1")
target_compile_definitions(runtime PUBLIC "-DKONAN_OBJC_INTEROP")
target_compile_definitions(runtime PUBLIC "-DKONAN_CORE_SYMBOLICATION=1")
target_compile_definitions(runtime PUBLIC "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1")
target_compile_definitions(runtime PUBLIC "-DKONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1")
+218
View File
@@ -0,0 +1,218 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.testing.native.*
import org.jetbrains.kotlin.bitcode.CompileToBitcode
plugins {
id("compile-to-bitcode")
id("runtime-testing")
}
googletest {
revision = project.property("gtestRevision") as String
refresh = project.hasProperty("refresh-gtest")
}
fun CompileToBitcode.includeRuntime() {
headersDirs += files("../common/src/hash/headers", "src/main/cpp")
}
val hostName: String by project
val targetList: List<String> by project
bitcode {
create("runtime", file("src/main")) {
dependsOn(
":common:${target}Hash",
"${target}StdAlloc",
"${target}OptAlloc",
"${target}Mimalloc",
"${target}Launcher",
"${target}Debug",
"${target}Release",
"${target}Strict",
"${target}Relaxed",
"${target}ProfileRuntime",
"${target}Objc",
"${target}ExceptionsSupport",
"${target}LegacyMemoryManager",
"${target}ExperimentalMemoryManager"
)
includeRuntime()
linkerArgs.add(project.file("../common/build/bitcode/main/$target/hash.bc").path)
}
create("mimalloc") {
language = CompileToBitcode.Language.C
includeFiles = listOf("**/*.c")
excludeFiles += listOf("**/alloc-override*.c", "**/page-queue.c", "**/static.c", "**/bitmap.inc.c")
srcDirs = files("$srcRoot/c")
compilerArgs.addAll(listOf("-DKONAN_MI_MALLOC=1", "-Wno-unknown-pragmas", "-ftls-model=initial-exec",
"-Wno-unused-function", "-Wno-error=atomic-alignment",
"-Wno-unused-parameter" /* for windows 32*/))
headersDirs = files("$srcRoot/c/include")
onlyIf { targetSupportsMimallocAllocator(target) }
}
create("launcher") {
includeRuntime()
}
create("debug") {
includeRuntime()
}
create("std_alloc")
create("opt_alloc")
create("exceptionsSupport", file("src/exceptions_support")) {
includeRuntime()
}
create("release") {
includeRuntime()
}
create("strict") {
includeRuntime()
}
create("relaxed") {
includeRuntime()
}
create("profileRuntime", file("src/profile_runtime"))
create("objc") {
includeRuntime()
}
create("test_support", outputGroup = "test") {
includeRuntime()
dependsOn("downloadGoogleTest")
headersDirs += googletest.headersDirs
}
create("legacy_memory_manager", file("src/legacymm")) {
includeRuntime()
}
create("experimental_memory_manager", file("src/mm")) {
includeRuntime()
}
}
targetList.forEach { targetName ->
createTestTask(
project,
"StdAlloc",
"${targetName}StdAllocRuntimeTests",
listOf(
"${targetName}Runtime",
"${targetName}LegacyMemoryManager",
"${targetName}Strict",
"${targetName}Release",
"${targetName}StdAlloc"
)
) {
includeRuntime()
}
createTestTask(
project,
"Mimalloc",
"${targetName}MimallocRuntimeTests",
listOf(
"${targetName}Runtime",
"${targetName}LegacyMemoryManager",
"${targetName}Strict",
"${targetName}Release",
"${targetName}Mimalloc",
"${targetName}OptAlloc"
)
) {
includeRuntime()
}
createTestTask(
project,
"ExperimentalMM",
"${targetName}ExperimentalMMRuntimeTests",
listOf(
"${targetName}Runtime",
"${targetName}ExperimentalMemoryManager",
"${targetName}Release",
"${targetName}Mimalloc",
"${targetName}OptAlloc"
)
)
tasks.register("${targetName}RuntimeTests") {
dependsOn("${targetName}StdAllocRuntimeTests")
dependsOn("${targetName}MimallocRuntimeTests")
dependsOn("${targetName}ExperimentalMMRuntimeTests")
}
}
val hostRuntime by tasks.registering {
dependsOn("${hostName}Runtime")
}
val hostRuntimeTests by tasks.registering {
dependsOn("${hostName}RuntimeTests")
}
val hostStdAllocRuntimeTests by tasks.registering {
dependsOn("${hostName}StdAllocRuntimeTests")
}
val hostMimallocRuntimeTests by tasks.registering {
dependsOn("${hostName}MimallocRuntimeTests")
}
val hostExperimentalMMRuntimeTests by tasks.registering {
dependsOn("${hostName}ExperimentalMMRuntimeTests")
}
val assemble by tasks.registering {
dependsOn(tasks.withType(CompileToBitcode::class).matching {
it.outputGroup == "main"
})
}
val clean by tasks.registering {
doLast {
delete(buildDir)
}
}
val generateJsMath by tasks.registering {
dependsOn(":distCompiler")
doLast {
val distDir: File by project
val jsinteropScript = if (PlatformInfo.isWindows()) "jsinterop.bat" else "jsinterop"
val jsinterop = "$distDir/bin/$jsinteropScript"
val targetDir = "$buildDir/generated"
project.exec {
commandLine(
jsinterop,
"-pkg", "kotlinx.interop.wasm.math",
"-o", "$targetDir/math",
"-target", "wasm32"
)
}
val generated = file("$targetDir/math-build/natives/js_stubs.js")
val mathJs = file("src/main/js/math.js")
mathJs.writeText(
"// NOTE: THIS FILE IS AUTO-GENERATED!\n" +
"// Run ':runtime:generateJsMath' to re-generate it.\n\n"
)
mathJs.appendText(generated.readText())
}
}
@@ -0,0 +1,20 @@
configurations {
generatorRuntime
}
repositories {
maven { url buildKotlinCompilerRepo }
}
dependencies {
generatorRuntime "org.jetbrains.kotlin:kotlin-stdlib:${kotlinStdlibVersion}"
generatorRuntime "org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}"
generatorRuntime "org.jetbrains.kotlin:kotlin-stdlib-gen:${kotlinStdlibVersion}"
}
task run(type: JavaExec) {
group 'application'
main 'generators.GenerateStandardLibKt'
classpath configurations.generatorRuntime
args = ["native", "${project(":runtime").projectDir}/src/main/kotlin/generated"]
}
@@ -0,0 +1,319 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KDebug.h"
#include <string.h>
#include "KAssert.h"
#include "KString.h"
#include "Memory.h"
#include "Natives.h"
#include "Porting.h"
#include "Types.h"
#ifndef KONAN_NO_DEBUG_API
extern "C" OBJ_GETTER(KonanObjectToUtf8Array, KRef object);
namespace {
char debugBuffer[4096];
constexpr int runtimeTypeSize[] = {
-1, // INVALID
sizeof(ObjHeader*), // OBJECT
1, // INT8
2, // INT16
4, // INT32
8, // INT64
4, // FLOAT32
8, // FLOAT64
sizeof(void*), // NATIVE_PTR
1, // BOOLEAN
16 // VECTOR128
};
constexpr int runtimeTypeAlignment[] = {
-1, // INVALID
alignof(ObjHeader*), // OBJECT
alignof(int8_t), // INT8
alignof(int16_t), // INT16
alignof(int32_t), // INT32
alignof(int64_t), // INT64
alignof(float), // FLOAT32
alignof(double), // FLOAT64
alignof(void*), // NATIVE_PTR
1, // BOOLEAN
16 // VECTOR128
};
// Never ever change numbering in this enum, as it will break debugging of older binaries.
enum Konan_DebugOperation {
DO_DebugBuffer = 1,
DO_DebugBufferSize = 2,
DO_DebugBufferWithObject = 3,
DO_DebugBufferSizeWithObject = 4,
DO_DebugObjectToUtf8Array = 5,
DO_DebugPrint = 6,
DO_DebugIsArray = 7,
DO_DebugGetFieldCount = 8,
DO_DebugGetFieldType = 9,
DO_DebugGetFieldAddress = 10,
DO_DebugGetFieldName = 11,
DO_DebugGetTypeName = 12,
};
template <typename F>
F getImpl(KRef obj, Konan_DebugOperation operation) {
if (obj == nullptr)
return nullptr;
auto* typeInfo = obj->type_info();
auto* extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return nullptr;
if (static_cast<int32_t>(operation) >= extendedTypeInfo->debugOperationsCount_)
return nullptr;
return reinterpret_cast<F>(extendedTypeInfo->debugOperations_[operation]);
}
// Buffer that can be used by debugger for inspections.
char* Konan_DebugBufferImpl() {
return debugBuffer;
}
int Konan_DebugBufferSizeImpl() {
return sizeof(debugBuffer);
}
char* Konan_DebugBufferWithObjectImpl(KRef obj) {
return debugBuffer;
}
int Konan_DebugBufferSizeWithObjectImpl(KRef obj) {
return sizeof(debugBuffer);
}
// Auxilary function which can be called by developer/debugger to inspect an object.
int32_t Konan_DebugObjectToUtf8ArrayImpl(KRef obj, char* buffer, int32_t bufferSize) {
ObjHolder stringHolder;
auto data = KonanObjectToUtf8Array(obj, stringHolder.slot())->array();
if (data == nullptr) return 0;
if (bufferSize < 1) return 0;
KInt toCopy = data->count_ > static_cast<uint32_t>(bufferSize - 1) ? bufferSize - 1 : data->count_;
::memcpy(buffer, ByteArrayAddressOfElementAt(data, 0), toCopy);
buffer[toCopy] = '\0';
return toCopy + 1;
}
int32_t Konan_DebugPrintImpl(KRef obj) {
int32_t size = Konan_DebugObjectToUtf8Array(obj, Konan_DebugBuffer(), Konan_DebugBufferSize());
if (size > 1)
konan::consoleWriteUtf8(Konan_DebugBuffer(), size - 1);
return 0;
}
int32_t Konan_DebugIsArrayImpl(KRef obj) {
return obj == nullptr || IsArray(obj) ? 1 : 0;
}
int32_t Konan_DebugGetFieldCountImpl(KRef obj) {
if (obj == nullptr)
return 0;
auto* typeInfo = obj->type_info();
auto* extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return 0;
if (IsArray(obj))
return obj->array()->count_;
return extendedTypeInfo->fieldsCount_;
}
int32_t Konan_DebugGetFieldTypeImpl(KRef obj, int32_t index) {
if (obj == nullptr || index < 0)
return Konan_RuntimeType::RT_INVALID;
auto typeInfo = obj->type_info();
auto extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return Konan_RuntimeType::RT_INVALID;
if (extendedTypeInfo->fieldsCount_ < 0)
return -extendedTypeInfo->fieldsCount_;
if (index >= extendedTypeInfo->fieldsCount_)
return Konan_RuntimeType::RT_INVALID;
return extendedTypeInfo->fieldTypes_[index];
}
void* Konan_DebugGetFieldAddressImpl(KRef obj, int32_t index) {
if (obj == nullptr || index < 0)
return nullptr;
auto typeInfo = obj->type_info();
auto extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return nullptr;
if (extendedTypeInfo->fieldsCount_ < 0) {
if (static_cast<uint32_t>(index) > obj->array()->count_)
return nullptr;
int32_t typeIndex = -extendedTypeInfo->fieldsCount_;
return reinterpret_cast<uint8_t*>(obj->array())
+ alignUp(sizeof(struct ArrayHeader), runtimeTypeAlignment[typeIndex])
+ index * runtimeTypeSize[typeIndex];
}
if (index >= extendedTypeInfo->fieldsCount_)
return nullptr;
return reinterpret_cast<uint8_t*>(obj) + extendedTypeInfo->fieldOffsets_[index];
}
// Compute address of field or an array element at the index, or null, if incorrect.
const char* Konan_DebugGetFieldNameImpl(KRef obj, int32_t index) {
if (obj == nullptr || index < 0)
return nullptr;
auto typeInfo = obj->type_info();
auto extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return nullptr;
// For arrays, field name makes not much sense.
if (extendedTypeInfo->fieldsCount_ < 0)
return "";
if (index >= extendedTypeInfo->fieldsCount_)
return nullptr;
return extendedTypeInfo->fieldNames_[index];
}
const char* Konan_DebugGetTypeNameImpl(KRef obj) {
if (obj == nullptr)
return nullptr;
auto type_info = obj->type_info();
if (type_info == nullptr)
return "<unknown>";
return CreateCStringFromString(type_info->relativeName_);
}
} // namespace
extern "C" {
RUNTIME_USED RUNTIME_WEAK char* Konan_DebugBuffer() {
return Konan_DebugBufferImpl();
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugBufferSize() {
return Konan_DebugBufferSizeImpl();
}
RUNTIME_USED RUNTIME_WEAK char* Konan_DebugBufferWithObject(KRef obj) {
auto* impl = getImpl<char* (*)(KRef)>(obj, DO_DebugBufferWithObject);
if (impl == nullptr) return nullptr;
return impl(obj);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugBufferSizeWithObject(KRef obj) {
auto* impl = getImpl<int32_t (*)(KRef)>(obj, DO_DebugBufferSizeWithObject);
if (impl == nullptr) return 0;
return impl(obj);
}
// Auxilary function which can be called by developer/debugger to inspect an object.
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, int32_t bufferSize) {
auto* impl = getImpl<int32_t (*)(KRef, char*, int32_t)>(obj, DO_DebugObjectToUtf8Array);
if (impl == nullptr) return 0;
return impl(obj, buffer, bufferSize);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugPrint(KRef obj) {
auto* impl = getImpl<int32_t (*)(KRef)>(obj, DO_DebugPrint);
if (impl == nullptr) return 0;
return impl(obj);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugIsArray(KRef obj) {
auto* impl = getImpl<int32_t (*)(KRef)>(obj, DO_DebugIsArray);
if (impl == nullptr) return 0;
return impl(obj);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugGetFieldCount(KRef obj) {
auto* impl = getImpl<int32_t (*)(KRef)>(obj, DO_DebugGetFieldCount);
if (impl == nullptr) return 0;
return impl(obj);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugGetFieldType(KRef obj, int32_t index) {
auto* impl = getImpl<int32_t (*)(KRef, int32_t)>(obj, DO_DebugGetFieldType);
if (impl == nullptr) return 0;
return impl(obj, index);
}
RUNTIME_USED RUNTIME_WEAK void* Konan_DebugGetFieldAddress(KRef obj, int32_t index) {
auto* impl = getImpl<void* (*)(KRef, int32_t)>(obj, DO_DebugGetFieldAddress);
if (impl == nullptr) return nullptr;
return impl(obj, index);
}
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED RUNTIME_WEAK const char* Konan_DebugGetFieldName(KRef obj, int32_t index) {
auto* impl = getImpl<const char* (*)(KRef, int32_t)>(obj, DO_DebugGetFieldName);
if (impl == nullptr) return nullptr;
return impl(obj, index);
}
RUNTIME_USED RUNTIME_WEAK const char* Konan_DebugGetTypeName(KRef obj) {
auto* impl = getImpl<const char* (*)(KRef)>(obj, DO_DebugGetTypeName);
if (impl == nullptr) return nullptr;
return impl(obj);
}
const void* Konan_debugOperationsList[] = {
nullptr,
reinterpret_cast<const void*>(&Konan_DebugBufferImpl),
reinterpret_cast<const void*>(&Konan_DebugBufferSizeImpl),
reinterpret_cast<const void*>(&Konan_DebugBufferWithObjectImpl),
reinterpret_cast<const void*>(&Konan_DebugBufferSizeWithObjectImpl),
reinterpret_cast<const void*>(&Konan_DebugObjectToUtf8ArrayImpl),
reinterpret_cast<const void*>(&Konan_DebugPrintImpl),
reinterpret_cast<const void*>(&Konan_DebugIsArrayImpl),
reinterpret_cast<const void*>(&Konan_DebugGetFieldCountImpl),
reinterpret_cast<const void*>(&Konan_DebugGetFieldTypeImpl),
reinterpret_cast<const void*>(&Konan_DebugGetFieldAddressImpl),
reinterpret_cast<const void*>(&Konan_DebugGetFieldNameImpl),
reinterpret_cast<const void*>(&Konan_DebugGetTypeNameImpl)
};
} // extern "C"
#endif // !KONAN_NO_DEBUG_API
@@ -0,0 +1,213 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SourceInfo.h"
#ifdef KONAN_CORE_SYMBOLICATION
#include <KAssert.h>
#include <cstdint>
#include <dlfcn.h>
#include <limits>
#include <string.h>
#include <unistd.h>
#define TRACE_SYMBOLICATION 0
#if TRACE_SYMBOLICATION
#include <stdio.h>
#define SYM_LOG(...) fprintf(stderr, __VA_ARGS__)
#define SYM_DUMP(p) CSShow((p))
#else
#define SYM_LOG(...)
#define SYM_DUMP(p)
#endif
typedef struct _CSTypeRef {
unsigned long type;
void* contents;
} CSTypeRef;
typedef CSTypeRef CSSymbolicatorRef;
typedef CSTypeRef CSSymbolOwnerRef;
typedef CSTypeRef CSSymbolRef;
typedef CSTypeRef CSSourceInfoRef;
typedef struct _CSRange {
unsigned long long location;
unsigned long long length;
} CSRange;
typedef unsigned long long CSArchitecture;
constexpr auto kCSNow = std::numeric_limits<long long>::max();
namespace {
CSSymbolicatorRef (*CSSymbolicatorCreateWithPid)(pid_t pid);
CSSymbolOwnerRef (*CSSymbolicatorGetSymbolOwnerWithAddressAtTime)(
CSSymbolicatorRef symbolicator,
unsigned long long address,
long long time
);
CSSourceInfoRef (*CSSymbolOwnerGetSourceInfoWithAddress)(
CSSymbolOwnerRef owner,
unsigned long long address
);
const char* (*CSSourceInfoGetPath)(CSSourceInfoRef info);
uint32_t (*CSSourceInfoGetLineNumber)(CSSourceInfoRef info);
uint32_t (*CSSourceInfoGetColumn)(CSSourceInfoRef info);
bool (*CSIsNull)(CSTypeRef);
CSSymbolRef (*CSSourceInfoGetSymbol)(CSSourceInfoRef info);
typedef int (^CSSourceInfoIterator)(CSSourceInfoRef);
int (*CSSymbolForeachSourceInfo)(CSSymbolRef, CSSourceInfoIterator);
CSRange (*CSSourceInfoGetRange)(CSSourceInfoRef);
CSSymbolRef (*CSSymbolOwnerGetSymbolWithAddress)(CSSymbolOwnerRef, unsigned long long);
CSSymbolicatorRef symbolicator;
/**
* Function used for debug.
*/
#if TRACE_SYMBOLICATION
void (*CSShow)(CSTypeRef);
#endif
bool TryInitializeCoreSymbolication() {
void* cs = dlopen("/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication", RTLD_LAZY);
if (!cs) return false;
#define KONAN_CS_LOOKUP(name) name = (decltype(name)) dlsym(cs, #name); if (!name) return false;
KONAN_CS_LOOKUP(CSSymbolicatorCreateWithPid)
KONAN_CS_LOOKUP(CSSymbolicatorGetSymbolOwnerWithAddressAtTime)
KONAN_CS_LOOKUP(CSSymbolOwnerGetSourceInfoWithAddress)
KONAN_CS_LOOKUP(CSSourceInfoGetPath)
KONAN_CS_LOOKUP(CSSourceInfoGetLineNumber)
KONAN_CS_LOOKUP(CSSourceInfoGetColumn)
KONAN_CS_LOOKUP(CSIsNull)
KONAN_CS_LOOKUP(CSSourceInfoGetSymbol)
KONAN_CS_LOOKUP(CSSymbolForeachSourceInfo)
KONAN_CS_LOOKUP(CSSymbolOwnerGetSymbolWithAddress)
KONAN_CS_LOOKUP(CSSourceInfoGetRange)
#if TRACE_SYMBOLICATION
KONAN_CS_LOOKUP(CSShow)
#endif
#undef KONAN_CS_LOOKUP
symbolicator = CSSymbolicatorCreateWithPid(getpid());
return !CSIsNull(symbolicator);
}
} // namespace
typedef struct {
const char * fileName;
int start;
int end;
} SymbolSourceInfoLimits;
extern "C" struct SourceInfo Kotlin_getSourceInfo(void* addr) {
__block SourceInfo result = { .fileName = nullptr, .lineNumber = -1, .column = -1 };
__block bool continueUpdateResult = true;
__block SymbolSourceInfoLimits limits = {.start = -1, .end = -1};
static bool csIsAvailable = TryInitializeCoreSymbolication();
if (csIsAvailable) {
unsigned long long address = static_cast<unsigned long long>((uintptr_t)addr);
CSSymbolOwnerRef symbolOwner = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, address, kCSNow);
if (CSIsNull(symbolOwner))
return result;
CSSymbolRef symbol = CSSymbolOwnerGetSymbolWithAddress(symbolOwner, address);
if (CSIsNull(symbol))
return result;
SYM_LOG("Kotlin_getSourceInfo: address: %p\n", addr);
SYM_DUMP(symbol);
/**
* ASSUMPTION: we assume that the _first_ and the _last_ source infos should belong to real function(symbol) the rest might belong to
* inlined functions.
*/
CSSymbolForeachSourceInfo(symbol,
^(CSSourceInfoRef ref) {
// Expecting CSSourceInfoGetLineNumber not to overflow int32_t max value.
int32_t lineNumber = CSSourceInfoGetLineNumber(ref);
if (lineNumber == 0)
return 0;
if (limits.start == -1) {
limits.start = lineNumber;
limits.fileName = CSSourceInfoGetPath(ref);
} else {
limits.end = lineNumber;
}
return 0;
});
SYM_LOG("limits: {%s %d..%d}\n", limits.fileName, limits.start, limits.end);
result.fileName = limits.fileName;
CSSymbolForeachSourceInfo(symbol,
^(CSSourceInfoRef ref) {
// Expecting CSSourceInfoGetLineNumber not to overflow int32_t max value.
int32_t lineNumber = CSSourceInfoGetLineNumber(ref);
if (lineNumber == 0)
return 0;
SYM_DUMP(ref);
CSRange range = CSSourceInfoGetRange(ref);
const char* fileName = CSSourceInfoGetPath(ref);
/**
* We need to change API fo Kotlin_getSourceInfo to return information about inlines,
* but for a moment we have to track that we updating result info _only_ for upper level or _inlined at_ and
* don't go deeper. at deeper level we check only that we at the right _inlined at_ position.
*/
if (continueUpdateResult
&& strcmp(limits.fileName, fileName) == 0
&& lineNumber >= limits.start
&& lineNumber <= limits.end) {
result.lineNumber = lineNumber;
result.column = CSSourceInfoGetColumn(ref);
}
/**
* if found right inlined function don't bother with
* updating high level inlined _at_ source info
*/
if (continueUpdateResult && (address >= range.location
&& address < range.location + range.length))
continueUpdateResult = false;
return 0;
});
}
return result;
}
#else // KONAN_CORE_SYMBOLICATION
extern "C" struct SourceInfo Kotlin_getSourceInfo(void* addr) {
return (SourceInfo) { .fileName = nullptr, .lineNumber = -1, .column = -1 };
}
#endif // KONAN_CORE_SYMBOLICATION
@@ -0,0 +1,15 @@
#include "Memory.h"
#include "Porting.h"
#include <typeinfo>
#ifndef KONAN_NO_EXCEPTIONS
std::type_info const* ExceptionObjHolderRTTI;
// Just some DCE-surviving code referencing RTTI of ExceptionObjHolder.
// This is needed during compilation to cache.
void referenceExceptionObjHolderRTTI() {
ExceptionObjHolderRTTI = &typeid(ExceptionObjHolder);
}
#endif
@@ -0,0 +1,240 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KString.h"
#include "Memory.h"
#include "Natives.h"
#include "Porting.h"
#include "Runtime.h"
#include "Types.h"
#ifdef KONAN_ANDROID
#include <unistd.h>
#include <pthread.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "androidLauncher.h"
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "Konan_main", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "Konan_main", __VA_ARGS__))
/* For debug builds, always enable the debug traces in this library */
#ifndef NDEBUG
# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "Konan_main", __VA_ARGS__))
#else
# define LOGV(...) ((void)0)
#endif
//--- main --------------------------------------------------------------------//
extern "C" KInt Konan_start(const ObjHeader*);
namespace {
typedef struct {
int pipeC;
int pipeKonan;
NativeActivityState nativeActivityState;
} LauncherState;
LauncherState* launcherState = nullptr;
}
extern "C" void getNativeActivityState(NativeActivityState* state) {
state->activity = launcherState->nativeActivityState.activity;
state->savedState = launcherState->nativeActivityState.savedState;
state->savedStateSize = launcherState->nativeActivityState.savedStateSize;
state->looper = launcherState->nativeActivityState.looper;
}
extern "C" void notifySysEventProcessed() {
int8_t message;
write(launcherState->pipeKonan, &message, sizeof(message));
}
namespace {
void launchMain() {
Kotlin_initRuntimeIfNeeded();
{
ObjHolder args;
AllocArrayInstance(theArrayTypeInfo, 0, args.slot());
Konan_start(args.obj());
}
Kotlin_deinitRuntimeIfNeeded();
}
void* entry(void* param) {
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, launcherState->pipeKonan, LOOPER_ID_SYS, ALOOPER_EVENT_INPUT, NULL, NULL);
launcherState->nativeActivityState.looper = looper;
launchMain();
return nullptr;
}
void runKonan_start(bool startThread) {
if (!startThread) {
launchMain();
return;
}
int pipes[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes)) {
LOGE("Could not create pipe: %s", strerror(errno));
return;
}
launcherState->pipeC = pipes[0];
launcherState->pipeKonan = pipes[1];
LOGE("runKonan_start() %d %d", launcherState->pipeC, launcherState->pipeKonan);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t thread;
pthread_create(&thread, &attr, entry, nullptr);
}
void putEventSynchronously(void* event) {
auto value = reinterpret_cast<uintptr_t>(event);
if (write(launcherState->pipeC, &value, sizeof(value)) != sizeof(value)) {
LOGE("Failure writing event: %s\n", strerror(errno));
}
int8_t response;
if (read(launcherState->pipeC, &response, sizeof(response)) != sizeof(response)) {
LOGE("Failure reading response: %s\n", strerror(errno));
}
}
void onDestroy(ANativeActivity* activity) {
LOGV("onDestroy called");
NativeActivityEvent event = { DESTROY };
putEventSynchronously(&event);
}
void onStart(ANativeActivity* activity) {
LOGV("onStart called");
NativeActivityEvent event = { START };
putEventSynchronously(&event);
}
void onResume(ANativeActivity* activity) {
LOGV("onResume called");
NativeActivitySaveStateEvent event = { RESUME, nullptr, 0 };
putEventSynchronously(&event);
}
void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
LOGV("onSaveInstanceState called");
NativeActivitySaveStateEvent event = { SAVE_INSTANCE_STATE, nullptr, 0 };
putEventSynchronously(&event);
*outLen = event.savedStateSize;
return event.savedState;
}
void onPause(ANativeActivity* activity) {
LOGV("onPause called");
NativeActivityEvent event = { PAUSE };
putEventSynchronously(&event);
}
void onStop(ANativeActivity* activity) {
LOGV("onStop called");
NativeActivityEvent event = { STOP };
putEventSynchronously(&event);
}
void onConfigurationChanged(ANativeActivity* activity) {
LOGV("onConfigurationChanged called");
NativeActivityEvent event = { CONFIGURATION_CHANGED };
putEventSynchronously(&event);
}
void onLowMemory(ANativeActivity* activity) {
LOGV("onLowMemory called");
NativeActivityEvent event = { LOW_MEMORY };
putEventSynchronously(&event);
}
void onWindowFocusChanged(ANativeActivity* activity, int focused) {
LOGV("onWindowFocusChanged called");
NativeActivityEvent event = { focused ? WINDOW_GAINED_FOCUS : WINDOW_LOST_FOCUS };
putEventSynchronously(&event);
}
void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
LOGV("onNativeWindowCreated called");
NativeActivityWindowEvent event = { NATIVE_WINDOW_CREATED, window };
putEventSynchronously(&event);
}
void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
LOGV("onNativeWindowDestroyed called");
NativeActivityWindowEvent event = { NATIVE_WINDOW_DESTROYED, window };
putEventSynchronously(&event);
}
void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
LOGV("onInputQueueCreated called");
NativeActivityQueueEvent event = { INPUT_QUEUE_CREATED, queue };
putEventSynchronously(&event);
}
void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
LOGV("onInputQueueDestroyed called");
NativeActivityQueueEvent event = { INPUT_QUEUE_DESTROYED, queue };
putEventSynchronously(&event);
}
}
extern "C" void RUNTIME_USED Konan_main(
ANativeActivity* activity, void* savedState, size_t savedStateSize) {
bool launchThread = activity->instance == nullptr;
if (launchThread) {
launcherState = (LauncherState*)konan::calloc(sizeof(LauncherState), 1);
launcherState->nativeActivityState = {activity, savedState, savedStateSize, nullptr};
activity->instance = launcherState;
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
} else {
launcherState = (LauncherState*)activity->instance;
}
runKonan_start(launchThread);
}
#endif // KONAN_ANDROID
@@ -0,0 +1,85 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_LAUNCHER_H
#define ANDROID_LAUNCHER_H
#include <unistd.h>
#include <android/configuration.h>
#include <android/looper.h>
#include <android/native_activity.h>
#ifdef __cplusplus
extern "C" {
#endif
struct NativeActivityState {
struct ANativeActivity* activity;
void* savedState;
size_t savedStateSize;
struct ALooper* looper;
};
void getNativeActivityState(struct NativeActivityState* state);
void notifySysEventProcessed();
#define LOOPER_ID_SYS 1
typedef enum NativeActivityEventKind {
UNKNOWN,
DESTROY,
START,
RESUME,
SAVE_INSTANCE_STATE,
PAUSE,
STOP,
CONFIGURATION_CHANGED,
LOW_MEMORY,
WINDOW_GAINED_FOCUS,
WINDOW_LOST_FOCUS,
NATIVE_WINDOW_CREATED,
NATIVE_WINDOW_DESTROYED,
INPUT_QUEUE_CREATED,
INPUT_QUEUE_DESTROYED
} NativeActivityEventKind;
struct NativeActivityEvent {
NativeActivityEventKind eventKind;
};
struct NativeActivitySaveStateEvent {
NativeActivityEventKind eventKind;
void* savedState;
size_t savedStateSize;
};
struct NativeActivityWindowEvent {
NativeActivityEventKind eventKind;
struct ANativeWindow* window;
};
struct NativeActivityQueueEvent {
NativeActivityEventKind eventKind;
struct AInputQueue* queue;
};
#ifdef __cplusplus
}
#endif
#endif // ANDROID_LAUNCHER_H
@@ -0,0 +1,98 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Cleaner.h"
#include "Memory.h"
#include "Natives.h"
#include "Runtime.h"
#include "KString.h"
#include "Types.h"
#include "Worker.h"
#ifndef KONAN_ANDROID
//--- Setup args --------------------------------------------------------------//
OBJ_GETTER(setupArgs, int argc, const char** argv) {
// The count is one less, because we skip argv[0] which is the binary name.
ObjHeader* result = AllocArrayInstance(theArrayTypeInfo, argc - 1, OBJ_RESULT);
ArrayHeader* array = result->array();
for (int index = 1; index < argc; index++) {
ObjHolder result;
CreateStringFromCString(argv[index], result.slot());
UpdateHeapRef(ArrayAddressOfElementAt(array, index - 1), result.obj());
}
return result;
}
//--- main --------------------------------------------------------------------//
extern "C" KInt Konan_start(const ObjHeader*);
extern "C" KInt Konan_run_start(int argc, const char** argv) {
ObjHolder args;
setupArgs(argc, argv, args.slot());
return Konan_start(args.obj());
}
extern "C" RUNTIME_USED int Init_and_run_start(int argc, const char** argv, int memoryDeInit) {
#ifdef KONAN_NO_CTORS_SECTION
extern void _Konan_constructors(void);
_Konan_constructors();
#endif
Kotlin_initRuntimeIfNeeded();
KInt exitStatus = Konan_run_start(argc, argv);
if (memoryDeInit) {
if (Kotlin_cleanersLeakCheckerEnabled()) {
// Make sure to collect any lingering cleaners.
PerformFullGC();
// Execute all the cleaner blocks and stop the Cleaner worker.
ShutdownCleaners(true);
} else {
// Stop the cleaner worker without executing remaining cleaner blocks.
ShutdownCleaners(false);
}
if (Kotlin_memoryLeakCheckerEnabled()) WaitNativeWorkersTermination();
Kotlin_deinitRuntimeIfNeeded();
}
return exitStatus;
}
extern "C" RUNTIME_USED int Konan_main(int argc, const char** argv) {
return Init_and_run_start(argc, argv, 1);
}
#ifdef KONAN_WASM
// Before we pass control to Konan_main, we need to obtain argv elements
// from the javascript world.
extern "C" int Konan_js_arg_size(int index);
extern "C" int Konan_js_fetch_arg(int index, char* ptr);
extern "C" RUNTIME_USED int Konan_js_main(int argc, int memoryDeInit) {
char** argv = (char**)konan::calloc(1, argc);
for (int i = 0; i< argc; ++i) {
argv[i] = (char*)konan::calloc(1, Konan_js_arg_size(i));
Konan_js_fetch_arg(i, argv[i]);
}
return Init_and_run_start(argc, (const char**)argv, memoryDeInit);
}
#endif
#endif
@@ -0,0 +1,14 @@
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="/dist/konan/nativelib/launcher.js"> </script>
<script>
loadAndInvoke("/program.wasm");
</script>
</body>
</html>
@@ -0,0 +1,247 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let instance;
let heap;
let global_arguments;
function isBrowser() {
return typeof self !== 'undefined';
}
let runtime;
if (isBrowser()) {
runtime = {
print: console.log,
stdout: '',
write: function (message) {
this.stdout += message;
const lastNewlineIndex = this.stdout.lastIndexOf('\n');
if (lastNewlineIndex == -1) return;
this.print(this.stdout.substring(0, lastNewlineIndex));
this.stdout = this.stdout.substring(lastNewlineIndex + 1)
},
flush: function () {
this.print(this.stdout);
},
exit: function (status) {
throw Error("Kotlin process called exit (" + status + ")");
}
};
} else {
runtime = {
write: write,
print: print,
flush: function () {
},
exit: quit
};
}
function print_usage() {
// TODO: any reliable way to obtain the current script name?
runtime.print('Usage: d8 --expose-wasm launcher.js -- <program.wasm> <program arg1> <program arg2> ...')
quit(1); // TODO: this is d8 specific
}
function utf8encode(s) {
return unescape(encodeURIComponent(s));
}
function utf8decode(s) {
return decodeURIComponent(escape(s));
}
function fromString(string, pointer) {
for (let i = 0; i < string.length; i++) {
heap[pointer + i] = string.charCodeAt(i);
}
heap[pointer + string.length] = 0;
}
function toString(pointer) {
let string = '';
for (let i = pointer; heap[i] != 0; i++) {
string += String.fromCharCode(heap[i]);
}
return string;
}
function toUTF16String(pointer, size) {
let string = '';
for (let i = pointer; i < pointer + size; i += 2) {
string += String.fromCharCode(heap[i] + heap[i + 1] * 256);
}
return string;
}
function twoIntsToDouble(upper, lower) {
const buffer = new ArrayBuffer(8);
const ints = new Int32Array(buffer);
const doubles = new Float64Array(buffer);
ints[1] = upper;
ints[0] = lower;
return doubles[0];
}
function doubleToTwoInts(value) {
const buffer = new ArrayBuffer(8);
const ints = new Int32Array(buffer);
const doubles = new Float64Array(buffer);
doubles[0] = value;
return {upper: ints[1], lower: ints[0]}
}
function int32ToHeap(value, pointer) {
heap[pointer] = value & 0xff;
heap[pointer + 1] = (value & 0xff00) >>> 8;
heap[pointer + 2] = (value & 0xff0000) >>> 16;
heap[pointer + 3] = (value & 0xff000000) >>> 24;
}
function doubleToReturnSlot(value) {
const twoInts = doubleToTwoInts(value);
instance.exports.ReturnSlot_setDouble(twoInts.upper, twoInts.lower);
}
let konan_dependencies = {
env: {
abort: function () {
throw new Error("abort()");
},
// TODO: Account for file and size.
fgets: function (str, size, file) {
// TODO: readline can't read lines without a newline.
// Browsers cant read from console at all.
fromString(utf8encode(readline() + '\n'), str);
return str;
},
read: function (file, str, size) {
let string = utf8encode(readline() + '\n');
fromString(string.substring(0, size), str);
return string.length;
},
Konan_notify_memory_grow: function() {
heap = new Uint8Array(instance.exports.memory.buffer);
},
Konan_abort: function (pointer) {
throw new Error("Konan_abort(" + utf8decode(toString(pointer)) + ")");
},
Konan_exit: function (status) {
runtime.exit(status);
},
Konan_js_arg_size: function (index) {
if (index >= global_arguments.length) return -1;
return global_arguments[index].length + 1; // + 1 for trailing zero.
},
Konan_js_fetch_arg: function (index, ptr) {
let arg = utf8encode(global_arguments[index]);
fromString(arg, ptr);
},
Konan_date_now: function (pointer) {
let now = Date.now();
let high = Math.floor(now / 0xffffffff);
let low = Math.floor(now % 0xffffffff);
int32ToHeap(low, pointer);
int32ToHeap(high, pointer + 4);
},
// TODO: Account for fd and size.
write: function (fd, str, size) {
if (fd != 1 && fd != 2) throw ("write(" + fd + ", ...)");
// TODO: There is no writeErr() in d8.
// Approximate it with write() to stdout for now.
runtime.write(utf8decode(toString(str)));
},
fflush: function(file) {
runtime.flush();
}
}
};
function linkJavaScriptLibraries() {
konan.libraries.forEach(function (library) {
for (const property in library) {
konan_dependencies.env[property] = library[property];
}
});
}
function invokeModule(inst, args) {
if (args.length < 1) print_usage();
global_arguments = args;
instance = inst;
heap = new Uint8Array(instance.exports.memory.buffer);
let exit_status = 0;
try {
exit_status = instance.exports.Konan_js_main(args.length, isBrowser() ? 0 : 1);
} catch (e) {
runtime.print("Exception executing entry point: " + e);
runtime.print(e.stack);
exit_status = 1;
}
runtime.flush();
return exit_status;
}
// Instantiate module in Browser.
function instantiateAndRun(arraybuffer, args) {
linkJavaScriptLibraries();
WebAssembly.instantiate(arraybuffer, konan_dependencies)
.then(resultObject => invokeModule(resultObject.instance, args));
}
// Instantiate module in d8 synchronously.
function instantiateAndRunSync(arraybuffer, args) {
const module = new WebAssembly.Module(arraybuffer);
linkJavaScriptLibraries();
const instance = new WebAssembly.Instance(module, konan_dependencies);
return invokeModule(instance, args)
}
// Instantiate module in Browser using streaming instantiation.
function instantiateAndRunStreaming(filename) {
linkJavaScriptLibraries();
WebAssembly.instantiateStreaming(fetch(filename), konan_dependencies)
.then(resultObject => invokeModule(resultObject.instance, [filename]));
}
konan.moduleEntry = function (args) {
if (isBrowser()) {
if (!document.currentScript.hasAttribute("wasm")) {
throw new Error('Could not find the wasm attribute pointing to the WebAssembly binary.');
}
const filename = document.currentScript.getAttribute("wasm");
if (typeof WebAssembly.instantiateStreaming === 'function') {
instantiateAndRunStreaming(filename);
} else {
fetch(filename)
.then(response => response.arrayBuffer())
.then(arraybuffer => instantiateAndRun(arraybuffer, [filename]));
}
} else {
// Invoke from d8.
const arrayBuffer = readbuffer(args[0]);
const exitStatus = instantiateAndRunSync(arrayBuffer, args);
quit(exitStatus);
}
};
@@ -0,0 +1,528 @@
/*
* Copyright 2010-2020 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KONAN_NO_THREADS
#define WITH_WORKERS 1
#endif
#include "Alloc.h"
#include "Atomic.h"
#include "KAssert.h"
#include "Memory.h"
#include "MemoryPrivate.hpp"
#include "Natives.h"
#include "Porting.h"
#include "Types.h"
#if WITH_WORKERS
#include <pthread.h>
#include "PthreadUtils.h"
#endif
#if WITH_WORKERS
// Define to 1 to print collector traces.
#define TRACE_COLLECTOR 0
#if TRACE_COLLECTOR
#define COLLECTOR_LOG(...) konan::consolePrintf(__VA_ARGS__);
#else
#define COLLECTOR_LOG(...)
#endif
/**
* Theory of operations:
*
* Kotlin/Native runtime has concurrent cyclic garbage collection for the shared mutable objects,
* such as `AtomicReference` and `FreezableAtomicReference` instances (further known as the atomic rootset).
* We perform such analysis by iterating over the transitive closure of the atomic rootset, and computing
* aggregated inner reference counter for rootset elements over this transitive closure.
* Collector runs in its own thread and is started by an explicit request or after certain time interval since last
* collection passes, thus its operation does not affect UI responsiveness in most cases.
* Atomic rootset is built by maintaining the set of all atomic and freezable atomic references objects.
* Elements whose transitive closure inner reference count matches the actual reference count are ones
* belonging to the garbage cycles and thus can be discarded.
* We ignore elements reachable from objects having external references (i.e. inner rc != real rc).
* If during computations of the aggregated RC there were modifications in the reference counts of
* elements of the atomic rootset:
* - if it is being increased, then someone already got an external reference to this element, thus we may not
* end up matching the inner reference count anyway
* - if it is being decreased and object become garbage, it will be collected next time
* If transitive closure of the atomic rootset mutates, it could only happen via changing the atomics references,
* as all elements of this closure are frozen.
* To handle such mutations we keep collector flag, which is cleared before analysis and set on every
* atomic reference value update. If flag's value changes - collector restarts its analysis.
* There are not so much of complications in this algorithm due to the delayed reference counting as if there's a
* stack reference to the shared object - it's reflected in the reference counter (see rememberNewContainer()).
* We release objects found by the collector on a rendezvouz callback, but not on the main thread,
* to keep UI responsive, as taking GC lock can take time, sometimes.
*/
namespace {
class Locker {
pthread_mutex_t* lock_;
public:
Locker(pthread_mutex_t* alock): lock_(alock) {
pthread_mutex_lock(lock_);
}
~Locker() {
pthread_mutex_unlock(lock_);
}
};
template <typename func>
inline void traverseObjectFields(ObjHeader* obj, func process) {
RuntimeAssert(obj != nullptr, "Must be non null");
const TypeInfo* typeInfo = obj->type_info();
if (typeInfo != theArrayTypeInfo) {
for (int index = 0; index < typeInfo->objOffsetsCount_; index++) {
ObjHeader** location = reinterpret_cast<ObjHeader**>(
reinterpret_cast<uintptr_t>(obj) + typeInfo->objOffsets_[index]);
process(location);
}
} else {
ArrayHeader* array = obj->array();
for (uint32_t index = 0; index < array->count_; index++) {
process(ArrayAddressOfElementAt(array, index));
}
}
}
inline bool isAtomicReference(ObjHeader* obj) {
return (obj->type_info()->flags_ & TF_LEAK_DETECTOR_CANDIDATE) != 0;
}
#define CHECK_CALL(call, message) RuntimeCheck((call) == 0, message)
class CyclicCollector {
pthread_mutex_t lock_;
pthread_mutex_t timestampLock_;
pthread_cond_t cond_;
pthread_t gcThread_;
int currentAliveWorkers_;
int gcRunning_;
int mutatedAtomics_;
int pendingRelease_;
bool shallRunCollector_;
bool terminateCollector_;
int32_t currentTick_;
int32_t lastTick_;
int64_t lastTimestampUs_;
void* mainWorker_;
KStdUnorderedSet<ObjHeader*> rootset_;
KStdUnorderedSet<ObjHeader*> toRelease_;
public:
CyclicCollector() {
CHECK_CALL(pthread_mutex_init(&lock_, nullptr), "Cannot init collector mutex")
CHECK_CALL(pthread_mutex_init(&timestampLock_, nullptr), "Cannot init collector timestamp mutex")
CHECK_CALL(pthread_cond_init(&cond_, nullptr), "Cannot init collector condition")
CHECK_CALL(pthread_create(&gcThread_, nullptr, gcWorkerRoutine, this), "Cannot start collector thread")
}
void clear() {
Locker lock(&lock_);
rootset_.clear();
toRelease_.clear();
}
void terminate(bool enabled) {
{
Locker locker(&lock_);
terminateCollector_ = true;
if (enabled) shallRunCollector_ = true;
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector")
}
// TODO: improve waiting for collector termination.
while (atomicGet(&terminateCollector_)) {}
releasePendingUnlocked(nullptr);
}
~CyclicCollector() {
pthread_cond_destroy(&cond_);
pthread_mutex_destroy(&lock_);
pthread_mutex_destroy(&timestampLock_);
}
static void* gcWorkerRoutine(void* argument) {
CyclicCollector* thiz = reinterpret_cast<CyclicCollector*>(argument);
thiz->gcProcessor();
return nullptr;
}
void gcProcessor() {
{
Locker locker(&lock_);
KStdDeque<ObjHeader*> toVisit;
KStdUnorderedSet<ObjHeader*> visited;
KStdUnorderedMap<ObjHeader*, int> sideRefCounts;
int restartCount = 0;
while (!terminateCollector_) {
CHECK_CALL(pthread_cond_wait(&cond_, &lock_), "Cannot wait collector condition")
if (!shallRunCollector_) continue;
atomicSet(&gcRunning_, 1);
restartCount = 0;
restart:
COLLECTOR_LOG("start cycle GC\n");
if (restartCount > 10 && !terminateCollector_) {
COLLECTOR_LOG("wait for some time to avoid GC thrashing\n");
uint64_t nsDelta = 1000LL * 1000LL * (restartCount - 10);
WaitOnCondVar(&cond_, &lock_, nsDelta);
}
atomicSet(&mutatedAtomics_, 0);
visited.clear();
toVisit.clear();
sideRefCounts.clear();
for (auto* root: rootset_) {
// We only care about frozen values here, as only they could become part of shared cycles.
if (!containerFor(root)->frozen()) continue;
COLLECTOR_LOG("process root %p\n", root);
toVisit.push_back(root);
sideRefCounts[root] = 0;
}
while (toVisit.size() > 0) {
if (atomicGet(&mutatedAtomics_) != 0) {
COLLECTOR_LOG("restarted during rootset visit\n")
restartCount++;
goto restart;
}
auto* obj = toVisit.front();
toVisit.pop_front();
COLLECTOR_LOG("visit %s%p\n", isAtomicReference(obj) ? "atomic " : "", obj);
auto* objContainer = containerFor(obj);
if (objContainer == nullptr) continue; // Permanent object.
RuntimeCheck(objContainer->shareable(), "Must be shareable");
if (visited.count(obj) == 0) {
visited.insert(obj);
traverseObjectFields(obj, [&toVisit, obj, &sideRefCounts](ObjHeader** location) {
ObjHeader* ref = *location;
if (ref != nullptr) {
COLLECTOR_LOG("object field %p in %p\n", ref, obj)
int increment;
// We shall not account for edges inside the same frozen container, unless it originates
// from an atomic reference.
if (isAtomicReference(obj) || (containerFor(obj) != containerFor(ref))) {
COLLECTOR_LOG("counting %p -> %p\n", obj, ref)
increment = 1;
} else {
COLLECTOR_LOG("not counting %p -> %p\n", obj, ref)
increment = 0;
}
sideRefCounts[ref] += increment;
toVisit.push_back(ref);
}
});
}
}
// Now find all elements with external references, and mark objects reachable from them as non suitable
// for collection by setting their side reference count to -1.
toVisit.clear();
for (auto it: sideRefCounts) {
auto* obj = it.first;
auto* objContainer = containerFor(obj);
if (objContainer == nullptr) continue; // Permanent object.
int refCount;
// If object is in aggregated container - sum up RC for all elements.
if (objContainer->objectCount() != 1) {
RuntimeAssert(objContainer->frozen(), "Must be frozen aggregate");
ContainerHeader** subContainer = reinterpret_cast<ContainerHeader**>(objContainer + 1);
refCount = 0;
for (uint32_t i = 0; i < objContainer->objectCount(); ++i) {
auto* componentObj = reinterpret_cast<ObjHeader*>((*subContainer) + 1);
refCount += sideRefCounts[componentObj];
subContainer++;
}
} else {
refCount = it.second;
}
RuntimeAssert(refCount <= objContainer->refCount(), "Must properly count inner refs");
if (refCount != objContainer->refCount()) {
COLLECTOR_LOG("for %p mismatched RC: %d vs %d, adding as possible root\n", obj, refCount, objContainer->refCount())
toVisit.push_back(it.first);
}
}
visited.clear();
while (toVisit.size() > 0) {
auto* obj = toVisit.front();
toVisit.pop_front();
auto* objContainer = containerFor(obj);
if (objContainer == nullptr) continue; // Permanent object.
RuntimeCheck(objContainer->shareable(), "Must be shareable");
sideRefCounts[obj] = -1;
visited.insert(obj);
if (atomicGet(&mutatedAtomics_) != 0) {
COLLECTOR_LOG("restarted during reachable visit\n")
restartCount++;
goto restart;
}
traverseObjectFields(obj, [&toVisit, &visited](ObjHeader** location) {
ObjHeader* ref = *location;
if (ref != nullptr && (visited.count(ref) == 0)) {
toVisit.push_back(ref);
}
});
}
// Now release all atomic roots with matching reference counters, as only their destruction is controlled.
for (auto it: sideRefCounts) {
auto* obj = it.first;
// Only do that for atomic rootset elements. For them we also do not have sum up references from
// other elements of an aggregate, as atomic references are always in single object containers.
if (!isAtomicReference(obj)) {
continue;
}
if (atomicGet(&mutatedAtomics_) != 0) {
COLLECTOR_LOG("restarted during matching check\n")
restartCount++;
goto restart;
}
auto* objContainer = containerFor(obj);
if (!objContainer->frozen()) continue;
RuntimeAssert(objContainer->objectCount() == 1, "Must be single object");
COLLECTOR_LOG("for %p inner %d actual %d\n", obj, it.second, objContainer->refCount());
// All references are inner. We compare the number of counted
// inner references with the number of non-stack references and per-thread ownership value
// (see rememberNewContainer()).
if (it.second == objContainer->refCount()) {
COLLECTOR_LOG("adding %p to release candidates\n", it.first);
toRelease_.insert(it.first);
}
}
if (toRelease_.size() > 0)
atomicSet(&pendingRelease_, 1);
atomicSet(&gcRunning_, 0);
shallRunCollector_ = false;
COLLECTOR_LOG("end cycle GC\n");
}
}
atomicSet(&terminateCollector_, false);
}
void addWorker(void* worker) {
suggestLockRelease();
Locker lock(&lock_);
currentAliveWorkers_++;
if (mainWorker_ == nullptr) mainWorker_ = worker;
}
void removeWorker(void* worker, bool enabled) {
suggestLockRelease();
Locker lock(&lock_);
// When exiting the worker - we shall collect the cyclic garbage here.
if (enabled) {
shallRunCollector_ = true;
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector")
}
currentAliveWorkers_--;
}
void addRoot(ObjHeader* obj) {
COLLECTOR_LOG("add root %p\n", obj);
// TODO: we can only add root when collector is not processing, which looks like a limitation,
// instead we can add elements to the side buffer or have a separate lock for that.
suggestLockRelease();
Locker lock(&lock_);
rootset_.insert(obj);
}
void removeRoot(ObjHeader* obj) {
COLLECTOR_LOG("remove root %p\n", obj);
// Note that we can only remove root when the collector is not processing.
suggestLockRelease();
Locker lock(&lock_);
toRelease_.erase(obj);
rootset_.erase(obj);
}
void mutateRoot(ObjHeader* newValue) {
// TODO: consider optimization, when clearing value (setting to null) in atomic reference shall not lead
// to invalidation of the collector analysis state.
atomicSet(&mutatedAtomics_, 1);
}
void suggestLockRelease() {
atomicSet(&mutatedAtomics_, 1);
}
bool checkIfShallCollect() {
auto tick = atomicAdd(&currentTick_, 1);
auto delta = tick - atomicGet(&lastTick_);
if (delta > 10 || delta < 0) {
auto currentTimestampUs = konan::getTimeMicros();
#if KONAN_NO_64BIT_ATOMIC
if (currentTimestampUs - *(volatile int64_t*)&lastTimestampUs_ > 10000) {
#else
if (currentTimestampUs - atomicGet(&lastTimestampUs_) > 10000) {
#endif // KONAN_NO_64BIT_ATOMIC
// Do we care if this lock is not here?
Locker locker(&timestampLock_);
lastTick_ = currentTick_;
lastTimestampUs_ = currentTimestampUs;
return true;
}
}
return false;
}
void releasePendingUnlocked(void* worker) {
// We are not doing that on the UI thread, as taking lock is slow, unless
// it happens on deinit of the collector or if there are no other workers.
if ((atomicGet(&pendingRelease_) != 0) && ((worker != mainWorker_) || (currentAliveWorkers_ == 1))) {
KStdVector<ObjHeader*> heapRefsToRelease;
{
suggestLockRelease();
Locker locker(&lock_);
COLLECTOR_LOG("clearing %d release candidates on %p\n", toRelease_.size(), worker);
for (auto* it: toRelease_) {
COLLECTOR_LOG("clear references in %p\n", it)
traverseObjectFields(it, [&heapRefsToRelease](ObjHeader** location) {
// Avoid using ZeroHeapRef here: it can provoke garbageCollect() which would then stuck on taking [lock_]
// (which is already taken above).
auto* value = *location;
if (reinterpret_cast<uintptr_t>(value) > 1) {
*location = nullptr;
heapRefsToRelease.push_back(value);
}
});
}
toRelease_.clear();
atomicSet(&pendingRelease_, 0);
}
for (auto* it: heapRefsToRelease) {
ReleaseHeapRef(it);
}
}
}
void collectorCallaback(void* worker) {
if (atomicGet(&gcRunning_) != 0) return;
releasePendingUnlocked(worker);
if (checkIfShallCollect()) {
Locker locker(&lock_);
shallRunCollector_ = true;
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector")
}
}
void scheduleGarbageCollect() {
if (atomicGet(&gcRunning_) != 0) return;
Locker lock(&lock_);
shallRunCollector_ = true;
CHECK_CALL(pthread_cond_signal(&cond_), "Cannot signal collector")
}
void localGC() {
// We just need to take GC lock here, to avoid release of object we walk on.
// TODO: consider optimization without taking the lock and just notifying collector via an atomic.
suggestLockRelease();
Locker locker(&lock_);
}
};
CyclicCollector* cyclicCollector = nullptr;
} // namespace
#endif // WITH_WORKERS
void cyclicInit() {
#if WITH_WORKERS
RuntimeAssert(cyclicCollector == nullptr, "Must be not yet inited");
cyclicCollector = konanConstructInstance<CyclicCollector>();
#endif
}
void cyclicDeinit(bool enabled) {
#if WITH_WORKERS
RuntimeAssert(cyclicCollector != nullptr, "Must be inited");
auto* local = cyclicCollector;
local->terminate(enabled);
cyclicCollector = nullptr;
// Workaround data race with threads non-atomically reading and then using [cyclicCollector].
// konanDestructInstance(local);
// Note: memory leaks here indeed, but usually it happens once per application.
// Make best effort to clean some memory:
local->clear();
#endif // WITH_WORKERS
}
void cyclicAddWorker(void* worker) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->addWorker(worker);
#endif // WITH_WORKERS
}
void cyclicRemoveWorker(void* worker, bool enabled) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->removeWorker(worker, enabled);
#endif // WITH_WORKERS
}
void cyclicCollectorCallback(void* worker) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->collectorCallaback(worker);
#endif // WITH_WORKERS
}
void cyclicScheduleGarbageCollect() {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->scheduleGarbageCollect();
#endif // WITH_WORKERS
}
void cyclicAddAtomicRoot(ObjHeader* obj) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->addRoot(obj);
#endif // WITH_WORKERS
}
void cyclicRemoveAtomicRoot(ObjHeader* obj) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->removeRoot(obj);
#endif // WITH_WORKERS
}
void cyclicMutateAtomicRoot(ObjHeader* newValue) {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->mutateRoot(newValue);
#endif // WITH_WORKERS
}
void cyclicLocalGC() {
#if WITH_WORKERS
auto* local = cyclicCollector;
if (local)
local->localGC();
#endif // WITH_WORKERS
}
@@ -0,0 +1,17 @@
#ifndef RUNTIME_CYCLIC_COLLECTOR_H
#define RUNTIME_CYCLIC_COLLECTOR_H
struct ObjHeader;
void cyclicInit();
void cyclicDeinit(bool enabled);
void cyclicAddWorker(void* worker);
void cyclicRemoveWorker(void* worker, bool enabled);
void cyclicAddAtomicRoot(ObjHeader* obj);
void cyclicRemoveAtomicRoot(ObjHeader* obj);
void cyclicMutateAtomicRoot(ObjHeader* newValue);
void cyclicCollectorCallback(void* worker);
void cyclicLocalGC();
void cyclicScheduleGarbageCollect();
#endif // RUNTIME_CYCLIC_COLLECTOR_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,337 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_MEMORYPRIVATE_HPP
#define RUNTIME_MEMORYPRIVATE_HPP
#include "Memory.h"
typedef enum {
// Those bit masks are applied to refCount_ field.
// Container is normal thread-local container.
CONTAINER_TAG_LOCAL = 0,
// Container is frozen, could only refer to other frozen objects.
// Refcounter update is atomics.
CONTAINER_TAG_FROZEN = 1 | 1, // shareable
// Stack container, no need to free, children cleanup still shall be there.
CONTAINER_TAG_STACK = 2,
// Atomic container, reference counter is atomically updated.
CONTAINER_TAG_SHARED = 3 | 1, // shareable
// Shift to get actual counter.
CONTAINER_TAG_SHIFT = 2,
// Actual value to increment/decrement container by. Tag is in lower bits.
CONTAINER_TAG_INCREMENT = 1 << CONTAINER_TAG_SHIFT,
// Mask for container type.
CONTAINER_TAG_MASK = CONTAINER_TAG_INCREMENT - 1,
// Shift to get actual object count, if has it.
CONTAINER_TAG_GC_SHIFT = 7,
CONTAINER_TAG_GC_MASK = (1 << CONTAINER_TAG_GC_SHIFT) - 1,
CONTAINER_TAG_GC_INCREMENT = 1 << CONTAINER_TAG_GC_SHIFT,
// Color mask of a container.
CONTAINER_TAG_COLOR_SHIFT = 3,
CONTAINER_TAG_GC_COLOR_MASK = (1 << CONTAINER_TAG_COLOR_SHIFT) - 1,
// Colors.
// In use or free.
CONTAINER_TAG_GC_BLACK = 0,
// Possible member of garbage cycle.
CONTAINER_TAG_GC_GRAY = 1,
// Member of garbage cycle.
CONTAINER_TAG_GC_WHITE = 2,
// Possible root of cycle.
CONTAINER_TAG_GC_PURPLE = 3,
// Acyclic.
CONTAINER_TAG_GC_GREEN = 4,
// Orange and red are currently unused.
// Candidate cycle awaiting epoch.
CONTAINER_TAG_GC_ORANGE = 5,
// Candidate cycle awaiting sigma computation.
CONTAINER_TAG_GC_RED = 6,
// Individual state bits used during GC and freezing.
CONTAINER_TAG_GC_MARKED = 1 << CONTAINER_TAG_COLOR_SHIFT,
CONTAINER_TAG_GC_BUFFERED = 1 << (CONTAINER_TAG_COLOR_SHIFT + 1),
CONTAINER_TAG_GC_SEEN = 1 << (CONTAINER_TAG_COLOR_SHIFT + 2),
// If indeed has more that one object.
CONTAINER_TAG_GC_HAS_OBJECT_COUNT = 1 << (CONTAINER_TAG_COLOR_SHIFT + 3)
} ContainerTag;
// Header of all container objects. Contains reference counter.
struct ContainerHeader {
// Reference counter of container. Uses CONTAINER_TAG_SHIFT, lower bits of counter
// for container type (for polymorphism in ::Release()).
uint32_t refCount_;
// Number of objects in the container.
uint32_t objectCount_;
inline bool local() const {
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_LOCAL;
}
inline bool frozen() const {
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_FROZEN;
}
inline void freeze() {
refCount_ = (refCount_ & ~CONTAINER_TAG_MASK) | CONTAINER_TAG_FROZEN;
}
inline void makeShared() {
refCount_ = (refCount_ & ~CONTAINER_TAG_MASK) | CONTAINER_TAG_SHARED;
}
inline bool shared() const {
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_SHARED;
}
inline bool shareable() const {
return (tag() & 1) != 0; // CONTAINER_TAG_FROZEN || CONTAINER_TAG_SHARED
}
inline bool stack() const {
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_STACK;
}
inline int refCount() const {
return (int)refCount_ >> CONTAINER_TAG_SHIFT;
}
inline void setRefCount(unsigned refCount) {
refCount_ = tag() | (refCount << CONTAINER_TAG_SHIFT);
}
template <bool Atomic>
inline void incRefCount() {
#ifdef KONAN_NO_THREADS
refCount_ += CONTAINER_TAG_INCREMENT;
#else
if (Atomic)
__sync_add_and_fetch(&refCount_, CONTAINER_TAG_INCREMENT);
else
refCount_ += CONTAINER_TAG_INCREMENT;
#endif
}
template <bool Atomic>
inline bool tryIncRefCount() {
if (Atomic) {
while (true) {
uint32_t currentRefCount_ = refCount_;
if (((int)currentRefCount_ >> CONTAINER_TAG_SHIFT) > 0) {
if (compareAndSet(&refCount_, currentRefCount_, currentRefCount_ + CONTAINER_TAG_INCREMENT)) {
return true;
}
} else {
return false;
}
}
} else {
// Note: tricky case here is doing this during cycle collection.
// This can actually happen due to deallocation hooks.
// Fortunately by this point reference counts have been made precise again.
if (refCount() > 0) {
incRefCount</* Atomic = */ false>();
return true;
} else {
return false;
}
}
}
template <bool Atomic>
inline int decRefCount() {
#ifdef KONAN_NO_THREADS
int value = refCount_ -= CONTAINER_TAG_INCREMENT;
#else
int value = Atomic ?
__sync_sub_and_fetch(&refCount_, CONTAINER_TAG_INCREMENT) : refCount_ -= CONTAINER_TAG_INCREMENT;
#endif
return value >> CONTAINER_TAG_SHIFT;
}
inline int decRefCount() {
#ifdef KONAN_NO_THREADS
int value = refCount_ -= CONTAINER_TAG_INCREMENT;
#else
int value = shareable() ?
__sync_sub_and_fetch(&refCount_, CONTAINER_TAG_INCREMENT) : refCount_ -= CONTAINER_TAG_INCREMENT;
#endif
return value >> CONTAINER_TAG_SHIFT;
}
inline unsigned tag() const {
return refCount_ & CONTAINER_TAG_MASK;
}
inline unsigned objectCount() const {
return (objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) != 0 ?
(objectCount_ >> CONTAINER_TAG_GC_SHIFT) : 1;
}
inline void incObjectCount() {
RuntimeAssert((objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) != 0, "Must have object count");
objectCount_ += CONTAINER_TAG_GC_INCREMENT;
}
inline void setObjectCount(int count) {
if (count == 1) {
objectCount_ &= ~CONTAINER_TAG_GC_HAS_OBJECT_COUNT;
} else {
objectCount_ = (count << CONTAINER_TAG_GC_SHIFT) | CONTAINER_TAG_GC_HAS_OBJECT_COUNT;
}
}
inline unsigned containerSize() const {
RuntimeAssert((objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) == 0, "Must be single-object");
return (objectCount_ >> CONTAINER_TAG_GC_SHIFT);
}
inline void setContainerSize(unsigned size) {
RuntimeAssert((objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) == 0, "Must not have object count");
objectCount_ = (objectCount_ & CONTAINER_TAG_GC_MASK) | (size << CONTAINER_TAG_GC_SHIFT);
}
inline bool hasContainerSize() {
return (objectCount_ & CONTAINER_TAG_GC_HAS_OBJECT_COUNT) == 0;
}
inline unsigned color() const {
return objectCount_ & CONTAINER_TAG_GC_COLOR_MASK;
}
inline void setColorAssertIfGreen(unsigned color) {
RuntimeAssert(this->color() != CONTAINER_TAG_GC_GREEN, "Must not be green");
setColorEvenIfGreen(color);
}
inline void setColorEvenIfGreen(unsigned color) {
// TODO: do we need atomic color update?
objectCount_ = (objectCount_ & ~CONTAINER_TAG_GC_COLOR_MASK) | color;
}
inline void setColorUnlessGreen(unsigned color) {
// TODO: do we need atomic color update?
unsigned objectCount = objectCount_;
if ((objectCount & CONTAINER_TAG_GC_COLOR_MASK) != CONTAINER_TAG_GC_GREEN)
objectCount_ = (objectCount & ~CONTAINER_TAG_GC_COLOR_MASK) | color;
}
inline bool buffered() const {
return (objectCount_ & CONTAINER_TAG_GC_BUFFERED) != 0;
}
inline void setBuffered() {
objectCount_ |= CONTAINER_TAG_GC_BUFFERED;
}
inline void resetBuffered() {
objectCount_ &= ~CONTAINER_TAG_GC_BUFFERED;
}
inline bool marked() const {
return (objectCount_ & CONTAINER_TAG_GC_MARKED) != 0;
}
inline void mark() {
objectCount_ |= CONTAINER_TAG_GC_MARKED;
}
inline void unMark() {
objectCount_ &= ~CONTAINER_TAG_GC_MARKED;
}
inline bool seen() const {
return (objectCount_ & CONTAINER_TAG_GC_SEEN) != 0;
}
inline void setSeen() {
objectCount_ |= CONTAINER_TAG_GC_SEEN;
}
inline void resetSeen() {
objectCount_ &= ~CONTAINER_TAG_GC_SEEN;
}
// Following operations only work on freed container which is in finalization queue.
// We cannot use 'this' here, as it conflicts with aliasing analysis in clang.
inline void setNextLink(ContainerHeader* next) {
*reinterpret_cast<ContainerHeader**>(this + 1) = next;
}
inline ContainerHeader* nextLink() {
return *reinterpret_cast<ContainerHeader**>(this + 1);
}
};
ALWAYS_INLINE ContainerHeader* containerFor(const ObjHeader* obj);
// Header for the meta-object.
struct MetaObjHeader {
// Pointer to the type info. Must be first, to match ArrayHeader and ObjHeader layout.
const TypeInfo* typeInfo_;
// Container pointer.
ContainerHeader* container_;
#ifdef KONAN_OBJC_INTEROP
void* associatedObject_;
#endif
// Flags for the object state.
int32_t flags_;
struct {
// Strong reference to the counter object.
ObjHeader* counter_;
} WeakReference;
};
extern "C" {
#define MODEL_VARIANTS(returnType, name, ...) \
returnType name##Strict(__VA_ARGS__) RUNTIME_NOTHROW; \
returnType name##Relaxed(__VA_ARGS__) RUNTIME_NOTHROW;
OBJ_GETTER(AllocInstanceStrict, const TypeInfo* type_info) RUNTIME_NOTHROW;
OBJ_GETTER(AllocInstanceRelaxed, const TypeInfo* type_info) RUNTIME_NOTHROW;
OBJ_GETTER(AllocArrayInstanceStrict, const TypeInfo* type_info, int32_t elements);
OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* type_info, int32_t elements);
OBJ_GETTER(InitInstanceStrict,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitInstanceRelaxed,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSharedInstanceStrict,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSharedInstanceRelaxed,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
MODEL_VARIANTS(void, SetStackRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, ZeroStackRef, ObjHeader** location);
MODEL_VARIANTS(void, UpdateStackRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, UpdateHeapRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, UpdateHeapRefIfNull, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, UpdateReturnRef, ObjHeader** returnSlot, const ObjHeader* object);
MODEL_VARIANTS(void, EnterFrame, ObjHeader** start, int parameters, int count);
MODEL_VARIANTS(void, LeaveFrame, ObjHeader** start, int parameters, int count);
MODEL_VARIANTS(void, ReleaseHeapRef, const ObjHeader* object);
MODEL_VARIANTS(void, ReleaseHeapRefNoCollect, const ObjHeader* object);
} // extern "C"
#endif // RUNTIME_MEMORYPRIVATE_HPP
+118
View File
@@ -0,0 +1,118 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_ALLOC_H
#define RUNTIME_ALLOC_H
#include <stddef.h>
#include <stdlib.h>
#include <new>
#include <utility>
#include "Porting.h"
inline void* konanAllocMemory(size_t size) {
return konan::calloc(1, size);
}
inline void konanFreeMemory(void* memory) {
konan::free(memory);
}
template<typename T>
inline T* konanAllocArray(size_t length) {
return reinterpret_cast<T*>(konanAllocMemory(length * sizeof(T)));
}
template <typename T, typename ...A>
inline T* konanConstructInstance(A&& ...args) {
return new (konanAllocMemory(sizeof(T))) T(::std::forward<A>(args)...);
}
template <typename T, typename ...A>
inline T* konanConstructSizedInstance(size_t size, A&& ...args) {
return new (konanAllocMemory(size)) T(::std::forward<A>(args)...);
}
template <typename T>
inline void konanDestructInstance(T* instance) {
instance->~T();
konanFreeMemory(instance);
}
template <class T> class KonanAllocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
KonanAllocator() {}
KonanAllocator(const KonanAllocator&) {}
pointer allocate(size_type n, const void * = 0) {
return reinterpret_cast<T*>(konanAllocMemory(n * sizeof(T)));
}
void deallocate(void* p, size_type) {
if (p != nullptr) konanFreeMemory(p);
}
pointer address(reference x) const { return &x; }
const_pointer address(const_reference x) const { return &x; }
KonanAllocator<T>& operator=(const KonanAllocator&) { return *this; }
void construct(pointer p, const T& val) { new ((T*) p) T(val); }
// C++-11 wants that.
template <class U, class ...A>
void construct(U* const p, A&& ...args) {
new (p) U(::std::forward<A>(args)...);
}
void destroy(pointer p) { p->~T(); }
size_type max_size() const { return size_t(-1); }
template <class U>
struct rebind { typedef KonanAllocator<U> other; };
template <class U>
KonanAllocator(const KonanAllocator<U>&) {}
template <class U>
KonanAllocator& operator=(const KonanAllocator<U>&) { return *this; }
};
template <class T, class U>
bool operator==(
KonanAllocator<T> const&, KonanAllocator<U> const&) noexcept {
return true;
}
template <class T, class U>
bool operator!=(
KonanAllocator<T> const& x, KonanAllocator<U> const& y) noexcept {
return !(x == y);
}
#endif // RUNTIME_ALLOC_H
@@ -0,0 +1,708 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <string.h>
#include "KAssert.h"
#include "Exceptions.h"
#include "Memory.h"
#include "Natives.h"
#include "Types.h"
extern "C" void checkRangeIndexes(KInt from, KInt to, KInt size);
namespace {
ALWAYS_INLINE inline void mutabilityCheck(KConstRef thiz) {
// TODO: optimize it!
if (!thiz->local() && isFrozen(thiz)) {
ThrowInvalidMutabilityException(thiz);
}
}
ALWAYS_INLINE inline void boundsCheck(const ArrayHeader* array, KInt index) {
// We couldn't have created an array bigger than max KInt value.
// So if index is < 0, conversion to an unsigned value would make it bigger
// than the array size.
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
}
template<typename T>
inline void fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, T value) {
ArrayHeader* array = thiz->array();
checkRangeIndexes(fromIndex, toIndex, array->count_);
mutabilityCheck(thiz);
T* address = PrimitiveArrayAddressOfElementAt<T>(array, fromIndex);
for (KInt index = fromIndex; index < toIndex; ++index) {
*address++ = value;
}
}
template<typename T>
inline void copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
const ArrayHeader* array = thiz->array();
ArrayHeader* destinationArray = destination->array();
if (count < 0 ||
fromIndex < 0 || static_cast<uint32_t>(count) + fromIndex > array->count_ ||
toIndex < 0 || static_cast<uint32_t>(count) + toIndex > destinationArray->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(destination);
memmove(PrimitiveArrayAddressOfElementAt<T>(destinationArray, toIndex),
PrimitiveArrayAddressOfElementAt<T>(array, fromIndex),
count * sizeof(T));
}
template <class T>
inline void PrimitiveArraySet(KRef thiz, KInt index, T value) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<T>(array, index) = value;
}
template <class T>
inline T PrimitiveArrayGet(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return *PrimitiveArrayAddressOfElementAt<T>(array, index);
}
} // namespace
extern "C" {
// Generated as part of Kotlin standard library.
extern const ObjHeader theEmptyArray;
// TODO: those must be compiler intrinsics afterwards.
// Array.kt
OBJ_GETTER(Kotlin_Array_get, KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
boundsCheck(array, index);
RETURN_OBJ(*ArrayAddressOfElementAt(array, index));
}
void Kotlin_Array_set(KRef thiz, KInt index, KConstRef value) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
mutabilityCheck(thiz);
UpdateHeapRef(ArrayAddressOfElementAt(array, index), value);
}
KInt Kotlin_Array_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
void Kotlin_Array_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KRef value) {
ArrayHeader* array = thiz->array();
checkRangeIndexes(fromIndex, toIndex, array->count_);
mutabilityCheck(thiz);
for (KInt index = fromIndex; index < toIndex; ++index) {
UpdateHeapRef(ArrayAddressOfElementAt(array, index), value);
}
}
void Kotlin_Array_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
const ArrayHeader* array = thiz->array();
ArrayHeader* destinationArray = destination->array();
if (count < 0 ||
fromIndex < 0 || static_cast<uint32_t>(count) + fromIndex > array->count_ ||
toIndex < 0 || static_cast<uint32_t>(count) + toIndex > destinationArray->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(destination);
if (fromIndex >= toIndex) {
for (int index = 0; index < count; index++) {
UpdateHeapRef(ArrayAddressOfElementAt(destinationArray, toIndex + index),
*ArrayAddressOfElementAt(array, fromIndex + index));
}
} else {
for (int index = count - 1; index >= 0; index--) {
UpdateHeapRef(ArrayAddressOfElementAt(destinationArray, toIndex + index),
*ArrayAddressOfElementAt(array, fromIndex + index));
}
}
}
// Arrays.kt
OBJ_GETTER0(Kotlin_emptyArray) {
RETURN_OBJ(const_cast<ObjHeader*>(&theEmptyArray));
}
KByte Kotlin_ByteArray_get(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return *ByteArrayAddressOfElementAt(array, index);
}
void Kotlin_ByteArray_set(KRef thiz, KInt index, KByte value) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
mutabilityCheck(thiz);
*ByteArrayAddressOfElementAt(array, index) = value;
}
KInt Kotlin_ByteArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
KChar Kotlin_ByteArray_getCharAt(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 1 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
#if KONAN_NO_UNALIGNED_ACCESS
const uint8_t* address = reinterpret_cast<const uint8_t*>(ByteArrayAddressOfElementAt(array, index));
return (static_cast<KChar>(address[0]) << 0) | (static_cast<KChar>(address[1]) << 8);
#else
auto result = *reinterpret_cast<const KChar*>(ByteArrayAddressOfElementAt(array, index));
#if __BIG_ENDIAN__
return __builtin_bswap16(result);
#else
return result;
#endif // __BIG_ENDIAN__
#endif // KONAN_NO_UNALIGNED_ACCESS
}
KShort Kotlin_ByteArray_getShortAt(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 1 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
#if KONAN_NO_UNALIGNED_ACCESS
const uint8_t* address = reinterpret_cast<const uint8_t*>(ByteArrayAddressOfElementAt(array, index));
return (static_cast<KShort>(address[0]) << 0) | (static_cast<KShort>(address[1]) << 8);
#else
auto result = *reinterpret_cast<const KShort*>(ByteArrayAddressOfElementAt(array, index));
#if __BIG_ENDIAN__
return __builtin_bswap16(result);
#else
return result;
#endif // __BIG_ENDIAN__
#endif // KONAN_NO_UNALIGNED_ACCESS
}
KInt Kotlin_ByteArray_getIntAt(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 3 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
#if KONAN_NO_UNALIGNED_ACCESS
const uint8_t* address = reinterpret_cast<const uint8_t*>(ByteArrayAddressOfElementAt(array, index));
return (static_cast<KInt>(address[0]) << 0) | (static_cast<KInt>(address[1]) << 8) |
(static_cast<KInt>(address[2]) << 16) | (static_cast<KInt>(address[3]) << 24);
#else
auto result = *reinterpret_cast<const KInt*>(ByteArrayAddressOfElementAt(array, index));
#if __BIG_ENDIAN__
return __builtin_bswap32(result);
#else
return result;
#endif // __BIG_ENDIAN__
#endif // KONAN_NO_UNALIGNED_ACCESS
}
KLong Kotlin_ByteArray_getLongAt(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 7 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
#if KONAN_NO_UNALIGNED_ACCESS
const uint8_t* address = reinterpret_cast<const uint8_t*>(ByteArrayAddressOfElementAt(array, index));
return (static_cast<KLong>(address[0]) << 0) | (static_cast<KLong>(address[1]) << 8) |
(static_cast<KLong>(address[2]) << 16) | (static_cast<KLong>(address[3]) << 24) |
(static_cast<KLong>(address[4]) << 32) | (static_cast<KLong>(address[5]) << 40) |
(static_cast<KLong>(address[6]) << 48) | (static_cast<KLong>(address[7]) << 56);
#else
auto result = *reinterpret_cast<const KLong*>(ByteArrayAddressOfElementAt(array, index));
#if __BIG_ENDIAN__
return __builtin_bswap64(result);
#else
return result;
#endif // __BIG_ENDIAN__
#endif // KONAN_NO_UNALIGNED_ACCESS
}
KFloat Kotlin_ByteArray_getFloatAt(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 3 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
#if KONAN_NO_UNALIGNED_ACCESS
const uint8_t* address = reinterpret_cast<const uint8_t*>(ByteArrayAddressOfElementAt(array, index));
union {
KFloat f;
uint8_t b[4];
} u;
#if __BIG_ENDIAN__
u.b[0] = address[3];
u.b[1] = address[2];
u.b[2] = address[1];
u.b[3] = address[0];
#else
u.b[0] = address[0];
u.b[1] = address[1];
u.b[2] = address[2];
u.b[3] = address[3];
#endif // __BIG_ENDIAN__
return u.f;
#else
auto result = *reinterpret_cast<const KFloat*>(ByteArrayAddressOfElementAt(array, index));
return result;
#endif // KONAN_NO_UNALIGNED_ACCESS
}
KDouble Kotlin_ByteArray_getDoubleAt(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 7 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
#if KONAN_NO_UNALIGNED_ACCESS
const uint8_t* address = reinterpret_cast<const uint8_t*>(ByteArrayAddressOfElementAt(array, index));
union {
KDouble d;
uint8_t b[8];
} u;
#if __BIG_ENDIAN__
u.b[0] = address[7];
u.b[1] = address[6];
u.b[2] = address[5];
u.b[3] = address[4];
u.b[4] = address[3];
u.b[5] = address[2];
u.b[6] = address[1];
u.b[7] = address[0];
#else
u.b[0] = address[0];
u.b[1] = address[1];
u.b[2] = address[2];
u.b[3] = address[3];
u.b[4] = address[4];
u.b[5] = address[5];
u.b[6] = address[6];
u.b[7] = address[7];
#endif // __BIG_ENDIAN__
return u.d;
#else
return *reinterpret_cast<const KDouble*>(ByteArrayAddressOfElementAt(array, index));
#endif // KONAN_NO_UNALIGNED_ACCESS
}
void Kotlin_ByteArray_setCharAt(KRef thiz, KInt index, KChar value) {
ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 1 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
#if KONAN_NO_UNALIGNED_ACCESS
uint8_t* address = reinterpret_cast<uint8_t*>(ByteArrayAddressOfElementAt(array, index));
address[0] = (value >> 0) & 0xff;
address[1] = (value >> 8) & 0xff;
#else
#if __BIG_ENDIAN__
value = __builtin_bswap16(value);
#endif // __BIG_ENDIAN__
*reinterpret_cast<KChar*>(ByteArrayAddressOfElementAt(array, index)) = value;
#endif // KONAN_NO_UNALIGNED_ACCESS
}
void Kotlin_ByteArray_setShortAt(KRef thiz, KInt index, KShort value) {
ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 1 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
#if KONAN_NO_UNALIGNED_ACCESS
uint8_t* address = reinterpret_cast<uint8_t*>(ByteArrayAddressOfElementAt(array, index));
address[0] = (value >> 0) & 0xff;
address[1] = (value >> 8) & 0xff;
#else
#if __BIG_ENDIAN__
value = __builtin_bswap16(value);
#endif
*reinterpret_cast<KShort*>(ByteArrayAddressOfElementAt(array, index)) = value;
#endif // KONAN_NO_UNALIGNED_ACCESS
}
void Kotlin_ByteArray_setIntAt(KRef thiz, KInt index, KInt value) {
ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 3 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
#if KONAN_NO_UNALIGNED_ACCESS
uint8_t* address = reinterpret_cast<uint8_t*>(ByteArrayAddressOfElementAt(array, index));
address[0] = (value >> 0) & 0xff;
address[1] = (value >> 8) & 0xff;
address[2] = (value >> 16) & 0xff;
address[3] = (value >> 24) & 0xff;
#else
#if __BIG_ENDIAN__
value = __builtin_bswap32(value);
#endif // __BIG_ENDIAN__
*reinterpret_cast<KInt*>(ByteArrayAddressOfElementAt(array, index)) = value;
#endif // KONAN_NO_UNALIGNED_ACCESS
}
void Kotlin_ByteArray_setLongAt(KRef thiz, KInt index, KLong value) {
ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 7 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
#if KONAN_NO_UNALIGNED_ACCESS
uint8_t* address = reinterpret_cast<uint8_t*>(ByteArrayAddressOfElementAt(array, index));
address[0] = (value >> 0) & 0xff;
address[1] = (value >> 8) & 0xff;
address[2] = (value >> 16) & 0xff;
address[3] = (value >> 24) & 0xff;
address[4] = (value >> 32) & 0xff;
address[5] = (value >> 40) & 0xff;
address[6] = (value >> 48) & 0xff;
address[7] = (value >> 56) & 0xff;
#else
#if __BIG_ENDIAN__
value = __builtin_bswap64(value);
#endif // __BIG_ENDIAN__
*reinterpret_cast<KLong*>(ByteArrayAddressOfElementAt(array, index)) = value;
#endif // KONAN_NO_UNALIGNED_ACCESS
}
void Kotlin_ByteArray_setFloatAt(KRef thiz, KInt index, KFloat value) {
ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 3 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
#if KONAN_NO_UNALIGNED_ACCESS
uint8_t* address = reinterpret_cast<uint8_t*>(ByteArrayAddressOfElementAt(array, index));
union {
KFloat f;
uint8_t b[4];
} u;
u.f = value;
address[0] = u.b[0];
address[1] = u.b[1];
address[2] = u.b[2];
address[3] = u.b[3];
#else
*reinterpret_cast<KFloat*>(ByteArrayAddressOfElementAt(array, index)) = value;
#endif // KONAN_NO_UNALIGNED_ACCESS
}
void Kotlin_ByteArray_setDoubleAt(KRef thiz, KInt index, KDouble value) {
ArrayHeader* array = thiz->array();
if (index < 0 || static_cast<uint32_t>(index) + 7 >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
#if KONAN_NO_UNALIGNED_ACCESS
uint8_t* address = reinterpret_cast<uint8_t*>(ByteArrayAddressOfElementAt(array, index));
union {
KDouble d;
uint8_t b[8];
} u;
u.d = value;
address[0] = u.b[0];
address[1] = u.b[1];
address[2] = u.b[2];
address[3] = u.b[3];
address[4] = u.b[4];
address[5] = u.b[5];
address[6] = u.b[6];
address[7] = u.b[7];
#else
*reinterpret_cast<KDouble*>(ByteArrayAddressOfElementAt(array, index)) = value;
#endif // KONAN_NO_UNALIGNED_ACCESS
}
KChar Kotlin_CharArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KChar>(thiz, index);
}
void Kotlin_CharArray_set(KRef thiz, KInt index, KChar value) {
PrimitiveArraySet(thiz, index, value);
}
OBJ_GETTER(Kotlin_CharArray_copyOf, KConstRef thiz, KInt newSize) {
const ArrayHeader* array = thiz->array();
if (newSize < 0) {
ThrowIllegalArgumentException();
}
ArrayHeader* result = AllocArrayInstance(array->type_info(), newSize, OBJ_RESULT)->array();
KInt toCopy = array->count_ < static_cast<uint32_t>(newSize) ? array->count_ : newSize;
memcpy(
PrimitiveArrayAddressOfElementAt<KChar>(result, 0),
PrimitiveArrayAddressOfElementAt<KChar>(array, 0),
toCopy * sizeof(KChar));
RETURN_OBJ(result->obj());
}
KInt Kotlin_CharArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
KShort Kotlin_ShortArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KShort>(thiz, index);
}
void Kotlin_ShortArray_set(KRef thiz, KInt index, KShort value) {
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_ShortArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
KInt Kotlin_IntArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KInt>(thiz, index);
}
void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value) {
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_IntArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
void Kotlin_ByteArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KByte value) {
fillImpl<KByte>(thiz, fromIndex, toIndex, value);
}
void Kotlin_ShortArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KShort value) {
fillImpl<KShort>(thiz, fromIndex, toIndex, value);
}
void Kotlin_CharArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KChar value) {
fillImpl<KChar>(thiz, fromIndex, toIndex, value);
}
void Kotlin_IntArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KInt value) {
fillImpl<KInt>(thiz, fromIndex, toIndex, value);
}
void Kotlin_LongArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KLong value) {
fillImpl<KLong>(thiz, fromIndex, toIndex, value);
}
void Kotlin_FloatArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KFloat value) {
fillImpl<KFloat>(thiz, fromIndex, toIndex, value);
}
void Kotlin_DoubleArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KDouble value) {
fillImpl<KDouble>(thiz, fromIndex, toIndex, value);
}
void Kotlin_BooleanArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KBoolean value) {
fillImpl<KBoolean>(thiz, fromIndex, toIndex, value);
}
void Kotlin_ByteArray_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
copyImpl<KByte>(thiz, fromIndex, destination, toIndex, count);
}
void Kotlin_ShortArray_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
copyImpl<KShort>(thiz, fromIndex, destination, toIndex, count);
}
void Kotlin_CharArray_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
copyImpl<KChar>(thiz, fromIndex, destination, toIndex, count);
}
void Kotlin_IntArray_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
copyImpl<KInt>(thiz, fromIndex, destination, toIndex, count);
}
void Kotlin_LongArray_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
copyImpl<KLong>(thiz, fromIndex, destination, toIndex, count);
}
void Kotlin_FloatArray_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
copyImpl<KFloat>(thiz, fromIndex, destination, toIndex, count);
}
void Kotlin_DoubleArray_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
copyImpl<KDouble>(thiz, fromIndex, destination, toIndex, count);
}
void Kotlin_BooleanArray_copyImpl(KConstRef thiz, KInt fromIndex,
KRef destination, KInt toIndex, KInt count) {
copyImpl<KBoolean>(thiz, fromIndex, destination, toIndex, count);
}
KLong Kotlin_LongArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KLong>(thiz, index);
}
void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value) {
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_LongArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
KFloat Kotlin_FloatArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KFloat>(thiz, index);
}
void Kotlin_FloatArray_set(KRef thiz, KInt index, KFloat value) {
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_FloatArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
KDouble Kotlin_DoubleArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KDouble>(thiz, index);
}
void Kotlin_DoubleArray_set(KRef thiz, KInt index, KDouble value) {
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_DoubleArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
KBoolean Kotlin_BooleanArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KBoolean>(thiz, index);
}
void Kotlin_BooleanArray_set(KRef thiz, KInt index, KBoolean value) {
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_BooleanArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
KNativePtr Kotlin_NativePtrArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KNativePtr>(thiz, index);
}
void Kotlin_NativePtrArray_set(KRef thiz, KInt index, KNativePtr value) {
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_NativePtrArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
OBJ_GETTER(Kotlin_ImmutableBlob_toByteArray, KConstRef thiz, KInt startIndex, KInt endIndex) {
const ArrayHeader* array = thiz->array();
if (startIndex < 0 || static_cast<uint32_t>(endIndex) > array->count_ || startIndex > endIndex) {
ThrowArrayIndexOutOfBoundsException();
}
KInt count = endIndex - startIndex;
ArrayHeader* result = AllocArrayInstance(theByteArrayTypeInfo, count, OBJ_RESULT)->array();
memcpy(PrimitiveArrayAddressOfElementAt<KByte>(result, 0),
PrimitiveArrayAddressOfElementAt<KByte>(array, startIndex),
count);
RETURN_OBJ(result->obj());
}
KNativePtr Kotlin_ImmutableBlob_asCPointerImpl(KRef thiz, KInt offset) {
ArrayHeader* array = thiz->array();
// We couldn't have created an array bigger than max KInt value.
// So if index is < 0, conversion to an unsigned value would make it bigger
// than the array size.
if (static_cast<uint32_t>(offset) > array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return PrimitiveArrayAddressOfElementAt<KByte>(array, offset);
}
KNativePtr Kotlin_Arrays_getByteArrayAddressOfElement(KRef thiz, KInt index) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return AddressOfElementAt<KByte>(array, index);
}
KNativePtr Kotlin_Arrays_getCharArrayAddressOfElement (KRef thiz, KInt index) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return CharArrayAddressOfElementAt(array, index);
}
KNativePtr Kotlin_Arrays_getStringAddressOfElement (KRef thiz, KInt index) {
return Kotlin_Arrays_getCharArrayAddressOfElement(thiz, index);
}
KNativePtr Kotlin_Arrays_getShortArrayAddressOfElement(KRef thiz, KInt index) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return AddressOfElementAt<KShort>(array, index);
}
KNativePtr Kotlin_Arrays_getIntArrayAddressOfElement(KRef thiz, KInt index) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return AddressOfElementAt<KInt>(array, index);
}
KNativePtr Kotlin_Arrays_getLongArrayAddressOfElement(KRef thiz, KInt index) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return AddressOfElementAt<KLong>(array, index);
}
KNativePtr Kotlin_Arrays_getFloatArrayAddressOfElement(KRef thiz, KInt index) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return AddressOfElementAt<KFloat>(array, index);
}
KNativePtr Kotlin_Arrays_getDoubleArrayAddressOfElement(KRef thiz, KInt index) {
ArrayHeader* array = thiz->array();
boundsCheck(array, index);
return AddressOfElementAt<KDouble>(array, index);
}
} // extern "C"
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "gtest/gtest.h"
#include "Porting.h"
// TODO: Replace these tests with real ones.
TEST(ArraysTest, GoodTest) {
konan::consolePrintf("I'm a good test\n");
EXPECT_EQ(true, true);
}
TEST(ArraysTest, BadTest) {
GTEST_SKIP();
konan::consolePrintf("I'm a bad test\n");
EXPECT_EQ(true, false);
}
@@ -0,0 +1,217 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Atomic.h"
#include "Common.h"
#include "Exceptions.h"
#include "Memory.h"
#include "Types.h"
namespace {
struct AtomicReferenceLayout {
ObjHeader header;
KRef value_;
KInt lock_;
KInt cookie_;
};
template<typename T> struct AtomicPrimitive {
ObjHeader header;
volatile T value_;
};
template <typename T> inline volatile T* getValueLocation(KRef thiz) {
AtomicPrimitive<T>* atomic = reinterpret_cast<AtomicPrimitive<T>*>(thiz);
return &atomic->value_;
}
template <typename T> void setImpl(KRef thiz, T value) {
volatile T* location = getValueLocation<T>(thiz);
atomicSet(location, value);
}
template <typename T> T getImpl(KRef thiz) {
volatile T* location = getValueLocation<T>(thiz);
return atomicGet(location);
}
template <typename T> T addAndGetImpl(KRef thiz, T delta) {
volatile T* location = getValueLocation<T>(thiz);
return atomicAdd(location, delta);
}
template <typename T> T compareAndSwapImpl(KRef thiz, T expectedValue, T newValue) {
volatile T* location = getValueLocation<T>(thiz);
return compareAndSwap(location, expectedValue, newValue);
}
template <typename T> KBoolean compareAndSetImpl(KRef thiz, T expectedValue, T newValue) {
volatile T* location = getValueLocation<T>(thiz);
return compareAndSet(location, expectedValue, newValue);
}
inline AtomicReferenceLayout* asAtomicReference(KRef thiz) {
return reinterpret_cast<AtomicReferenceLayout*>(thiz);
}
} // namespace
extern "C" {
KInt Kotlin_AtomicInt_addAndGet(KRef thiz, KInt delta) {
return addAndGetImpl(thiz, delta);
}
KInt Kotlin_AtomicInt_compareAndSwap(KRef thiz, KInt expectedValue, KInt newValue) {
return compareAndSwapImpl(thiz, expectedValue, newValue);
}
KBoolean Kotlin_AtomicInt_compareAndSet(KRef thiz, KInt expectedValue, KInt newValue) {
return compareAndSetImpl(thiz, expectedValue, newValue);
}
void Kotlin_AtomicInt_set(KRef thiz, KInt newValue) {
setImpl(thiz, newValue);
}
KInt Kotlin_AtomicInt_get(KRef thiz) {
return getImpl<KInt>(thiz);
}
KLong Kotlin_AtomicLong_addAndGet(KRef thiz, KLong delta) {
return addAndGetImpl(thiz, delta);
}
#if KONAN_NO_64BIT_ATOMIC
static int lock64 = 0;
#endif
KLong Kotlin_AtomicLong_compareAndSwap(KRef thiz, KLong expectedValue, KLong newValue) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
KLong old = *address;
if (old == expectedValue) {
*address = newValue;
}
compareAndSwap(&lock64, 1, 0);
return old;
#else
return compareAndSwapImpl(thiz, expectedValue, newValue);
#endif
}
KBoolean Kotlin_AtomicLong_compareAndSet(KRef thiz, KLong expectedValue, KLong newValue) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
KBoolean result = false;
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
KLong old = *address;
if (old == expectedValue) {
result = true;
*address = newValue;
}
compareAndSwap(&lock64, 1, 0);
return result;
#else
return compareAndSetImpl(thiz, expectedValue, newValue);
#endif
}
void Kotlin_AtomicLong_set(KRef thiz, KLong newValue) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
*address = newValue;
compareAndSwap(&lock64, 1, 0);
#else
setImpl(thiz, newValue);
#endif
}
KLong Kotlin_AtomicLong_get(KRef thiz) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
KLong value = *address;
compareAndSwap(&lock64, 1, 0);
return value;
#else
return getImpl<KLong>(thiz);
#endif
}
KNativePtr Kotlin_AtomicNativePtr_compareAndSwap(KRef thiz, KNativePtr expectedValue, KNativePtr newValue) {
return compareAndSwapImpl(thiz, expectedValue, newValue);
}
KBoolean Kotlin_AtomicNativePtr_compareAndSet(KRef thiz, KNativePtr expectedValue, KNativePtr newValue) {
return compareAndSetImpl(thiz, expectedValue, newValue);
}
void Kotlin_AtomicNativePtr_set(KRef thiz, KNativePtr newValue) {
setImpl(thiz, newValue);
}
KNativePtr Kotlin_AtomicNativePtr_get(KRef thiz) {
return getImpl<KNativePtr>(thiz);
}
void Kotlin_AtomicReference_checkIfFrozen(KRef value) {
if (value != nullptr && !isPermanentOrFrozen(value)) {
ThrowInvalidMutabilityException(value);
}
}
OBJ_GETTER(Kotlin_AtomicReference_compareAndSwap, KRef thiz, KRef expectedValue, KRef newValue) {
Kotlin_AtomicReference_checkIfFrozen(newValue);
// See Kotlin_AtomicReference_get() for explanations, why locking is needed.
AtomicReferenceLayout* ref = asAtomicReference(thiz);
RETURN_RESULT_OF(SwapHeapRefLocked, &ref->value_, expectedValue, newValue,
&ref->lock_, &ref->cookie_);
}
KBoolean Kotlin_AtomicReference_compareAndSet(KRef thiz, KRef expectedValue, KRef newValue) {
Kotlin_AtomicReference_checkIfFrozen(newValue);
// See Kotlin_AtomicReference_get() for explanations, why locking is needed.
AtomicReferenceLayout* ref = asAtomicReference(thiz);
ObjHolder holder;
auto old = SwapHeapRefLocked(&ref->value_, expectedValue, newValue,
&ref->lock_, &ref->cookie_, holder.slot());
return old == expectedValue;
}
void Kotlin_AtomicReference_set(KRef thiz, KRef newValue) {
Kotlin_AtomicReference_checkIfFrozen(newValue);
AtomicReferenceLayout* ref = asAtomicReference(thiz);
SetHeapRefLocked(&ref->value_, newValue, &ref->lock_, &ref->cookie_);
}
OBJ_GETTER(Kotlin_AtomicReference_get, KRef thiz) {
// Here we must take a lock to prevent race when value, while taken here, is CASed and immediately
// destroyed by an another thread. AtomicReference no longer holds such an object, so if we got
// rescheduled unluckily, between the moment value is read from the field and RC is incremented,
// object may go away.
AtomicReferenceLayout* ref = asAtomicReference(thiz);
RETURN_RESULT_OF(ReadHeapRefLocked, &ref->value_, &ref->lock_, &ref->cookie_);
}
} // extern "C"
@@ -0,0 +1,80 @@
#ifndef RUNTIME_ATOMIC_H
#define RUNTIME_ATOMIC_H
#include "Common.h"
template <typename T>
ALWAYS_INLINE inline T atomicAdd(volatile T* where, T what) {
#ifndef KONAN_NO_THREADS
return __sync_add_and_fetch(where, what);
#else
return *where += what;
#endif
}
template <typename T>
ALWAYS_INLINE inline T compareAndSwap(volatile T* where, T expectedValue, T newValue) {
#ifndef KONAN_NO_THREADS
return __sync_val_compare_and_swap(where, expectedValue, newValue);
#else
T oldValue = *where;
if (oldValue == expectedValue) {
*where = newValue;
}
return oldValue;
#endif
}
template <typename T>
ALWAYS_INLINE inline bool compareAndSet(volatile T* where, T expectedValue, T newValue) {
#ifndef KONAN_NO_THREADS
return __sync_bool_compare_and_swap(where, expectedValue, newValue);
#else
T oldValue = *where;
if (oldValue == expectedValue) {
*where = newValue;
return true;
}
return false;
#endif
}
#pragma clang diagnostic push
#if (KONAN_ANDROID || KONAN_IOS || KONAN_WATCHOS || KONAN_LINUX) && (KONAN_ARM32 || KONAN_X86 || KONAN_MIPS32 || KONAN_MIPSEL32)
// On 32-bit Android clang generates library calls for "large" atomic operations
// and warns about "significant performance penalty". See more details here:
// https://github.com/llvm/llvm-project/blob/ce56e1a1cc5714f4af5675dd963cfebed766d9e1/clang/lib/CodeGen/CGAtomic.cpp#L775
// Ignore these warnings:
#pragma clang diagnostic ignored "-Watomic-alignment"
#endif
template <typename T>
ALWAYS_INLINE inline void atomicSet(volatile T* where, T what) {
#ifndef KONAN_NO_THREADS
__atomic_store(where, &what, __ATOMIC_SEQ_CST);
#else
*where = what;
#endif
}
template <typename T>
ALWAYS_INLINE inline T atomicGet(volatile T* where) {
#ifndef KONAN_NO_THREADS
T what;
__atomic_load(where, &what, __ATOMIC_SEQ_CST);
return what;
#else
return *where;
#endif
}
#pragma clang diagnostic pop
static ALWAYS_INLINE inline void synchronize() {
#ifndef KONAN_NO_THREADS
__sync_synchronize();
#endif
}
#endif // RUNTIME_ATOMIC_H
@@ -0,0 +1,121 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Memory.h"
#include "Types.h"
// C++ part of box caching.
template<class T>
struct KBox {
ObjHeader header;
const T value;
};
// Keep naming of these in sync with codegen part.
extern const KBoolean BOOLEAN_RANGE_FROM;
extern const KBoolean BOOLEAN_RANGE_TO;
extern const KByte BYTE_RANGE_FROM;
extern const KByte BYTE_RANGE_TO;
extern const KChar CHAR_RANGE_FROM;
extern const KChar CHAR_RANGE_TO;
extern const KShort SHORT_RANGE_FROM;
extern const KShort SHORT_RANGE_TO;
extern const KInt INT_RANGE_FROM;
extern const KInt INT_RANGE_TO;
extern const KLong LONG_RANGE_FROM;
extern const KLong LONG_RANGE_TO;
extern KBox<KBoolean> BOOLEAN_CACHE[];
extern KBox<KByte> BYTE_CACHE[];
extern KBox<KChar> CHAR_CACHE[];
extern KBox<KShort> SHORT_CACHE[];
extern KBox<KInt> INT_CACHE[];
extern KBox<KLong> LONG_CACHE[];
namespace {
template<class T>
inline bool isInRange(T value, T from, T to) {
return value >= from && value <= to;
}
template<class T>
OBJ_GETTER(getCachedBox, T value, KBox<T> cache[], T from) {
uint64_t index = value - from;
RETURN_OBJ(&cache[index].header);
}
} // namespace
extern "C" {
bool inBooleanBoxCache(KBoolean value) {
return isInRange(value, BOOLEAN_RANGE_FROM, BOOLEAN_RANGE_TO);
}
bool inByteBoxCache(KByte value) {
return isInRange(value, BYTE_RANGE_FROM, BYTE_RANGE_TO);
}
bool inCharBoxCache(KChar value) {
return isInRange(value, CHAR_RANGE_FROM, CHAR_RANGE_TO);
}
bool inShortBoxCache(KShort value) {
return isInRange(value, SHORT_RANGE_FROM, SHORT_RANGE_TO);
}
bool inIntBoxCache(KInt value) {
return isInRange(value, INT_RANGE_FROM, INT_RANGE_TO);
}
bool inLongBoxCache(KLong value) {
return isInRange(value, LONG_RANGE_FROM, LONG_RANGE_TO);
}
OBJ_GETTER(getCachedBooleanBox, KBoolean value) {
RETURN_RESULT_OF(getCachedBox, value, BOOLEAN_CACHE, BOOLEAN_RANGE_FROM);
}
OBJ_GETTER(getCachedByteBox, KByte value) {
// Remember that KByte can't handle values >= 127
// so it can't be used as indexing type.
RETURN_RESULT_OF(getCachedBox, value, BYTE_CACHE, BYTE_RANGE_FROM);
}
OBJ_GETTER(getCachedCharBox, KChar value) {
RETURN_RESULT_OF(getCachedBox, value, CHAR_CACHE, CHAR_RANGE_FROM);
}
OBJ_GETTER(getCachedShortBox, KShort value) {
RETURN_RESULT_OF(getCachedBox, value, SHORT_CACHE, SHORT_RANGE_FROM);
}
OBJ_GETTER(getCachedIntBox, KInt value) {
RETURN_RESULT_OF(getCachedBox, value, INT_CACHE, INT_RANGE_FROM);
}
OBJ_GETTER(getCachedLongBox, KLong value) {
RETURN_RESULT_OF(getCachedBox, value, LONG_CACHE, LONG_RANGE_FROM);
}
}
@@ -0,0 +1,130 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "Cleaner.h"
#include "Memory.h"
#include "Runtime.h"
#include "Worker.h"
// Defined in Cleaner.kt
extern "C" void Kotlin_CleanerImpl_shutdownCleanerWorker(KInt, bool);
extern "C" KInt Kotlin_CleanerImpl_createCleanerWorker();
namespace {
struct CleanerImpl {
ObjHeader header;
KNativePtr cleanerStablePtr;
};
constexpr KInt kCleanerWorkerUninitialized = 0;
constexpr KInt kCleanerWorkerInitializing = -1;
constexpr KInt kCleanerWorkerShutdown = -2;
KInt globalCleanerWorker = kCleanerWorkerUninitialized;
void disposeCleaner(CleanerImpl* thiz) {
auto worker = atomicGet(&globalCleanerWorker);
RuntimeAssert(
worker != kCleanerWorkerUninitialized && worker != kCleanerWorkerInitializing,
"Cleaner worker must've been initialized by now");
if (worker == kCleanerWorkerShutdown) {
if (Kotlin_cleanersLeakCheckerEnabled()) {
konan::consoleErrorf(
"Cleaner %p was disposed during program exit\n"
"Use `Platform.isCleanersLeakCheckerActive = false` to avoid this check.\n",
thiz);
RuntimeCheck(false, "Terminating now");
}
return;
}
RuntimeAssert(worker > 0, "Cleaner worker must be fully initialized here");
bool result = WorkerSchedule(worker, thiz->cleanerStablePtr);
RuntimeAssert(result, "Couldn't find Cleaner worker");
}
} // namespace
RUNTIME_NOTHROW void DisposeCleaner(KRef thiz) {
#if KONAN_NO_EXCEPTIONS
disposeCleaner(reinterpret_cast<CleanerImpl*>(thiz));
#else
try {
disposeCleaner(reinterpret_cast<CleanerImpl*>(thiz));
} catch (...) {
// A trick to terminate with unhandled exception. This will print a stack trace
// and write to iOS crash log.
std::terminate();
}
#endif
}
void ShutdownCleaners(bool executeScheduledCleaners) {
KInt worker = 0;
do {
worker = atomicGet(&globalCleanerWorker);
RuntimeAssert(worker != kCleanerWorkerShutdown, "Cleaner worker must not be shutdown twice");
if (worker == kCleanerWorkerUninitialized) {
if (!compareAndSet(&globalCleanerWorker, kCleanerWorkerUninitialized, kCleanerWorkerShutdown)) {
// Someone is trying to initialize the worker. Try again.
continue;
}
// worker was never initialized. Just return.
return;
}
if (worker == kCleanerWorkerInitializing) {
// Someone is trying to initialize the worker. Try again.
continue;
}
// Worker is in some proper state.
break;
} while (true);
RuntimeAssert(worker > 0, "Cleaner worker must be fully initialized here");
atomicSet(&globalCleanerWorker, kCleanerWorkerShutdown);
Kotlin_CleanerImpl_shutdownCleanerWorker(worker, executeScheduledCleaners);
WaitNativeWorkerTermination(worker);
}
extern "C" KInt Kotlin_CleanerImpl_getCleanerWorker() {
KInt worker = 0;
do {
worker = atomicGet(&globalCleanerWorker);
RuntimeAssert(worker != kCleanerWorkerShutdown, "Cleaner worker must not have been shutdown");
if (worker == kCleanerWorkerUninitialized) {
if (!compareAndSet(&globalCleanerWorker, kCleanerWorkerUninitialized, kCleanerWorkerInitializing)) {
// Someone else is trying to initialize the worker. Try again.
continue;
}
worker = Kotlin_CleanerImpl_createCleanerWorker();
if (!compareAndSet(&globalCleanerWorker, kCleanerWorkerInitializing, worker)) {
RuntimeCheck(false, "Someone interrupted worker initializing");
}
// Worker is initialized.
break;
}
if (worker == kCleanerWorkerInitializing) {
// Someone is trying to initialize the worker. Try again.
continue;
}
// Worker is in some proper state.
break;
} while (true);
RuntimeAssert(worker > 0, "Cleaner worker must be fully initialized here");
return worker;
}
void ResetCleanerWorkerForTests() {
atomicSet(&globalCleanerWorker, kCleanerWorkerUninitialized);
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_CLEANER_H
#define RUNTIME_CLEANER_H
#include "Common.h"
#include "Types.h"
RUNTIME_NOTHROW void DisposeCleaner(KRef thiz);
void ShutdownCleaners(bool executeScheduledCleaners);
extern "C" KInt Kotlin_CleanerImpl_getCleanerWorker();
void ResetCleanerWorkerForTests();
#endif // RUNTIME_CLEANER_H
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "Cleaner.h"
#include <future>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "Atomic.h"
#include "TestSupportCompilerGenerated.hpp"
using testing::_;
// TODO: Also test disposal. (This requires extracting Worker interface)
TEST(CleanerTest, ConcurrentCreation) {
ResetCleanerWorkerForTests();
constexpr int threadCount = 100;
constexpr KInt workerId = 42;
auto createCleanerWorkerMock = ScopedCreateCleanerWorkerMock();
EXPECT_CALL(*createCleanerWorkerMock, Call()).Times(1).WillOnce(testing::Return(workerId));
int startedThreads = 0;
bool allowRunning = false;
std::vector<std::future<KInt>> futures;
for (int i = 0; i < threadCount; ++i) {
auto future = std::async(std::launch::async, [&startedThreads, &allowRunning]() {
atomicAdd(&startedThreads, 1);
while (!atomicGet(&allowRunning)) {
}
return Kotlin_CleanerImpl_getCleanerWorker();
});
futures.push_back(std::move(future));
}
while (atomicGet(&startedThreads) != threadCount) {
}
atomicSet(&allowRunning, true);
std::vector<KInt> values;
for (auto& future : futures) {
values.push_back(future.get());
}
ASSERT_THAT(values.size(), threadCount);
EXPECT_THAT(values, testing::Each(workerId));
}
TEST(CleanerTest, ShutdownWithoutCreation) {
ResetCleanerWorkerForTests();
auto createCleanerWorkerMock = ScopedCreateCleanerWorkerMock();
auto shutdownCleanerWorkerMock = ScopedShutdownCleanerWorkerMock();
EXPECT_CALL(*createCleanerWorkerMock, Call()).Times(0);
EXPECT_CALL(*shutdownCleanerWorkerMock, Call(_, _)).Times(0);
ShutdownCleaners(true);
}
TEST(CleanerTest, ShutdownWithCreation) {
ResetCleanerWorkerForTests();
constexpr KInt workerId = 42;
constexpr bool executeScheduledCleaners = true;
auto createCleanerWorkerMock = ScopedCreateCleanerWorkerMock();
auto shutdownCleanerWorkerMock = ScopedShutdownCleanerWorkerMock();
EXPECT_CALL(*createCleanerWorkerMock, Call()).WillOnce(testing::Return(workerId));
Kotlin_CleanerImpl_getCleanerWorker();
EXPECT_CALL(*shutdownCleanerWorkerMock, Call(workerId, executeScheduledCleaners));
ShutdownCleaners(executeScheduledCleaners);
}
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_COMMON_H
#define RUNTIME_COMMON_H
#define RUNTIME_NOTHROW __attribute__((nothrow))
#define RUNTIME_NORETURN __attribute__((noreturn))
#define RUNTIME_CONST __attribute__((const))
#define RUNTIME_PURE __attribute__((pure))
#define RUNTIME_USED __attribute__((used))
#define RUNTIME_WEAK __attribute__((weak))
#define ALWAYS_INLINE __attribute__((always_inline))
#define NO_INLINE __attribute__((noinline))
#if KONAN_NO_THREADS
#define THREAD_LOCAL_VARIABLE
#else
#define THREAD_LOCAL_VARIABLE __thread
#endif
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#if KONAN_OBJC_INTEROP
#define KONAN_TYPE_INFO_HAS_WRITABLE_PART 1
#endif
#endif // RUNTIME_COMMON_H
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "Memory.h"
namespace {
template <typename T>
T defaultValue() {
return T();
}
template <typename Ret, typename... Args>
void ensureUsed(Ret (*f)(Args...)) {
f(defaultValue<Args>()...);
}
} // namespace
// This is a hack to force clang to emit possibly unused declarations.
// TODO: Make sure this function gets DCE'd in the final binary.
// TODO: Should be done with some sort of annotation on the declaration.
void EnsureDeclarationsEmitted() {
ensureUsed(AllocInstance);
ensureUsed(AllocArrayInstance);
ensureUsed(InitInstance);
ensureUsed(InitSharedInstance);
ensureUsed(UpdateHeapRef);
ensureUsed(UpdateStackRef);
ensureUsed(UpdateReturnRef);
ensureUsed(ZeroHeapRef);
ensureUsed(ZeroArrayRefs);
ensureUsed(EnterFrame);
ensureUsed(LeaveFrame);
ensureUsed(AddTLSRecord);
ensureUsed(ClearTLSRecord);
ensureUsed(LookupTLS);
ensureUsed(MutationCheck);
ensureUsed(CheckLifetimesConstraint);
ensureUsed(FreezeSubgraph);
ensureUsed(FreezeSubgraph);
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KAssert.h"
#include "Memory.h"
#include "Natives.h"
#include "KString.h"
#include "Porting.h"
#include "Types.h"
#include "Exceptions.h"
#include "utf8.h"
extern "C" {
// io/Console.kt
void Kotlin_io_Console_print(KString message) {
if (message->type_info() != theStringTypeInfo) {
ThrowClassCastException(message->obj(), theStringTypeInfo);
}
// TODO: system stdout must be aware about UTF-8.
const KChar* utf16 = CharArrayAddressOfElementAt(message, 0);
KStdString utf8;
utf8.reserve(message->count_);
// Replace incorrect sequences with a default codepoint (see utf8::with_replacement::default_replacement)
utf8::with_replacement::utf16to8(utf16, utf16 + message->count_, back_inserter(utf8));
konan::consoleWriteUtf8(utf8.c_str(), utf8.size());
}
void Kotlin_io_Console_println(KString message) {
Kotlin_io_Console_print(message);
#ifndef KONAN_ANDROID
// On Android single print produces logcat entry, so no need in linefeed.
Kotlin_io_Console_println0();
#endif
}
void Kotlin_io_Console_println0() {
konan::consoleWriteUtf8("\n", 1);
}
OBJ_GETTER0(Kotlin_io_Console_readLine) {
char data[4096];
if (konan::consoleReadUtf8(data, sizeof(data)) < 0) {
RETURN_OBJ(nullptr);
}
RETURN_RESULT_OF(CreateStringFromCString, data);
}
} // extern "C"
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_DOUBLECONVERSIONS_H
#define RUNTIME_DOUBLECONVERSIONS_H
#include "Types.h"
namespace {
typedef union {
KLong l;
KDouble d;
} DoubleAlias;
typedef union {
KInt i;
KFloat f;
} FloatAlias;
}
inline KDouble bitsToDouble(KLong bits) {
DoubleAlias alias;
alias.l = bits;
return alias.d;
}
inline KLong doubleToBits(KDouble value) {
DoubleAlias alias;
alias.d = value;
return alias.l;
}
inline KFloat bitsToFloat(KInt bits) {
FloatAlias alias;
alias.i = bits;
return alias.f;
}
inline KInt floatToBits(KFloat value) {
FloatAlias alias;
alias.f = value;
return alias.i;
}
extern "C" KInt doubleUpper(KDouble value);
extern "C" KInt doubleLower(KDouble value);
#endif // RUNTIME_DOUBLECONVERSIONS_H
@@ -0,0 +1,347 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <exception>
#include <unistd.h>
#if KONAN_NO_EXCEPTIONS
#define OMIT_BACKTRACE 1
#endif
#ifndef OMIT_BACKTRACE
#if USE_GCC_UNWIND
// GCC unwinder for backtrace.
#include <unwind.h>
#else
// Glibc backtrace() function.
#include <execinfo.h>
#endif
#endif // OMIT_BACKTRACE
#include "KAssert.h"
#include "Exceptions.h"
#include "ExecFormat.h"
#include "Memory.h"
#include "Natives.h"
#include "KString.h"
#include "SourceInfo.h"
#include "Types.h"
#include "Utils.h"
#include "ObjCExceptions.h"
namespace {
// RuntimeUtils.kt
extern "C" void ReportUnhandledException(KRef throwable);
extern "C" void ExceptionReporterLaunchpad(KRef reporter, KRef throwable);
KRef currentUnhandledExceptionHook = nullptr;
int32_t currentUnhandledExceptionHookLock = 0;
int32_t currentUnhandledExceptionHookCookie = 0;
#if USE_GCC_UNWIND
struct Backtrace {
Backtrace(int count, int skip) : index(0), skipCount(skip) {
uint32_t size = count - skipCount;
if (size < 0) {
size = 0;
}
auto result = AllocArrayInstance(theNativePtrArrayTypeInfo, size, arrayHolder.slot());
// TODO: throw cached OOME?
RuntimeCheck(result != nullptr, "Cannot create backtrace array");
}
void setNextElement(_Unwind_Ptr element) {
Kotlin_NativePtrArray_set(obj(), index++, (KNativePtr) element);
}
ObjHeader* obj() { return arrayHolder.obj(); }
int index;
int skipCount;
ObjHolder arrayHolder;
};
_Unwind_Reason_Code depthCountCallback(
struct _Unwind_Context * context, void* arg) {
int* result = reinterpret_cast<int*>(arg);
(*result)++;
return _URC_NO_REASON;
}
_Unwind_Reason_Code unwindCallback(
struct _Unwind_Context* context, void* arg) {
Backtrace* backtrace = reinterpret_cast<Backtrace*>(arg);
if (backtrace->skipCount > 0) {
backtrace->skipCount--;
return _URC_NO_REASON;
}
#if (__MINGW32__ || __MINGW64__)
_Unwind_Ptr address = _Unwind_GetRegionStart(context);
#else
_Unwind_Ptr address = _Unwind_GetIP(context);
#endif
backtrace->setNextElement(address);
return _URC_NO_REASON;
}
#endif
THREAD_LOCAL_VARIABLE bool disallowSourceInfo = false;
SourceInfo getSourceInfo(KConstRef stackTrace, int index) {
return disallowSourceInfo
? SourceInfo { .fileName = nullptr, .lineNumber = -1, .column = -1 }
: Kotlin_getSourceInfo(*PrimitiveArrayAddressOfElementAt<KNativePtr>(stackTrace->array(), index));
}
} // namespace
// TODO: this implementation is just a hack, e.g. the result is inexact;
// however it is better to have an inexact stacktrace than not to have any.
NO_INLINE OBJ_GETTER0(Kotlin_getCurrentStackTrace) {
#if OMIT_BACKTRACE
return AllocArrayInstance(theNativePtrArrayTypeInfo, 0, OBJ_RESULT);
#else
// Skips first 2 elements as irrelevant: this function and primary Throwable constructor.
constexpr int kSkipFrames = 2;
#if USE_GCC_UNWIND
int depth = 0;
_Unwind_Backtrace(depthCountCallback, &depth);
Backtrace result(depth, kSkipFrames);
if (result.obj()->array()->count_ > 0) {
_Unwind_Backtrace(unwindCallback, &result);
}
RETURN_OBJ(result.obj());
#else
const int maxSize = 32;
void* buffer[maxSize];
int size = backtrace(buffer, maxSize);
if (size < kSkipFrames)
return AllocArrayInstance(theNativePtrArrayTypeInfo, 0, OBJ_RESULT);
ObjHolder resultHolder;
ObjHeader* result = AllocArrayInstance(theNativePtrArrayTypeInfo, size - kSkipFrames, resultHolder.slot());
for (int index = kSkipFrames; index < size; ++index) {
Kotlin_NativePtrArray_set(result, index - kSkipFrames, buffer[index]);
}
RETURN_OBJ(result);
#endif
#endif // !OMIT_BACKTRACE
}
OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace) {
#if OMIT_BACKTRACE
ObjHeader* result = AllocArrayInstance(theArrayTypeInfo, 1, OBJ_RESULT);
ObjHolder holder;
CreateStringFromCString("<UNIMPLEMENTED>", holder.slot());
UpdateHeapRef(ArrayAddressOfElementAt(result->array(), 0), holder.obj());
return result;
#else
uint32_t size = stackTrace->array()->count_;
ObjHolder resultHolder;
ObjHeader* strings = AllocArrayInstance(theArrayTypeInfo, size, resultHolder.slot());
#if USE_GCC_UNWIND
for (uint32_t index = 0; index < size; ++index) {
KNativePtr address = Kotlin_NativePtrArray_get(stackTrace, index);
char symbol[512];
if (!AddressToSymbol((const void*) address, symbol, sizeof(symbol))) {
// Make empty string:
symbol[0] = '\0';
}
char line[512];
konan::snprintf(line, sizeof(line) - 1, "%s (%p)", symbol, (void*)(intptr_t)address);
ObjHolder holder;
CreateStringFromCString(line, holder.slot());
UpdateHeapRef(ArrayAddressOfElementAt(strings->array(), index), holder.obj());
}
#else
if (size > 0) {
char **symbols = backtrace_symbols(PrimitiveArrayAddressOfElementAt<KNativePtr>(stackTrace->array(), 0), size);
RuntimeCheck(symbols != nullptr, "Not enough memory to retrieve the stacktrace");
for (uint32_t index = 0; index < size; ++index) {
auto sourceInfo = getSourceInfo(stackTrace, index);
const char* symbol = symbols[index];
const char* result;
char line[1024];
if (sourceInfo.fileName != nullptr) {
if (sourceInfo.lineNumber != -1) {
konan::snprintf(line, sizeof(line) - 1, "%s (%s:%d:%d)",
symbol, sourceInfo.fileName, sourceInfo.lineNumber, sourceInfo.column);
} else {
konan::snprintf(line, sizeof(line) - 1, "%s (%s:<unknown>)", symbol, sourceInfo.fileName);
}
result = line;
} else {
result = symbol;
}
ObjHolder holder;
CreateStringFromCString(result, holder.slot());
UpdateHeapRef(ArrayAddressOfElementAt(strings->array(), index), holder.obj());
}
// Not konan::free. Used to free memory allocated in backtrace_symbols where malloc is used.
free(symbols);
}
#endif
RETURN_OBJ(strings);
#endif // !OMIT_BACKTRACE
}
void ThrowException(KRef exception) {
RuntimeAssert(exception != nullptr && IsInstance(exception, theThrowableTypeInfo),
"Throwing something non-throwable");
#if KONAN_NO_EXCEPTIONS
PrintThrowable(exception);
RuntimeCheck(false, "Exceptions unsupported");
#else
throw ExceptionObjHolder(exception);
#endif
}
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook) {
RETURN_RESULT_OF(SwapHeapRefLocked,
&currentUnhandledExceptionHook, currentUnhandledExceptionHook, hook, &currentUnhandledExceptionHookLock,
&currentUnhandledExceptionHookCookie);
}
void OnUnhandledException(KRef throwable) {
ObjHolder handlerHolder;
auto* handler = SwapHeapRefLocked(&currentUnhandledExceptionHook, currentUnhandledExceptionHook, nullptr,
&currentUnhandledExceptionHookLock, &currentUnhandledExceptionHookCookie, handlerHolder.slot());
if (handler == nullptr) {
ReportUnhandledException(throwable);
} else {
ExceptionReporterLaunchpad(handler, throwable);
}
}
namespace {
class {
/**
* Timeout 5 sec for concurrent (second) terminate attempt to give a chance the first one to finish.
* If the terminate handler hangs for 5 sec it is probably fatally broken, so let's do abnormal _Exit in that case.
*/
unsigned int timeoutSec = 5;
int terminatingFlag = 0;
public:
template <class Fun> RUNTIME_NORETURN void operator()(Fun block) {
if (compareAndSet(&terminatingFlag, 0, 1)) {
block();
// block() is supposed to be NORETURN, otherwise go to normal abort()
konan::abort();
} else {
sleep(timeoutSec);
// We come here when another terminate handler hangs for 5 sec, that looks fatally broken. Go to forced exit now.
}
_Exit(EXIT_FAILURE); // force exit
}
} concurrentTerminateWrapper;
//! Process exception hook (if any) or just printStackTrace + write crash log
void processUnhandledKotlinException(KRef throwable) {
OnUnhandledException(throwable);
#if KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG
ReportBacktraceToIosCrashLog(throwable);
#endif
}
} // namespace
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef throwable) {
concurrentTerminateWrapper([=]() {
processUnhandledKotlinException(throwable);
konan::abort();
});
}
// Some libstdc++-based targets has limited support for std::current_exception and other C++11 functions.
// This restriction can be lifted later when toolchains will be updated.
#if KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS
namespace {
class TerminateHandler {
// In fact, it's safe to call my_handler directly from outside: it will do the job and then invoke original handler,
// even if it has not been initialized yet. So one may want to make it public and/or not the class member
RUNTIME_NORETURN static void kotlinHandler() {
concurrentTerminateWrapper([]() {
if (auto currentException = std::current_exception()) {
try {
std::rethrow_exception(currentException);
} catch (ExceptionObjHolder& e) {
processUnhandledKotlinException(e.obj());
konan::abort();
} catch (...) {
// Not a Kotlin exception - call default handler
instance().queuedHandler_();
}
}
// Come here in case of direct terminate() call or unknown exception - go to default terminate handler.
instance().queuedHandler_();
});
}
using QH = __attribute__((noreturn)) void(*)();
QH queuedHandler_;
/// Use machinery like Meyers singleton to provide thread safety
TerminateHandler()
: queuedHandler_((QH)std::set_terminate(kotlinHandler)) {}
static TerminateHandler& instance() {
static TerminateHandler singleton [[clang::no_destroy]];
return singleton;
}
// Copy, move and assign would be safe, but not much useful, so let's delete all (rule of 5)
TerminateHandler(const TerminateHandler&) = delete;
TerminateHandler(TerminateHandler&&) = delete;
TerminateHandler& operator=(const TerminateHandler&) = delete;
TerminateHandler& operator=(TerminateHandler&&) = delete;
// Dtor might be in use to restore original handler. However, consequent install
// will not reconstruct handler anyway, so let's keep dtor deleted to avoid confusion.
~TerminateHandler() = delete;
public:
/// First call will do the job, all consequent will do nothing.
static void install() {
instance(); // Use side effect of warming up
}
};
} // anon namespace
// Use one public function to limit access to the class declaration
void SetKonanTerminateHandler() {
TerminateHandler::install();
}
#else // KONAN_OBJC_INTEROP
void SetKonanTerminateHandler() {
// Nothing to do.
}
#endif // KONAN_OBJC_INTEROP
void DisallowSourceInfo() {
disallowSourceInfo = true;
}
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_EXCEPTIONS_H
#define RUNTIME_EXCEPTIONS_H
#include "Types.h"
#ifdef __cplusplus
extern "C" {
#endif
// Returns current stacktrace as Array<String>.
OBJ_GETTER0(Kotlin_getCurrentStackTrace);
OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace);
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook);
// Throws arbitrary exception.
void ThrowException(KRef exception);
void OnUnhandledException(KRef throwable);
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef exception);
void SetKonanTerminateHandler();
// The functions below are implemented in Kotlin (at package kotlin.native.internal).
// Throws null pointer exception. Context is evaluated from caller's address.
void RUNTIME_NORETURN ThrowNullPointerException();
// Throws array index out of bounds exception.
// Context is evaluated from caller's address.
void RUNTIME_NORETURN ThrowArrayIndexOutOfBoundsException();
// Throws class cast exception.
void RUNTIME_NORETURN ThrowClassCastException(const ObjHeader* instance, const TypeInfo* type_info);
// Throws arithmetic exception.
void RUNTIME_NORETURN ThrowArithmeticException();
// Throws number format exception.
void RUNTIME_NORETURN ThrowNumberFormatException();
// Throws out of memory error.
void RUNTIME_NORETURN ThrowOutOfMemoryError();
// Throws not implemented error.
void RUNTIME_NORETURN ThrowNotImplementedError();
// Throws character coding exception (used in UTF8/UTF16 conversions).
void RUNTIME_NORETURN ThrowCharacterCodingException();
void RUNTIME_NORETURN ThrowIllegalArgumentException();
void RUNTIME_NORETURN ThrowIllegalStateException();
void RUNTIME_NORETURN ThrowInvalidMutabilityException(KConstRef where);
void RUNTIME_NORETURN ThrowIncorrectDereferenceException();
void RUNTIME_NORETURN ThrowIllegalObjectSharingException(KConstNativePtr typeInfo, KConstNativePtr address);
void RUNTIME_NORETURN ThrowFreezingException(KRef toFreeze, KRef blocker);
// Prints out message of Throwable.
void PrintThrowable(KRef);
#ifdef __cplusplus
} // extern "C"
#endif
// It's not always safe to extract SourceInfo during unhandled exception termination.
void DisallowSourceInfo();
#endif // RUNTIME_NAMES_H
@@ -0,0 +1,333 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ExecFormat.h"
#include "Types.h"
#if USE_ELF_SYMBOLS
#include <dlfcn.h>
#include <elf.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <vector>
#include "KAssert.h"
namespace {
#if !defined(ELFSIZE)
#error "Define ELFSIZE to 32 or 64"
#endif
#if ELFSIZE == 32
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#elif ELFSIZE == 64
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#else
#error "Impossible ELFSIZE"
#endif
struct SymRecord {
Elf_Sym* symtabBegin;
Elf_Sym* symtabEnd;
char* strtab;
};
typedef KStdVector<SymRecord> SymRecordList;
SymRecordList* symbols = nullptr;
// Unfortunately, symbol tables are stored in ELF sections not mapped
// during regular execution, so we have to map binary ourselves.
Elf_Ehdr* findElfHeader() {
int fd = open("/proc/self/exe", O_RDONLY);
if (fd < 0) return nullptr;
struct stat fd_stat;
if (fstat(fd, &fd_stat) < 0) return nullptr;
void* result = mmap(nullptr, fd_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (result == MAP_FAILED) return nullptr;
return (Elf_Ehdr*)result;
}
void initSymbols() {
RuntimeAssert(symbols == nullptr, "Init twice");
symbols = konanConstructInstance<SymRecordList>();
Elf_Ehdr* ehdr = findElfHeader();
if (ehdr == nullptr) return;
RuntimeAssert(strncmp((const char*)ehdr->e_ident, ELFMAG, SELFMAG) == 0, "Must be an ELF");
char* mapAddress = (char*)ehdr;
Elf_Shdr* shdr = (Elf_Shdr*)(mapAddress + ehdr->e_shoff);
for (int i = 0; i < ehdr->e_shnum; i++) {
if (shdr[i].sh_type == SHT_SYMTAB) { // Static symbol table.
SymRecord record;
record.symtabBegin = (Elf_Sym*)(mapAddress + shdr[i].sh_offset);
record.symtabEnd = (Elf_Sym*)((char*)record.symtabBegin + shdr[i].sh_size);
record.strtab = (char *)(mapAddress + shdr[shdr[i].sh_link].sh_offset);
symbols->push_back(record);
}
if (shdr[i].sh_type == SHT_DYNSYM) { // Dynamic symbol table.
SymRecord record;
record.symtabBegin = (Elf_Sym*)(mapAddress + shdr[i].sh_offset);
record.symtabEnd = (Elf_Sym*)((char*)record.symtabBegin + shdr[i].sh_size);
record.strtab = (char*)(mapAddress + shdr[shdr[i].sh_link].sh_offset);
symbols->push_back(record);
}
}
}
const char* addressToSymbol(const void* address) {
if (address == nullptr) return nullptr;
// First, look up in dynamically loaded symbols.
Dl_info info;
if (dladdr(address, &info) != 0 && info.dli_sname != nullptr) {
return info.dli_sname;
}
// Otherwise, consult symbol table of the file.
if (symbols == nullptr) {
initSymbols();
}
unsigned long addressValue = (unsigned long)address;
for (auto record : *symbols) {
auto begin = record.symtabBegin;
auto end = record.symtabEnd;
while (begin < end) {
// st_value is load address adjusted.
if (addressValue >= begin->st_value && addressValue < begin->st_value + begin->st_size) {
return &record.strtab[begin->st_name];
}
begin++;
}
}
return nullptr;
}
} // namespace
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) {
const char* result = addressToSymbol(address);
if (result == nullptr) {
return false;
} else {
strncpy(resultBuffer, result, resultBufferSize);
resultBuffer[resultBufferSize - 1] = '\0';
return true;
}
}
#elif USE_PE_COFF_SYMBOLS
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include "KAssert.h"
namespace {
static void* mapModuleFile(HMODULE hModule) {
DWORD bufferLength = 64;
wchar_t* buffer = nullptr;
for (;;) {
auto newBuffer = (wchar_t*)konanAllocMemory(sizeof(wchar_t) * bufferLength);
RuntimeAssert(newBuffer != nullptr, "Out of memory");
if (buffer != nullptr) {
konanFreeMemory(buffer);
}
buffer = newBuffer;
DWORD res = GetModuleFileNameW(hModule, buffer, bufferLength);
if (res != 0 && res < bufferLength) {
break;
}
const int MAX_BUFFER_SIZE = 32768; // Max path length + 1.
if (res == bufferLength && bufferLength < MAX_BUFFER_SIZE) {
// Buffer is too small, continue:
bufferLength *= 2;
continue;
}
// Invalid result.
konanFreeMemory(buffer);
return nullptr;
}
HANDLE hFile = CreateFileW(
/* lpFileName = */ buffer,
/* dwDesiredAccess = */ GENERIC_READ,
/* dwShareMode = */ FILE_SHARE_READ,
/* lpSecurityAttributes = */ nullptr,
/* dwCreationDisposition = */ OPEN_EXISTING,
/* dwFlagsAndAttributes = */ FILE_ATTRIBUTE_NORMAL,
/* hTemplateFile = */ nullptr
);
konanFreeMemory(buffer);
if (hFile == INVALID_HANDLE_VALUE) {
// Can't open module file.
return nullptr;
}
HANDLE hFileMappingObject = CreateFileMapping(
hFile,
/* lpAttributes = */ nullptr,
/* flProtect = */ PAGE_READONLY,
/* dwMaximumSizeHigh = */ 0,
/* dwMaximumSizeLow = */ 0,
/* lpName = */ nullptr
);
if (hFileMappingObject == nullptr) {
// Can't create file mapping.
CloseHandle(hFile);
return nullptr;
}
LPVOID mapAddress = MapViewOfFile(
hFileMappingObject,
/* dwDesiredAccess = */ FILE_MAP_READ,
/* dwFileOffsetHigh = */ 0,
/* dwFileOffsetLow = */ 0,
/* dwNumberOfBytesToMap = */ 0
);
if (mapAddress == nullptr) {
// Failed to create map view.
CloseHandle(hFileMappingObject);
CloseHandle(hFile);
return nullptr;
}
return mapAddress;
}
class SymbolTable {
private:
char* imageBase = nullptr;
IMAGE_SECTION_HEADER* sectionHeaders = nullptr;
IMAGE_SYMBOL* symbols = nullptr;
DWORD numberOfSymbols = 0;
// Note: it doesn't free resources yet.
~SymbolTable() {}
static const int SYMBOL_SHORT_NAME_LENGTH = 8;
void getSymbolName(IMAGE_SYMBOL* sym, char* resultBuffer, size_t resultBufferSize) {
if (sym->N.Name.Short != 0) {
// ShortName is not zero-terminated if its length exactly equals SYMBOL_SHORT_NAME_LENGTH.
// Copy it to the buffer and zero-terminate explicitly:
size_t bytesToCopy = SYMBOL_SHORT_NAME_LENGTH;
if (bytesToCopy > resultBufferSize - 1) bytesToCopy = resultBufferSize - 1;
memcpy(resultBuffer, sym->N.ShortName, bytesToCopy);
resultBuffer[bytesToCopy] = '\0';
} else {
const char* strTable = (const char*)(symbols + numberOfSymbols);
const char* result = strTable + sym->N.Name.Long;
strncpy(resultBuffer, result, resultBufferSize);
resultBuffer[resultBufferSize - 1] = '\0';
}
}
const void* getSymbolAddress(IMAGE_SYMBOL* symbol) {
IMAGE_SECTION_HEADER* sectionHeader = &sectionHeaders[symbol->SectionNumber - 1];
return (const void*)(imageBase + sectionHeader->VirtualAddress + symbol->Value);
}
IMAGE_SYMBOL* findFunctionSymbol(const void* address) {
for (DWORD i = 0; i < numberOfSymbols; ++i) {
IMAGE_SYMBOL* symbol = &symbols[i];
if (symbol->Type == 0x20 && address == getSymbolAddress(symbol)) {
return symbol;
}
}
return nullptr;
}
public:
explicit SymbolTable(HMODULE hModule) {
imageBase = (char*)hModule;
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)imageBase;
RuntimeAssert(dosHeader->e_magic == IMAGE_DOS_SIGNATURE, "PE executable e_magic mismatch");
IMAGE_NT_HEADERS* ntHeaders = (IMAGE_NT_HEADERS*)(imageBase + dosHeader->e_lfanew);
RuntimeAssert(ntHeaders->Signature == IMAGE_NT_SIGNATURE, "PE executable NT signature mismatch");
IMAGE_FILE_HEADER* fileHeader = &ntHeaders->FileHeader;
sectionHeaders = (IMAGE_SECTION_HEADER*)(((char*)(fileHeader + 1)) + fileHeader->SizeOfOptionalHeader);
if (fileHeader->PointerToSymbolTable == 0 || fileHeader->NumberOfSymbols == 0) {
// No symbols.
return;
}
// Symbol table doesn't get mapped to the memory, so we have to load it ourselves:
char* mappedModuleFile = (char*)mapModuleFile(hModule);
if (mappedModuleFile != nullptr) {
symbols = (IMAGE_SYMBOL*)(mappedModuleFile + fileHeader->PointerToSymbolTable);
numberOfSymbols = fileHeader->NumberOfSymbols;
}
}
bool functionAddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) {
IMAGE_SYMBOL* symbol = findFunctionSymbol(address);
if (symbol == nullptr) {
return false;
} else {
getSymbolName(symbol, resultBuffer, resultBufferSize);
return true;
}
}
};
SymbolTable* theExeSymbolTable = nullptr;
} // namespace
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) {
if (theExeSymbolTable == nullptr) {
// Note: do not protecting the lazy initialization by critical sections for simplicity;
// this doesn't have any serious consequences.
HMODULE hModule = nullptr;
int rv = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&AddressToSymbol), &hModule);
RuntimeAssert(rv != 0, "GetModuleHandleExW fails");
theExeSymbolTable = konanConstructInstance<SymbolTable>(hModule);
}
return theExeSymbolTable->functionAddressToSymbol(address, resultBuffer, resultBufferSize);
}
#else
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) {
return false;
}
#endif // USE_ELF_SYMBOLS
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_EXECFORMAT_H
#define RUNTIME_EXECFORMAT_H
#include <stddef.h>
extern "C" {
bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize);
} // extern "C"
#endif // RUNTIME_EXECFORMAT_H
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include "Alloc.h"
#include "Memory.h"
#include "MemorySharedRefs.hpp"
#include "Types.h"
extern "C" {
KNativePtr Kotlin_Interop_createStablePointer(KRef any) {
KRefSharedHolder* holder = konanConstructInstance<KRefSharedHolder>();
holder->init(any);
return holder;
}
void Kotlin_Interop_disposeStablePointer(KNativePtr pointer) {
KRefSharedHolder* holder = reinterpret_cast<KRefSharedHolder*>(pointer);
holder->dispose();
konanDestructInstance(holder);
}
OBJ_GETTER(Kotlin_Interop_derefStablePointer, KNativePtr pointer) {
KRefSharedHolder* holder = reinterpret_cast<KRefSharedHolder*>(pointer);
RETURN_OBJ(holder->ref<ErrorPolicy::kThrow>());
}
}
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Porting.h"
#include "Types.h"
typedef KInt Arena;
typedef KInt Object;
typedef KInt Pointer;
#ifndef KONAN_WASM
extern "C" {
// These functions are implemented in JS file for WASM and are not available on other platforms.
RUNTIME_NORETURN Arena Konan_js_allocateArena() {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN void Konan_js_freeArena(Arena arena) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN void Konan_js_pushIntToArena(Arena arena, KInt value) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN KInt Konan_js_getInt(Arena arena,
Object obj,
Pointer propertyPtr,
KInt propertyLen) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN KInt Konan_js_getProperty(Arena arena,
Object obj,
Pointer propertyPtr,
KInt propertyLen) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN void Konan_js_setFunction(Arena arena,
Object obj,
Pointer propertyName,
KInt propertyLength,
KInt function) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
RUNTIME_NORETURN void Konan_js_setString(Arena arena,
Object obj,
Pointer propertyName,
KInt propertyLength,
Pointer stringPtr,
KInt stringLength) {
RuntimeAssert(false, "JavaScript interop is disabled");
konan::abort();
}
}; // extern "C"
#endif // #ifndef KONAN_WASM
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KAssert.h"
#include "Porting.h"
void RuntimeAssertFailed(const char* location, const char* message) {
// TODO: produce stacktrace and such.
char buf[1024];
if (location != nullptr)
konan::snprintf(buf, sizeof(buf), "%s: runtime assert: %s\n", location, message);
else
konan::snprintf(buf, sizeof(buf), "runtime assert: %s\n", message);
konan::consoleErrorUtf8(buf, konan::strnlen(buf, sizeof(buf)));
konan::abort();
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_ASSERT_H
#define RUNTIME_ASSERT_H
#include "Common.h"
// To avoid cluttering optimized code with asserts, they could be turned off.
#define KONAN_ENABLE_ASSERT 1
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* message);
// During codegeneration we set this constant to 1 or 0 to allow bitcode optimizer
// to get rid of code behind condition.
extern "C" const int KonanNeedDebugInfo;
#if KONAN_ENABLE_ASSERT
// Use RuntimeAssert() in internal state checks, which could be ignored in production.
#define RuntimeAssert(condition, message) \
if (KonanNeedDebugInfo && (!(condition))) { \
RuntimeAssertFailed( __FILE__ ":" TOSTRING(__LINE__), message); \
}
#else
#define RuntimeAssert(condition, message)
#endif
// Use RuntimeCheck() in runtime checks that could fail due to external condition and shall lead
// to program termination. Never compiled out.
#define RuntimeCheck(condition, message) \
if (!(condition)) { \
RuntimeAssertFailed(nullptr, message); \
}
#endif // RUNTIME_ASSERT_H
@@ -0,0 +1,97 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_KDEBUG_H
#define RUNTIME_KDEBUG_H
#include "Common.h"
#include "Memory.h"
#include "Types.h"
#include "TypeInfo.h"
#ifndef KONAN_NO_DEBUG_API
#ifdef __cplusplus
extern "C" {
#endif
// PLEASE READ: please do not alter signatures of the existing functions, and when adding
// the new function please do not forget to add new functions into the debug operations list.
// Get memory buffer where debugger can put data in Konan app process.
RUNTIME_USED RUNTIME_WEAK
char* Konan_DebugBuffer();
// Same, but runtime-specific.
RUNTIME_USED RUNTIME_WEAK
char* Konan_DebugBufferWithObject(KRef obj);
// Get size of memory buffer where debugger can put data in Konan app process.
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugBufferSize();
// Same, but runtime-specific.
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugBufferSizeWithObject(KRef obj);
// Put string representation of an object to the provided buffer.
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, int32_t bufferSize);
// Print to console string representation of an object.
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugPrint(KRef obj);
// Returns 1 if obj refers to an array, string or binary blob and 0 otherwise.
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugIsArray(KRef obj);
// Returns number of fields in an objects, or elements in an array.
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugGetFieldCount(KRef obj);
// Compute type of field or an array element at the index, or 0, if incorrect,
// see Konan_RuntimeType.
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugGetFieldType(KRef obj, int32_t index);
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED RUNTIME_WEAK
void* Konan_DebugGetFieldAddress(KRef obj, int32_t index);
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED RUNTIME_WEAK
const char* Konan_DebugGetFieldName(KRef obj, int32_t index);
// Returns name of type.
RUNTIME_USED RUNTIME_WEAK
const char* Konan_DebugGetTypeName(KRef obj);
/**
* Given an object finds debugger interface operation suitable for manipulation with this object.
* Important for cases where multiple K/N runtimes coexist in the same address space and debugger
* doesn't know which debug operation to use on particular instance.
*/
RUNTIME_USED RUNTIME_WEAK
void* Konan_DebugGetOperation(KRef obj, /* Konan_DebugOperation */ int32_t operation);
#ifdef __cplusplus
}
#endif
#endif // !KONAN_NO_DEBUG_API
#endif // RUNTIME_KDEBUG_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_KSTRING_H
#define RUNTIME_KSTRING_H
#include "Common.h"
#include "Memory.h"
#include "Types.h"
#include "TypeInfo.h"
#ifdef __cplusplus
extern "C" {
#endif
OBJ_GETTER(CreateStringFromCString, const char* cstring);
OBJ_GETTER(CreateStringFromUtf8, const char* utf8, uint32_t lengthBytes);
char* CreateCStringFromString(KConstRef kstring);
void DisposeCString(char* cstring);
#ifdef __cplusplus
}
#endif
template <typename T>
int binarySearchRange(const T* array, int arrayLength, T needle) {
int bottom = 0;
int top = arrayLength - 1;
int middle = -1;
T value = 0;
while (bottom <= top) {
middle = (bottom + top) / 2;
value = array[middle];
if (needle > value)
bottom = middle + 1;
else if (needle == value)
return middle;
else
top = middle - 1;
}
return middle - (needle < value ? 1 : 0);
}
#endif // RUNTIME_KSTRING_H
@@ -0,0 +1,580 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include "DoubleConversions.h"
#include "Exceptions.h"
#include "KotlinMath.h"
#include "ReturnSlot.h"
#include "Types.h"
#if (__MINGW32__ || __MINGW64__)
#define KONAN_NEED_ASINH_ACOSH 1
#else
#define KONAN_NEED_ASINH_ACOSH 0
#endif
#if KONAN_NEED_ASINH_ACOSH
namespace {
// MinGW's implmenetation of asinh/acosh function returns NaN for large arguments so we use another implementation.
// Both implementations derived from boost special math functions and are also used by Kotlin/JVM.
// Copyright Eric Ford & Hubert Holin 2001.
constexpr KDouble LN2 = 0.69314718055994530942;
constexpr KDouble SQRT2 = 1.41421356237309504880;
KDouble taylor_2_bound = sqrt(DBL_EPSILON);
KDouble taylor_n_bound = sqrt(taylor_2_bound);
KDouble upper_taylor_2_bound = 1.0 / taylor_2_bound;
KDouble upper_taylor_n_bound = 1.0 / taylor_n_bound;
KDouble custom_asinh(KDouble x) {
if (x >= +taylor_n_bound) {
if (x > upper_taylor_n_bound) {
if (x > upper_taylor_2_bound) {
// approximation by laurent series in 1/x at 0+ order from -1 to 0
return log(x) + LN2;
} else {
// approximation by laurent series in 1/x at 0+ order from -1 to 1
return log(x * 2 + (1.0 / (x * 2)));
}
} else {
return log(x + sqrt(x * x + 1));
}
} else if (x <= -taylor_n_bound) {
return -custom_asinh(-x);
} else {
// approximation by taylor series in x at 0 up to order 2
KDouble result = x;
if (fabs(x) >= taylor_2_bound) {
// approximation by taylor series in x at 0 up to order 4
result -= (x * x * x) / 6;
}
return result;
}
}
KDouble custom_acosh(KDouble x) {
if (x < 1) {
return NAN;
} else if (x > upper_taylor_2_bound) {
// approximation by laurent series in 1/x at 0+ order from -1 to 0
return log(x) + LN2;
} else if (x - 1 >= taylor_n_bound) {
return log(x + sqrt(x * x - 1));
} else {
KDouble y = sqrt(x - 1);
// approximation by taylor series in y at 0 up to order 2
KDouble result = y;
if (y >= taylor_2_bound) {
// approximation by taylor series in y at 0 up to order 4
result -= (y * y * y) / 12;
}
return SQRT2 * result;
}
}
}
#endif
extern "C" {
#ifndef KONAN_NO_MATH // We have a platform math library. Call its math functions.
#ifndef KONAN_WASM // Use libm.
// region Double math.
KDouble Kotlin_math_sin(KDouble x) { return sin(x); }
KDouble Kotlin_math_cos(KDouble x) { return cos(x); }
KDouble Kotlin_math_tan(KDouble x) { return tan(x); }
KDouble Kotlin_math_asin(KDouble x) { return asin(x); }
KDouble Kotlin_math_acos(KDouble x) { return acos(x); }
KDouble Kotlin_math_atan(KDouble x) { return atan(x); }
KDouble Kotlin_math_atan2(KDouble y, KDouble x) { return atan2(y, x); }
KDouble Kotlin_math_sinh(KDouble x) { return sinh(x); }
KDouble Kotlin_math_cosh(KDouble x) { return cosh(x); }
KDouble Kotlin_math_tanh(KDouble x) { return tanh(x); }
KDouble Kotlin_math_asinh(KDouble x) {
#if (KONAN_NEED_ASINH_ACOSH)
return custom_asinh(x);
#else
return asinh(x);
#endif
}
KDouble Kotlin_math_acosh(KDouble x) {
#if (KONAN_NEED_ASINH_ACOSH)
return custom_acosh(x);
#else
return acosh(x);
#endif
}
KDouble Kotlin_math_atanh(KDouble x) { return atanh(x); }
KDouble Kotlin_math_hypot(KDouble x, KDouble y) {
if (isinf(x) || isinf(y)) return INFINITY;
if (isnan(x) || isnan(y)) return NAN;
return hypot(x, y);
}
KDouble Kotlin_math_sqrt(KDouble x) { return sqrt(x); }
KDouble Kotlin_math_exp(KDouble x) { return exp(x); }
KDouble Kotlin_math_expm1(KDouble x) { return expm1(x); }
KDouble Kotlin_math_ln(KDouble x) { return log(x); }
KDouble Kotlin_math_log10(KDouble x) { return log10(x); }
KDouble Kotlin_math_log2(KDouble x) { return log2(x); }
KDouble Kotlin_math_ln1p(KDouble x) { return log1p(x); }
KDouble Kotlin_math_ceil(KDouble x) { return ceil(x); }
KDouble Kotlin_math_floor(KDouble x) { return floor(x); }
KDouble Kotlin_math_round(KDouble x) { return rint(x); }
KDouble Kotlin_math_abs(KDouble x) { return fabs(x); }
// extensions
KDouble Kotlin_math_Double_pow(KDouble thiz, KDouble x) {
// Kotlin corner cases
if (x == 0.0 || x == -0.0) return 1.0;
if (isinf(x) && (thiz == 1.0 || thiz == -1.0)) return NAN;
return pow(thiz, x);
}
KDouble Kotlin_math_Double_IEEErem(KDouble thiz, KDouble divisor) { return remainder(thiz, divisor); }
KDouble Kotlin_math_Double_withSign(KDouble thiz, KDouble sign) { return copysign(thiz, sign); }
KDouble Kotlin_math_Double_nextUp(KDouble thiz) { return nextafter(thiz, HUGE_VAL); }
KDouble Kotlin_math_Double_nextDown(KDouble thiz) { return nextafter(thiz, -HUGE_VAL); }
KDouble Kotlin_math_Double_nextTowards(KDouble thiz, KDouble to) {
return (thiz == to) ? to : nextafter(thiz, to);
}
KBoolean Kotlin_math_Double_signBit(KDouble thiz) { return signbit(thiz) != 0; }
// endregion
// region Float math.
KFloat Kotlin_math_sinf(KFloat x) { return sinf(x); }
KFloat Kotlin_math_cosf(KFloat x) { return cosf(x); }
KFloat Kotlin_math_tanf(KFloat x) { return tanf(x); }
KFloat Kotlin_math_asinf(KFloat x) { return asinf(x); }
KFloat Kotlin_math_acosf(KFloat x) { return acosf(x); }
KFloat Kotlin_math_atanf(KFloat x) { return atanf(x); }
KFloat Kotlin_math_atan2f(KFloat y, KFloat x) { return atan2f(y, x); }
KFloat Kotlin_math_sinhf(KFloat x) { return sinhf(x); }
KFloat Kotlin_math_coshf(KFloat x) { return coshf(x); }
KFloat Kotlin_math_tanhf(KFloat x) { return tanhf(x); }
KFloat Kotlin_math_asinhf(KFloat x) {
#if (KONAN_NEED_ASINH_ACOSH)
return (KFloat)custom_asinh((KDouble)x);
#else
return asinhf(x);
#endif
}
KFloat Kotlin_math_acoshf(KFloat x) {
#if (KONAN_NEED_ASINH_ACOSH)
return (KFloat)custom_acosh((KDouble)x);
#else
return acoshf(x);
#endif
}
KFloat Kotlin_math_atanhf(KFloat x) { return atanhf(x); }
KFloat Kotlin_math_hypotf(KFloat x, KFloat y) {
if (isinf(x) || isinf(y)) return INFINITY;
if (isnan(x) || isnan(y)) return NAN;
return hypotf(x, y);
}
KFloat Kotlin_math_sqrtf(KFloat x) { return sqrtf(x); }
KFloat Kotlin_math_expf(KFloat x) { return expf(x); }
KFloat Kotlin_math_expm1f(KFloat x) { return expm1f(x); }
KFloat Kotlin_math_lnf(KFloat x) { return logf(x); }
KFloat Kotlin_math_log10f(KFloat x) { return log10f(x); }
KFloat Kotlin_math_log2f(KFloat x) { return log2f(x); }
KFloat Kotlin_math_ln1pf(KFloat x) { return log1pf(x); }
KFloat Kotlin_math_ceilf(KFloat x) { return ceilf(x); }
KFloat Kotlin_math_floorf(KFloat x) { return floorf(x); }
KFloat Kotlin_math_roundf(KFloat x) { return rintf(x); }
KFloat Kotlin_math_absf(KFloat x) { return fabsf(x); }
// extensions
KFloat Kotlin_math_Float_pow(KFloat thiz, KFloat x) {
// Kotlin corner cases
if (x == 0.0 || x == -0.0) return 1.0;
if (isinf(x) && (thiz == 1.0 || thiz == -1.0)) return NAN;
return powf(thiz, x);
}
KFloat Kotlin_math_Float_IEEErem(KFloat thiz, KFloat divisor) { return remainderf(thiz, divisor); }
KFloat Kotlin_math_Float_withSign(KFloat thiz, KFloat sign) { return copysignf(thiz, sign); }
KFloat Kotlin_math_Float_nextUp(KFloat thiz) { return nextafterf(thiz, HUGE_VALF); }
KFloat Kotlin_math_Float_nextDown(KFloat thiz) { return nextafterf(thiz, -HUGE_VALF); }
KFloat Kotlin_math_Float_nextTowards(KFloat thiz, KFloat to) {
return (thiz == to) ? to : nextafterf(thiz, to);
}
KBoolean Kotlin_math_Float_signBit(KFloat thiz) { return signbit(thiz) != 0; }
// endregion
// region Integer math.
KInt Kotlin_math_absi(KInt x) { return labs(x); }
KLong Kotlin_math_absl(KLong x) { return llabs(x); }
// endregion
#else // KONAN_WASM defined. Use JS math implementation.
#define RETURN_RESULT_OF_JS_CALL(call, doubleArg) { \
call(doubleUpper(doubleArg), doubleLower(doubleArg)); \
return ReturnSlot_getDouble(); \
}
#define RETURN_RESULT_OF_JS_CALL2(call, doubleArg1, doubleArg2) { \
call(doubleUpper(doubleArg1), \
doubleLower(doubleArg1), \
doubleUpper(doubleArg2), \
doubleLower(doubleArg2)); \
return ReturnSlot_getDouble(); \
}
KDouble Kotlin_math_sin(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_sin, x); }
KDouble Kotlin_math_cos(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_cos, x); }
KDouble Kotlin_math_tan(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_tan, x); }
KDouble Kotlin_math_asin(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_asin, x); }
KDouble Kotlin_math_acos(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_acos, x); }
KDouble Kotlin_math_atan(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_atan, x); }
KDouble Kotlin_math_sinh(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_sinh, x); }
KDouble Kotlin_math_cosh(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_cosh, x); }
KDouble Kotlin_math_tanh(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_tanh, x); }
KDouble Kotlin_math_asinh(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_asinh, x); }
KDouble Kotlin_math_acosh(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_acosh, x); }
KDouble Kotlin_math_atanh(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_atanh, x); }
KDouble Kotlin_math_sqrt(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_sqrt, x); }
KDouble Kotlin_math_exp(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_exp, x); }
KDouble Kotlin_math_expm1(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_expm1, x); }
KDouble Kotlin_math_ln(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_log, x); }
KDouble Kotlin_math_log10(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_log10, x); }
KDouble Kotlin_math_log2(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_log2, x); }
KDouble Kotlin_math_ln1p(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_log1p, x); }
KDouble Kotlin_math_ceil(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_ceil, x); }
KDouble Kotlin_math_floor(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_floor, x); }
KDouble Kotlin_math_round(KDouble x) {
if (fmod(x, 0.5) != 0.0) {
RETURN_RESULT_OF_JS_CALL(knjs__Math_round, x);
}
KDouble f = floor(x);
return (fmod(f, 2) == 0.0) ? f : ceil(x);
}
KDouble Kotlin_math_abs(KDouble x) { RETURN_RESULT_OF_JS_CALL(knjs__Math_abs, x); }
KDouble Kotlin_math_atan2(KDouble y, KDouble x) { RETURN_RESULT_OF_JS_CALL2(knjs__Math_atan2, y, x); }
KDouble Kotlin_math_hypot(KDouble x, KDouble y) { RETURN_RESULT_OF_JS_CALL2(knjs__Math_hypot, x, y); }
// extensions
KDouble Kotlin_math_Double_pow(KDouble thiz, KDouble x) { RETURN_RESULT_OF_JS_CALL2(knjs__Math_pow, thiz, x); }
KDouble Kotlin_math_Double_IEEErem(KDouble thiz, KDouble divisor) {
if (isnan(thiz) || isnan(divisor) || isinf(thiz) || divisor == 0.0) {
return NAN;
}
if (!isinf(thiz) && isinf(divisor)) {
return thiz;
}
KDouble rounded = Kotlin_math_round(thiz / divisor);
return thiz - rounded * divisor;
}
KDouble Kotlin_math_Double_nextUp(KDouble thiz) {
if (isnan(thiz) || thiz == HUGE_VAL) {
return thiz;
}
if (thiz == 0.0) {
return DBL_TRUE_MIN;
}
return bitsToDouble(doubleToBits(thiz) + (thiz > 0 ? 1 : -1));
}
KDouble Kotlin_math_Double_nextDown(KDouble thiz) {
if (isnan(thiz) || thiz == -HUGE_VAL) {
return thiz;
}
if (thiz == 0.0) {
return -DBL_TRUE_MIN;
}
return bitsToDouble(doubleToBits(thiz) - (thiz > 0 ? 1 : -1));
}
KDouble Kotlin_math_Double_nextTowards(KDouble thiz, KDouble to) {
if (isnan(thiz) || isnan(to)) {
return NAN;
}
if (to > thiz) {
return Kotlin_math_Double_nextUp(thiz);
}
if (to < thiz) {
return Kotlin_math_Double_nextDown(thiz);
}
// thiz == to
return to;
}
KBoolean Kotlin_math_Double_signBit(KDouble thiz) {
return (doubleToBits(thiz) & (KLong) 1 << 63) != 0;
}
KDouble Kotlin_math_Double_withSign(KDouble thiz, KDouble sign) {
bool oldSign = Kotlin_math_Double_signBit(thiz);
bool newSign = Kotlin_math_Double_signBit(sign);
return (oldSign == newSign) ? thiz : -thiz;
}
// endregion
// region Float math.
KFloat Kotlin_math_sinf(KFloat x) { return (KFloat)Kotlin_math_sin (x); }
KFloat Kotlin_math_cosf(KFloat x) { return (KFloat)Kotlin_math_cos (x); }
KFloat Kotlin_math_tanf(KFloat x) { return (KFloat)Kotlin_math_tan (x); }
KFloat Kotlin_math_asinf(KFloat x) { return (KFloat)Kotlin_math_asin (x); }
KFloat Kotlin_math_acosf(KFloat x) { return (KFloat)Kotlin_math_acos (x); }
KFloat Kotlin_math_atanf(KFloat x) { return (KFloat)Kotlin_math_atan (x); }
KFloat Kotlin_math_sinhf(KFloat x) { return (KFloat)Kotlin_math_sinh (x); }
KFloat Kotlin_math_coshf(KFloat x) { return (KFloat)Kotlin_math_cosh (x); }
KFloat Kotlin_math_tanhf(KFloat x) { return (KFloat)Kotlin_math_tanh (x); }
KFloat Kotlin_math_asinhf(KFloat x) { return (KFloat)Kotlin_math_asinh (x); }
KFloat Kotlin_math_acoshf(KFloat x) { return (KFloat)Kotlin_math_acosh (x); }
KFloat Kotlin_math_atanhf(KFloat x) { return (KFloat)Kotlin_math_atanh (x); }
KFloat Kotlin_math_sqrtf(KFloat x) { return (KFloat)Kotlin_math_sqrt (x); }
KFloat Kotlin_math_expf(KFloat x) { return (KFloat)Kotlin_math_exp (x); }
KFloat Kotlin_math_expm1f(KFloat x) { return (KFloat)Kotlin_math_expm1 (x); }
KFloat Kotlin_math_lnf(KFloat x) { return (KFloat)Kotlin_math_ln (x); }
KFloat Kotlin_math_log10f(KFloat x) { return (KFloat)Kotlin_math_log10 (x); }
KFloat Kotlin_math_log2f(KFloat x) { return (KFloat)Kotlin_math_log2 (x); }
KFloat Kotlin_math_ln1pf(KFloat x) { return (KFloat)Kotlin_math_ln1p (x); }
KFloat Kotlin_math_ceilf(KFloat x) { return (KFloat)Kotlin_math_ceil (x); }
KFloat Kotlin_math_floorf(KFloat x) { return (KFloat)Kotlin_math_floor (x); }
KFloat Kotlin_math_roundf(KFloat x) { return (KFloat)Kotlin_math_round (x); }
KFloat Kotlin_math_absf(KFloat x) { return (KFloat)Kotlin_math_abs (x); }
KFloat Kotlin_math_atan2f(KFloat y, KFloat x) { return (KFloat)Kotlin_math_atan2(y, x); }
KFloat Kotlin_math_hypotf(KFloat x, KFloat y) { return (KFloat)Kotlin_math_hypot(x, y); }
// extensions
KFloat Kotlin_math_Float_pow(KFloat thiz, KFloat x) { return (KFloat)Kotlin_math_Double_pow(thiz, x); }
KFloat Kotlin_math_Float_IEEErem(KFloat thiz, KFloat divisor) {
return (KFloat)Kotlin_math_Double_IEEErem(thiz, divisor);
}
KFloat Kotlin_math_Float_withSign(KFloat thiz, KFloat sign) {
return (KFloat)Kotlin_math_Double_withSign(thiz, sign);
}
KFloat Kotlin_math_Float_nextUp(KFloat thiz) {
if (isnan(thiz) || thiz == HUGE_VALF) {
return thiz;
}
if (thiz == 0.0) {
return FLT_TRUE_MIN;
}
return bitsToFloat(floatToBits(thiz) + (thiz > 0 ? 1 : -1));
}
KFloat Kotlin_math_Float_nextDown(KFloat thiz) {
if (isnan(thiz) || thiz == -HUGE_VALF) {
return thiz;
}
if (thiz == 0.0) {
return -FLT_TRUE_MIN;
}
return bitsToFloat(floatToBits(thiz) - (thiz > 0 ? 1 : -1));
}
KFloat Kotlin_math_Float_nextTowards(KFloat thiz, KFloat to) {
if (isnan(thiz) || isnan(to)) {
return NAN;
}
if (to > thiz) {
return Kotlin_math_Float_nextUp(thiz);
}
if (to < thiz) {
return Kotlin_math_Float_nextDown(thiz);
}
// thiz == to
return to;
}
KBoolean Kotlin_math_Float_signBit(KFloat thiz) { return Kotlin_math_Double_signBit(thiz); }
// endregion
// region Integer math
KInt Kotlin_math_absi(KInt x) { return (x >= 0) ? x : -x; }
KLong Kotlin_math_absl(KLong x) { return (x >= 0) ? x : -x; }
#endif // #ifndef KONAN_WASM
#else // KONAN_NO_MATH defined - we have no patform math library. Throw NotImplementedError from math functions.
namespace {
RUNTIME_NORETURN void NotImplemented() {
ThrowNotImplementedError();
}
} // namespace
KDouble Kotlin_math_sin(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_cos(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_tan(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_asin(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_acos(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_atan(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_atan2(KDouble y, KDouble x) { NotImplemented(); }
KDouble Kotlin_math_sinh(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_cosh(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_tanh(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_asinh(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_acosh(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_atanh(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_hypot(KDouble x, KDouble y) { NotImplemented(); }
KDouble Kotlin_math_sqrt(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_exp(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_expm1(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_ln(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_log10(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_log2(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_ln1p(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_ceil(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_floor(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_round(KDouble x) { NotImplemented(); }
KDouble Kotlin_math_abs(KDouble x) { NotImplemented(); }
// extensions
KDouble Kotlin_math_Double_pow(KDouble thiz, KDouble x) { NotImplemented(); }
KDouble Kotlin_math_Double_IEEErem(KDouble thiz, KDouble divisor) { NotImplemented(); }
KDouble Kotlin_math_Double_withSign(KDouble thiz, KDouble sign) { NotImplemented(); }
KDouble Kotlin_math_Double_nextUp(KDouble thiz) { NotImplemented(); }
KDouble Kotlin_math_Double_nextDown(KDouble thiz) { NotImplemented(); }
KDouble Kotlin_math_Double_nextTowards(KDouble thiz, KDouble to) { NotImplemented(); }
KBoolean Kotlin_math_Double_signBit(KDouble thiz) { NotImplemented(); }
// endregion
// region Float math.
KFloat Kotlin_math_sinf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_cosf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_tanf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_asinf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_acosf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_atanf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_atan2f(KFloat y, KFloat x) { NotImplemented(); }
KFloat Kotlin_math_sinhf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_coshf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_tanhf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_asinhf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_acoshf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_atanhf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_hypotf(KFloat x, KFloat y) { NotImplemented(); }
KFloat Kotlin_math_sqrtf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_expf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_expm1f(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_lnf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_log10f(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_log2f(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_ln1pf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_ceilf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_floorf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_roundf(KFloat x) { NotImplemented(); }
KFloat Kotlin_math_absf(KFloat x) { NotImplemented(); }
// extensions
KFloat Kotlin_math_Float_pow(KFloat thiz, KFloat x) { NotImplemented(); }
KFloat Kotlin_math_Float_IEEErem(KFloat thiz, KFloat divisor) { NotImplemented(); }
KFloat Kotlin_math_Float_withSign(KFloat thiz, KFloat sign) { NotImplemented(); }
KFloat Kotlin_math_Float_nextUp(KFloat thiz) { NotImplemented(); }
KFloat Kotlin_math_Float_nextDown(KFloat thiz) { NotImplemented(); }
KFloat Kotlin_math_Float_nextTowards(KFloat thiz, KFloat to) { NotImplemented(); }
KBoolean Kotlin_math_Float_signBit(KFloat thiz) { NotImplemented(); }
// endregion
// region Integer math
KInt Kotlin_math_absi(KInt x) { NotImplemented(); }
KInt Kotlin_math_mini(KInt a, KInt b) { NotImplemented(); }
KInt Kotlin_math_maxi(KInt a, KInt b) { NotImplemented(); }
KLong Kotlin_math_absl(KLong x) { NotImplemented(); }
KLong Kotlin_math_minl(KLong a, KLong b) { NotImplemented(); }
KLong Kotlin_math_maxl(KLong a, KLong b) { NotImplemented(); }
#endif // #ifndef KONAN_NO_MATH
} // extern "C"
@@ -0,0 +1,69 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_KOTLINMATH_H
#define RUNTIME_KOTLINMATH_H
#include "Types.h"
#ifdef KONAN_WASM
extern "C" {
// TODO: consider auto-generating this header file.
// Bridges for JS math.
void knjs__Math_abs(KInt xUpper, KInt xLower);
void knjs__Math_acos(KInt xUpper, KInt xLower);
void knjs__Math_acosh(KInt xUpper, KInt xLower);
void knjs__Math_asin(KInt xUpper, KInt xLower);
void knjs__Math_asinh(KInt xUpper, KInt xLower);
void knjs__Math_atan(KInt xUpper, KInt xLower);
void knjs__Math_atan2(KInt yUpper, KInt yLower, KInt xUpper, KInt xLower);
void knjs__Math_atanh(KInt xUpper, KInt xLower);
void knjs__Math_cbrt(KInt xUpper, KInt xLower);
void knjs__Math_ceil(KInt xUpper, KInt xLower);
void knjs__Math_clz32(KInt xUpper, KInt xLower);
void knjs__Math_cos(KInt xUpper, KInt xLower);
void knjs__Math_cosh(KInt xUpper, KInt xLower);
void knjs__Math_exp(KInt xUpper, KInt xLower);
void knjs__Math_expm1(KInt xUpper, KInt xLower);
void knjs__Math_floor(KInt xUpper, KInt xLower);
void knjs__Math_fround(KInt xUpper, KInt xLower);
void knjs__Math_log(KInt xUpper, KInt xLower);
void knjs__Math_log1p(KInt xUpper, KInt xLower);
void knjs__Math_log10(KInt xUpper, KInt xLower);
void knjs__Math_log2(KInt xUpper, KInt xLower);
void knjs__Math_round(KInt xUpper, KInt xLower);
void knjs__Math_sign(KInt xUpper, KInt xLower);
void knjs__Math_sin(KInt xUpper, KInt xLower);
void knjs__Math_sinh(KInt xUpper, KInt xLower);
void knjs__Math_sqrt(KInt xUpper, KInt xLower);
void knjs__Math_tan(KInt xUpper, KInt xLower);
void knjs__Math_tanh(KInt xUpper, KInt xLower);
void knjs__Math_trunc(KInt xUpper, KInt xLower);
void knjs__Math_hypot(KInt xUpper, KInt xLower, KInt yUpper, KInt yLower);
void knjs__Math_max(KInt xUpper, KInt xLower, KInt yUpper, KInt yLower);
void knjs__Math_min(KInt xUpper, KInt xLower, KInt yUpper, KInt yLower);
void knjs__Math_pow(KInt xUpper, KInt xLower, KInt yUpper, KInt yLower);
}
#endif // KONAN_WASM
#endif // RUNTIME_KOTLINMATH_H
+321
View File
@@ -0,0 +1,321 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_MEMORY_H
#define RUNTIME_MEMORY_H
#include "KAssert.h"
#include "Common.h"
#include "TypeInfo.h"
#include "Atomic.h"
#include "PointerBits.h"
typedef enum {
// Must match to permTag() in Kotlin.
OBJECT_TAG_PERMANENT_CONTAINER = 1 << 0,
OBJECT_TAG_NONTRIVIAL_CONTAINER = 1 << 1,
// Keep in sync with immTypeInfoMask in Kotlin.
OBJECT_TAG_MASK = (1 << 2) - 1
} ObjectTag;
struct ArrayHeader;
struct MetaObjHeader;
// Header of every object.
struct ObjHeader {
TypeInfo* typeInfoOrMeta_;
const TypeInfo* type_info() const {
return clearPointerBits(typeInfoOrMeta_, OBJECT_TAG_MASK)->typeInfo_;
}
bool has_meta_object() const {
auto* typeInfoOrMeta = clearPointerBits(typeInfoOrMeta_, OBJECT_TAG_MASK);
return (typeInfoOrMeta != typeInfoOrMeta->typeInfo_);
}
MetaObjHeader* meta_object() {
return has_meta_object() ?
reinterpret_cast<MetaObjHeader*>(clearPointerBits(typeInfoOrMeta_, OBJECT_TAG_MASK)) :
createMetaObject(&typeInfoOrMeta_);
}
ALWAYS_INLINE ObjHeader** GetWeakCounterLocation();
#ifdef KONAN_OBJC_INTEROP
ALWAYS_INLINE void* GetAssociatedObject();
ALWAYS_INLINE void** GetAssociatedObjectLocation();
ALWAYS_INLINE void SetAssociatedObject(void* obj);
#endif
inline bool local() const {
unsigned bits = getPointerBits(typeInfoOrMeta_, OBJECT_TAG_MASK);
return (bits & (OBJECT_TAG_PERMANENT_CONTAINER | OBJECT_TAG_NONTRIVIAL_CONTAINER)) ==
(OBJECT_TAG_PERMANENT_CONTAINER | OBJECT_TAG_NONTRIVIAL_CONTAINER);
}
// Unsafe cast to ArrayHeader. Use carefully!
ArrayHeader* array() { return reinterpret_cast<ArrayHeader*>(this); }
const ArrayHeader* array() const { return reinterpret_cast<const ArrayHeader*>(this); }
inline bool permanent() const {
return hasPointerBits(typeInfoOrMeta_, OBJECT_TAG_PERMANENT_CONTAINER);
}
static MetaObjHeader* createMetaObject(TypeInfo** location);
static void destroyMetaObject(TypeInfo** location);
};
// Header of value type array objects. Keep layout in sync with that of object header.
struct ArrayHeader {
TypeInfo* typeInfoOrMeta_;
const TypeInfo* type_info() const {
return clearPointerBits(typeInfoOrMeta_, OBJECT_TAG_MASK)->typeInfo_;
}
ObjHeader* obj() { return reinterpret_cast<ObjHeader*>(this); }
const ObjHeader* obj() const { return reinterpret_cast<const ObjHeader*>(this); }
// Elements count. Element size is stored in instanceSize_ field of TypeInfo, negated.
uint32_t count_;
};
ALWAYS_INLINE bool isFrozen(const ObjHeader* obj);
ALWAYS_INLINE bool isPermanentOrFrozen(const ObjHeader* obj);
ALWAYS_INLINE bool isShareable(const ObjHeader* obj);
class ForeignRefManager;
typedef ForeignRefManager* ForeignRefContext;
#ifdef __cplusplus
extern "C" {
#endif
#define OBJ_RESULT __result__
#define OBJ_GETTER0(name) ObjHeader* name(ObjHeader** OBJ_RESULT)
#define OBJ_GETTER(name, ...) ObjHeader* name(__VA_ARGS__, ObjHeader** OBJ_RESULT)
#define RETURN_OBJ(value) { ObjHeader* __obj = value; \
UpdateReturnRef(OBJ_RESULT, __obj); \
return __obj; }
#define RETURN_RESULT_OF0(name) { \
ObjHeader* __obj = name(OBJ_RESULT); \
return __obj; \
}
#define RETURN_RESULT_OF(name, ...) { \
ObjHeader* __result = name(__VA_ARGS__, OBJ_RESULT); \
return __result; \
}
struct MemoryState;
MemoryState* InitMemory();
void DeinitMemory(MemoryState*);
void RestoreMemory(MemoryState*);
//
// Object allocation.
//
// Allocation can happen in either GLOBAL, FRAME or ARENA scope. Depending on that,
// Alloc* or ArenaAlloc* is called. Regular alloc means allocation happens in the heap,
// and each object gets its individual container. Otherwise, allocator uses aux slot in
// an implementation-defined manner, current behavior is to keep arena pointer there.
// Arena containers are not reference counted, and is explicitly freed when leaving
// its owner frame.
// Escape analysis algorithm is the provider of information for decision on exact aux slot
// selection, and comes from upper bound esteemation of object lifetime.
//
OBJ_GETTER(AllocInstance, const TypeInfo* type_info) RUNTIME_NOTHROW;
OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, int32_t elements);
OBJ_GETTER(InitInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSharedInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
//
// Object reference management.
//
// Reference management scheme we use assumes significant degree of flexibility, so that
// one could implement either pure reference counting scheme, or tracing collector without
// much ado.
// Most important primitive is Update*Ref() API, which modifies location to use new
// object reference. In pure reference counted scheme it will check old value,
// decrement reference, increment counter on the new value, and store it into the field.
// In tracing collector-like scheme, only field updates counts, and all other operations are
// essentially no-ops.
//
// On codegeneration phase we adopt following approaches:
// - every stack frame has several slots, holding object references (allRefs)
// - those are known by compiler (and shall be grouped together)
// - it keeps all locally allocated objects in such slot
// - all local variables keeping an object also allocate a slot
// - most manipulations on objects happens in SSA variables and do no affect slots
// - exception handlers knowns slot locations for every function, and can update references
// in intermediate frames when throwing
//
// Controls the current memory model, is compile-time constant.
extern const bool IsStrictMemoryModel;
// Sets stack location.
void SetStackRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
// Sets heap location.
void SetHeapRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
// Zeroes heap location.
void ZeroHeapRef(ObjHeader** location) RUNTIME_NOTHROW;
// Zeroes an array.
void ZeroArrayRefs(ArrayHeader* array) RUNTIME_NOTHROW;
// Zeroes stack location.
void ZeroStackRef(ObjHeader** location) RUNTIME_NOTHROW;
// Updates stack location.
void UpdateStackRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
// Updates heap/static data location.
void UpdateHeapRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
// Updates location if it is null, atomically.
void UpdateHeapRefIfNull(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
// Updates reference in return slot.
void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) RUNTIME_NOTHROW;
// Compares and swaps reference with taken lock.
OBJ_GETTER(SwapHeapRefLocked,
ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue, int32_t* spinlock,
int32_t* cookie) RUNTIME_NOTHROW;
// Sets reference with taken lock.
void SetHeapRefLocked(ObjHeader** location, ObjHeader* newValue, int32_t* spinlock,
int32_t* cookie) RUNTIME_NOTHROW;
// Reads reference with taken lock.
OBJ_GETTER(ReadHeapRefLocked, ObjHeader** location, int32_t* spinlock, int32_t* cookie) RUNTIME_NOTHROW;
// Called on frame enter, if it has object slots.
void EnterFrame(ObjHeader** start, int parameters, int count) RUNTIME_NOTHROW;
// Called on frame leave, if it has object slots.
void LeaveFrame(ObjHeader** start, int parameters, int count) RUNTIME_NOTHROW;
// Clears object subgraph references from memory subsystem, and optionally
// checks if subgraph referenced by given root is disjoint from the rest of
// object graph, i.e. no external references exists.
bool ClearSubgraphReferences(ObjHeader* root, bool checked) RUNTIME_NOTHROW;
// Creates stable pointer out of the object.
void* CreateStablePointer(ObjHeader* obj) RUNTIME_NOTHROW;
// Disposes stable pointer to the object.
void DisposeStablePointer(void* pointer) RUNTIME_NOTHROW;
// Translate stable pointer to object reference.
OBJ_GETTER(DerefStablePointer, void*) RUNTIME_NOTHROW;
// Move stable pointer ownership.
OBJ_GETTER(AdoptStablePointer, void*) RUNTIME_NOTHROW;
// Check mutability state.
void MutationCheck(ObjHeader* obj);
void CheckLifetimesConstraint(ObjHeader* obj, ObjHeader* pointee) RUNTIME_NOTHROW;
// Freeze object subgraph.
void FreezeSubgraph(ObjHeader* obj);
// Ensure this object shall block freezing.
void EnsureNeverFrozen(ObjHeader* obj);
// Add TLS object storage, called by the generated code.
void AddTLSRecord(MemoryState* memory, void** key, int size) RUNTIME_NOTHROW;
// Clear TLS object storage, called by the generated code.
void ClearTLSRecord(MemoryState* memory, void** key) RUNTIME_NOTHROW;
// Lookup element in TLS object storage.
ObjHeader** LookupTLS(void** key, int index) RUNTIME_NOTHROW;
// APIs for the async GC.
void GC_RegisterWorker(void* worker) RUNTIME_NOTHROW;
void GC_UnregisterWorker(void* worker) RUNTIME_NOTHROW;
void GC_CollectorCallback(void* worker) RUNTIME_NOTHROW;
bool Kotlin_Any_isShareable(ObjHeader* thiz);
void PerformFullGC() RUNTIME_NOTHROW;
bool TryAddHeapRef(const ObjHeader* object);
void ReleaseHeapRef(const ObjHeader* object) RUNTIME_NOTHROW;
void ReleaseHeapRefNoCollect(const ObjHeader* object) RUNTIME_NOTHROW;
ForeignRefContext InitLocalForeignRef(ObjHeader* object);
ForeignRefContext InitForeignRef(ObjHeader* object);
void DeinitForeignRef(ObjHeader* object, ForeignRefContext context);
bool IsForeignRefAccessible(ObjHeader* object, ForeignRefContext context);
// Should be used when reference is read from a possibly shared variable,
// and there's nothing else keeping the object alive.
void AdoptReferenceFromSharedVariable(ObjHeader* object);
#ifdef __cplusplus
}
#endif
struct FrameOverlay {
void* arena;
FrameOverlay* previous;
// As they go in pair, sizeof(FrameOverlay) % sizeof(void*) == 0 is always held.
int32_t parameters;
int32_t count;
};
// Class holding reference to an object, holding object during C++ scope.
class ObjHolder {
public:
ObjHolder() : obj_(nullptr) {
EnterFrame(frame(), 0, sizeof(*this)/sizeof(void*));
}
explicit ObjHolder(const ObjHeader* obj) {
EnterFrame(frame(), 0, sizeof(*this)/sizeof(void*));
::SetStackRef(slot(), obj);
}
~ObjHolder() {
LeaveFrame(frame(), 0, sizeof(*this)/sizeof(void*));
}
ObjHeader* obj() { return obj_; }
const ObjHeader* obj() const { return obj_; }
ObjHeader** slot() {
return &obj_;
}
void clear() { ::ZeroStackRef(&obj_); }
private:
ObjHeader** frame() { return reinterpret_cast<ObjHeader**>(&frame_); }
FrameOverlay frame_;
ObjHeader* obj_;
};
//! TODO Follow the Rule of Zero to prevent dangling on unintented copy ctor
class ExceptionObjHolder {
public:
explicit ExceptionObjHolder(const ObjHeader* obj) {
::SetHeapRef(&obj_, obj);
}
~ExceptionObjHolder() {
ZeroHeapRef(&obj_);
}
ObjHeader* obj() { return obj_; }
const ObjHeader* obj() const { return obj_; }
private:
ObjHeader* obj_;
};
#endif // RUNTIME_MEMORY_H
@@ -0,0 +1,216 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "Exceptions.h"
#include "MemorySharedRefs.hpp"
#include "Runtime.h"
#include "Types.h"
extern "C" {
// Returns a string describing object at `address` of type `typeInfo`.
OBJ_GETTER(DescribeObjectForDebugging, KConstNativePtr typeInfo, KConstNativePtr address);
} // extern "C"
namespace {
inline bool isForeignRefAccessible(ObjHeader* object, ForeignRefContext context) {
// If runtime has not been initialized on this thread, then the object is either unowned or shared.
// In the former case initialized runtime is required to throw exceptions
// in the latter case -- to provide proper execution context for caller.
Kotlin_initRuntimeIfNeeded();
return IsForeignRefAccessible(object, context);
}
RUNTIME_NORETURN inline void throwIllegalSharingException(ObjHeader* object) {
// TODO: add some info about the context.
// Note: retrieving 'type_info()' is supposed to be correct even for unowned object.
ThrowIllegalObjectSharingException(object->type_info(), object);
}
RUNTIME_NORETURN inline void terminateWithIllegalSharingException(ObjHeader* object) {
#if KONAN_NO_EXCEPTIONS
// This will terminate.
throwIllegalSharingException(object);
#else
try {
throwIllegalSharingException(object);
} catch (...) {
// A trick to terminate with unhandled exception. This will print a stack trace
// and write to iOS crash log.
std::terminate();
}
#endif
}
template <ErrorPolicy errorPolicy>
bool ensureRefAccessible(ObjHeader* object, ForeignRefContext context) {
static_assert(errorPolicy != ErrorPolicy::kIgnore, "Must've been handled by specialization");
if (isForeignRefAccessible(object, context)) {
return true;
}
switch (errorPolicy) {
case ErrorPolicy::kDefaultValue:
return false;
case ErrorPolicy::kThrow:
throwIllegalSharingException(object);
case ErrorPolicy::kTerminate:
terminateWithIllegalSharingException(object);
}
}
template <>
bool ensureRefAccessible<ErrorPolicy::kIgnore>(ObjHeader* object, ForeignRefContext context) {
return true;
}
} // namespace
void KRefSharedHolder::initLocal(ObjHeader* obj) {
RuntimeAssert(obj != nullptr, "must not be null");
context_ = InitLocalForeignRef(obj);
obj_ = obj;
}
void KRefSharedHolder::init(ObjHeader* obj) {
RuntimeAssert(obj != nullptr, "must not be null");
context_ = InitForeignRef(obj);
obj_ = obj;
}
template <ErrorPolicy errorPolicy>
ObjHeader* KRefSharedHolder::ref() const {
if (!ensureRefAccessible<errorPolicy>(obj_, context_)) {
return nullptr;
}
AdoptReferenceFromSharedVariable(obj_);
return obj_;
}
template ObjHeader* KRefSharedHolder::ref<ErrorPolicy::kDefaultValue>() const;
template ObjHeader* KRefSharedHolder::ref<ErrorPolicy::kThrow>() const;
template ObjHeader* KRefSharedHolder::ref<ErrorPolicy::kTerminate>() const;
void KRefSharedHolder::dispose() const {
if (obj_ == nullptr) {
// To handle the case when it is not initialized. See [KotlinMutableSet/Dictionary dealloc].
return;
}
DeinitForeignRef(obj_, context_);
}
OBJ_GETTER0(KRefSharedHolder::describe) const {
// Note: retrieving 'type_info()' is supposed to be correct even for unowned object.
RETURN_RESULT_OF(DescribeObjectForDebugging, obj_->type_info(), obj_);
}
void BackRefFromAssociatedObject::initAndAddRef(ObjHeader* obj) {
RuntimeAssert(obj != nullptr, "must not be null");
obj_ = obj;
// Generally a specialized addRef below:
context_ = InitForeignRef(obj);
refCount = 1;
}
template <ErrorPolicy errorPolicy>
void BackRefFromAssociatedObject::addRef() {
static_assert(errorPolicy != ErrorPolicy::kDefaultValue, "Cannot use default return value here");
if (atomicAdd(&refCount, 1) == 1) {
if (obj_ == nullptr) return; // E.g. after [detach].
// There are no references to the associated object itself, so Kotlin object is being passed from Kotlin,
// and it is owned therefore.
ensureRefAccessible<errorPolicy>(obj_, context_); // TODO: consider removing explicit verification.
// Foreign reference has already been deinitialized (see [releaseRef]).
// Create a new one:
context_ = InitForeignRef(obj_);
}
}
template void BackRefFromAssociatedObject::addRef<ErrorPolicy::kThrow>();
template void BackRefFromAssociatedObject::addRef<ErrorPolicy::kTerminate>();
template <ErrorPolicy errorPolicy>
bool BackRefFromAssociatedObject::tryAddRef() {
static_assert(errorPolicy != ErrorPolicy::kDefaultValue, "Cannot use default return value here");
if (obj_ == nullptr) return false; // E.g. after [detach].
// Suboptimal but simple:
ensureRefAccessible<errorPolicy>(obj_, context_);
ObjHeader* obj = obj_;
if (!TryAddHeapRef(obj)) return false;
RuntimeAssert(isForeignRefAccessible(obj_, context_), "Cannot be inaccessible because of the check above");
// TODO: This is a very weird way to ask for "unsafe" addRef.
addRef<ErrorPolicy::kIgnore>();
ReleaseHeapRefNoCollect(obj); // Balance TryAddHeapRef.
// TODO: consider optimizing for non-shared objects.
return true;
}
template bool BackRefFromAssociatedObject::tryAddRef<ErrorPolicy::kThrow>();
template bool BackRefFromAssociatedObject::tryAddRef<ErrorPolicy::kTerminate>();
void BackRefFromAssociatedObject::releaseRef() {
ForeignRefContext context = context_;
if (atomicAdd(&refCount, -1) == 0) {
if (obj_ == nullptr) return; // E.g. after [detach].
// Note: by this moment "subsequent" addRef may have already happened and patched context_.
// So use the value loaded before refCount update:
DeinitForeignRef(obj_, context);
// From this moment [context] is generally a dangling pointer.
// This is handled in [IsForeignRefAccessible] and [addRef].
}
}
void BackRefFromAssociatedObject::detach() {
RuntimeAssert(atomicGet(&refCount) == 0, "unexpected refCount")
obj_ = nullptr; // Handled in addRef/tryAddRef/releaseRef/ref.
}
template <ErrorPolicy errorPolicy>
ObjHeader* BackRefFromAssociatedObject::ref() const {
RuntimeAssert(obj_ != nullptr, "no valid Kotlin object found");
if (!ensureRefAccessible<errorPolicy>(obj_, context_)) {
return nullptr;
}
AdoptReferenceFromSharedVariable(obj_);
return obj_;
}
template ObjHeader* BackRefFromAssociatedObject::ref<ErrorPolicy::kDefaultValue>() const;
template ObjHeader* BackRefFromAssociatedObject::ref<ErrorPolicy::kThrow>() const;
template ObjHeader* BackRefFromAssociatedObject::ref<ErrorPolicy::kTerminate>() const;
extern "C" {
RUNTIME_NOTHROW void KRefSharedHolder_initLocal(KRefSharedHolder* holder, ObjHeader* obj) {
holder->initLocal(obj);
}
RUNTIME_NOTHROW void KRefSharedHolder_init(KRefSharedHolder* holder, ObjHeader* obj) {
holder->init(obj);
}
RUNTIME_NOTHROW void KRefSharedHolder_dispose(const KRefSharedHolder* holder) {
holder->dispose();
}
RUNTIME_NOTHROW ObjHeader* KRefSharedHolder_ref(const KRefSharedHolder* holder) {
return holder->ref<ErrorPolicy::kTerminate>();
}
} // extern "C"
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_MEMORYSHAREDREFS_HPP
#define RUNTIME_MEMORYSHAREDREFS_HPP
#include <type_traits>
#include "Memory.h"
// TODO: Generalize for uses outside this file.
enum class ErrorPolicy {
kIgnore, // Ignore any errors. (i.e. unsafe mode)
kDefaultValue, // Return the default value from the function when an error happens.
kThrow, // Throw a Kotlin exception when an error happens. The exact exception is chosen by the callee.
kTerminate, // Terminate immediately when an error happens.
};
class KRefSharedHolder {
public:
void initLocal(ObjHeader* obj);
void init(ObjHeader* obj);
// Error if called from the wrong worker with non-frozen obj_.
template <ErrorPolicy errorPolicy>
ObjHeader* ref() const;
void dispose() const;
OBJ_GETTER0(describe) const;
private:
ObjHeader* obj_;
ForeignRefContext context_;
};
static_assert(std::is_trivially_destructible<KRefSharedHolder>::value,
"KRefSharedHolder destructor is not guaranteed to be called.");
class BackRefFromAssociatedObject {
public:
void initAndAddRef(ObjHeader* obj);
// Error if refCount is zero and it's called from the wrong worker with non-frozen obj_.
template <ErrorPolicy errorPolicy>
void addRef();
// Error if called from the wrong worker with non-frozen obj_.
template <ErrorPolicy errorPolicy>
bool tryAddRef();
void releaseRef();
void detach();
// Error if called from the wrong worker with non-frozen obj_.
template <ErrorPolicy errorPolicy>
ObjHeader* ref() const;
private:
ObjHeader* obj_; // May be null before [initAndAddRef] or after [detach].
ForeignRefContext context_;
volatile int refCount;
};
static_assert(std::is_trivially_destructible<BackRefFromAssociatedObject>::value,
"BackRefFromAssociatedObject destructor is not guaranteed to be called.");
#endif // RUNTIME_MEMORYSHAREDREFS_HPP
@@ -0,0 +1,84 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <type_traits>
#include "KAssert.h"
#include "Exceptions.h"
#include "Memory.h"
#include "Natives.h"
#include "Types.h"
extern "C" {
// Any.kt
KBoolean Kotlin_Any_equals(KConstRef thiz, KConstRef other) {
return thiz == other;
}
KInt Kotlin_Any_hashCode(KConstRef thiz) {
// Here we will use different mechanism for stable hashcode, using meta-objects
// if moving collector will be used.
return reinterpret_cast<uintptr_t>(thiz);
}
OBJ_GETTER(Kotlin_getStackTraceStrings, KConstRef stackTrace) {
RETURN_RESULT_OF(GetStackTraceStrings, stackTrace);
}
// TODO: consider handling it with compiler magic instead.
OBJ_GETTER0(Kotlin_native_internal_undefined) {
RETURN_OBJ(nullptr);
}
void* Kotlin_interop_malloc(KLong size, KInt align) {
if (size < 0 || static_cast<std::make_unsigned<decltype(size)>::type>(size) > std::numeric_limits<size_t>::max()) {
return nullptr;
}
RuntimeAssert(align > 0, "Unsupported alignment");
RuntimeAssert((align & (align - 1)) == 0, "Alignment must be power of two");
void* result = konan::calloc_aligned(1, size, align);
if ((reinterpret_cast<uintptr_t>(result) & (align - 1)) != 0) {
// Unaligned!
RuntimeAssert(false, "unsupported alignment");
}
return result;
}
void Kotlin_interop_free(void* ptr) {
konan::free(ptr);
}
void Kotlin_system_exitProcess(KInt status) {
konan::exit(status);
}
const void* Kotlin_Any_getTypeInfo(KConstRef obj) {
return obj->type_info();
}
void Kotlin_CPointer_CopyMemory(KNativePtr to, KNativePtr from, KInt count) {
memcpy(to, from, count);
}
} // extern "C"
@@ -0,0 +1,97 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_NATIVES_H
#define RUNTIME_NATIVES_H
#include "Types.h"
#include "Exceptions.h"
#include "Memory.h"
constexpr size_t alignUp(size_t size, size_t alignment) {
return (size + alignment - 1) & ~(alignment - 1);
}
template <typename T>
inline T* AddressOfElementAt(ArrayHeader* obj, KInt index) {
int8_t* body = reinterpret_cast<int8_t*>(obj) + alignUp(sizeof(ArrayHeader), alignof(T));
return reinterpret_cast<T*>(body) + index;
}
template <typename T>
inline const T* AddressOfElementAt(const ArrayHeader* obj, KInt index) {
const int8_t* body = reinterpret_cast<const int8_t*>(obj) + alignUp(sizeof(ArrayHeader), alignof(T));
return reinterpret_cast<const T*>(body) + index;
}
// Optimized versions not accessing type info.
inline KByte* ByteArrayAddressOfElementAt(ArrayHeader* obj, KInt index) {
return AddressOfElementAt<KByte>(obj, index);
}
inline const KByte* ByteArrayAddressOfElementAt(const ArrayHeader* obj, KInt index) {
return AddressOfElementAt<KByte>(obj, index);
}
inline KChar* CharArrayAddressOfElementAt(ArrayHeader* obj, KInt index) {
return AddressOfElementAt<KChar>(obj, index);
}
inline const KChar* CharArrayAddressOfElementAt(const ArrayHeader* obj, KInt index) {
return AddressOfElementAt<KChar>(obj, index);
}
inline KInt* IntArrayAddressOfElementAt(ArrayHeader* obj, KInt index) {
return AddressOfElementAt<KInt>(obj, index);
}
inline const KInt* IntArrayAddressOfElementAt(const ArrayHeader* obj, KInt index) {
return AddressOfElementAt<KInt>(obj, index);
}
// Consider aligning of base to sizeof(T).
template <typename T>
inline T* PrimitiveArrayAddressOfElementAt(ArrayHeader* obj, KInt index) {
return AddressOfElementAt<T>(obj, index);
}
template <typename T>
inline const T* PrimitiveArrayAddressOfElementAt(const ArrayHeader* obj, KInt index) {
return AddressOfElementAt<T>(obj, index);
}
inline KRef* ArrayAddressOfElementAt(ArrayHeader* obj, KInt index) {
return AddressOfElementAt<KRef>(obj, index);
}
inline const KRef* ArrayAddressOfElementAt(const ArrayHeader* obj, KInt index) {
return AddressOfElementAt<KRef>(obj, index);
}
#ifdef __cplusplus
extern "C" {
#endif
OBJ_GETTER0(TheEmptyString);
void Kotlin_io_Console_println0();
void Kotlin_NativePtrArray_set(KRef thiz, KInt index, KNativePtr value);
KNativePtr Kotlin_NativePtrArray_get(KConstRef thiz, KInt index);
#ifdef __cplusplus
}
#endif
#endif // RUNTIME_NATIVES_H
@@ -0,0 +1,125 @@
#if KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG
#include <dlfcn.h>
#include <inttypes.h>
#include <mach-o/loader.h>
#include <CoreFoundation/CFRunLoop.h>
#include "Natives.h"
#include "ObjCExceptions.h"
#include "Types.h"
extern "C" OBJ_GETTER(Kotlin_Throwable_getStackTrace, KRef throwable);
static void writeStackTraceToBuffer(KRef throwable, char* buffer, unsigned long bufferSize) {
if (bufferSize < 2) return;
ObjHolder stackTraceHolder;
ArrayHeader* stackTrace = Kotlin_Throwable_getStackTrace(throwable, stackTraceHolder.slot())->array();
char* bufferPointer = buffer;
unsigned long remainingBytes = bufferSize;
*(bufferPointer++) = '(';
--remainingBytes;
for (uint32_t index = 0; index < stackTrace->count_; ++index) {
KNativePtr ptr = *PrimitiveArrayAddressOfElementAt<KNativePtr>(stackTrace, index);
int bytes = snprintf(bufferPointer, remainingBytes, "0x%" PRIxPTR " ", reinterpret_cast<uintptr_t>(ptr));
if (bytes < 0 || static_cast<unsigned long>(bytes) >= remainingBytes) {
break;
}
bufferPointer += bytes;
remainingBytes -= bytes;
}
*(bufferPointer - 1) = ')'; // Replace last space.
*bufferPointer = '\0';
}
#if !defined(MACHSIZE)
#error "Define MACHSIZE to 32 or 64"
#endif
#if MACHSIZE == 32
typedef struct mach_header mach_header_target;
typedef struct segment_command segment_command_target;
typedef struct section section_target;
static const uint32_t MH_MAGIC_TARGET = MH_MAGIC;
static const uint32_t LC_SEGMENT_TARGET = LC_SEGMENT;
#elif MACHSIZE == 64
typedef struct mach_header_64 mach_header_target;
typedef struct segment_command_64 segment_command_target;
typedef struct section_64 section_target;
static const uint32_t MH_MAGIC_TARGET = MH_MAGIC_64;
static const uint32_t LC_SEGMENT_TARGET = LC_SEGMENT_64;
#else
#error "Impossible MACHSIZE"
#endif
static mach_header_target* findCoreFoundationMachHeader() {
Dl_info info;
if (dladdr(reinterpret_cast<void*>(&CFRunLoopRun), &info) == 0) return nullptr;
return reinterpret_cast<mach_header_target*>(info.dli_fbase);
}
template<int n>
bool bufferEqualsString(const char (&buffer)[n], const char* str) {
return strncmp(buffer, str, n) == 0;
}
static char* findExceptionBacktraceSection(unsigned long *size) {
mach_header_target* header = findCoreFoundationMachHeader();
if (header == nullptr) return nullptr;
if (header->magic != MH_MAGIC_TARGET) return nullptr;
uintptr_t textVmaddr = 0;
load_command* loadCommand = reinterpret_cast<load_command*>(header + 1);
for (uint32_t loadCommandIndex = 0; loadCommandIndex < header->ncmds; ++loadCommandIndex) {
if (loadCommand->cmd == LC_SEGMENT_TARGET) {
segment_command_target* segmentCommand = reinterpret_cast<segment_command_target*>(loadCommand);
if (bufferEqualsString(segmentCommand->segname, "__TEXT")) {
textVmaddr = segmentCommand->vmaddr;
}
if (bufferEqualsString(segmentCommand->segname, "__DATA")) {
section_target* sections = reinterpret_cast<section_target*>(segmentCommand + 1);
for (uint32_t sectionIndex = 0; sectionIndex < segmentCommand->nsects; ++sectionIndex) {
section_target* section = &sections[sectionIndex];
if (bufferEqualsString(section->sectname, "__cf_except_bt") && bufferEqualsString(section->segname, "__DATA")) {
*size = section->size;
return reinterpret_cast<char*>(reinterpret_cast<uintptr_t>(header) + section->addr - textVmaddr);
}
}
}
}
loadCommand = reinterpret_cast<load_command*>(reinterpret_cast<uintptr_t>(loadCommand) + loadCommand->cmdsize);
}
return nullptr;
}
void ReportBacktraceToIosCrashLog(KRef throwable) {
unsigned long bufferSize = 0;
char* buffer = findExceptionBacktraceSection(&bufferSize);
if (buffer == nullptr) return;
// Note: access to this buffer is protected by a lock, but it is not easily accessible.
// Instead assume that typically this buffer is accessed only during termination, and
// rely on caller guaranteeing this code to be executed only before system termination handlers.
writeStackTraceToBuffer(throwable, buffer, bufferSize);
}
#endif // KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG
@@ -0,0 +1,13 @@
#ifndef RUNTIME_OBJCEXCEPTIONS_H
#define RUNTIME_OBJCEXCEPTIONS_H
#if KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG
#include "Common.h"
#include "Types.h"
void ReportBacktraceToIosCrashLog(KRef throwable);
#endif // KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG
#endif // RUNTIME_OBJCEXCEPTIONS_H
@@ -0,0 +1,44 @@
#ifndef RUNTIME_OBJCEXPORT_H
#define RUNTIME_OBJCEXPORT_H
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import <Foundation/NSString.h>
#import "Types.h"
#import "Memory.h"
extern "C" id objc_retain(id self);
extern "C" id objc_retainBlock(id self);
extern "C" void objc_release(id self);
inline static id GetAssociatedObject(ObjHeader* obj) {
return (id)obj->GetAssociatedObject();
}
// Note: this function shall not be used on shared objects.
inline static void SetAssociatedObject(ObjHeader* obj, id value) {
obj->SetAssociatedObject((void*)value);
}
inline static id AtomicCompareAndSwapAssociatedObject(ObjHeader* obj, id expectedValue, id newValue) {
id* location = reinterpret_cast<id*>(obj->GetAssociatedObjectLocation());
return __sync_val_compare_and_swap(location, expectedValue, newValue);
}
inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* typeInfo, id associatedObject) {
ObjHeader* result = AllocInstance(typeInfo, OBJ_RESULT);
SetAssociatedObject(result, associatedObject);
return result;
}
extern "C" id Kotlin_ObjCExport_refToObjC(ObjHeader* obj);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_refFromObjC, id obj);
extern "C" id Kotlin_Interop_CreateNSStringFromKString(KRef str);
extern "C" OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, NSString* str);
#endif // KONAN_OBJC_INTEROP
#endif // RUNTIME_OBJCEXPORT_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,269 @@
/*
* Copyright 2010-2019 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Memory.h"
#import "Types.h"
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSSet.h>
#import "Exceptions.h"
#import "ObjCExport.h"
#import "ObjCExportCollections.h"
extern "C" {
// Imports from ObjCExportUtils.kt:
OBJ_GETTER0(Kotlin_NSEnumeratorAsKIterator_create);
void Kotlin_NSEnumeratorAsKIterator_done(KRef thiz);
void Kotlin_NSEnumeratorAsKIterator_setNext(KRef thiz, KRef value);
void Kotlin_ObjCExport_ThrowCollectionTooLarge();
void Kotlin_ObjCExport_ThrowCollectionConcurrentModification();
}
static inline KInt objCSizeToKotlinOrThrow(NSUInteger size) {
if (size > std::numeric_limits<KInt>::max()) {
Kotlin_ObjCExport_ThrowCollectionTooLarge();
}
return size;
}
// Exported to ObjCExportUtils.kt:
extern "C" KInt Kotlin_NSArrayAsKList_getSize(KRef obj) {
NSArray* array = (NSArray*) GetAssociatedObject(obj);
return objCSizeToKotlinOrThrow([array count]);
}
extern "C" OBJ_GETTER(Kotlin_NSArrayAsKList_get, KRef obj, KInt index) {
NSArray* array = (NSArray*) GetAssociatedObject(obj);
id element = [array objectAtIndex:index];
RETURN_RESULT_OF(refFromObjCOrNSNull, element);
}
extern "C" void Kotlin_NSMutableArrayAsKMutableList_add(KRef thiz, KInt index, KRef element) {
NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz);
[mutableArray insertObject:refToObjCOrNSNull(element) atIndex:index];
}
extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_removeAt, KRef thiz, KInt index) {
NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz);
KRef res = refFromObjCOrNSNull([mutableArray objectAtIndex:index], OBJ_RESULT);
[mutableArray removeObjectAtIndex:index];
return res;
}
extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_set, KRef thiz, KInt index, KRef element) {
NSMutableArray* mutableArray = (NSMutableArray*) GetAssociatedObject(thiz);
KRef res = refFromObjCOrNSNull([mutableArray objectAtIndex:index], OBJ_RESULT);
[mutableArray replaceObjectAtIndex:index withObject:refToObjCOrNSNull(element)];
return res;
}
extern "C" void Kotlin_NSEnumeratorAsKIterator_computeNext(KRef thiz) {
NSEnumerator* enumerator = (NSEnumerator*) GetAssociatedObject(thiz);
id next = [enumerator nextObject];
if (next == nullptr) {
Kotlin_NSEnumeratorAsKIterator_done(thiz);
} else {
ObjHolder holder;
Kotlin_NSEnumeratorAsKIterator_setNext(thiz, refFromObjCOrNSNull(next, holder.slot()));
}
}
extern "C" KInt Kotlin_NSSetAsKSet_getSize(KRef thiz) {
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
return objCSizeToKotlinOrThrow(set.count);
}
extern "C" KBoolean Kotlin_NSSetAsKSet_contains(KRef thiz, KRef element) {
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
return [set containsObject:refToObjCOrNSNull(element)];
}
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_getElement, KRef thiz, KRef element) {
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
id res = [set member:refToObjCOrNSNull(element)];
RETURN_RESULT_OF(refFromObjCOrNSNull, res);
}
static inline OBJ_GETTER(CreateKIteratorFromNSEnumerator, NSEnumerator* enumerator) {
RETURN_RESULT_OF(invokeAndAssociate, Kotlin_NSEnumeratorAsKIterator_create, objc_retain(enumerator));
}
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_iterator, KRef thiz) {
NSSet* set = (NSSet*) GetAssociatedObject(thiz);
RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [set objectEnumerator]);
}
extern "C" KInt Kotlin_NSDictionaryAsKMap_getSize(KRef thiz) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
return objCSizeToKotlinOrThrow(dict.count);
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsKey(KRef thiz, KRef key) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
return [dict objectForKey:refToObjCOrNSNull(key)] != nullptr;
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsValue(KRef thiz, KRef value) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
id objCValue = refToObjCOrNSNull(value);
for (id key in dict) {
if ([[dict objectForKey:key] isEqual:objCValue]) {
return true;
}
}
return false;
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_get, KRef thiz, KRef key) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
id value = [dict objectForKey:refToObjCOrNSNull(key)];
RETURN_RESULT_OF(refFromObjCOrNSNull, value);
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_getOrThrowConcurrentModification, KRef thiz, KRef key) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
id value = [dict objectForKey:refToObjCOrNSNull(key)];
if (value == nullptr) {
Kotlin_ObjCExport_ThrowCollectionConcurrentModification();
}
RETURN_RESULT_OF(refFromObjCOrNSNull, value);
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsEntry(KRef thiz, KRef key, KRef value) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
return [refToObjCOrNSNull(value) isEqual:[dict objectForKey:refToObjCOrNSNull(key)]];
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_keyIterator, KRef thiz) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [dict keyEnumerator]);
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_valueIterator, KRef thiz) {
NSDictionary* dict = (NSDictionary*) GetAssociatedObject(thiz);
RETURN_RESULT_OF(CreateKIteratorFromNSEnumerator, [dict objectEnumerator]);
}
#else // KONAN_OBJC_INTEROP
extern "C" KInt Kotlin_NSArrayAsKList_getSize(KRef obj) {
RuntimeAssert(false, "Objective-C interop is disabled");
return -1;
}
extern "C" OBJ_GETTER(Kotlin_NSArrayAsKList_get, KRef obj, KInt index) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" void Kotlin_NSMutableArrayAsKMutableList_add(KRef thiz, KInt index, KRef element) {
RuntimeAssert(false, "Objective-C interop is disabled");
}
extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_removeAt, KRef thiz, KInt index) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" OBJ_GETTER(Kotlin_NSMutableArrayAsKMutableList_set, KRef thiz, KInt index, KRef element) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" void Kotlin_NSEnumeratorAsKIterator_computeNext(KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
}
extern "C" KInt Kotlin_NSSetAsKSet_getSize(KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
return -1;
}
extern "C" KBoolean Kotlin_NSSetAsKSet_contains(KRef thiz, KRef element) {
RuntimeAssert(false, "Objective-C interop is disabled");
return false;
}
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_getElement, KRef thiz, KRef element) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" OBJ_GETTER(Kotlin_NSSetAsKSet_iterator, KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" KInt Kotlin_NSDictionaryAsKMap_getSize(KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
return -1;
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsKey(KRef thiz, KRef key) {
RuntimeAssert(false, "Objective-C interop is disabled");
return false;
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsValue(KRef thiz, KRef value) {
RuntimeAssert(false, "Objective-C interop is disabled");
return false;
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_get, KRef thiz, KRef key) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_getOrThrowConcurrentModification, KRef thiz, KRef key) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" KBoolean Kotlin_NSDictionaryAsKMap_containsEntry(KRef thiz, KRef key, KRef value) {
RuntimeAssert(false, "Objective-C interop is disabled");
return false;
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_keyIterator, KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
extern "C" OBJ_GETTER(Kotlin_NSDictionaryAsKMap_valueIterator, KRef thiz) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
#endif // KONAN_OBJC_INTEROP
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2019 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_OBJCEXPORTCOLLECTIONS_H
#define RUNTIME_OBJCEXPORTCOLLECTIONS_H
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import <Foundation/NSNull.h>
#import "Memory.h"
#import "ObjCExport.h"
#import "Runtime.h"
// Objective-C collections can't store `nil`, and the common convention is to use `NSNull.null` instead.
// Follow the convention when converting Kotlin `null`:
static inline id refToObjCOrNSNull(KRef obj) {
if (obj == nullptr) {
return NSNull.null;
} else {
return Kotlin_ObjCExport_refToObjC(obj);
}
}
static inline OBJ_GETTER(refFromObjCOrNSNull, id obj) {
if (obj == NSNull.null) {
RETURN_OBJ(nullptr);
} else {
RETURN_RESULT_OF(Kotlin_ObjCExport_refFromObjC, obj);
}
}
static inline OBJ_GETTER(invokeAndAssociate, KRef (*func)(KRef* result), id obj) {
Kotlin_initRuntimeIfNeeded();
KRef kotlinObj = func(OBJ_RESULT);
SetAssociatedObject(kotlinObj, obj);
return kotlinObj;
}
#endif // KONAN_OBJC_INTEROP
#endif // RUNTIME_OBJCEXPORTCOLLECTIONS_H
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#if KONAN_OBJC_INTEROP
#import <pthread.h>
#import <Foundation/NSException.h>
#import <Foundation/NSObject.h>
#import "ObjCExport.h"
#import "ObjCExportErrors.h"
typedef void (^Completion)(id _Nullable, NSError* _Nullable);
extern "C" void Kotlin_ObjCExport_runCompletionSuccess(KRef completionHolder, KRef result) {
Completion completion = (Completion)GetAssociatedObject(completionHolder);
completion(Kotlin_ObjCExport_refToObjC(result), nullptr);
}
extern "C" void Kotlin_ObjCExport_runCompletionFailure(
KRef completionHolder,
KRef exception,
const TypeInfo** exceptionTypes
) {
id error = Kotlin_ObjCExport_ExceptionAsNSError(exception, exceptionTypes);
Completion completion = (Completion)GetAssociatedObject(completionHolder);
completion(nullptr, error);
}
extern "C" OBJ_GETTER(Kotlin_ObjCExport_createContinuationArgumentImpl,
KRef completionHolder, const TypeInfo** exceptionTypes);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_createContinuationArgument, id completion, const TypeInfo** exceptionTypes) {
if (pthread_main_np() != 1) {
[NSException raise:NSGenericException
format:@"Calling Kotlin suspend functions from Swift/Objective-C is currently supported only on main thread"];
}
ObjHolder slot;
KRef completionHolder = AllocInstanceWithAssociatedObject(theForeignObjCObjectTypeInfo,
objc_retainBlock(completion), slot.slot());
RETURN_RESULT_OF(Kotlin_ObjCExport_createContinuationArgumentImpl, completionHolder, exceptionTypes);
}
extern "C" void Kotlin_ObjCExport_resumeContinuationSuccess(KRef continuation, KRef result);
extern "C" void Kotlin_ObjCExport_resumeContinuationFailure(KRef continuation, KRef exception);
extern "C" void Kotlin_ObjCExport_resumeContinuation(KRef continuation, id result, id error) {
ObjHolder holder;
if (error != nullptr) {
if (result != nullptr) {
[NSException raise:NSGenericException
format:@"Kotlin completion handler is called with both result (%@) and error (%@) specified",
result, error];
}
KRef exception = Kotlin_ObjCExport_NSErrorAsException(error, holder.slot());
Kotlin_ObjCExport_resumeContinuationFailure(continuation, exception);
} else {
KRef kotlinResult = Kotlin_ObjCExport_refFromObjC(result, holder.slot());
Kotlin_ObjCExport_resumeContinuationSuccess(continuation, kotlinResult);
}
}
#endif
@@ -0,0 +1,9 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#import "Types.h"
extern "C" id Kotlin_ObjCExport_ExceptionAsNSError(KRef exception, const TypeInfo** types);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsException, id error);
@@ -0,0 +1,137 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if KONAN_OBJC_INTEROP
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSString.h>
#import "Exceptions.h"
#import "ObjCExport.h"
#import "Porting.h"
#import "Runtime.h"
#import "Utils.h"
#import "ObjCExportErrors.h"
extern "C" OBJ_GETTER(Kotlin_Throwable_getMessage, KRef throwable);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_getWrappedError, KRef throwable);
static void printlnMessage(const char* message) {
konan::consolePrintf("%s\n", message);
}
extern "C" RUNTIME_NORETURN void Kotlin_ObjCExport_trapOnUndeclaredException(KRef exception) {
printlnMessage("Function doesn't have or inherit @Throws annotation and thus exception isn't propagated "
"from Kotlin to Objective-C/Swift as NSError.\n"
"It is considered unexpected and unhandled instead. Program will be terminated.");
TerminateWithUnhandledException(exception);
}
static char kotlinExceptionOriginChar;
static bool isExceptionOfType(KRef exception, const TypeInfo** types) {
if (types) for (int i = 0; types[i] != nullptr; ++i) {
// TODO: use fast instance check when possible.
if (IsInstance(exception, types[i])) return true;
}
return false;
}
extern "C" id Kotlin_ObjCExport_ExceptionAsNSError(KRef exception, const TypeInfo** types) {
ObjHolder errorHolder;
KRef error = Kotlin_ObjCExport_getWrappedError(exception, errorHolder.slot());
if (error != nullptr) {
// Thrown originally by Swift/Objective-C.
// Not actually a Kotlin exception, so don't check if it matches [types].
return Kotlin_ObjCExport_refToObjC(error);
}
if (!isExceptionOfType(exception, types)) {
printlnMessage("Exception doesn't match @Throws-specified class list and thus isn't propagated "
"from Kotlin to Objective-C/Swift as NSError.\n"
"It is considered unexpected and unhandled instead. Program will be terminated.");
TerminateWithUnhandledException(exception);
}
NSMutableDictionary<NSErrorUserInfoKey, id>* userInfo = [[NSMutableDictionary new] autorelease];
userInfo[@"KotlinException"] = Kotlin_ObjCExport_refToObjC(exception);
userInfo[@"KotlinExceptionOrigin"] = @(&kotlinExceptionOriginChar); // Support for different Kotlin runtimes loaded.
ObjHolder messageHolder;
KRef message = Kotlin_Throwable_getMessage(exception, messageHolder.slot());
NSString* description = Kotlin_Interop_CreateNSStringFromKString(message);
if (description != nullptr) {
userInfo[NSLocalizedDescriptionKey] = description;
}
return [NSError errorWithDomain:@"KotlinException" code:0 userInfo:userInfo];
}
extern "C" void Kotlin_ObjCExport_RethrowExceptionAsNSError(KRef exception, id* outError, const TypeInfo** types) {
id error = Kotlin_ObjCExport_ExceptionAsNSError(exception, types); // Also traps on unexpected exception.
if (outError != nullptr) *outError = error;
}
extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsExceptionImpl, KRef message, KRef error);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsException, id error) {
NSString* description;
NSError* e = (NSError*) error;
if (e != nullptr) {
auto userInfo = e.userInfo;
if (userInfo != nullptr) {
id kotlinException = userInfo[@"KotlinException"];
id kotlinExceptionOrigin = userInfo[@"KotlinExceptionOrigin"];
if (kotlinException != nullptr &&
kotlinExceptionOrigin != nullptr && [kotlinExceptionOrigin isEqual:@(&kotlinExceptionOriginChar)]
) {
RETURN_RESULT_OF(Kotlin_ObjCExport_refFromObjC, kotlinException);
}
}
description = e.localizedDescription;
} else {
description = nullptr;
}
ObjHolder messageHolder, errorHolder;
KRef message = Kotlin_Interop_CreateKStringFromNSString(description, messageHolder.slot());
KRef kotlinError = Kotlin_ObjCExport_refFromObjC(error, errorHolder.slot()); // TODO: a simple opaque wrapper would be enough.
RETURN_RESULT_OF(Kotlin_ObjCExport_NSErrorAsExceptionImpl, message, kotlinError);
}
extern "C" void Kotlin_ObjCExport_RethrowNSErrorAsException(id error) {
ObjHolder holder;
KRef exception = Kotlin_ObjCExport_NSErrorAsException(error, holder.slot());
ThrowException(exception);
}
@interface NSError (NSErrorKotlinException)
@end;
@implementation NSError (NSErrorKotlinException)
-(id)kotlinException {
auto userInfo = self.userInfo;
return userInfo == nullptr ? nullptr : userInfo[@"KotlinException"];
}
@end;
#endif
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2020 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Memory.h"
#import "Types.h"
#ifdef __cplusplus
extern "C" {
#endif
OBJ_GETTER(Kotlin_ObjCExport_ExceptionDetails, KRef thiz, KRef exceptionHolder);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2020 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Memory.h"
#import "ObjCExportExceptionDetails.h"
#import "ObjCExport.h"
#if KONAN_OBJC_INTEROP
#import <Foundation/NSException.h>
//! TODO: Use not_null signature.
OBJ_GETTER(Kotlin_ObjCExport_ExceptionDetails, KRef /*thiz*/, KRef exceptionHolder) {
if (NSException* exception = (NSException*)Kotlin_ObjCExport_refToObjC(exceptionHolder)) {
RuntimeAssert([exception isKindOfClass:[NSException class]], "Illegal type: NSException expected");
NSString* ret = [NSString stringWithFormat: @"%@:: %@", exception.name, exception.reason];
RETURN_RESULT_OF(Kotlin_Interop_CreateKStringFromNSString, ret);
}
RETURN_OBJ(nullptr);
}
#else // KONAN_OBJC_INTEROP
OBJ_GETTER(Kotlin_ObjCExport_ExceptionDetails, KRef /*thiz*/, KRef /*exceptionHolder*/) {
RETURN_OBJ(nullptr);
}
#endif
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_OBJCEXPORTINIT_H
#define RUNTIME_OBJCEXPORTINIT_H
#if KONAN_OBJC_INTEROP
extern "C" void Kotlin_ObjCExport_initialize(void);
#endif // KONAN_OBJC_INTEROP
#endif // RUNTIME_OBJCEXPORTINIT_H
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_OBJCEXPORTPRIVATE_H
#define RUNTIME_OBJCEXPORTPRIVATE_H
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import "Types.h"
#import "Memory.h"
#import "ObjCExport.h"
@interface KotlinBase : NSObject <NSCopying>
+(instancetype)createWrapper:(ObjHeader*)obj;
@end;
extern "C" void Kotlin_ObjCExport_initializeClass(Class clazz);
extern "C" const TypeInfo* Kotlin_ObjCExport_getAssociatedTypeInfo(Class clazz);
extern "C" OBJ_GETTER(Kotlin_ObjCExport_convertUnmappedObjCObject, id obj);
extern "C" SEL Kotlin_ObjCExport_toKotlinSelector;
extern "C" SEL Kotlin_ObjCExport_releaseAsAssociatedObjectSelector;
const TypeInfo* Kotlin_ObjCExport_createTypeInfoWithKotlinFieldsFrom(Class clazz, const TypeInfo* fieldsInfo);
#endif // KONAN_OBJC_INTEROP
#endif // RUNTIME_OBJCEXPORTPRIVATE_H
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_OBJCINTEROP_H
#define RUNTIME_OBJCINTEROP_H
#if KONAN_OBJC_INTEROP
const char* Kotlin_ObjCInterop_getUniquePrefix();
#endif // KONAN_OBJC_INTEROP
#endif // RUNTIME_OBJCINTEROP_H
@@ -0,0 +1,374 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if KONAN_OBJC_INTEROP
#import <Foundation/NSException.h>
#import <objc/objc-exception.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <objc/message.h>
#include <cstdio>
#include <cstdint>
#include "Memory.h"
#include "MemorySharedRefs.hpp"
#include "Natives.h"
#include "ObjCInterop.h"
#include "ObjCExportPrivate.h"
#include "Types.h"
#include "Utils.h"
// Replaced in ObjCExportCodeGenerator.
__attribute__((weak)) const char* Kotlin_ObjCInterop_uniquePrefix = nullptr;
const char* Kotlin_ObjCInterop_getUniquePrefix() {
auto result = Kotlin_ObjCInterop_uniquePrefix;
RuntimeCheck(result != nullptr, "unique prefix is not initialized");
return result;
}
extern "C" id objc_msgSendSuper2(struct objc_super *super, SEL op, ...);
struct KotlinClassData {
const TypeInfo* typeInfo;
int32_t bodyOffset;
};
static inline struct KotlinClassData* GetKotlinClassData(Class clazz) {
void* ivars = object_getIndexedIvars(reinterpret_cast<id>(clazz));
return static_cast<struct KotlinClassData*>(ivars);
}
namespace {
BackRefFromAssociatedObject* getBackRef(id obj, KotlinClassData* classData) {
void* body = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + classData->bodyOffset);
return reinterpret_cast<BackRefFromAssociatedObject*>(body);
}
BackRefFromAssociatedObject* getBackRef(id obj) {
// TODO: suboptimal; consider specializing methods for each class.
auto* classData = GetKotlinClassData(object_getClass(obj));
return getBackRef(obj, classData);
}
OBJ_GETTER(toKotlinImp, id self, SEL _cmd) {
RETURN_OBJ(getBackRef(self)->ref<ErrorPolicy::kTerminate>());
}
id allocWithZoneImp(Class self, SEL _cmd, void* zone) {
// [super allocWithZone:zone]
struct objc_super s = {(id)self, object_getClass((id)self)};
auto messenger = reinterpret_cast<id (*) (struct objc_super*, SEL _cmd, void* zone)>(objc_msgSendSuper2);
id result = messenger(&s, _cmd, zone);
auto* classData = GetKotlinClassData(self); // TODO: suboptimal; consider specializing.
auto* typeInfo = classData->typeInfo;
ObjHolder holder;
auto kotlinObj = AllocInstanceWithAssociatedObject(typeInfo, result, holder.slot());
getBackRef(result, classData)->initAndAddRef(kotlinObj);
return result;
}
id retainImp(id self, SEL _cmd) {
getBackRef(self)->addRef<ErrorPolicy::kTerminate>();
return self;
}
BOOL _tryRetainImp(id self, SEL _cmd) {
// TODO: [tryAddRef] currently works only on the owner thread for non-shared objects;
// this is a regression for instances of Kotlin subclasses of Obj-C classes:
// loading a reference to such an object from Obj-C weak reference now fails on "wrong" thread
// unless the object is frozen.
try {
return getBackRef(self)->tryAddRef<ErrorPolicy::kThrow>();
} catch (ExceptionObjHolder& e) {
// TODO: check for IncorrectDereferenceException and possible weak property access
// Cannot use SourceInfo here, because CoreSymbolication framework (CSSymbolOwnerGetSymbolWithAddress)
// fails at recursive retain lock. Similarly, cannot use objc exception here, because its unhandled
// exception handler might fail at recursive retain lock too.
// TODO: Refactor to be more explicit. Instead of relying on an unhandled exception termination
// (and effectively setting a global to alter its behavior), just call an appropriate termination
// function by hand.
DisallowSourceInfo();
std::terminate();
}
}
void releaseImp(id self, SEL _cmd) {
getBackRef(self)->releaseRef();
}
void releaseAsAssociatedObjectImp(id self, SEL _cmd) {
// This function is called by the GC. It made a decision to reclaim Kotlin object, and runs
// deallocation hooks at the moment, including deallocation of the "associated object" ([self])
// using the [super release] call below.
// The deallocation involves running [self dealloc] which can contain arbitrary code.
// In particular, this code can retain and release [self]. Obj-C and Swift runtimes handle this
// gracefully (unless the object gets accessed after the deallocation of course), but Kotlin doesn't.
// For example, this happens in https://youtrack.jetbrains.com/issue/KT-41811, provoked by
// UIViewController.dealloc (which retains-releases self._view._viewDelegate == self) and UIView.dealloc.
// Generally retaining and releasing Kotlin object that is being deallocated would lead to
// use-after-dispose and double-dispose problems (with unpredictable consequences) or to an assertion failure.
// To workaround this, detach the back ref from the Kotlin object:
getBackRef(self)->detach();
// So retain/release/etc. on [self] won't affect the Kotlin object, and an attempt to get
// the reference to it (e.g. when calling Kotlin method on [self]) would crash.
// The latter is generally ok, because by the time superclass dealloc gets launched, subclass state
// should already be deinitialized, and Kotlin methods operate on the subclass.
// [super release]
Class clazz = object_getClass(self);
struct objc_super s = {self, clazz};
auto messenger = reinterpret_cast<void (*) (struct objc_super*, SEL _cmd)>(objc_msgSendSuper2);
messenger(&s, @selector(release));
}
}
extern "C" {
Class Kotlin_Interop_getObjCClass(const char* name);
static inline void SetKotlinTypeInfo(Class clazz, const TypeInfo* typeInfo) {
GetKotlinClassData(clazz)->typeInfo = typeInfo;
}
const TypeInfo* GetObjCKotlinTypeInfo(ObjHeader* obj) RUNTIME_NOTHROW;
RUNTIME_NOTHROW const TypeInfo* GetObjCKotlinTypeInfo(ObjHeader* obj) {
void* objcPtr = obj->GetAssociatedObject();
RuntimeAssert(objcPtr != nullptr, "");
Class clazz = object_getClass(reinterpret_cast<id>(objcPtr));
return GetKotlinClassData(clazz)->typeInfo;
}
static void AddNSObjectOverride(bool isClassMethod, Class clazz, SEL selector, void* imp) {
Class nsObjectClass = Kotlin_Interop_getObjCClass("NSObject");
Method nsObjectMethod = class_getInstanceMethod(
isClassMethod ? object_getClass((id)nsObjectClass) : nsObjectClass, selector);
RuntimeCheck(nsObjectMethod != nullptr, "NSObject method not found");
const char* nsObjectMethodTypeEncoding = method_getTypeEncoding(nsObjectMethod);
RuntimeCheck(nsObjectMethodTypeEncoding != nullptr, "NSObject method has no encoding provided");
// TODO: something of the above can be cached.
BOOL added = class_addMethod(
isClassMethod ? object_getClass((id)clazz) : clazz, selector, (IMP)imp, nsObjectMethodTypeEncoding);
RuntimeCheck(added, "Unable to add method to Objective-C class");
}
struct ObjCMethodDescription {
void* (*imp)(void*, void*, ...);
const char* selector;
const char* encoding;
};
struct KotlinObjCClassInfo {
const char* name;
int exported;
const char* superclassName;
const char** protocolNames;
const struct ObjCMethodDescription* instanceMethods;
int32_t instanceMethodsNum;
const struct ObjCMethodDescription* classMethods;
int32_t classMethodsNum;
int32_t* bodyOffset;
const TypeInfo* typeInfo;
const TypeInfo* metaTypeInfo;
void** createdClass;
};
static void AddMethods(Class clazz, const struct ObjCMethodDescription* methods, int32_t methodsNum) {
for (int32_t i = 0; i < methodsNum; ++i) {
const struct ObjCMethodDescription* method = &methods[i];
BOOL added = class_addMethod(clazz, sel_registerName(method->selector), (IMP)method->imp, method->encoding);
RuntimeAssert(added == YES, "Unable to add method to Objective-C class");
}
}
static SimpleMutex classCreationMutex;
static int anonymousClassNextId = 0;
static Class allocateClass(const KotlinObjCClassInfo* info) {
Class superclass = Kotlin_Interop_getObjCClass(info->superclassName);
size_t extraBytes = sizeof(struct KotlinClassData);
if (info->exported) {
RuntimeCheck(info->name != nullptr, "exported Objective-C class must have a name");
Class result = objc_allocateClassPair(superclass, info->name, extraBytes);
if (result != nullptr) return result;
// Similar to how Objective-C runtime handles this:
fprintf(stderr, "Class %s has multiple implementations. Which one will be used is undefined.\n", info->name);
}
KStdString className = Kotlin_ObjCInterop_getUniquePrefix();
if (info->name != nullptr) {
className += info->name;
} else {
className += "_kobjc";
}
int classId = anonymousClassNextId++;
className += std::to_string(classId);
Class result = objc_allocateClassPair(superclass, className.c_str(), extraBytes);
RuntimeCheck(result != nullptr, "Failed to allocate Objective-C class");
return result;
}
void* CreateKotlinObjCClass(const KotlinObjCClassInfo* info) {
LockGuard<SimpleMutex> lockGuard(classCreationMutex);
void* createdClass = *info->createdClass;
if (createdClass != nullptr) {
return createdClass;
}
Class newClass = allocateClass(info);
RuntimeAssert(newClass != nullptr, "Failed to allocate Objective-C class");
Class newMetaclass = object_getClass(reinterpret_cast<id>(newClass));
for (size_t i = 0;; ++i) {
const char* protocolName = info->protocolNames[i];
if (protocolName == nullptr) break;
Protocol* proto = objc_getProtocol(protocolName);
if (proto != nullptr) {
BOOL added = class_addProtocol(newClass, proto);
RuntimeAssert(added == YES, "Unable to add protocol to Objective-C class");
added = class_addProtocol(newMetaclass, proto);
RuntimeAssert(added == YES, "Unable to add protocol to Objective-C metaclass");
}
}
AddNSObjectOverride(false, newClass, Kotlin_ObjCExport_toKotlinSelector, (void*)&toKotlinImp);
AddNSObjectOverride(true, newClass, @selector(allocWithZone:), (void*)&allocWithZoneImp);
AddNSObjectOverride(false, newClass, @selector(retain), (void*)&retainImp);
AddNSObjectOverride(false, newClass, @selector(_tryRetain), (void*)&_tryRetainImp);
AddNSObjectOverride(false, newClass, @selector(release), (void*)&releaseImp);
AddNSObjectOverride(false, newClass, Kotlin_ObjCExport_releaseAsAssociatedObjectSelector,
(void*)&releaseAsAssociatedObjectImp);
AddMethods(newClass, info->instanceMethods, info->instanceMethodsNum);
AddMethods(newMetaclass, info->classMethods, info->classMethodsNum);
SetKotlinTypeInfo(newClass, Kotlin_ObjCExport_createTypeInfoWithKotlinFieldsFrom(newClass, info->typeInfo));
int bodySize = sizeof(BackRefFromAssociatedObject);
char bodyTypeEncoding[16];
snprintf(bodyTypeEncoding, sizeof(bodyTypeEncoding), "[%dc]", bodySize);
BOOL added = class_addIvar(newClass, "kotlinBody", bodySize, /* log2(align) = */ 3, bodyTypeEncoding);
RuntimeAssert(added == YES, "Unable to add ivar to Objective-C class");
objc_registerClassPair(newClass);
Ivar body = class_getInstanceVariable(newClass, "kotlinBody");
RuntimeAssert(body != nullptr, "Unable to get ivar added to Objective-C class");
int32_t offset = (int32_t)ivar_getOffset(body);
GetKotlinClassData(newClass)->bodyOffset = offset;
*info->bodyOffset = offset;
*info->createdClass = newClass;
return newClass;
}
void* objc_autoreleasePoolPush();
void objc_autoreleasePoolPop(void* ptr);
id objc_allocWithZone(Class clazz);
id objc_retain(id ptr);
void objc_release(id ptr);
void* Kotlin_objc_autoreleasePoolPush() {
return objc_autoreleasePoolPush();
}
void Kotlin_objc_autoreleasePoolPop(void* ptr) {
objc_autoreleasePoolPop(ptr);
}
id Kotlin_objc_allocWithZone(Class clazz) {
return objc_allocWithZone(clazz);
}
id Kotlin_objc_retain(id ptr) {
return objc_retain(ptr);
}
void Kotlin_objc_release(id ptr) {
objc_release(ptr);
}
Class Kotlin_objc_lookUpClass(const char* name) {
return objc_lookUpClass(name);
}
} // extern "C"
#else // KONAN_OBJC_INTEROP
#include "KAssert.h"
extern "C" {
void* Kotlin_objc_autoreleasePoolPush() {
RuntimeAssert(false, "Objective-C interop is disabled");
return nullptr;
}
void Kotlin_objc_autoreleasePoolPop(void* ptr) {
RuntimeAssert(false, "Objective-C interop is disabled");
}
void* Kotlin_objc_allocWithZone(void* clazz) {
RuntimeAssert(false, "Objective-C interop is disabled");
return nullptr;
}
void* Kotlin_objc_retain(void* ptr) {
RuntimeAssert(false, "Objective-C interop is disabled");
return nullptr;
}
void Kotlin_objc_release(void* ptr) {
RuntimeAssert(false, "Objective-C interop is disabled");
}
void* Kotlin_objc_lookUpClass(const char* name) {
RuntimeAssert(false, "Objective-C interop is disabled");
return nullptr;
}
} // extern "C"
#endif // KONAN_OBJC_INTEROP
@@ -0,0 +1,174 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Natives.h"
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import <CoreFoundation/CFString.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#import "Memory.h"
#import "ObjCInteropUtilsPrivate.h"
namespace {
Class nsStringClass = nullptr;
Class getNSStringClass() {
Class result = nsStringClass;
if (result == nullptr) {
// Lookup dynamically to avoid direct reference to Foundation:
result = objc_getClass("NSString");
RuntimeAssert(result != nullptr, "NSString class not found");
nsStringClass = result;
}
return result;
}
// Note: using @"foo" string literals leads to linkage dependency on frameworks.
NSString* cStringToNS(const char* str) {
return [getNSStringClass() stringWithCString:str encoding:NSUTF8StringEncoding];
}
}
extern "C" {
id Kotlin_ObjCExport_CreateNSStringFromKString(ObjHeader* str);
id Kotlin_Interop_CreateNSStringFromKString(ObjHeader* str) {
// Note: this function is just a bit specialized [Kotlin_Interop_refToObjC].
if (str == nullptr) {
return nullptr;
}
if (void* associatedObject = str->GetAssociatedObject()) {
return (id)associatedObject;
}
return Kotlin_ObjCExport_CreateNSStringFromKString(str);
}
OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, NSString* str) {
if (str == nullptr) {
RETURN_OBJ(nullptr);
}
CFStringRef immutableCopyOrSameStr = CFStringCreateCopy(nullptr, (CFStringRef)str);
auto length = CFStringGetLength(immutableCopyOrSameStr);
CFRange range = {0, length};
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, length, OBJ_RESULT)->array();
KChar* rawResult = CharArrayAddressOfElementAt(result, 0);
CFStringGetCharacters(immutableCopyOrSameStr, range, rawResult);
result->obj()->SetAssociatedObject((void*)immutableCopyOrSameStr);
RETURN_OBJ(result->obj());
}
// Note: this body is used for init methods with signatures differing from this;
// it is correct on arm64 and x86_64, because the body uses only the first two arguments which are fixed,
// and returns pointers.
id MissingInitImp(id self, SEL _cmd) {
const char* className = object_getClassName(self);
[self release]; // Since init methods receive ownership on the receiver.
// Lookup dynamically to avoid direct reference to Foundation:
Class nsExceptionClass = objc_getClass("NSException");
RuntimeAssert(nsExceptionClass != nullptr, "NSException class not found");
[nsExceptionClass raise:cStringToNS("Initializer is not implemented")
format:cStringToNS("%s is not implemented in %s"),
sel_getName(_cmd), className];
return nullptr;
}
// Initialized in [ObjCInteropUtilsClasses.mm].
id (*Kotlin_Interop_createKotlinObjectHolder_ptr)(KRef any) = nullptr;
KRef (*Kotlin_Interop_unwrapKotlinObjectHolder_ptr)(id holder) = nullptr;
id Kotlin_Interop_createKotlinObjectHolder(KRef any) {
return Kotlin_Interop_createKotlinObjectHolder_ptr(any);
}
KRef Kotlin_Interop_unwrapKotlinObjectHolder(id holder) {
return Kotlin_Interop_unwrapKotlinObjectHolder_ptr(holder);
}
KBoolean Kotlin_Interop_DoesObjectConformToProtocol(id obj, void* prot, KBoolean isMeta) {
BOOL objectIsClass = class_isMetaClass(object_getClass(obj));
if ((isMeta && !objectIsClass) || (!isMeta && objectIsClass)) return false;
// TODO: handle root classes properly.
return [((id<NSObject>)obj) conformsToProtocol:(Protocol*)prot];
}
KBoolean Kotlin_Interop_IsObjectKindOfClass(id obj, void* cls) {
return [((id<NSObject>)obj) isKindOfClass:(Class)cls];
}
OBJ_GETTER((*Konan_ObjCInterop_getWeakReference_ptr), KRef ref) = nullptr;
void (*Konan_ObjCInterop_initWeakReference_ptr)(KRef ref, id objcPtr) = nullptr;
OBJ_GETTER(Konan_ObjCInterop_getWeakReference, KRef ref) {
RETURN_RESULT_OF(Konan_ObjCInterop_getWeakReference_ptr, ref);
}
void Konan_ObjCInterop_initWeakReference(KRef ref, id objcPtr) {
Konan_ObjCInterop_initWeakReference_ptr(ref, objcPtr);
}
} // extern "C"
#else // KONAN_OBJC_INTEROP
extern "C" {
void* Kotlin_Interop_CreateNSStringFromKString(const ArrayHeader* str) {
RuntimeAssert(false, "Objective-C interop is disabled");
return nullptr;
}
OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, void* str) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
void* Kotlin_Interop_createKotlinObjectHolder(KRef any) {
RuntimeAssert(false, "Objective-C interop is disabled");
return nullptr;
}
KRef Kotlin_Interop_unwrapKotlinObjectHolder(void* holder) {
RuntimeAssert(false, "Objective-C interop is disabled");
return nullptr;
}
OBJ_GETTER(Konan_ObjCInterop_getWeakReference, KRef ref) {
RuntimeAssert(false, "Objective-C interop is disabled");
RETURN_OBJ(nullptr);
}
void Konan_ObjCInterop_initWeakReference(KRef ref, void* objcPtr) {
RuntimeAssert(false, "Objective-C interop is disabled");
}
} // extern "C"
#endif // KONAN_OBJC_INTEROP
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_OBJCINTEROPUTILSPRIVATE_H
#define RUNTIME_OBJCINTEROPUTILSPRIVATE_H
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import "Types.h"
#import "Memory.h"
extern "C" id (*Kotlin_Interop_createKotlinObjectHolder_ptr)(KRef any);
extern "C" KRef (*Kotlin_Interop_unwrapKotlinObjectHolder_ptr)(id holder);
extern "C" OBJ_GETTER((*Konan_ObjCInterop_getWeakReference_ptr), KRef ref);
extern "C" void (*Konan_ObjCInterop_initWeakReference_ptr)(KRef ref, id objcPtr);
#endif // KONAN_OBJC_INTEROP
#endif // RUNTIME_OBJCINTEROPUTILSPRIVATE_H
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_OBJCMMAPI_H
#define RUNTIME_OBJCMMAPI_H
#include "Common.h"
#if KONAN_OBJC_INTEROP
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
#endif // KONAN_OBJC_INTEROP
#endif // RUNTIME_OBJCMMAPI_H
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <math.h>
#include <limits.h>
#include "DoubleConversions.h"
#include "Natives.h"
#include "Common.h"
extern "C" {
//--- Float -------------------------------------------------------------------//
KInt Kotlin_Float_toInt(KFloat a) {
if (isnan(a)) return 0;
if (a >= (KFloat) INT32_MAX) return INT32_MAX;
if (a <= (KFloat) INT32_MIN) return INT32_MIN;
return a;
}
KLong Kotlin_Float_toLong(KFloat a) {
if (isnan(a)) return 0;
if (a >= (KFloat) INT64_MAX) return INT64_MAX;
if (a <= (KFloat) INT64_MIN) return INT64_MIN;
return a;
}
KByte Kotlin_Float_toByte(KFloat a) { return (KByte) Kotlin_Float_toInt(a); }
KShort Kotlin_Float_toShort(KFloat a) { return (KShort) Kotlin_Float_toInt(a); }
ALWAYS_INLINE KBoolean Kotlin_Float_isNaN(KFloat a) { return isnan(a); }
ALWAYS_INLINE KBoolean Kotlin_Float_isInfinite(KFloat a) { return isinf(a); }
ALWAYS_INLINE KBoolean Kotlin_Float_isFinite(KFloat a) { return isfinite(a); }
//--- Double ------------------------------------------------------------------//
KInt Kotlin_Double_toInt(KDouble a) {
if (isnan(a)) return 0;
if (a >= (KDouble) INT32_MAX) return INT32_MAX;
if (a <= (KDouble) INT32_MIN) return INT32_MIN;
return a;
}
KLong Kotlin_Double_toLong(KDouble a) {
if (isnan(a)) return 0;
if (a >= (KDouble) INT64_MAX) return INT64_MAX;
if (a <= (KDouble) INT64_MIN) return INT64_MIN;
return a;
}
ALWAYS_INLINE KBoolean Kotlin_Double_isNaN(KDouble a) { return isnan(a); }
ALWAYS_INLINE KBoolean Kotlin_Double_isInfinite(KDouble a) { return isinf(a); }
ALWAYS_INLINE KBoolean Kotlin_Double_isFinite(KDouble a) { return isfinite(a); }
//--- Bit operations ---------------------------------------------------------//
ALWAYS_INLINE KInt Kotlin_Int_countOneBits(KInt value) { return __builtin_popcount(value); }
ALWAYS_INLINE KInt Kotlin_Long_countOneBits(KLong value) { return __builtin_popcountll(value); }
ALWAYS_INLINE KInt Kotlin_Int_countTrailingZeroBits(KInt value) { return __builtin_ctz(value); }
ALWAYS_INLINE KInt Kotlin_Long_countTrailingZeroBits(KLong value) { return __builtin_ctzll(value); }
ALWAYS_INLINE KInt Kotlin_Int_countLeadingZeroBits(KInt value) { return __builtin_clz(value); }
ALWAYS_INLINE KInt Kotlin_Long_countLeadingZeroBits(KLong value) { return __builtin_clzll(value); }
} // extern "C"
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_POINTER_BITS_H
#define RUNTIME_POINTER_BITS_H
#include <cstdint>
#include "Common.h"
template <typename T>
ALWAYS_INLINE T* setPointerBits(T* ptr, unsigned bits) {
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) | bits);
}
template <typename T>
ALWAYS_INLINE T* clearPointerBits(T* ptr, unsigned bits) {
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) & ~static_cast<uintptr_t>(bits));
}
template <typename T>
ALWAYS_INLINE unsigned getPointerBits(T* ptr, unsigned bits) {
return reinterpret_cast<uintptr_t>(ptr) & static_cast<uintptr_t>(bits);
}
template <typename T>
ALWAYS_INLINE bool hasPointerBits(T* ptr, unsigned bits) {
return getPointerBits(ptr, bits) != 0;
}
#endif // RUNTIME_POINTER_BITS_H
@@ -0,0 +1,545 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef KONAN_ANDROID
#include <android/log.h>
#endif
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if !KONAN_NO_THREADS
#include <pthread.h>
#endif
#include <unistd.h>
#if KONAN_WINDOWS
#include <windows.h>
#endif
#include <chrono>
#include "Common.h"
#include "Porting.h"
#include "KAssert.h"
#if KONAN_WASM || KONAN_ZEPHYR
extern "C" RUNTIME_NORETURN void Konan_abort(const char*);
extern "C" RUNTIME_NORETURN void Konan_exit(int32_t status);
#endif
#ifdef KONAN_ZEPHYR
// In Zephyr's Newlib strnlen(3) is not included from string.h by default.
extern "C" size_t strnlen(const char* buffer, size_t maxSize);
#endif
namespace konan {
// Console operations.
void consoleInit() {
#if KONAN_WINDOWS
// Note that this code enforces UTF-8 console output, so we may want to rethink
// how we perform console IO, if it turns out, that UTF-16 is better output format.
::SetConsoleCP(CP_UTF8);
::SetConsoleOutputCP(CP_UTF8);
// FIXME: should set original CP back during the deinit of the program.
// Otherwise, this codepage remains in the console.
#endif
}
void consoleWriteUtf8(const void* utf8, uint32_t sizeBytes) {
#ifdef KONAN_ANDROID
// TODO: use sizeBytes!
__android_log_print(ANDROID_LOG_INFO, "Konan_main", "%s", utf8);
#else
::write(STDOUT_FILENO, utf8, sizeBytes);
#endif
}
void consoleErrorUtf8(const void* utf8, uint32_t sizeBytes) {
#ifdef KONAN_ANDROID
// TODO: use sizeBytes!
__android_log_print(ANDROID_LOG_ERROR, "Konan_main", "%s", utf8);
#else
::write(STDERR_FILENO, utf8, sizeBytes);
#endif
}
#if KONAN_WINDOWS
int getLastErrorMessage(char* message, uint32_t size) {
auto errCode = ::GetLastError();
if (errCode) {
auto flags = FORMAT_MESSAGE_FROM_SYSTEM;
auto errMsgBufSize = size / 4;
wchar_t errMsgBuffer[errMsgBufSize];
::FormatMessageW(flags, NULL, errCode, 0, errMsgBuffer, errMsgBufSize, NULL);
::WideCharToMultiByte(CP_UTF8, 0, errMsgBuffer, -1, message, size, NULL, NULL);
}
return errCode;
}
#endif
int32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes) {
#ifdef KONAN_ZEPHYR
return 0;
#elif KONAN_WINDOWS
auto length = 0;
void *stdInHandle = ::GetStdHandle(STD_INPUT_HANDLE);
if (::GetFileType(stdInHandle) == FILE_TYPE_CHAR) {
unsigned long bufferRead;
// In UTF-16 there are surrogate pairs that a 2 * 16-bit long (4 bytes).
auto bufferLength = maxSizeBytes / 4 - 1;
wchar_t buffer[bufferLength];
if (::ReadConsoleW(stdInHandle, buffer, bufferLength, &bufferRead, NULL)) {
length = ::WideCharToMultiByte(CP_UTF8, 0, buffer, bufferRead, (char*) utf8,
maxSizeBytes - 1, NULL, NULL);
if (!length && KonanNeedDebugInfo) {
char msg[512];
auto errCode = getLastErrorMessage(msg, sizeof(msg));
consoleErrorf("UTF-16 to UTF-8 conversion error %d: %s", errCode, msg);
}
((char*) utf8)[length] = 0;
} else if (KonanNeedDebugInfo) {
char msg[512];
auto errCode = getLastErrorMessage(msg, sizeof(msg));
consoleErrorf("Console read failure: %d %s", errCode, msg);
}
} else {
length = ::read(STDIN_FILENO, utf8, maxSizeBytes - 1);
}
#else
auto length = ::read(STDIN_FILENO, utf8, maxSizeBytes - 1);
#endif
if (length <= 0) return -1;
char* start = reinterpret_cast<char*>(utf8);
char* current = start + length - 1;
bool isTrimming = true;
while (current >= start && isTrimming) {
switch (*current) {
case '\n':
case '\r':
*current = 0;
length--;
break;
default:
isTrimming = false;
}
current--;
}
return length;
}
#if KONAN_INTERNAL_SNPRINTF
extern "C" int rpl_vsnprintf(char *, size_t, const char *, va_list);
#define vsnprintf_impl rpl_vsnprintf
#else
#define vsnprintf_impl ::vsnprintf
#endif
void consolePrintf(const char* format, ...) {
char buffer[1024];
va_list args;
va_start(args, format);
int rv = vsnprintf_impl(buffer, sizeof(buffer), format, args);
if (rv < 0) return; // TODO: this may be too much exotic, but should i try to print itoa(error) and terminate?
if (static_cast<size_t>(rv) >= sizeof(buffer)) rv = sizeof(buffer) - 1; // TODO: Consider realloc or report truncating.
va_end(args);
consoleWriteUtf8(buffer, rv);
}
// TODO: Avoid code duplication.
void consoleErrorf(const char* format, ...) {
char buffer[1024];
va_list args;
va_start(args, format);
int rv = vsnprintf_impl(buffer, sizeof(buffer), format, args);
if (rv < 0) return; // TODO: this may be too much exotic, but should i try to print itoa(error) and terminate?
if (static_cast<size_t>(rv) >= sizeof(buffer)) rv = sizeof(buffer) - 1; // TODO: Consider realloc or report truncating.
va_end(args);
consoleErrorUtf8(buffer, rv);
}
void consoleFlush() {
::fflush(stdout);
::fflush(stderr);
}
// Thread execution.
#if !KONAN_NO_THREADS
pthread_key_t terminationKey;
pthread_once_t terminationKeyOnceControl = PTHREAD_ONCE_INIT;
typedef void (*destructor_t)(void*);
struct DestructorRecord {
struct DestructorRecord* next;
destructor_t destructor;
void* destructorParameter;
};
static void onThreadExitCallback(void* value) {
DestructorRecord* record = reinterpret_cast<DestructorRecord*>(value);
while (record != nullptr) {
record->destructor(record->destructorParameter);
auto next = record->next;
free(record);
record = next;
}
pthread_setspecific(terminationKey, nullptr);
}
#if KONAN_LINUX
static pthread_key_t dummyKey;
#endif
static void onThreadExitInit() {
#if KONAN_LINUX
// Due to glibc bug we have to create first key as dummy, to avoid
// conflicts with potentially uninitialized dlfcn error key.
// https://code.woboq.org/userspace/glibc/dlfcn/dlerror.c.html#237
// As one may see, glibc checks value of the key even if it was not inited (and == 0),
// and so data associated with our legit key (== 0 as being the first one) is used.
// Other libc are not affected, as usually == 0 pthread key is impossible.
pthread_key_create(&dummyKey, nullptr);
#endif
pthread_key_create(&terminationKey, onThreadExitCallback);
}
#endif // !KONAN_NO_THREADS
void onThreadExit(void (*destructor)(void*), void* destructorParameter) {
#if KONAN_NO_THREADS
#if KONAN_WASM || KONAN_ZEPHYR
// No way to do that.
#else
#error "How to do onThreadExit()?"
#endif
#else // !KONAN_NO_THREADS
// We cannot use pthread_cleanup_push() as it is lexical scope bound.
pthread_once(&terminationKeyOnceControl, onThreadExitInit);
DestructorRecord* destructorRecord = (DestructorRecord*)calloc(1, sizeof(DestructorRecord));
destructorRecord->destructor = destructor;
destructorRecord->destructorParameter = destructorParameter;
destructorRecord->next =
reinterpret_cast<DestructorRecord*>(pthread_getspecific(terminationKey));
pthread_setspecific(terminationKey, destructorRecord);
#endif // !KONAN_NO_THREADS
}
// Process execution.
void abort(void) {
::abort();
}
#if KONAN_WASM || KONAN_ZEPHYR
void exit(int32_t status) {
Konan_exit(status);
}
#else
void exit(int32_t status) {
::exit(status);
}
#endif
// String/byte operations.
// memcpy/memmove are not here intentionally, as frequently implemented/optimized
// by C compiler.
void* memmem(const void *big, size_t bigLen, const void *little, size_t littleLen) {
#if KONAN_NO_MEMMEM
for (size_t i = 0; i + littleLen <= bigLen; ++i) {
void* pos = ((char*)big) + i;
if (::memcmp(little, pos, littleLen) == 0) return pos;
}
return nullptr;
#else
return ::memmem(big, bigLen, little, littleLen);
#endif
}
// The sprintf family.
int snprintf(char* buffer, size_t size, const char* format, ...) {
va_list args;
va_start(args, format);
int rv = vsnprintf_impl(buffer, size, format, args);
va_end(args);
return rv;
}
size_t strnlen(const char* buffer, size_t maxSize) {
return ::strnlen(buffer, maxSize);
}
// Memory operations.
#if KONAN_INTERNAL_DLMALLOC
extern "C" void* dlcalloc(size_t, size_t);
extern "C" void dlfree(void*);
#define calloc_impl dlcalloc
#define free_impl dlfree
#define calloc_aligned_impl(count, size, alignment) dlcalloc(count, size)
#else
extern "C" void* konan_calloc_impl(size_t, size_t);
extern "C" void konan_free_impl(void*);
extern "C" void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment);
#define calloc_impl konan_calloc_impl
#define free_impl konan_free_impl
#define calloc_aligned_impl konan_calloc_aligned_impl
#endif
void* calloc(size_t count, size_t size) {
return calloc_impl(count, size);
}
void* calloc_aligned(size_t count, size_t size, size_t alignment) {
return calloc_aligned_impl(count, size, alignment);
}
void free(void* pointer) {
free_impl(pointer);
}
#if KONAN_INTERNAL_NOW
#ifdef KONAN_ZEPHYR
void Konan_date_now(uint64_t* arg) {
// TODO: so how will we support time for embedded?
*arg = 0LL;
}
#else
extern "C" void Konan_date_now(uint64_t*);
#endif
uint64_t getTimeMillis() {
uint64_t now;
Konan_date_now(&now);
return now;
}
uint64_t getTimeMicros() {
return getTimeMillis() * 1000ULL;
}
uint64_t getTimeNanos() {
return getTimeMillis() * 1000000ULL;
}
#else
// Time operations.
using namespace std::chrono;
// Get steady clock as a source of time
using steady_time_clock = std::conditional<high_resolution_clock::is_steady, high_resolution_clock, steady_clock>::type;
uint64_t getTimeMillis() {
return duration_cast<milliseconds>(steady_time_clock::now().time_since_epoch()).count();
}
uint64_t getTimeNanos() {
return duration_cast<nanoseconds>(steady_time_clock::now().time_since_epoch()).count();
}
uint64_t getTimeMicros() {
return duration_cast<microseconds>(steady_time_clock::now().time_since_epoch()).count();
}
#endif
#if KONAN_INTERNAL_DLMALLOC
// This function is being called when memory allocator needs more RAM.
#if KONAN_WASM
namespace {
constexpr uint32_t MFAIL = ~(uint32_t)0;
constexpr uint32_t WASM_PAGESIZE_EXPONENT = 16;
constexpr uint32_t WASM_PAGESIZE = 1u << WASM_PAGESIZE_EXPONENT;
constexpr uint32_t WASM_PAGEMASK = WASM_PAGESIZE-1;
uint32_t pageAlign(int32_t value) {
return (value + WASM_PAGEMASK) & ~ (WASM_PAGEMASK);
}
uint32_t inBytes(uint32_t pageCount) {
return pageCount << WASM_PAGESIZE_EXPONENT;
}
uint32_t inPages(uint32_t value) {
return value >> WASM_PAGESIZE_EXPONENT;
}
extern "C" void Konan_notify_memory_grow();
uint32_t memorySize() {
return __builtin_wasm_memory_size(0);
}
int32_t growMemory(uint32_t delta) {
int32_t oldLength = __builtin_wasm_memory_grow(0, delta);
Konan_notify_memory_grow();
return oldLength;
}
}
void* moreCore(int32_t delta) {
uint32_t top = inBytes(memorySize());
if (delta > 0) {
if (growMemory(inPages(pageAlign(delta))) == 0) {
return (void *) MFAIL;
}
} else if (delta < 0) {
return (void *) MFAIL;
}
return (void *) top;
}
// dlmalloc() wants to know the page size.
long getpagesize() {
return WASM_PAGESIZE;
}
#else
void* moreCore(int size) {
return sbrk(size);
}
long getpagesize() {
return sysconf(_SC_PAGESIZE);
}
#endif
#endif
} // namespace konan
extern "C" {
// TODO: get rid of these.
#if (KONAN_WASM || KONAN_ZEPHYR)
void _ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv(void) {
Konan_abort("TODO: throw_length_error not implemented.");
}
void _ZNKSt3__220__vector_base_commonILb1EE20__throw_length_errorEv(void) {
Konan_abort("TODO: throw_length_error not implemented.");
}
void _ZNKSt3__121__basic_string_commonILb1EE20__throw_length_errorEv(void) {
Konan_abort("TODO: throw_length_error not implemented.");
}
void _ZNKSt3__221__basic_string_commonILb1EE20__throw_length_errorEv(void) {
Konan_abort("TODO: throw_length_error not implemented.");
}
int _ZNSt3__212__next_primeEj(unsigned long n) {
static unsigned long primes[] = {
11UL,
101UL,
1009UL,
10007UL,
100003UL,
1000003UL,
10000019UL,
100000007UL,
1000000007UL
};
size_t table_length = sizeof(primes)/sizeof(unsigned long);
if (n > primes[table_length - 1]) konan::abort();
unsigned long prime = primes[0];
for (unsigned long i=0; i< table_length; i++) {
prime = primes[i];
if (prime >= n) break;
}
return prime;
}
int _ZNSt3__212__next_primeEm(int n) {
return _ZNSt3__212__next_primeEj(n);
}
int _ZNSt3__112__next_primeEj(unsigned long n) {
return _ZNSt3__212__next_primeEj(n);
}
void __assert_fail(const char * assertion, const char * file, unsigned int line, const char * function) {
char buf[1024];
konan::snprintf(buf, sizeof(buf), "%s:%d in %s: runtime assert: %s\n", file, line, function, assertion);
Konan_abort(buf);
}
int* __errno_location() {
static int theErrno = 0;
return &theErrno;
}
// Some math.h functions.
double pow(double x, double y) {
return __builtin_pow(x, y);
}
#endif
#ifdef KONAN_WASM
// Some string.h functions.
void *memcpy(void *dst, const void *src, size_t n) {
for (size_t i = 0; i != n; ++i)
*((char*)dst + i) = *((char*)src + i);
return dst;
}
void *memmove(void *dst, const void *src, size_t len) {
if (src < dst) {
for (long i = len; i != 0; --i) {
*((char*)dst + i - 1) = *((char*)src + i - 1);
}
} else {
memcpy(dst, src, len);
}
return dst;
}
int memcmp(const void *s1, const void *s2, size_t n) {
for (size_t i = 0; i != n; ++i) {
if (*((char*)s1 + i) != *((char*)s2 + i)) {
return *((char*)s1 + i) - *((char*)s2 + i);
}
}
return 0;
}
void *memset(void *b, int c, size_t len) {
for (size_t i = 0; i != len; ++i) {
*((char*)b + i) = c;
}
return b;
}
size_t strlen(const char *s) {
for (long i = 0;; ++i) {
if (s[i] == 0) return i;
}
}
size_t strnlen(const char *s, size_t maxlen) {
for (size_t i = 0; i<=maxlen; ++i) {
if (s[i] == 0) return i;
}
return maxlen;
}
#endif
#ifdef KONAN_ZEPHYR
RUNTIME_USED void Konan_abort(const char*) {
while(1) {}
}
#endif // KONAN_ZEPHYR
} // extern "C"
@@ -0,0 +1,100 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_PORTING_H
#define RUNTIME_PORTING_H
#include <stdint.h>
#include <stddef.h>
#include "Common.h"
namespace konan {
// Console operations.
void consoleInit();
void consolePrintf(const char* format, ...);
void consoleErrorf(const char* format, ...);
void consoleWriteUtf8(const void* utf8, uint32_t sizeBytes);
void consoleErrorUtf8(const void* utf8, uint32_t sizeBytes);
// Negative return value denotes that read wasn't successful.
int32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes);
void consoleFlush();
// Process control.
RUNTIME_NORETURN void abort(void);
RUNTIME_NORETURN void exit(int32_t status);
// Thread control.
void onThreadExit(void (*destructor)(void*), void* destructorParameter);
// String/byte operations.
// memcpy/memmove/memcmp are not here intentionally, as frequently implemented/optimized
// by C compiler.
void* memmem(const void *big, size_t bigLen, const void *little, size_t littleLen);
int snprintf(char* buffer, size_t size, const char* format, ...);
size_t strnlen(const char* buffer, size_t maxSize);
// These functions should be marked with RUNTIME_USED attribute for wasm target
// because clang replaces these operations with intrinsics that will be
// replaced back to library calls only on codegen step. And there is no stdlib
// for wasm target for now :(
// Otherwise `opt` will see no usages of these definitions and will remove them.
extern "C" {
#ifdef KONAN_WASM
RUNTIME_USED
double pow(double x, double y);
RUNTIME_USED
void *memcpy(void *dst, const void *src, size_t n);
RUNTIME_USED
void *memmove(void *dst, const void *src, size_t len);
RUNTIME_USED
int memcmp(const void *s1, const void *s2, size_t n);
RUNTIME_USED
void *memset(void *b, int c, size_t len);
#endif
}
// Memory operations.
void* calloc(size_t count, size_t size);
void* calloc_aligned(size_t count, size_t size, size_t alignment);
void free(void* ptr);
// Time operations.
uint64_t getTimeMillis();
uint64_t getTimeMicros();
uint64_t getTimeNanos();
#if KONAN_NO_EXCEPTIONS
#define TRY_CATCH(tryAction, actionWithoutExceptions, catchAction) actionWithoutExceptions;
#else
#define TRY_CATCH(tryAction, actionWithoutExceptions, catchAction) \
do { \
try { tryAction; } \
catch(...) { catchAction; } \
} while(0)
#endif
} // namespace konan
#endif // RUNTIME_PORTING_H
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "PthreadUtils.h"
#include <pthread.h>
#include <sys/time.h>
namespace {
constexpr int64_t kNanosecondsInASecond = 1000000000LL;
} // namespace
int WaitOnCondVar(
pthread_cond_t* cond,
pthread_mutex_t* mutex,
uint64_t timeoutNanoseconds,
uint64_t* microsecondsPassed) {
struct timeval tvBefore;
// TODO: Error reporting?
gettimeofday(&tvBefore, nullptr);
struct timespec ts;
const uint64_t nanoseconds = tvBefore.tv_usec * 1000LL + timeoutNanoseconds;
ts.tv_sec = tvBefore.tv_sec + nanoseconds / kNanosecondsInASecond;
ts.tv_nsec = nanoseconds % kNanosecondsInASecond;
auto result = pthread_cond_timedwait(cond, mutex, &ts);
if (microsecondsPassed) {
struct timeval tvAfter;
// TODO: Error reporting?
gettimeofday(&tvAfter, nullptr);
*microsecondsPassed = (tvAfter.tv_sec - tvBefore.tv_sec) * 1000000LL +
tvAfter.tv_usec - tvBefore.tv_usec;
}
return result;
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_PTHREAD_UTILS_H
#define RUNTIME_PTHREAD_UTILS_H
#include <cstdint>
#include <pthread.h>
// Releases mutex and waits on cond for timeoutNanoseconds.
// Returns ETIMEDOUT if timeoutNanoseconds has passed.
int WaitOnCondVar(
pthread_cond_t* cond,
pthread_mutex_t* mutex,
uint64_t timeoutNanoseconds,
uint64_t* microsecondsPassed = nullptr);
#endif // RUNTIME_PTHREAD_UTILS_H
@@ -0,0 +1,622 @@
#include <cstring>
#include "Types.h"
#include "KString.h"
#include "Natives.h"
namespace {
/* Contains canonical classes (see http://www.unicode.org/Public/4.0-Update/UnicodeData-4.0.0.txt). */
constexpr KInt canonicalClassesKeys[] = {
768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790,
791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813,
814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836,
837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860,
861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 1155, 1156, 1157, 1158,
1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443,
1444, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462,
1463, 1464, 1465, 1467, 1468, 1469, 1471, 1473, 1474, 1476, 1477, 1479, 1552, 1553, 1554, 1555, 1556, 1557, 1611,
1612, 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, 1621, 1622, 1623, 1624, 1625, 1626, 1627, 1628, 1629, 1630,
1648, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1759, 1760, 1761, 1762, 1763, 1764, 1767, 1768, 1770, 1771, 1772,
1773, 1809, 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1850, 1851, 1852, 1853, 1854, 1855, 1856,
1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, 2364, 2381, 2385, 2386, 2387, 2388, 2492, 2509, 2620,
2637, 2748, 2765, 2876, 2893, 3021, 3149, 3157, 3158, 3260, 3277, 3405, 3530, 3640, 3641, 3642, 3656, 3657, 3658,
3659, 3768, 3769, 3784, 3785, 3786, 3787, 3864, 3865, 3893, 3895, 3897, 3953, 3954, 3956, 3962, 3963, 3964, 3965,
3968, 3970, 3971, 3972, 3974, 3975, 4038, 4151, 4153, 4959, 5908, 5940, 6098, 6109, 6313, 6457, 6458, 6459, 6679,
6680, 7616, 7617, 7618, 7619, 8400, 8401, 8402, 8403, 8404, 8405, 8406, 8407, 8408, 8409, 8410, 8411, 8412, 8417,
8421, 8422, 8423, 8424, 8425, 8426, 8427, 12330, 12331, 12332, 12333, 12334, 12335, 12441, 12442, 43014, 64286, 65056,
65057, 65058, 65059, 68109, 68111, 68152, 68153, 68154, 68159, 119141, 119142, 119143, 119144, 119145, 119149, 119150,
119151, 119152, 119153, 119154, 119163, 119164, 119165, 119166, 119167, 119168, 119169, 119170, 119173, 119174,
119175, 119176, 119177, 119178, 119179, 119210, 119211, 119212, 119213, 119362, 119363, 119364,
};
constexpr KInt canonicalClassesValues[] = {
230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 232, 220,
220, 220, 220, 232, 216, 220, 220, 220, 220, 220, 202, 202, 220, 220, 220, 220, 202, 202, 220, 220, 220, 220, 220,
220, 220, 220, 220, 220, 220, 1, 1, 1, 1, 1, 220, 220, 220, 220, 230, 230, 230, 230, 230, 230, 230, 230, 240, 230,
220, 220, 220, 230, 230, 230, 220, 220, 230, 230, 230, 220, 220, 220, 220, 230, 232, 220, 220, 230, 233, 234, 234,
233, 234, 234, 233, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 220, 230,
230, 230, 230, 220, 230, 230, 230, 222, 220, 230, 230, 230, 230, 230, 230, 220, 220, 220, 220, 220, 220, 230, 230,
220, 230, 230, 222, 228, 230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 230, 220, 18, 230, 230,
230, 230, 230, 230, 27, 28, 29, 30, 31, 32, 33, 34, 230, 230, 220, 220, 230, 230, 230, 230, 230, 220, 230, 230, 35,
230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 220, 230, 230, 230, 220, 230, 230, 220, 36, 230, 220, 230, 230,
220, 230, 230, 220, 220, 220, 230, 220, 220, 230, 220, 230, 230, 230, 220, 230, 220, 230, 220, 230, 220, 230, 230, 7,
9, 230, 220, 230, 230, 7, 9, 7, 9, 7, 9, 7, 9, 9, 9, 84, 91, 7, 9, 9, 9, 103, 103, 9, 107, 107, 107, 107, 118, 118,
122, 122, 122, 122, 220, 220, 220, 220, 216, 129, 130, 132, 130, 130, 130, 130, 130, 230, 230, 9, 230, 230, 220, 7, 9,
230, 9, 9, 9, 230, 228, 222, 230, 220, 230, 220, 230, 230, 220, 230, 230, 230, 1, 1, 230, 230, 230, 230, 1, 1, 1, 230,
230, 230, 1, 1, 230, 220, 230, 1, 1, 218, 228, 232, 222, 224, 224, 8, 8, 9, 26, 230, 230, 230, 230, 220, 230, 230, 1,
220, 9, 216, 216, 1, 1, 1, 226, 216, 216, 216, 216, 216, 220, 220, 220, 220, 220, 220, 220, 220, 230, 230, 230, 230,
230, 220, 220, 230, 230, 230, 230, 230, 230, 230,
};
/* Symbols that are one symbol decompositions (see http://www.unicode.org/Public/4.0-Update/UnicodeData-4.0.0.txt). */
constexpr KInt singleDecompositions[] = {
59, 75, 96, 180, 183, 197, 697, 768, 769, 787, 901, 902, 904, 905, 906, 908, 910, 911, 912, 937, 940, 941, 942, 943,
944, 953, 972, 973, 974, 8194, 8195, 12296, 12297, 13470, 13497, 13499, 13535, 13589, 14062, 14076, 14209, 14383,
14434, 14460, 14535, 14563, 14620, 14650, 14894, 14956, 15076, 15112, 15129, 15177, 15261, 15384, 15438, 15667, 15766,
16044, 16056, 16155, 16380, 16392, 16408, 16441, 16454, 16534, 16611, 16687, 16898, 16935, 17056, 17153, 17204, 17241,
17365, 17369, 17419, 17515, 17707, 17757, 17761, 17771, 17879, 17913, 17973, 18110, 18119, 18837, 18918, 19054, 19062,
19122, 19251, 19406, 19662, 19693, 19704, 19798, 19981, 20006, 20018, 20024, 20025, 20029, 20033, 20098, 20102, 20142,
20160, 20172, 20196, 20320, 20352, 20358, 20363, 20398, 20411, 20415, 20482, 20523, 20602, 20633, 20687, 20698, 20711,
20800, 20805, 20813, 20820, 20836, 20839, 20840, 20841, 20845, 20855, 20864, 20877, 20882, 20885, 20887, 20900, 20908,
20917, 20919, 20937, 20940, 20956, 20958, 20981, 20995, 20999, 21015, 21033, 21050, 21051, 21062, 21106, 21111, 21129,
21147, 21155, 21171, 21191, 21193, 21202, 21214, 21220, 21237, 21242, 21253, 21254, 21271, 21311, 21321, 21329, 21338,
21363, 21365, 21373, 21375, 21443, 21450, 21471, 21477, 21483, 21489, 21510, 21519, 21533, 21560, 21570, 21576, 21608,
21662, 21666, 21693, 21750, 21776, 21843, 21845, 21859, 21892, 21895, 21913, 21917, 21931, 21939, 21952, 21954, 21986,
22022, 22097, 22120, 22132, 22265, 22294, 22295, 22411, 22478, 22516, 22541, 22577, 22578, 22592, 22618, 22622, 22696,
22700, 22707, 22744, 22751, 22766, 22770, 22775, 22790, 22810, 22818, 22852, 22856, 22865, 22868, 22882, 22899, 23000,
23020, 23067, 23079, 23138, 23142, 23221, 23304, 23336, 23358, 23429, 23491, 23512, 23527, 23534, 23539, 23551, 23558,
23586, 23615, 23648, 23650, 23652, 23653, 23662, 23693, 23744, 23833, 23875, 23888, 23915, 23918, 23932, 23986, 23994,
24033, 24034, 24061, 24104, 24125, 24169, 24180, 24230, 24240, 24243, 24246, 24265, 24266, 24274, 24275, 24281, 24300,
24318, 24324, 24354, 24403, 24418, 24425, 24427, 24459, 24474, 24489, 24493, 24525, 24535, 24565, 24569, 24594, 24604,
24705, 24724, 24775, 24792, 24801, 24840, 24900, 24904, 24908, 24910, 24928, 24936, 24954, 24974, 24976, 24996, 25007,
25010, 25054, 25074, 25078, 25088, 25104, 25115, 25134, 25140, 25181, 25265, 25289, 25295, 25299, 25300, 25340, 25342,
25405, 25424, 25448, 25467, 25475, 25504, 25513, 25540, 25541, 25572, 25628, 25634, 25682, 25705, 25719, 25726, 25754,
25757, 25796, 25935, 25942, 25964, 25976, 26009, 26053, 26082, 26083, 26131, 26185, 26228, 26248, 26257, 26268, 26292,
26310, 26356, 26360, 26368, 26391, 26395, 26401, 26446, 26451, 26454, 26462, 26491, 26501, 26519, 26611, 26618, 26647,
26655, 26706, 26753, 26757, 26766, 26792, 26900, 26946, 27043, 27114, 27138, 27155, 27304, 27347, 27355, 27396, 27425,
27476, 27506, 27511, 27513, 27551, 27566, 27578, 27579, 27726, 27751, 27784, 27839, 27852, 27853, 27877, 27926, 27931,
27934, 27956, 27966, 27969, 28009, 28010, 28023, 28024, 28037, 28107, 28122, 28138, 28153, 28186, 28207, 28270, 28316,
28346, 28359, 28363, 28369, 28379, 28431, 28450, 28451, 28526, 28614, 28651, 28670, 28699, 28702, 28729, 28746, 28784,
28791, 28797, 28825, 28845, 28872, 28889, 28997, 29001, 29038, 29084, 29134, 29136, 29200, 29211, 29224, 29227, 29237,
29264, 29282, 29312, 29333, 29359, 29376, 29436, 29482, 29557, 29562, 29575, 29579, 29605, 29618, 29662, 29702, 29705,
29730, 29767, 29788, 29801, 29809, 29829, 29833, 29848, 29898, 29958, 29988, 30011, 30014, 30041, 30053, 30064, 30178,
30224, 30237, 30239, 30274, 30313, 30410, 30427, 30439, 30452, 30465, 30494, 30495, 30528, 30538, 30603, 30631, 30798,
30827, 30860, 30865, 30922, 30924, 30971, 31018, 31036, 31038, 31048, 31049, 31056, 31062, 31069, 31070, 31077, 31103,
31117, 31118, 31119, 31150, 31178, 31211, 31260, 31296, 31306, 31311, 31361, 31409, 31435, 31470, 31520, 31680, 31686,
31689, 31806, 31840, 31867, 31890, 31934, 31954, 31958, 31971, 31975, 31976, 32000, 32016, 32034, 32047, 32091, 32099,
32160, 32190, 32199, 32244, 32258, 32265, 32311, 32321, 32325, 32574, 32626, 32633, 32634, 32645, 32661, 32666, 32701,
32762, 32769, 32773, 32838, 32864, 32879, 32880, 32894, 32907, 32941, 32946, 33027, 33086, 33240, 33256, 33261, 33281,
33284, 33391, 33401, 33419, 33425, 33437, 33457, 33459, 33469, 33509, 33510, 33565, 33571, 33590, 33618, 33619, 33635,
33709, 33725, 33737, 33738, 33740, 33756, 33767, 33775, 33777, 33853, 33865, 33879, 34030, 34033, 34035, 34044, 34070,
34148, 34253, 34298, 34310, 34322, 34349, 34367, 34384, 34396, 34407, 34409, 34440, 34473, 34530, 34574, 34600, 34667,
34681, 34694, 34746, 34785, 34817, 34847, 34892, 34912, 34915, 35010, 35023, 35031, 35038, 35041, 35064, 35066, 35088,
35137, 35172, 35206, 35211, 35222, 35488, 35498, 35519, 35531, 35538, 35542, 35565, 35576, 35582, 35585, 35641, 35672,
35712, 35722, 35912, 35925, 36011, 36033, 36034, 36040, 36051, 36104, 36123, 36215, 36284, 36299, 36335, 36336, 36554,
36564, 36646, 36650, 36664, 36667, 36706, 36766, 36784, 36790, 36899, 36920, 36978, 36988, 37007, 37012, 37070, 37105,
37117, 37137, 37147, 37226, 37273, 37300, 37324, 37327, 37329, 37428, 37432, 37494, 37500, 37591, 37592, 37636, 37706,
37881, 37909, 38283, 38317, 38327, 38446, 38475, 38477, 38517, 38520, 38524, 38534, 38563, 38584, 38595, 38626, 38627,
38646, 38647, 38691, 38706, 38728, 38742, 38875, 38880, 38911, 38923, 38936, 38953, 38971, 39006, 39138, 39151, 39164,
39208, 39209, 39335, 39362, 39409, 39422, 39530, 39698, 39791, 40000, 40023, 40189, 40295, 40372, 40442, 40478, 40575,
40599, 40607, 40635, 40654, 40697, 40702, 40709, 40719, 40726, 40763, 40771, 40845, 40846, 40860, 131362, 132380,
132389, 132427, 132666, 133124, 133342, 133676, 133987, 136420, 136872, 136938, 137672, 138008, 138507, 138724,
138726, 139651, 139679, 140081, 141012, 141380, 141386, 142092, 142321, 143370, 144056, 144223, 144275, 144284,
144323, 144341, 144493, 145059, 145575, 146061, 146170, 146620, 146718, 147153, 147294, 147342, 148067, 148395,
149000, 149301, 149524, 150582, 150674, 151457, 151480, 151620, 151794, 151795, 151833, 151859, 152137, 152605,
153126, 153242, 153285, 153980, 154279, 154539, 154752, 154832, 155526, 156122, 156200, 156231, 156377, 156478,
156890, 156963, 157096, 157607, 157621, 158524, 158774, 158933, 159083, 159532, 159665, 159954, 160714, 161383,
161966, 162150, 162984, 163539, 163631, 165330, 165357, 165678, 166906, 167287, 168261, 168415, 168474, 168970,
169110, 169398, 170800, 172238, 172293, 172558, 172689, 172946, 173568
};
constexpr KInt decompositionKeys[] = {
192, 193, 194, 195, 196, 197, 199, 200, 201, 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 217, 218,
219, 220, 221, 224, 225, 226, 227, 228, 229, 231, 232, 233, 234, 235, 236, 237, 238, 239, 241, 242, 243, 244, 245,
246, 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271,
274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 296, 297, 298,
299, 300, 301, 302, 303, 304, 308, 309, 310, 311, 313, 314, 315, 316, 317, 318, 323, 324, 325, 326, 327, 328, 332,
333, 334, 335, 336, 337, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357,
360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382,
416, 417, 431, 432, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 478, 479, 480,
481, 482, 483, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 500, 501, 504, 505, 506, 507, 508, 509, 510,
511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533,
534, 535, 536, 537, 538, 539, 542, 543, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 832,
833, 835, 836, 884, 894, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 938, 939, 940, 941, 942, 943, 944, 970,
971, 972, 973, 974, 979, 980, 1024, 1025, 1027, 1031, 1036, 1037, 1038, 1049, 1081, 1104, 1105, 1107, 1111, 1116,
1117, 1118, 1142, 1143, 1217, 1218, 1232, 1233, 1234, 1235, 1238, 1239, 1242, 1243, 1244, 1245, 1246, 1247, 1250,
1251, 1252, 1253, 1254, 1255, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269, 1272, 1273,
1570, 1571, 1572, 1573, 1574, 1728, 1730, 1747, 2345, 2353, 2356, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399,
2507, 2508, 2524, 2525, 2527, 2611, 2614, 2649, 2650, 2651, 2654, 2888, 2891, 2892, 2908, 2909, 2964, 3018, 3019,
3020, 3144, 3264, 3271, 3272, 3274, 3275, 3402, 3403, 3404, 3546, 3548, 3549, 3550, 3907, 3917, 3922, 3927, 3932,
3945, 3955, 3957, 3958, 3960, 3969, 3987, 3997, 4002, 4007, 4012, 4025, 4134, 7680, 7681, 7682, 7683, 7684, 7685,
7686, 7687, 7688, 7689, 7690, 7691, 7692, 7693, 7694, 7695, 7696, 7697, 7698, 7699, 7700, 7701, 7702, 7703, 7704,
7705, 7706, 7707, 7708, 7709, 7710, 7711, 7712, 7713, 7714, 7715, 7716, 7717, 7718, 7719, 7720, 7721, 7722, 7723,
7724, 7725, 7726, 7727, 7728, 7729, 7730, 7731, 7732, 7733, 7734, 7735, 7736, 7737, 7738, 7739, 7740, 7741, 7742,
7743, 7744, 7745, 7746, 7747, 7748, 7749, 7750, 7751, 7752, 7753, 7754, 7755, 7756, 7757, 7758, 7759, 7760, 7761,
7762, 7763, 7764, 7765, 7766, 7767, 7768, 7769, 7770, 7771, 7772, 7773, 7774, 7775, 7776, 7777, 7778, 7779, 7780,
7781, 7782, 7783, 7784, 7785, 7786, 7787, 7788, 7789, 7790, 7791, 7792, 7793, 7794, 7795, 7796, 7797, 7798, 7799,
7800, 7801, 7802, 7803, 7804, 7805, 7806, 7807, 7808, 7809, 7810, 7811, 7812, 7813, 7814, 7815, 7816, 7817, 7818,
7819, 7820, 7821, 7822, 7823, 7824, 7825, 7826, 7827, 7828, 7829, 7830, 7831, 7832, 7833, 7835, 7840, 7841, 7842,
7843, 7844, 7845, 7846, 7847, 7848, 7849, 7850, 7851, 7852, 7853, 7854, 7855, 7856, 7857, 7858, 7859, 7860, 7861,
7862, 7863, 7864, 7865, 7866, 7867, 7868, 7869, 7870, 7871, 7872, 7873, 7874, 7875, 7876, 7877, 7878, 7879, 7880,
7881, 7882, 7883, 7884, 7885, 7886, 7887, 7888, 7889, 7890, 7891, 7892, 7893, 7894, 7895, 7896, 7897, 7898, 7899,
7900, 7901, 7902, 7903, 7904, 7905, 7906, 7907, 7908, 7909, 7910, 7911, 7912, 7913, 7914, 7915, 7916, 7917, 7918,
7919, 7920, 7921, 7922, 7923, 7924, 7925, 7926, 7927, 7928, 7929, 7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943,
7944, 7945, 7946, 7947, 7948, 7949, 7950, 7951, 7952, 7953, 7954, 7955, 7956, 7957, 7960, 7961, 7962, 7963, 7964,
7965, 7968, 7969, 7970, 7971, 7972, 7973, 7974, 7975, 7976, 7977, 7978, 7979, 7980, 7981, 7982, 7983, 7984, 7985,
7986, 7987, 7988, 7989, 7990, 7991, 7992, 7993, 7994, 7995, 7996, 7997, 7998, 7999, 8000, 8001, 8002, 8003, 8004,
8005, 8008, 8009, 8010, 8011, 8012, 8013, 8016, 8017, 8018, 8019, 8020, 8021, 8022, 8023, 8025, 8027, 8029, 8031,
8032, 8033, 8034, 8035, 8036, 8037, 8038, 8039, 8040, 8041, 8042, 8043, 8044, 8045, 8046, 8047, 8048, 8049, 8050,
8051, 8052, 8053, 8054, 8055, 8056, 8057, 8058, 8059, 8060, 8061, 8064, 8065, 8066, 8067, 8068, 8069, 8070, 8071,
8072, 8073, 8074, 8075, 8076, 8077, 8078, 8079, 8080, 8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089, 8090,
8091, 8092, 8093, 8094, 8095, 8096, 8097, 8098, 8099, 8100, 8101, 8102, 8103, 8104, 8105, 8106, 8107, 8108, 8109,
8110, 8111, 8112, 8113, 8114, 8115, 8116, 8118, 8119, 8120, 8121, 8122, 8123, 8124, 8126, 8129, 8130, 8131, 8132,
8134, 8135, 8136, 8137, 8138, 8139, 8140, 8141, 8142, 8143, 8144, 8145, 8146, 8147, 8150, 8151, 8152, 8153, 8154,
8155, 8157, 8158, 8159, 8160, 8161, 8162, 8163, 8164, 8165, 8166, 8167, 8168, 8169, 8170, 8171, 8172, 8173, 8174,
8175, 8178, 8179, 8180, 8182, 8183, 8184, 8185, 8186, 8187, 8188, 8189, 8192, 8193, 8486, 8490, 8491, 8602, 8603,
8622, 8653, 8654, 8655, 8708, 8713, 8716, 8740, 8742, 8769, 8772, 8775, 8777, 8800, 8802, 8813, 8814, 8815, 8816,
8817, 8820, 8821, 8824, 8825, 8832, 8833, 8836, 8837, 8840, 8841, 8876, 8877, 8878, 8879, 8928, 8929, 8930, 8931,
8938, 8939, 8940, 8941, 9001, 9002, 10972, 12364, 12366, 12368, 12370, 12372, 12374, 12376, 12378, 12380, 12382,
12384, 12386, 12389, 12391, 12393, 12400, 12401, 12403, 12404, 12406, 12407, 12409, 12410, 12412, 12413, 12436, 12446,
12460, 12462, 12464, 12466, 12468, 12470, 12472, 12474, 12476, 12478, 12480, 12482, 12485, 12487, 12489, 12496, 12497,
12499, 12500, 12502, 12503, 12505, 12506, 12508, 12509, 12532, 12535, 12536, 12537, 12538, 12542, 63744, 63745, 63746,
63747, 63748, 63749, 63750, 63751, 63752, 63753, 63754, 63755, 63756, 63757, 63758, 63759, 63760, 63761, 63762, 63763,
63764, 63765, 63766, 63767, 63768, 63769, 63770, 63771, 63772, 63773, 63774, 63775, 63776, 63777, 63778, 63779, 63780,
63781, 63782, 63783, 63784, 63785, 63786, 63787, 63788, 63789, 63790, 63791, 63792, 63793, 63794, 63795, 63796, 63797,
63798, 63799, 63800, 63801, 63802, 63803, 63804, 63805, 63806, 63807, 63808, 63809, 63810, 63811, 63812, 63813, 63814,
63815, 63816, 63817, 63818, 63819, 63820, 63821, 63822, 63823, 63824, 63825, 63826, 63827, 63828, 63829, 63830, 63831,
63832, 63833, 63834, 63835, 63836, 63837, 63838, 63839, 63840, 63841, 63842, 63843, 63844, 63845, 63846, 63847, 63848,
63849, 63850, 63851, 63852, 63853, 63854, 63855, 63856, 63857, 63858, 63859, 63860, 63861, 63862, 63863, 63864, 63865,
63866, 63867, 63868, 63869, 63870, 63871, 63872, 63873, 63874, 63875, 63876, 63877, 63878, 63879, 63880, 63881, 63882,
63883, 63884, 63885, 63886, 63887, 63888, 63889, 63890, 63891, 63892, 63893, 63894, 63895, 63896, 63897, 63898, 63899,
63900, 63901, 63902, 63903, 63904, 63905, 63906, 63907, 63908, 63909, 63910, 63911, 63912, 63913, 63914, 63915, 63916,
63917, 63918, 63919, 63920, 63921, 63922, 63923, 63924, 63925, 63926, 63927, 63928, 63929, 63930, 63931, 63932, 63933,
63934, 63935, 63936, 63937, 63938, 63939, 63940, 63941, 63942, 63943, 63944, 63945, 63946, 63947, 63948, 63949, 63950,
63951, 63952, 63953, 63954, 63955, 63956, 63957, 63958, 63959, 63960, 63961, 63962, 63963, 63964, 63965, 63966, 63967,
63968, 63969, 63970, 63971, 63972, 63973, 63974, 63975, 63976, 63977, 63978, 63979, 63980, 63981, 63982, 63983, 63984,
63985, 63986, 63987, 63988, 63989, 63990, 63991, 63992, 63993, 63994, 63995, 63996, 63997, 63998, 63999, 64000, 64001,
64002, 64003, 64004, 64005, 64006, 64007, 64008, 64009, 64010, 64011, 64012, 64013, 64016, 64018, 64021, 64022, 64023,
64024, 64025, 64026, 64027, 64028, 64029, 64030, 64032, 64034, 64037, 64038, 64042, 64043, 64044, 64045, 64048, 64049,
64050, 64051, 64052, 64053, 64054, 64055, 64056, 64057, 64058, 64059, 64060, 64061, 64062, 64063, 64064, 64065, 64066,
64067, 64068, 64069, 64070, 64071, 64072, 64073, 64074, 64075, 64076, 64077, 64078, 64079, 64080, 64081, 64082, 64083,
64084, 64085, 64086, 64087, 64088, 64089, 64090, 64091, 64092, 64093, 64094, 64095, 64096, 64097, 64098, 64099, 64100,
64101, 64102, 64103, 64104, 64105, 64106, 64112, 64113, 64114, 64115, 64116, 64117, 64118, 64119, 64120, 64121, 64122,
64123, 64124, 64125, 64126, 64127, 64128, 64129, 64130, 64131, 64132, 64133, 64134, 64135, 64136, 64137, 64138, 64139,
64140, 64141, 64142, 64143, 64144, 64145, 64146, 64147, 64148, 64149, 64150, 64151, 64152, 64153, 64154, 64155, 64156,
64157, 64158, 64159, 64160, 64161, 64162, 64163, 64164, 64165, 64166, 64167, 64168, 64169, 64170, 64171, 64172, 64173,
64174, 64175, 64176, 64177, 64178, 64179, 64180, 64181, 64182, 64183, 64184, 64185, 64186, 64187, 64188, 64189, 64190,
64191, 64192, 64193, 64194, 64195, 64196, 64197, 64198, 64199, 64200, 64201, 64202, 64203, 64204, 64205, 64206, 64207,
64208, 64209, 64210, 64211, 64212, 64213, 64214, 64215, 64216, 64217, 64285, 64287, 64298, 64299, 64300, 64301, 64302,
64303, 64304, 64305, 64306, 64307, 64308, 64309, 64310, 64312, 64313, 64314, 64315, 64316, 64318, 64320, 64321, 64323,
64324, 64326, 64327, 64328, 64329, 64330, 64331, 64332, 64333, 64334, 119134, 119135, 119136, 119137, 119138, 119139,
119140, 119227, 119228, 119229, 119230, 119231, 119232, 194560, 194561, 194562, 194563, 194564, 194565, 194566,
194567, 194568, 194569, 194570, 194571, 194572, 194573, 194574, 194575, 194576, 194577, 194578, 194579, 194580,
194581, 194582, 194583, 194584, 194585, 194586, 194587, 194588, 194589, 194590, 194591, 194592, 194593, 194594,
194595, 194596, 194597, 194598, 194599, 194600, 194601, 194602, 194603, 194604, 194605, 194606, 194607, 194608,
194609, 194610, 194611, 194612, 194613, 194614, 194615, 194616, 194617, 194618, 194619, 194620, 194621, 194622,
194623, 194624, 194625, 194626, 194627, 194628, 194629, 194630, 194631, 194632, 194633, 194634, 194635, 194636,
194637, 194638, 194639, 194640, 194641, 194642, 194643, 194644, 194645, 194646, 194647, 194648, 194649, 194650,
194651, 194652, 194653, 194654, 194655, 194656, 194657, 194658, 194659, 194660, 194661, 194662, 194663, 194664,
194665, 194666, 194667, 194668, 194669, 194670, 194671, 194672, 194673, 194674, 194675, 194676, 194677, 194678,
194679, 194680, 194681, 194682, 194683, 194684, 194685, 194686, 194687, 194688, 194689, 194690, 194691, 194692,
194693, 194694, 194695, 194696, 194697, 194698, 194699, 194700, 194701, 194702, 194703, 194704, 194705, 194706,
194707, 194708, 194709, 194710, 194711, 194712, 194713, 194714, 194715, 194716, 194717, 194718, 194719, 194720,
194721, 194722, 194723, 194724, 194725, 194726, 194727, 194728, 194729, 194730, 194731, 194732, 194733, 194734,
194735, 194736, 194737, 194738, 194739, 194740, 194741, 194742, 194743, 194744, 194745, 194746, 194747, 194748,
194749, 194750, 194751, 194752, 194753, 194754, 194755, 194756, 194757, 194758, 194759, 194760, 194761, 194762,
194763, 194764, 194765, 194766, 194767, 194768, 194769, 194770, 194771, 194772, 194773, 194774, 194775, 194776,
194777, 194778, 194779, 194780, 194781, 194782, 194783, 194784, 194785, 194786, 194787, 194788, 194789, 194790,
194791, 194792, 194793, 194794, 194795, 194796, 194797, 194798, 194799, 194800, 194801, 194802, 194803, 194804,
194805, 194806, 194807, 194808, 194809, 194810, 194811, 194812, 194813, 194814, 194815, 194816, 194817, 194818,
194819, 194820, 194821, 194822, 194823, 194824, 194825, 194826, 194827, 194828, 194829, 194830, 194831, 194832,
194833, 194834, 194835, 194836, 194837, 194838, 194839, 194840, 194841, 194842, 194843, 194844, 194845, 194846,
194847, 194848, 194849, 194850, 194851, 194852, 194853, 194854, 194855, 194856, 194857, 194858, 194859, 194860,
194861, 194862, 194863, 194864, 194865, 194866, 194867, 194868, 194869, 194870, 194871, 194872, 194873, 194874,
194875, 194876, 194877, 194878, 194879, 194880, 194881, 194882, 194883, 194884, 194885, 194886, 194887, 194888,
194889, 194890, 194891, 194892, 194893, 194894, 194895, 194896, 194897, 194898, 194899, 194900, 194901, 194902,
194903, 194904, 194905, 194906, 194907, 194908, 194909, 194910, 194911, 194912, 194913, 194914, 194915, 194916,
194917, 194918, 194919, 194920, 194921, 194922, 194923, 194924, 194925, 194926, 194927, 194928, 194929, 194930,
194931, 194932, 194933, 194934, 194935, 194936, 194937, 194938, 194939, 194940, 194941, 194942, 194943, 194944,
194945, 194946, 194947, 194948, 194949, 194950, 194951, 194952, 194953, 194954, 194955, 194956, 194957, 194958,
194959, 194960, 194961, 194962, 194963, 194964, 194965, 194966, 194967, 194968, 194969, 194970, 194971, 194972,
194973, 194974, 194975, 194976, 194977, 194978, 194979, 194980, 194981, 194982, 194983, 194984, 194985, 194986,
194987, 194988, 194989, 194990, 194991, 194992, 194993, 194994, 194995, 194996, 194997, 194998, 194999, 195000,
195001, 195002, 195003, 195004, 195005, 195006, 195007, 195008, 195009, 195010, 195011, 195012, 195013, 195014,
195015, 195016, 195017, 195018, 195019, 195020, 195021, 195022, 195023, 195024, 195025, 195026, 195027, 195028,
195029, 195030, 195031, 195032, 195033, 195034, 195035, 195036, 195037, 195038, 195039, 195040, 195041, 195042,
195043, 195044, 195045, 195046, 195047, 195048, 195049, 195050, 195051, 195052, 195053, 195054, 195055, 195056,
195057, 195058, 195059, 195060, 195061, 195062, 195063, 195064, 195065, 195066, 195067, 195068, 195069, 195070,
195071, 195072, 195073, 195074, 195075, 195076, 195077, 195078, 195079, 195080, 195081, 195082, 195083, 195084,
195085, 195086, 195087, 195088, 195089, 195090, 195091, 195092, 195093, 195094, 195095, 195096, 195097, 195098,
195099, 195100, 195101
};
struct Decomposition {
const KInt array[4];
const KByte length;
};
constexpr Decomposition decompositionValues[] = {
{{65, 768}, 2}, {{65, 769}, 2}, {{65, 770}, 2}, {{65, 771}, 2}, {{65, 776}, 2}, {{65, 778}, 2}, {{67, 807}, 2},
{{69, 768}, 2}, {{69, 769}, 2}, {{69, 770}, 2}, {{69, 776}, 2}, {{73, 768}, 2}, {{73, 769}, 2}, {{73, 770}, 2},
{{73, 776}, 2}, {{78, 771}, 2}, {{79, 768}, 2}, {{79, 769}, 2}, {{79, 770}, 2}, {{79, 771}, 2}, {{79, 776}, 2},
{{85, 768}, 2}, {{85, 769}, 2}, {{85, 770}, 2}, {{85, 776}, 2}, {{89, 769}, 2}, {{97, 768}, 2}, {{97, 769}, 2},
{{97, 770}, 2}, {{97, 771}, 2}, {{97, 776}, 2}, {{97, 778}, 2}, {{99, 807}, 2}, {{101, 768}, 2}, {{101, 769}, 2},
{{101, 770}, 2}, {{101, 776}, 2}, {{105, 768}, 2}, {{105, 769}, 2}, {{105, 770}, 2}, {{105, 776}, 2}, {{110, 771}, 2},
{{111, 768}, 2}, {{111, 769}, 2}, {{111, 770}, 2}, {{111, 771}, 2}, {{111, 776}, 2}, {{117, 768}, 2}, {{117, 769}, 2},
{{117, 770}, 2}, {{117, 776}, 2}, {{121, 769}, 2}, {{121, 776}, 2}, {{65, 772}, 2}, {{97, 772}, 2}, {{65, 774}, 2},
{{97, 774}, 2}, {{65, 808}, 2}, {{97, 808}, 2}, {{67, 769}, 2}, {{99, 769}, 2}, {{67, 770}, 2}, {{99, 770}, 2},
{{67, 775}, 2}, {{99, 775}, 2}, {{67, 780}, 2}, {{99, 780}, 2}, {{68, 780}, 2}, {{100, 780}, 2}, {{69, 772}, 2},
{{101, 772}, 2}, {{69, 774}, 2}, {{101, 774}, 2}, {{69, 775}, 2}, {{101, 775}, 2}, {{69, 808}, 2}, {{101, 808}, 2},
{{69, 780}, 2}, {{101, 780}, 2}, {{71, 770}, 2}, {{103, 770}, 2}, {{71, 774}, 2}, {{103, 774}, 2}, {{71, 775}, 2},
{{103, 775}, 2}, {{71, 807}, 2}, {{103, 807}, 2}, {{72, 770}, 2}, {{104, 770}, 2}, {{73, 771}, 2}, {{105, 771}, 2},
{{73, 772}, 2}, {{105, 772}, 2}, {{73, 774}, 2}, {{105, 774}, 2}, {{73, 808}, 2}, {{105, 808}, 2}, {{73, 775}, 2},
{{74, 770}, 2}, {{106, 770}, 2}, {{75, 807}, 2}, {{107, 807}, 2}, {{76, 769}, 2}, {{108, 769}, 2}, {{76, 807}, 2},
{{108, 807}, 2}, {{76, 780}, 2}, {{108, 780}, 2}, {{78, 769}, 2}, {{110, 769}, 2}, {{78, 807}, 2}, {{110, 807}, 2},
{{78, 780}, 2}, {{110, 780}, 2}, {{79, 772}, 2}, {{111, 772}, 2}, {{79, 774}, 2}, {{111, 774}, 2}, {{79, 779}, 2},
{{111, 779}, 2}, {{82, 769}, 2}, {{114, 769}, 2}, {{82, 807}, 2}, {{114, 807}, 2}, {{82, 780}, 2}, {{114, 780}, 2},
{{83, 769}, 2}, {{115, 769}, 2}, {{83, 770}, 2}, {{115, 770}, 2}, {{83, 807}, 2}, {{115, 807}, 2}, {{83, 780}, 2},
{{115, 780}, 2}, {{84, 807}, 2}, {{116, 807}, 2}, {{84, 780}, 2}, {{116, 780}, 2}, {{85, 771}, 2}, {{117, 771}, 2},
{{85, 772}, 2}, {{117, 772}, 2}, {{85, 774}, 2}, {{117, 774}, 2}, {{85, 778}, 2}, {{117, 778}, 2}, {{85, 779}, 2},
{{117, 779}, 2}, {{85, 808}, 2}, {{117, 808}, 2}, {{87, 770}, 2}, {{119, 770}, 2}, {{89, 770}, 2}, {{121, 770}, 2},
{{89, 776}, 2}, {{90, 769}, 2}, {{122, 769}, 2}, {{90, 775}, 2}, {{122, 775}, 2}, {{90, 780}, 2}, {{122, 780}, 2},
{{79, 795}, 2}, {{111, 795}, 2}, {{85, 795}, 2}, {{117, 795}, 2}, {{65, 780}, 2}, {{97, 780}, 2}, {{73, 780}, 2},
{{105, 780}, 2}, {{79, 780}, 2}, {{111, 780}, 2}, {{85, 780}, 2}, {{117, 780}, 2}, {{85, 776, 772}, 3},
{{117, 776, 772}, 3}, {{85, 776, 769}, 3}, {{117, 776, 769}, 3}, {{85, 776, 780}, 3}, {{117, 776, 780}, 3},
{{85, 776, 768}, 3}, {{117, 776, 768}, 3}, {{65, 776, 772}, 3}, {{97, 776, 772}, 3}, {{65, 775, 772}, 3},
{{97, 775, 772}, 3}, {{198, 772}, 2}, {{230, 772}, 2}, {{71, 780}, 2}, {{103, 780}, 2}, {{75, 780}, 2},
{{107, 780}, 2}, {{79, 808}, 2}, {{111, 808}, 2}, {{79, 808, 772}, 3}, {{111, 808, 772}, 3}, {{439, 780}, 2},
{{658, 780}, 2}, {{106, 780}, 2}, {{71, 769}, 2}, {{103, 769}, 2}, {{78, 768}, 2}, {{110, 768}, 2},
{{65, 778, 769}, 3}, {{97, 778, 769}, 3}, {{198, 769}, 2}, {{230, 769}, 2}, {{216, 769}, 2}, {{248, 769}, 2},
{{65, 783}, 2}, {{97, 783}, 2}, {{65, 785}, 2}, {{97, 785}, 2}, {{69, 783}, 2}, {{101, 783}, 2}, {{69, 785}, 2},
{{101, 785}, 2}, {{73, 783}, 2}, {{105, 783}, 2}, {{73, 785}, 2}, {{105, 785}, 2}, {{79, 783}, 2}, {{111, 783}, 2},
{{79, 785}, 2}, {{111, 785}, 2}, {{82, 783}, 2}, {{114, 783}, 2}, {{82, 785}, 2}, {{114, 785}, 2}, {{85, 783}, 2},
{{117, 783}, 2}, {{85, 785}, 2}, {{117, 785}, 2}, {{83, 806}, 2}, {{115, 806}, 2}, {{84, 806}, 2}, {{116, 806}, 2},
{{72, 780}, 2}, {{104, 780}, 2}, {{65, 775}, 2}, {{97, 775}, 2}, {{69, 807}, 2}, {{101, 807}, 2}, {{79, 776, 772}, 3},
{{111, 776, 772}, 3}, {{79, 771, 772}, 3}, {{111, 771, 772}, 3}, {{79, 775}, 2}, {{111, 775}, 2}, {{79, 775, 772}, 3},
{{111, 775, 772}, 3}, {{89, 772}, 2}, {{121, 772}, 2}, {{768}, 1}, {{769}, 1}, {{787}, 1}, {{776, 769}, 2},
{{697}, 1}, {{59}, 1}, {{168, 769}, 2}, {{913, 769}, 2}, {{183}, 1}, {{917, 769}, 2}, {{919, 769}, 2},
{{921, 769}, 2}, {{927, 769}, 2}, {{933, 769}, 2}, {{937, 769}, 2}, {{953, 776, 769}, 3}, {{921, 776}, 2},
{{933, 776}, 2}, {{945, 769}, 2}, {{949, 769}, 2}, {{951, 769}, 2}, {{953, 769}, 2}, {{965, 776, 769}, 3},
{{953, 776}, 2}, {{965, 776}, 2}, {{959, 769}, 2}, {{965, 769}, 2}, {{969, 769}, 2}, {{978, 769}, 2}, {{978, 776}, 2},
{{1045, 768}, 2}, {{1045, 776}, 2}, {{1043, 769}, 2}, {{1030, 776}, 2}, {{1050, 769}, 2}, {{1048, 768}, 2},
{{1059, 774}, 2}, {{1048, 774}, 2}, {{1080, 774}, 2}, {{1077, 768}, 2}, {{1077, 776}, 2}, {{1075, 769}, 2},
{{1110, 776}, 2}, {{1082, 769}, 2}, {{1080, 768}, 2}, {{1091, 774}, 2}, {{1140, 783}, 2}, {{1141, 783}, 2},
{{1046, 774}, 2}, {{1078, 774}, 2}, {{1040, 774}, 2}, {{1072, 774}, 2}, {{1040, 776}, 2}, {{1072, 776}, 2},
{{1045, 774}, 2}, {{1077, 774}, 2}, {{1240, 776}, 2}, {{1241, 776}, 2}, {{1046, 776}, 2}, {{1078, 776}, 2},
{{1047, 776}, 2}, {{1079, 776}, 2}, {{1048, 772}, 2}, {{1080, 772}, 2}, {{1048, 776}, 2}, {{1080, 776}, 2},
{{1054, 776}, 2}, {{1086, 776}, 2}, {{1256, 776}, 2}, {{1257, 776}, 2}, {{1069, 776}, 2}, {{1101, 776}, 2},
{{1059, 772}, 2}, {{1091, 772}, 2}, {{1059, 776}, 2}, {{1091, 776}, 2}, {{1059, 779}, 2}, {{1091, 779}, 2},
{{1063, 776}, 2}, {{1095, 776}, 2}, {{1067, 776}, 2}, {{1099, 776}, 2}, {{1575, 1619}, 2}, {{1575, 1620}, 2},
{{1608, 1620}, 2}, {{1575, 1621}, 2}, {{1610, 1620}, 2}, {{1749, 1620}, 2}, {{1729, 1620}, 2}, {{1746, 1620}, 2},
{{2344, 2364}, 2}, {{2352, 2364}, 2}, {{2355, 2364}, 2}, {{2325, 2364}, 2}, {{2326, 2364}, 2}, {{2327, 2364}, 2},
{{2332, 2364}, 2}, {{2337, 2364}, 2}, {{2338, 2364}, 2}, {{2347, 2364}, 2}, {{2351, 2364}, 2}, {{2503, 2494}, 2},
{{2503, 2519}, 2}, {{2465, 2492}, 2}, {{2466, 2492}, 2}, {{2479, 2492}, 2}, {{2610, 2620}, 2}, {{2616, 2620}, 2},
{{2582, 2620}, 2}, {{2583, 2620}, 2}, {{2588, 2620}, 2}, {{2603, 2620}, 2}, {{2887, 2902}, 2}, {{2887, 2878}, 2},
{{2887, 2903}, 2}, {{2849, 2876}, 2}, {{2850, 2876}, 2}, {{2962, 3031}, 2}, {{3014, 3006}, 2}, {{3015, 3006}, 2},
{{3014, 3031}, 2}, {{3142, 3158}, 2}, {{3263, 3285}, 2}, {{3270, 3285}, 2}, {{3270, 3286}, 2}, {{3270, 3266}, 2},
{{3270, 3266, 3285}, 3}, {{3398, 3390}, 2}, {{3399, 3390}, 2}, {{3398, 3415}, 2}, {{3545, 3530}, 2},
{{3545, 3535}, 2}, {{3545, 3535, 3530}, 3}, {{3545, 3551}, 2}, {{3906, 4023}, 2}, {{3916, 4023}, 2},
{{3921, 4023}, 2}, {{3926, 4023}, 2}, {{3931, 4023}, 2}, {{3904, 4021}, 2}, {{3953, 3954}, 2}, {{3953, 3956}, 2},
{{4018, 3968}, 2}, {{4019, 3968}, 2}, {{3953, 3968}, 2}, {{3986, 4023}, 2}, {{3996, 4023}, 2}, {{4001, 4023}, 2},
{{4006, 4023}, 2}, {{4011, 4023}, 2}, {{3984, 4021}, 2}, {{4133, 4142}, 2}, {{65, 805}, 2}, {{97, 805}, 2},
{{66, 775}, 2}, {{98, 775}, 2}, {{66, 803}, 2}, {{98, 803}, 2}, {{66, 817}, 2}, {{98, 817}, 2}, {{67, 807, 769}, 3},
{{99, 807, 769}, 3}, {{68, 775}, 2}, {{100, 775}, 2}, {{68, 803}, 2}, {{100, 803}, 2}, {{68, 817}, 2},
{{100, 817}, 2}, {{68, 807}, 2}, {{100, 807}, 2}, {{68, 813}, 2}, {{100, 813}, 2}, {{69, 772, 768}, 3},
{{101, 772, 768}, 3}, {{69, 772, 769}, 3}, {{101, 772, 769}, 3}, {{69, 813}, 2}, {{101, 813}, 2}, {{69, 816}, 2},
{{101, 816}, 2}, {{69, 807, 774}, 3}, {{101, 807, 774}, 3}, {{70, 775}, 2}, {{102, 775}, 2}, {{71, 772}, 2},
{{103, 772}, 2}, {{72, 775}, 2}, {{104, 775}, 2}, {{72, 803}, 2}, {{104, 803}, 2}, {{72, 776}, 2}, {{104, 776}, 2},
{{72, 807}, 2}, {{104, 807}, 2}, {{72, 814}, 2}, {{104, 814}, 2}, {{73, 816}, 2}, {{105, 816}, 2},
{{73, 776, 769}, 3}, {{105, 776, 769}, 3}, {{75, 769}, 2}, {{107, 769}, 2}, {{75, 803}, 2}, {{107, 803}, 2},
{{75, 817}, 2}, {{107, 817}, 2}, {{76, 803}, 2}, {{108, 803}, 2}, {{76, 803, 772}, 3}, {{108, 803, 772}, 3},
{{76, 817}, 2}, {{108, 817}, 2}, {{76, 813}, 2}, {{108, 813}, 2}, {{77, 769}, 2}, {{109, 769}, 2}, {{77, 775}, 2},
{{109, 775}, 2}, {{77, 803}, 2}, {{109, 803}, 2}, {{78, 775}, 2}, {{110, 775}, 2}, {{78, 803}, 2}, {{110, 803}, 2},
{{78, 817}, 2}, {{110, 817}, 2}, {{78, 813}, 2}, {{110, 813}, 2}, {{79, 771, 769}, 3}, {{111, 771, 769}, 3},
{{79, 771, 776}, 3}, {{111, 771, 776}, 3}, {{79, 772, 768}, 3}, {{111, 772, 768}, 3}, {{79, 772, 769}, 3},
{{111, 772, 769}, 3}, {{80, 769}, 2}, {{112, 769}, 2}, {{80, 775}, 2}, {{112, 775}, 2}, {{82, 775}, 2},
{{114, 775}, 2}, {{82, 803}, 2}, {{114, 803}, 2}, {{82, 803, 772}, 3}, {{114, 803, 772}, 3}, {{82, 817}, 2},
{{114, 817}, 2}, {{83, 775}, 2}, {{115, 775}, 2}, {{83, 803}, 2}, {{115, 803}, 2}, {{83, 769, 775}, 3},
{{115, 769, 775}, 3}, {{83, 780, 775}, 3}, {{115, 780, 775}, 3}, {{83, 803, 775}, 3}, {{115, 803, 775}, 3},
{{84, 775}, 2}, {{116, 775}, 2}, {{84, 803}, 2}, {{116, 803}, 2}, {{84, 817}, 2}, {{116, 817}, 2}, {{84, 813}, 2},
{{116, 813}, 2}, {{85, 804}, 2}, {{117, 804}, 2}, {{85, 816}, 2}, {{117, 816}, 2}, {{85, 813}, 2}, {{117, 813}, 2},
{{85, 771, 769}, 3}, {{117, 771, 769}, 3}, {{85, 772, 776}, 3}, {{117, 772, 776}, 3}, {{86, 771}, 2}, {{118, 771}, 2},
{{86, 803}, 2}, {{118, 803}, 2}, {{87, 768}, 2}, {{119, 768}, 2}, {{87, 769}, 2}, {{119, 769}, 2}, {{87, 776}, 2},
{{119, 776}, 2}, {{87, 775}, 2}, {{119, 775}, 2}, {{87, 803}, 2}, {{119, 803}, 2}, {{88, 775}, 2}, {{120, 775}, 2},
{{88, 776}, 2}, {{120, 776}, 2}, {{89, 775}, 2}, {{121, 775}, 2}, {{90, 770}, 2}, {{122, 770}, 2}, {{90, 803}, 2},
{{122, 803}, 2}, {{90, 817}, 2}, {{122, 817}, 2}, {{104, 817}, 2}, {{116, 776}, 2}, {{119, 778}, 2}, {{121, 778}, 2},
{{383, 775}, 2}, {{65, 803}, 2}, {{97, 803}, 2}, {{65, 777}, 2}, {{97, 777}, 2}, {{65, 770, 769}, 3},
{{97, 770, 769}, 3}, {{65, 770, 768}, 3}, {{97, 770, 768}, 3}, {{65, 770, 777}, 3}, {{97, 770, 777}, 3},
{{65, 770, 771}, 3}, {{97, 770, 771}, 3}, {{65, 803, 770}, 3}, {{97, 803, 770}, 3}, {{65, 774, 769}, 3},
{{97, 774, 769}, 3}, {{65, 774, 768}, 3}, {{97, 774, 768}, 3}, {{65, 774, 777}, 3}, {{97, 774, 777}, 3},
{{65, 774, 771}, 3}, {{97, 774, 771}, 3}, {{65, 803, 774}, 3}, {{97, 803, 774}, 3}, {{69, 803}, 2}, {{101, 803}, 2},
{{69, 777}, 2}, {{101, 777}, 2}, {{69, 771}, 2}, {{101, 771}, 2}, {{69, 770, 769}, 3}, {{101, 770, 769}, 3},
{{69, 770, 768}, 3}, {{101, 770, 768}, 3}, {{69, 770, 777}, 3}, {{101, 770, 777}, 3}, {{69, 770, 771}, 3},
{{101, 770, 771}, 3}, {{69, 803, 770}, 3}, {{101, 803, 770}, 3}, {{73, 777}, 2}, {{105, 777}, 2}, {{73, 803}, 2},
{{105, 803}, 2}, {{79, 803}, 2}, {{111, 803}, 2}, {{79, 777}, 2}, {{111, 777}, 2}, {{79, 770, 769}, 3},
{{111, 770, 769}, 3}, {{79, 770, 768}, 3}, {{111, 770, 768}, 3}, {{79, 770, 777}, 3}, {{111, 770, 777}, 3},
{{79, 770, 771}, 3}, {{111, 770, 771}, 3}, {{79, 803, 770}, 3}, {{111, 803, 770}, 3}, {{79, 795, 769}, 3},
{{111, 795, 769}, 3}, {{79, 795, 768}, 3}, {{111, 795, 768}, 3}, {{79, 795, 777}, 3}, {{111, 795, 777}, 3},
{{79, 795, 771}, 3}, {{111, 795, 771}, 3}, {{79, 795, 803}, 3}, {{111, 795, 803}, 3}, {{85, 803}, 2}, {{117, 803}, 2},
{{85, 777}, 2}, {{117, 777}, 2}, {{85, 795, 769}, 3}, {{117, 795, 769}, 3}, {{85, 795, 768}, 3}, {{117, 795, 768}, 3},
{{85, 795, 777}, 3}, {{117, 795, 777}, 3}, {{85, 795, 771}, 3}, {{117, 795, 771}, 3}, {{85, 795, 803}, 3},
{{117, 795, 803}, 3}, {{89, 768}, 2}, {{121, 768}, 2}, {{89, 803}, 2}, {{121, 803}, 2}, {{89, 777}, 2},
{{121, 777}, 2}, {{89, 771}, 2}, {{121, 771}, 2}, {{945, 787}, 2}, {{945, 788}, 2}, {{945, 787, 768}, 3},
{{945, 788, 768}, 3}, {{945, 787, 769}, 3}, {{945, 788, 769}, 3}, {{945, 787, 834}, 3}, {{945, 788, 834}, 3},
{{913, 787}, 2}, {{913, 788}, 2}, {{913, 787, 768}, 3}, {{913, 788, 768}, 3}, {{913, 787, 769}, 3},
{{913, 788, 769}, 3}, {{913, 787, 834}, 3}, {{913, 788, 834}, 3}, {{949, 787}, 2}, {{949, 788}, 2},
{{949, 787, 768}, 3}, {{949, 788, 768}, 3}, {{949, 787, 769}, 3}, {{949, 788, 769}, 3}, {{917, 787}, 2},
{{917, 788}, 2}, {{917, 787, 768}, 3}, {{917, 788, 768}, 3}, {{917, 787, 769}, 3}, {{917, 788, 769}, 3},
{{951, 787}, 2}, {{951, 788}, 2}, {{951, 787, 768}, 3}, {{951, 788, 768}, 3}, {{951, 787, 769}, 3},
{{951, 788, 769}, 3}, {{951, 787, 834}, 3}, {{951, 788, 834}, 3}, {{919, 787}, 2}, {{919, 788}, 2},
{{919, 787, 768}, 3}, {{919, 788, 768}, 3}, {{919, 787, 769}, 3}, {{919, 788, 769}, 3}, {{919, 787, 834}, 3},
{{919, 788, 834}, 3}, {{953, 787}, 2}, {{953, 788}, 2}, {{953, 787, 768}, 3}, {{953, 788, 768}, 3},
{{953, 787, 769}, 3}, {{953, 788, 769}, 3}, {{953, 787, 834}, 3}, {{953, 788, 834}, 3}, {{921, 787}, 2},
{{921, 788}, 2}, {{921, 787, 768}, 3}, {{921, 788, 768}, 3}, {{921, 787, 769}, 3}, {{921, 788, 769}, 3},
{{921, 787, 834}, 3}, {{921, 788, 834}, 3}, {{959, 787}, 2}, {{959, 788}, 2}, {{959, 787, 768}, 3},
{{959, 788, 768}, 3}, {{959, 787, 769}, 3}, {{959, 788, 769}, 3}, {{927, 787}, 2}, {{927, 788}, 2},
{{927, 787, 768}, 3}, {{927, 788, 768}, 3}, {{927, 787, 769}, 3}, {{927, 788, 769}, 3}, {{965, 787}, 2},
{{965, 788}, 2}, {{965, 787, 768}, 3}, {{965, 788, 768}, 3}, {{965, 787, 769}, 3}, {{965, 788, 769}, 3},
{{965, 787, 834}, 3}, {{965, 788, 834}, 3}, {{933, 788}, 2}, {{933, 788, 768}, 3}, {{933, 788, 769}, 3},
{{933, 788, 834}, 3}, {{969, 787}, 2}, {{969, 788}, 2}, {{969, 787, 768}, 3}, {{969, 788, 768}, 3},
{{969, 787, 769}, 3}, {{969, 788, 769}, 3}, {{969, 787, 834}, 3}, {{969, 788, 834}, 3}, {{937, 787}, 2},
{{937, 788}, 2}, {{937, 787, 768}, 3}, {{937, 788, 768}, 3}, {{937, 787, 769}, 3}, {{937, 788, 769}, 3},
{{937, 787, 834}, 3}, {{937, 788, 834}, 3}, {{945, 768}, 2}, {{945, 769}, 2}, {{949, 768}, 2}, {{949, 769}, 2},
{{951, 768}, 2}, {{951, 769}, 2}, {{953, 768}, 2}, {{953, 769}, 2}, {{959, 768}, 2}, {{959, 769}, 2}, {{965, 768}, 2},
{{965, 769}, 2}, {{969, 768}, 2}, {{969, 769}, 2}, {{945, 787, 837}, 3}, {{945, 788, 837}, 3},
{{945, 787, 768, 837}, 4}, {{945, 788, 768, 837}, 4}, {{945, 787, 769, 837}, 4}, {{945, 788, 769, 837}, 4},
{{945, 787, 834, 837}, 4}, {{945, 788, 834, 837}, 4}, {{913, 787, 837}, 3}, {{913, 788, 837}, 3},
{{913, 787, 768, 837}, 4}, {{913, 788, 768, 837}, 4}, {{913, 787, 769, 837}, 4}, {{913, 788, 769, 837}, 4},
{{913, 787, 834, 837}, 4}, {{913, 788, 834, 837}, 4}, {{951, 787, 837}, 3}, {{951, 788, 837}, 3},
{{951, 787, 768, 837}, 4}, {{951, 788, 768, 837}, 4}, {{951, 787, 769, 837}, 4}, {{951, 788, 769, 837}, 4},
{{951, 787, 834, 837}, 4}, {{951, 788, 834, 837}, 4}, {{919, 787, 837}, 3}, {{919, 788, 837}, 3},
{{919, 787, 768, 837}, 4}, {{919, 788, 768, 837}, 4}, {{919, 787, 769, 837}, 4}, {{919, 788, 769, 837}, 4},
{{919, 787, 834, 837}, 4}, {{919, 788, 834, 837}, 4}, {{969, 787, 837}, 3}, {{969, 788, 837}, 3},
{{969, 787, 768, 837}, 4}, {{969, 788, 768, 837}, 4}, {{969, 787, 769, 837}, 4}, {{969, 788, 769, 837}, 4},
{{969, 787, 834, 837}, 4}, {{969, 788, 834, 837}, 4}, {{937, 787, 837}, 3}, {{937, 788, 837}, 3},
{{937, 787, 768, 837}, 4}, {{937, 788, 768, 837}, 4}, {{937, 787, 769, 837}, 4}, {{937, 788, 769, 837}, 4},
{{937, 787, 834, 837}, 4}, {{937, 788, 834, 837}, 4}, {{945, 774}, 2}, {{945, 772}, 2}, {{945, 768, 837}, 3},
{{945, 837}, 2}, {{945, 769, 837}, 3}, {{945, 834}, 2}, {{945, 834, 837}, 3}, {{913, 774}, 2}, {{913, 772}, 2},
{{913, 768}, 2}, {{913, 769}, 2}, {{913, 837}, 2}, {{953}, 1}, {{168, 834}, 2}, {{951, 768, 837}, 3}, {{951, 837}, 2},
{{951, 769, 837}, 3}, {{951, 834}, 2}, {{951, 834, 837}, 3}, {{917, 768}, 2}, {{917, 769}, 2}, {{919, 768}, 2},
{{919, 769}, 2}, {{919, 837}, 2}, {{8127, 768}, 2}, {{8127, 769}, 2}, {{8127, 834}, 2}, {{953, 774}, 2},
{{953, 772}, 2}, {{953, 776, 768}, 3}, {{953, 776, 769}, 3}, {{953, 834}, 2}, {{953, 776, 834}, 3}, {{921, 774}, 2},
{{921, 772}, 2}, {{921, 768}, 2}, {{921, 769}, 2}, {{8190, 768}, 2}, {{8190, 769}, 2}, {{8190, 834}, 2},
{{965, 774}, 2}, {{965, 772}, 2}, {{965, 776, 768}, 3}, {{965, 776, 769}, 3}, {{961, 787}, 2}, {{961, 788}, 2},
{{965, 834}, 2}, {{965, 776, 834}, 3}, {{933, 774}, 2}, {{933, 772}, 2}, {{933, 768}, 2}, {{933, 769}, 2},
{{929, 788}, 2}, {{168, 768}, 2}, {{168, 769}, 2}, {{96}, 1}, {{969, 768, 837}, 3}, {{969, 837}, 2},
{{969, 769, 837}, 3}, {{969, 834}, 2}, {{969, 834, 837}, 3}, {{927, 768}, 2}, {{927, 769}, 2}, {{937, 768}, 2},
{{937, 769}, 2}, {{937, 837}, 2}, {{180}, 1}, {{8194}, 1}, {{8195}, 1}, {{937}, 1}, {{75}, 1}, {{65, 778}, 2},
{{8592, 824}, 2}, {{8594, 824}, 2}, {{8596, 824}, 2}, {{8656, 824}, 2}, {{8660, 824}, 2}, {{8658, 824}, 2},
{{8707, 824}, 2}, {{8712, 824}, 2}, {{8715, 824}, 2}, {{8739, 824}, 2}, {{8741, 824}, 2}, {{8764, 824}, 2},
{{8771, 824}, 2}, {{8773, 824}, 2}, {{8776, 824}, 2}, {{61, 824}, 2}, {{8801, 824}, 2}, {{8781, 824}, 2},
{{60, 824}, 2}, {{62, 824}, 2}, {{8804, 824}, 2}, {{8805, 824}, 2}, {{8818, 824}, 2}, {{8819, 824}, 2},
{{8822, 824}, 2}, {{8823, 824}, 2}, {{8826, 824}, 2}, {{8827, 824}, 2}, {{8834, 824}, 2}, {{8835, 824}, 2},
{{8838, 824}, 2}, {{8839, 824}, 2}, {{8866, 824}, 2}, {{8872, 824}, 2}, {{8873, 824}, 2}, {{8875, 824}, 2},
{{8828, 824}, 2}, {{8829, 824}, 2}, {{8849, 824}, 2}, {{8850, 824}, 2}, {{8882, 824}, 2}, {{8883, 824}, 2},
{{8884, 824}, 2}, {{8885, 824}, 2}, {{12296}, 1}, {{12297}, 1}, {{10973, 824}, 2}, {{12363, 12441}, 2},
{{12365, 12441}, 2}, {{12367, 12441}, 2}, {{12369, 12441}, 2}, {{12371, 12441}, 2}, {{12373, 12441}, 2},
{{12375, 12441}, 2}, {{12377, 12441}, 2}, {{12379, 12441}, 2}, {{12381, 12441}, 2}, {{12383, 12441}, 2},
{{12385, 12441}, 2}, {{12388, 12441}, 2}, {{12390, 12441}, 2}, {{12392, 12441}, 2}, {{12399, 12441}, 2},
{{12399, 12442}, 2}, {{12402, 12441}, 2}, {{12402, 12442}, 2}, {{12405, 12441}, 2}, {{12405, 12442}, 2},
{{12408, 12441}, 2}, {{12408, 12442}, 2}, {{12411, 12441}, 2}, {{12411, 12442}, 2}, {{12358, 12441}, 2},
{{12445, 12441}, 2}, {{12459, 12441}, 2}, {{12461, 12441}, 2}, {{12463, 12441}, 2}, {{12465, 12441}, 2},
{{12467, 12441}, 2}, {{12469, 12441}, 2}, {{12471, 12441}, 2}, {{12473, 12441}, 2}, {{12475, 12441}, 2},
{{12477, 12441}, 2}, {{12479, 12441}, 2}, {{12481, 12441}, 2}, {{12484, 12441}, 2}, {{12486, 12441}, 2},
{{12488, 12441}, 2}, {{12495, 12441}, 2}, {{12495, 12442}, 2}, {{12498, 12441}, 2}, {{12498, 12442}, 2},
{{12501, 12441}, 2}, {{12501, 12442}, 2}, {{12504, 12441}, 2}, {{12504, 12442}, 2}, {{12507, 12441}, 2},
{{12507, 12442}, 2}, {{12454, 12441}, 2}, {{12527, 12441}, 2}, {{12528, 12441}, 2}, {{12529, 12441}, 2},
{{12530, 12441}, 2}, {{12541, 12441}, 2}, {{35912}, 1}, {{26356}, 1}, {{36554}, 1}, {{36040}, 1}, {{28369}, 1},
{{20018}, 1}, {{21477}, 1}, {{40860}, 1}, {{40860}, 1}, {{22865}, 1}, {{37329}, 1}, {{21895}, 1}, {{22856}, 1},
{{25078}, 1}, {{30313}, 1}, {{32645}, 1}, {{34367}, 1}, {{34746}, 1}, {{35064}, 1}, {{37007}, 1}, {{27138}, 1},
{{27931}, 1}, {{28889}, 1}, {{29662}, 1}, {{33853}, 1}, {{37226}, 1}, {{39409}, 1}, {{20098}, 1}, {{21365}, 1},
{{27396}, 1}, {{29211}, 1}, {{34349}, 1}, {{40478}, 1}, {{23888}, 1}, {{28651}, 1}, {{34253}, 1}, {{35172}, 1},
{{25289}, 1}, {{33240}, 1}, {{34847}, 1}, {{24266}, 1}, {{26391}, 1}, {{28010}, 1}, {{29436}, 1}, {{37070}, 1},
{{20358}, 1}, {{20919}, 1}, {{21214}, 1}, {{25796}, 1}, {{27347}, 1}, {{29200}, 1}, {{30439}, 1}, {{32769}, 1},
{{34310}, 1}, {{34396}, 1}, {{36335}, 1}, {{38706}, 1}, {{39791}, 1}, {{40442}, 1}, {{30860}, 1}, {{31103}, 1},
{{32160}, 1}, {{33737}, 1}, {{37636}, 1}, {{40575}, 1}, {{35542}, 1}, {{22751}, 1}, {{24324}, 1}, {{31840}, 1},
{{32894}, 1}, {{29282}, 1}, {{30922}, 1}, {{36034}, 1}, {{38647}, 1}, {{22744}, 1}, {{23650}, 1}, {{27155}, 1},
{{28122}, 1}, {{28431}, 1}, {{32047}, 1}, {{32311}, 1}, {{38475}, 1}, {{21202}, 1}, {{32907}, 1}, {{20956}, 1},
{{20940}, 1}, {{31260}, 1}, {{32190}, 1}, {{33777}, 1}, {{38517}, 1}, {{35712}, 1}, {{25295}, 1}, {{27138}, 1},
{{35582}, 1}, {{20025}, 1}, {{23527}, 1}, {{24594}, 1}, {{29575}, 1}, {{30064}, 1}, {{21271}, 1}, {{30971}, 1},
{{20415}, 1}, {{24489}, 1}, {{19981}, 1}, {{27852}, 1}, {{25976}, 1}, {{32034}, 1}, {{21443}, 1}, {{22622}, 1},
{{30465}, 1}, {{33865}, 1}, {{35498}, 1}, {{27578}, 1}, {{36784}, 1}, {{27784}, 1}, {{25342}, 1}, {{33509}, 1},
{{25504}, 1}, {{30053}, 1}, {{20142}, 1}, {{20841}, 1}, {{20937}, 1}, {{26753}, 1}, {{31975}, 1}, {{33391}, 1},
{{35538}, 1}, {{37327}, 1}, {{21237}, 1}, {{21570}, 1}, {{22899}, 1}, {{24300}, 1}, {{26053}, 1}, {{28670}, 1},
{{31018}, 1}, {{38317}, 1}, {{39530}, 1}, {{40599}, 1}, {{40654}, 1}, {{21147}, 1}, {{26310}, 1}, {{27511}, 1},
{{36706}, 1}, {{24180}, 1}, {{24976}, 1}, {{25088}, 1}, {{25754}, 1}, {{28451}, 1}, {{29001}, 1}, {{29833}, 1},
{{31178}, 1}, {{32244}, 1}, {{32879}, 1}, {{36646}, 1}, {{34030}, 1}, {{36899}, 1}, {{37706}, 1}, {{21015}, 1},
{{21155}, 1}, {{21693}, 1}, {{28872}, 1}, {{35010}, 1}, {{35498}, 1}, {{24265}, 1}, {{24565}, 1}, {{25467}, 1},
{{27566}, 1}, {{31806}, 1}, {{29557}, 1}, {{20196}, 1}, {{22265}, 1}, {{23527}, 1}, {{23994}, 1}, {{24604}, 1},
{{29618}, 1}, {{29801}, 1}, {{32666}, 1}, {{32838}, 1}, {{37428}, 1}, {{38646}, 1}, {{38728}, 1}, {{38936}, 1},
{{20363}, 1}, {{31150}, 1}, {{37300}, 1}, {{38584}, 1}, {{24801}, 1}, {{20102}, 1}, {{20698}, 1}, {{23534}, 1},
{{23615}, 1}, {{26009}, 1}, {{27138}, 1}, {{29134}, 1}, {{30274}, 1}, {{34044}, 1}, {{36988}, 1}, {{40845}, 1},
{{26248}, 1}, {{38446}, 1}, {{21129}, 1}, {{26491}, 1}, {{26611}, 1}, {{27969}, 1}, {{28316}, 1}, {{29705}, 1},
{{30041}, 1}, {{30827}, 1}, {{32016}, 1}, {{39006}, 1}, {{20845}, 1}, {{25134}, 1}, {{38520}, 1}, {{20523}, 1},
{{23833}, 1}, {{28138}, 1}, {{36650}, 1}, {{24459}, 1}, {{24900}, 1}, {{26647}, 1}, {{29575}, 1}, {{38534}, 1},
{{21033}, 1}, {{21519}, 1}, {{23653}, 1}, {{26131}, 1}, {{26446}, 1}, {{26792}, 1}, {{27877}, 1}, {{29702}, 1},
{{30178}, 1}, {{32633}, 1}, {{35023}, 1}, {{35041}, 1}, {{37324}, 1}, {{38626}, 1}, {{21311}, 1}, {{28346}, 1},
{{21533}, 1}, {{29136}, 1}, {{29848}, 1}, {{34298}, 1}, {{38563}, 1}, {{40023}, 1}, {{40607}, 1}, {{26519}, 1},
{{28107}, 1}, {{33256}, 1}, {{31435}, 1}, {{31520}, 1}, {{31890}, 1}, {{29376}, 1}, {{28825}, 1}, {{35672}, 1},
{{20160}, 1}, {{33590}, 1}, {{21050}, 1}, {{20999}, 1}, {{24230}, 1}, {{25299}, 1}, {{31958}, 1}, {{23429}, 1},
{{27934}, 1}, {{26292}, 1}, {{36667}, 1}, {{34892}, 1}, {{38477}, 1}, {{35211}, 1}, {{24275}, 1}, {{20800}, 1},
{{21952}, 1}, {{22618}, 1}, {{26228}, 1}, {{20958}, 1}, {{29482}, 1}, {{30410}, 1}, {{31036}, 1}, {{31070}, 1},
{{31077}, 1}, {{31119}, 1}, {{38742}, 1}, {{31934}, 1}, {{32701}, 1}, {{34322}, 1}, {{35576}, 1}, {{36920}, 1},
{{37117}, 1}, {{39151}, 1}, {{39164}, 1}, {{39208}, 1}, {{40372}, 1}, {{20398}, 1}, {{20711}, 1}, {{20813}, 1},
{{21193}, 1}, {{21220}, 1}, {{21329}, 1}, {{21917}, 1}, {{22022}, 1}, {{22120}, 1}, {{22592}, 1}, {{22696}, 1},
{{23652}, 1}, {{23662}, 1}, {{24724}, 1}, {{24936}, 1}, {{24974}, 1}, {{25074}, 1}, {{25935}, 1}, {{26082}, 1},
{{26257}, 1}, {{26757}, 1}, {{28023}, 1}, {{28186}, 1}, {{28450}, 1}, {{29038}, 1}, {{29227}, 1}, {{29730}, 1},
{{30865}, 1}, {{31038}, 1}, {{31049}, 1}, {{31048}, 1}, {{31056}, 1}, {{31062}, 1}, {{31069}, 1}, {{31117}, 1},
{{31118}, 1}, {{31296}, 1}, {{31361}, 1}, {{31680}, 1}, {{32244}, 1}, {{32265}, 1}, {{32321}, 1}, {{32626}, 1},
{{32773}, 1}, {{33261}, 1}, {{33401}, 1}, {{33401}, 1}, {{33879}, 1}, {{35088}, 1}, {{35222}, 1}, {{35585}, 1},
{{35641}, 1}, {{36051}, 1}, {{36104}, 1}, {{36790}, 1}, {{36920}, 1}, {{38627}, 1}, {{38911}, 1}, {{38971}, 1},
{{20006}, 1}, {{20917}, 1}, {{20840}, 1}, {{20352}, 1}, {{20805}, 1}, {{20864}, 1}, {{21191}, 1}, {{21242}, 1},
{{21917}, 1}, {{21845}, 1}, {{21913}, 1}, {{21986}, 1}, {{22618}, 1}, {{22707}, 1}, {{22852}, 1}, {{22868}, 1},
{{23138}, 1}, {{23336}, 1}, {{24274}, 1}, {{24281}, 1}, {{24425}, 1}, {{24493}, 1}, {{24792}, 1}, {{24910}, 1},
{{24840}, 1}, {{24974}, 1}, {{24928}, 1}, {{25074}, 1}, {{25140}, 1}, {{25540}, 1}, {{25628}, 1}, {{25682}, 1},
{{25942}, 1}, {{26228}, 1}, {{26391}, 1}, {{26395}, 1}, {{26454}, 1}, {{27513}, 1}, {{27578}, 1}, {{27969}, 1},
{{28379}, 1}, {{28363}, 1}, {{28450}, 1}, {{28702}, 1}, {{29038}, 1}, {{30631}, 1}, {{29237}, 1}, {{29359}, 1},
{{29482}, 1}, {{29809}, 1}, {{29958}, 1}, {{30011}, 1}, {{30237}, 1}, {{30239}, 1}, {{30410}, 1}, {{30427}, 1},
{{30452}, 1}, {{30538}, 1}, {{30528}, 1}, {{30924}, 1}, {{31409}, 1}, {{31680}, 1}, {{31867}, 1}, {{32091}, 1},
{{32244}, 1}, {{32574}, 1}, {{32773}, 1}, {{33618}, 1}, {{33775}, 1}, {{34681}, 1}, {{35137}, 1}, {{35206}, 1},
{{35222}, 1}, {{35519}, 1}, {{35576}, 1}, {{35531}, 1}, {{35585}, 1}, {{35582}, 1}, {{35565}, 1}, {{35641}, 1},
{{35722}, 1}, {{36104}, 1}, {{36664}, 1}, {{36978}, 1}, {{37273}, 1}, {{37494}, 1}, {{38524}, 1}, {{38627}, 1},
{{38742}, 1}, {{38875}, 1}, {{38911}, 1}, {{38923}, 1}, {{38971}, 1}, {{39698}, 1}, {{40860}, 1}, {{141386}, 1},
{{141380}, 1}, {{144341}, 1}, {{15261}, 1}, {{16408}, 1}, {{16441}, 1}, {{152137}, 1}, {{154832}, 1}, {{163539}, 1},
{{40771}, 1}, {{40846}, 1}, {{1497, 1460}, 2}, {{1522, 1463}, 2}, {{1513, 1473}, 2}, {{1513, 1474}, 2},
{{1513, 1468, 1473}, 3}, {{1513, 1468, 1474}, 3}, {{1488, 1463}, 2}, {{1488, 1464}, 2}, {{1488, 1468}, 2},
{{1489, 1468}, 2}, {{1490, 1468}, 2}, {{1491, 1468}, 2}, {{1492, 1468}, 2}, {{1493, 1468}, 2}, {{1494, 1468}, 2},
{{1496, 1468}, 2}, {{1497, 1468}, 2}, {{1498, 1468}, 2}, {{1499, 1468}, 2}, {{1500, 1468}, 2}, {{1502, 1468}, 2},
{{1504, 1468}, 2}, {{1505, 1468}, 2}, {{1507, 1468}, 2}, {{1508, 1468}, 2}, {{1510, 1468}, 2}, {{1511, 1468}, 2},
{{1512, 1468}, 2}, {{1513, 1468}, 2}, {{1514, 1468}, 2}, {{1493, 1465}, 2}, {{1489, 1471}, 2}, {{1499, 1471}, 2},
{{1508, 1471}, 2}, {{119127, 119141}, 2}, {{119128, 119141}, 2}, {{119128, 119141, 119150}, 3},
{{119128, 119141, 119151}, 3}, {{119128, 119141, 119152}, 3}, {{119128, 119141, 119153}, 3},
{{119128, 119141, 119154}, 3}, {{119225, 119141}, 2}, {{119226, 119141}, 2}, {{119225, 119141, 119150}, 3},
{{119226, 119141, 119150}, 3}, {{119225, 119141, 119151}, 3}, {{119226, 119141, 119151}, 3}, {{20029}, 1},
{{20024}, 1}, {{20033}, 1}, {{131362}, 1}, {{20320}, 1}, {{20398}, 1}, {{20411}, 1}, {{20482}, 1}, {{20602}, 1},
{{20633}, 1}, {{20711}, 1}, {{20687}, 1}, {{13470}, 1}, {{132666}, 1}, {{20813}, 1}, {{20820}, 1}, {{20836}, 1},
{{20855}, 1}, {{132380}, 1}, {{13497}, 1}, {{20839}, 1}, {{20877}, 1}, {{132427}, 1}, {{20887}, 1}, {{20900}, 1},
{{20172}, 1}, {{20908}, 1}, {{20917}, 1}, {{168415}, 1}, {{20981}, 1}, {{20995}, 1}, {{13535}, 1}, {{21051}, 1},
{{21062}, 1}, {{21106}, 1}, {{21111}, 1}, {{13589}, 1}, {{21191}, 1}, {{21193}, 1}, {{21220}, 1}, {{21242}, 1},
{{21253}, 1}, {{21254}, 1}, {{21271}, 1}, {{21321}, 1}, {{21329}, 1}, {{21338}, 1}, {{21363}, 1}, {{21373}, 1},
{{21375}, 1}, {{21375}, 1}, {{21375}, 1}, {{133676}, 1}, {{28784}, 1}, {{21450}, 1}, {{21471}, 1}, {{133987}, 1},
{{21483}, 1}, {{21489}, 1}, {{21510}, 1}, {{21662}, 1}, {{21560}, 1}, {{21576}, 1}, {{21608}, 1}, {{21666}, 1},
{{21750}, 1}, {{21776}, 1}, {{21843}, 1}, {{21859}, 1}, {{21892}, 1}, {{21892}, 1}, {{21913}, 1}, {{21931}, 1},
{{21939}, 1}, {{21954}, 1}, {{22294}, 1}, {{22022}, 1}, {{22295}, 1}, {{22097}, 1}, {{22132}, 1}, {{20999}, 1},
{{22766}, 1}, {{22478}, 1}, {{22516}, 1}, {{22541}, 1}, {{22411}, 1}, {{22578}, 1}, {{22577}, 1}, {{22700}, 1},
{{136420}, 1}, {{22770}, 1}, {{22775}, 1}, {{22790}, 1}, {{22810}, 1}, {{22818}, 1}, {{22882}, 1}, {{136872}, 1},
{{136938}, 1}, {{23020}, 1}, {{23067}, 1}, {{23079}, 1}, {{23000}, 1}, {{23142}, 1}, {{14062}, 1}, {{14076}, 1},
{{23304}, 1}, {{23358}, 1}, {{23358}, 1}, {{137672}, 1}, {{23491}, 1}, {{23512}, 1}, {{23527}, 1}, {{23539}, 1},
{{138008}, 1}, {{23551}, 1}, {{23558}, 1}, {{24403}, 1}, {{23586}, 1}, {{14209}, 1}, {{23648}, 1}, {{23662}, 1},
{{23744}, 1}, {{23693}, 1}, {{138724}, 1}, {{23875}, 1}, {{138726}, 1}, {{23918}, 1}, {{23915}, 1}, {{23932}, 1},
{{24033}, 1}, {{24034}, 1}, {{14383}, 1}, {{24061}, 1}, {{24104}, 1}, {{24125}, 1}, {{24169}, 1}, {{14434}, 1},
{{139651}, 1}, {{14460}, 1}, {{24240}, 1}, {{24243}, 1}, {{24246}, 1}, {{24266}, 1}, {{172946}, 1}, {{24318}, 1},
{{140081}, 1}, {{140081}, 1}, {{33281}, 1}, {{24354}, 1}, {{24354}, 1}, {{14535}, 1}, {{144056}, 1}, {{156122}, 1},
{{24418}, 1}, {{24427}, 1}, {{14563}, 1}, {{24474}, 1}, {{24525}, 1}, {{24535}, 1}, {{24569}, 1}, {{24705}, 1},
{{14650}, 1}, {{14620}, 1}, {{24724}, 1}, {{141012}, 1}, {{24775}, 1}, {{24904}, 1}, {{24908}, 1}, {{24910}, 1},
{{24908}, 1}, {{24954}, 1}, {{24974}, 1}, {{25010}, 1}, {{24996}, 1}, {{25007}, 1}, {{25054}, 1}, {{25074}, 1},
{{25078}, 1}, {{25104}, 1}, {{25115}, 1}, {{25181}, 1}, {{25265}, 1}, {{25300}, 1}, {{25424}, 1}, {{142092}, 1},
{{25405}, 1}, {{25340}, 1}, {{25448}, 1}, {{25475}, 1}, {{25572}, 1}, {{142321}, 1}, {{25634}, 1}, {{25541}, 1},
{{25513}, 1}, {{14894}, 1}, {{25705}, 1}, {{25726}, 1}, {{25757}, 1}, {{25719}, 1}, {{14956}, 1}, {{25935}, 1},
{{25964}, 1}, {{143370}, 1}, {{26083}, 1}, {{26360}, 1}, {{26185}, 1}, {{15129}, 1}, {{26257}, 1}, {{15112}, 1},
{{15076}, 1}, {{20882}, 1}, {{20885}, 1}, {{26368}, 1}, {{26268}, 1}, {{32941}, 1}, {{17369}, 1}, {{26391}, 1},
{{26395}, 1}, {{26401}, 1}, {{26462}, 1}, {{26451}, 1}, {{144323}, 1}, {{15177}, 1}, {{26618}, 1}, {{26501}, 1},
{{26706}, 1}, {{26757}, 1}, {{144493}, 1}, {{26766}, 1}, {{26655}, 1}, {{26900}, 1}, {{15261}, 1}, {{26946}, 1},
{{27043}, 1}, {{27114}, 1}, {{27304}, 1}, {{145059}, 1}, {{27355}, 1}, {{15384}, 1}, {{27425}, 1}, {{145575}, 1},
{{27476}, 1}, {{15438}, 1}, {{27506}, 1}, {{27551}, 1}, {{27578}, 1}, {{27579}, 1}, {{146061}, 1}, {{138507}, 1},
{{146170}, 1}, {{27726}, 1}, {{146620}, 1}, {{27839}, 1}, {{27853}, 1}, {{27751}, 1}, {{27926}, 1}, {{27966}, 1},
{{28023}, 1}, {{27969}, 1}, {{28009}, 1}, {{28024}, 1}, {{28037}, 1}, {{146718}, 1}, {{27956}, 1}, {{28207}, 1},
{{28270}, 1}, {{15667}, 1}, {{28363}, 1}, {{28359}, 1}, {{147153}, 1}, {{28153}, 1}, {{28526}, 1}, {{147294}, 1},
{{147342}, 1}, {{28614}, 1}, {{28729}, 1}, {{28702}, 1}, {{28699}, 1}, {{15766}, 1}, {{28746}, 1}, {{28797}, 1},
{{28791}, 1}, {{28845}, 1}, {{132389}, 1}, {{28997}, 1}, {{148067}, 1}, {{29084}, 1}, {{148395}, 1}, {{29224}, 1},
{{29237}, 1}, {{29264}, 1}, {{149000}, 1}, {{29312}, 1}, {{29333}, 1}, {{149301}, 1}, {{149524}, 1}, {{29562}, 1},
{{29579}, 1}, {{16044}, 1}, {{29605}, 1}, {{16056}, 1}, {{16056}, 1}, {{29767}, 1}, {{29788}, 1}, {{29809}, 1},
{{29829}, 1}, {{29898}, 1}, {{16155}, 1}, {{29988}, 1}, {{150582}, 1}, {{30014}, 1}, {{150674}, 1}, {{30064}, 1},
{{139679}, 1}, {{30224}, 1}, {{151457}, 1}, {{151480}, 1}, {{151620}, 1}, {{16380}, 1}, {{16392}, 1}, {{30452}, 1},
{{151795}, 1}, {{151794}, 1}, {{151833}, 1}, {{151859}, 1}, {{30494}, 1}, {{30495}, 1}, {{30495}, 1}, {{30538}, 1},
{{16441}, 1}, {{30603}, 1}, {{16454}, 1}, {{16534}, 1}, {{152605}, 1}, {{30798}, 1}, {{30860}, 1}, {{30924}, 1},
{{16611}, 1}, {{153126}, 1}, {{31062}, 1}, {{153242}, 1}, {{153285}, 1}, {{31119}, 1}, {{31211}, 1}, {{16687}, 1},
{{31296}, 1}, {{31306}, 1}, {{31311}, 1}, {{153980}, 1}, {{154279}, 1}, {{154279}, 1}, {{31470}, 1}, {{16898}, 1},
{{154539}, 1}, {{31686}, 1}, {{31689}, 1}, {{16935}, 1}, {{154752}, 1}, {{31954}, 1}, {{17056}, 1}, {{31976}, 1},
{{31971}, 1}, {{32000}, 1}, {{155526}, 1}, {{32099}, 1}, {{17153}, 1}, {{32199}, 1}, {{32258}, 1}, {{32325}, 1},
{{17204}, 1}, {{156200}, 1}, {{156231}, 1}, {{17241}, 1}, {{156377}, 1}, {{32634}, 1}, {{156478}, 1}, {{32661}, 1},
{{32762}, 1}, {{32773}, 1}, {{156890}, 1}, {{156963}, 1}, {{32864}, 1}, {{157096}, 1}, {{32880}, 1}, {{144223}, 1},
{{17365}, 1}, {{32946}, 1}, {{33027}, 1}, {{17419}, 1}, {{33086}, 1}, {{23221}, 1}, {{157607}, 1}, {{157621}, 1},
{{144275}, 1}, {{144284}, 1}, {{33281}, 1}, {{33284}, 1}, {{36766}, 1}, {{17515}, 1}, {{33425}, 1}, {{33419}, 1},
{{33437}, 1}, {{21171}, 1}, {{33457}, 1}, {{33459}, 1}, {{33469}, 1}, {{33510}, 1}, {{158524}, 1}, {{33509}, 1},
{{33565}, 1}, {{33635}, 1}, {{33709}, 1}, {{33571}, 1}, {{33725}, 1}, {{33767}, 1}, {{33879}, 1}, {{33619}, 1},
{{33738}, 1}, {{33740}, 1}, {{33756}, 1}, {{158774}, 1}, {{159083}, 1}, {{158933}, 1}, {{17707}, 1}, {{34033}, 1},
{{34035}, 1}, {{34070}, 1}, {{160714}, 1}, {{34148}, 1}, {{159532}, 1}, {{17757}, 1}, {{17761}, 1}, {{159665}, 1},
{{159954}, 1}, {{17771}, 1}, {{34384}, 1}, {{34396}, 1}, {{34407}, 1}, {{34409}, 1}, {{34473}, 1}, {{34440}, 1},
{{34574}, 1}, {{34530}, 1}, {{34681}, 1}, {{34600}, 1}, {{34667}, 1}, {{34694}, 1}, {{17879}, 1}, {{34785}, 1},
{{34817}, 1}, {{17913}, 1}, {{34912}, 1}, {{34915}, 1}, {{161383}, 1}, {{35031}, 1}, {{35038}, 1}, {{17973}, 1},
{{35066}, 1}, {{13499}, 1}, {{161966}, 1}, {{162150}, 1}, {{18110}, 1}, {{18119}, 1}, {{35488}, 1}, {{35565}, 1},
{{35722}, 1}, {{35925}, 1}, {{162984}, 1}, {{36011}, 1}, {{36033}, 1}, {{36123}, 1}, {{36215}, 1}, {{163631}, 1},
{{133124}, 1}, {{36299}, 1}, {{36284}, 1}, {{36336}, 1}, {{133342}, 1}, {{36564}, 1}, {{36664}, 1}, {{165330}, 1},
{{165357}, 1}, {{37012}, 1}, {{37105}, 1}, {{37137}, 1}, {{165678}, 1}, {{37147}, 1}, {{37432}, 1}, {{37591}, 1},
{{37592}, 1}, {{37500}, 1}, {{37881}, 1}, {{37909}, 1}, {{166906}, 1}, {{38283}, 1}, {{18837}, 1}, {{38327}, 1},
{{167287}, 1}, {{18918}, 1}, {{38595}, 1}, {{23986}, 1}, {{38691}, 1}, {{168261}, 1}, {{168474}, 1}, {{19054}, 1},
{{19062}, 1}, {{38880}, 1}, {{168970}, 1}, {{19122}, 1}, {{169110}, 1}, {{38923}, 1}, {{38923}, 1}, {{38953}, 1},
{{169398}, 1}, {{39138}, 1}, {{19251}, 1}, {{39209}, 1}, {{39335}, 1}, {{39362}, 1}, {{39422}, 1}, {{19406}, 1},
{{170800}, 1}, {{39698}, 1}, {{40000}, 1}, {{40189}, 1}, {{19662}, 1}, {{19693}, 1}, {{40295}, 1}, {{172238}, 1},
{{19704}, 1}, {{172293}, 1}, {{172558}, 1}, {{172689}, 1}, {{40635}, 1}, {{19798}, 1}, {{40697}, 1}, {{40702}, 1},
{{40709}, 1}, {{40719}, 1}, {{40726}, 1}, {{40763}, 1}, {{173568}, 1}
};
KInt getCanonicalClass(KInt ch) {
int index = binarySearchRange(canonicalClassesKeys, ARRAY_SIZE(canonicalClassesKeys), ch);
if (canonicalClassesKeys[index] != ch) {
return 0;
}
return canonicalClassesValues[index];
}
const Decomposition* getDecomposition(KInt codePoint) {
int index = binarySearchRange(decompositionKeys, ARRAY_SIZE(decompositionKeys), codePoint);
if (decompositionKeys[index] != codePoint) {
return nullptr;
}
return &decompositionValues[index];
}
} // namespace
extern "C" {
KInt Kotlin_text_regex_getCanonicalClassInternal(KInt ch) {
return getCanonicalClass(ch);
}
KBoolean Kotlin_text_regex_hasSingleCodepointDecompositionInternal(KInt ch) {
int index = binarySearchRange(singleDecompositions, ARRAY_SIZE(singleDecompositions), ch);
return singleDecompositions[index] == ch;
}
OBJ_GETTER(Kotlin_text_regex_getDecompositionInternal, KInt ch) {
const Decomposition* decomposition = getDecomposition(ch);
if (decomposition == nullptr) {
return nullptr;
}
ArrayHeader* result = AllocArrayInstance(theIntArrayTypeInfo, decomposition->length, OBJ_RESULT)->array();
KInt* resultRaw = IntArrayAddressOfElementAt(result, 0);
for (int i = 0; i < decomposition->length; i++) {
*resultRaw++ = decomposition->array[i];
}
RETURN_OBJ(result->obj());
}
KInt Kotlin_text_regex_decomposeString(ArrayHeader* inputCodePoints, KInt inputLength, ArrayHeader* outputCodePoints) {
RuntimeAssert(inputCodePoints->type_info() == theIntArrayTypeInfo, "Must use an Int array");
RuntimeAssert(outputCodePoints->type_info() == theIntArrayTypeInfo, "Must use an Int array");
RuntimeAssert(inputLength >= 0, "Input length must be >= 0");
if (inputLength == 0) {
return 0;
}
int outputLength = 0;
const KInt* inputArray = IntArrayAddressOfElementAt(inputCodePoints, 0);
KInt* outputArray = IntArrayAddressOfElementAt(outputCodePoints, 0);
for (int i = 0; i < inputLength; i++) {
const Decomposition* decomposition = getDecomposition(inputArray[i]);
if (decomposition == nullptr) {
outputArray[outputLength++] = inputArray[i];
} else {
memcpy(outputArray + outputLength, decomposition->array, decomposition->length * sizeof(KInt));
outputLength+=decomposition->length;
}
}
return outputLength;
}
KInt Kotlin_text_regex_decomposeCodePoint(KInt codePoint, ArrayHeader* outputCodePoints, KInt fromIndex) {
RuntimeAssert(outputCodePoints->type_info() == theIntArrayTypeInfo, "Must be an Int array");
RuntimeAssert(fromIndex >= 0 && static_cast<uint32_t>(fromIndex) < outputCodePoints->count_, "Start index must be >= 0 and < array size");
KInt* rawResult = IntArrayAddressOfElementAt(outputCodePoints, fromIndex);
const Decomposition* decomposition = getDecomposition(codePoint);
if (decomposition == nullptr) {
*rawResult = codePoint;
return 1;
} else {
memcpy(rawResult, decomposition->array, decomposition->length * sizeof(KInt));
return decomposition->length;
}
}
} // extern "C"
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ReturnSlot.h"
#ifdef KONAN_WASM
namespace {
THREAD_LOCAL_VARIABLE long long storage;
} // namespace
extern "C" {
RUNTIME_USED
KDouble ReturnSlot_getDouble() {
return *reinterpret_cast<KDouble*>(&::storage);
}
RUNTIME_USED
void ReturnSlot_setDouble(KInt upper, KInt lower) {
reinterpret_cast<KInt*>(&::storage)[0] = lower;
reinterpret_cast<KInt*>(&::storage)[1] = upper;
}
} // extern "C"
#endif // KONAN_WASM
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Types.h"
#ifdef KONAN_WASM
#ifdef __cplusplus
extern "C" {
#endif
KDouble ReturnSlot_getDouble();
void ReturnSlot_setDouble(KInt upper, KInt lower);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // KONAN_WASM
@@ -0,0 +1,256 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Alloc.h"
#include "Atomic.h"
#include "Cleaner.h"
#include "Exceptions.h"
#include "KAssert.h"
#include "Memory.h"
#include "ObjCExportInit.h"
#include "Porting.h"
#include "Runtime.h"
#include "Worker.h"
typedef void (*Initializer)(int initialize, MemoryState* memory);
struct InitNode {
Initializer init;
InitNode* next;
};
namespace {
InitNode* initHeadNode = nullptr;
InitNode* initTailNode = nullptr;
enum class RuntimeStatus {
kUninitialized,
kRunning,
kDestroying,
};
struct RuntimeState {
MemoryState* memoryState;
Worker* worker;
RuntimeStatus status = RuntimeStatus::kUninitialized;
};
enum {
INIT_GLOBALS = 0,
INIT_THREAD_LOCAL_GLOBALS = 1,
DEINIT_THREAD_LOCAL_GLOBALS = 2,
DEINIT_GLOBALS = 3
};
void InitOrDeinitGlobalVariables(int initialize, MemoryState* memory) {
InitNode* currentNode = initHeadNode;
while (currentNode != nullptr) {
currentNode->init(initialize, memory);
currentNode = currentNode->next;
}
}
KBoolean g_checkLeaks = KonanNeedDebugInfo;
KBoolean g_checkLeakedCleaners = KonanNeedDebugInfo;
constexpr RuntimeState* kInvalidRuntime = nullptr;
THREAD_LOCAL_VARIABLE RuntimeState* runtimeState = kInvalidRuntime;
THREAD_LOCAL_VARIABLE int isMainThread = 0;
inline bool isValidRuntime() {
return ::runtimeState != kInvalidRuntime;
}
volatile int aliveRuntimesCount = 0;
RuntimeState* initRuntime() {
SetKonanTerminateHandler();
RuntimeState* result = konanConstructInstance<RuntimeState>();
if (!result) return kInvalidRuntime;
RuntimeCheck(!isValidRuntime(), "No active runtimes allowed");
::runtimeState = result;
result->memoryState = InitMemory();
result->worker = WorkerInit(true);
bool firstRuntime = atomicAdd(&aliveRuntimesCount, 1) == 1;
// Keep global variables in state as well.
if (firstRuntime) {
isMainThread = 1;
konan::consoleInit();
#if KONAN_OBJC_INTEROP
Kotlin_ObjCExport_initialize();
#endif
InitOrDeinitGlobalVariables(INIT_GLOBALS, result->memoryState);
}
InitOrDeinitGlobalVariables(INIT_THREAD_LOCAL_GLOBALS, result->memoryState);
RuntimeAssert(result->status == RuntimeStatus::kUninitialized, "Runtime must still be in the uninitialized state");
result->status = RuntimeStatus::kRunning;
return result;
}
void deinitRuntime(RuntimeState* state) {
RuntimeAssert(state->status == RuntimeStatus::kRunning, "Runtime must be in the running state");
state->status = RuntimeStatus::kDestroying;
// This may be called after TLS is zeroed out, so ::memoryState in Memory cannot be trusted.
RestoreMemory(state->memoryState);
bool lastRuntime = atomicAdd(&aliveRuntimesCount, -1) == 0;
InitOrDeinitGlobalVariables(DEINIT_THREAD_LOCAL_GLOBALS, state->memoryState);
if (lastRuntime)
InitOrDeinitGlobalVariables(DEINIT_GLOBALS, state->memoryState);
auto workerId = GetWorkerId(state->worker);
WorkerDeinit(state->worker);
DeinitMemory(state->memoryState);
konanDestructInstance(state);
WorkerDestroyThreadDataIfNeeded(workerId);
}
void Kotlin_deinitRuntimeCallback(void* argument) {
auto* state = reinterpret_cast<RuntimeState*>(argument);
deinitRuntime(state);
}
} // namespace
extern "C" {
void AppendToInitializersTail(InitNode *next) {
// TODO: use RuntimeState.
if (initHeadNode == nullptr) {
initHeadNode = next;
} else {
initTailNode->next = next;
}
initTailNode = next;
}
void Kotlin_initRuntimeIfNeeded() {
if (!isValidRuntime()) {
initRuntime();
// Register runtime deinit function at thread cleanup.
konan::onThreadExit(Kotlin_deinitRuntimeCallback, runtimeState);
}
}
void Kotlin_deinitRuntimeIfNeeded() {
if (isValidRuntime()) {
deinitRuntime(::runtimeState);
::runtimeState = kInvalidRuntime;
}
}
void CheckIsMainThread() {
if (!isMainThread)
ThrowIncorrectDereferenceException();
}
KInt Konan_Platform_canAccessUnaligned() {
#if KONAN_NO_UNALIGNED_ACCESS
return 0;
#else
return 1;
#endif
}
KInt Konan_Platform_isLittleEndian() {
#ifdef __BIG_ENDIAN__
return 0;
#else
return 1;
#endif
}
KInt Konan_Platform_getOsFamily() {
#if KONAN_MACOSX
return 1;
#elif KONAN_IOS
return 2;
#elif KONAN_LINUX
return 3;
#elif KONAN_WINDOWS
return 4;
#elif KONAN_ANDROID
return 5;
#elif KONAN_WASM
return 6;
#elif KONAN_TVOS
return 7;
#elif KONAN_WATCHOS
return 8;
#else
#warning "Unknown platform"
return 0;
#endif
}
KInt Konan_Platform_getCpuArchitecture() {
#if KONAN_ARM32
return 1;
#elif KONAN_ARM64
return 2;
#elif KONAN_X86
return 3;
#elif KONAN_X64
return 4;
#elif KONAN_MIPS32
return 5;
#elif KONAN_MIPSEL32
return 6;
#elif KONAN_WASM
return 7;
#else
#warning "Unknown CPU"
return 0;
#endif
}
KInt Konan_Platform_getMemoryModel() {
return IsStrictMemoryModel ? 0 : 1;
}
KBoolean Konan_Platform_isDebugBinary() {
return KonanNeedDebugInfo ? true : false;
}
void Kotlin_zeroOutTLSGlobals() {
if (runtimeState != nullptr && runtimeState->memoryState != nullptr)
InitOrDeinitGlobalVariables(DEINIT_THREAD_LOCAL_GLOBALS, runtimeState->memoryState);
}
bool Kotlin_memoryLeakCheckerEnabled() {
return g_checkLeaks;
}
KBoolean Konan_Platform_getMemoryLeakChecker() {
return g_checkLeaks;
}
void Konan_Platform_setMemoryLeakChecker(KBoolean value) {
g_checkLeaks = value;
}
bool Kotlin_cleanersLeakCheckerEnabled() {
return g_checkLeakedCleaners;
}
KBoolean Konan_Platform_getCleanersLeakChecker() {
return g_checkLeakedCleaners;
}
void Konan_Platform_setCleanersLeakChecker(KBoolean value) {
g_checkLeakedCleaners = value;
}
} // extern "C"
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_RUNTIME_H
#define RUNTIME_RUNTIME_H
#include "Porting.h"
struct InitNode;
#ifdef __cplusplus
extern "C" {
#endif
void Kotlin_initRuntimeIfNeeded();
void Kotlin_deinitRuntimeIfNeeded();
// Appends given node to an initializer list.
void AppendToInitializersTail(struct InitNode*);
// Zero out all Kotlin thread local globals.
void Kotlin_zeroOutTLSGlobals();
bool Kotlin_memoryLeakCheckerEnabled();
bool Kotlin_cleanersLeakCheckerEnabled();
#ifdef __cplusplus
}
#endif
#endif // RUNTIME_RUNTIME_H
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_SOURCEINFO_H
#define RUNTIME_SOURCEINFO_H
struct SourceInfo {
const char* fileName;
int lineNumber;
int column;
};
#ifdef __cplusplus
extern "C" {
#endif
struct SourceInfo Kotlin_getSourceInfo(void* addr);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // RUNTIME_SOURCEINFO_H
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KAssert.h"
#include "Porting.h"
#include "Common.h"
#if KONAN_LINUX || KONAN_WINDOWS
// This function replaces `__cxa_demangle` defined in GNU libstdc++
// by adding `--defsym` flag in `konan.properties`.
// This allows to avoid linking `__cxa_demangle` and its dependencies, thus reducing binary size.
RUNTIME_USED RUNTIME_WEAK extern "C" char* Konan_cxa_demangle(
const char* __mangled_name, char* __output_buffer,
size_t* __length, int* __status
) {
*__status = -2; // __mangled_name is not a valid name under the C++ ABI mangling rules.
return nullptr;
}
namespace std {
void __throw_length_error(const char* __s __attribute__((unused))) {
RuntimeAssert(false, __s);
}
} // namespace std
#endif // KONAN_LINUX || KONAN_WINDOWS
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include <memory>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "Types.h"
template <class F>
class ScopedStrictMockFunction {
public:
using Mock = testing::StrictMock<testing::MockFunction<F>>;
explicit ScopedStrictMockFunction(Mock** globalMockLocation) : globalMockLocation_(globalMockLocation) {
RuntimeCheck(globalMockLocation != nullptr, "ScopedStrictMockFunction needs non-null global mock location");
RuntimeCheck(*globalMockLocation == nullptr, "ScopedStrictMockFunction needs null global mock");
// TODO: Use make_unique when sysroots on Linux get updated.
mock_ = std::unique_ptr<Mock>(new Mock());
*globalMockLocation_ = mock_.get();
}
ScopedStrictMockFunction(const ScopedStrictMockFunction&) = delete;
ScopedStrictMockFunction& operator=(const ScopedStrictMockFunction&) = delete;
ScopedStrictMockFunction(ScopedStrictMockFunction&& rhs) : globalMockLocation_(rhs.globalMockLocation_), mock_(std::move(rhs.mock_)) {
rhs.globalMockLocation_ = nullptr;
}
ScopedStrictMockFunction& operator=(ScopedStrictMockFunction&& rhs) {
ScopedStrictMockFunction tmp(std::move(rhs));
swap(tmp);
return *this;
}
~ScopedStrictMockFunction() {
if (!globalMockLocation_) return;
RuntimeCheck(*globalMockLocation_ == mock_.get(), "unexpected global mock location");
testing::Mock::VerifyAndClear(mock_.get());
mock_.reset();
*globalMockLocation_ = nullptr;
}
void swap(ScopedStrictMockFunction& other) {
std::swap(globalMockLocation_, other.globalMockLocation_);
std::swap(mock_, other.mock_);
}
Mock& get() { return *mock_; }
Mock& operator*() { return *mock_; }
private:
// Can be null if moved-out of.
Mock** globalMockLocation_;
std::unique_ptr<Mock> mock_;
};
ScopedStrictMockFunction<KInt()> ScopedCreateCleanerWorkerMock();
ScopedStrictMockFunction<void(KInt, bool)> ScopedShutdownCleanerWorkerMock();
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Natives.h"
#include "Porting.h"
#include "Types.h"
extern "C" {
KLong Kotlin_system_getTimeMillis() {
return konan::getTimeMillis();
}
KLong Kotlin_system_getTimeNanos() {
return konan::getTimeNanos();
}
KLong Kotlin_system_getTimeMicros() {
return konan::getTimeMicros();
}
} // extern "C"
@@ -0,0 +1,122 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "KAssert.h"
#include "Exceptions.h"
#include "Memory.h"
#include "Natives.h"
#include "KString.h"
#include "Porting.h"
#include "Types.h"
namespace {
char int_to_digit(uint32_t value) {
if (value < 10) {
return '0' + value;
} else {
return 'a' + (value - 10);
}
}
// Radix is checked on the Kotlin side.
template <typename T> OBJ_GETTER(Kotlin_toStringRadix, T value, KInt radix) {
if (value == 0) {
RETURN_RESULT_OF(CreateStringFromCString, "0");
}
// In the worst case, we convert to binary, with sign.
char cstring[sizeof(T) * CHAR_BIT + 2];
bool negative = (value < 0);
if (!negative) {
value = -value;
}
int32_t length = 0;
while (value < 0) {
cstring[length++] = int_to_digit(-(value % radix));
value /= radix;
}
if (negative) {
cstring[length++] = '-';
}
for (int i = 0, j = length - 1; i < j; i++, j--) {
char tmp = cstring[i];
cstring[i] = cstring[j];
cstring[j] = tmp;
}
cstring[length] = '\0';
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
} // namespace
extern "C" {
OBJ_GETTER(Kotlin_Byte_toString, KByte value) {
char cstring[8];
konan::snprintf(cstring, sizeof(cstring), "%d", value);
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
OBJ_GETTER(Kotlin_Char_toString, KChar value) {
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, 1, OBJ_RESULT)->array();
*CharArrayAddressOfElementAt(result, 0) = value;
RETURN_OBJ(result->obj());
}
OBJ_GETTER(Kotlin_Short_toString, KShort value) {
char cstring[8];
konan::snprintf(cstring, sizeof(cstring), "%d", value);
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
OBJ_GETTER(Kotlin_Int_toString, KInt value) {
char cstring[16];
konan::snprintf(cstring, sizeof(cstring), "%d", value);
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
OBJ_GETTER(Kotlin_Int_toStringRadix, KInt value, KInt radix) {
RETURN_RESULT_OF(Kotlin_toStringRadix<KInt>, value, radix)
}
OBJ_GETTER(Kotlin_Long_toString, KLong value) {
char cstring[32];
konan::snprintf(cstring, sizeof(cstring), "%lld", static_cast<long long>(value));
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
OBJ_GETTER(Kotlin_Long_toStringRadix, KLong value, KInt radix) {
RETURN_RESULT_OF(Kotlin_toStringRadix<KLong>, value, radix)
}
OBJ_GETTER(Kotlin_DurationValue_formatToExactDecimals, KDouble value, KInt decimals) {
char cstring[32];
konan::snprintf(cstring, sizeof(cstring), "%.*f", decimals, value);
RETURN_RESULT_OF(CreateStringFromCString, cstring)
}
OBJ_GETTER(Kotlin_DurationValue_formatScientificImpl, KDouble value) {
char cstring[16];
konan::snprintf(cstring, sizeof(cstring), "%.2e", value);
RETURN_RESULT_OF(CreateStringFromCString, cstring)
}
} // extern "C"
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KAssert.h"
#include "TypeInfo.h"
// If one shall use binary search when looking up methods and fields.
// TODO: maybe select strategy basing on number of elements.
#define USE_BINARY_SEARCH 1
extern "C" {
#if USE_BINARY_SEARCH
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
int bottom = 0;
int top = info->openMethodsCount_ - 1;
while (bottom <= top) {
int middle = (bottom + top) / 2;
if (info->openMethods_[middle].nameSignature_ < nameSignature)
bottom = middle + 1;
else if (info->openMethods_[middle].nameSignature_ == nameSignature)
return info->openMethods_[middle].methodEntryPoint_;
else
top = middle - 1;
}
RuntimeAssert(false, "Unknown open method");
return nullptr;
}
#else
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
for (int i = 0; i < info->openMethodsCount_; ++i) {
if (info->openMethods_[i].nameSignature_ == nameSignature) {
return info->openMethods_[i].methodEntryPoint_;
}
}
RuntimeAssert(false, "Unknown open method");
return nullptr;
}
#endif
// Seeks for the specified id. In case of failure returns a valid pointer to some record, never returns nullptr.
// It is the caller's responsibility to check if the search has succeeded or not.
InterfaceTableRecord const* LookupInterfaceTableRecord(InterfaceTableRecord const* interfaceTable,
int interfaceTableSize, ClassId interfaceId) {
if (interfaceTableSize <= 8) {
// Linear search.
int i;
for (i = 0; i < interfaceTableSize - 1 && interfaceTable[i].id < interfaceId; ++i);
return interfaceTable + i;
}
int l = 0, r = interfaceTableSize - 1;
while (l < r) {
int m = (l + r) / 2;
if (interfaceTable[m].id < interfaceId)
l = m + 1;
else r = m;
}
return interfaceTable + l;
}
}
@@ -0,0 +1,181 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_TYPEINFO_H
#define RUNTIME_TYPEINFO_H
#include <cstdint>
#include "Common.h"
#include "Names.h"
#if KONAN_TYPE_INFO_HAS_WRITABLE_PART
struct WritableTypeInfo;
#endif
struct ObjHeader;
struct AssociatedObjectTableRecord;
// An element of sorted by hash in-place array representing methods.
// For systems where introspection is not needed - only open methods are in
// this table.
struct MethodTableRecord {
MethodNameHash nameSignature_;
void* methodEntryPoint_;
};
// Type for runtime representation of Konan object.
// Keep in sync with runtimeTypeMap in RTTIGenerator.
enum Konan_RuntimeType {
RT_INVALID = 0,
RT_OBJECT = 1,
RT_INT8 = 2,
RT_INT16 = 3,
RT_INT32 = 4,
RT_INT64 = 5,
RT_FLOAT32 = 6,
RT_FLOAT64 = 7,
RT_NATIVE_PTR = 8,
RT_BOOLEAN = 9,
RT_VECTOR128 = 10
};
// Flags per type.
// Keep in sync with constants in RTTIGenerator.
enum Konan_TypeFlags {
TF_IMMUTABLE = 1 << 0,
TF_ACYCLIC = 1 << 1,
TF_INTERFACE = 1 << 2,
TF_OBJC_DYNAMIC = 1 << 3,
TF_LEAK_DETECTOR_CANDIDATE = 1 << 4,
TF_SUSPEND_FUNCTION = 1 << 5,
TF_HAS_FINALIZER = 1 << 6,
};
// Flags per object instance.
enum Konan_MetaFlags {
// If freeze attempt happens on such an object - throw an exception.
MF_NEVER_FROZEN = 1 << 0,
};
// Extended information about a type.
struct ExtendedTypeInfo {
// Number of fields (negated Konan_RuntimeType for array types).
int32_t fieldsCount_;
// Offsets of all fields.
const int32_t* fieldOffsets_;
// Types of all fields.
const uint8_t* fieldTypes_;
// Names of all fields.
const char** fieldNames_;
// Number of supported debug operations.
int32_t debugOperationsCount_;
// Table of supported debug operations functions.
void** debugOperations_;
};
typedef void const* VTableElement;
typedef int32_t ClassId;
const ClassId kInvalidInterfaceId = 0;
struct InterfaceTableRecord {
ClassId id;
uint32_t vtableSize;
VTableElement const* vtable;
};
// This struct represents runtime type information and by itself is the compile time
// constant.
struct TypeInfo {
// Reference to self, to allow simple obtaining TypeInfo via meta-object.
const TypeInfo* typeInfo_;
// Extended RTTI, to retain cross-version debuggability, since ABI version 5 shall always be at the second position.
const ExtendedTypeInfo* extendedInfo_;
// Unused field.
uint32_t unused_;
// Negative value marks array class/string, and it is negated element size.
int32_t instanceSize_;
// Must be pointer to Any for array classes, and null for Any.
const TypeInfo* superType_;
// All object reference fields inside this object.
const int32_t* objOffsets_;
// Count of object reference fields inside this object.
// 1 for kotlin.Array to mark it as non-leaf.
int32_t objOffsetsCount_;
const TypeInfo* const* implementedInterfaces_;
int32_t implementedInterfacesCount_;
// Null for abstract classes and interfaces.
const MethodTableRecord* openMethods_;
uint32_t openMethodsCount_;
int32_t interfaceTableSize_;
InterfaceTableRecord const* interfaceTable_;
// String for the fully qualified dot-separated name of the package containing class,
// or `null` if the class is local or anonymous.
ObjHeader* packageName_;
// String for the qualified class name relative to the containing package
// (e.g. TopLevel.Nested1.Nested2), or simple class name if it is local,
// or `null` if the class is anonymous.
ObjHeader* relativeName_;
// Various flags.
int32_t flags_;
// Class id built with the whole class hierarchy taken into account. The details are in ClassLayoutBuilder.
ClassId classId_;
#if KONAN_TYPE_INFO_HAS_WRITABLE_PART
WritableTypeInfo* writableInfo_;
#endif
// Null-terminated array.
const AssociatedObjectTableRecord* associatedObjects;
// vtable starts just after declared contents of the TypeInfo:
// void* const vtable_[];
#ifdef __cplusplus
inline VTableElement const* vtable() const {
return reinterpret_cast<VTableElement const*>(this + 1);
}
inline VTableElement* vtable() {
return reinterpret_cast<VTableElement*>(this + 1);
}
#endif
};
#ifdef __cplusplus
extern "C" {
#endif
// Find open method by its hash. Other methods are resolved in compile-time.
// Note, that we use attribute const, which assumes function doesn't
// dereference global memory, while this function does. However, it seems
// to be safe, as actual result of this computation depends only on 'type_info'
// and 'hash' numeric values and doesn't really depends on global memory state
// (as TypeInfo is compile time constant and type info pointers are stable).
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) RUNTIME_CONST;
InterfaceTableRecord const* LookupInterfaceTableRecord(InterfaceTableRecord const* interfaceTable,
int interfaceTableSize, ClassId interfaceId) RUNTIME_CONST;
#ifdef __cplusplus
} // extern "C"
#endif
#endif // RUNTIME_TYPEINFO_H
@@ -0,0 +1,111 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Types.h"
#include "Exceptions.h"
extern "C" {
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) {
// We assume null check is handled by caller.
RuntimeAssert(obj != nullptr, "must not be null");
const TypeInfo* obj_type_info = obj->type_info();
// If it is an interface - check in list of implemented interfaces.
if ((type_info->flags_ & TF_INTERFACE) != 0) {
for (int i = 0; i < obj_type_info->implementedInterfacesCount_; ++i) {
if (obj_type_info->implementedInterfaces_[i] == type_info) {
return 1;
}
}
return 0;
}
while (obj_type_info != nullptr && obj_type_info != type_info) {
obj_type_info = obj_type_info->superType_;
}
return obj_type_info != nullptr;
}
KBoolean IsInstanceOfClassFast(const ObjHeader* obj, int32_t lo, int32_t hi) {
// We assume null check is handled by caller.
RuntimeAssert(obj != nullptr, "must not be null");
const TypeInfo* obj_type_info = obj->type_info();
// Super type's interval should contain our interval.
return obj_type_info->classId_ >= lo && obj_type_info->classId_ <= hi;
}
KBoolean IsArray(KConstRef obj) {
RuntimeAssert(obj != nullptr, "Object must not be null");
return obj->type_info()->instanceSize_ < 0;
}
KBoolean Kotlin_TypeInfo_isInstance(KConstRef obj, KNativePtr typeInfo) {
return IsInstance(obj, reinterpret_cast<const TypeInfo*>(typeInfo));
}
OBJ_GETTER(Kotlin_TypeInfo_getPackageName, KNativePtr typeInfo) {
RETURN_OBJ(reinterpret_cast<const TypeInfo*>(typeInfo)->packageName_);
}
OBJ_GETTER(Kotlin_TypeInfo_getRelativeName, KNativePtr typeInfo) {
RETURN_OBJ(reinterpret_cast<const TypeInfo*>(typeInfo)->relativeName_);
}
struct AssociatedObjectTableRecord {
const TypeInfo* key;
OBJ_GETTER0((*getAssociatedObjectInstance));
};
OBJ_GETTER(Kotlin_TypeInfo_findAssociatedObject, KNativePtr typeInfo, KNativePtr key) {
const AssociatedObjectTableRecord* associatedObjects = reinterpret_cast<const TypeInfo*>(typeInfo)->associatedObjects;
if (associatedObjects == nullptr) {
RETURN_OBJ(nullptr);
}
for (int index = 0; associatedObjects[index].key != nullptr; ++index) {
if (associatedObjects[index].key == key) {
RETURN_RESULT_OF0(associatedObjects[index].getAssociatedObjectInstance);
}
}
RETURN_OBJ(nullptr);
}
bool IsSubInterface(const TypeInfo* thiz, const TypeInfo* other) {
for (int i = 0; i < thiz->implementedInterfacesCount_; ++i) {
if (thiz->implementedInterfaces_[i] == other) {
return true;
}
}
return false;
}
KVector4f Kotlin_Vector4f_of(KFloat f0, KFloat f1, KFloat f2, KFloat f3) {
return {f0, f1, f2, f3};
}
/*
* In the current design all simd types are mapped internally to floating type, e.g. <4 x float>.
* However, some platforms (ex. arm32) have different calling convention for <4 x float> and <4 x i32>.
* To avoid illegal bitcast from/to function types the following function
* return type MUST be <4 x float> and explicit type cast is done on the variable type.
*/
KVector4f Kotlin_Vector4i32_of(KInt f0, KInt f1, KInt f2, KInt f3) {
KInt __attribute__ ((__vector_size__(16))) v4i = {f0, f1, f2, f3};
return (KVector4f)v4i;
}
} // extern "C"
+121
View File
@@ -0,0 +1,121 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RUNTIME_TYPES_H
#define RUNTIME_TYPES_H
#include <stdlib.h>
#if (KONAN_WASM || KONAN_ZEPHYR) && !defined(assert)
// assert() is needed by STLport.
#define assert(cond) if (!(cond)) abort()
#endif
#include <deque>
#include <list>
#include <map>
#include <string>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "Alloc.h"
#include "Common.h"
#include "Memory.h"
#include "TypeInfo.h"
// Note that almost all types are signed.
typedef bool KBoolean;
typedef int8_t KByte;
typedef uint16_t KChar;
typedef int16_t KShort;
typedef int32_t KInt;
typedef int64_t KLong;
typedef uint8_t KUByte;
typedef uint16_t KUShort;
typedef uint32_t KUInt;
typedef uint64_t KULong;
typedef float KFloat;
typedef double KDouble;
typedef void* KNativePtr;
typedef KFloat __attribute__ ((__vector_size__ (16))) KVector4f;
typedef const void* KConstNativePtr;
typedef ObjHeader* KRef;
typedef const ObjHeader* KConstRef;
typedef const ArrayHeader* KString;
// Definitions of STL classes used inside Konan runtime.
typedef std::basic_string<char, std::char_traits<char>,
KonanAllocator<char>> KStdString;
template<class Value>
using KStdDeque = std::deque<Value, KonanAllocator<Value>>;
template<class Key, class Value>
using KStdUnorderedMap = std::unordered_map<Key, Value,
std::hash<Key>, std::equal_to<Key>,
KonanAllocator<std::pair<const Key, Value>>>;
template<class Value>
using KStdUnorderedSet = std::unordered_set<Value,
std::hash<Value>, std::equal_to<Value>,
KonanAllocator<Value>>;
template<class Value, class Compare = std::less<Value>>
using KStdOrderedSet = std::set<Value, Compare, KonanAllocator<Value>>;
template<class Key, class Value, class Compare = std::less<Key>>
using KStdOrderedMap = std::map<Key, Value, Compare, KonanAllocator<std::pair<const Key, Value>>>;
template<class Value>
using KStdVector = std::vector<Value, KonanAllocator<Value>>;
template<class Value>
using KStdList = std::list<Value, KonanAllocator<Value>>;
#ifdef __cplusplus
extern "C" {
#endif
extern const TypeInfo* theAnyTypeInfo;
extern const TypeInfo* theArrayTypeInfo;
extern const TypeInfo* theBooleanArrayTypeInfo;
extern const TypeInfo* theByteArrayTypeInfo;
extern const TypeInfo* theCharArrayTypeInfo;
extern const TypeInfo* theDoubleArrayTypeInfo;
extern const TypeInfo* theForeignObjCObjectTypeInfo;
extern const TypeInfo* theIntArrayTypeInfo;
extern const TypeInfo* theLongArrayTypeInfo;
extern const TypeInfo* theNativePtrArrayTypeInfo;
extern const TypeInfo* theFloatArrayTypeInfo;
extern const TypeInfo* theForeignObjCObjectTypeInfo;
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo;
extern const TypeInfo* theObjCObjectWrapperTypeInfo;
extern const TypeInfo* theOpaqueFunctionTypeInfo;
extern const TypeInfo* theShortArrayTypeInfo;
extern const TypeInfo* theStringTypeInfo;
extern const TypeInfo* theThrowableTypeInfo;
extern const TypeInfo* theUnitTypeInfo;
extern const TypeInfo* theWorkerBoundReferenceTypeInfo;
extern const TypeInfo* theCleanerImplTypeInfo;
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
KBoolean IsInstanceOfClassFast(const ObjHeader* obj, int32_t lo, int32_t hi) RUNTIME_PURE;
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
KBoolean IsArray(KConstRef obj) RUNTIME_PURE;
bool IsSubInterface(const TypeInfo* thiz, const TypeInfo* other) RUNTIME_PURE;
#ifdef __cplusplus
}
#endif
#endif // RUNTIME_TYPES_H
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstdint>
#include "KAssert.h"
class SimpleMutex {
private:
int32_t atomicInt = 0;
public:
void lock() {
while (!__sync_bool_compare_and_swap(&atomicInt, 0, 1)) {
// TODO: yield.
}
}
void unlock() {
if (!__sync_bool_compare_and_swap(&atomicInt, 1, 0)) {
RuntimeAssert(false, "Unable to unlock");
}
}
};
// TODO: use std::lock_guard instead?
template <class Mutex>
class LockGuard {
public:
explicit LockGuard(Mutex& mutex_) : mutex(mutex_) {
mutex.lock();
}
~LockGuard() {
mutex.unlock();
}
private:
Mutex& mutex;
LockGuard(const LockGuard&) = delete;
LockGuard& operator=(const LockGuard&) = delete;
};
+106
View File
@@ -0,0 +1,106 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Weak.h"
#include "Memory.h"
#include "Types.h"
namespace {
// TODO: an ugly hack with fixed layout.
struct WeakReferenceCounter {
ObjHeader header;
KRef referred;
KInt lock;
KInt cookie;
};
inline WeakReferenceCounter* asWeakReferenceCounter(ObjHeader* obj) {
return reinterpret_cast<WeakReferenceCounter*>(obj);
}
#if !KONAN_NO_THREADS
inline void lock(int32_t* address) {
RuntimeAssert(*address == 0 || *address == 1, "Incorrect lock state");
while (__sync_val_compare_and_swap(address, 0, 1) == 1);
}
inline void unlock(int32_t* address) {
int old = __sync_val_compare_and_swap(address, 1, 0);
RuntimeAssert(old == 1, "Incorrect lock state");
}
#endif
} // namespace
extern "C" {
OBJ_GETTER(makeWeakReferenceCounter, void*);
OBJ_GETTER(makeObjCWeakReferenceImpl, void*);
OBJ_GETTER(makePermanentWeakReferenceImpl, ObjHeader*);
// See Weak.kt for implementation details.
// Retrieve link on the counter object.
OBJ_GETTER(Konan_getWeakReferenceImpl, ObjHeader* referred) {
if (referred->permanent()) {
RETURN_RESULT_OF(makePermanentWeakReferenceImpl, referred);
}
#if KONAN_OBJC_INTEROP
if (IsInstance(referred, theObjCObjectWrapperTypeInfo)) {
RETURN_RESULT_OF(makeObjCWeakReferenceImpl, referred->GetAssociatedObject());
}
#endif // KONAN_OBJC_INTEROP
ObjHeader** weakCounterLocation = referred->GetWeakCounterLocation();
if (*weakCounterLocation == nullptr) {
ObjHolder counterHolder;
// Cast unneeded, just to emphasize we store an object reference as void*.
ObjHeader* counter = makeWeakReferenceCounter(reinterpret_cast<void*>(referred), counterHolder.slot());
UpdateHeapRefIfNull(weakCounterLocation, counter);
}
RETURN_OBJ(*weakCounterLocation);
}
// Materialize a weak reference to either null or the real reference.
OBJ_GETTER(Konan_WeakReferenceCounter_get, ObjHeader* counter) {
ObjHeader** referredAddress = &asWeakReferenceCounter(counter)->referred;
#if KONAN_NO_THREADS
RETURN_OBJ(*referredAddress);
#else
auto* weakCounter = asWeakReferenceCounter(counter);
RETURN_RESULT_OF(ReadHeapRefLocked, referredAddress, &weakCounter->lock, &weakCounter->cookie);
#endif
}
void WeakReferenceCounterClear(ObjHeader* counter) {
ObjHeader** referredAddress = &asWeakReferenceCounter(counter)->referred;
// Note, that we don't do UpdateRef here, as reference is weak.
#if KONAN_NO_THREADS
*referredAddress = nullptr;
#else
int32_t* lockAddress = &asWeakReferenceCounter(counter)->lock;
// Spinlock.
lock(lockAddress);
*referredAddress = nullptr;
unlock(lockAddress);
#endif
}
} // extern "C"
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_WEAK_H
#define RUNTIME_WEAK_H
#include "Memory.h"
extern "C" {
// Atomically clears counter object reference.
void WeakReferenceCounterClear(ObjHeader* counter);
} // extern "C"
#endif // RUNTIME_WEAK_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
#ifndef RUNTIME_WORKER_H
#define RUNTIME_WORKER_H
#include "Common.h"
#include "Types.h"
class Worker;
KInt GetWorkerId(Worker* worker);
Worker* WorkerInit(KBoolean errorReporting);
void WorkerDeinit(Worker* worker);
// Clean up all associated thread state, if this was a native worker.
void WorkerDestroyThreadDataIfNeeded(KInt id);
// Wait until all terminating native workers finish termination. Expected to be called at most once.
void WaitNativeWorkersTermination();
// Wait until terminating native worker `id` finishes termination. Expected to be called at most once for each worker.
void WaitNativeWorkerTermination(KInt id);
// Schedule the job without the result.
bool WorkerSchedule(KInt id, KNativePtr jobStablePtr);
#endif // RUNTIME_WORKER_H
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "WorkerBoundReference.h"
#include "Alloc.h"
#include "Memory.h"
#include "MemorySharedRefs.hpp"
namespace {
struct WorkerBoundReference {
ObjHeader header;
KRefSharedHolder* holder;
};
WorkerBoundReference* asWorkerBoundReference(KRef thiz) {
return reinterpret_cast<WorkerBoundReference*>(thiz);
}
} // namespace
RUNTIME_NOTHROW void DisposeWorkerBoundReference(KRef thiz) {
// DisposeSharedRef is only called when all references to thiz are gone.
// Can be null if WorkerBoundReference wasn't frozen.
if (auto* holder = asWorkerBoundReference(thiz)->holder) {
holder->dispose();
konanDestructInstance(holder);
}
}
// Defined in WorkerBoundReference.kt
extern "C" void Kotlin_WorkerBoundReference_freezeHook(KRef thiz);
RUNTIME_NOTHROW void WorkerBoundReferenceFreezeHook(KRef thiz) {
Kotlin_WorkerBoundReference_freezeHook(thiz);
}
extern "C" {
KNativePtr Kotlin_WorkerBoundReference_create(KRef value) {
auto* holder = konanConstructInstance<KRefSharedHolder>();
holder->init(value);
return holder;
}
OBJ_GETTER(Kotlin_WorkerBoundReference_deref, KNativePtr holder) {
RETURN_OBJ(reinterpret_cast<KRefSharedHolder*>(holder)->ref<ErrorPolicy::kDefaultValue>());
}
OBJ_GETTER(Kotlin_WorkerBoundReference_describe, KNativePtr holder) {
RETURN_RESULT_OF0(reinterpret_cast<KRefSharedHolder*>(holder)->describe);
}
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_SHAREDREF_H
#define RUNTIME_SHAREDREF_H
#include "Common.h"
#include "Types.h"
RUNTIME_NOTHROW void DisposeWorkerBoundReference(KRef thiz);
RUNTIME_NOTHROW void WorkerBoundReferenceFreezeHook(KRef thiz);
#endif // RUNTIME_SHAREDREF_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,904 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include "cbigint.h"
#if defined(LINUX) || defined(FREEBSD) || defined(ZOS) || defined(MACOSX) || defined(AIX)
#define USE_LL
#endif
#ifdef HY_LITTLE_ENDIAN
#define at(i) (i)
#else
#define at(i) ((i)^1)
/* the sequence for halfAt is -1, 2, 1, 4, 3, 6, 5, 8... */
/* and it should correspond to 0, 1, 2, 3, 4, 5, 6, 7... */
#define halfAt(i) (-((-(i)) ^ 1))
#endif
#define HIGH_IN_U64(u64) ((u64) >> 32)
#if defined(USE_LL)
#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFFLL)
#else
#if defined(USE_L)
#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFFL)
#else
#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFF)
#endif /* USE_L */
#endif /* USE_LL */
#if defined(USE_LL)
#define TEN_E1 (0xALL)
#define TEN_E2 (0x64LL)
#define TEN_E3 (0x3E8LL)
#define TEN_E4 (0x2710LL)
#define TEN_E5 (0x186A0LL)
#define TEN_E6 (0xF4240LL)
#define TEN_E7 (0x989680LL)
#define TEN_E8 (0x5F5E100LL)
#define TEN_E9 (0x3B9ACA00LL)
#define TEN_E19 (0x8AC7230489E80000LL)
#else
#if defined(USE_L)
#define TEN_E1 (0xAL)
#define TEN_E2 (0x64L)
#define TEN_E3 (0x3E8L)
#define TEN_E4 (0x2710L)
#define TEN_E5 (0x186A0L)
#define TEN_E6 (0xF4240L)
#define TEN_E7 (0x989680L)
#define TEN_E8 (0x5F5E100L)
#define TEN_E9 (0x3B9ACA00L)
#define TEN_E19 (0x8AC7230489E80000L)
#else
#define TEN_E1 (0xA)
#define TEN_E2 (0x64)
#define TEN_E3 (0x3E8)
#define TEN_E4 (0x2710)
#define TEN_E5 (0x186A0)
#define TEN_E6 (0xF4240)
#define TEN_E7 (0x989680)
#define TEN_E8 (0x5F5E100)
#define TEN_E9 (0x3B9ACA00)
#define TEN_E19 (0x8AC7230489E80000)
#endif /* USE_L */
#endif /* USE_LL */
#define TIMES_TEN(x) (((x) << 3) + ((x) << 1))
#define bitSection(x, mask, shift) (((x) & (mask)) >> (shift))
#define DOUBLE_TO_LONGBITS(dbl) (*((U_64 *)(&dbl)))
#define FLOAT_TO_INTBITS(flt) (*((U_32 *)(&flt)))
#define CREATE_DOUBLE_BITS(normalizedM, e) (((normalizedM) & MANTISSA_MASK) | (((U_64)((e) + E_OFFSET)) << 52))
#if defined(USE_LL)
#define MANTISSA_MASK (0x000FFFFFFFFFFFFFLL)
#define EXPONENT_MASK (0x7FF0000000000000LL)
#define NORMAL_MASK (0x0010000000000000LL)
#define SIGN_MASK (0x8000000000000000LL)
#else
#if defined(USE_L)
#define MANTISSA_MASK (0x000FFFFFFFFFFFFFL)
#define EXPONENT_MASK (0x7FF0000000000000L)
#define NORMAL_MASK (0x0010000000000000L)
#define SIGN_MASK (0x8000000000000000L)
#else
#define MANTISSA_MASK (0x000FFFFFFFFFFFFF)
#define EXPONENT_MASK (0x7FF0000000000000)
#define NORMAL_MASK (0x0010000000000000)
#define SIGN_MASK (0x8000000000000000)
#endif /* USE_L */
#endif /* USE_LL */
#define E_OFFSET (1075)
#define FLOAT_MANTISSA_MASK (0x007FFFFF)
#define FLOAT_EXPONENT_MASK (0x7F800000)
#define FLOAT_NORMAL_MASK (0x00800000)
#define FLOAT_E_OFFSET (150)
IDATA
simpleAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2)
{
/* assumes length > 0 */
IDATA index = 1;
*arg1 += arg2;
if (arg2 <= *arg1)
return 0;
else if (length == 1)
return 1;
while (++arg1[index] == 0 && ++index < length);
return (IDATA) index == length;
}
IDATA
addHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2)
{
/* addition is limited by length of arg1 as it this function is
* storing the result in arg1 */
/* fix for cc (GCC) 3.2 20020903 (Red Hat Linux 8.0 3.2-7): code generated does not
* do the temp1 + temp2 + carry addition correct. carry is 64 bit because gcc has
* subtle issues when you mix 64 / 32 bit maths. */
U_64 temp1, temp2, temp3; /* temporary variables to help the SH-4, and gcc */
U_64 carry;
IDATA index;
if (length1 == 0 || length2 == 0)
{
return 0;
}
else if (length1 < length2)
{
length2 = length1;
}
carry = 0;
index = 0;
do
{
temp1 = arg1[index];
temp2 = arg2[index];
temp3 = temp1 + temp2;
arg1[index] = temp3 + carry;
if (arg2[index] < arg1[index])
carry = 0;
else if (arg2[index] != arg1[index])
carry = 1;
}
while (++index < length2);
if (!carry)
return 0;
else if (index == length1)
return 1;
while (++arg1[index] == 0 && ++index < length1);
return (IDATA) index == length1;
}
void
subtractHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2)
{
/* assumes arg1 > arg2 */
IDATA index;
for (index = 0; index < length1; ++index)
arg1[index] = ~arg1[index];
simpleAddHighPrecision (arg1, length1, 1);
while (length2 > 0 && arg2[length2 - 1] == 0)
--length2;
addHighPrecision (arg1, length1, arg2, length2);
for (index = 0; index < length1; ++index)
arg1[index] = ~arg1[index];
simpleAddHighPrecision (arg1, length1, 1);
}
U_32
simpleMultiplyHighPrecision (U_64 * arg1, IDATA length, U_64 arg2)
{
/* assumes arg2 only holds 32 bits of information */
U_64 product;
IDATA index;
index = 0;
product = 0;
do
{
product =
HIGH_IN_U64 (product) + arg2 * LOW_U32_FROM_PTR (arg1 + index);
LOW_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (product);
product =
HIGH_IN_U64 (product) + arg2 * HIGH_U32_FROM_PTR (arg1 + index);
HIGH_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (product);
}
while (++index < length);
return HIGH_U32_FROM_VAR (product);
}
void
simpleMultiplyAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2,
U_32 * result)
{
/* Assumes result can hold the product and arg2 only holds 32 bits
of information */
U_64 product;
IDATA index, resultIndex;
index = resultIndex = 0;
product = 0;
do
{
product =
HIGH_IN_U64 (product) + result[at (resultIndex)] +
arg2 * LOW_U32_FROM_PTR (arg1 + index);
result[at (resultIndex)] = LOW_U32_FROM_VAR (product);
++resultIndex;
product =
HIGH_IN_U64 (product) + result[at (resultIndex)] +
arg2 * HIGH_U32_FROM_PTR (arg1 + index);
result[at (resultIndex)] = LOW_U32_FROM_VAR (product);
++resultIndex;
}
while (++index < length);
result[at (resultIndex)] += HIGH_U32_FROM_VAR (product);
if (result[at (resultIndex)] < HIGH_U32_FROM_VAR (product))
{
/* must be careful with ++ operator and macro expansion */
++resultIndex;
while (++result[at (resultIndex)] == 0)
++resultIndex;
}
}
#ifndef HY_LITTLE_ENDIAN
void simpleMultiplyAddHighPrecisionBigEndianFix(U_64 *arg1, IDATA length, U_64 arg2, U_32 *result) {
/* Assumes result can hold the product and arg2 only holds 32 bits
of information */
U_64 product;
IDATA index, resultIndex;
index = resultIndex = 0;
product = 0;
do {
product = HIGH_IN_U64(product) + result[halfAt(resultIndex)] + arg2 * LOW_U32_FROM_PTR(arg1 + index);
result[halfAt(resultIndex)] = LOW_U32_FROM_VAR(product);
++resultIndex;
product = HIGH_IN_U64(product) + result[halfAt(resultIndex)] + arg2 * HIGH_U32_FROM_PTR(arg1 + index);
result[halfAt(resultIndex)] = LOW_U32_FROM_VAR(product);
++resultIndex;
} while (++index < length);
result[halfAt(resultIndex)] += HIGH_U32_FROM_VAR(product);
if (result[halfAt(resultIndex)] < HIGH_U32_FROM_VAR(product)) {
/* must be careful with ++ operator and macro expansion */
++resultIndex;
while (++result[halfAt(resultIndex)] == 0) ++resultIndex;
}
}
#endif
void
multiplyHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2,
U_64 * result, IDATA length)
{
/* assumes result is large enough to hold product */
U_64 *temp;
U_32 *resultIn32;
IDATA count, index;
if (length1 < length2)
{
temp = arg1;
arg1 = arg2;
arg2 = temp;
count = length1;
length1 = length2;
length2 = count;
}
memset (result, 0, sizeof (U_64) * length);
/* length1 > length2 */
resultIn32 = (U_32 *) result;
index = -1;
for (count = 0; count < length2; ++count)
{
simpleMultiplyAddHighPrecision (arg1, length1, LOW_IN_U64 (arg2[count]),
resultIn32 + (++index));
#ifdef HY_LITTLE_ENDIAN
simpleMultiplyAddHighPrecision(arg1, length1, HIGH_IN_U64(arg2[count]), resultIn32 + (++index));
#else
simpleMultiplyAddHighPrecisionBigEndianFix(arg1, length1, HIGH_IN_U64(arg2[count]), resultIn32 + (++index));
#endif
}
}
U_32
simpleAppendDecimalDigitHighPrecision (U_64 * arg1, IDATA length, U_64 digit)
{
/* assumes digit is less than 32 bits */
U_64 arg;
IDATA index = 0;
digit <<= 32;
do
{
arg = LOW_IN_U64 (arg1[index]);
digit = HIGH_IN_U64 (digit) + TIMES_TEN (arg);
LOW_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (digit);
arg = HIGH_IN_U64 (arg1[index]);
digit = HIGH_IN_U64 (digit) + TIMES_TEN (arg);
HIGH_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (digit);
}
while (++index < length);
return HIGH_U32_FROM_VAR (digit);
}
void
simpleShiftLeftHighPrecision (U_64 * arg1, IDATA length, IDATA arg2)
{
/* assumes length > 0 */
IDATA index, offset;
if (arg2 >= 64)
{
offset = arg2 >> 6;
index = length;
while (--index - offset >= 0)
arg1[index] = arg1[index - offset];
do
{
arg1[index] = 0;
}
while (--index >= 0);
arg2 &= 0x3F;
}
if (arg2 == 0)
return;
while (--length > 0)
{
arg1[length] = arg1[length] << arg2 | arg1[length - 1] >> (64 - arg2);
}
*arg1 <<= arg2;
}
IDATA
highestSetBit (U_64 * y)
{
U_32 x;
IDATA result;
if (*y == 0)
return 0;
#if defined(USE_LL)
if (*y & 0xFFFFFFFF00000000LL)
{
x = HIGH_U32_FROM_PTR (y);
result = 32;
}
else
{
x = LOW_U32_FROM_PTR (y);
result = 0;
}
#else
#if defined(USE_L)
if (*y & 0xFFFFFFFF00000000L)
{
x = HIGH_U32_FROM_PTR (y);
result = 32;
}
else
{
x = LOW_U32_FROM_PTR (y);
result = 0;
}
#else
if (*y & 0xFFFFFFFF00000000)
{
x = HIGH_U32_FROM_PTR (y);
result = 32;
}
else
{
x = LOW_U32_FROM_PTR (y);
result = 0;
}
#endif /* USE_L */
#endif /* USE_LL */
if (x & 0xFFFF0000)
{
x = bitSection (x, 0xFFFF0000, 16);
result += 16;
}
if (x & 0xFF00)
{
x = bitSection (x, 0xFF00, 8);
result += 8;
}
if (x & 0xF0)
{
x = bitSection (x, 0xF0, 4);
result += 4;
}
if (x > 0x7)
return result + 4;
else if (x > 0x3)
return result + 3;
else if (x > 0x1)
return result + 2;
else
return result + 1;
}
IDATA
lowestSetBit (U_64 * y)
{
U_32 x;
IDATA result;
if (*y == 0)
return 0;
#if defined(USE_LL)
if (*y & 0x00000000FFFFFFFFLL)
{
x = LOW_U32_FROM_PTR (y);
result = 0;
}
else
{
x = HIGH_U32_FROM_PTR (y);
result = 32;
}
#else
#if defined(USE_L)
if (*y & 0x00000000FFFFFFFFL)
{
x = LOW_U32_FROM_PTR (y);
result = 0;
}
else
{
x = HIGH_U32_FROM_PTR (y);
result = 32;
}
#else
if (*y & 0x00000000FFFFFFFF)
{
x = LOW_U32_FROM_PTR (y);
result = 0;
}
else
{
x = HIGH_U32_FROM_PTR (y);
result = 32;
}
#endif /* USE_L */
#endif /* USE_LL */
if (!(x & 0xFFFF))
{
x = bitSection (x, 0xFFFF0000, 16);
result += 16;
}
if (!(x & 0xFF))
{
x = bitSection (x, 0xFF00, 8);
result += 8;
}
if (!(x & 0xF))
{
x = bitSection (x, 0xF0, 4);
result += 4;
}
if (x & 0x1)
return result + 1;
else if (x & 0x2)
return result + 2;
else if (x & 0x4)
return result + 3;
else
return result + 4;
}
IDATA
highestSetBitHighPrecision (U_64 * arg, IDATA length)
{
IDATA highBit;
while (--length >= 0)
{
highBit = highestSetBit (arg + length);
if (highBit)
return highBit + 64 * length;
}
return 0;
}
IDATA
lowestSetBitHighPrecision (U_64 * arg, IDATA length)
{
IDATA lowBit, index = -1;
while (++index < length)
{
lowBit = lowestSetBit (arg + index);
if (lowBit)
return lowBit + 64 * index;
}
return 0;
}
IDATA
compareHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2)
{
while (--length1 >= 0 && arg1[length1] == 0);
while (--length2 >= 0 && arg2[length2] == 0);
if (length1 > length2)
return 1;
else if (length1 < length2)
return -1;
else if (length1 > -1)
{
do
{
if (arg1[length1] > arg2[length1])
return 1;
else if (arg1[length1] < arg2[length1])
return -1;
}
while (--length1 >= 0);
}
return 0;
}
KDouble
toDoubleHighPrecision (U_64 * arg, IDATA length)
{
IDATA highBit;
U_64 mantissa, test64;
U_32 test;
KDouble result;
while (length > 0 && arg[length - 1] == 0)
--length;
if (length == 0)
result = 0.0;
else if (length > 16)
{
DOUBLE_TO_LONGBITS (result) = EXPONENT_MASK;
}
else if (length == 1)
{
highBit = highestSetBit (arg);
if (highBit <= 53)
{
highBit = 53 - highBit;
mantissa = *arg << highBit;
DOUBLE_TO_LONGBITS (result) =
CREATE_DOUBLE_BITS (mantissa, -highBit);
}
else
{
highBit -= 53;
mantissa = *arg >> highBit;
DOUBLE_TO_LONGBITS (result) =
CREATE_DOUBLE_BITS (mantissa, highBit);
/* perform rounding, round to even in case of tie */
test = (LOW_U32_FROM_PTR (arg) << (11 - highBit)) & 0x7FF;
if (test > 0x400 || ((test == 0x400) && (mantissa & 1)))
DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1;
}
}
else
{
highBit = highestSetBit (arg + (--length));
if (highBit <= 53)
{
highBit = 53 - highBit;
if (highBit > 0)
{
mantissa =
(arg[length] << highBit) | (arg[length - 1] >>
(64 - highBit));
}
else
{
mantissa = arg[length];
}
DOUBLE_TO_LONGBITS (result) =
CREATE_DOUBLE_BITS (mantissa, length * 64 - highBit);
/* perform rounding, round to even in case of tie */
test64 = arg[--length] << highBit;
if (test64 > SIGN_MASK || ((test64 == SIGN_MASK) && (mantissa & 1)))
DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1;
else if (test64 == SIGN_MASK)
{
while (--length >= 0)
{
if (arg[length] != 0)
{
DOUBLE_TO_LONGBITS (result) =
DOUBLE_TO_LONGBITS (result) + 1;
break;
}
}
}
}
else
{
highBit -= 53;
mantissa = arg[length] >> highBit;
DOUBLE_TO_LONGBITS (result) =
CREATE_DOUBLE_BITS (mantissa, length * 64 + highBit);
/* perform rounding, round to even in case of tie */
test = (LOW_U32_FROM_PTR (arg + length) << (11 - highBit)) & 0x7FF;
if (test > 0x400 || ((test == 0x400) && (mantissa & 1)))
DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1;
else if (test == 0x400)
{
do
{
if (arg[--length] != 0)
{
DOUBLE_TO_LONGBITS (result) =
DOUBLE_TO_LONGBITS (result) + 1;
break;
}
}
while (length > 0);
}
}
}
return result;
}
IDATA
tenToTheEHighPrecision (U_64 * result, IDATA length, int e)
{
/* size test */
if (length < ((e / 19) + 1))
return 0;
memset (result, 0, length * sizeof (U_64));
*result = 1;
if (e == 0)
return 1;
length = 1;
length = timesTenToTheEHighPrecision (result, length, e);
/* bad O(n) way of doing it, but simple */
/*
do {
overflow = simpleAppendDecimalDigitHighPrecision(result, length, 0);
if (overflow)
result[length++] = overflow;
} while (--e);
*/
return length;
}
IDATA
timesTenToTheEHighPrecision (U_64 * result, IDATA length, int e)
{
/* assumes result can hold value */
U_64 overflow;
int exp10 = e;
if (e == 0)
return length;
/* bad O(n) way of doing it, but simple */
/*
do {
overflow = simpleAppendDecimalDigitHighPrecision(result, length, 0);
if (overflow)
result[length++] = overflow;
} while (--e);
*/
/* Replace the current implementation which performs a
* "multiplication" by 10 e number of times with an actual
* multiplication. 10e19 is the largest exponent to the power of ten
* that will fit in a 64-bit integer, and 10e9 is the largest exponent to
* the power of ten that will fit in a 64-bit integer. Not sure where the
* break-even point is between an actual multiplication and a
* simpleAappendDecimalDigit() so just pick 10e3 as that point for
* now.
*/
while (exp10 >= 19)
{
overflow = simpleMultiplyHighPrecision64 (result, length, TEN_E19);
if (overflow)
result[length++] = overflow;
exp10 -= 19;
}
while (exp10 >= 9)
{
overflow = simpleMultiplyHighPrecision (result, length, TEN_E9);
if (overflow)
result[length++] = overflow;
exp10 -= 9;
}
if (exp10 == 0)
return length;
else if (exp10 == 1)
{
overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0);
if (overflow)
result[length++] = overflow;
}
else if (exp10 == 2)
{
overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0);
if (overflow)
result[length++] = overflow;
overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0);
if (overflow)
result[length++] = overflow;
}
else if (exp10 == 3)
{
overflow = simpleMultiplyHighPrecision (result, length, TEN_E3);
if (overflow)
result[length++] = overflow;
}
else if (exp10 == 4)
{
overflow = simpleMultiplyHighPrecision (result, length, TEN_E4);
if (overflow)
result[length++] = overflow;
}
else if (exp10 == 5)
{
overflow = simpleMultiplyHighPrecision (result, length, TEN_E5);
if (overflow)
result[length++] = overflow;
}
else if (exp10 == 6)
{
overflow = simpleMultiplyHighPrecision (result, length, TEN_E6);
if (overflow)
result[length++] = overflow;
}
else if (exp10 == 7)
{
overflow = simpleMultiplyHighPrecision (result, length, TEN_E7);
if (overflow)
result[length++] = overflow;
}
else if (exp10 == 8)
{
overflow = simpleMultiplyHighPrecision (result, length, TEN_E8);
if (overflow)
result[length++] = overflow;
}
return length;
}
U_64
doubleMantissa (KDouble z)
{
U_64 m = DOUBLE_TO_LONGBITS (z);
if ((m & EXPONENT_MASK) != 0)
m = (m & MANTISSA_MASK) | NORMAL_MASK;
else
m = (m & MANTISSA_MASK);
return m;
}
IDATA
doubleExponent (KDouble z)
{
/* assumes positive double */
IDATA k = HIGH_U32_FROM_VAR (z) >> 20;
if (k)
k -= E_OFFSET;
else
k = 1 - E_OFFSET;
return k;
}
UDATA
floatMantissa (KFloat z)
{
UDATA m = (UDATA) FLOAT_TO_INTBITS (z);
if ((m & FLOAT_EXPONENT_MASK) != 0)
m = (m & FLOAT_MANTISSA_MASK) | FLOAT_NORMAL_MASK;
else
m = (m & FLOAT_MANTISSA_MASK);
return m;
}
IDATA
floatExponent (KFloat z)
{
/* assumes positive float */
IDATA k = FLOAT_TO_INTBITS (z) >> 23;
if (k)
k -= FLOAT_E_OFFSET;
else
k = 1 - FLOAT_E_OFFSET;
return k;
}
/* Allow a 64-bit value in arg2 */
U_64
simpleMultiplyHighPrecision64 (U_64 * arg1, IDATA length, U_64 arg2)
{
U_64 intermediate, *pArg1, carry1, carry2, prod1, prod2, sum;
IDATA index;
U_32 buf32;
index = 0;
intermediate = 0;
pArg1 = arg1 + index;
carry1 = carry2 = 0;
do
{
if ((*pArg1 != 0) || (intermediate != 0))
{
prod1 =
(U_64) LOW_U32_FROM_VAR (arg2) * (U_64) LOW_U32_FROM_PTR (pArg1);
sum = intermediate + prod1;
if ((sum < prod1) || (sum < intermediate))
{
carry1 = 1;
}
else
{
carry1 = 0;
}
prod1 =
(U_64) LOW_U32_FROM_VAR (arg2) * (U_64) HIGH_U32_FROM_PTR (pArg1);
prod2 =
(U_64) HIGH_U32_FROM_VAR (arg2) * (U_64) LOW_U32_FROM_PTR (pArg1);
intermediate = carry2 + HIGH_IN_U64 (sum) + prod1 + prod2;
if ((intermediate < prod1) || (intermediate < prod2))
{
carry2 = 1;
}
else
{
carry2 = 0;
}
LOW_U32_FROM_PTR (pArg1) = LOW_U32_FROM_VAR (sum);
buf32 = HIGH_U32_FROM_PTR (pArg1);
HIGH_U32_FROM_PTR (pArg1) = LOW_U32_FROM_VAR (intermediate);
intermediate = carry1 + HIGH_IN_U64 (intermediate)
+ (U_64) HIGH_U32_FROM_VAR (arg2) * (U_64) buf32;
}
pArg1++;
}
while (++index < length);
return intermediate;
}
@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !defined(cbigint_h)
#define cbigint_h
#include "fltconst.h"
#include "../Types.h"
//#include "vmi.h"
#define LOW_U32_FROM_VAR(u64) LOW_U32_FROM_LONG64(u64)
#define LOW_U32_FROM_PTR(u64ptr) LOW_U32_FROM_LONG64_PTR(u64ptr)
#define HIGH_U32_FROM_VAR(u64) HIGH_U32_FROM_LONG64(u64)
#define HIGH_U32_FROM_PTR(u64ptr) HIGH_U32_FROM_LONG64_PTR(u64ptr)
#if defined(__cplusplus)
extern "C"
{
#endif
void multiplyHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2,
IDATA length2, U_64 * result, IDATA length);
U_32 simpleAppendDecimalDigitHighPrecision (U_64 * arg1, IDATA length, U_64 digit);
KDouble toDoubleHighPrecision (U_64 * arg, IDATA length);
IDATA tenToTheEHighPrecision (U_64 * result, IDATA length, int e);
U_64 doubleMantissa (KDouble z);
IDATA compareHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2);
IDATA highestSetBitHighPrecision (U_64 * arg, IDATA length);
void subtractHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2);
IDATA doubleExponent (KDouble z);
U_32 simpleMultiplyHighPrecision (U_64 * arg1, IDATA length, U_64 arg2);
IDATA addHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2);
void simpleMultiplyAddHighPrecisionBigEndianFix (U_64 * arg1, IDATA length, U_64 arg2, U_32 * result);
IDATA lowestSetBit (U_64 * y);
IDATA timesTenToTheEHighPrecision (U_64 * result, IDATA length, int e);
void simpleMultiplyAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2, U_32 * result);
IDATA highestSetBit (U_64 * y);
IDATA lowestSetBitHighPrecision (U_64 * arg, IDATA length);
void simpleShiftLeftHighPrecision (U_64 * arg1, IDATA length, IDATA arg2);
UDATA floatMantissa (KFloat z);
U_64 simpleMultiplyHighPrecision64 (U_64 * arg1, IDATA length, U_64 arg2);
IDATA simpleAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2);
IDATA floatExponent (KFloat z);
#if defined(__cplusplus)
}
#endif
#endif /* cbigint_h */
@@ -0,0 +1,878 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "cbigint.h"
#include "../Exceptions.h"
#include "../KString.h"
#include "../Natives.h"
#include "../utf8.h"
#if defined(LINUX) || defined(FREEBSD) || defined(ZOS) || defined(MACOSX) || defined(AIX)
#define USE_LL
#endif
#define LOW_I32_FROM_VAR(u64) LOW_I32_FROM_LONG64(u64)
#define LOW_I32_FROM_PTR(u64ptr) LOW_I32_FROM_LONG64_PTR(u64ptr)
#define HIGH_I32_FROM_VAR(u64) HIGH_I32_FROM_LONG64(u64)
#define HIGH_I32_FROM_PTR(u64ptr) HIGH_I32_FROM_LONG64_PTR(u64ptr)
#define MAX_ACCURACY_WIDTH 17
#define DEFAULT_WIDTH MAX_ACCURACY_WIDTH
extern "C" {
KDouble Kotlin_native_FloatingPointParser_parseDoubleImpl (KString s, KInt e);
void Kotlin_native_NumberConverter_bigIntDigitGeneratorInstImpl (KRef results,
KRef uArray,
KLong f,
KInt e,
KBoolean isDenormalized,
KBoolean mantissaIsZero,
KInt p);
KDouble Kotlin_native_NumberConverter_ceil(KDouble x) {
return ceil(x);
}
void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value);
KDouble Kotlin_native_long_bits_to_double(KLong x);
}
KDouble Kotlin_native_long_bits_to_double(KLong x) {
union {
int64_t x;
double d;
} tmp;
tmp.x = x;
return tmp.d;
}
KDouble createDouble (const char *s, KInt e);
KDouble createDouble1 (U_64 * f, IDATA length, KInt e);
KDouble doubleAlgorithm (U_64 * f, IDATA length, KInt e, KDouble z);
U_64 dblparse_shiftRight64 (U_64 * lp, volatile int mbe);
static const KDouble tens[] = {
1.0,
1.0e1,
1.0e2,
1.0e3,
1.0e4,
1.0e5,
1.0e6,
1.0e7,
1.0e8,
1.0e9,
1.0e10,
1.0e11,
1.0e12,
1.0e13,
1.0e14,
1.0e15,
1.0e16,
1.0e17,
1.0e18,
1.0e19,
1.0e20,
1.0e21,
1.0e22
};
#define tenToTheE(e) (*(tens + (e)))
#define LOG5_OF_TWO_TO_THE_N 23
#define INV_LOG_OF_TEN_BASE_2 (0.30102999566398114)
#define DOUBLE_MIN_VALUE 5.0e-324
#define sizeOfTenToTheE(e) (((e) / 19) + 1)
#if defined(USE_LL)
#define INFINITE_LONGBITS (0x7FF0000000000000LL)
#else
#if defined(USE_L)
#define INFINITE_LONGBITS (0x7FF0000000000000L)
#else
#define INFINITE_LONGBITS (0x7FF0000000000000)
#endif /* USE_L */
#endif /* USE_LL */
#define MINIMUM_LONGBITS (0x1)
#if defined(USE_LL)
#define MANTISSA_MASK (0x000FFFFFFFFFFFFFLL)
#define EXPONENT_MASK (0x7FF0000000000000LL)
#define NORMAL_MASK (0x0010000000000000LL)
#else
#if defined(USE_L)
#define MANTISSA_MASK (0x000FFFFFFFFFFFFFL)
#define EXPONENT_MASK (0x7FF0000000000000L)
#define NORMAL_MASK (0x0010000000000000L)
#else
#define MANTISSA_MASK (0x000FFFFFFFFFFFFF)
#define EXPONENT_MASK (0x7FF0000000000000)
#define NORMAL_MASK (0x0010000000000000)
#endif /* USE_L */
#endif /* USE_LL */
#define DOUBLE_TO_LONGBITS(dbl) (*((U_64 *)(&dbl)))
/* Keep a count of the number of times we decrement and increment to
* approximate the double, and attempt to detect the case where we
* could potentially toggle back and forth between decrementing and
* incrementing. It is possible for us to be stuck in the loop when
* incrementing by one or decrementing by one may exceed or stay below
* the value that we are looking for. In this case, just break out of
* the loop if we toggle between incrementing and decrementing for more
* than twice.
*/
#define INCREMENT_DOUBLE(_x, _decCount, _incCount) \
{ \
++DOUBLE_TO_LONGBITS(_x); \
_incCount++; \
if( (_incCount > 2) && (_decCount > 2) ) { \
if( _decCount > _incCount ) { \
DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \
} else if( _incCount > _decCount ) { \
DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \
} \
break; \
} \
}
#define DECREMENT_DOUBLE(_x, _decCount, _incCount) \
{ \
--DOUBLE_TO_LONGBITS(_x); \
_decCount++; \
if( (_incCount > 2) && (_decCount > 2) ) { \
if( _decCount > _incCount ) { \
DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \
} else if( _incCount > _decCount ) { \
DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \
} \
break; \
} \
}
#define ERROR_OCCURED(x) (HIGH_I32_FROM_VAR(x) < 0)
#define allocateU64(x, n) if (!((x) = (U_64*) konan::calloc(1, (n) * sizeof(U_64)))) goto OutOfMemory;
#define release(r) if ((r)) konan::free((r));
/*NB the Number converter methods are synchronized so it is possible to
*have global data for use by bigIntDigitGenerator */
#define RM_SIZE 21
#define STemp_SIZE 22
KDouble createDouble (const char *s, KInt e)
{
/* assumes s is a null terminated string with at least one
* character in it */
U_64 def[DEFAULT_WIDTH];
U_64 defBackup[DEFAULT_WIDTH];
U_64 *f, *fNoOverflow, *g, *tempBackup;
U_32 overflow;
KDouble result;
IDATA index = 1;
int unprocessedDigits = 0;
f = def;
fNoOverflow = defBackup;
*f = 0;
tempBackup = g = 0;
do
{
if (*s >= '0' && *s <= '9')
{
/* Make a back up of f before appending, so that we can
* back out of it if there is no more room, i.e. index >
* MAX_ACCURACY_WIDTH.
*/
memcpy (fNoOverflow, f, sizeof (U_64) * index);
overflow =
simpleAppendDecimalDigitHighPrecision (f, index, *s - '0');
if (overflow)
{
f[index++] = overflow;
/* There is an overflow, but there is no more room
* to store the result. We really only need the top 52
* bits anyway, so we must back out of the overflow,
* and ignore the rest of the string.
*/
if (index >= MAX_ACCURACY_WIDTH)
{
index--;
memcpy (f, fNoOverflow, sizeof (U_64) * index);
break;
}
if (tempBackup)
{
fNoOverflow = tempBackup;
}
}
}
else
index = -1;
}
while (index > 0 && *(++s) != '\0');
/* We've broken out of the parse loop either because we've reached
* the end of the string or we've overflowed the maximum accuracy
* limit of a double. If we still have unprocessed digits in the
* given string, then there are three possible results:
* 1. (unprocessed digits + e) == 0, in which case we simply
* convert the existing bits that are already parsed
* 2. (unprocessed digits + e) < 0, in which case we simply
* convert the existing bits that are already parsed along
* with the given e
* 3. (unprocessed digits + e) > 0 indicates that the value is
* simply too big to be stored as a double, so return Infinity
*/
if ((unprocessedDigits = strlen (s)) > 0)
{
e += unprocessedDigits;
if (index > -1)
{
if (e == 0)
result = toDoubleHighPrecision (f, index);
else if (e < 0)
result = createDouble1 (f, index, e);
else
{
DOUBLE_TO_LONGBITS (result) = INFINITE_LONGBITS;
}
}
else
{
LOW_I32_FROM_VAR (result) = -1;
HIGH_I32_FROM_VAR (result) = -1;
}
}
else
{
if (index > -1)
{
if (e == 0)
result = toDoubleHighPrecision (f, index);
else
result = createDouble1 (f, index, e);
}
else
{
LOW_I32_FROM_VAR (result) = -1;
HIGH_I32_FROM_VAR (result) = -1;
}
}
return result;
}
KDouble
createDouble1 (U_64 * f, IDATA length, KInt e)
{
IDATA numBits;
KDouble result;
#define APPROX_MIN_MAGNITUDE -309
#define APPROX_MAX_MAGNITUDE 309
numBits = highestSetBitHighPrecision (f, length) + 1;
numBits -= lowestSetBitHighPrecision (f, length);
if (numBits < 54 && e >= 0 && e < LOG5_OF_TWO_TO_THE_N)
{
return toDoubleHighPrecision (f, length) * tenToTheE (e);
}
else if (numBits < 54 && e < 0 && (-e) < LOG5_OF_TWO_TO_THE_N)
{
return toDoubleHighPrecision (f, length) / tenToTheE (-e);
}
else if (e >= 0 && e < APPROX_MAX_MAGNITUDE)
{
result = toDoubleHighPrecision (f, length) * pow (10.0, (double) e);
}
else if (e >= APPROX_MAX_MAGNITUDE)
{
/* Convert the partial result to make sure that the
* non-exponential part is not zero. This check fixes the case
* where the user enters 0.0e309! */
result = toDoubleHighPrecision (f, length);
/* Don't go straight to zero as the fact that x*0 = 0 independent of x might
cause the algorithm to produce an incorrect result. Instead try the min value
first and let it fall to zero if need be. */
if (result == 0.0)
DOUBLE_TO_LONGBITS (result) = MINIMUM_LONGBITS;
else
DOUBLE_TO_LONGBITS (result) = INFINITE_LONGBITS;
}
else if (e > APPROX_MIN_MAGNITUDE)
{
result = toDoubleHighPrecision (f, length) / pow (10.0, (double) -e);
}
if (e <= APPROX_MIN_MAGNITUDE)
{
result = toDoubleHighPrecision (f, length) * pow (10.0, (double) (e + 52));
result = result * pow (10.0, (double) -52);
}
/* Don't go straight to zero as the fact that x*0 = 0 independent of x might
cause the algorithm to produce an incorrect result. Instead try the min value
first and let it fall to zero if need be. */
if (result == 0.0)
DOUBLE_TO_LONGBITS (result) = MINIMUM_LONGBITS;
return doubleAlgorithm (f, length, e, result);
}
U_64
dblparse_shiftRight64 (U_64 * lp, volatile int mbe)
{
U_64 b1Value = 0;
U_32 hi = HIGH_U32_FROM_LONG64_PTR (lp);
U_32 lo = LOW_U32_FROM_LONG64_PTR (lp);
int srAmt;
if (mbe == 0)
return 0;
if (mbe >= 128)
{
HIGH_U32_FROM_LONG64_PTR (lp) = 0;
LOW_U32_FROM_LONG64_PTR (lp) = 0;
return 0;
}
/* Certain platforms do not handle de-referencing a 64-bit value
* from a pointer on the stack correctly (e.g. MVL-hh/XScale)
* because the pointer may not be properly aligned, so we'll have
* to handle two 32-bit chunks. */
if (mbe < 32)
{
LOW_U32_FROM_LONG64 (b1Value) = 0;
HIGH_U32_FROM_LONG64 (b1Value) = lo << (32 - mbe);
LOW_U32_FROM_LONG64_PTR (lp) = (hi << (32 - mbe)) | (lo >> mbe);
HIGH_U32_FROM_LONG64_PTR (lp) = hi >> mbe;
}
else if (mbe == 32)
{
LOW_U32_FROM_LONG64 (b1Value) = 0;
HIGH_U32_FROM_LONG64 (b1Value) = lo;
LOW_U32_FROM_LONG64_PTR (lp) = hi;
HIGH_U32_FROM_LONG64_PTR (lp) = 0;
}
else if (mbe < 64)
{
srAmt = mbe - 32;
LOW_U32_FROM_LONG64 (b1Value) = lo << (32 - srAmt);
HIGH_U32_FROM_LONG64 (b1Value) = (hi << (32 - srAmt)) | (lo >> srAmt);
LOW_U32_FROM_LONG64_PTR (lp) = hi >> srAmt;
HIGH_U32_FROM_LONG64_PTR (lp) = 0;
}
else if (mbe == 64)
{
LOW_U32_FROM_LONG64 (b1Value) = lo;
HIGH_U32_FROM_LONG64 (b1Value) = hi;
LOW_U32_FROM_LONG64_PTR (lp) = 0;
HIGH_U32_FROM_LONG64_PTR (lp) = 0;
}
else if (mbe < 96)
{
srAmt = mbe - 64;
b1Value = *lp;
HIGH_U32_FROM_LONG64_PTR (lp) = 0;
LOW_U32_FROM_LONG64_PTR (lp) = 0;
LOW_U32_FROM_LONG64 (b1Value) >>= srAmt;
LOW_U32_FROM_LONG64 (b1Value) |= (hi << (32 - srAmt));
HIGH_U32_FROM_LONG64 (b1Value) >>= srAmt;
}
else if (mbe == 96)
{
LOW_U32_FROM_LONG64 (b1Value) = hi;
HIGH_U32_FROM_LONG64 (b1Value) = 0;
HIGH_U32_FROM_LONG64_PTR (lp) = 0;
LOW_U32_FROM_LONG64_PTR (lp) = 0;
}
else
{
LOW_U32_FROM_LONG64 (b1Value) = hi >> (mbe - 96);
HIGH_U32_FROM_LONG64 (b1Value) = 0;
HIGH_U32_FROM_LONG64_PTR (lp) = 0;
LOW_U32_FROM_LONG64_PTR (lp) = 0;
}
return b1Value;
}
#if defined(WIN32)
/* disable global optimizations on the microsoft compiler for the
* doubleAlgorithm function otherwise it won't compile */
#pragma optimize("g",off)
#endif
/* The algorithm for the function doubleAlgorithm() below can be found
* in:
*
* "How to Read Floating-Point Numbers Accurately", William D.
* Clinger, Proceedings of the ACM SIGPLAN '90 Conference on
* Programming Language Design and Implementation, June 20-22,
* 1990, pp. 92-101.
*
* There is a possibility that the function will end up in an endless
* loop if the given approximating floating-point number (a very small
* floating-point whose value is very close to zero) straddles between
* two approximating integer values. We modified the algorithm slightly
* to detect the case where it oscillates back and forth between
* incrementing and decrementing the floating-point approximation. It
* is currently set such that if the oscillation occurs more than twice
* then return the original approximation.
*/
KDouble doubleAlgorithm (U_64 * f, IDATA length, KInt e, KDouble z)
{
U_64 m;
IDATA k, comparison, comparison2;
U_64 *x, *y, *D, *D2;
IDATA xLength, yLength, DLength, D2Length, decApproxCount, incApproxCount;
//PORT_ACCESS_FROM_ENV (env);
x = y = D = D2 = 0;
xLength = yLength = DLength = D2Length = 0;
decApproxCount = incApproxCount = 0;
do
{
m = doubleMantissa (z);
k = doubleExponent (z);
if (x && x != f)
//jclmem_free_memory (env, x);
release(x);
release (y);
release (D);
release (D2);
if (e >= 0 && k >= 0)
{
xLength = sizeOfTenToTheE (e) + length;
allocateU64 (x, xLength);
memset (x + length, 0, sizeof (U_64) * (xLength - length));
memcpy (x, f, sizeof (U_64) * length);
timesTenToTheEHighPrecision (x, xLength, e);
yLength = (k >> 6) + 2;
allocateU64 (y, yLength);
memset (y + 1, 0, sizeof (U_64) * (yLength - 1));
*y = m;
simpleShiftLeftHighPrecision (y, yLength, k);
}
else if (e >= 0)
{
xLength = sizeOfTenToTheE (e) + length + ((-k) >> 6) + 1;
allocateU64 (x, xLength);
memset (x + length, 0, sizeof (U_64) * (xLength - length));
memcpy (x, f, sizeof (U_64) * length);
timesTenToTheEHighPrecision (x, xLength, e);
simpleShiftLeftHighPrecision (x, xLength, -k);
yLength = 1;
allocateU64 (y, 1);
*y = m;
}
else if (k >= 0)
{
xLength = length;
x = f;
yLength = sizeOfTenToTheE (-e) + 2 + (k >> 6);
allocateU64 (y, yLength);
memset (y + 1, 0, sizeof (U_64) * (yLength - 1));
*y = m;
timesTenToTheEHighPrecision (y, yLength, -e);
simpleShiftLeftHighPrecision (y, yLength, k);
}
else
{
xLength = length + ((-k) >> 6) + 1;
allocateU64 (x, xLength);
memset (x + length, 0, sizeof (U_64) * (xLength - length));
memcpy (x, f, sizeof (U_64) * length);
simpleShiftLeftHighPrecision (x, xLength, -k);
yLength = sizeOfTenToTheE (-e) + 1;
allocateU64 (y, yLength);
memset (y + 1, 0, sizeof (U_64) * (yLength - 1));
*y = m;
timesTenToTheEHighPrecision (y, yLength, -e);
}
comparison = compareHighPrecision (x, xLength, y, yLength);
if (comparison > 0)
{ /* x > y */
DLength = xLength;
allocateU64 (D, DLength);
memcpy (D, x, DLength * sizeof (U_64));
subtractHighPrecision (D, DLength, y, yLength);
}
else if (comparison)
{ /* y > x */
DLength = yLength;
allocateU64 (D, DLength);
memcpy (D, y, DLength * sizeof (U_64));
subtractHighPrecision (D, DLength, x, xLength);
}
else
{ /* y == x */
DLength = 1;
allocateU64 (D, 1);
*D = 0;
}
D2Length = DLength + 1;
allocateU64 (D2, D2Length);
m <<= 1;
multiplyHighPrecision (D, DLength, &m, 1, D2, D2Length);
m >>= 1;
comparison2 = compareHighPrecision (D2, D2Length, y, yLength);
if (comparison2 < 0)
{
if (comparison < 0 && m == NORMAL_MASK)
{
simpleShiftLeftHighPrecision (D2, D2Length, 1);
if (compareHighPrecision (D2, D2Length, y, yLength) > 0)
{
DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
}
else
{
break;
}
}
else
{
break;
}
}
else if (comparison2 == 0)
{
if ((LOW_U32_FROM_VAR (m) & 1) == 0)
{
if (comparison < 0 && m == NORMAL_MASK)
{
DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
}
else
{
break;
}
}
else if (comparison < 0)
{
DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
break;
}
else
{
INCREMENT_DOUBLE (z, decApproxCount, incApproxCount);
break;
}
}
else if (comparison < 0)
{
DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
}
else
{
if (DOUBLE_TO_LONGBITS (z) == INFINITE_LONGBITS)
break;
INCREMENT_DOUBLE (z, decApproxCount, incApproxCount);
}
}
while (1);
if (x && x != f)
//jclmem_free_memory (env, x);
release(x);
release (y);
release (D);
release (D2);
return z;
OutOfMemory:
if (x && x != f)
//jclmem_free_memory (env, x);
release(x);
release (y);
release (D);
release (D2);
DOUBLE_TO_LONGBITS (z) = -2;
return z;
}
#if defined(WIN32)
#pragma optimize("",on) /*restore optimizations */
#endif
KDouble Kotlin_native_FloatingPointParser_parseDoubleImpl (KString s, KInt e)
{
const KChar* utf16 = CharArrayAddressOfElementAt(s, 0);
KStdString utf8;
utf8.reserve(s->count_);
TRY_CATCH(utf8::utf16to8(utf16, utf16 + s->count_, back_inserter(utf8)),
utf8::unchecked::utf16to8(utf16, utf16 + s->count_, back_inserter(utf8)),
/* Illegal UTF-16 string. */ ThrowNumberFormatException());
const char *str = utf8.c_str();
auto dbl = createDouble (str, e);
if (!ERROR_OCCURED (dbl))
{
return dbl;
}
else if (LOW_I32_FROM_VAR (dbl) == (I_32) - 1)
{ /* NumberFormatException */
ThrowNumberFormatException();
}
else
{ /* OutOfMemoryError */
ThrowOutOfMemoryError();
}
return 0.0;
}
/* The algorithm for this particular function can be found in:
*
* Printing Floating-Point Numbers Quickly and Accurately, Robert
* G. Burger, and R. Kent Dybvig, Programming Language Design and
* Implementation (PLDI) 1996, pp.108-116.
*
* The previous implementation of this function combined m+ and m- into
* one single M which caused some inaccuracy of the last digit. The
* particular case below shows this inaccuracy:
*
* System.out.println(new Double((1.234123412431233E107)).toString());
* System.out.println(new Double((1.2341234124312331E107)).toString());
* System.out.println(new Double((1.2341234124312332E107)).toString());
*
* outputs the following:
*
* 1.234123412431233E107
* 1.234123412431233E107
* 1.234123412431233E107
*
* instead of:
*
* 1.234123412431233E107
* 1.2341234124312331E107
* 1.2341234124312331E107
*
*/
void Kotlin_native_NumberConverter_bigIntDigitGeneratorInstImpl (KRef results,
KRef uArray,
KLong f,
KInt e,
KBoolean isDenormalized,
KBoolean mantissaIsZero,
KInt p)
{
int RLength, SLength, TempLength, mplus_Length, mminus_Length;
int high, low, i;
int k, firstK, U;
int getCount, setCount;
U_64 R[RM_SIZE], S[STemp_SIZE], mplus[RM_SIZE], mminus[RM_SIZE], Temp[STemp_SIZE];
memset (R, 0, RM_SIZE * sizeof (U_64));
memset (S, 0, STemp_SIZE * sizeof (U_64));
memset (mplus, 0, RM_SIZE * sizeof (U_64));
memset (mminus, 0, RM_SIZE * sizeof (U_64));
memset (Temp, 0, STemp_SIZE * sizeof (U_64));
if (e >= 0)
{
*R = f;
*mplus = *mminus = 1;
simpleShiftLeftHighPrecision (mminus, RM_SIZE, e);
if (f != (2 << (p - 1)))
{
simpleShiftLeftHighPrecision (R, RM_SIZE, e + 1);
*S = 2;
/*
* m+ = m+ << e results in 1.0e23 to be printed as
* 0.9999999999999999E23
* m+ = m+ << e+1 results in 1.0e23 to be printed as
* 1.0e23 (caused too much rounding)
* 470fffffffffffff = 2.0769187434139308E34
* 4710000000000000 = 2.076918743413931E34
*/
simpleShiftLeftHighPrecision (mplus, RM_SIZE, e);
}
else
{
simpleShiftLeftHighPrecision (R, RM_SIZE, e + 2);
*S = 4;
simpleShiftLeftHighPrecision (mplus, RM_SIZE, e + 1);
}
}
else
{
if (isDenormalized || (f != (2 << (p - 1))))
{
*R = f << 1;
*S = 1;
simpleShiftLeftHighPrecision (S, STemp_SIZE, 1 - e);
*mplus = *mminus = 1;
}
else
{
*R = f << 2;
*S = 1;
simpleShiftLeftHighPrecision (S, STemp_SIZE, 2 - e);
*mplus = 2;
*mminus = 1;
}
}
k = (int) ceil ((e + p - 1) * INV_LOG_OF_TEN_BASE_2 - 1e-10);
if (k > 0)
{
timesTenToTheEHighPrecision (S, STemp_SIZE, k);
}
else
{
timesTenToTheEHighPrecision (R, RM_SIZE, -k);
timesTenToTheEHighPrecision (mplus, RM_SIZE, -k);
timesTenToTheEHighPrecision (mminus, RM_SIZE, -k);
}
RLength = mplus_Length = mminus_Length = RM_SIZE;
SLength = TempLength = STemp_SIZE;
memset (Temp + RM_SIZE, 0, (STemp_SIZE - RM_SIZE) * sizeof (U_64));
memcpy (Temp, R, RM_SIZE * sizeof (U_64));
while (RLength > 1 && R[RLength - 1] == 0)
--RLength;
while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
--mplus_Length;
while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
--mminus_Length;
while (SLength > 1 && S[SLength - 1] == 0)
--SLength;
TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1;
addHighPrecision (Temp, TempLength, mplus, mplus_Length);
if (compareHighPrecision (Temp, TempLength, S, SLength) >= 0)
{
firstK = k;
}
else
{
firstK = k - 1;
simpleAppendDecimalDigitHighPrecision (R, ++RLength, 0);
simpleAppendDecimalDigitHighPrecision (mplus, ++mplus_Length, 0);
simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0);
while (RLength > 1 && R[RLength - 1] == 0)
--RLength;
while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
--mplus_Length;
while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
--mminus_Length;
}
getCount = setCount = 0;
do
{
U = 0;
for (i = 3; i >= 0; --i)
{
TempLength = SLength + 1;
Temp[SLength] = 0;
memcpy (Temp, S, SLength * sizeof (U_64));
simpleShiftLeftHighPrecision (Temp, TempLength, i);
if (compareHighPrecision (R, RLength, Temp, TempLength) >= 0)
{
subtractHighPrecision (R, RLength, Temp, TempLength);
U += 1 << i;
}
}
low = compareHighPrecision (R, RLength, mminus, mminus_Length) <= 0;
memset (Temp + RLength, 0, (STemp_SIZE - RLength) * sizeof (U_64));
memcpy (Temp, R, RLength * sizeof (U_64));
TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1;
addHighPrecision (Temp, TempLength, mplus, mplus_Length);
high = compareHighPrecision (Temp, TempLength, S, SLength) >= 0;
if (low || high)
break;
simpleAppendDecimalDigitHighPrecision (R, ++RLength, 0);
simpleAppendDecimalDigitHighPrecision (mplus, ++mplus_Length, 0);
simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0);
while (RLength > 1 && R[RLength - 1] == 0)
--RLength;
while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0)
--mplus_Length;
while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0)
--mminus_Length;
Kotlin_IntArray_set(uArray, setCount++, U);
//uArray[setCount++] = U;
}
while (1);
simpleShiftLeftHighPrecision (R, ++RLength, 1);
if (low && !high)
Kotlin_IntArray_set(uArray, setCount++, U);
//uArray[setCount++] = U;
else if (high && !low)
Kotlin_IntArray_set(uArray, setCount++, U + 1);
//uArray[setCount++] = U + 1;
else if (compareHighPrecision (R, RLength, S, SLength) < 0)
Kotlin_IntArray_set(uArray, setCount++, U);
//uArray[setCount++] = U;
else
Kotlin_IntArray_set(uArray, setCount++, U + 1);
//uArray[setCount++] = U + 1;
Kotlin_IntArray_set(results, 0, setCount);
// fid = (*env)->GetFieldID (env, clazz, "setCount", "I");
// (*env)->SetIntField (env, inst, fid, setCount);
Kotlin_IntArray_set(results, 1, getCount);
// fid = (*env)->GetFieldID (env, clazz, "getCount", "I");
// (*env)->SetIntField (env, inst, fid, getCount);
Kotlin_IntArray_set(results, 2, firstK);
// fid = (*env)->GetFieldID (env, clazz, "firstK", "I");
// (*env)->SetIntField (env, inst, fid, firstK);
}
@@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !defined(fltconst_h)
#define fltconst_h
#include "hycomp.h"
/* IEEE floats consist of: sign bit, exponent field, significand field
single: 31 = sign bit, 30..23 = exponent (8 bits), 22..0 = significand (23 bits)
double: 63 = sign bit, 62..52 = exponent (11 bits), 51..0 = significand (52 bits)
inf == (all exponent bits set) and (all mantissa bits clear)
nan == (all exponent bits set) and (at least one mantissa bit set)
finite == (at least one exponent bit clear)
zero == (all exponent bits clear) and (all mantissa bits clear)
denormal == (all exponent bits clear) and (at least one mantissa bit set)
positive == sign bit clear
negative == sign bit set
*/
#define MAX_U32_DOUBLE (ESDOUBLE) (4294967296.0) /* 2^32 */
#define MAX_U32_SINGLE (ESSINGLE) (4294967296.0) /* 2^32 */
#define HY_POS_PI (ESDOUBLE)(3.141592653589793)
#ifdef HY_LITTLE_ENDIAN
#ifdef HY_PLATFORM_DOUBLE_ORDER
#define DOUBLE_LO_OFFSET 0
#define DOUBLE_HI_OFFSET 1
#else
#define DOUBLE_LO_OFFSET 1
#define DOUBLE_HI_OFFSET 0
#endif
#define LONG_LO_OFFSET 0
#define LONG_HI_OFFSET 1
#else
#ifdef HY_PLATFORM_DOUBLE_ORDER
#define DOUBLE_LO_OFFSET 1
#define DOUBLE_HI_OFFSET 0
#else
#define DOUBLE_LO_OFFSET 0
#define DOUBLE_HI_OFFSET 1
#endif
#define LONG_LO_OFFSET 1
#define LONG_HI_OFFSET 0
#endif
#define RETURN_FINITE 0
#define RETURN_NAN 1
#define RETURN_POS_INF 2
#define RETURN_NEG_INF 3
#define DOUBLE_SIGN_MASK_HI 0x80000000
#define DOUBLE_EXPONENT_MASK_HI 0x7FF00000
#define DOUBLE_MANTISSA_MASK_LO 0xFFFFFFFF
#define DOUBLE_MANTISSA_MASK_HI 0x000FFFFF
#define SINGLE_SIGN_MASK 0x80000000
#define SINGLE_EXPONENT_MASK 0x7F800000
#define SINGLE_MANTISSA_MASK 0x007FFFFF
#define SINGLE_NAN_BITS (SINGLE_EXPONENT_MASK | 0x00400000)
typedef union u64u32dbl_tag {
U_64 u64val;
U_32 u32val[2];
I_32 i32val[2];
double dval;
} U64U32DBL;
/* Replace P_FLOAT_HI and P_FLOAT_LOW */
/* These macros are used to access the high and low 32-bit parts of a double (64-bit) value. */
#define LOW_U32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->u32val[DOUBLE_LO_OFFSET])
#define HIGH_U32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->u32val[DOUBLE_HI_OFFSET])
#define LOW_I32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->i32val[DOUBLE_LO_OFFSET])
#define HIGH_I32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->i32val[DOUBLE_HI_OFFSET])
#define LOW_U32_FROM_DBL(dbl) LOW_U32_FROM_DBL_PTR(&(dbl))
#define HIGH_U32_FROM_DBL(dbl) HIGH_U32_FROM_DBL_PTR(&(dbl))
#define LOW_I32_FROM_DBL(dbl) LOW_I32_FROM_DBL_PTR(&(dbl))
#define HIGH_I32_FROM_DBL(dbl) HIGH_I32_FROM_DBL_PTR(&(dbl))
#define LOW_U32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->u32val[LONG_LO_OFFSET])
#define HIGH_U32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->u32val[LONG_HI_OFFSET])
#define LOW_I32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->i32val[LONG_LO_OFFSET])
#define HIGH_I32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->i32val[LONG_HI_OFFSET])
#define LOW_U32_FROM_LONG64(long64) LOW_U32_FROM_LONG64_PTR(&(long64))
#define HIGH_U32_FROM_LONG64(long64) HIGH_U32_FROM_LONG64_PTR(&(long64))
#define LOW_I32_FROM_LONG64(long64) LOW_I32_FROM_LONG64_PTR(&(long64))
#define HIGH_I32_FROM_LONG64(long64) HIGH_I32_FROM_LONG64_PTR(&(long64))
#define IS_ZERO_DBL_PTR(dblptr) ((LOW_U32_FROM_DBL_PTR(dblptr) == 0) && ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0) || (HIGH_U32_FROM_DBL_PTR(dblptr) == DOUBLE_SIGN_MASK_HI)))
#define IS_ONE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0x3ff00000 || HIGH_U32_FROM_DBL_PTR(dblptr) == 0xbff00000) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0))
#define IS_NAN_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) == DOUBLE_EXPONENT_MASK_HI) && (LOW_U32_FROM_DBL_PTR(dblptr) | (HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_MANTISSA_MASK_HI)))
#define IS_INF_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & (DOUBLE_EXPONENT_MASK_HI|DOUBLE_MANTISSA_MASK_HI)) == DOUBLE_EXPONENT_MASK_HI) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0))
#define IS_DENORMAL_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) == 0) && ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_MANTISSA_MASK_HI) != 0 || (LOW_U32_FROM_DBL_PTR(dblptr) != 0)))
#define IS_FINITE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) < DOUBLE_EXPONENT_MASK_HI)
#define IS_POSITIVE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_SIGN_MASK_HI) == 0)
#define IS_NEGATIVE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_SIGN_MASK_HI) != 0)
#define IS_NEGATIVE_MAX_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0xFFEFFFFF) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0xFFFFFFFF))
#define IS_ZERO_DBL(dbl) IS_ZERO_DBL_PTR(&(dbl))
#define IS_ONE_DBL(dbl) IS_ONE_DBL_PTR(&(dbl))
#define IS_NAN_DBL(dbl) IS_NAN_DBL_PTR(&(dbl))
#define IS_INF_DBL(dbl) IS_INF_DBL_PTR(&(dbl))
#define IS_DENORMAL_DBL(dbl) IS_DENORMAL_DBL_PTR(&(dbl))
#define IS_FINITE_DBL(dbl) IS_FINITE_DBL_PTR(&(dbl))
#define IS_POSITIVE_DBL(dbl) IS_POSITIVE_DBL_PTR(&(dbl))
#define IS_NEGATIVE_DBL(dbl) IS_NEGATIVE_DBL_PTR(&(dbl))
#define IS_NEGATIVE_MAX_DBL(dbl) IS_NEGATIVE_MAX_DBL_PTR(&(dbl))
#define IS_ZERO_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) == (U_32)0)
#define IS_ONE_SNGL_PTR(fltptr) ((*U32P((fltptr)) == 0x3f800000) || (*U32P((fltptr)) == 0xbf800000))
#define IS_NAN_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) > (U_32)SINGLE_EXPONENT_MASK)
#define IS_INF_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) == (U_32)SINGLE_EXPONENT_MASK)
#define IS_DENORMAL_SNGL_PTR(fltptr) (((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK)-(U_32)1) < (U_32)SINGLE_MANTISSA_MASK)
#define IS_FINITE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) < (U_32)SINGLE_EXPONENT_MASK)
#define IS_POSITIVE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)SINGLE_SIGN_MASK) == (U_32)0)
#define IS_NEGATIVE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)SINGLE_SIGN_MASK) != (U_32)0)
#define IS_ZERO_SNGL(flt) IS_ZERO_SNGL_PTR(&(flt))
#define IS_ONE_SNGL(flt) IS_ONE_SNGL_PTR(&(flt))
#define IS_NAN_SNGL(flt) IS_NAN_SNGL_PTR(&(flt))
#define IS_INF_SNGL(flt) IS_INF_SNGL_PTR(&(flt))
#define IS_DENORMAL_SNGL(flt) IS_DENORMAL_SNGL_PTR(&(flt))
#define IS_FINITE_SNGL(flt) IS_FINITE_SNGL_PTR(&(flt))
#define IS_POSITIVE_SNGL(flt) IS_POSITIVE_SNGL_PTR(&(flt))
#define IS_NEGATIVE_SNGL(flt) IS_NEGATIVE_SNGL_PTR(&(flt))
#define SET_NAN_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = (DOUBLE_EXPONENT_MASK_HI | 0x00080000); LOW_U32_FROM_DBL_PTR(dblptr) = 0
#define SET_PZERO_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = 0; LOW_U32_FROM_DBL_PTR(dblptr) = 0
#define SET_NZERO_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = DOUBLE_SIGN_MASK_HI; LOW_U32_FROM_DBL_PTR(dblptr) = 0
#define SET_PINF_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = DOUBLE_EXPONENT_MASK_HI; LOW_U32_FROM_DBL_PTR(dblptr) = 0
#define SET_NINF_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = (DOUBLE_EXPONENT_MASK_HI | DOUBLE_SIGN_MASK_HI); LOW_U32_FROM_DBL_PTR(dblptr) = 0
#define SET_NAN_SNGL_PTR(fltptr) *U32P((fltptr)) = ((U_32)SINGLE_NAN_BITS)
#define SET_PZERO_SNGL_PTR(fltptr) *U32P((fltptr)) = 0
#define SET_NZERO_SNGL_PTR(fltptr) *U32P((fltptr)) = SINGLE_SIGN_MASK
#define SET_PINF_SNGL_PTR(fltptr) *U32P((fltptr)) = SINGLE_EXPONENT_MASK
#define SET_NINF_SNGL_PTR(fltptr) *U32P((fltptr)) = (SINGLE_EXPONENT_MASK | SINGLE_SIGN_MASK)
#if defined(HY_WORD64)
#define PTR_DOUBLE_VALUE(dstPtr, aDoublePtr) ((U64U32DBL *)(aDoublePtr))->u64val = ((U64U32DBL *)(dstPtr))->u64val
#define PTR_DOUBLE_STORE(dstPtr, aDoublePtr) ((U64U32DBL *)(dstPtr))->u64val = ((U64U32DBL *)(aDoublePtr))->u64val
#define STORE_LONG(dstPtr, hi, lo) ((U64U32DBL *)(dstPtr))->u64val = (((U_64)(hi)) << 32) | (lo)
#else
/* on some platforms (HP720) we cannot reference an unaligned float. Build them by hand, one U_32 at a time. */
#if defined(ATOMIC_FLOAT_ACCESS)
#define PTR_DOUBLE_STORE(dstPtr, aDoublePtr) HIGH_U32_FROM_DBL_PTR(dstPtr) = HIGH_U32_FROM_DBL_PTR(aDoublePtr); LOW_U32_FROM_DBL_PTR(dstPtr) = LOW_U32_FROM_DBL_PTR(aDoublePtr)
#define PTR_DOUBLE_VALUE(dstPtr, aDoublePtr) HIGH_U32_FROM_DBL_PTR(aDoublePtr) = HIGH_U32_FROM_DBL_PTR(dstPtr); LOW_U32_FROM_DBL_PTR(aDoublePtr) = LOW_U32_FROM_DBL_PTR(dstPtr)
#else
#define PTR_DOUBLE_STORE(dstPtr, aDoublePtr) (*(dstPtr) = *(aDoublePtr))
#define PTR_DOUBLE_VALUE(dstPtr, aDoublePtr) (*(aDoublePtr) = *(dstPtr))
#endif
#define STORE_LONG(dstPtr, hi, lo) HIGH_U32_FROM_LONG64_PTR(dstPtr) = (hi); LOW_U32_FROM_LONG64_PTR(dstPtr) = (lo)
#endif /* HY_WORD64 */
#define PTR_SINGLE_VALUE(dstPtr, aSinglePtr) (*U32P(aSinglePtr) = *U32P(dstPtr))
#define PTR_SINGLE_STORE(dstPtr, aSinglePtr) *((U_32 *)(dstPtr)) = (*U32P(aSinglePtr))
#endif /* fltconst_h */
@@ -0,0 +1,563 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "cbigint.h"
#include "../Exceptions.h"
#include "../KString.h"
#include "../Natives.h"
#include "../utf8.h"
#if defined(LINUX) || defined(FREEBSD) || defined(MACOSX) || defined(ZOS) || defined(AIX)
#define USE_LL
#endif
#ifdef HY_LITTLE_ENDIAN
#define LOW_I32_FROM_PTR(ptr64) (*(I_32 *) (ptr64))
#else
#define LOW_I32_FROM_PTR(ptr64) (*(((I_32 *) (ptr64)) + 1))
#endif
#define MAX_ACCURACY_WIDTH 8
#define DEFAULT_WIDTH MAX_ACCURACY_WIDTH
extern "C" KFloat Kotlin_native_int_bits_to_float(KInt x) {
union {
int32_t x;
float f;
} tmp;
tmp.x = x;
return tmp.f;
}
KFloat createFloat1 (U_64 * f, IDATA length, KInt e);
KFloat floatAlgorithm (U_64 * f, IDATA length, KInt e, KFloat z);
KFloat createFloat (const char *s, KInt e);
static const U_32 tens[] = {
0x3f800000,
0x41200000,
0x42c80000,
0x447a0000,
0x461c4000,
0x47c35000,
0x49742400,
0x4b189680,
0x4cbebc20,
0x4e6e6b28,
0x501502f9 /* 10 ^ 10 in float */
};
#define tenToTheE(e) (*((KFloat *) (tens + (e))))
#define LOG5_OF_TWO_TO_THE_N 11
#define sizeOfTenToTheE(e) (((e) / 19) + 1)
#define INFINITE_INTBITS (0x7F800000)
#define MINIMUM_INTBITS (1)
#define MANTISSA_MASK (0x007FFFFF)
#define EXPONENT_MASK (0x7F800000)
#define NORMAL_MASK (0x00800000)
#define FLOAT_TO_INTBITS(flt) (*((U_32 *)(&flt)))
/* Keep a count of the number of times we decrement and increment to
* approximate the double, and attempt to detect the case where we
* could potentially toggle back and forth between decrementing and
* incrementing. It is possible for us to be stuck in the loop when
* incrementing by one or decrementing by one may exceed or stay below
* the value that we are looking for. In this case, just break out of
* the loop if we toggle between incrementing and decrementing for more
* than twice.
*/
#define INCREMENT_FLOAT(_x, _decCount, _incCount) \
{ \
++FLOAT_TO_INTBITS(_x); \
_incCount++; \
if( (_incCount > 2) && (_decCount > 2) ) { \
if( _decCount > _incCount ) { \
FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \
} else if( _incCount > _decCount ) { \
FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \
} \
break; \
} \
}
#define DECREMENT_FLOAT(_x, _decCount, _incCount) \
{ \
--FLOAT_TO_INTBITS(_x); \
_decCount++; \
if( (_incCount > 2) && (_decCount > 2) ) { \
if( _decCount > _incCount ) { \
FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \
} else if( _incCount > _decCount ) { \
FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \
} \
break; \
} \
}
#define allocateU64(x, n) if (!((x) = (U_64*) konan::calloc(1, (n) * sizeof(U_64)))) goto OutOfMemory;
#define release(r) if ((r)) konan::free((r));
KFloat createFloat(const char *s, KInt e) {
/* assumes s is a null terminated string with at least one
* character in it */
U_64 def[DEFAULT_WIDTH];
U_64 defBackup[DEFAULT_WIDTH];
U_64 *f, *fNoOverflow, *g, *tempBackup;
U_32 overflow;
KFloat result;
IDATA index = 1;
int unprocessedDigits = 0;
f = def;
fNoOverflow = defBackup;
*f = 0;
tempBackup = g = 0;
do
{
if (*s >= '0' && *s <= '9')
{
/* Make a back up of f before appending, so that we can
* back out of it if there is no more room, i.e. index >
* MAX_ACCURACY_WIDTH.
*/
memcpy (fNoOverflow, f, sizeof (U_64) * index);
overflow =
simpleAppendDecimalDigitHighPrecision (f, index, *s - '0');
if (overflow)
{
f[index++] = overflow;
/* There is an overflow, but there is no more room
* to store the result. We really only need the top 52
* bits anyway, so we must back out of the overflow,
* and ignore the rest of the string.
*/
if (index >= MAX_ACCURACY_WIDTH)
{
index--;
memcpy (f, fNoOverflow, sizeof (U_64) * index);
break;
}
if (tempBackup)
{
fNoOverflow = tempBackup;
}
}
}
else
index = -1;
}
while (index > 0 && *(++s) != '\0');
/* We've broken out of the parse loop either because we've reached
* the end of the string or we've overflowed the maximum accuracy
* limit of a double. If we still have unprocessed digits in the
* given string, then there are three possible results:
* 1. (unprocessed digits + e) == 0, in which case we simply
* convert the existing bits that are already parsed
* 2. (unprocessed digits + e) < 0, in which case we simply
* convert the existing bits that are already parsed along
* with the given e
* 3. (unprocessed digits + e) > 0 indicates that the value is
* simply too big to be stored as a double, so return Infinity
*/
if ((unprocessedDigits = strlen (s)) > 0)
{
e += unprocessedDigits;
if (index > -1)
{
if (e <= 0)
{
result = createFloat1 (f, index, e);
}
else
{
FLOAT_TO_INTBITS (result) = INFINITE_INTBITS;
}
}
else
{
result = *(KFloat *) & index;
}
}
else
{
if (index > -1)
{
result = createFloat1 (f, index, e);
}
else
{
result = *(KFloat *) & index;
}
}
return result;
}
KFloat
createFloat1 (U_64 * f, IDATA length, KInt e)
{
IDATA numBits;
KDouble dresult;
KFloat result;
numBits = highestSetBitHighPrecision (f, length) + 1;
if (numBits < 25 && e >= 0 && e < LOG5_OF_TWO_TO_THE_N)
{
return ((KFloat) LOW_I32_FROM_PTR (f)) * tenToTheE (e);
}
else if (numBits < 25 && e < 0 && (-e) < LOG5_OF_TWO_TO_THE_N)
{
return ((KFloat) LOW_I32_FROM_PTR (f)) / tenToTheE (-e);
}
else if (e >= 0 && e < 39)
{
result = (KFloat) (toDoubleHighPrecision (f, length) * pow (10.0, (double) e));
}
else if (e >= 39)
{
/* Convert the partial result to make sure that the
* non-exponential part is not zero. This check fixes the case
* where the user enters 0.0e309! */
result = (KFloat) toDoubleHighPrecision (f, length);
if (result == 0.0)
FLOAT_TO_INTBITS (result) = MINIMUM_INTBITS;
else
FLOAT_TO_INTBITS (result) = INFINITE_INTBITS;
}
else if (e > -309)
{
int dexp;
U_32 fmant, fovfl;
U_64 dmant;
dresult = toDoubleHighPrecision (f, length) / pow (10.0, (double) -e);
if (IS_DENORMAL_DBL (dresult))
{
FLOAT_TO_INTBITS (result) = 0;
return result;
}
dexp = doubleExponent (dresult) + 51;
dmant = doubleMantissa (dresult);
/* Is it too small to be represented by a single-precision
* float? */
if (dexp <= -155)
{
FLOAT_TO_INTBITS (result) = 0;
return result;
}
/* Is it a denormalized single-precision float? */
if ((dexp <= -127) && (dexp > -155))
{
/* Only interested in 24 msb bits of the 53-bit double mantissa */
fmant = (U_32) (dmant >> 29);
fovfl = ((U_32) (dmant & 0x1FFFFFFF)) << 3;
while ((dexp < -127) && ((fmant | fovfl) != 0))
{
if ((fmant & 1) != 0)
{
fovfl |= 0x80000000;
}
fovfl >>= 1;
fmant >>= 1;
dexp++;
}
if ((fovfl & 0x80000000) != 0)
{
if ((fovfl & 0x7FFFFFFC) != 0)
{
fmant++;
}
else if ((fmant & 1) != 0)
{
fmant++;
}
}
else if ((fovfl & 0x40000000) != 0)
{
if ((fovfl & 0x3FFFFFFC) != 0)
{
fmant++;
}
}
FLOAT_TO_INTBITS (result) = fmant;
}
else
{
result = (KFloat) dresult;
}
}
/* Don't go straight to zero as the fact that x*0 = 0 independent
* of x might cause the algorithm to produce an incorrect result.
* Instead try the min value first and let it fall to zero if need
* be.
*/
if (e <= -309 || FLOAT_TO_INTBITS (result) == 0)
FLOAT_TO_INTBITS (result) = MINIMUM_INTBITS;
return floatAlgorithm (f, length, e, result);
}
#if defined(WIN32)
/* disable global optimizations on the microsoft compiler for the
* floatAlgorithm function otherwise it won't properly compile */
#pragma optimize("g",off)
#endif
/* The algorithm for the function floatAlgorithm() below can be found
* in:
*
* "How to Read Floating-Point Numbers Accurately", William D.
* Clinger, Proceedings of the ACM SIGPLAN '90 Conference on
* Programming Language Design and Implementation, June 20-22,
* 1990, pp. 92-101.
*
* There is a possibility that the function will end up in an endless
* loop if the given approximating floating-point number (a very small
* floating-point whose value is very close to zero) straddles between
* two approximating integer values. We modified the algorithm slightly
* to detect the case where it oscillates back and forth between
* incrementing and decrementing the floating-point approximation. It
* is currently set such that if the oscillation occurs more than twice
* then return the original approximation.
*/
KFloat
floatAlgorithm (U_64 * f, IDATA length, KInt e, KFloat z)
{
U_64 m;
IDATA k, comparison, comparison2;
U_64 *x, *y, *D, *D2;
IDATA xLength, yLength, DLength, D2Length;
IDATA decApproxCount, incApproxCount;
//PORT_ACCESS_FROM_ENV (env);
x = y = D = D2 = 0;
xLength = yLength = DLength = D2Length = 0;
decApproxCount = incApproxCount = 0;
do
{
m = floatMantissa (z);
k = floatExponent (z);
if (x && x != f)
//jclmem_free_memory (env, x);
release(x);
release (y);
release (D);
release (D2);
if (e >= 0 && k >= 0)
{
xLength = sizeOfTenToTheE (e) + length;
allocateU64 (x, xLength);
memset (x + length, 0, sizeof (U_64) * (xLength - length));
memcpy (x, f, sizeof (U_64) * length);
timesTenToTheEHighPrecision (x, xLength, e);
yLength = (k >> 6) + 2;
allocateU64 (y, yLength);
memset (y + 1, 0, sizeof (U_64) * (yLength - 1));
*y = m;
simpleShiftLeftHighPrecision (y, yLength, k);
}
else if (e >= 0)
{
xLength = sizeOfTenToTheE (e) + length + ((-k) >> 6) + 1;
allocateU64 (x, xLength);
memset (x + length, 0, sizeof (U_64) * (xLength - length));
memcpy (x, f, sizeof (U_64) * length);
timesTenToTheEHighPrecision (x, xLength, e);
simpleShiftLeftHighPrecision (x, xLength, -k);
yLength = 1;
allocateU64 (y, 1);
*y = m;
}
else if (k >= 0)
{
xLength = length;
x = f;
yLength = sizeOfTenToTheE (-e) + 2 + (k >> 6);
allocateU64 (y, yLength);
memset (y + 1, 0, sizeof (U_64) * (yLength - 1));
*y = m;
timesTenToTheEHighPrecision (y, yLength, -e);
simpleShiftLeftHighPrecision (y, yLength, k);
}
else
{
xLength = length + ((-k) >> 6) + 1;
allocateU64 (x, xLength);
memset (x + length, 0, sizeof (U_64) * (xLength - length));
memcpy (x, f, sizeof (U_64) * length);
simpleShiftLeftHighPrecision (x, xLength, -k);
yLength = sizeOfTenToTheE (-e) + 1;
allocateU64 (y, yLength);
memset (y + 1, 0, sizeof (U_64) * (yLength - 1));
*y = m;
timesTenToTheEHighPrecision (y, yLength, -e);
}
comparison = compareHighPrecision (x, xLength, y, yLength);
if (comparison > 0)
{ /* x > y */
DLength = xLength;
allocateU64 (D, DLength);
memcpy (D, x, DLength * sizeof (U_64));
subtractHighPrecision (D, DLength, y, yLength);
}
else if (comparison)
{ /* y > x */
DLength = yLength;
allocateU64 (D, DLength);
memcpy (D, y, DLength * sizeof (U_64));
subtractHighPrecision (D, DLength, x, xLength);
}
else
{ /* y == x */
DLength = 1;
allocateU64 (D, 1);
*D = 0;
}
D2Length = DLength + 1;
allocateU64 (D2, D2Length);
m <<= 1;
multiplyHighPrecision (D, DLength, &m, 1, D2, D2Length);
m >>= 1;
comparison2 = compareHighPrecision (D2, D2Length, y, yLength);
if (comparison2 < 0)
{
if (comparison < 0 && m == NORMAL_MASK)
{
simpleShiftLeftHighPrecision (D2, D2Length, 1);
if (compareHighPrecision (D2, D2Length, y, yLength) > 0)
{
DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
}
else
{
break;
}
}
else
{
break;
}
}
else if (comparison2 == 0)
{
if ((m & 1) == 0)
{
if (comparison < 0 && m == NORMAL_MASK)
{
DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
}
else
{
break;
}
}
else if (comparison < 0)
{
DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
break;
}
else
{
INCREMENT_FLOAT (z, decApproxCount, incApproxCount);
break;
}
}
else if (comparison < 0)
{
DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
}
else
{
if (FLOAT_TO_INTBITS (z) == EXPONENT_MASK)
break;
INCREMENT_FLOAT (z, decApproxCount, incApproxCount);
}
}
while (1);
if (x && x != f)
//jclmem_free_memory (env, x);
release(x);
release (y);
release (D);
release (D2);
return z;
OutOfMemory:
if (x && x != f)
//jclmem_free_memory (env, x);
release(x);
release (y);
release (D);
release (D2);
FLOAT_TO_INTBITS (z) = -2;
return z;
}
#if defined(WIN32)
#pragma optimize("",on) /*restore optimizations */
#endif
extern "C" KFloat
Kotlin_native_FloatingPointParser_parseFloatImpl(KString s, KInt e)
{
const KChar* utf16 = CharArrayAddressOfElementAt(s, 0);
KStdString utf8;
utf8.reserve(s->count_);
TRY_CATCH(utf8::utf16to8(utf16, utf16 + s->count_, back_inserter(utf8)),
utf8::unchecked::utf16to8(utf16, utf16 + s->count_, back_inserter(utf8)),
/* Illegal UTF-16 string. */ ThrowNumberFormatException());
const char *str = utf8.c_str();
auto flt = createFloat(str, e);
if (((I_32) FLOAT_TO_INTBITS (flt)) >= 0) {
return flt;
} else if (((I_32) FLOAT_TO_INTBITS (flt)) == (I_32) - 1) {
/* NumberFormatException */
ThrowNumberFormatException();
} else {
/* OutOfMemoryError */
ThrowOutOfMemoryError();
}
return 0.0f;
}
@@ -0,0 +1,523 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !defined(hycomp_h)
#define hycomp_h
// TODO: Move to settings
#define MACOSX
/**
* USE_PROTOTYPES: Use full ANSI prototypes.
*
* CLOCK_PRIMS: We want the timer/clock prims to be used
*
* LITTLE_ENDIAN: This is for the intel machines or other
* little endian processors. Defaults to big endian.
*
* NO_LVALUE_CASTING: This is for compilers that don't like the left side
* of assigns to be cast. It hacks around to do the
* right thing.
*
* ATOMIC_FLOAT_ACCESS: So that float operations will work.
*
* LINKED_USER_PRIMITIVES: Indicates that user primitives are statically linked
* with the VM executeable.
*
* OLD_SPACE_SIZE_DIFF: The 68k uses a different amount of old space.
* This "legitimizes" the change.
*
* SIMPLE_SIGNAL: For machines that don't use real signals in C.
* (eg: PC, 68k)
*
* OS_NAME_LOOKUP: Use nlist to lookup user primitive addresses.
*
* VMCALL: Tag for all functions called by the VM.
*
* VMAPICALL: Tag for all functions called via the PlatformFunction
* callWith: mechanism.
*
* SYS_FLOAT: For some math functions where extended types (80 or 96 bits) are returned
* Most platforms return as a double
*
* FLOAT_EXTENDED: If defined, the type name for extended precision floats.
*
* PLATFORM_IS_ASCII: Must be defined if the platform is ASCII
*
* EXE_EXTENSION_CHAR: the executable has a delimiter that we want to stop at as part of argv[0].
*/
/**
* By default order doubles in the native (that is big/little endian) ordering.
*/
#define HY_PLATFORM_DOUBLE_ORDER
/**
* Define common types:
* <ul>
* <li><code>U_32 / I_32</code> - unsigned/signed 32 bits</li>
* <li><code>U_16 / I_16</code> - unsigned/signed 16 bits</li>
* <li><code>U_8 / I_8</code> - unsigned/signed 8 bits (bytes -- not to be
* confused with char)</li>
* </ul>
*/
typedef int I_32;
typedef short I_16;
typedef signed char I_8; /* chars can be unsigned */
typedef unsigned int U_32;
typedef unsigned short U_16;
typedef unsigned char U_8;
/**
* Define platform specific types:
* <ul>
* <li><code>U_64 / I_64</code> - unsigned/signed 64 bits</li>
* </ul>
*/
#if defined(LINUX) || defined(FREEBSD) || defined(AIX) || defined(MACOSX)
#define DATA_TYPES_DEFINED
/* NOTE: Linux supports different processors -- do not assume 386 */
#if defined(HYX86_64) || defined(HYIA64) || defined(HYPPC64) || defined(HYS390X)
typedef unsigned long int U_64; /* 64bits */
typedef long int I_64;
#define TOC_UNWRAP_ADDRESS(wrappedPointer) ((void *) (wrappedPointer)[0])
#define TOC_STORE_TOC(dest,wrappedPointer) (dest = ((UDATA*)wrappedPointer)[1])
#define HY_WORD64
#else
typedef long long I_64;
typedef unsigned long long U_64;
#endif
#if defined(HYS390X) || defined(HYS390) || defined(HYPPC64) || defined(HYPPC32) || defined(__MIPSEB__)
#define HY_BIG_ENDIAN
#else
#define HY_LITTLE_ENDIAN
#endif
#if defined(HYPPC32) && defined(LINUX)
#define VA_PTR(valist) (&valist[0])
#endif
typedef double SYS_FLOAT;
#define HYCONST64(x) x##LL
#define NO_LVALUE_CASTING
#define FLOAT_EXTENDED long double
#define PLATFORM_IS_ASCII
#define PLATFORM_LINE_DELIMITER "\012"
#define DIR_SEPARATOR '/'
#define DIR_SEPARATOR_STR "/"
#define PATH_SEPARATOR ':'
#define PATH_SEPARATOR_STR ":"
#if defined(AIX)
#define LIBPATH_ENV_VAR "LIBPATH"
#else
#if defined(MACOSX)
#define LIBPATH_ENV_VAR "DYLD_LIBRARY_PATH"
#else
#define LIBPATH_ENV_VAR "LD_LIBRARY_PATH"
#endif
#endif
#if defined(MACOSX)
#define PLATFORM_DLL_EXTENSION ".dylib"
#else
#define PLATFORM_DLL_EXTENSION ".so"
#endif
/**
* No priorities on Linux
*/
#define HY_PRIORITY_MAP {0,0,0,0,0,0,0,0,0,0,0,0}
typedef U_32 BOOLEAN;
#endif
/* Win32 - Windows 3.1 & NT using Win32 */
#if defined(WIN32)
#define HY_LITTLE_ENDIAN
/* Define 64-bit integers for Windows */
typedef __int64 I_64;
typedef unsigned __int64 U_64;
typedef double SYS_FLOAT;
#define NO_LVALUE_CASTING
#define VMAPICALL _stdcall
#define VMCALL _cdecl
#define EXE_EXTENSION_CHAR '.'
#define DIR_SEPARATOR '\\'
#define DIR_SEPARATOR_STR "\\"
#define PATH_SEPARATOR ';'
#define PATH_SEPARATOR_STR ";"
#define LIBPATH_ENV_VAR "PATH"
/* Modifications for the Alpha running WIN-NT */
#if defined(_ALPHA_)
#undef small /* defined as char in rpcndr.h */
typedef double FLOAT_EXTENDED;
#endif
#define HY_PRIORITY_MAP { \
THREAD_PRIORITY_IDLE, /* 0 */\
THREAD_PRIORITY_LOWEST, /* 1 */\
THREAD_PRIORITY_BELOW_NORMAL, /* 2 */\
THREAD_PRIORITY_BELOW_NORMAL, /* 3 */\
THREAD_PRIORITY_BELOW_NORMAL, /* 4 */\
THREAD_PRIORITY_NORMAL, /* 5 */\
THREAD_PRIORITY_ABOVE_NORMAL, /* 6 */\
THREAD_PRIORITY_ABOVE_NORMAL, /* 7 */\
THREAD_PRIORITY_ABOVE_NORMAL, /* 8 */\
THREAD_PRIORITY_ABOVE_NORMAL, /* 9 */\
THREAD_PRIORITY_HIGHEST, /*10 */\
THREAD_PRIORITY_TIME_CRITICAL /*11 */}
#endif /* defined(WIN32) */
#if defined(ZOS)
#define HY_BIG_ENDIAN
#define DATA_TYPES_DEFINED
typedef unsigned int BOOLEAN;
#if defined (HYS390X)
typedef unsigned long U_64;
typedef long I_64;
#else
typedef signed long long I_64;
typedef unsigned long long U_64;
#endif
typedef double SYS_FLOAT;
#define HYCONST64(x) x##LL
#define NO_LVALUE_CASTING
#define PLATFORM_LINE_DELIMITER "\012"
#define DIR_SEPARATOR '/'
#define DIR_SEPARATOR_STR "/"
#define PATH_SEPARATOR ':'
#define PATH_SEPARATOR_STR ":"
#define LIBPATH_ENV_VAR "LIBPATH"
#define VA_PTR(valist) (&valist[0])
typedef struct {
#if !defined(HYS390X)
char stuff[16];
#endif
char *ada;
void (*rawFnAddress)();
} HyFunctionDescriptor_T;
#define TOC_UNWRAP_ADDRESS(wrappedPointer) (((HyFunctionDescriptor_T *) (wrappedPointer))->rawFnAddress)
#define PLATFORM_DLL_EXTENSION ".so"
#ifdef HYS390X
#ifndef HY_WORD64
#define HY_WORD64
#endif /* ifndef HY_WORD64 */
#endif /* HYS390X */
#endif /* ZOS */
#if !defined(VMCALL)
#define VMCALL
#define VMAPICALL
#endif
#define PVMCALL VMCALL *
#define GLOBAL_DATA(symbol) ((void*)&(symbol))
#define GLOBAL_TABLE(symbol) GLOBAL_DATA(symbol)
/**
* Define platform specific types:
* <ul>
* <li><code>UDATA</code> - unsigned data, can be used as an integer or
* pointer storage</li>
* <li><code>IDATA</code> - signed data, can be used as an integer or
* pointer storage</li>
* </ul>
*/
/* FIXME: POINTER64 */
#if defined(HYX86_64) || defined(HYIA64) || defined(HYPPC64) || defined(HYS390X) || defined(POINTER64)
typedef I_64 IDATA;
typedef U_64 UDATA;
#else /* this is default for non-64bit systems */
typedef I_32 IDATA;
typedef U_32 UDATA;
#endif /* defined(HYX86_64) */
#if !defined(DATA_TYPES_DEFINED)
/* no generic U_64 or I_64 */
/* don't typedef BOOLEAN since it's already def'ed on Win32 */
#define BOOLEAN UDATA
#ifndef HY_BIG_ENDIAN
#define HY_LITTLE_ENDIAN
#endif
#endif
#if !defined(HYCONST64)
#define HYCONST64(x) x##L
#endif
#if !defined(HY_DEFAULT_SCHED)
/**
* By default, pthreads platforms use the <code>SCHED_OTHER</code> thread
* scheduling policy.
*/
#define HY_DEFAULT_SCHED SCHED_OTHER
#endif
#if !defined(HY_PRIORITY_MAP)
/**
* If no priority map if provided, priorities will be determined
* algorithmically.
*/
#endif
#if !defined(FALSE)
#define FALSE ((BOOLEAN) 0)
#if !defined(TRUE)
#define TRUE ((BOOLEAN) (!FALSE))
#endif
#endif
#if !defined(NULL)
#if defined(__cplusplus)
#define NULL (0)
#else
#define NULL ((void *)0)
#endif
#endif
#define USE_PROTOTYPES
#if defined(USE_PROTOTYPES)
#define PROTOTYPE(x) x
#define VARARGS , ...
#else
#define PROTOTYPE(x) ()
#define VARARGS
#endif
/**
* Assign the default line delimiter, if it was not set.
*/
#if !defined(PLATFORM_LINE_DELIMITER)
#define PLATFORM_LINE_DELIMITER "\015\012"
#endif
/**
* Set the max path length, if it was not set.
*/
#if !defined(MAX_IMAGE_PATH_LENGTH)
#define MAX_IMAGE_PATH_LENGTH (2048)
#endif
typedef double ESDOUBLE;
typedef float ESSINGLE;
/**
* Helpers for U_64s.
*/
#define CLEAR_U64(u64) (u64 = (U_64)0)
#define LOW_LONG(l) (*((U_32 *) &(l)))
#define HIGH_LONG(l) (*(((U_32 *) &(l)) + 1))
#define I8(x) ((I_8) (x))
#define I8P(x) ((I_8 *) (x))
#define U16(x) ((U_16) (x))
#define I16(x) ((I_16) (x))
#define I16P(x) ((I_16 *) (x))
#define U32(x) ((U_32) (x))
#define I32(x) ((I_32) (x))
#define I32P(x) ((I_32 *) (x))
#define U16P(x) ((U_16 *) (x))
#define U32P(x) ((U_32 *) (x))
#define OBJP(x) ((HyObject *) (x))
#define OBJPP(x) ((HyObject **) (x))
#define OBJPPP(x) ((HyObject ***) (x))
#define CLASSP(x) ((Class *) (x))
#define CLASSPP(x) ((Class **) (x))
#define BYTEP(x) ((BYTE *) (x))
/**
* Test - was conflicting with OS2.h
*/
#define ESCHAR(x) ((CHARACTER) (x))
#define FLT(x) ((FLOAT) x)
#define FLTP(x) ((FLOAT *) (x))
#if defined(NO_LVALUE_CASTING)
#define LI8(x) (*((I_8 *) &(x)))
#define LI8P(x) (*((I_8 **) &(x)))
#define LU16(x) (*((U_16 *) &(x)))
#define LI16(x) (*((I_16 *) &(x)))
#define LU32(x) (*((U_32 *) &(x)))
#define LI32(x) (*((I_32 *) &(x)))
#define LI32P(x) (*((I_32 **) &(x)))
#define LU16P(x) (*((U_16 **) &(x)))
#define LU32P(x) (*((U_32 **) &(x)))
#define LOBJP(x) (*((HyObject **) &(x)))
#define LOBJPP(x) (*((HyObject ***) &(x)))
#define LOBJPPP(x) (*((HyObject ****) &(x))
#define LCLASSP(x) (*((Class **) &(x)))
#define LBYTEP(x) (*((BYTE **) &(x)))
#define LCHAR(x) (*((CHARACTER) &(x)))
#define LFLT(x) (*((FLOAT) &x))
#define LFLTP(x) (*((FLOAT *) &(x)))
#else
#define LI8(x) I8((x))
#define LI8P(x) I8P((x))
#define LU16(x) U16((x))
#define LI16(x) I16((x))
#define LU32(x) U32((x))
#define LI32(x) I32((x))
#define LI32P(x) I32P((x))
#define LU16P(x) U16P((x))
#define LU32P(x) U32P((x))
#define LOBJP(x) OBJP((x))
#define LOBJPP(x) OBJPP((x))
#define LOBJPPP(x) OBJPPP((x))
#define LIOBJP(x) IOBJP((x))
#define LCLASSP(x) CLASSP((x))
#define LBYTEP(x) BYTEP((x))
#define LCHAR(x) CHAR((x))
#define LFLT(x) FLT((x))
#define LFLTP(x) FLTP((x))
#endif
/**
* Macros for converting between words and longs and accessing bits.
*/
#define HIGH_WORD(x) U16(U32((x)) >> 16)
#define LOW_WORD(x) U16(U32((x)) & 0xFFFF)
#define LOW_BIT(o) (U32((o)) & 1)
#define LOW_2_BITS(o) (U32((o)) & 3)
#define LOW_3_BITS(o) (U32((o)) & 7)
#define LOW_4_BITS(o) (U32((o)) & 15)
#define MAKE_32(h, l) ((U32((h)) << 16) | U32((l)))
#define MAKE_64(h, l) ((((I_64)(h)) << 32) | (l))
#if defined(__cplusplus)
#define HY_CFUNC "C"
#define HY_CDATA "C"
#else
#define HY_CFUNC
#define HY_CDATA
#endif
/**
* Macros for tagging functions which read/write the vm thread.
*/
#define READSVMTHREAD
#define WRITESVMTHREAD
#define REQUIRESSTACKFRAME
/**
* Macro for tagging functions, which never return.
*/
#if defined(__GNUC__)
/**
* On GCC, we can actually pass this information on to the compiler.
*/
#define NORETURN __attribute__((noreturn))
#else
#define NORETURN
#endif
/**
* On some systems va_list is an array type. This is probably in
* violation of the ANSI C spec, but it's not entirely clear. Because of
* this, we end up with an undesired extra level of indirection if we take
* the address of a va_list argument.
*
* To get it right, always use the VA_PTR macro
*/
#if !defined(VA_PTR)
#define VA_PTR(valist) (&valist)
#endif
#if !defined(TOC_UNWRAP_ADDRESS)
#define TOC_UNWRAP_ADDRESS(wrappedPointer) (wrappedPointer)
#endif
#if !defined(TOC_STORE_TOC)
#define TOC_STORE_TOC(dest,wrappedPointer)
#endif
/**
* Macros for accessing I_64 values.
*/
#if defined(ATOMIC_LONG_ACCESS)
#define PTR_LONG_STORE(dstPtr, aLongPtr) ((*U32P(dstPtr) = *U32P(aLongPtr)), (*(U32P(dstPtr)+1) = *(U32P(aLongPtr)+1)))
#define PTR_LONG_VALUE(dstPtr, aLongPtr) ((*U32P(aLongPtr) = *U32P(dstPtr)), (*(U32P(aLongPtr)+1) = *(U32P(dstPtr)+1)))
#else
#define PTR_LONG_STORE(dstPtr, aLongPtr) (*(dstPtr) = *(aLongPtr))
#define PTR_LONG_VALUE(dstPtr, aLongPtr) (*(aLongPtr) = *(dstPtr))
#endif
/**
* Macro used when declaring tables which require relocations.
*/
#if !defined(HYCONST_TABLE)
#define HYCONST_TABLE const
#endif
/**
* ANSI qsort is not always available.
*/
#if !defined(HY_SORT)
#define HY_SORT(base, nmemb, size, compare) qsort((base), (nmemb), (size), (compare))
#endif
/**
* Helper macros for storing/restoring pointers to jlong.
*/
#define jlong2addr(a, x) ((a *)((IDATA)(x)))
#define addr2jlong(x) ((jlong)((IDATA)(x)))
#endif /* hycomp_h */
@@ -0,0 +1,128 @@
musl as a whole is licensed under the following standard MIT license:
----------------------------------------------------------------------
Copyright © 2005-2014 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
----------------------------------------------------------------------
Authors/contributors include:
Anthony G. Basile
Arvid Picciani
Bobby Bingham
Boris Brezillon
Chris Spiegel
Emil Renner Berthing
Hiltjo Posthuma
Isaac Dunham
Jens Gustedt
Jeremy Huntwork
John Spencer
Justin Cormack
Luca Barbato
Luka Perkov
Michael Forney
Nicholas J. Kain
orc
Pascal Cuoq
Pierre Carrier
Rich Felker
Richard Pennington
Solar Designer
Strake
Szabolcs Nagy
Timo Teräs
Valentin Ochs
William Haddon
Portions of this software are derived from third-party works licensed
under terms compatible with the above MIT license:
The TRE regular expression implementation (src/regex/reg* and
src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed
under a 2-clause BSD license (license text in the source files). The
included version has been heavily modified by Rich Felker in 2012, in
the interests of size, simplicity, and namespace cleanliness.
Much of the math library code (src/math/* and src/complex/*) is
Copyright © 1993,2004 Sun Microsystems or
Copyright © 2003-2011 David Schultz or
Copyright © 2003-2009 Steven G. Kargl or
Copyright © 2003-2009 Bruce D. Evans or
Copyright © 2008 Stephen L. Moshier
and labelled as such in comments in the individual source files. All
have been licensed under extremely permissive terms.
The ARM memcpy code (src/string/armel/memcpy.s) is Copyright © 2008
The Android Open Source Project and is licensed under a two-clause BSD
license. It was taken from Bionic libc, used on Android.
The implementation of DES for crypt (src/misc/crypt_des.c) is
Copyright © 1994 David Burren. It is licensed under a BSD license.
The implementation of blowfish crypt (src/misc/crypt_blowfish.c) was
originally written by Solar Designer and placed into the public
domain. The code also comes with a fallback permissive license for use
in jurisdictions that may not recognize the public domain.
The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
Valentin Ochs and is licensed under an MIT-style license.
The BSD PRNG implementation (src/prng/random.c) and XSI search API
(src/search/*.c) functions are Copyright © 2011 Szabolcs Nagy and
licensed under following terms: "Permission to use, copy, modify,
and/or distribute this code for any purpose with or without fee is
hereby granted. There is no warranty."
The x86_64 port was written by Nicholas J. Kain. Several files (crt)
were released into the public domain; others are licensed under the
standard MIT license terms at the top of this file. See individual
files for their copyright status.
The mips and microblaze ports were originally written by Richard
Pennington for use in the ellcc project. The original code was adapted
by Rich Felker for build system and code conventions during upstream
integration. It is licensed under the standard MIT terms.
The powerpc port was also originally written by Richard Pennington,
and later supplemented and integrated by John Spencer. It is licensed
under the standard MIT terms.
All other files which have no copyright comments are original works
produced specifically for use as part of this library, written either
by Rich Felker, the main author of the library, or by one or more
contibutors listed above. Details on authorship of individual files
can be found in the git version control history of the project. The
omission of copyright and license comments in each file is in the
interest of source tree size.
All public header files (include/* and arch/*/bits/*) should be
treated as Public Domain as they intentionally contain no content
which can be covered by copyright. Some source modules may fall in
this category as well. If you believe that a file is so trivial that
it should be in the Public Domain, please contact the authors and
request an explicit statement releasing it from copyright.
The following files are trivial, believed not to be copyrightable in
the first place, and hereby explicitly released to the Public Domain:
All public headers: include/*, arch/*/bits/*
Startup files: crt/*
@@ -0,0 +1,82 @@
#ifndef _ENDIAN_H
#define _ENDIAN_H
#include <features.h>
#define __LITTLE_ENDIAN 1234
#define __BIG_ENDIAN 4321
#define __PDP_ENDIAN 3412
#if defined(__GNUC__) && defined(__BYTE_ORDER__)
#define __BYTE_ORDER __BYTE_ORDER__
#else
#include <bits/endian.h>
#endif
#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
#define BIG_ENDIAN __BIG_ENDIAN
#define LITTLE_ENDIAN __LITTLE_ENDIAN
#define PDP_ENDIAN __PDP_ENDIAN
#define BYTE_ORDER __BYTE_ORDER
#include <stdint.h>
static __inline uint16_t __bswap16(uint16_t __x)
{
return __x<<8 | __x>>8;
}
static __inline uint32_t __bswap32(uint32_t __x)
{
return __x>>24 | __x>>8&0xff00 | __x<<8&0xff0000 | __x<<24;
}
static __inline uint64_t __bswap64(uint64_t __x)
{
return __bswap32(__x)+0ULL<<32 | __bswap32(__x>>32);
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define htobe16(x) __bswap16(x)
#define be16toh(x) __bswap16(x)
#define betoh16(x) __bswap16(x)
#define htobe32(x) __bswap32(x)
#define be32toh(x) __bswap32(x)
#define betoh32(x) __bswap32(x)
#define htobe64(x) __bswap64(x)
#define be64toh(x) __bswap64(x)
#define betoh64(x) __bswap64(x)
#define htole16(x) (uint16_t)(x)
#define le16toh(x) (uint16_t)(x)
#define letoh16(x) (uint16_t)(x)
#define htole32(x) (uint32_t)(x)
#define le32toh(x) (uint32_t)(x)
#define letoh32(x) (uint32_t)(x)
#define htole64(x) (uint64_t)(x)
#define le64toh(x) (uint64_t)(x)
#define letoh64(x) (uint64_t)(x)
#else
#define htobe16(x) (uint16_t)(x)
#define be16toh(x) (uint16_t)(x)
#define betoh16(x) (uint16_t)(x)
#define htobe32(x) (uint32_t)(x)
#define be32toh(x) (uint32_t)(x)
#define betoh32(x) (uint32_t)(x)
#define htobe64(x) (uint64_t)(x)
#define be64toh(x) (uint64_t)(x)
#define betoh64(x) (uint64_t)(x)
#define htole16(x) __bswap16(x)
#define le16toh(x) __bswap16(x)
#define letoh16(x) __bswap16(x)
#define htole32(x) __bswap32(x)
#define le32toh(x) __bswap32(x)
#define letoh32(x) __bswap32(x)
#define htole64(x) __bswap64(x)
#define le64toh(x) __bswap64(x)
#define letoh64(x) __bswap64(x)
#endif
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More