More preparations for relaxed mode. (#3115)
This commit is contained in:
+4
-5
@@ -104,8 +104,6 @@ internal val cKeywords = setOf(
|
||||
"xor_eq"
|
||||
)
|
||||
|
||||
private val cnameAnnotation = FqName("kotlin.native.CName")
|
||||
|
||||
private fun org.jetbrains.kotlin.types.KotlinType.isGeneric() =
|
||||
constructor.declarationDescriptor is TypeParameterDescriptor
|
||||
|
||||
@@ -143,7 +141,7 @@ private fun AnnotationDescriptor.properValue(key: String) =
|
||||
|
||||
private fun functionImplName(descriptor: DeclarationDescriptor, default: String, shortName: Boolean): String {
|
||||
assert(descriptor is FunctionDescriptor)
|
||||
val annotation = descriptor.annotations.findAnnotation(cnameAnnotation) ?: return default
|
||||
val annotation = descriptor.annotations.findAnnotation(RuntimeNames.cnameAnnotation) ?: return default
|
||||
val key = if (shortName) "shortName" else "externName"
|
||||
val value = annotation.properValue(key)
|
||||
return value.takeIf { value != null && value.isNotEmpty() } ?: default
|
||||
@@ -265,9 +263,10 @@ private class ExportedElement(val kind: ElementKind,
|
||||
val isFunction = declaration is FunctionDescriptor
|
||||
val isTopLevelFunction: Boolean
|
||||
get() {
|
||||
if (declaration !is FunctionDescriptor || !declaration.annotations.hasAnnotation(cnameAnnotation))
|
||||
if (declaration !is FunctionDescriptor ||
|
||||
!declaration.annotations.hasAnnotation(RuntimeNames.cnameAnnotation))
|
||||
return false
|
||||
val annotation = declaration.annotations.findAnnotation(cnameAnnotation)!!
|
||||
val annotation = declaration.annotations.findAnnotation(RuntimeNames.cnameAnnotation)!!
|
||||
val externName = annotation.properValue("externName")
|
||||
return externName != null && externName.isNotEmpty()
|
||||
}
|
||||
|
||||
+1
@@ -128,6 +128,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
||||
|
||||
internal val defaultNativeLibraries: List<String> = mutableListOf<String>().apply {
|
||||
add(if (debug) "debug.bc" else "release.bc")
|
||||
add(if (memoryModel == MemoryModel.STRICT) "strict.bc" else "relaxed.bc")
|
||||
if (produce == CompilerOutputKind.PROGRAM) {
|
||||
addAll(distribution.launcherFiles)
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@ package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
enum class MemoryModel(val suffix: String) {
|
||||
STRICT("Strict"),
|
||||
RELAXED("Relaxed)")
|
||||
RELAXED("Relaxed")
|
||||
}
|
||||
+5
-1
@@ -3,14 +3,18 @@ package org.jetbrains.kotlin.backend.konan
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
object RuntimeNames {
|
||||
val symbolName = FqName("kotlin.native.SymbolName")
|
||||
val symbolNameAnnotation = FqName("kotlin.native.SymbolName")
|
||||
val cnameAnnotation = FqName("kotlin.native.CName")
|
||||
val frozenAnnotation = FqName("kotlin.native.internal.Frozen")
|
||||
val exportForCppRuntime = FqName("kotlin.native.internal.ExportForCppRuntime")
|
||||
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
|
||||
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
|
||||
val cCall = FqName("kotlinx.cinterop.internal.CCall")
|
||||
val objCMethodAnnotation = FqName("kotlinx.cinterop.ObjCMethod")
|
||||
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
||||
val independent = FqName("kotlin.native.internal.Independent")
|
||||
val filterExceptions = FqName("kotlin.native.internal.FilterExceptions")
|
||||
val kotlinNativeInternalPackageName = FqName.fromSegments(listOf("kotlin", "native", "internal"))
|
||||
val associatedObjectKey = FqName("kotlin.reflect.AssociatedObjectKey")
|
||||
val typedIntrinsicAnnotation = FqName("kotlin.native.internal.TypedIntrinsic")
|
||||
}
|
||||
|
||||
+5
-10
@@ -216,19 +216,14 @@ fun CallableMemberDescriptor.findSourceFile(): SourceFile {
|
||||
}
|
||||
}
|
||||
|
||||
internal val TypedIntrinsic = FqName("kotlin.native.internal.TypedIntrinsic")
|
||||
private val symbolNameAnnotation = FqName("kotlin.native.SymbolName")
|
||||
private val objCMethodAnnotation = FqName("kotlinx.cinterop.ObjCMethod")
|
||||
private val frozenAnnotation = FqName("kotlin.native.internal.Frozen")
|
||||
|
||||
internal val DeclarationDescriptor.isFrozen: Boolean
|
||||
get() = this.annotations.hasAnnotation(frozenAnnotation) ||
|
||||
get() = this.annotations.hasAnnotation(RuntimeNames.frozenAnnotation) ||
|
||||
(this is org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
// RTTI is used for non-reference type box or Objective-C object wrapper:
|
||||
&& (!this.defaultType.binaryTypeIsReference() || this.isObjCClass()))
|
||||
|
||||
internal val FunctionDescriptor.isTypedIntrinsic: Boolean
|
||||
get() = this.annotations.hasAnnotation(TypedIntrinsic)
|
||||
get() = this.annotations.hasAnnotation(RuntimeNames.typedIntrinsicAnnotation)
|
||||
|
||||
// TODO: coalesce all our annotation value getters into fewer functions.
|
||||
fun getAnnotationValue(annotation: AnnotationDescriptor): String? {
|
||||
@@ -239,12 +234,12 @@ fun getAnnotationValue(annotation: AnnotationDescriptor): String? {
|
||||
}
|
||||
|
||||
fun CallableMemberDescriptor.externalSymbolOrThrow(): String? {
|
||||
this.annotations.findAnnotation(symbolNameAnnotation)?.let {
|
||||
this.annotations.findAnnotation(RuntimeNames.symbolNameAnnotation)?.let {
|
||||
return getAnnotationValue(it)!!
|
||||
}
|
||||
if (this.annotations.hasAnnotation(objCMethodAnnotation)) return null
|
||||
if (this.annotations.hasAnnotation(RuntimeNames.objCMethodAnnotation)) return null
|
||||
|
||||
if (this.annotations.hasAnnotation(TypedIntrinsic)) return null
|
||||
if (this.annotations.hasAnnotation(RuntimeNames.typedIntrinsicAnnotation)) return null
|
||||
|
||||
if (this.annotations.hasAnnotation(RuntimeNames.cCall)) return null
|
||||
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
|
||||
|
||||
val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context))
|
||||
|
||||
val symbolName = topLevelClass(RuntimeNames.symbolName)
|
||||
val symbolName = topLevelClass(RuntimeNames.symbolNameAnnotation)
|
||||
val filterExceptions = topLevelClass(RuntimeNames.filterExceptions)
|
||||
val exportForCppRuntime = topLevelClass(RuntimeNames.exportForCppRuntime)
|
||||
|
||||
|
||||
+5
-33
@@ -16,20 +16,15 @@ import org.jetbrains.kotlin.backend.konan.ir.getObjCMethodInfo
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isObjCClassMethod
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isUnit
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler.isExported
|
||||
import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.util.fqNameSafe
|
||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.ir.util.isVararg
|
||||
import org.jetbrains.kotlin.backend.konan.isInlinedNative
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
import org.jetbrains.kotlin.library.uniqueName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
|
||||
// This file describes the ABI for Kotlin descriptors of exported declarations.
|
||||
@@ -53,34 +48,25 @@ object KonanMangler : KotlinManglerImpl() {
|
||||
override fun IrDeclaration.isPlatformSpecificExported(): Boolean {
|
||||
// TODO: revise
|
||||
val descriptorAnnotations = this.descriptor.annotations
|
||||
if (descriptorAnnotations.hasAnnotation(symbolNameAnnotation)) {
|
||||
if (descriptorAnnotations.hasAnnotation(RuntimeNames.symbolNameAnnotation)) {
|
||||
// Treat any `@SymbolName` declaration as exported.
|
||||
return true
|
||||
}
|
||||
if (descriptorAnnotations.hasAnnotation(exportForCppRuntimeAnnotation)) {
|
||||
if (descriptorAnnotations.hasAnnotation(RuntimeNames.exportForCppRuntime)) {
|
||||
// Treat any `@ExportForCppRuntime` declaration as exported.
|
||||
return true
|
||||
}
|
||||
if (descriptorAnnotations.hasAnnotation(cnameAnnotation)) {
|
||||
if (descriptorAnnotations.hasAnnotation(RuntimeNames.cnameAnnotation)) {
|
||||
// Treat `@CName` declaration as exported.
|
||||
return true
|
||||
}
|
||||
if (descriptorAnnotations.hasAnnotation(exportForCompilerAnnotation)) {
|
||||
if (descriptorAnnotations.hasAnnotation(RuntimeNames.exportForCompilerAnnotation)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private val symbolNameAnnotation = RuntimeNames.symbolName
|
||||
|
||||
private val cnameAnnotation = FqName("kotlin.native.CName")
|
||||
|
||||
private val exportForCppRuntimeAnnotation = RuntimeNames.exportForCppRuntime
|
||||
|
||||
private val exportForCompilerAnnotation = RuntimeNames.exportForCompilerAnnotation
|
||||
|
||||
|
||||
override val IrFunction.argsPart get() = this.valueParameters.map {
|
||||
|
||||
// TODO: there are clashes originating from ObjectiveC interop.
|
||||
@@ -128,7 +114,7 @@ object KonanMangler : KotlinManglerImpl() {
|
||||
}
|
||||
}
|
||||
|
||||
this.descriptor.annotations.findAnnotation(exportForCppRuntimeAnnotation)?.let {
|
||||
this.descriptor.annotations.findAnnotation(RuntimeNames.exportForCppRuntime)?.let {
|
||||
val name = getAnnotationValue(it) ?: this.name.asString()
|
||||
return name // no wrapping currently required
|
||||
}
|
||||
@@ -148,8 +134,6 @@ internal val IrClass.writableTypeInfoSymbolName: String
|
||||
return "ktypew:" + this.fqNameForIrSerialization.toString()
|
||||
}
|
||||
|
||||
internal val theUnitInstanceName = "kobj:kotlin.Unit"
|
||||
|
||||
internal val IrClass.objectInstanceFieldSymbolName: String
|
||||
get() {
|
||||
assert (this.isExported())
|
||||
@@ -194,21 +178,9 @@ internal fun RuntimeAware.getLlvmFunctionType(function: IrFunction): LLVMTypeRef
|
||||
return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray())
|
||||
}
|
||||
|
||||
internal fun RuntimeAware.getLlvmFunctionType(symbol: DataFlowIR.FunctionSymbol): LLVMTypeRef {
|
||||
val returnType = if (symbol.returnsUnit) voidType else getLLVMType(symbol.returnParameter.type)
|
||||
val paramTypes = ArrayList(symbol.parameters.map { getLLVMType(it.type) })
|
||||
if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr)
|
||||
|
||||
return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray())
|
||||
}
|
||||
|
||||
internal val IrClass.typeInfoHasVtableAttached: Boolean
|
||||
get() = !this.isAbstract() && !this.isExternalObjCClass()
|
||||
|
||||
internal fun ModuleDescriptor.privateFunctionSymbolName(index: Int, functionName: String?) = "private_functions_${name.asString()}_${functionName}_$index"
|
||||
|
||||
internal fun ModuleDescriptor.privateClassSymbolName(index: Int, className: String?) = "private_classes_${name.asString()}_${className}_$index"
|
||||
|
||||
internal val String.moduleConstructorName
|
||||
get() = "_Konan_init_${this}"
|
||||
|
||||
|
||||
+8
-5
@@ -260,7 +260,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
throw IllegalArgumentException("function $name already exists")
|
||||
}
|
||||
|
||||
val externalFunction = LLVMGetNamedFunction(otherModule, name)!!
|
||||
val externalFunction = LLVMGetNamedFunction(otherModule, name) ?:
|
||||
throw Error("function $name not found")
|
||||
|
||||
val functionType = getFunctionType(externalFunction)
|
||||
val function = LLVMAddFunction(llvmModule, name, functionType)!!
|
||||
@@ -418,13 +419,15 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
}
|
||||
|
||||
private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule)
|
||||
private fun importModelSpecificRtFunction(name: String) =
|
||||
importRtFunction(name + context.memoryModel.suffix)
|
||||
|
||||
private fun importRtGlobal(name: String) = importGlobal(name, runtime.llvmModule)
|
||||
|
||||
val allocInstanceFunction = importRtFunction("AllocInstance")
|
||||
val allocArrayFunction = importRtFunction("AllocArrayInstance")
|
||||
val initInstanceFunction = importRtFunction("InitInstance")
|
||||
val initSharedInstanceFunction = importRtFunction("InitSharedInstance")
|
||||
val allocInstanceFunction = importModelSpecificRtFunction("AllocInstance")
|
||||
val allocArrayFunction = importModelSpecificRtFunction("AllocArrayInstance")
|
||||
val initInstanceFunction = importModelSpecificRtFunction("InitInstance")
|
||||
val initSharedInstanceFunction = importModelSpecificRtFunction("InitSharedInstance")
|
||||
val updateHeapRefFunction = importRtFunction("UpdateHeapRef")
|
||||
val updateStackRefFunction = importRtFunction("UpdateStackRef")
|
||||
val updateReturnRefFunction = importRtFunction("UpdateReturnRef")
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlinx.cinterop.cValuesOf
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.TypedIntrinsic
|
||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.genObjCSelector
|
||||
import org.jetbrains.kotlin.backend.konan.reportCompilationError
|
||||
@@ -113,7 +113,7 @@ internal fun tryGetIntrinsicType(callSite: IrFunctionAccessExpression): Intrinsi
|
||||
|
||||
private fun getIntrinsicType(callSite: IrFunctionAccessExpression): IntrinsicType {
|
||||
val function = callSite.symbol.owner
|
||||
val annotation = function.descriptor.annotations.findAnnotation(TypedIntrinsic)!!
|
||||
val annotation = function.descriptor.annotations.findAnnotation(RuntimeNames.typedIntrinsicAnnotation)!!
|
||||
val value = annotation.allValueArguments.getValue(Name.identifier("kind")).value as String
|
||||
return IntrinsicType.valueOf(value)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ targetList.each { targetName ->
|
||||
dependsOn "${targetName}Launcher"
|
||||
dependsOn "${targetName}Debug"
|
||||
dependsOn "${targetName}Release"
|
||||
dependsOn "${targetName}Strict"
|
||||
dependsOn "${targetName}Relaxed"
|
||||
target targetName
|
||||
includeRuntime(delegate)
|
||||
linkerArgs project.file("../common/build/$targetName/hash.bc").path
|
||||
@@ -44,6 +46,20 @@ targetList.each { targetName ->
|
||||
target targetName
|
||||
includeRuntime(delegate)
|
||||
}
|
||||
|
||||
task ("${targetName}Strict", type: CompileCppToBitcode) {
|
||||
name "strict"
|
||||
srcRoot file('src/strict')
|
||||
target targetName
|
||||
includeRuntime(delegate)
|
||||
}
|
||||
|
||||
task ("${targetName}Relaxed", type: CompileCppToBitcode) {
|
||||
name "relaxed"
|
||||
srcRoot file('src/relaxed')
|
||||
target targetName
|
||||
includeRuntime(delegate)
|
||||
}
|
||||
}
|
||||
|
||||
task hostRuntime(dependsOn: "${hostName}Runtime")
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "KString.h"
|
||||
#include "Memory.h"
|
||||
#include "Natives.h"
|
||||
#include "Porting.h"
|
||||
#include "Runtime.h"
|
||||
#include "KString.h"
|
||||
#include "Types.h"
|
||||
|
||||
#ifdef KONAN_ANDROID
|
||||
@@ -212,7 +213,7 @@ extern "C" void RUNTIME_USED Konan_main(
|
||||
ANativeActivity* activity, void* savedState, size_t savedStateSize) {
|
||||
bool launchThread = activity->instance == nullptr;
|
||||
if (launchThread) {
|
||||
launcherState = (LauncherState*)calloc(sizeof(LauncherState), 1);
|
||||
launcherState = (LauncherState*)konan::calloc(sizeof(LauncherState), 1);
|
||||
launcherState->nativeActivityState = {activity, savedState, savedStateSize, nullptr};
|
||||
activity->instance = launcherState;
|
||||
activity->callbacks->onDestroy = onDestroy;
|
||||
|
||||
@@ -443,8 +443,7 @@ OBJ_GETTER(Kotlin_CharArray_copyOf, KConstRef thiz, KInt newSize) {
|
||||
if (newSize < 0) {
|
||||
ThrowIllegalArgumentException();
|
||||
}
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
array->type_info(), newSize, OBJ_RESULT)->array();
|
||||
ArrayHeader* result = AllocArrayInstance(array->type_info(), newSize, OBJ_RESULT)->array();
|
||||
KInt toCopy = array->count_ < newSize ? array->count_ : newSize;
|
||||
memcpy(
|
||||
PrimitiveArrayAddressOfElementAt<KChar>(result, 0),
|
||||
@@ -606,8 +605,7 @@ OBJ_GETTER(Kotlin_ImmutableBlob_toByteArray, KConstRef thiz, KInt startIndex, KI
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
KInt count = endIndex - startIndex;
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
theByteArrayTypeInfo, count, OBJ_RESULT)->array();
|
||||
ArrayHeader* result = AllocArrayInstance(theByteArrayTypeInfo, count, OBJ_RESULT)->array();
|
||||
memcpy(PrimitiveArrayAddressOfElementAt<KByte>(result, 0),
|
||||
PrimitiveArrayAddressOfElementAt<KByte>(array, startIndex),
|
||||
count);
|
||||
|
||||
+105
-112
@@ -728,6 +728,111 @@ void DisposeCString(char* cstring) {
|
||||
}
|
||||
|
||||
// String.kt
|
||||
OBJ_GETTER(Kotlin_String_replace, KString thiz, KChar oldChar, KChar newChar, KBoolean ignoreCase) {
|
||||
auto count = thiz->count_;
|
||||
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, count, OBJ_RESULT)->array();
|
||||
const KChar* thizRaw = CharArrayAddressOfElementAt(thiz, 0);
|
||||
KChar* resultRaw = CharArrayAddressOfElementAt(result, 0);
|
||||
if (ignoreCase) {
|
||||
KChar oldCharLower = towlower_Konan(oldChar);
|
||||
for (KInt index = 0; index < count; ++index) {
|
||||
KChar thizChar = *thizRaw++;
|
||||
*resultRaw++ = towlower_Konan(thizChar) == oldCharLower ? newChar : thizChar;
|
||||
}
|
||||
} else {
|
||||
for (KInt index = 0; index < count; ++index) {
|
||||
KChar thizChar = *thizRaw++;
|
||||
*resultRaw++ = thizChar == oldChar ? newChar : thizChar;
|
||||
}
|
||||
}
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_plusImpl, KString thiz, KString other) {
|
||||
RuntimeAssert(thiz != nullptr, "this cannot be null");
|
||||
RuntimeAssert(other != nullptr, "other cannot be null");
|
||||
RuntimeAssert(thiz->type_info() == theStringTypeInfo, "Must be a string");
|
||||
RuntimeAssert(other->type_info() == theStringTypeInfo, "Must be a string");
|
||||
KInt result_length = thiz->count_ + other->count_;
|
||||
if (result_length < thiz->count_ || result_length < other->count_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, result_length, OBJ_RESULT)->array();
|
||||
memcpy(
|
||||
CharArrayAddressOfElementAt(result, 0),
|
||||
CharArrayAddressOfElementAt(thiz, 0),
|
||||
thiz->count_ * sizeof(KChar));
|
||||
memcpy(
|
||||
CharArrayAddressOfElementAt(result, thiz->count_),
|
||||
CharArrayAddressOfElementAt(other, 0),
|
||||
other->count_ * sizeof(KChar));
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_toUpperCase, KString thiz) {
|
||||
auto count = thiz->count_;
|
||||
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, count, OBJ_RESULT)->array();
|
||||
const KChar* thizRaw = CharArrayAddressOfElementAt(thiz, 0);
|
||||
KChar* resultRaw = CharArrayAddressOfElementAt(result, 0);
|
||||
for (KInt index = 0; index < count; ++index) {
|
||||
*resultRaw++ = towupper_Konan(*thizRaw++);
|
||||
}
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_toLowerCase, KString thiz) {
|
||||
auto count = thiz->count_;
|
||||
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, count, OBJ_RESULT)->array();
|
||||
const KChar* thizRaw = CharArrayAddressOfElementAt(thiz, 0);
|
||||
KChar* resultRaw = CharArrayAddressOfElementAt(result, 0);
|
||||
for (KInt index = 0; index < count; ++index) {
|
||||
*resultRaw++ = towlower_Konan(*thizRaw++);
|
||||
}
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_fromCharArray, KConstRef thiz, KInt start, KInt size) {
|
||||
const ArrayHeader* array = thiz->array();
|
||||
RuntimeAssert(array->type_info() == theCharArrayTypeInfo, "Must use a char array");
|
||||
if (start < 0 || size < 0 || size > array->count_ - start) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
RETURN_RESULT_OF0(TheEmptyString);
|
||||
}
|
||||
|
||||
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, size, OBJ_RESULT)->array();
|
||||
memcpy(CharArrayAddressOfElementAt(result, 0),
|
||||
CharArrayAddressOfElementAt(array, start),
|
||||
size * sizeof(KChar));
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_toCharArray, KString string, KInt start, KInt size) {
|
||||
ArrayHeader* result = AllocArrayInstance(theCharArrayTypeInfo, size, OBJ_RESULT)->array();
|
||||
memcpy(CharArrayAddressOfElementAt(result, 0),
|
||||
CharArrayAddressOfElementAt(string, start),
|
||||
size * sizeof(KChar));
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_subSequence, KString thiz, KInt startIndex, KInt endIndex) {
|
||||
if (startIndex < 0 || endIndex > thiz->count_ || startIndex > endIndex) {
|
||||
// TODO: is it correct exception?
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
if (startIndex == endIndex) {
|
||||
RETURN_RESULT_OF0(TheEmptyString);
|
||||
}
|
||||
KInt length = endIndex - startIndex;
|
||||
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, length, OBJ_RESULT)->array();
|
||||
memcpy(CharArrayAddressOfElementAt(result, 0),
|
||||
CharArrayAddressOfElementAt(thiz, startIndex),
|
||||
length * sizeof(KChar));
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
KInt Kotlin_String_compareTo(KString thiz, KString other) {
|
||||
int result = memcmp(
|
||||
CharArrayAddressOfElementAt(thiz, 0),
|
||||
@@ -806,56 +911,6 @@ OBJ_GETTER(Kotlin_String_toUtf8OrThrow, KString thiz, KInt start, KInt size) {
|
||||
RETURN_RESULT_OF(utf16ToUtf8Impl<utf16toUtf8OrThrow>, thiz, start, size);
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_fromCharArray, KConstRef thiz, KInt start, KInt size) {
|
||||
const ArrayHeader* array = thiz->array();
|
||||
RuntimeAssert(array->type_info() == theCharArrayTypeInfo, "Must use a char array");
|
||||
if (start < 0 || size < 0 || size > array->count_ - start) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
RETURN_RESULT_OF0(TheEmptyString);
|
||||
}
|
||||
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
theStringTypeInfo, size, OBJ_RESULT)->array();
|
||||
memcpy(CharArrayAddressOfElementAt(result, 0),
|
||||
CharArrayAddressOfElementAt(array, start),
|
||||
size * sizeof(KChar));
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_toCharArray, KString string, KInt start, KInt size) {
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
theCharArrayTypeInfo, size, OBJ_RESULT)->array();
|
||||
memcpy(CharArrayAddressOfElementAt(result, 0),
|
||||
CharArrayAddressOfElementAt(string, start),
|
||||
size * sizeof(KChar));
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_plusImpl, KString thiz, KString other) {
|
||||
RuntimeAssert(thiz != nullptr, "this cannot be null");
|
||||
RuntimeAssert(other != nullptr, "other cannot be null");
|
||||
RuntimeAssert(thiz->type_info() == theStringTypeInfo, "Must be a string");
|
||||
RuntimeAssert(other->type_info() == theStringTypeInfo, "Must be a string");
|
||||
KInt result_length = thiz->count_ + other->count_;
|
||||
if (result_length < thiz->count_ || result_length < other->count_) {
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
theStringTypeInfo, result_length, OBJ_RESULT)->array();
|
||||
memcpy(
|
||||
CharArrayAddressOfElementAt(result, 0),
|
||||
CharArrayAddressOfElementAt(thiz, 0),
|
||||
thiz->count_ * sizeof(KChar));
|
||||
memcpy(
|
||||
CharArrayAddressOfElementAt(result, thiz->count_),
|
||||
CharArrayAddressOfElementAt(other, 0),
|
||||
other->count_ * sizeof(KChar));
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
KInt Kotlin_StringBuilder_insertString(KRef builder, KInt position, KString fromString) {
|
||||
auto toArray = builder->array();
|
||||
RuntimeAssert(toArray->count_ >= fromString->count_ + position, "must be true");
|
||||
@@ -907,52 +962,6 @@ KBoolean Kotlin_String_equalsIgnoreCase(KString thiz, KConstRef other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_replace, KString thiz, KChar oldChar, KChar newChar,
|
||||
KBoolean ignoreCase) {
|
||||
auto count = thiz->count_;
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
theStringTypeInfo, count, OBJ_RESULT)->array();
|
||||
const KChar* thizRaw = CharArrayAddressOfElementAt(thiz, 0);
|
||||
KChar* resultRaw = CharArrayAddressOfElementAt(result, 0);
|
||||
if (ignoreCase) {
|
||||
KChar oldCharLower = towlower_Konan(oldChar);
|
||||
for (KInt index = 0; index < count; ++index) {
|
||||
KChar thizChar = *thizRaw++;
|
||||
*resultRaw++ = towlower_Konan(thizChar) == oldCharLower ? newChar : thizChar;
|
||||
}
|
||||
} else {
|
||||
for (KInt index = 0; index < count; ++index) {
|
||||
KChar thizChar = *thizRaw++;
|
||||
*resultRaw++ = thizChar == oldChar ? newChar : thizChar;
|
||||
}
|
||||
}
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_toUpperCase, KString thiz) {
|
||||
auto count = thiz->count_;
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
theStringTypeInfo, count, OBJ_RESULT)->array();
|
||||
const KChar* thizRaw = CharArrayAddressOfElementAt(thiz, 0);
|
||||
KChar* resultRaw = CharArrayAddressOfElementAt(result, 0);
|
||||
for (KInt index = 0; index < count; ++index) {
|
||||
*resultRaw++ = towupper_Konan(*thizRaw++);
|
||||
}
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_toLowerCase, KString thiz) {
|
||||
auto count = thiz->count_;
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
theStringTypeInfo, count, OBJ_RESULT)->array();
|
||||
const KChar* thizRaw = CharArrayAddressOfElementAt(thiz, 0);
|
||||
KChar* resultRaw = CharArrayAddressOfElementAt(result, 0);
|
||||
for (KInt index = 0; index < count; ++index) {
|
||||
*resultRaw++ = towlower_Konan(*thizRaw++);
|
||||
}
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
KBoolean Kotlin_String_regionMatches(KString thiz, KInt thizOffset,
|
||||
KString other, KInt otherOffset,
|
||||
KInt length, KBoolean ignoreCase) {
|
||||
@@ -1161,22 +1170,6 @@ KInt Kotlin_String_hashCode(KString thiz) {
|
||||
CharArrayAddressOfElementAt(thiz, 0), thiz->count_ * sizeof(KChar));
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_subSequence, KString thiz, KInt startIndex, KInt endIndex) {
|
||||
if (startIndex < 0 || endIndex > thiz->count_ || startIndex > endIndex) {
|
||||
// TODO: is it correct exception?
|
||||
ThrowArrayIndexOutOfBoundsException();
|
||||
}
|
||||
if (startIndex == endIndex) {
|
||||
RETURN_RESULT_OF0(TheEmptyString);
|
||||
}
|
||||
KInt length = endIndex - startIndex;
|
||||
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, length, OBJ_RESULT)->array();
|
||||
memcpy(CharArrayAddressOfElementAt(result, 0),
|
||||
CharArrayAddressOfElementAt(thiz, startIndex),
|
||||
length * sizeof(KChar));
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
const KChar* Kotlin_String_utf16pointer(KString message) {
|
||||
RuntimeAssert(message->type_info() == theStringTypeInfo, "Must use a string");
|
||||
const KChar* utf16 = CharArrayAddressOfElementAt(message, 0);
|
||||
|
||||
+720
-526
File diff suppressed because it is too large
Load Diff
@@ -426,11 +426,30 @@ void ResumeMemory(MemoryState* state);
|
||||
// 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(AllocInstanceStrict, const TypeInfo* type_info) RUNTIME_NOTHROW;
|
||||
OBJ_GETTER(AllocInstanceRelaxed, const TypeInfo* type_info) RUNTIME_NOTHROW;
|
||||
OBJ_GETTER(AllocInstance, 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(AllocArrayInstance, 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(InitInstance,
|
||||
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
OBJ_GETTER(InitSharedInstanceStrict,
|
||||
ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
OBJ_GETTER(InitSharedInstanceRelaxed,
|
||||
ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
OBJ_GETTER(InitSharedInstance,
|
||||
ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
|
||||
|
||||
// Cleanup references inside object.
|
||||
void DeinitInstanceBody(const TypeInfo* typeInfo, void* body);
|
||||
OBJ_GETTER(InitInstance, ObjHeader** location, const TypeInfo* type_info,
|
||||
void (*ctor)(ObjHeader*));
|
||||
|
||||
// Weak reference operations.
|
||||
// Atomically clears counter object reference.
|
||||
|
||||
@@ -84,72 +84,10 @@ inline const KRef* ArrayAddressOfElementAt(const ArrayHeader* obj, KInt index) {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// RuntimeUtils.kt.
|
||||
OBJ_GETTER0(TheEmptyString);
|
||||
|
||||
// Any.kt
|
||||
KBoolean Kotlin_Any_equals(KConstRef thiz, KConstRef other);
|
||||
KInt Kotlin_Any_hashCode(KConstRef thiz);
|
||||
OBJ_GETTER(Kotlin_Any_toString, KConstRef thiz);
|
||||
|
||||
// Arrays.kt
|
||||
// TODO: those must be compiler intrinsics afterwards.
|
||||
OBJ_GETTER(Kotlin_Array_clone, KConstRef thiz);
|
||||
OBJ_GETTER(Kotlin_Array_get, KConstRef thiz, KInt index);
|
||||
void Kotlin_Array_set(KRef thiz, KInt index, KConstRef value);
|
||||
KInt Kotlin_Array_getArrayLength(KConstRef thiz);
|
||||
|
||||
OBJ_GETTER(Kotlin_ByteArray_clone, KConstRef thiz);
|
||||
KByte Kotlin_ByteArray_get(KConstRef thiz, KInt index);
|
||||
void Kotlin_ByteArray_set(KRef thiz, KInt index, KByte value);
|
||||
KInt Kotlin_ByteArray_getArrayLength(KConstRef thiz);
|
||||
|
||||
OBJ_GETTER(Kotlin_CharArray_clone, KConstRef thiz);
|
||||
KChar Kotlin_CharArray_get(KConstRef thiz, KInt index);
|
||||
void Kotlin_CharArray_set(KRef thiz, KInt index, KChar value);
|
||||
KInt Kotlin_CharArray_getArrayLength(KConstRef thiz);
|
||||
|
||||
OBJ_GETTER(Kotlin_IntArray_clone, KConstRef thiz);
|
||||
KInt Kotlin_IntArray_get(KConstRef thiz, KInt index);
|
||||
void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value);
|
||||
KInt Kotlin_IntArray_getArrayLength(KConstRef thiz);
|
||||
|
||||
KLong Kotlin_LongArray_get(KConstRef thiz, KInt index);
|
||||
void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value);
|
||||
|
||||
KNativePtr Kotlin_NativePtrArray_get(KConstRef thiz, KInt index);
|
||||
void Kotlin_NativePtrArray_set(KRef thiz, KInt index, KNativePtr value);
|
||||
KInt Kotlin_NativePtrArray_getArrayLength(KConstRef thiz);
|
||||
|
||||
// io/Console.kt
|
||||
void Kotlin_io_Console_print(KString message);
|
||||
void Kotlin_io_Console_println(KString message);
|
||||
void Kotlin_io_Console_println0();
|
||||
OBJ_GETTER0(Kotlin_io_Console_readLine);
|
||||
|
||||
// Primitives.kt.
|
||||
OBJ_GETTER(Kotlin_Int_toString, KInt value);
|
||||
|
||||
// String.kt
|
||||
KInt Kotlin_String_hashCode(KString thiz);
|
||||
KBoolean Kotlin_String_equals(KString thiz, KConstRef other);
|
||||
KInt Kotlin_String_compareTo(KString thiz, KString other);
|
||||
KInt Kotlin_String_compareToIgnoreCase(KString thiz, KConstRef other);
|
||||
KChar Kotlin_String_get(KString thiz, KInt index);
|
||||
OBJ_GETTER(Kotlin_String_fromUtf8Array, KConstRef array, KInt start, KInt size);
|
||||
OBJ_GETTER(Kotlin_String_fromCharArray, KConstRef array, KInt start, KInt size);
|
||||
OBJ_GETTER(Kotlin_String_plusImpl, KString thiz, KString other);
|
||||
KInt Kotlin_String_getStringLength(KString thiz);
|
||||
OBJ_GETTER(Kotlin_String_subSequence, KString thiz, KInt startIndex, KInt endIndex);
|
||||
|
||||
OBJ_GETTER0(Kotlin_getCurrentStackTrace);
|
||||
|
||||
OBJ_GETTER(Kotlin_getStackTraceStrings, KConstRef stackTrace);
|
||||
|
||||
OBJ_GETTER0(Kotlin_native_internal_undefined);
|
||||
|
||||
void Kotlin_native_internal_GC_suspend(KRef);
|
||||
void Kotlin_native_internal_GC_resume(KRef);
|
||||
void Kotlin_NativePtrArray_set(KRef thiz, KInt index, KNativePtr value);
|
||||
KNativePtr Kotlin_NativePtrArray_get(KConstRef thiz, KInt index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ extern "C" id Kotlin_ObjCExport_GetAssociatedObject(ObjHeader* obj) {
|
||||
}
|
||||
|
||||
inline static OBJ_GETTER(AllocInstanceWithAssociatedObject, const TypeInfo* typeInfo, id associatedObject) {
|
||||
// TODO: memory model!
|
||||
ObjHeader* result = AllocInstance(typeInfo, OBJ_RESULT);
|
||||
SetAssociatedObject(result, associatedObject);
|
||||
return result;
|
||||
|
||||
@@ -548,6 +548,14 @@ KInt getCanonicalClass(KInt ch) {
|
||||
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" {
|
||||
@@ -561,14 +569,6 @@ KBoolean Kotlin_text_regex_hasSingleCodepointDecompositionInternal(KInt ch) {
|
||||
return singleDecompositions[index] == ch;
|
||||
}
|
||||
|
||||
const Decomposition* getDecomposition(KInt codePoint) {
|
||||
int index = binarySearchRange(decompositionKeys, ARRAY_SIZE(decompositionKeys), codePoint);
|
||||
if (decompositionKeys[index] != codePoint) {
|
||||
return nullptr;
|
||||
}
|
||||
return &decompositionValues[index];
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_text_regex_getDecompositionInternal, KInt ch) {
|
||||
const Decomposition* decomposition = getDecomposition(ch);
|
||||
if (decomposition == nullptr) {
|
||||
|
||||
@@ -76,8 +76,7 @@ OBJ_GETTER(Kotlin_Byte_toString, KByte value) {
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_Char_toString, KChar value) {
|
||||
ArrayHeader* result = AllocArrayInstance(
|
||||
theStringTypeInfo, 1, OBJ_RESULT)->array();
|
||||
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, 1, OBJ_RESULT)->array();
|
||||
*CharArrayAddressOfElementAt(result, 0) = value;
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public final class String : Comparable<String>, CharSequence {
|
||||
external private fun getStringLength(): Int
|
||||
|
||||
@SymbolName("Kotlin_String_plusImpl")
|
||||
external private fun plusImpl(other: Any): String
|
||||
external private fun plusImpl(other: String): String
|
||||
|
||||
@SymbolName("Kotlin_String_equals")
|
||||
external public override fun equals(other: Any?): Boolean
|
||||
|
||||
@@ -55,7 +55,6 @@ internal external fun stringEqualsIgnoreCase(thiz: String, other: String): Boole
|
||||
public actual external fun String.replace(
|
||||
oldChar: Char, newChar: Char, ignoreCase: Boolean): String
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new string obtained by replacing all occurrences of the [oldValue] substring in this string
|
||||
* with the specified [newValue] string.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 "Memory.h"
|
||||
|
||||
// Note that only C++ part of the runtime goes via those functions, Kotlin uses specialized versions.
|
||||
|
||||
extern "C" {
|
||||
|
||||
OBJ_GETTER(AllocInstance, const TypeInfo* typeInfo) {
|
||||
RETURN_RESULT_OF(AllocInstanceRelaxed, typeInfo);
|
||||
}
|
||||
|
||||
OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
|
||||
RETURN_RESULT_OF(AllocArrayInstanceRelaxed, typeInfo, elements);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitInstance,
|
||||
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitInstanceRelaxed, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSharedInstance,
|
||||
ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitSharedInstanceRelaxed, location, localLocation, typeInfo, ctor);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 "Memory.h"
|
||||
|
||||
// Note that only C++ part of the runtime goes via those functions, Kotlin uses specialized versions.
|
||||
|
||||
extern "C" {
|
||||
|
||||
OBJ_GETTER(AllocInstance, const TypeInfo* typeInfo) {
|
||||
RETURN_RESULT_OF(AllocInstanceStrict, typeInfo);
|
||||
}
|
||||
|
||||
OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
|
||||
RETURN_RESULT_OF(AllocArrayInstanceStrict, typeInfo, elements);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitInstance,
|
||||
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitInstanceStrict, location, typeInfo, ctor);
|
||||
}
|
||||
|
||||
OBJ_GETTER(InitSharedInstance,
|
||||
ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
|
||||
RETURN_RESULT_OF(InitSharedInstanceStrict, location, localLocation, typeInfo, ctor);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
Reference in New Issue
Block a user