diff --git a/CHANGELOG.md b/CHANGELOG.md index cb0ffe557..61b57907a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +6.3.1 +== + +## Bug Fixes + +- [Kotlin enum values not visible at runtime (#1560)](https://github.com/NativeScript/android-runtime/issues/1560) +- [Code cache breaks HMR (#1554)](https://github.com/NativeScript/android-runtime/issues/1554) +- [Worker memory leak in android (#1550)](https://github.com/NativeScript/android-runtime/issues/1550) + 6.3.0 == diff --git a/package.json b/package.json index 178af4c37..d9b7b3118 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tns-android", "description": "NativeScript Runtime for Android", - "version": "6.3.0", + "version": "6.3.1", "repository": { "type": "git", "url": "https://github.com/NativeScript/android-runtime.git" diff --git a/test-app/app/src/main/assets/app/mainpage.js b/test-app/app/src/main/assets/app/mainpage.js index d4adf51d4..dfa8d5806 100644 --- a/test-app/app/src/main/assets/app/mainpage.js +++ b/test-app/app/src/main/assets/app/mainpage.js @@ -61,4 +61,5 @@ require("./tests/kotlin/delegation/testDelegationSupport"); require("./tests/kotlin/objects/testObjectsSupport"); require("./tests/kotlin/functions/testTopLevelFunctionsSupport"); require("./tests/kotlin/extensions/testExtensionFunctionsSupport"); +require("./tests/kotlin/enums/testEnumsSupport"); require("./tests/kotlin/access/testInternalLanguageFeaturesSupport"); \ No newline at end of file diff --git a/test-app/app/src/main/assets/app/tests/kotlin/enums/testEnumsSupport.js b/test-app/app/src/main/assets/app/tests/kotlin/enums/testEnumsSupport.js new file mode 100644 index 000000000..3ff0bd04d --- /dev/null +++ b/test-app/app/src/main/assets/app/tests/kotlin/enums/testEnumsSupport.js @@ -0,0 +1,10 @@ +describe("Tests Kotlin extension functions support", function () { + + it("Test Kotlin enum entries are accessible", function () { + var enumEntry = com.tns.tests.kotlin.enums.KotlinEnum.TEST_ENTRY; + + expect(enumEntry).not.toBe(undefined); + expect(enumEntry).not.toBe(null); + }); + +}); \ No newline at end of file diff --git a/test-app/app/src/main/java/com/tns/tests/kotlin/enums/KotlinEnum.kt b/test-app/app/src/main/java/com/tns/tests/kotlin/enums/KotlinEnum.kt new file mode 100644 index 000000000..4503bc553 --- /dev/null +++ b/test-app/app/src/main/java/com/tns/tests/kotlin/enums/KotlinEnum.kt @@ -0,0 +1,5 @@ +package com.tns.tests.kotlin.enums + +enum class KotlinEnum { + TEST_ENTRY +} \ No newline at end of file diff --git a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/classes/KotlinClassDescriptor.kt b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/classes/KotlinClassDescriptor.kt index 86289580c..eb42a1814 100644 --- a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/classes/KotlinClassDescriptor.kt +++ b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/classes/KotlinClassDescriptor.kt @@ -7,6 +7,7 @@ import com.telerik.metadata.parsing.NativeMethodDescriptor import com.telerik.metadata.parsing.NativePropertyDescriptor import com.telerik.metadata.parsing.bytecode.classes.NativeClassBytecodeDescriptor import com.telerik.metadata.parsing.kotlin.fields.KotlinCompanionFieldDescriptor +import com.telerik.metadata.parsing.kotlin.fields.KotlinEnumFieldDescriptor import com.telerik.metadata.parsing.kotlin.fields.KotlinJvmFieldDescriptor import com.telerik.metadata.parsing.kotlin.metadata.MetadataAnnotation import com.telerik.metadata.parsing.kotlin.metadata.bytecode.BytecodeClassMetadataParser @@ -36,7 +37,6 @@ class KotlinClassDescriptor(nativeClass: JavaClass, private val metadataAnnotati override val fields: Array by lazy { val fields = ArrayList() - val meta = kotlinMetadata var kotlinMetadataProperties: Collection = emptyList() @@ -48,6 +48,12 @@ class KotlinClassDescriptor(nativeClass: JavaClass, private val metadataAnnotati if (possibleCompanionField.isPresent) { fields.add(possibleCompanionField.get()) } + + if(metaClass.enumEntries.isNotEmpty()){ + val enumFields = getEnumEntriesAsFields(nativeClass, metaClass.enumEntries) + fields.addAll(enumFields) + } + } else if (meta is KotlinClassMetadata.FileFacade) { kotlinMetadataProperties = meta.toKmPackage().properties } else if (meta is KotlinClassMetadata.MultiFileClassPart) { @@ -107,6 +113,20 @@ class KotlinClassDescriptor(nativeClass: JavaClass, private val metadataAnnotati return Optional.empty() } + private fun getEnumEntriesAsFields(nativeClass: JavaClass, metadataEnumEntries: Collection): Collection { + val bytecodeFields = nativeClass.fields + + val matchingEnumFields = bytecodeFields + .filter { + metadataEnumEntries.contains(it.name) + } + .map { + KotlinEnumFieldDescriptor(it, isPublic, isInternal, isProtected) + } + + return matchingEnumFields + } + override val properties: Array by lazy { val metadata = kotlinMetadata if (metadata is KotlinClassMetadata.Class) { diff --git a/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/fields/KotlinEnumFieldDescriptor.kt b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/fields/KotlinEnumFieldDescriptor.kt new file mode 100644 index 000000000..1fab52697 --- /dev/null +++ b/test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/kotlin/fields/KotlinEnumFieldDescriptor.kt @@ -0,0 +1,9 @@ +package com.telerik.metadata.parsing.kotlin.fields + +import com.telerik.metadata.parsing.bytecode.fields.NativeFieldBytecodeDescriptor +import org.apache.bcel.classfile.Field + +class KotlinEnumFieldDescriptor(field: Field, + override val isPublic: Boolean, + override val isInternal: Boolean, + override val isProtected: Boolean) : NativeFieldBytecodeDescriptor(field) \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/JSONObjectHelper.cpp b/test-app/runtime/src/main/cpp/JSONObjectHelper.cpp index 9ca78d5a2..a415e0c0a 100644 --- a/test-app/runtime/src/main/cpp/JSONObjectHelper.cpp +++ b/test-app/runtime/src/main/cpp/JSONObjectHelper.cpp @@ -7,33 +7,54 @@ using namespace v8; using namespace tns; -JSONObjectHelper::JSONObjectHelper() - : m_objectManager(nullptr), m_serializeFunc(nullptr) { -} - -void JSONObjectHelper::CreateConvertFunctions(Isolate *isolate, const Local &global, ObjectManager* objectManager) { - m_objectManager = objectManager; +void JSONObjectHelper::RegisterFromFunction(Isolate *isolate, Local& jsonObject) { + if (!jsonObject->IsFunction()) { + return; + } - m_serializeFunc = new Persistent(isolate, CreateSerializeFunc(isolate)); + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); Local context = isolate->GetCurrentContext(); + Context::Scope context_scope(context); - Local extData = External::New(isolate, this); - Local fromFunc = FunctionTemplate::New(isolate, ConvertCallbackStatic, extData)->GetFunction(context).ToLocalChecked(); - - Local jsonObjectFunc = global->Get(context, ArgConverter::ConvertToV8String(isolate, "org")) - .ToLocalChecked().As()->Get(context, ArgConverter::ConvertToV8String(isolate, "json")) - .ToLocalChecked().As()->Get(context, ArgConverter::ConvertToV8String(isolate, "JSONObject")) - .ToLocalChecked().As(); + Local jsonObjectFunc = jsonObject.As(); + auto fromKey = ArgConverter::ConvertToV8String(isolate, "from"); + if (jsonObjectFunc->Has(context, fromKey).FromMaybe(false)) { + return; + } - jsonObjectFunc->Set(context, ArgConverter::ConvertToV8String(isolate, "from"), fromFunc); + Persistent* serializeFunc = new Persistent(isolate, CreateSerializeFunc(context)); + Local extData = External::New(isolate, serializeFunc); + Local fromFunc; + bool ok = FunctionTemplate::New(isolate, ConvertCallbackStatic, extData)->GetFunction(context).ToLocal(&fromFunc); + assert(ok); + jsonObjectFunc->Set(context, fromKey, fromFunc); } void JSONObjectHelper::ConvertCallbackStatic(const FunctionCallbackInfo& info) { try { Local extData = info.Data().As(); - auto thiz = reinterpret_cast(extData->Value()); - thiz->ConvertCallback(info); + auto poSerializeFunc = reinterpret_cast*>(extData->Value()); + Isolate* isolate = info.GetIsolate(); + Local serializeFunc = poSerializeFunc->Get(isolate); + + if (info.Length() < 1) { + NativeScriptException nsEx(std::string("The \"from\" function expects one parameter")); + nsEx.ReThrowToV8(); + return; + } + + Local context = isolate->GetCurrentContext(); + + Local args[] = { info[0] }; + Local result; + TryCatch tc(isolate); + if (!serializeFunc->Call(context, Undefined(isolate), 1, args).ToLocal(&result)) { + throw NativeScriptException(tc, "Error serializing to JSONObject"); + } + + info.GetReturnValue().Set(result); } catch (NativeScriptException& e) { e.ReThrowToV8(); } catch (std::exception e) { @@ -47,28 +68,7 @@ void JSONObjectHelper::ConvertCallbackStatic(const FunctionCallbackInfo& } } -void JSONObjectHelper::ConvertCallback(const FunctionCallbackInfo& info) { - if (info.Length() < 1) { - NativeScriptException nsEx(std::string("The \"from\" function expects one parameter")); - nsEx.ReThrowToV8(); - return; - } - - Isolate* isolate = info.GetIsolate(); - Local context = isolate->GetCurrentContext(); - - Local serializeFunc = m_serializeFunc->Get(isolate); - Local args[] = { info[0] }; - Local result; - TryCatch tc(isolate); - if (!serializeFunc->Call(context, Undefined(isolate), 1, args).ToLocal(&result)) { - throw NativeScriptException(tc, "Error serializing to JSONObject"); - } - - info.GetReturnValue().Set(result); -} - -Local JSONObjectHelper::CreateSerializeFunc(Isolate* isolate) { +Local JSONObjectHelper::CreateSerializeFunc(Local context) { std::string source = "(() => function serialize(data) {" " let store;" @@ -102,7 +102,7 @@ Local JSONObjectHelper::CreateSerializeFunc(Isolate* isolate) { " }" "})()"; - Local context = isolate->GetCurrentContext(); + Isolate* isolate = context->GetIsolate(); Local