Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
==

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
1 change: 1 addition & 0 deletions test-app/app/src/main/assets/app/mainpage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Original file line number Diff line number Diff line change
@@ -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);
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.tns.tests.kotlin.enums

enum class KotlinEnum {
TEST_ENTRY
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -36,7 +37,6 @@ class KotlinClassDescriptor(nativeClass: JavaClass, private val metadataAnnotati

override val fields: Array<NativeFieldDescriptor> by lazy {
val fields = ArrayList<NativeFieldDescriptor>()

val meta = kotlinMetadata
var kotlinMetadataProperties: Collection<KmProperty> = emptyList()

Expand All @@ -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) {
Expand Down Expand Up @@ -107,6 +113,20 @@ class KotlinClassDescriptor(nativeClass: JavaClass, private val metadataAnnotati
return Optional.empty()
}

private fun getEnumEntriesAsFields(nativeClass: JavaClass, metadataEnumEntries: Collection<String>): Collection<KotlinEnumFieldDescriptor> {
val bytecodeFields = nativeClass.fields

val matchingEnumFields = bytecodeFields
.filter {
metadataEnumEntries.contains(it.name)
}
.map {
KotlinEnumFieldDescriptor(it, isPublic, isInternal, isProtected)
}

return matchingEnumFields
}

override val properties: Array<out NativePropertyDescriptor> by lazy {
val metadata = kotlinMetadata
if (metadata is KotlinClassMetadata.Class) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
80 changes: 40 additions & 40 deletions test-app/runtime/src/main/cpp/JSONObjectHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Object> &global, ObjectManager* objectManager) {
m_objectManager = objectManager;
void JSONObjectHelper::RegisterFromFunction(Isolate *isolate, Local<Value>& jsonObject) {
if (!jsonObject->IsFunction()) {
return;
}

m_serializeFunc = new Persistent<Function>(isolate, CreateSerializeFunc(isolate));
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);

Local<Context> context = isolate->GetCurrentContext();
Context::Scope context_scope(context);

Local<External> extData = External::New(isolate, this);
Local<Function> fromFunc = FunctionTemplate::New(isolate, ConvertCallbackStatic, extData)->GetFunction(context).ToLocalChecked();

Local<Function> jsonObjectFunc = global->Get(context, ArgConverter::ConvertToV8String(isolate, "org"))
.ToLocalChecked().As<Object>()->Get(context, ArgConverter::ConvertToV8String(isolate, "json"))
.ToLocalChecked().As<Object>()->Get(context, ArgConverter::ConvertToV8String(isolate, "JSONObject"))
.ToLocalChecked().As<Function>();
Local<Function> jsonObjectFunc = jsonObject.As<Function>();
auto fromKey = ArgConverter::ConvertToV8String(isolate, "from");
if (jsonObjectFunc->Has(context, fromKey).FromMaybe(false)) {
return;
}

jsonObjectFunc->Set(context, ArgConverter::ConvertToV8String(isolate, "from"), fromFunc);
Persistent<Function>* serializeFunc = new Persistent<Function>(isolate, CreateSerializeFunc(context));
Local<External> extData = External::New(isolate, serializeFunc);
Local<Function> fromFunc;
bool ok = FunctionTemplate::New(isolate, ConvertCallbackStatic, extData)->GetFunction(context).ToLocal(&fromFunc);
assert(ok);
jsonObjectFunc->Set(context, fromKey, fromFunc);
}

void JSONObjectHelper::ConvertCallbackStatic(const FunctionCallbackInfo<Value>& info) {
try {
Local<External> extData = info.Data().As<External>();
auto thiz = reinterpret_cast<JSONObjectHelper*>(extData->Value());
thiz->ConvertCallback(info);
auto poSerializeFunc = reinterpret_cast<Persistent<Function>*>(extData->Value());
Isolate* isolate = info.GetIsolate();
Local<Function> serializeFunc = poSerializeFunc->Get(isolate);

if (info.Length() < 1) {
NativeScriptException nsEx(std::string("The \"from\" function expects one parameter"));
nsEx.ReThrowToV8();
return;
}

Local<Context> context = isolate->GetCurrentContext();

Local<Value> args[] = { info[0] };
Local<Value> 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) {
Expand All @@ -47,28 +68,7 @@ void JSONObjectHelper::ConvertCallbackStatic(const FunctionCallbackInfo<Value>&
}
}

void JSONObjectHelper::ConvertCallback(const FunctionCallbackInfo<Value>& info) {
if (info.Length() < 1) {
NativeScriptException nsEx(std::string("The \"from\" function expects one parameter"));
nsEx.ReThrowToV8();
return;
}

Isolate* isolate = info.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();

Local<Function> serializeFunc = m_serializeFunc->Get(isolate);
Local<Value> args[] = { info[0] };
Local<Value> 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<Function> JSONObjectHelper::CreateSerializeFunc(Isolate* isolate) {
Local<Function> JSONObjectHelper::CreateSerializeFunc(Local<Context> context) {
std::string source =
"(() => function serialize(data) {"
" let store;"
Expand Down Expand Up @@ -102,7 +102,7 @@ Local<Function> JSONObjectHelper::CreateSerializeFunc(Isolate* isolate) {
" }"
"})()";

Local<Context> context = isolate->GetCurrentContext();
Isolate* isolate = context->GetIsolate();

Local<Script> script = Script::Compile(context, ArgConverter::ConvertToV8String(isolate, source)).ToLocalChecked();

Expand Down
10 changes: 2 additions & 8 deletions test-app/runtime/src/main/cpp/JSONObjectHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,14 @@
#define JSONOBJECTHELPER_H_

#include "v8.h"
#include "ObjectManager.h"

namespace tns {

class JSONObjectHelper {
public:
JSONObjectHelper();
void CreateConvertFunctions(v8::Isolate* isolate, const v8::Local<v8::Object>& global, ObjectManager* objectManager);
static void RegisterFromFunction(v8::Isolate *isolate, v8::Local<v8::Value>& jsonObject);
private:
ObjectManager* m_objectManager;
v8::Persistent<v8::Function>* m_serializeFunc;

v8::Local<v8::Function> CreateSerializeFunc(v8::Isolate* isolate);
void ConvertCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
static v8::Local<v8::Function> CreateSerializeFunc(v8::Local<v8::Context> context);
static void ConvertCallbackStatic(const v8::FunctionCallbackInfo<v8::Value>& info);
};

Expand Down
5 changes: 5 additions & 0 deletions test-app/runtime/src/main/cpp/MetadataNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cctype>
#include <dirent.h>
#include "ManualInstrumentation.h"
#include "JSONObjectHelper.h"

#include "v8.h"

Expand Down Expand Up @@ -1367,6 +1368,10 @@ void MetadataNode::PackageGetterCallback(Local<Name> property, const PropertyCal
}

V8SetPrivateValue(isolate, thiz, strProperty, cachedItem);

if (node->m_name == "org/json" && child.name == "JSONObject") {
JSONObjectHelper::RegisterFromFunction(isolate, cachedItem);
}
}
}

Expand Down
23 changes: 23 additions & 0 deletions test-app/runtime/src/main/cpp/ModuleInternal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <mutex>
#include <libgen.h>
#include <dlfcn.h>
#include <sys/stat.h>

using namespace v8;
using namespace std;
Expand All @@ -31,6 +32,15 @@ ModuleInternal::ModuleInternal()
: m_isolate(nullptr), m_requireFunction(nullptr), m_requireFactoryFunction(nullptr) {
}

ModuleInternal::~ModuleInternal() {
delete this->m_requireFunction;
delete this->m_requireFactoryFunction;
for (const auto pair: this->m_requireCache) {
delete pair.second;
}
this->m_requireCache.clear();
}

void ModuleInternal::Init(Isolate* isolate, const string& baseDir) {
JEnv env;

Expand Down Expand Up @@ -451,6 +461,19 @@ ScriptCompiler::CachedData* ModuleInternal::TryLoadScriptCache(const std::string
}

auto cachePath = path + ".cache";

struct stat result;
if (stat(cachePath.c_str(), &result) == 0) {
auto cacheLastModifiedTime = result.st_mtime;
if (stat(path.c_str(), &result) == 0) {
auto jsLastModifiedTime = result.st_mtime;
if (jsLastModifiedTime > 0 && cacheLastModifiedTime > 0 && jsLastModifiedTime > cacheLastModifiedTime) {
// The javascript file is more recent than the cache file => ignore the cache
return nullptr;
}
}
}

int length = 0;
auto data = File::ReadBinary(cachePath, length);
if (!data) {
Expand Down
2 changes: 2 additions & 0 deletions test-app/runtime/src/main/cpp/ModuleInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class ModuleInternal {
public:
ModuleInternal();

~ModuleInternal();

void Init(v8::Isolate* isolate, const std::string& baseDir = "");

void Load(const std::string& path);
Expand Down
7 changes: 6 additions & 1 deletion test-app/runtime/src/main/cpp/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ void Runtime::Init(jstring filesPath, jstring nativeLibDir, bool verboseLoggingE
m_isolate = PrepareV8Runtime(filesRoot, nativeLibDirStr, packageNameStr, isDebuggable, callingDirStr, profilerOutputDirStr, maxLogcatObjectSize, forceLog);
}

Runtime::~Runtime() {
delete this->m_objectManager;
delete this->m_heapSnapshotBlob;
delete this->m_startupData;
}

std::string Runtime::ReadFileText(const std::string& filePath) {
#ifdef APPLICATION_IN_DEBUG
std::lock_guard<std::mutex> lock(m_fileWriteMutex);
Expand Down Expand Up @@ -717,7 +723,6 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath, const string& native
ArrayHelper::Init(context);

m_arrayBufferHelper.CreateConvertFunctions(isolate, global, m_objectManager);
m_jsonObjectHelper.CreateConvertFunctions(isolate, global, m_objectManager);

s_mainThreadInitialized = true;

Expand Down
4 changes: 2 additions & 2 deletions test-app/runtime/src/main/cpp/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "SimpleAllocator.h"
#include "WeakRef.h"
#include "ArrayBufferHelper.h"
#include "JSONObjectHelper.h"
#include "Profiler.h"
#include "ModuleInternal.h"
#include "File.h"
Expand All @@ -23,6 +22,8 @@ class Runtime {
CONSTANTS = 1
};

~Runtime();

static Runtime* GetRuntime(int runtimeId);

static Runtime* GetRuntime(v8::Isolate* isolate);
Expand Down Expand Up @@ -78,7 +79,6 @@ class Runtime {
ModuleInternal m_module;

ArrayBufferHelper m_arrayBufferHelper;
JSONObjectHelper m_jsonObjectHelper;

WeakRef m_weakRef;

Expand Down
14 changes: 10 additions & 4 deletions test-app/runtime/src/main/cpp/com_tns_Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,12 +322,18 @@ extern "C" JNIEXPORT void Java_com_tns_Runtime_TerminateWorkerCallback(JNIEnv* e

auto isolate = runtime->GetIsolate();

v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handleScope(isolate);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handleScope(isolate);

CallbackHandlers::TerminateWorkerThread(isolate);

runtime->DestroyRuntime();
}

CallbackHandlers::TerminateWorkerThread(isolate);
isolate->Dispose();

runtime->DestroyRuntime();
delete runtime;
}

extern "C" JNIEXPORT void Java_com_tns_Runtime_ClearWorkerPersistent(JNIEnv* env, jobject obj, jint runtimeId, jint workerId) {
Expand Down
2 changes: 1 addition & 1 deletion v8-versions.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
">=6.2.9 <6.4.0": "7.8.279.19",
">=6.2.9 <6.5.0": "7.8.279.19",
">=6.1.9 <6.3.0": "7.7.299.11",
">=6.0.9 <6.2.0": "7.6.303.28",
">=5.9.9 <6.0.9": "7.5.288.22",
Expand Down