diff --git a/test-app/app/src/main/assets/app/mainpage.js b/test-app/app/src/main/assets/app/mainpage.js index 5f19aa045..3b6b4f6f3 100644 --- a/test-app/app/src/main/assets/app/mainpage.js +++ b/test-app/app/src/main/assets/app/mainpage.js @@ -67,4 +67,5 @@ require("./tests/kotlin/enums/testEnumsSupport"); require("./tests/kotlin/access/testInternalLanguageFeaturesSupport"); require("./tests/testPackagePrivate"); require("./tests/kotlin/properties/testPropertiesSupport.js"); -require('./tests/testNativeTimers'); \ No newline at end of file +require('./tests/testNativeTimers'); +require("./tests/console/logTests.js"); \ No newline at end of file diff --git a/test-app/app/src/main/assets/app/tests/console/logTests.js b/test-app/app/src/main/assets/app/tests/console/logTests.js new file mode 100644 index 000000000..6f4ea0e2f --- /dev/null +++ b/test-app/app/src/main/assets/app/tests/console/logTests.js @@ -0,0 +1,31 @@ +describe("Test JSONObject conversions", () => { + it("console.log with number param should not crash", () => { + console.log(123); + }); + + it("console.log with string param should not crash", () => { + console.log("123"); + }); + + it("console.log with object param should not crash", () => { + console.log({ num: 123 }); + }); + + it("console.log with function param should not crash", () => { + console.log(function() {}); + }); + + it("console.log with arrow function param should not crash", () => { + console.log(() => {}); + }); + + it("console.log with primitive array param should not crash", () => { + console.log([1, 2, 3]); + }); + + it("console.log with object array param should not crash", () => { + console.log([{ + num: 123 + }]); + }); +}); \ No newline at end of file diff --git a/test-app/runtime/src/main/cpp/CallbackHandlers.cpp b/test-app/runtime/src/main/cpp/CallbackHandlers.cpp index bf475eb40..ab018268f 100644 --- a/test-app/runtime/src/main/cpp/CallbackHandlers.cpp +++ b/test-app/runtime/src/main/cpp/CallbackHandlers.cpp @@ -1091,8 +1091,10 @@ CallbackHandlers::WorkerObjectPostMessageCallback(const v8::FunctionCallbackInfo ArgConverter::ConvertToV8String(isolate, "workerId"), jsId); - Local msg = tns::JsonStringifyObject(isolate, args[0], false); auto context = isolate->GetCurrentContext(); + auto objToStringify = args[0]->ToObject(context).ToLocalChecked(); + std::string msg = tns::JsonStringifyObject(isolate, objToStringify, false); + // get worker's ID that is associated on the other side - in Java auto id = jsId->Int32Value(context).ToChecked(); @@ -1100,7 +1102,7 @@ CallbackHandlers::WorkerObjectPostMessageCallback(const v8::FunctionCallbackInfo auto mId = env.GetStaticMethodID(RUNTIME_CLASS, "sendMessageFromMainToWorker", "(ILjava/lang/String;)V"); - auto jmsg = ArgConverter::ConvertToJavaString(msg); + jstring jmsg = env.NewStringUTF(msg.c_str()); JniLocalRef jmsgRef(jmsg); env.CallStaticVoidMethod(RUNTIME_CLASS, mId, id, (jstring) jmsgRef); @@ -1190,13 +1192,15 @@ CallbackHandlers::WorkerGlobalPostMessageCallback(const v8::FunctionCallbackInfo return; } - Local msg = tns::JsonStringifyObject(isolate, args[0], false); + auto context = isolate->GetCurrentContext(); + auto objToStringify = args[0]->ToObject(context).ToLocalChecked(); + std::string msg = tns::JsonStringifyObject(isolate, objToStringify, false); JEnv env; auto mId = env.GetStaticMethodID(RUNTIME_CLASS, "sendMessageFromWorkerToMain", "(Ljava/lang/String;)V"); - auto jmsg = ArgConverter::ConvertToJavaString(msg); + auto jmsg = env.NewStringUTF(msg.c_str()); JniLocalRef jmsgRef(jmsg); env.CallStaticVoidMethod(RUNTIME_CLASS, mId, (jstring) jmsgRef); diff --git a/test-app/runtime/src/main/cpp/V8GlobalHelpers.cpp b/test-app/runtime/src/main/cpp/V8GlobalHelpers.cpp index 988ca4c09..2fc637848 100644 --- a/test-app/runtime/src/main/cpp/V8GlobalHelpers.cpp +++ b/test-app/runtime/src/main/cpp/V8GlobalHelpers.cpp @@ -66,9 +66,9 @@ Local GetSmartJSONStringifyFunction(Isolate* isolate) { return smartStringifyPersistentFunction->Get(isolate); } -Local tns::JsonStringifyObject(Isolate* isolate, Handle value, bool handleCircularReferences) { +std::string tns::JsonStringifyObject(Isolate* isolate, v8::Local value, bool handleCircularReferences) { if (value.IsEmpty()) { - return String::Empty(isolate); + return ""; } auto context = isolate->GetCurrentContext(); @@ -80,11 +80,14 @@ Local tns::JsonStringifyObject(Isolate* isolate, Handle value v8::Local resultValue; v8::TryCatch tc(isolate); - Local args[] = { value->ToObject(context).ToLocalChecked() }; - auto success = smartJSONStringifyFunction->Call(context, Undefined(isolate), 1, args).ToLocal(&resultValue); + Local args[] = { value }; + auto success = smartJSONStringifyFunction + ->Call(context, Undefined(isolate), 1, args) + .ToLocal(&resultValue); if (success && !tc.HasCaught()) { - return resultValue->ToString(context).ToLocalChecked(); + auto res = resultValue.As(); + return ArgConverter::ConvertToString(res); } } } @@ -92,13 +95,13 @@ Local tns::JsonStringifyObject(Isolate* isolate, Handle value v8::Local resultString; v8::TryCatch tc(isolate); - auto success = v8::JSON::Stringify(context, value->ToObject(context).ToLocalChecked()).ToLocal(&resultString); + auto success = v8::JSON::Stringify(context, value).ToLocal(&resultString); if (!success && tc.HasCaught()) { throw NativeScriptException(tc); } - return resultString; + return ArgConverter::ConvertToString(resultString); } bool tns::V8GetPrivateValue(Isolate* isolate, const Local& obj, const Local& propName, Local& out) { diff --git a/test-app/runtime/src/main/cpp/V8GlobalHelpers.h b/test-app/runtime/src/main/cpp/V8GlobalHelpers.h index b91208304..0dc71fc8e 100644 --- a/test-app/runtime/src/main/cpp/V8GlobalHelpers.h +++ b/test-app/runtime/src/main/cpp/V8GlobalHelpers.h @@ -8,7 +8,7 @@ #include namespace tns { -v8::Local JsonStringifyObject(v8::Isolate* isolate, v8::Handle value, bool handleCircularReferences = true); +std::string JsonStringifyObject(v8::Isolate* isolate, v8::Handle value, bool handleCircularReferences = true); bool V8GetPrivateValue(v8::Isolate* isolate, const v8::Local& obj, const v8::Local& propName, v8::Local& out); diff --git a/test-app/runtime/src/main/cpp/console/Console.cpp b/test-app/runtime/src/main/cpp/console/Console.cpp index b57419723..7890aefac 100644 --- a/test-app/runtime/src/main/cpp/console/Console.cpp +++ b/test-app/runtime/src/main/cpp/console/Console.cpp @@ -72,27 +72,24 @@ void Console::sendToDevToolsFrontEnd(v8::Isolate* isolate, const std::string& me } } -const v8::Local transformJSObject(v8::Isolate* isolate, v8::Local object) { +std::string transformJSObject(v8::Isolate* isolate, v8::Local object) { auto context = isolate->GetCurrentContext(); auto objToString = object->ToString(context).ToLocalChecked(); - v8::Local resultString; + auto objToCppString = ArgConverter::ConvertToString(objToString); - auto hasCustomToStringImplementation = ArgConverter::ConvertToString(objToString).find("[object Object]") == std::string::npos; + auto hasCustomToStringImplementation = objToCppString.find("[object Object]") == std::string::npos; if (hasCustomToStringImplementation) { - resultString = objToString; + return objToCppString; } else { - v8::HandleScope scope(isolate); - resultString = JsonStringifyObject(isolate, object); + return JsonStringifyObject(isolate, object); } - - return resultString; } -const v8::Local buildStringFromArg(v8::Isolate* isolate, const v8::Local& val) { - v8::Local argString; +std::string buildStringFromArg(v8::Isolate* isolate, const v8::Local& val) { if (val->IsFunction()) { - val->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString); + auto v8FunctionString = val->ToDetailString(isolate->GetCurrentContext()).ToLocalChecked(); + return ArgConverter::ConvertToString(v8FunctionString); } else if (val->IsArray()) { auto context = isolate->GetCurrentContext(); auto cachedSelf = val; @@ -100,69 +97,57 @@ const v8::Local buildStringFromArg(v8::Isolate* isolate, const v8::L auto arrayEntryKeys = array->GetPropertyNames(isolate->GetCurrentContext()).ToLocalChecked(); auto arrayLength = arrayEntryKeys->Length(); - - argString = ArgConverter::ConvertToV8String(isolate, "["); + std::string argString = "["; for (int i = 0; i < arrayLength; i++) { auto propertyName = arrayEntryKeys->Get(context, i).ToLocalChecked(); - auto propertyValue = array->Get(context, propertyName).ToLocalChecked(); // avoid bottomless recursion with cyclic reference to the same array if (propertyValue->StrictEquals(cachedSelf)) { - argString = v8::String::Concat(isolate, argString, ArgConverter::ConvertToV8String(isolate, "[Circular]")); + argString.append("[Circular]"); continue; } auto objectString = buildStringFromArg(isolate, propertyValue); - - argString = v8::String::Concat(isolate, argString, objectString); + argString.append(objectString); if (i != arrayLength - 1) { - argString = v8::String::Concat(isolate, argString, ArgConverter::ConvertToV8String(isolate, ", ")); + argString.append(", "); } } - argString = v8::String::Concat(isolate, argString, ArgConverter::ConvertToV8String(isolate, "]")); + return argString.append("]"); } else if (val->IsObject()) { v8::Local obj = val.As(); - - argString = transformJSObject(isolate, obj); + return transformJSObject(isolate, obj); } else { - val->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString); + auto v8DefaultToString = val->ToDetailString(isolate->GetCurrentContext()).ToLocalChecked(); + return ArgConverter::ConvertToString(v8DefaultToString); } - - return argString; } -const std::string buildLogString(const v8::FunctionCallbackInfo& info, int startingIndex = 0) { +std::string buildLogString(const v8::FunctionCallbackInfo& info, int startingIndex = 0) { auto isolate = info.GetIsolate(); - v8::HandleScope scope(isolate); - std::stringstream ss; auto argLen = info.Length(); if (argLen) { for (int i = startingIndex; i < argLen; i++) { - v8::Local argString; - - argString = buildStringFromArg(isolate, info[i]); - // separate args with a space if (i != 0) { ss << " "; } - - ss << ArgConverter::ConvertToString(argString); + + std::string argString = buildStringFromArg(isolate, info[i]); + ss << argString; } } else { ss << std::endl; } - std::string stringResult = ss.str(); - - return stringResult; + return ss.str(); } void Console::assertCallback(const v8::FunctionCallbackInfo& info) { @@ -170,7 +155,6 @@ void Console::assertCallback(const v8::FunctionCallbackInfo& info) { auto isolate = info.GetIsolate(); auto argLen = info.Length(); - auto context = isolate->GetCurrentContext(); auto expressionPasses = argLen && info[0]->BooleanValue(isolate); if (!expressionPasses) { @@ -307,13 +291,11 @@ void Console::dirCallback(const v8::FunctionCallbackInfo& info) { if (propIsFunction) { ss << "()"; } else if (propertyValue->IsArray()) { - auto stringResult = buildStringFromArg(isolate, propertyValue); - std::string jsonStringifiedArray = ArgConverter::ConvertToString(stringResult); + std::string jsonStringifiedArray = buildStringFromArg(isolate, propertyValue); ss << ": " << jsonStringifiedArray; } else if (propertyValue->IsObject()) { auto obj = propertyValue->ToObject(context).ToLocalChecked(); - auto objString = transformJSObject(isolate, obj); - std::string jsonStringifiedObject = ArgConverter::ConvertToString(objString); + auto jsonStringifiedObject = transformJSObject(isolate, obj); // if object prints out as the error string for circular references, replace with #CR instead for brevity if (jsonStringifiedObject.find("circular structure") != std::string::npos) { jsonStringifiedObject = "#CR";