Visitar URL original
refactor: replace assert() with NS_CHECK/NS_DCHECK by edusperoni · Pull Request #2014 · NativeScript/android · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions test-app/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ add_library(
src/main/cpp/ModuleBinding.cpp
src/main/cpp/ModuleInternal.cpp
src/main/cpp/ModuleInternalCallbacks.cpp
src/main/cpp/NativeScriptAssert.cpp
src/main/cpp/NativeScriptException.cpp
src/main/cpp/NativeScriptPlatform.cpp
src/main/cpp/NsBuiltinModules.cpp
Expand Down
6 changes: 3 additions & 3 deletions test-app/runtime/src/main/cpp/ArgConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,16 @@ Local<Value> ArgConverter::ConvertFromJavaLong(Isolate* isolate, jlong value) {
}

int64_t ArgConverter::ConvertToJavaLong(Isolate* isolate, const Local<Value>& value) {
assert(!value.IsEmpty());
NS_DCHECK(!value.IsEmpty());

auto obj = Local<Object>::Cast(value);

assert(!obj.IsEmpty());
NS_DCHECK(!obj.IsEmpty());

auto context = isolate->GetCurrentContext();
Local<Value> temp;
bool success = obj->Get(context, V8StringConstants::GetValue(isolate)).ToLocal(&temp);
assert(success && !temp.IsEmpty());
NS_DCHECK(success && !temp.IsEmpty());
auto valueProp = temp.As<Object>();

string num = ConvertToString(valueProp->ToString(context).ToLocalChecked());
Expand Down
9 changes: 5 additions & 4 deletions test-app/runtime/src/main/cpp/ArrayBufferHelper.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "ArrayBufferHelper.h"
#include "NativeScriptAssert.h"
#include "ArgConverter.h"
#include "NativeScriptException.h"
#include <sstream>
Expand Down Expand Up @@ -65,7 +66,7 @@ void ArrayBufferHelper::CreateFromCallbackImpl(const FunctionCallbackInfo<Value>

if (m_ByteBufferClass == nullptr) {
m_ByteBufferClass = env.FindClass("java/nio/ByteBuffer");
assert(m_ByteBufferClass != nullptr);
NS_CHECK(m_ByteBufferClass != nullptr);
}

auto isByteBuffer = env.IsInstanceOf(obj, m_ByteBufferClass);
Expand All @@ -76,7 +77,7 @@ void ArrayBufferHelper::CreateFromCallbackImpl(const FunctionCallbackInfo<Value>

if (m_isDirectMethodID == nullptr) {
m_isDirectMethodID = env.GetMethodID(m_ByteBufferClass, "isDirect", "()Z");
assert(m_isDirectMethodID != nullptr);
NS_CHECK(m_isDirectMethodID != nullptr);
}

auto ret = env.CallBooleanMethod(obj, m_isDirectMethodID);
Expand All @@ -99,14 +100,14 @@ void ArrayBufferHelper::CreateFromCallbackImpl(const FunctionCallbackInfo<Value>
} else {
if (m_remainingMethodID == nullptr) {
m_remainingMethodID = env.GetMethodID(m_ByteBufferClass, "remaining", "()I");
assert(m_remainingMethodID != nullptr);
NS_CHECK(m_remainingMethodID != nullptr);
}

int bufferRemainingSize = env.CallIntMethod(obj, m_remainingMethodID);

if (m_getMethodID == nullptr) {
m_getMethodID = env.GetMethodID(m_ByteBufferClass, "get", "([BII)Ljava/nio/ByteBuffer;");
assert(m_getMethodID != nullptr);
NS_CHECK(m_getMethodID != nullptr);
}

jbyteArray byteArray = env.NewByteArray(bufferRemainingSize);
Expand Down
5 changes: 3 additions & 2 deletions test-app/runtime/src/main/cpp/ArrayHelper.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "ArrayHelper.h"
#include "NativeScriptAssert.h"
#include "ArgConverter.h"
#include "NativeScriptException.h"
#include "Runtime.h"
Expand All @@ -15,10 +16,10 @@ void ArrayHelper::Init(const Local<Context>& context) {
JEnv env;

RUNTIME_CLASS = env.FindClass("com/tns/Runtime");
assert(RUNTIME_CLASS != nullptr);
NS_CHECK(RUNTIME_CLASS != nullptr);

CREATE_ARRAY_HELPER = env.GetStaticMethodID(RUNTIME_CLASS, "createArrayHelper", "(Ljava/lang/String;I)Ljava/lang/Object;");
assert(CREATE_ARRAY_HELPER != nullptr);
NS_CHECK(CREATE_ARRAY_HELPER != nullptr);

auto isolate = v8::Isolate::GetCurrent();
auto global = context->Global();
Expand Down
74 changes: 62 additions & 12 deletions test-app/runtime/src/main/cpp/AssetExtractor.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#include "jni.h"
#include "zip.h"
#include <assert.h>
#include "NativeScriptAssert.h"
#include <libgen.h>
#include <utime.h>
#include <unistd.h>
#include <sys/stat.h>
#include <cerrno>
#include <cstdio>
#include <string>
#include "AssetExtractor.h"

using namespace tns;
Expand All @@ -22,7 +26,9 @@ void AssetExtractor::ExtractAssets(JNIEnv* env, jobject obj, jstring apk, jstrin
int err = 0;
auto z = zip_open(strApk.c_str(), 0, &err);

assert(z != nullptr);
// Nothing downstream can run without the app's assets, and every call
// below dereferences the archive.
NS_CHECK(z != nullptr);
zip_int64_t num = zip_get_num_entries(z, 0);
struct zip_stat sb;
struct zip_file* zf;
Expand Down Expand Up @@ -53,23 +59,67 @@ void AssetExtractor::ExtractAssets(JNIEnv* env, jobject obj, jstring apk, jstrin
mkdir_rec(dirFullname.c_str());

zf = zip_fopen_index(z, i, 0);
assert(zf != nullptr);
if (zf == nullptr) {
DEBUG_WRITE_FORCE(
"AssetExtractor: skipping '%s', it could not be opened "
"inside the apk (%s)",
sb.name, zip_strerror(z));
continue;
}

/*
* Extracted through a temporary and renamed once complete.
* Writing in place would truncate a good asset up front, and a
* read that fails partway would leave the remains behind: the
* mtime comparison above keeps whatever is already on disk
* unless the apk's copy is strictly newer, so a partial file
* would never be replaced on a later launch.
*/
std::string tempFullname(assetFullname);
tempFullname.append(".ns-partial.");
tempFullname.append(std::to_string(getpid()));

auto fd = fopen(assetFullname.c_str(), "w");
auto fd = fopen(tempFullname.c_str(), "w");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (fd != nullptr) {
zip_int64_t sum = 0;
while (sum != sb.size) {
bool complete = true;
while (sum != (zip_int64_t)sb.size) {
zip_int64_t len = zip_fread(zf, buf, sizeof(buf));
assert(len > 0);

fwrite(buf, 1, len, fd);
// 0 means the entry ended early, -1 a read error. Both
// leave the loop unable to advance, and -1 would reach
// fwrite as a huge size_t.
if (len <= 0) {
DEBUG_WRITE_FORCE(
"AssetExtractor: '%s' ended after %lld of %llu "
"bytes (%s)",
sb.name, (long long)sum,
(unsigned long long)sb.size,
zip_file_strerror(zf));
complete = false;
break;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (fwrite(buf, 1, len, fd) != (size_t)len) {
DEBUG_WRITE_FORCE(
"AssetExtractor: could not write '%s' (errno %d)",
assetFullname.c_str(), errno);
complete = false;
break;
}
sum += len;
}
fclose(fd);
utimbuf t;
t.modtime = sb.mtime;
utime(assetFullname.c_str(), &t);

complete = (fclose(fd) == 0) && complete;

if (complete && rename(tempFullname.c_str(),
assetFullname.c_str()) == 0) {
utimbuf t;
t.modtime = sb.mtime;
utime(assetFullname.c_str(), &t);
} else {
remove(tempFullname.c_str());
}
}

zip_fclose(zf);
Expand Down
25 changes: 13 additions & 12 deletions test-app/runtime/src/main/cpp/CallbackHandlers.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "CallbackHandlers.h"
#include "NativeScriptAssert.h"
#include "MetadataNode.h"
#include "Util.h"
#include "V8GlobalHelpers.h"
Expand Down Expand Up @@ -30,33 +31,33 @@ void CallbackHandlers::Init(Isolate *isolate) {
JEnv env;

JAVA_LANG_STRING = env.FindClass("java/lang/String");
assert(JAVA_LANG_STRING != nullptr);
NS_CHECK(JAVA_LANG_STRING != nullptr);

RUNTIME_CLASS = env.FindClass("com/tns/Runtime");
assert(RUNTIME_CLASS != nullptr);
NS_CHECK(RUNTIME_CLASS != nullptr);

RESOLVE_CLASS_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "resolveClass",
"(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Z)Ljava/lang/Class;");
assert(RESOLVE_CLASS_METHOD_ID != nullptr);
NS_CHECK(RESOLVE_CLASS_METHOD_ID != nullptr);

CURRENT_OBJECTID_FIELD_ID = env.GetFieldID(RUNTIME_CLASS, "currentObjectId", "I");
assert(CURRENT_OBJECTID_FIELD_ID != nullptr);
NS_CHECK(CURRENT_OBJECTID_FIELD_ID != nullptr);

MAKE_INSTANCE_STRONG_ID = env.GetMethodID(RUNTIME_CLASS, "makeInstanceStrong",
"(Ljava/lang/Object;I)V");
assert(MAKE_INSTANCE_STRONG_ID != nullptr);
NS_CHECK(MAKE_INSTANCE_STRONG_ID != nullptr);

GET_TYPE_METADATA = env.GetStaticMethodID(RUNTIME_CLASS, "getTypeMetadata",
"(Ljava/lang/String;I)[Ljava/lang/String;");
assert(GET_TYPE_METADATA != nullptr);
NS_CHECK(GET_TYPE_METADATA != nullptr);

ENABLE_VERBOSE_LOGGING_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "enableVerboseLogging",
"()V");
assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr);
NS_CHECK(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr);

DISABLE_VERBOSE_LOGGING_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "disableVerboseLogging",
"()V");
assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr);
NS_CHECK(DISABLE_VERBOSE_LOGGING_METHOD_ID != nullptr);

MetadataNode::Init(isolate);

Expand Down Expand Up @@ -559,7 +560,7 @@ void CallbackHandlers::CallJavaMethod(const Local<Object> &caller, const string
break;
}
default: {
assert(false);
NS_DCHECK(false);
break;
}
}
Expand Down Expand Up @@ -674,7 +675,7 @@ CallbackHandlers::GetMethodOverrides(JEnv &env, const Local<Object> &implementat
}

void CallbackHandlers::RunOnMainThreadCallback(const FunctionCallbackInfo<v8::Value> &args) {
assert(args[0]->IsFunction());
NS_DCHECK(args[0]->IsFunction());
Isolate *isolate = args.GetIsolate();

v8::Locker locker(isolate);
Expand All @@ -695,7 +696,7 @@ void CallbackHandlers::RunOnMainThreadCallback(const FunctionCallbackInfo<v8::Va
std::lock_guard<std::mutex> lock(cacheMutex_);
bool inserted;
std::tie(std::ignore, inserted) = cache_.try_emplace(key, isolate, callback);
assert(inserted && "Main thread callback ID should not be duplicated");
NS_DCHECK(inserted && "Main thread callback ID should not be duplicated");
}

// bare entry: the closure locks the CALLER's isolate (possibly a
Expand Down Expand Up @@ -957,7 +958,7 @@ vector<string> CallbackHandlers::GetTypeMetadata(const string &name, int index)

jsize length = env.GetArrayLength(pubApi);

assert(length > 0);
NS_DCHECK(length > 0);

vector<string> result;

Expand Down
70 changes: 66 additions & 4 deletions test-app/runtime/src/main/cpp/CrashBreadcrumbs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ constexpr size_t kMaxRuntimes = 16;
constexpr size_t kFieldMax = 160;
constexpr size_t kBufferMax = 8192;
constexpr size_t kHeaderMax = 128;
constexpr size_t kFatalMax = 256;
Comment thread
edusperoni marked this conversation as resolved.

struct Slot {
bool used;
Expand All @@ -43,6 +44,24 @@ std::atomic<int> g_storeFd{-1};
std::atomic_flag g_recorded = ATOMIC_FLAG_INIT;
struct sigaction g_previous[NSIG];

/*
* Written by whichever thread is on its way to abort(), read by the signal
* handler. Kept out of the rendered buffers so that recording it needs no
* lock -- the thread may be aborting from under one.
*/
enum FatalState { kFatalFree = 0, kFatalClaiming = 1, kFatalPublished = 2 };

char g_fatalMessage[kFatalMax];
size_t g_fatalLength = 0;
std::atomic<int> g_fatalState{kFatalFree};

/*
* How long the handler waits on a thread that has claimed the slot but not
* filled it yet. Bounded, so a thread that never gets to publish cannot stall
* the crash path.
*/
constexpr int kFatalSpinLimit = 200000;

thread_local Slot* t_slot = nullptr;

int CurrentTid() { return static_cast<int>(syscall(__NR_gettid)); }
Expand Down Expand Up @@ -154,9 +173,30 @@ void Handler(int signalNumber, siginfo_t* info, void* context) {
AppendRaw(header, sizeof(header), length, "\n");

ssize_t written = pwrite(fd, header, length, 0);
int active = g_active.load(std::memory_order_acquire);
if (written > 0 && active >= 0) {
pwrite(fd, g_rendered[active], g_renderedLength[active], written);
if (written > 0) {
off_t offset = written;

// A thread that has claimed the slot is a memcpy away from filling
// it. Waiting for it beats emitting a record that omits the very
// check that brought the process here.
for (int spin = 0;
spin < kFatalSpinLimit &&
g_fatalState.load(std::memory_order_acquire) == kFatalClaiming;
++spin) {
}

if (g_fatalState.load(std::memory_order_acquire) == kFatalPublished) {
ssize_t fatalWritten =
pwrite(fd, g_fatalMessage, g_fatalLength, offset);
if (fatalWritten > 0) {
offset += fatalWritten;
}
}

int active = g_active.load(std::memory_order_acquire);
if (active >= 0) {
pwrite(fd, g_rendered[active], g_renderedLength[active], offset);
}
}
}
}
Expand Down Expand Up @@ -206,7 +246,7 @@ void CrashBreadcrumbs::OpenStore(const std::string& filesRoot) {
return;
}

char previous[kBufferMax + kHeaderMax];
char previous[kHeaderMax + kFatalMax + kBufferMax];
ssize_t length = read(fd, previous, sizeof(previous) - 1);
if (length > 0) {
previous[length] = '\0';
Expand Down Expand Up @@ -278,6 +318,28 @@ void CrashBreadcrumbs::SetWorkerScript(int runtimeId, const char* script) {
RenderLocked();
}

void CrashBreadcrumbs::RecordFatal(const char* message) {
if (message == nullptr) {
return;
}
// Only the first caller writes. A second thread failing a check at the same
// moment would otherwise overlap this copy, including while the signal
// handler is reading the buffer out.
int expected = kFatalFree;
if (!g_fatalState.compare_exchange_strong(expected, kFatalClaiming,
std::memory_order_acq_rel)) {
return;
}
// Room is reserved for the newline and the terminator.
size_t length = strnlen(message, kFatalMax - 2);
memcpy(g_fatalMessage, message, length);
g_fatalMessage[length] = '\n';
g_fatalMessage[length + 1] = '\0';
g_fatalLength = length + 1;
// Publishes the buffer and the length together.
g_fatalState.store(kFatalPublished, std::memory_order_release);
}

CrashBreadcrumbs::ModuleScope::ModuleScope(const char* modulePath) {
Slot* slot = t_slot;
if (slot == nullptr) {
Expand Down
7 changes: 7 additions & 0 deletions test-app/runtime/src/main/cpp/CrashBreadcrumbs.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ class CrashBreadcrumbs {
/* Marks a registered runtime as a worker started from `script`. */
static void SetWorkerScript(int runtimeId, const char* script);

/*
* Records a line to be written ahead of the runtime state should the process
* die. Takes no lock, so it stays usable from a thread that is about to
* abort and may already hold any of the runtime's own locks.
*/
static void RecordFatal(const char* message);

/*
* Records the module the calling runtime is executing for the lifetime of
* the scope. Module loads nest (`require` inside a module body), so the
Expand Down
Loading
Loading