JavaScriptLogging.h
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sw=2 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_JavaScriptLogging__
#define mozilla_jsipc_JavaScriptLogging__
#include "nsString.h"
#include "nsPrintfCString.h"
#include "jsfriendapi.h"
#include "js/Wrapper.h"
namespace mozilla {
namespace jsipc {
#define LOG(...) \
PR_BEGIN_MACRO \
if (LoggingEnabled()) { \
Logging log(this, cx); \
log.print(__VA_ARGS__); \
} \
PR_END_MACRO
#define LOG_STACK() \
PR_BEGIN_MACRO \
if (StackLoggingEnabled()) { \
js::DumpBacktrace(cx); \
} \
PR_END_MACRO
struct ReceiverObj {
ObjectId id;
explicit ReceiverObj(ObjectId id) : id(id) {}
};
struct InVariant {
JSVariant variant;
explicit InVariant(const JSVariant& variant) : variant(variant) {}
};
struct OutVariant {
JSVariant variant;
explicit OutVariant(const JSVariant& variant) : variant(variant) {}
};
struct Identifier {
JSIDVariant variant;
explicit Identifier(const JSIDVariant& variant) : variant(variant) {}
};
class Logging {
public:
Logging(JavaScriptShared* shared, JSContext* cx) : shared(shared), cx(cx) {}
void print(const nsCString& str) {
const char* side = shared->isParent() ? "from child" : "from parent";
printf("CPOW %s: %s\n", side, str.get());
}
void print(const char* str) { print(nsCString(str)); }
template <typename T1>
void print(const char* fmt, const T1& a1) {
nsAutoCString tmp1;
format(a1, tmp1);
print(nsPrintfCString(fmt, tmp1.get()));
}
template <typename T1, typename T2>
void print(const char* fmt, const T1& a1, const T2& a2) {
nsAutoCString tmp1;
nsAutoCString tmp2;
format(a1, tmp1);
format(a2, tmp2);
print(nsPrintfCString(fmt, tmp1.get(), tmp2.get()));
}
template <typename T1, typename T2, typename T3>
void print(const char* fmt, const T1& a1, const T2& a2, const T3& a3) {
nsAutoCString tmp1;
nsAutoCString tmp2;
nsAutoCString tmp3;
format(a1, tmp1);
format(a2, tmp2);
format(a3, tmp3);
print(nsPrintfCString(fmt, tmp1.get(), tmp2.get(), tmp3.get()));
}
void format(const nsString& str, nsCString& out) {
out = NS_ConvertUTF16toUTF8(str);
}
void formatObject(bool incoming, bool local, ObjectId id, nsCString& out) {
const char* side;
const char* objDesc;
void* ptr;
if (local == incoming) {
JS::RootedObject obj(cx);
obj = shared->objects_.find(id);
obj = js::UncheckedUnwrap(obj, true);
JSAutoRealm ar(cx, obj);
objDesc = js::ObjectClassName(cx, obj);
side = shared->isParent() ? "parent" : "child";
ptr = obj;
} else {
objDesc = "<cpow>";
side = shared->isParent() ? "child" : "parent";
ptr = nullptr;
}
out = nsPrintfCString("<%s %s:%" PRIu64 ":%p>", side, objDesc,
id.serialNumber(), ptr);
}
void format(const ReceiverObj& obj, nsCString& out) {
formatObject(true, true, obj.id, out);
}
void format(const nsTArray<JSParam>& values, nsCString& out) {
nsAutoCString tmp;
out.Truncate();
for (size_t i = 0; i < values.Length(); i++) {
if (i) {
out.AppendLiteral(", ");
}
if (values[i].type() == JSParam::Tvoid_t) {
out.AppendLiteral("<void>");
} else {
format(InVariant(values[i].get_JSVariant()), tmp);
out += tmp;
}
}
}
void format(const InVariant& value, nsCString& out) {
format(true, value.variant, out);
}
void format(const OutVariant& value, nsCString& out) {
format(false, value.variant, out);
}
void format(bool incoming, const JSVariant& value, nsCString& out) {
switch (value.type()) {
case JSVariant::TUndefinedVariant: {
out = "undefined";
break;
}
case JSVariant::TNullVariant: {
out = "null";
break;
}
case JSVariant::TnsString: {
nsAutoCString tmp;
format(value.get_nsString(), tmp);
out = nsPrintfCString("\"%s\"", tmp.get());
break;
}
case JSVariant::TObjectVariant: {
const ObjectVariant& ovar = value.get_ObjectVariant();
if (ovar.type() == ObjectVariant::TLocalObject) {
Maybe<ObjectId> objId(
ObjectId::deserialize(ovar.get_LocalObject().serializedId()));
MOZ_RELEASE_ASSERT(objId.isSome());
formatObject(incoming, true, objId.value(), out);
} else {
Maybe<ObjectId> objId(
ObjectId::deserialize(ovar.get_RemoteObject().serializedId()));
MOZ_RELEASE_ASSERT(objId.isSome());
formatObject(incoming, false, objId.value(), out);
}
break;
}
case JSVariant::TSymbolVariant: {
out = "<Symbol>";
break;
}
case JSVariant::Tdouble: {
out = nsPrintfCString("%.0f", value.get_double());
break;
}
case JSVariant::Tbool: {
out = value.get_bool() ? "true" : "false";
break;
}
case JSVariant::TJSIID: {
out = "<JSIID>";
break;
}
default: {
out = "<JSIID>";
break;
}
}
}
void format(const Identifier& id, nsCString& out) {
switch (id.variant.type()) {
case JSIDVariant::TSymbolVariant: {
out = "<Symbol>";
break;
}
case JSIDVariant::TnsString: {
nsAutoCString tmp;
format(id.variant.get_nsString(), tmp);
out = nsPrintfCString("\"%s\"", tmp.get());
break;
}
case JSIDVariant::Tint32_t: {
out = nsPrintfCString("%d", id.variant.get_int32_t());
break;
}
default: {
out = "Unknown";
break;
}
}
}
private:
JavaScriptShared* shared;
JSContext* cx;
};
} // namespace jsipc
} // namespace mozilla
#endif