Commit db505f42 authored by Kenton Varda's avatar Kenton Varda

Refactor assertion macros, specifically with regards to recoverability.

parent 850af66a
......@@ -89,7 +89,9 @@ SegmentReader* ReaderArena::tryGetSegment(SegmentId id) {
}
void ReaderArena::reportReadLimitReached() {
FAIL_VALIDATE_INPUT("Exceeded message traversal limit. See capnproto::ReaderOptions.");
KJ_FAIL_REQUIRE("Exceeded message traversal limit. See capnproto::ReaderOptions.") {
return;
}
}
// =======================================================================================
......@@ -201,8 +203,10 @@ SegmentReader* BuilderArena::tryGetSegment(SegmentId id) {
}
void BuilderArena::reportReadLimitReached() {
FAIL_RECOVERABLE_ASSERT(
"Read limit reached for BuilderArena, but it should have been unlimited.") {}
KJ_FAIL_ASSERT(
"Read limit reached for BuilderArena, but it should have been unlimited.") {
return;
}
}
} // namespace internal
......
......@@ -112,9 +112,10 @@ kj::Maybe<EnumSchema::Enumerant> DynamicEnum::getEnumerant() {
}
uint16_t DynamicEnum::asImpl(uint64_t requestedTypeId) {
RECOVERABLE_REQUIRE(requestedTypeId == schema.getProto().getId(),
"Type mismatch in DynamicEnum.as().") {
KJ_REQUIRE(requestedTypeId == schema.getProto().getId(),
"Type mismatch in DynamicEnum.as().") {
// use it anyway
break;
}
return value;
}
......@@ -125,7 +126,7 @@ DynamicStruct::Reader DynamicObject::as(StructSchema schema) {
if (reader.kind == internal::ObjectKind::NULL_POINTER) {
return DynamicStruct::Reader(schema, internal::StructReader());
}
RECOVERABLE_REQUIRE(reader.kind == internal::ObjectKind::STRUCT, "Object is not a struct.") {
KJ_REQUIRE(reader.kind == internal::ObjectKind::STRUCT, "Object is not a struct.") {
// Return default struct.
return DynamicStruct::Reader(schema, internal::StructReader());
}
......@@ -136,7 +137,7 @@ DynamicList::Reader DynamicObject::as(ListSchema schema) {
if (reader.kind == internal::ObjectKind::NULL_POINTER) {
return DynamicList::Reader(schema, internal::ListReader());
}
RECOVERABLE_REQUIRE(reader.kind == internal::ObjectKind::LIST, "Object is not a list.") {
KJ_REQUIRE(reader.kind == internal::ObjectKind::LIST, "Object is not a list.") {
// Return empty list.
return DynamicList::Reader(schema, internal::ListReader());
}
......@@ -880,7 +881,7 @@ void DynamicStruct::Builder::setImpl(
getImpl(builder, member).as<DynamicUnion>().set(member, src.get());
return;
} else {
FAIL_RECOVERABLE_REQUIRE(
KJ_FAIL_REQUIRE(
"Trying to copy a union value, but the union's discriminant is not recognized. It "
"was probably constructed using a newer version of the schema.") {
// Just don't copy anything.
......@@ -928,8 +929,8 @@ void DynamicStruct::Builder::setImpl(
rawValue = enumSchema.getEnumerantByName(value.as<Text>()).getOrdinal();
} else {
DynamicEnum enumValue = value.as<DynamicEnum>();
RECOVERABLE_REQUIRE(enumValue.getSchema() == enumSchema,
"Type mismatch when using DynamicList::Builder::set().") {
KJ_REQUIRE(enumValue.getSchema() == enumSchema,
"Type mismatch when using DynamicList::Builder::set().") {
return;
}
rawValue = enumValue.getRaw();
......@@ -967,8 +968,9 @@ void DynamicStruct::Builder::setImpl(
return;
}
FAIL_RECOVERABLE_REQUIRE("can't set field of unknown type", (uint)type.which());
return;
KJ_FAIL_REQUIRE("can't set field of unknown type", (uint)type.which()) {
return;
}
}
}
......@@ -1109,8 +1111,9 @@ DynamicValue::Reader DynamicList::Reader::operator[](uint index) const {
reader.getObjectElement(index * ELEMENTS)));
case schema::Type::Body::INTERFACE_TYPE:
FAIL_RECOVERABLE_ASSERT("Interfaces not implemented.") {}
return nullptr;
KJ_FAIL_ASSERT("Interfaces not implemented.") {
return nullptr;
}
}
return nullptr;
......@@ -1171,15 +1174,16 @@ DynamicValue::Builder DynamicList::Builder::operator[](uint index) const {
return nullptr;
case schema::Type::Body::INTERFACE_TYPE:
FAIL_RECOVERABLE_ASSERT("Interfaces not implemented.") {}
return nullptr;
KJ_FAIL_ASSERT("Interfaces not implemented.") {
return nullptr;
}
}
return nullptr;
}
void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
RECOVERABLE_REQUIRE(index < size(), "List index out-of-bounds.") {
KJ_REQUIRE(index < size(), "List index out-of-bounds.") {
return;
}
......@@ -1219,8 +1223,9 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
// Not supported for the same reason List<struct> doesn't support it -- the space for the
// element is already allocated, and if it's smaller than the input value the copy would
// have to be lossy.
FAIL_RECOVERABLE_ASSERT("DynamicList of structs does not support set().");
return;
KJ_FAIL_ASSERT("DynamicList of structs does not support set().") {
return;
}
case schema::Type::Body::ENUM_TYPE: {
uint16_t rawValue;
......@@ -1229,8 +1234,8 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
rawValue = schema.getEnumElementType().getEnumerantByName(value.as<Text>()).getOrdinal();
} else {
DynamicEnum enumValue = value.as<DynamicEnum>();
RECOVERABLE_REQUIRE(schema.getEnumElementType() == enumValue.getSchema(),
"Type mismatch when using DynamicList::Builder::set().") {
KJ_REQUIRE(schema.getEnumElementType() == enumValue.getSchema(),
"Type mismatch when using DynamicList::Builder::set().") {
return;
}
rawValue = enumValue.getRaw();
......@@ -1240,15 +1245,19 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
}
case schema::Type::Body::OBJECT_TYPE:
FAIL_RECOVERABLE_ASSERT("List(Object) not supported.");
return;
KJ_FAIL_ASSERT("List(Object) not supported.") {
return;
}
case schema::Type::Body::INTERFACE_TYPE:
FAIL_RECOVERABLE_ASSERT("Interfaces not implemented.") {}
return;
KJ_FAIL_ASSERT("Interfaces not implemented.") {
return;
}
}
FAIL_RECOVERABLE_REQUIRE("can't set element of unknown type", (uint)schema.whichElementType());
KJ_FAIL_REQUIRE("can't set element of unknown type", (uint)schema.whichElementType()) {
return;
}
}
DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) {
......@@ -1343,42 +1352,46 @@ namespace {
template <typename T>
T signedToUnsigned(long long value) {
RECOVERABLE_REQUIRE(value >= 0 && T(value) == value,
"Value out-of-range for requested type.", value) {
KJ_REQUIRE(value >= 0 && T(value) == value, "Value out-of-range for requested type.", value) {
// Use it anyway.
break;
}
return value;
}
template <>
uint64_t signedToUnsigned<uint64_t>(long long value) {
RECOVERABLE_REQUIRE(value >= 0, "Value out-of-range for requested type.", value) {
KJ_REQUIRE(value >= 0, "Value out-of-range for requested type.", value) {
// Use it anyway.
break;
}
return value;
}
template <typename T>
T unsignedToSigned(unsigned long long value) {
RECOVERABLE_REQUIRE(T(value) >= 0 && (unsigned long long)T(value) == value,
"Value out-of-range for requested type.", value) {
KJ_REQUIRE(T(value) >= 0 && (unsigned long long)T(value) == value,
"Value out-of-range for requested type.", value) {
// Use it anyway.
break;
}
return value;
}
template <>
int64_t unsignedToSigned<int64_t>(unsigned long long value) {
RECOVERABLE_REQUIRE(int64_t(value) >= 0, "Value out-of-range for requested type.", value) {
KJ_REQUIRE(int64_t(value) >= 0, "Value out-of-range for requested type.", value) {
// Use it anyway.
break;
}
return value;
}
template <typename T, typename U>
T checkRoundTrip(U value) {
RECOVERABLE_REQUIRE(T(value) == value, "Value out-of-range for requested type.", value) {
KJ_REQUIRE(T(value) == value, "Value out-of-range for requested type.", value) {
// Use it anyway.
break;
}
return value;
}
......@@ -1395,10 +1408,9 @@ typeName DynamicValue::Reader::AsImpl<typeName>::apply(Reader reader) { \
case FLOAT: \
return ifFloat<typeName>(reader.floatValue); \
default: \
FAIL_RECOVERABLE_REQUIRE("Type mismatch when using DynamicValue::Reader::as().") { \
/* use zero */ \
KJ_FAIL_REQUIRE("Type mismatch when using DynamicValue::Reader::as().") { \
return 0; \
} \
return 0; \
} \
} \
typeName DynamicValue::Builder::AsImpl<typeName>::apply(Builder builder) { \
......@@ -1410,10 +1422,9 @@ typeName DynamicValue::Builder::AsImpl<typeName>::apply(Builder builder) { \
case FLOAT: \
return ifFloat<typeName>(builder.floatValue); \
default: \
FAIL_RECOVERABLE_REQUIRE("Type mismatch when using DynamicValue::Builder::as().") { \
/* use zero */ \
KJ_FAIL_REQUIRE("Type mismatch when using DynamicValue::Builder::as().") { \
return 0; \
} \
return 0; \
} \
}
......@@ -1459,8 +1470,7 @@ Data::Reader DynamicValue::Reader::AsImpl<Data>::apply(Reader reader) {
// Implicitly convert from text.
return reader.textValue;
}
RECOVERABLE_REQUIRE(reader.type == DATA,
"Type mismatch when using DynamicValue::Reader::as().") {
KJ_REQUIRE(reader.type == DATA, "Type mismatch when using DynamicValue::Reader::as().") {
return Data::Reader();
}
return reader.dataValue;
......@@ -1470,8 +1480,7 @@ Data::Builder DynamicValue::Builder::AsImpl<Data>::apply(Builder builder) {
// Implicitly convert from text.
return builder.textValue;
}
RECOVERABLE_REQUIRE(builder.type == DATA,
"Type mismatch when using DynamicValue::Builder::as().") {
KJ_REQUIRE(builder.type == DATA, "Type mismatch when using DynamicValue::Builder::as().") {
return Data::Builder();
}
return builder.dataValue;
......@@ -1479,15 +1488,13 @@ Data::Builder DynamicValue::Builder::AsImpl<Data>::apply(Builder builder) {
// As in the header, HANDLE_TYPE(void, VOID, Void) crashes GCC 4.7.
Void DynamicValue::Reader::AsImpl<Void>::apply(Reader reader) {
RECOVERABLE_REQUIRE(reader.type == VOID,
"Type mismatch when using DynamicValue::Reader::as().") {
KJ_REQUIRE(reader.type == VOID, "Type mismatch when using DynamicValue::Reader::as().") {
return Void();
}
return reader.voidValue;
}
Void DynamicValue::Builder::AsImpl<Void>::apply(Builder builder) {
RECOVERABLE_REQUIRE(builder.type == VOID,
"Type mismatch when using DynamicValue::Builder::as().") {
KJ_REQUIRE(builder.type == VOID, "Type mismatch when using DynamicValue::Builder::as().") {
return Void();
}
return builder.voidValue;
......
......@@ -273,15 +273,15 @@ struct WireHelpers {
if (segment != nullptr && ref->kind() == WirePointer::FAR) {
// Look up the segment containing the landing pad.
segment = segment->getArena()->tryGetSegment(ref->farRef.segmentId.get());
VALIDATE_INPUT(segment != nullptr, "Message contains far pointer to unknown segment.") {
KJ_REQUIRE(segment != nullptr, "Message contains far pointer to unknown segment.") {
return nullptr;
}
// Find the landing pad and check that it is within bounds.
const word* ptr = segment->getStartPtr() + ref->farPositionInSegment();
WordCount padWords = (1 + ref->isDoubleFar()) * POINTER_SIZE_IN_WORDS;
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr + padWords),
"Message contains out-of-bounds far pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr, ptr + padWords),
"Message contains out-of-bounds far pointer.") {
return nullptr;
}
......@@ -298,8 +298,7 @@ struct WireHelpers {
ref = pad + 1;
segment = segment->getArena()->tryGetSegment(pad->farRef.segmentId.get());
VALIDATE_INPUT(segment != nullptr,
"Message contains double-far pointer to unknown segment.") {
KJ_REQUIRE(segment != nullptr, "Message contains double-far pointer to unknown segment.") {
return nullptr;
}
......@@ -338,7 +337,9 @@ struct WireHelpers {
break;
}
case WirePointer::RESERVED_3:
FAIL_RECOVERABLE_ASSERT("Don't know how to handle RESERVED_3.") {}
KJ_FAIL_ASSERT("Don't know how to handle RESERVED_3.") {
break;
}
break;
}
}
......@@ -404,10 +405,14 @@ struct WireHelpers {
break;
}
case WirePointer::FAR:
FAIL_RECOVERABLE_ASSERT("Unexpected FAR pointer.") {}
KJ_FAIL_ASSERT("Unexpected FAR pointer.") {
break;
}
break;
case WirePointer::RESERVED_3:
FAIL_RECOVERABLE_ASSERT("Don't know how to handle RESERVED_3.") {}
KJ_FAIL_ASSERT("Don't know how to handle RESERVED_3.") {
break;
}
break;
}
}
......@@ -435,7 +440,7 @@ struct WireHelpers {
return 0 * WORDS;
}
VALIDATE_INPUT(nestingLimit > 0, "Message is too deeply-nested.") {
KJ_REQUIRE(nestingLimit > 0, "Message is too deeply-nested.") {
return 0 * WORDS;
}
--nestingLimit;
......@@ -446,9 +451,9 @@ struct WireHelpers {
switch (ref->kind()) {
case WirePointer::STRUCT: {
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr + ref->structRef.wordSize()),
"Message contained out-of-bounds struct pointer.") {
break;
KJ_REQUIRE(boundsCheck(segment, ptr, ptr + ref->structRef.wordSize()),
"Message contained out-of-bounds struct pointer.") {
return result;
}
result += ref->structRef.wordSize();
......@@ -473,9 +478,9 @@ struct WireHelpers {
WordCount totalWords = roundUpToWords(
ElementCount64(ref->listRef.elementCount()) *
dataBitsPerElement(ref->listRef.elementSize()));
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr + totalWords),
"Message contained out-of-bounds list pointer.") {
break;
KJ_REQUIRE(boundsCheck(segment, ptr, ptr + totalWords),
"Message contained out-of-bounds list pointer.") {
return result;
}
result += totalWords;
break;
......@@ -483,9 +488,9 @@ struct WireHelpers {
case FieldSize::POINTER: {
WirePointerCount count = ref->listRef.elementCount() * (POINTERS / ELEMENTS);
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr + count * WORDS_PER_POINTER),
"Message contained out-of-bounds list pointer.") {
break;
KJ_REQUIRE(boundsCheck(segment, ptr, ptr + count * WORDS_PER_POINTER),
"Message contained out-of-bounds list pointer.") {
return result;
}
result += count * WORDS_PER_POINTER;
......@@ -498,10 +503,9 @@ struct WireHelpers {
}
case FieldSize::INLINE_COMPOSITE: {
WordCount wordCount = ref->listRef.inlineCompositeWordCount();
VALIDATE_INPUT(
boundsCheck(segment, ptr, ptr + wordCount + POINTER_SIZE_IN_WORDS),
"Message contained out-of-bounds list pointer.") {
break;
KJ_REQUIRE(boundsCheck(segment, ptr, ptr + wordCount + POINTER_SIZE_IN_WORDS),
"Message contained out-of-bounds list pointer.") {
return result;
}
result += wordCount + POINTER_SIZE_IN_WORDS;
......@@ -509,14 +513,14 @@ struct WireHelpers {
const WirePointer* elementTag = reinterpret_cast<const WirePointer*>(ptr);
ElementCount count = elementTag->inlineCompositeListElementCount();
VALIDATE_INPUT(elementTag->kind() == WirePointer::STRUCT,
"Don't know how to handle non-STRUCT inline composite.") {
break;
KJ_REQUIRE(elementTag->kind() == WirePointer::STRUCT,
"Don't know how to handle non-STRUCT inline composite.") {
return result;
}
VALIDATE_INPUT(elementTag->structRef.wordSize() / ELEMENTS * count <= wordCount,
"Struct list pointer's elements overran size.") {
break;
KJ_REQUIRE(elementTag->structRef.wordSize() / ELEMENTS * count <= wordCount,
"Struct list pointer's elements overran size.") {
return result;
}
WordCount dataSize = elementTag->structRef.dataSize.get();
......@@ -538,13 +542,13 @@ struct WireHelpers {
break;
}
case WirePointer::FAR:
FAIL_RECOVERABLE_ASSERT("Unexpected FAR pointer.") {
KJ_FAIL_ASSERT("Unexpected FAR pointer.") {
break;
}
break;
case WirePointer::RESERVED_3:
FAIL_VALIDATE_INPUT("Don't know how to handle RESERVED_3.") {
break;
KJ_FAIL_REQUIRE("Don't know how to handle RESERVED_3.") {
return result;
}
break;
}
......@@ -743,7 +747,7 @@ struct WireHelpers {
SegmentBuilder* oldSegment = segment;
word* oldPtr = followFars(oldRef, oldSegment);
VALIDATE_INPUT(oldRef->kind() == WirePointer::STRUCT,
KJ_REQUIRE(oldRef->kind() == WirePointer::STRUCT,
"Message contains non-struct pointer where struct pointer was expected.") {
goto useDefault;
}
......@@ -871,7 +875,7 @@ struct WireHelpers {
SegmentBuilder* segment = origSegment;
word* ptr = followFars(ref, segment);
VALIDATE_INPUT(ref->kind() == WirePointer::LIST,
KJ_REQUIRE(ref->kind() == WirePointer::LIST,
"Called getList{Field,Element}() but existing pointer is not a list.") {
goto useDefault;
}
......@@ -904,13 +908,17 @@ struct WireHelpers {
case FieldSize::TWO_BYTES:
case FieldSize::FOUR_BYTES:
case FieldSize::EIGHT_BYTES:
VALIDATE_INPUT(dataSize >= 1 * WORDS,
"Existing list value is incompatible with expected type.");
KJ_REQUIRE(dataSize >= 1 * WORDS,
"Existing list value is incompatible with expected type.") {
goto useDefault;
}
break;
case FieldSize::POINTER:
VALIDATE_INPUT(pointerCount >= 1 * POINTERS,
"Existing list value is incompatible with expected type.");
KJ_REQUIRE(pointerCount >= 1 * POINTERS,
"Existing list value is incompatible with expected type.") {
goto useDefault;
}
// Adjust the pointer to point at the reference segment.
ptr += dataSize;
break;
......@@ -930,10 +938,14 @@ struct WireHelpers {
BitCount dataSize = dataBitsPerElement(oldSize) * ELEMENTS;
WirePointerCount pointerCount = pointersPerElement(oldSize) * ELEMENTS;
VALIDATE_INPUT(dataSize >= dataBitsPerElement(elementSize) * ELEMENTS,
"Existing list value is incompatible with expected type.");
VALIDATE_INPUT(pointerCount >= pointersPerElement(elementSize) * ELEMENTS,
"Existing list value is incompatible with expected type.");
KJ_REQUIRE(dataSize >= dataBitsPerElement(elementSize) * ELEMENTS,
"Existing list value is incompatible with expected type.") {
goto useDefault;
}
KJ_REQUIRE(pointerCount >= pointersPerElement(elementSize) * ELEMENTS,
"Existing list value is incompatible with expected type.") {
goto useDefault;
}
auto step = (dataSize + pointerCount * BITS_PER_POINTER) / ELEMENTS;
return ListBuilder(segment, ptr, step, ref->listRef.elementCount(),
......@@ -960,8 +972,8 @@ struct WireHelpers {
SegmentBuilder* oldSegment = origSegment;
word* oldPtr = followFars(oldRef, oldSegment);
VALIDATE_INPUT(oldRef->kind() == WirePointer::LIST,
"Called getList{Field,Element}() but existing pointer is not a list.") {
KJ_REQUIRE(oldRef->kind() == WirePointer::LIST,
"Called getList{Field,Element}() but existing pointer is not a list.") {
goto useDefault;
}
......@@ -972,8 +984,8 @@ struct WireHelpers {
WirePointer* oldTag = reinterpret_cast<WirePointer*>(oldPtr);
oldPtr += POINTER_SIZE_IN_WORDS;
VALIDATE_INPUT(oldTag->kind() == WirePointer::STRUCT,
"INLINE_COMPOSITE list with non-STRUCT elements not supported.") {
KJ_REQUIRE(oldTag->kind() == WirePointer::STRUCT,
"INLINE_COMPOSITE list with non-STRUCT elements not supported.") {
goto useDefault;
}
......@@ -1046,8 +1058,8 @@ struct WireHelpers {
// No expectations.
break;
case FieldSize::POINTER:
VALIDATE_INPUT(oldSize == FieldSize::POINTER || oldSize == FieldSize::VOID,
"Struct list has incompatible element size.") {
KJ_REQUIRE(oldSize == FieldSize::POINTER || oldSize == FieldSize::VOID,
"Struct list has incompatible element size.") {
goto useDefault;
}
break;
......@@ -1060,8 +1072,8 @@ struct WireHelpers {
case FieldSize::FOUR_BYTES:
case FieldSize::EIGHT_BYTES:
// Preferred size is data-only.
VALIDATE_INPUT(oldSize != FieldSize::POINTER,
"Struct list has incompatible element size.") {
KJ_REQUIRE(oldSize != FieldSize::POINTER,
"Struct list has incompatible element size.") {
goto useDefault;
}
break;
......@@ -1433,8 +1445,8 @@ struct WireHelpers {
defaultValue = nullptr; // If the default value is itself invalid, don't use it again.
}
VALIDATE_INPUT(nestingLimit > 0,
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
KJ_REQUIRE(nestingLimit > 0,
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
goto useDefault;
}
......@@ -1444,13 +1456,13 @@ struct WireHelpers {
goto useDefault;
}
VALIDATE_INPUT(ref->kind() == WirePointer::STRUCT,
"Message contains non-struct pointer where struct pointer was expected.") {
KJ_REQUIRE(ref->kind() == WirePointer::STRUCT,
"Message contains non-struct pointer where struct pointer was expected.") {
goto useDefault;
}
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr + ref->structRef.wordSize()),
"Message contained out-of-bounds struct pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr, ptr + ref->structRef.wordSize()),
"Message contained out-of-bounds struct pointer.") {
goto useDefault;
}
......@@ -1475,8 +1487,8 @@ struct WireHelpers {
defaultValue = nullptr; // If the default value is itself invalid, don't use it again.
}
VALIDATE_INPUT(nestingLimit > 0,
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
KJ_REQUIRE(nestingLimit > 0,
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
goto useDefault;
}
......@@ -1486,8 +1498,8 @@ struct WireHelpers {
goto useDefault;
}
VALIDATE_INPUT(ref->kind() == WirePointer::LIST,
"Message contains non-list pointer where list pointer was expected.") {
KJ_REQUIRE(ref->kind() == WirePointer::LIST,
"Message contains non-list pointer where list pointer was expected.") {
goto useDefault;
}
......@@ -1501,21 +1513,21 @@ struct WireHelpers {
const WirePointer* tag = reinterpret_cast<const WirePointer*>(ptr);
ptr += POINTER_SIZE_IN_WORDS;
VALIDATE_INPUT(boundsCheck(segment, ptr - POINTER_SIZE_IN_WORDS, ptr + wordCount),
"Message contains out-of-bounds list pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr - POINTER_SIZE_IN_WORDS, ptr + wordCount),
"Message contains out-of-bounds list pointer.") {
goto useDefault;
}
VALIDATE_INPUT(tag->kind() == WirePointer::STRUCT,
"INLINE_COMPOSITE lists of non-STRUCT type are not supported.") {
KJ_REQUIRE(tag->kind() == WirePointer::STRUCT,
"INLINE_COMPOSITE lists of non-STRUCT type are not supported.") {
goto useDefault;
}
size = tag->inlineCompositeListElementCount();
wordsPerElement = tag->structRef.wordSize() / ELEMENTS;
VALIDATE_INPUT(size * wordsPerElement <= wordCount,
"INLINE_COMPOSITE list's elements overrun its word count.") {
KJ_REQUIRE(size * wordsPerElement <= wordCount,
"INLINE_COMPOSITE list's elements overrun its word count.") {
goto useDefault;
}
......@@ -1530,7 +1542,7 @@ struct WireHelpers {
break;
case FieldSize::BIT:
FAIL_VALIDATE_INPUT("Expected a bit list, but got a list of structs.") {
KJ_FAIL_REQUIRE("Expected a bit list, but got a list of structs.") {
goto useDefault;
}
break;
......@@ -1539,8 +1551,8 @@ struct WireHelpers {
case FieldSize::TWO_BYTES:
case FieldSize::FOUR_BYTES:
case FieldSize::EIGHT_BYTES:
VALIDATE_INPUT(tag->structRef.dataSize.get() > 0 * WORDS,
"Expected a primitive list, but got a list of pointer-only structs.") {
KJ_REQUIRE(tag->structRef.dataSize.get() > 0 * WORDS,
"Expected a primitive list, but got a list of pointer-only structs.") {
goto useDefault;
}
break;
......@@ -1550,8 +1562,8 @@ struct WireHelpers {
// in the struct is the pointer we were looking for, we want to munge the pointer to
// point at the first element's pointer segment.
ptr += tag->structRef.dataSize.get();
VALIDATE_INPUT(tag->structRef.ptrCount.get() > 0 * POINTERS,
"Expected a pointer list, but got a list of data-only structs.") {
KJ_REQUIRE(tag->structRef.ptrCount.get() > 0 * POINTERS,
"Expected a pointer list, but got a list of data-only structs.") {
goto useDefault;
}
break;
......@@ -1573,9 +1585,9 @@ struct WireHelpers {
pointersPerElement(ref->listRef.elementSize()) * ELEMENTS;
auto step = (dataSize + pointerCount * BITS_PER_POINTER) / ELEMENTS;
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr +
roundUpToWords(ElementCount64(ref->listRef.elementCount()) * step)),
"Message contains out-of-bounds list pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr, ptr +
roundUpToWords(ElementCount64(ref->listRef.elementCount()) * step)),
"Message contains out-of-bounds list pointer.") {
goto useDefault;
}
......@@ -1589,12 +1601,12 @@ struct WireHelpers {
WirePointerCount expectedPointersPerElement =
pointersPerElement(expectedElementSize) * ELEMENTS;
VALIDATE_INPUT(expectedDataBitsPerElement <= dataSize,
"Message contained list with incompatible element type.") {
KJ_REQUIRE(expectedDataBitsPerElement <= dataSize,
"Message contained list with incompatible element type.") {
goto useDefault;
}
VALIDATE_INPUT(expectedPointersPerElement <= pointerCount,
"Message contained list with incompatible element type.") {
KJ_REQUIRE(expectedPointersPerElement <= pointerCount,
"Message contained list with incompatible element type.") {
goto useDefault;
}
......@@ -1620,30 +1632,30 @@ struct WireHelpers {
uint size = ref->listRef.elementCount() / ELEMENTS;
VALIDATE_INPUT(ref->kind() == WirePointer::LIST,
"Message contains non-list pointer where text was expected.") {
KJ_REQUIRE(ref->kind() == WirePointer::LIST,
"Message contains non-list pointer where text was expected.") {
goto useDefault;
}
VALIDATE_INPUT(ref->listRef.elementSize() == FieldSize::BYTE,
"Message contains list pointer of non-bytes where text was expected.") {
KJ_REQUIRE(ref->listRef.elementSize() == FieldSize::BYTE,
"Message contains list pointer of non-bytes where text was expected.") {
goto useDefault;
}
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr +
roundUpToWords(ref->listRef.elementCount() * (1 * BYTES / ELEMENTS))),
"Message contained out-of-bounds text pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr, ptr +
roundUpToWords(ref->listRef.elementCount() * (1 * BYTES / ELEMENTS))),
"Message contained out-of-bounds text pointer.") {
goto useDefault;
}
VALIDATE_INPUT(size > 0, "Message contains text that is not NUL-terminated.") {
KJ_REQUIRE(size > 0, "Message contains text that is not NUL-terminated.") {
goto useDefault;
}
const char* cptr = reinterpret_cast<const char*>(ptr);
--size; // NUL terminator
VALIDATE_INPUT(cptr[size] == '\0', "Message contains text that is not NUL-terminated.") {
KJ_REQUIRE(cptr[size] == '\0', "Message contains text that is not NUL-terminated.") {
goto useDefault;
}
......@@ -1667,19 +1679,19 @@ struct WireHelpers {
uint size = ref->listRef.elementCount() / ELEMENTS;
VALIDATE_INPUT(ref->kind() == WirePointer::LIST,
"Message contains non-list pointer where data was expected.") {
KJ_REQUIRE(ref->kind() == WirePointer::LIST,
"Message contains non-list pointer where data was expected.") {
goto useDefault;
}
VALIDATE_INPUT(ref->listRef.elementSize() == FieldSize::BYTE,
"Message contains list pointer of non-bytes where data was expected.") {
KJ_REQUIRE(ref->listRef.elementSize() == FieldSize::BYTE,
"Message contains list pointer of non-bytes where data was expected.") {
goto useDefault;
}
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr +
roundUpToWords(ref->listRef.elementCount() * (1 * BYTES / ELEMENTS))),
"Message contained out-of-bounds data pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr, ptr +
roundUpToWords(ref->listRef.elementCount() * (1 * BYTES / ELEMENTS))),
"Message contained out-of-bounds data pointer.") {
goto useDefault;
}
......@@ -1716,13 +1728,13 @@ struct WireHelpers {
switch (ref->kind()) {
case WirePointer::STRUCT:
VALIDATE_INPUT(nestingLimit > 0,
KJ_REQUIRE(nestingLimit > 0,
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
goto useDefault;
}
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr + ref->structRef.wordSize()),
"Message contained out-of-bounds struct pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr, ptr + ref->structRef.wordSize()),
"Message contained out-of-bounds struct pointer.") {
goto useDefault;
}
return ObjectReader(
......@@ -1734,7 +1746,7 @@ struct WireHelpers {
case WirePointer::LIST: {
FieldSize elementSize = ref->listRef.elementSize();
VALIDATE_INPUT(nestingLimit > 0,
KJ_REQUIRE(nestingLimit > 0,
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
goto useDefault;
}
......@@ -1744,21 +1756,23 @@ struct WireHelpers {
const WirePointer* tag = reinterpret_cast<const WirePointer*>(ptr);
ptr += POINTER_SIZE_IN_WORDS;
VALIDATE_INPUT(boundsCheck(segment, ptr - POINTER_SIZE_IN_WORDS, ptr + wordCount),
"Message contains out-of-bounds list pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr - POINTER_SIZE_IN_WORDS, ptr + wordCount),
"Message contains out-of-bounds list pointer.") {
goto useDefault;
}
VALIDATE_INPUT(tag->kind() == WirePointer::STRUCT,
"INLINE_COMPOSITE lists of non-STRUCT type are not supported.") {
KJ_REQUIRE(tag->kind() == WirePointer::STRUCT,
"INLINE_COMPOSITE lists of non-STRUCT type are not supported.") {
goto useDefault;
}
ElementCount elementCount = tag->inlineCompositeListElementCount();
auto wordsPerElement = tag->structRef.wordSize() / ELEMENTS;
VALIDATE_INPUT(wordsPerElement * elementCount <= wordCount,
"INLINE_COMPOSITE list's elements overrun its word count.");
KJ_REQUIRE(wordsPerElement * elementCount <= wordCount,
"INLINE_COMPOSITE list's elements overrun its word count.") {
goto useDefault;
}
return ObjectReader(
ListReader(segment, ptr, elementCount, wordsPerElement * BITS_PER_WORD,
......@@ -1771,8 +1785,8 @@ struct WireHelpers {
ElementCount elementCount = ref->listRef.elementCount();
WordCount wordCount = roundUpToWords(ElementCount64(elementCount) * step);
VALIDATE_INPUT(boundsCheck(segment, ptr, ptr + wordCount),
"Message contains out-of-bounds list pointer.") {
KJ_REQUIRE(boundsCheck(segment, ptr, ptr + wordCount),
"Message contains out-of-bounds list pointer.") {
goto useDefault;
}
......@@ -1782,8 +1796,9 @@ struct WireHelpers {
}
}
default:
FAIL_VALIDATE_INPUT("Message contained invalid pointer.") {}
goto useDefault;
KJ_FAIL_REQUIRE("Message contained invalid pointer.") {
goto useDefault;
}
}
}
};
......@@ -1909,8 +1924,8 @@ StructReader StructReader::readRootUnchecked(const word* location) {
StructReader StructReader::readRoot(
const word* location, SegmentReader* segment, int nestingLimit) {
VALIDATE_INPUT(WireHelpers::boundsCheck(segment, location, location + POINTER_SIZE_IN_WORDS),
"Root location out-of-bounds.") {
KJ_REQUIRE(WireHelpers::boundsCheck(segment, location, location + POINTER_SIZE_IN_WORDS),
"Root location out-of-bounds.") {
location = nullptr;
}
......@@ -1980,21 +1995,21 @@ WordCount64 StructReader::totalSize() const {
// ListBuilder
Text::Builder ListBuilder::asText() {
VALIDATE_INPUT(structDataSize == 8 * BITS && structPointerCount == 0 * POINTERS,
"Expected Text, got list of non-bytes.") {
KJ_REQUIRE(structDataSize == 8 * BITS && structPointerCount == 0 * POINTERS,
"Expected Text, got list of non-bytes.") {
return Text::Builder();
}
size_t size = elementCount / ELEMENTS;
VALIDATE_INPUT(size > 0, "Message contains text that is not NUL-terminated.") {
KJ_REQUIRE(size > 0, "Message contains text that is not NUL-terminated.") {
return Text::Builder();
}
char* cptr = reinterpret_cast<char*>(ptr);
--size; // NUL terminator
VALIDATE_INPUT(cptr[size] == '\0', "Message contains text that is not NUL-terminated.") {
KJ_REQUIRE(cptr[size] == '\0', "Message contains text that is not NUL-terminated.") {
return Text::Builder();
}
......@@ -2002,8 +2017,8 @@ Text::Builder ListBuilder::asText() {
}
Data::Builder ListBuilder::asData() {
VALIDATE_INPUT(structDataSize == 8 * BITS && structPointerCount == 0 * POINTERS,
"Expected Text, got list of non-bytes.") {
KJ_REQUIRE(structDataSize == 8 * BITS && structPointerCount == 0 * POINTERS,
"Expected Text, got list of non-bytes.") {
return Data::Builder();
}
......@@ -2101,21 +2116,21 @@ ListReader ListBuilder::asReader() const {
// ListReader
Text::Reader ListReader::asText() {
VALIDATE_INPUT(structDataSize == 8 * BITS && structPointerCount == 0 * POINTERS,
"Expected Text, got list of non-bytes.") {
KJ_REQUIRE(structDataSize == 8 * BITS && structPointerCount == 0 * POINTERS,
"Expected Text, got list of non-bytes.") {
return Text::Reader();
}
size_t size = elementCount / ELEMENTS;
VALIDATE_INPUT(size > 0, "Message contains text that is not NUL-terminated.") {
KJ_REQUIRE(size > 0, "Message contains text that is not NUL-terminated.") {
return Text::Reader();
}
const char* cptr = reinterpret_cast<const char*>(ptr);
--size; // NUL terminator
VALIDATE_INPUT(cptr[size] == '\0', "Message contains text that is not NUL-terminated.") {
KJ_REQUIRE(cptr[size] == '\0', "Message contains text that is not NUL-terminated.") {
return Text::Reader();
}
......@@ -2123,8 +2138,8 @@ Text::Reader ListReader::asText() {
}
Data::Reader ListReader::asData() {
VALIDATE_INPUT(structDataSize == 8 * BITS && structPointerCount == 0 * POINTERS,
"Expected Text, got list of non-bytes.") {
KJ_REQUIRE(structDataSize == 8 * BITS && structPointerCount == 0 * POINTERS,
"Expected Text, got list of non-bytes.") {
return Data::Reader();
}
......@@ -2132,8 +2147,8 @@ Data::Reader ListReader::asData() {
}
StructReader ListReader::getStructElement(ElementCount index) const {
VALIDATE_INPUT(nestingLimit > 0,
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
KJ_REQUIRE(nestingLimit > 0,
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
return StructReader();
}
......
......@@ -50,9 +50,9 @@ internal::StructReader MessageReader::getRootInternal() {
}
internal::SegmentReader* segment = arena()->tryGetSegment(internal::SegmentId(0));
VALIDATE_INPUT(segment != nullptr &&
segment->containsInterval(segment->getStartPtr(), segment->getStartPtr() + 1),
"Message did not contain a root pointer.") {
KJ_REQUIRE(segment != nullptr &&
segment->containsInterval(segment->getStartPtr(), segment->getStartPtr() + 1),
"Message did not contain a root pointer.") {
return internal::StructReader();
}
......
......@@ -145,9 +145,9 @@ private:
std::map<std::pair<uint, Text::Reader>, uint> members;
#define VALIDATE_SCHEMA(condition, ...) \
VALIDATE_INPUT(condition, ##__VA_ARGS__) { isValid = false; return; }
KJ_REQUIRE(condition, ##__VA_ARGS__) { isValid = false; return; }
#define FAIL_VALIDATE_SCHEMA(...) \
FAIL_VALIDATE_INPUT(__VA_ARGS__) { isValid = false; return; }
KJ_FAIL_REQUIRE(__VA_ARGS__) { isValid = false; return; }
void validate(schema::FileNode::Reader fileNode) {
// Nothing needs validation.
......@@ -472,9 +472,9 @@ private:
Compatibility compatibility;
#define VALIDATE_SCHEMA(condition, ...) \
VALIDATE_INPUT(condition, ##__VA_ARGS__) { compatibility = INCOMPATIBLE; return; }
KJ_REQUIRE(condition, ##__VA_ARGS__) { compatibility = INCOMPATIBLE; return; }
#define FAIL_VALIDATE_SCHEMA(...) \
FAIL_VALIDATE_INPUT(__VA_ARGS__) { compatibility = INCOMPATIBLE; return; }
KJ_FAIL_REQUIRE(__VA_ARGS__) { compatibility = INCOMPATIBLE; return; }
void replacementIsNewer() {
switch (compatibility) {
......@@ -934,7 +934,7 @@ private:
schema::Value::Reader replacement) {
// Note that we test default compatibility only after testing type compatibility, and default
// values have already been validated as matching their types, so this should pass.
RECOVERABLE_ASSERT(value.getBody().which() == replacement.getBody().which()) {
KJ_ASSERT(value.getBody().which() == replacement.getBody().which()) {
compatibility = INCOMPATIBLE;
return;
}
......
......@@ -47,7 +47,7 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
uint8_t* const outMin = reinterpret_cast<uint8_t*>(dst) + minBytes;
kj::ArrayPtr<const byte> buffer = inner.getReadBuffer();
VALIDATE_INPUT(buffer.size() > 0, "Premature end of packed input.") {
KJ_REQUIRE(buffer.size() > 0, "Premature end of packed input.") {
return minBytes; // garbage
}
const uint8_t* __restrict__ in = reinterpret_cast<const uint8_t*>(buffer.begin());
......@@ -55,7 +55,7 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
#define REFRESH_BUFFER() \
inner.skip(buffer.size()); \
buffer = inner.getReadBuffer(); \
VALIDATE_INPUT(buffer.size() > 0, "Premature end of packed input.") { \
KJ_REQUIRE(buffer.size() > 0, "Premature end of packed input.") { \
return minBytes; /* garbage */ \
} \
in = reinterpret_cast<const uint8_t*>(buffer.begin())
......@@ -126,8 +126,8 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
uint runLength = *in++ * sizeof(word);
VALIDATE_INPUT(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary.") {
KJ_REQUIRE(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary.") {
return std::max<size_t>(minBytes, out - reinterpret_cast<uint8_t*>(dst)); // garbage
}
memset(out, 0, runLength);
......@@ -138,8 +138,8 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
uint runLength = *in++ * sizeof(word);
VALIDATE_INPUT(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary.") {
KJ_REQUIRE(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary.") {
return std::max<size_t>(minBytes, out - reinterpret_cast<uint8_t*>(dst)); // garbage
}
......@@ -198,7 +198,7 @@ void PackedInputStream::skip(size_t bytes) {
#define REFRESH_BUFFER() \
inner.skip(buffer.size()); \
buffer = inner.getReadBuffer(); \
VALIDATE_INPUT(buffer.size() > 0, "Premature end of packed input.") return; \
KJ_REQUIRE(buffer.size() > 0, "Premature end of packed input.") { return; } \
in = reinterpret_cast<const uint8_t*>(buffer.begin())
for (;;) {
......@@ -252,8 +252,7 @@ void PackedInputStream::skip(size_t bytes) {
uint runLength = *in++ * sizeof(word);
VALIDATE_INPUT(runLength <= bytes,
"Packed input did not end cleanly on a segment boundary.") {
KJ_REQUIRE(runLength <= bytes, "Packed input did not end cleanly on a segment boundary.") {
return;
}
......@@ -264,8 +263,7 @@ void PackedInputStream::skip(size_t bytes) {
uint runLength = *in++ * sizeof(word);
VALIDATE_INPUT(runLength <= bytes,
"Packed input did not end cleanly on a segment boundary.") {
KJ_REQUIRE(runLength <= bytes, "Packed input did not end cleanly on a segment boundary.") {
return;
}
......
......@@ -106,11 +106,12 @@ void SnappyInputStream::skip(size_t bytes) {
void SnappyInputStream::refill() {
uint32_t length = 0;
InputStreamSnappySource snappySource(inner);
VALIDATE_INPUT(
KJ_REQUIRE(
snappy::RawUncompress(
&snappySource, reinterpret_cast<char*>(buffer.begin()), buffer.size(), &length),
"Snappy decompression failed.") {
length = 1; // garbage
break;
}
bufferAvailable = buffer.slice(0, length);
......
......@@ -42,7 +42,7 @@ FlatArrayMessageReader::FlatArrayMessageReader(
uint segmentCount = table[0].get() + 1;
size_t offset = segmentCount / 2u + 1u;
VALIDATE_INPUT(array.size() >= offset, "Message ends prematurely in segment table.") {
KJ_REQUIRE(array.size() >= offset, "Message ends prematurely in segment table.") {
return;
}
......@@ -52,8 +52,8 @@ FlatArrayMessageReader::FlatArrayMessageReader(
uint segmentSize = table[1].get();
VALIDATE_INPUT(array.size() >= offset + segmentSize,
"Message ends prematurely in first segment.") {
KJ_REQUIRE(array.size() >= offset + segmentSize,
"Message ends prematurely in first segment.") {
return;
}
......@@ -66,7 +66,7 @@ FlatArrayMessageReader::FlatArrayMessageReader(
for (uint i = 1; i < segmentCount; i++) {
uint segmentSize = table[i + 1].get();
VALIDATE_INPUT(array.size() >= offset + segmentSize, "Message ends prematurely.") {
KJ_REQUIRE(array.size() >= offset + segmentSize, "Message ends prematurely.") {
moreSegments = nullptr;
return;
}
......@@ -142,9 +142,10 @@ InputStreamMessageReader::InputStreamMessageReader(
size_t totalWords = segment0Size;
// Reject messages with too many segments for security reasons.
VALIDATE_INPUT(segmentCount < 512, "Message has too many segments.") {
KJ_REQUIRE(segmentCount < 512, "Message has too many segments.") {
segmentCount = 1;
segment0Size = 1;
break;
}
// Read sizes for all segments except the first. Include padding if necessary.
......@@ -159,12 +160,13 @@ InputStreamMessageReader::InputStreamMessageReader(
// Don't accept a message which the receiver couldn't possibly traverse without hitting the
// traversal limit. Without this check, a malicious client could transmit a very large segment
// size to make the receiver allocate excessive space and possibly crash.
VALIDATE_INPUT(totalWords <= options.traversalLimitInWords,
"Message is too large. To increase the limit on the receiving end, see "
"capnproto::ReaderOptions.") {
KJ_REQUIRE(totalWords <= options.traversalLimitInWords,
"Message is too large. To increase the limit on the receiving end, see "
"capnproto::ReaderOptions.") {
segmentCount = 1;
segment0Size = std::min<size_t>(segment0Size, options.traversalLimitInWords);
totalWords = segment0Size;
break;
}
if (scratchSpace.size() < totalWords) {
......
......@@ -162,7 +162,9 @@ static void print(std::ostream& os, DynamicValue::Reader value,
break;
}
case DynamicValue::INTERFACE:
FAIL_RECOVERABLE_ASSERT("Don't know how to print interfaces.") {}
KJ_FAIL_ASSERT("Don't know how to print interfaces.") {
break;
}
break;
case DynamicValue::OBJECT:
os << "(opaque object)";
......
......@@ -30,9 +30,11 @@ namespace internal {
void inlineRequireFailure(const char* file, int line, const char* expectation,
const char* macroArgs, const char* message) {
if (message == nullptr) {
Log::fatalFault(file, line, Exception::Nature::PRECONDITION, expectation, macroArgs);
Log::Fault f(file, line, Exception::Nature::PRECONDITION, 0, expectation, macroArgs);
f.fatal();
} else {
Log::fatalFault(file, line, Exception::Nature::PRECONDITION, expectation, macroArgs, message);
Log::Fault f(file, line, Exception::Nature::PRECONDITION, 0, expectation, macroArgs, message);
f.fatal();
}
}
......
......@@ -30,7 +30,7 @@
#ifndef KJ_COMMON_H_
#define KJ_COMMON_H_
#if __cplusplus < 201103L
#if __cplusplus < 201103L && !__CDT_PARSER__
#error "This code requires C++11. Either your compiler does not support it or it is not enabled."
#ifdef __GNUC__
// Compiler claims compatibility with GCC, so presumably supports -std.
......
......@@ -32,9 +32,8 @@ namespace kj {
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature) {
static const char* NATURE_STRINGS[] = {
"precondition not met",
"requirement not met",
"bug in code",
"invalid input data",
"error from OS",
"network failure",
"error"
......@@ -174,7 +173,7 @@ void ExceptionCallback::logMessage(StringPtr text) {
}
void ExceptionCallback::useProcessWide() {
RECOVERABLE_REQUIRE(globalCallback == nullptr,
KJ_REQUIRE(globalCallback == nullptr,
"Can't register multiple global ExceptionCallbacks at once.") {
return;
}
......
......@@ -49,7 +49,6 @@ public:
PRECONDITION,
LOCAL_BUG,
INPUT,
OS_ERROR,
NETWORK_FAILURE,
OTHER
......
......@@ -188,8 +188,9 @@ ArrayPtr<const byte> ArrayInputStream::getReadBuffer() {
size_t ArrayInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
size_t n = std::min(maxBytes, array.size());
size_t result = n;
VALIDATE_INPUT(n >= minBytes, "ArrayInputStream ended prematurely.") {
KJ_REQUIRE(n >= minBytes, "ArrayInputStream ended prematurely.") {
result = minBytes; // garbage
break;
}
memcpy(dst, array.begin(), n);
array = array.slice(n, array.size());
......@@ -197,8 +198,9 @@ size_t ArrayInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
}
void ArrayInputStream::skip(size_t bytes) {
VALIDATE_INPUT(array.size() >= bytes, "ArrayInputStream ended prematurely.") {
KJ_REQUIRE(array.size() >= bytes, "ArrayInputStream ended prematurely.") {
bytes = array.size();
break;
}
array = array.slice(bytes, array.size());
}
......@@ -228,7 +230,9 @@ void ArrayOutputStream::write(const void* src, size_t size) {
AutoCloseFd::~AutoCloseFd() {
if (fd >= 0 && close(fd) < 0) {
FAIL_RECOVERABLE_SYSCALL("close", errno, fd);
FAIL_SYSCALL("close", errno, fd) {
break;
}
}
}
......@@ -240,8 +244,9 @@ size_t FdInputStream::read(void* buffer, size_t minBytes, size_t maxBytes) {
byte* max = pos + maxBytes;
while (pos < min) {
ssize_t n = KJ_SYSCALL(::read(fd, pos, max - pos), fd);
VALIDATE_INPUT(n > 0, "Premature EOF") {
ssize_t n;
KJ_SYSCALL(n = ::read(fd, pos, max - pos), fd);
KJ_REQUIRE(n > 0, "Premature EOF") {
return minBytes;
}
pos += n;
......@@ -256,7 +261,8 @@ void FdOutputStream::write(const void* buffer, size_t size) {
const char* pos = reinterpret_cast<const char*>(buffer);
while (size > 0) {
ssize_t n = KJ_SYSCALL(::write(fd, pos, size), fd);
ssize_t n;
KJ_SYSCALL(n = ::write(fd, pos, size), fd);
KJ_ASSERT(n > 0, "write() returned zero.");
pos += n;
size -= n;
......@@ -280,7 +286,8 @@ void FdOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
}
while (current < iov.end()) {
ssize_t n = KJ_SYSCALL(::writev(fd, current, iov.end() - current), fd);
ssize_t n;
KJ_SYSCALL(n = ::writev(fd, current, iov.end() - current), fd);
KJ_ASSERT(n > 0, "writev() returned zero.");
while (static_cast<size_t>(n) >= current->iov_len) {
......
......@@ -101,18 +101,39 @@ TEST(Logging, Log) {
mockCallback.text);
mockCallback.text.clear();
KJ_DBG("Some debug text."); line = __LINE__;
EXPECT_EQ("log message: debug: " + fileLine(__FILE__, line) + ": Some debug text.\n",
mockCallback.text);
mockCallback.text.clear();
// INFO logging is disabled by default.
KJ_LOG(INFO, "Info."); line = __LINE__;
EXPECT_EQ("", mockCallback.text);
mockCallback.text.clear();
// Enable it.
Log::setLogLevel(Log::Severity::INFO);
KJ_LOG(INFO, "Some text."); line = __LINE__;
EXPECT_EQ("log message: info: " + fileLine(__FILE__, line) + ": Some text.\n",
mockCallback.text);
mockCallback.text.clear();
// Back to default.
Log::setLogLevel(Log::Severity::WARNING);
KJ_ASSERT(1 == 1);
EXPECT_THROW(KJ_ASSERT(1 == 2), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected "
"1 == 2\n", mockCallback.text);
mockCallback.text.clear();
RECOVERABLE_ASSERT(1 == 1) {
KJ_ASSERT(1 == 1) {
ADD_FAILURE() << "Shouldn't call recovery code when check passes.";
break;
};
bool recovered = false;
RECOVERABLE_ASSERT(1 == 2, "1 is not 2") { recovered = true; } line = __LINE__;
KJ_ASSERT(1 == 2, "1 is not 2") { recovered = true; break; } line = __LINE__;
EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) + ": bug in code: expected "
"1 == 2; 1 is not 2\n", mockCallback.text);
EXPECT_TRUE(recovered);
......@@ -124,11 +145,11 @@ TEST(Logging, Log) {
mockCallback.text.clear();
EXPECT_THROW(KJ_REQUIRE(1 == 2, i, "hi", str), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": precondition not met: expected "
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": requirement not met: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
mockCallback.text.clear();
EXPECT_THROW(KJ_ASSERT(false, "foo"), MockException); line = __LINE__;
EXPECT_THROW(KJ_FAIL_ASSERT("foo"), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: foo\n",
mockCallback.text);
mockCallback.text.clear();
......@@ -142,7 +163,8 @@ TEST(Logging, Syscall) {
int i = 123;
const char* str = "foo";
int fd = KJ_SYSCALL(dup(STDIN_FILENO));
int fd;
KJ_SYSCALL(fd = dup(STDIN_FILENO));
KJ_SYSCALL(close(fd));
EXPECT_THROW(KJ_SYSCALL(close(fd), i, "bar", str), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": error from OS: close(fd): "
......@@ -151,7 +173,7 @@ TEST(Logging, Syscall) {
int result = 0;
bool recovered = false;
RECOVERABLE_SYSCALL(result = close(fd), i, "bar", str) { recovered = true; } line = __LINE__;
KJ_SYSCALL(result = close(fd), i, "bar", str) { recovered = true; break; } line = __LINE__;
EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) + ": error from OS: close(fd): "
+ strerror(EBADF) + "; i = 123; bar; str = foo\n", mockCallback.text);
EXPECT_LT(result, 0);
......
......@@ -29,14 +29,15 @@
namespace kj {
Log::Severity Log::minSeverity = Log::Severity::INFO;
Log::Severity Log::minSeverity = Log::Severity::WARNING;
ArrayPtr<const char> KJ_STRINGIFY(Log::Severity severity) {
static const char* SEVERITY_STRINGS[] = {
"info",
"warning",
"error",
"fatal"
"fatal",
"debug"
};
const char* s = SEVERITY_STRINGS[static_cast<uint>(severity)];
......@@ -110,6 +111,10 @@ static String makeDescription(DescriptionStyle style, const char* code, int erro
}
}
if (style == ASSERTION && code == nullptr) {
style = LOG;
}
{
StringPtr expected = "expected ";
StringPtr codeArray = style == LOG ? nullptr : StringPtr(code);
......@@ -117,11 +122,6 @@ static String makeDescription(DescriptionStyle style, const char* code, int erro
StringPtr delim = "; ";
StringPtr colon = ": ";
if (style == ASSERTION && strcmp(code, "false") == 0) {
// Don't print "expected false", that's silly.
style = LOG;
}
StringPtr sysErrorArray;
#if __USE_GNU
char buffer[256];
......@@ -194,38 +194,28 @@ void Log::logInternal(const char* file, int line, Severity severity, const char*
makeDescription(LOG, nullptr, 0, macroArgs, argValues), '\n'));
}
void Log::recoverableFaultInternal(
const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
getExceptionCallback().onRecoverableException(
Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(ASSERTION, condition, 0, macroArgs, argValues)));
Log::Fault::~Fault() noexcept(false) {
if (exception != nullptr) {
Exception copy = mv(*exception);
delete exception;
getExceptionCallback().onRecoverableException(mv(copy));
}
}
void Log::fatalFaultInternal(
const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
getExceptionCallback().onFatalException(
Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(ASSERTION, condition, 0, macroArgs, argValues)));
void Log::Fault::fatal() {
Exception copy = mv(*exception);
delete exception;
exception = nullptr;
getExceptionCallback().onFatalException(mv(copy));
abort();
}
void Log::recoverableFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<String> argValues) {
getExceptionCallback().onRecoverableException(
Exception(Exception::Nature::OS_ERROR, Exception::Durability::PERMANENT, file, line,
makeDescription(SYSCALL, call, errorNumber, macroArgs, argValues)));
}
void Log::fatalFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<String> argValues) {
getExceptionCallback().onFatalException(
Exception(Exception::Nature::OS_ERROR, Exception::Durability::PERMANENT, file, line,
makeDescription(SYSCALL, call, errorNumber, macroArgs, argValues)));
abort();
void Log::Fault::init(
const char* file, int line, Exception::Nature nature, int errorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
exception = new Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(nature == Exception::Nature::OS_ERROR ? SYSCALL : ASSERTION,
condition, errorNumber, macroArgs, argValues));
}
void Log::addContextToInternal(Exception& exception, const char* file, int line,
......
......@@ -36,43 +36,48 @@
//
// * `KJ_LOG(severity, ...)`: Just writes a log message, to stderr by default (but you can
// intercept messages by implementing an ExceptionCallback). `severity` is `INFO`, `WARNING`,
// `ERROR`, or `FATAL`. If the severity is not higher than the global logging threshold, nothing
// will be written and in fact the log message won't even be evaluated.
// `ERROR`, or `FATAL`. By default, `INFO` logs are not written, but for command-line apps the
// user should be able to pass a flag like `--verbose` to enable them. Other log levels are
// enabled by default. Log messages -- like exceptions -- can be intercepted by registering an
// ExceptionCallback.
//
// * `KJ_DBG(...)`: Like `KJ_LOG`, but intended specifically for temporary log lines added while
// debugging a particular problem. Calls to `KJ_DBG` should always be deleted before committing
// code. It is suggested that you set up a pre-commit hook that checks for this.
//
// * `KJ_ASSERT(condition, ...)`: Throws an exception if `condition` is false, or aborts if
// exceptions are disabled. This macro should be used to check for bugs in the surrounding code
// and its dependencies, but NOT to check for invalid input.
// and its dependencies, but NOT to check for invalid input. The macro may be followed by a
// brace-delimited code block; if so, the block will be executed in the case where the assertion
// fails, before throwing the exception. If control jumps out of the block (e.g. with "break",
// "return", or "goto"), then the error is considered "recoverable" -- in this case, if
// exceptions are disabled, execution will continue normally rather than aborting (but if
// exceptions are enabled, an exception will still be thrown on exiting the block). A "break"
// statement in particular will jump to the code immediately after the block (it does not break
// any surrounding loop or switch). Example:
//
// KJ_ASSERT(value >= 0, "Value cannot be negative.", value) {
// // Assertion failed. Set value to zero to "recover".
// value = 0;
// // Don't abort if exceptions are disabled. Continue normally.
// // (Still throw an exception if they are enabled, though.)
// break;
// }
// // When exceptions are disabled, we'll get here even if the assertion fails.
// // Otherwise, we get here only if the assertion passes.
//
// * `KJ_REQUIRE(condition, ...)`: Like `KJ_ASSERT` but used to check preconditions -- e.g. to
// validate parameters passed from a caller. A failure indicates that the caller is buggy.
//
// * `RECOVERABLE_ASSERT(condition, ...) { ... }`: Like `KJ_ASSERT` except that if exceptions are
// disabled, instead of aborting, the following code block will be executed. This block should
// do whatever it can to fill in dummy values so that the code can continue executing, even if
// this means the eventual output will be garbage.
//
// * `RECOVERABLE_REQUIRE(condition, ...) { ... }`: Like `RECOVERABLE_ASSERT` and `KJ_REQUIRE`.
//
// * `VALIDATE_INPUT(condition, ...) { ... }`: Like `RECOVERABLE_PRECOND` but used to validate
// input that may have come from the user or some other untrusted source. Recoverability is
// required in this case.
//
// * `KJ_SYSCALL(code, ...)`: Executes `code` assuming it makes a system call. A negative return
// value is considered an error. EINTR is handled by retrying. Other errors are handled by
// throwing an exception. The macro also returns the call's result. For example, the following
// calls `open()` and includes the file name in any error message:
//
// int fd = KJ_SYSCALL(open(filename, O_RDONLY), filename);
//
// * `RECOVERABLE_SYSCALL(code, ...) { ... }`: Like `RECOVERABLE_ASSERT` and `SYSCALL`. Note that
// unfortunately this macro cannot return a value since it implements control flow, but you can
// assign to a variable *inside* the parameter instead:
// * `KJ_SYSCALL(code, ...)`: Executes `code` assuming it makes a system call. A negative result
// is considered an error, with error code reported via `errno`. EINTR is handled by retrying.
// Other errors are handled by throwing an exception. If you need to examine the return code,
// assign it to a variable like so:
//
// int fd;
// RECOVERABLE_SYSCALL(fd = open(filename, O_RDONLY), filename) {
// // Failed. Open /dev/null instead.
// fd = SYSCALL(open("/dev/null", O_RDONLY));
// }
// KJ_SYSCALL(fd = open(filename, O_RDONLY), filename);
//
// `KJ_SYSCALL` can be followed by a recovery block, just like `KJ_ASSERT`.
//
// * `KJ_CONTEXT(...)`: Notes additional contextual information relevant to any exceptions thrown
// from within the current scope. That is, until control exits the block in which KJ_CONTEXT()
......@@ -102,12 +107,17 @@
namespace kj {
class Log {
// Mostly-internal
public:
enum class Severity {
INFO, // Information useful for debugging. No problem detected.
INFO, // Information describing what the code is up to, which users may request to see
// with a flag like `--verbose`. Does not indicate a problem. Not printed by
// default; you must call setLogLevel(INFO) to enable.
WARNING, // A problem was detected but execution can continue with correct output.
ERROR, // Something is wrong, but execution can continue with garbage output.
FATAL // Something went wrong, and execution cannot continue.
FATAL, // Something went wrong, and execution cannot continue.
DEBUG // Temporary debug logging. See KJ_DBG.
// Make sure to update the stringifier if you add a new severity level.
};
......@@ -122,32 +132,35 @@ public:
static void log(const char* file, int line, Severity severity, const char* macroArgs,
Params&&... params);
template <typename... Params>
static void recoverableFault(const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params);
class Fault {
public:
template <typename... Params>
Fault(const char* file, int line, Exception::Nature nature, int errorNumber,
const char* condition, const char* macroArgs, Params&&... params);
~Fault() noexcept(false);
template <typename... Params>
static void fatalFault(const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params)
KJ_NORETURN;
void fatal() KJ_NORETURN;
// Throw the exception.
template <typename Call, typename... Params>
static bool recoverableSyscall(Call&& call, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params);
private:
void init(const char* file, int line, Exception::Nature nature, int errorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
template <typename Call, typename... Params>
static auto syscall(Call&& call, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) -> decltype(call());
Exception* exception;
};
template <typename... Params>
static void reportFailedRecoverableSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params);
class SyscallResult {
public:
inline SyscallResult(int errorNumber): errorNumber(errorNumber) {}
inline operator void*() { return errorNumber == 0 ? this : nullptr; }
inline int getErrorNumber() { return errorNumber; }
template <typename... Params>
static void reportFailedSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params);
private:
int errorNumber;
};
template <typename Call>
static SyscallResult syscall(Call&& call);
class Context: public ExceptionCallback {
public:
......@@ -187,20 +200,6 @@ private:
static void logInternal(const char* file, int line, Severity severity, const char* macroArgs,
ArrayPtr<String> argValues);
static void recoverableFaultInternal(
const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
static void fatalFaultInternal(
const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues)
KJ_NORETURN;
static void recoverableFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<String> argValues);
static void fatalFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<String> argValues)
KJ_NORETURN;
static void addContextToInternal(Exception& exception, const char* file, int line,
const char* macroArgs, ArrayPtr<String> argValues);
......@@ -215,53 +214,33 @@ ArrayPtr<const char> KJ_STRINGIFY(Log::Severity severity);
::kj::Log::log(__FILE__, __LINE__, ::kj::Log::Severity::severity, \
#__VA_ARGS__, __VA_ARGS__)
#define KJ_FAULT(nature, cond, ...) \
if (KJ_EXPECT_TRUE(cond)) {} else \
::kj::Log::fatalFault(__FILE__, __LINE__, \
::kj::Exception::Nature::nature, #cond, #__VA_ARGS__, ##__VA_ARGS__)
#define KJ_DBG(...) KJ_LOG(DEBUG, ##__VA_ARGS__)
#define RECOVERABLE_FAULT(nature, cond, ...) \
#define _kJ_FAULT(nature, cond, ...) \
if (KJ_EXPECT_TRUE(cond)) {} else \
if (::kj::Log::recoverableFault(__FILE__, __LINE__, \
::kj::Exception::Nature::nature, #cond, #__VA_ARGS__, ##__VA_ARGS__), false) {} \
else
#define KJ_ASSERT(...) KJ_FAULT(LOCAL_BUG, __VA_ARGS__)
#define RECOVERABLE_ASSERT(...) RECOVERABLE_FAULT(LOCAL_BUG, __VA_ARGS__)
#define KJ_REQUIRE(...) KJ_FAULT(PRECONDITION, __VA_ARGS__)
#define RECOVERABLE_REQUIRE(...) RECOVERABLE_FAULT(PRECONDITION, __VA_ARGS__)
#define VALIDATE_INPUT(...) RECOVERABLE_FAULT(INPUT, __VA_ARGS__)
#define KJ_FAIL_ASSERT(...) KJ_ASSERT(false, ##__VA_ARGS__)
#define FAIL_RECOVERABLE_ASSERT(...) RECOVERABLE_ASSERT(false, ##__VA_ARGS__)
#define KJ_FAIL_REQUIRE(...) KJ_REQUIRE(false, ##__VA_ARGS__)
#define FAIL_RECOVERABLE_REQUIRE(...) RECOVERABLE_REQUIRE(false, ##__VA_ARGS__)
#define FAIL_VALIDATE_INPUT(...) VALIDATE_INPUT(false, ##__VA_ARGS__)
for (::kj::Log::Fault f(__FILE__, __LINE__, ::kj::Exception::Nature::nature, 0, \
#cond, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
#define KJ_SYSCALL(call, ...) \
::kj::Log::syscall( \
[&](){return (call);}, __FILE__, __LINE__, #call, #__VA_ARGS__, ##__VA_ARGS__)
#define _kJ_FAIL_FAULT(nature, ...) \
for (::kj::Log::Fault f(__FILE__, __LINE__, ::kj::Exception::Nature::nature, 0, \
nullptr, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
#define RECOVERABLE_SYSCALL(call, ...) \
if (::kj::Log::recoverableSyscall( \
[&](){return (call);}, __FILE__, __LINE__, #call, #__VA_ARGS__, ##__VA_ARGS__)) {} \
else
#define KJ_ASSERT(...) _kJ_FAULT(LOCAL_BUG, ##__VA_ARGS__)
#define KJ_REQUIRE(...) _kJ_FAULT(PRECONDITION, ##__VA_ARGS__)
#define KJ_FAIL_ASSERT(...) _kJ_FAIL_FAULT(LOCAL_BUG, ##__VA_ARGS__)
#define KJ_FAIL_REQUIRE(...) _kJ_FAIL_FAULT(PRECONDITION, ##__VA_ARGS__)
#define KJ_SYSCALL(call, ...) \
if (auto _kjSyscallResult = ::kj::Log::syscall([&](){return (call);})) {} else \
for (::kj::Log::Fault f( \
__FILE__, __LINE__, ::kj::Exception::Nature::OS_ERROR, \
_kjSyscallResult.getErrorNumber(), #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
#define FAIL_SYSCALL(code, errorNumber, ...) \
do { \
/* make sure to read error number before doing anything else that could change it */ \
int _errorNumber = errorNumber; \
::kj::Log::reportFailedSyscall( \
_errorNumber, __FILE__, __LINE__, #code, #__VA_ARGS__, ##__VA_ARGS__); \
} while (false)
#define FAIL_RECOVERABLE_SYSCALL(code, errorNumber, ...) \
do { \
/* make sure to read error number before doing anything else that could change it */ \
int _errorNumber = errorNumber; \
::kj::Log::reportFailedRecoverableSyscall( \
_errorNumber, __FILE__, __LINE__, #code, #__VA_ARGS__, ##__VA_ARGS__); \
} while (false)
for (::kj::Log::Fault f( \
__FILE__, __LINE__, ::kj::Exception::Nature::OS_ERROR, \
errorNumber, code, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
#define KJ_CONTEXT(...) \
auto _kjContextFunc = [&](::kj::Exception& exception) { \
......@@ -273,15 +252,11 @@ ArrayPtr<const char> KJ_STRINGIFY(Log::Severity severity);
#ifdef NDEBUG
#define KJ_DLOG(...) do {} while (false)
#define KJ_DASSERT(...) do {} while (false)
#define KJ_RECOVERABLE_DASSERT(...) do {} while (false)
#define KJ_DREQUIRE(...) do {} while (false)
#define KJ_RECOVERABLE_DREQUIRE(...) do {} while (false)
#else
#define KJ_DLOG LOG
#define KJ_DASSERT KJ_ASSERT
#define KJ_RECOVERABLE_DASSERT RECOVERABLE_ASSERT
#define KJ_DREQUIRE KJ_REQUIRE
#define KJ_RECOVERABLE_DREQUIRE RECOVERABLE_REQUIRE
#endif
template <typename... Params>
......@@ -292,72 +267,24 @@ void Log::log(const char* file, int line, Severity severity, const char* macroAr
}
template <typename... Params>
void Log::recoverableFault(const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params) {
String argValues[sizeof...(Params)] = {str(params)...};
recoverableFaultInternal(file, line, nature, condition, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
template <typename... Params>
void Log::fatalFault(const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params) {
Log::Fault::Fault(const char* file, int line, Exception::Nature nature, int errorNumber,
const char* condition, const char* macroArgs, Params&&... params)
: exception(nullptr) {
String argValues[sizeof...(Params)] = {str(params)...};
fatalFaultInternal(file, line, nature, condition, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
init(file, line, nature, errorNumber, condition, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
template <typename Call, typename... Params>
bool Log::recoverableSyscall(Call&& call, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) {
int result;
while ((result = call()) < 0) {
template <typename Call>
Log::SyscallResult Log::syscall(Call&& call) {
while (call() < 0) {
int errorNum = getOsErrorNumber();
// getOsErrorNumber() returns -1 to indicate EINTR
if (errorNum != -1) {
String argValues[sizeof...(Params)] = {str(params)...};
recoverableFailedSyscallInternal(file, line, callText, errorNum,
macroArgs, arrayPtr(argValues, sizeof...(Params)));
return false;
return SyscallResult(errorNum);
}
}
return true;
}
#ifndef __CDT_PARSER__ // Eclipse dislikes the late return spec.
template <typename Call, typename... Params>
auto Log::syscall(Call&& call, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) -> decltype(call()) {
decltype(call()) result;
while ((result = call()) < 0) {
int errorNum = getOsErrorNumber();
// getOsErrorNumber() returns -1 to indicate EINTR
if (errorNum != -1) {
String argValues[sizeof...(Params)] = {str(params)...};
fatalFailedSyscallInternal(file, line, callText, errorNum,
macroArgs, arrayPtr(argValues, sizeof...(Params)));
}
}
return result;
}
#endif
template <typename... Params>
void Log::reportFailedRecoverableSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) {
String argValues[sizeof...(Params)] = {str(params)...};
recoverableFailedSyscallInternal(file, line, callText, errorNumber, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
template <typename... Params>
void Log::reportFailedSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) {
String argValues[sizeof...(Params)] = {str(params)...};
fatalFailedSyscallInternal(file, line, callText, errorNumber, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
return SyscallResult(0);
}
template <typename... Params>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment