From 1565371cda7ccae23f6f01580b98b09ec1289dda Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Fri, 14 Jul 2017 14:23:52 +0300 Subject: [PATCH] Porting layer (#741) --- runtime/src/main/cpp/Alloc.h | 6 +- runtime/src/main/cpp/Assert.cpp | 9 ++- runtime/src/main/cpp/Console.cpp | 30 ++------ runtime/src/main/cpp/KString.cpp | 15 +--- runtime/src/main/cpp/Porting.cpp | 127 +++++++++++++++++++++++++++++++ runtime/src/main/cpp/Porting.h | 52 +++++++++++++ runtime/src/main/cpp/Runtime.cpp | 17 +---- runtime/src/main/cpp/Time.cpp | 30 ++++++-- runtime/src/main/cpp/Types.cpp | 6 +- 9 files changed, 222 insertions(+), 70 deletions(-) create mode 100644 runtime/src/main/cpp/Porting.cpp create mode 100644 runtime/src/main/cpp/Porting.h diff --git a/runtime/src/main/cpp/Alloc.h b/runtime/src/main/cpp/Alloc.h index 5783e438148..1b73cbd94eb 100644 --- a/runtime/src/main/cpp/Alloc.h +++ b/runtime/src/main/cpp/Alloc.h @@ -23,12 +23,14 @@ #include #include +#include "Porting.h" + inline void* konanAllocMemory(size_t size) { - return calloc(1, size); + return konan::calloc(1, size); } inline void konanFreeMemory(void* memory) { - free(memory); + konan::free(memory); } template diff --git a/runtime/src/main/cpp/Assert.cpp b/runtime/src/main/cpp/Assert.cpp index e9b198455d9..2eb022f6faf 100644 --- a/runtime/src/main/cpp/Assert.cpp +++ b/runtime/src/main/cpp/Assert.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ -#include -#include +#include "Porting.h" void RuntimeAssertFailed(const char* location, const char* message) { // TODO: produce stacktrace and such. - fprintf(stderr, "%s: runtime assert: %s\n", location, message); - abort(); + char buf[1024]; + konan::snprintf(buf, sizeof(buf), "%s: runtime assert: %s\n", location, message); + konan::consoleErrorUtf8(buf, konan::strnlen(buf, sizeof(buf))); + konan::abort(); } diff --git a/runtime/src/main/cpp/Console.cpp b/runtime/src/main/cpp/Console.cpp index b1e15716544..ba8fed8a23c 100644 --- a/runtime/src/main/cpp/Console.cpp +++ b/runtime/src/main/cpp/Console.cpp @@ -13,27 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include -#include -#include -#include -#include - -#include - #include "Assert.h" #include "Memory.h" #include "Natives.h" #include "KString.h" +#include "Porting.h" #include "Types.h" #include "utf8.h" -#ifdef KONAN_ANDROID -#include -#endif - extern "C" { // io/Console.kt @@ -43,11 +31,7 @@ void Kotlin_io_Console_print(KString message) { const KChar* utf16 = CharArrayAddressOfElementAt(message, 0); KStdString utf8; utf8::utf16to8(utf16, utf16 + message->count_, back_inserter(utf8)); -#ifdef KONAN_ANDROID - __android_log_print(ANDROID_LOG_INFO, "Konan_main", "%s", utf8.c_str()); -#else - write(STDOUT_FILENO, utf8.c_str(), utf8.size()); -#endif + konan::consoleWriteUtf8(utf8.c_str(), utf8.size()); } void Kotlin_io_Console_println(KString message) { @@ -56,17 +40,13 @@ void Kotlin_io_Console_println(KString message) { } void Kotlin_io_Console_println0() { -#ifdef KONAN_ANDROID - __android_log_print(ANDROID_LOG_INFO, "Konan_main", "\n"); -#else - write(STDOUT_FILENO, "\n", 1); -#endif + konan::consoleWriteUtf8("\n", 1); } OBJ_GETTER0(Kotlin_io_Console_readLine) { char data[4096]; - if (!fgets(data, sizeof(data) - 1, stdin)) { - return nullptr; + if (konan::consoleReadUtf8(data, sizeof(data)) == 0) { + RETURN_OBJ(nullptr); } RETURN_RESULT_OF(CreateStringFromCString, data); } diff --git a/runtime/src/main/cpp/KString.cpp b/runtime/src/main/cpp/KString.cpp index 6218a736c48..9129e545f42 100644 --- a/runtime/src/main/cpp/KString.cpp +++ b/runtime/src/main/cpp/KString.cpp @@ -30,6 +30,7 @@ #include "Memory.h" #include "Natives.h" #include "KString.h" +#include "Porting.h" #include "Types.h" #include "utf8.h" @@ -1015,16 +1016,6 @@ KInt Kotlin_String_lastIndexOfChar(KString thiz, KChar ch, KInt fromIndex) { return -1; } -#ifdef _WIN32 -static void* memmem(const void* big, size_t big_len, const void* little, size_t little_len) { - for (size_t i = 0; i + little_len <= big_len; ++i) { - void* pos = ((char*)big) + i; - if (memcmp(little, pos, little_len) == 0) return pos; - } - return nullptr; -} -#endif - // TODO: or code up Knuth-Moris-Pratt. KInt Kotlin_String_indexOfString(KString thiz, KString other, KInt fromIndex) { if (fromIndex < 0 || fromIndex > thiz->count_ || @@ -1038,8 +1029,8 @@ KInt Kotlin_String_indexOfString(KString thiz, KString other, KInt fromIndex) { KInt count = thiz->count_; const KChar* thizRaw = CharArrayAddressOfElementAt(thiz, fromIndex); const KChar* otherRaw = CharArrayAddressOfElementAt(other, 0); - void* result = memmem(thizRaw, (thiz->count_ - fromIndex) * sizeof(KChar), - otherRaw, other->count_ * sizeof(KChar)); + void* result = konan::memmem(thizRaw, (thiz->count_ - fromIndex) * sizeof(KChar), + otherRaw, other->count_ * sizeof(KChar)); if (result == nullptr) return -1; return (reinterpret_cast(result) - reinterpret_cast( diff --git a/runtime/src/main/cpp/Porting.cpp b/runtime/src/main/cpp/Porting.cpp new file mode 100644 index 00000000000..eada87ca9d0 --- /dev/null +++ b/runtime/src/main/cpp/Porting.cpp @@ -0,0 +1,127 @@ +/* + * 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 +#endif +#include +#include +#include +#include +#include +#include +#if KONAN_WINDOWS +#include +#endif + +#include + +#include "Porting.h" + +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); +#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 +} + +uint32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes) { + char* result = ::fgets(reinterpret_cast(utf8), maxSizeBytes - 1, stdin); + if (result == nullptr) return 0; + return ::strlen(result); +} + +// Process execution. +void abort() { + ::abort(); +} + +// 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_WINDOWS + 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 + +} + +int snprintf(char* buffer, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int rv = ::vsnprintf(buffer, size, format, args); + va_end(args); + return rv; +} + +size_t strnlen(const char* buffer, size_t maxSize) { + return ::strnlen(buffer, maxSize); +} + +// Memory operations. +void* calloc(size_t count, size_t size) { + return ::calloc(count, size); +} + +void free(void* pointer) { + return ::free(pointer); +} + +// Time operations. +using namespace std::chrono; + +uint64_t getTimeMillis() { + return duration_cast(high_resolution_clock::now().time_since_epoch()).count(); +} + +uint64_t getTimeNanos() { + return duration_cast(high_resolution_clock::now().time_since_epoch()).count(); +} + +uint64_t getTimeMicros() { + return duration_cast(high_resolution_clock::now().time_since_epoch()).count(); +} + +} // namespace konan diff --git a/runtime/src/main/cpp/Porting.h b/runtime/src/main/cpp/Porting.h new file mode 100644 index 00000000000..f0d38d6044e --- /dev/null +++ b/runtime/src/main/cpp/Porting.h @@ -0,0 +1,52 @@ +/* + * 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 +#include + +namespace konan { + +// Console operations. +void consoleInit(); +void consoleWriteUtf8(const void* utf8, uint32_t sizeBytes); +void consoleErrorUtf8(const void* utf8, uint32_t sizeBytes); +uint32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes); + +// Process control. +void abort(); + +// 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); + +// Memory operations. +void* calloc(size_t count, size_t size); +void free(void* ptr); + +// Time operations. +uint64_t getTimeMillis(); +uint64_t getTimeMicros(); +uint64_t getTimeNanos(); + +} // namespace konan + +#endif // RUNTIME_PORTING_H diff --git a/runtime/src/main/cpp/Runtime.cpp b/runtime/src/main/cpp/Runtime.cpp index ef3a99534ac..6a90d9aa3fe 100644 --- a/runtime/src/main/cpp/Runtime.cpp +++ b/runtime/src/main/cpp/Runtime.cpp @@ -15,12 +15,10 @@ */ #include -#if KONAN_WINDOWS -#include -#endif #include "Alloc.h" #include "Memory.h" +#include "Porting.h" #include "Runtime.h" struct RuntimeState { @@ -48,9 +46,7 @@ void InitOrDeinitGlobalVariables(int initialize) { } // namespace -#ifdef __cplusplus extern "C" { -#endif void AppendToInitializersTail(InitNode *next) { // TODO: use RuntimeState. @@ -68,12 +64,7 @@ RuntimeState* InitRuntime() { result->memoryState = InitMemory(); // Keep global variables in state as well. InitOrDeinitGlobalVariables(true); -#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); -#endif + konan::consoleInit(); return result; } @@ -85,6 +76,4 @@ void DeinitRuntime(RuntimeState* state) { } } -#ifdef __cplusplus -} -#endif +} // extern "C" diff --git a/runtime/src/main/cpp/Time.cpp b/runtime/src/main/cpp/Time.cpp index 4ad6cbc883c..7c14e1f920d 100644 --- a/runtime/src/main/cpp/Time.cpp +++ b/runtime/src/main/cpp/Time.cpp @@ -1,21 +1,35 @@ -#include -#include "Types.h" -#include "Natives.h" +/* + * 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. + */ -using namespace std::chrono; +#include "Natives.h" +#include "Porting.h" +#include "Types.h" extern "C" { KLong Kotlin_system_getTimeMillis() { - return duration_cast(high_resolution_clock::now().time_since_epoch()).count(); + return konan::getTimeMillis(); } KLong Kotlin_system_getTimeNanos() { - return duration_cast(high_resolution_clock::now().time_since_epoch()).count(); + return konan::getTimeNanos(); } KLong Kotlin_system_getTimeMicros() { - return duration_cast(high_resolution_clock::now().time_since_epoch()).count(); + return konan::getTimeMicros(); } -} // extern "C" \ No newline at end of file +} // extern "C" diff --git a/runtime/src/main/cpp/Types.cpp b/runtime/src/main/cpp/Types.cpp index 6dfd78bd731..b522d31aa66 100644 --- a/runtime/src/main/cpp/Types.cpp +++ b/runtime/src/main/cpp/Types.cpp @@ -17,9 +17,7 @@ #include "Types.h" #include "Exceptions.h" -#ifdef __cplusplus extern "C" { -#endif KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) { // We assume null check is handled by caller. @@ -52,6 +50,4 @@ void CheckInstance(const ObjHeader* obj, const TypeInfo* type_info) { ThrowClassCastException(); } -#ifdef __cplusplus -} -#endif +} // extern "C"