diff --git a/NativeScript/runtime/Helpers.h b/NativeScript/runtime/Helpers.h index e840943a..5c15d459 100644 --- a/NativeScript/runtime/Helpers.h +++ b/NativeScript/runtime/Helpers.h @@ -67,6 +67,8 @@ bool LiveSync(v8::Isolate* isolate); void Assert(bool condition, v8::Isolate* isolate = nullptr); +void StopExecutionAndLogStackTrace(v8::Isolate* isolate); + } #endif /* Helpers_h */ diff --git a/NativeScript/runtime/Helpers.mm b/NativeScript/runtime/Helpers.mm index 578ded00..d759c1c9 100644 --- a/NativeScript/runtime/Helpers.mm +++ b/NativeScript/runtime/Helpers.mm @@ -691,3 +691,7 @@ Log(@"%s", stack.c_str()); assert(false); } + +void tns::StopExecutionAndLogStackTrace(v8::Isolate* isolate) { + Assert(false, isolate); +} diff --git a/NativeScript/runtime/Interop.h b/NativeScript/runtime/Interop.h index b5057163..9601a097 100644 --- a/NativeScript/runtime/Interop.h +++ b/NativeScript/runtime/Interop.h @@ -126,6 +126,7 @@ class Interop { static id ToObject(v8::Local context, v8::Local arg); static v8::Local GetPrimitiveReturnType(v8::Local context, BinaryTypeEncodingType type, BaseCall* call); private: + static void ExecuteWriteValueDebugValidationsIfInDebug(v8::Local context, const TypeEncoding* typeEncoding, void* dest, v8::Local arg); static std::pair CreateMethodInternal(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData); static CFTypeRef CreateBlock(const uint8_t initialParamIndex, const uint8_t argsCount, const TypeEncoding* typeEncoding, FFIMethodCallback callback, void* userData); template diff --git a/NativeScript/runtime/Interop.mm b/NativeScript/runtime/Interop.mm index de86059c..052113ac 100644 --- a/NativeScript/runtime/Interop.mm +++ b/NativeScript/runtime/Interop.mm @@ -15,6 +15,7 @@ #include "Reference.h" #include "Pointer.h" #include "ExtVector.h" +#include "RuntimeConfig.h" #include "SymbolIterator.h" #include "UnmanagedType.h" #include "OneByteStringResource.h" @@ -143,7 +144,7 @@ void Interop::WriteValue(Local context, const TypeEncoding* typeEncoding, void* dest, Local arg) { Isolate* isolate = context->GetIsolate(); - + ExecuteWriteValueDebugValidationsIfInDebug(context, typeEncoding, dest, arg); if (arg.IsEmpty() || arg->IsNullOrUndefined()) { ffi_type* ffiType = FFICall::GetArgumentType(typeEncoding, true); size_t size = ffiType->size; @@ -1464,4 +1465,72 @@ return result; } +// MARK: - Debug Messages for the runtime + +void ExecuteWriteValueValidationsAndStopExecutionAndLogStackTrace(Local context, const TypeEncoding* typeEncoding, void* dest, Local arg) { + if (!RuntimeConfig.IsDebug) { + return; + } + Isolate* isolate = context->GetIsolate(); + std::string destName = typeEncoding->details.interfaceDeclarationReference.name.valuePtr(); + Local originArg = arg; + if (typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference) { + if (originArg->IsObject()) { + Local originObj = originArg.As(); + if ((originObj->IsArrayBuffer() || originObj->IsArrayBufferView()) && + destName != "NSArray") { + tns::StopExecutionAndLogStackTrace(isolate); + } + } + if (destName == "NSString" && tns::IsNumber(originArg)) { + tns::StopExecutionAndLogStackTrace(isolate); + } + if (destName == "NSString" && tns::IsBool(originArg)) { + tns::StopExecutionAndLogStackTrace(isolate); + } + if (destName == "NSString" && tns::IsArrayOrArrayLike(isolate, originArg)) { + tns::StopExecutionAndLogStackTrace(isolate); + } + } +} + +bool IsTypeEncondingHandldedByDebugMessages(const TypeEncoding* typeEncoding) { + if (typeEncoding->type != BinaryTypeEncodingType::InterfaceDeclarationReference && + typeEncoding->type != BinaryTypeEncodingType::StructDeclarationReference && + typeEncoding->type != BinaryTypeEncodingType::IdEncoding) { + return true; + } else { + return false; + } +} + +void LogWriteValueTraceMessage(Local context, const TypeEncoding* typeEncoding, void* dest, Local arg) { + if (!RuntimeConfig.IsDebug) { + return; + } + Isolate* isolate = context->GetIsolate(); + std::string destName = typeEncoding->details.interfaceDeclarationReference.name.valuePtr(); + std::string originName = tns::ToString(isolate, arg); + if (originName == "") { + // empty string + originName = "\"\""; + } + NSString* message = [NSString stringWithFormat:@"Interop::WriteValue: from {%s} to {%s}", originName.c_str(), destName.c_str()]; + Log(@"%@", message); +} + +void Interop::ExecuteWriteValueDebugValidationsIfInDebug(Local context, const TypeEncoding* typeEncoding, void* dest, Local arg) { + if (!RuntimeConfig.IsDebug) { + return; + } + if (arg.IsEmpty() || arg->IsNullOrUndefined()) { + return; + } + if (IsTypeEncondingHandldedByDebugMessages(typeEncoding)) { + return; + } + LogWriteValueTraceMessage(context, typeEncoding, dest, arg); + ExecuteWriteValueValidationsAndStopExecutionAndLogStackTrace(context, typeEncoding, dest, arg); +} + } diff --git a/NativeScript/runtime/Metadata.h b/NativeScript/runtime/Metadata.h index 7f532552..6b32c015 100644 --- a/NativeScript/runtime/Metadata.h +++ b/NativeScript/runtime/Metadata.h @@ -563,6 +563,31 @@ struct Meta { return (MetaType)(this->_flags & MetaTypeMask); } + const char* typeName() const { + switch (type()) { + case Undefined: + return "Undefined"; + case Struct: + return "Struct"; + case Union: + return "Union"; + case Function: + return "Function"; + case JsCode: + return "JsCode"; + case Var: + return "Var"; + case Interface: + return "Interface"; + case ProtocolType: + return "ProtocolType"; + case Vector: + return "Vector"; + default: + return "Unknwon"; + } + } + const ModuleMeta* topLevelModule() const { return this->_topLevelModule.valuePtr(); } diff --git a/NativeScript/runtime/MetadataBuilder.mm b/NativeScript/runtime/MetadataBuilder.mm index e619eb54..2237abae 100644 --- a/NativeScript/runtime/MetadataBuilder.mm +++ b/NativeScript/runtime/MetadataBuilder.mm @@ -11,6 +11,7 @@ #include "Worker.h" #include "Caches.h" #include "Tasks.h" +#include "RuntimeConfig.h" using namespace v8; @@ -810,6 +811,16 @@ Class klass = objc_getClass(containingClass.c_str()); // TODO: Find out if the isMethodCallback property can be determined based on a UITableViewController.prototype.viewDidLoad.call(this) or super.viewDidLoad() call + if (RuntimeConfig.IsDebug) { + NSString* message = [NSString stringWithFormat:@"MetadataBuilder::InvokeMethod class {%s}, selector {%s}, isInitializer {%s}, type {%s}, lib {%s}", + containingClass.c_str(), + meta->selectorAsString(), + meta->isInitializer() ? "true":"false", + meta->typeName(), + meta->topLevelModule()->getName()]; + Log(@"%@", message); + } + try { return ArgConverter::Invoke(context, klass, receiver, args, meta, isMethodCallback); } catch (NativeScriptException& ex) {