Commit 98e6519e authored by Kenton Varda's avatar Kenton Varda

Refactor value expression compilation code using new Orphan<DynamicValue>.

parent c1f3e3ff
......@@ -529,6 +529,7 @@ NodeTranslator::NodeTranslator(
const Declaration::Reader& decl, Orphan<schema::Node> wipNodeParam,
bool compileAnnotations)
: resolver(resolver), errorReporter(errorReporter),
orphanage(Orphanage::getForMessageContaining(wipNodeParam.get())),
compileAnnotations(compileAnnotations), wipNode(kj::mv(wipNodeParam)) {
compileNode(decl, wipNode.get());
}
......@@ -1229,8 +1230,7 @@ private:
}
schema::Node::Builder newGroupNode(schema::Node::Reader parent, kj::StringPtr name) {
auto orphan = Orphanage::getForMessageContaining(translator.wipNode.get())
.newOrphan<schema::Node>();
auto orphan = translator.orphanage.newOrphan<schema::Node>();
auto node = orphan.get();
// We'll set the ID and scope ID later.
......@@ -1386,147 +1386,6 @@ void NodeTranslator::compileDefaultDefaultValue(
}
}
class NodeTranslator::DynamicSlot {
// Acts like a pointer to a field or list element. The target's value can be set or initialized.
// This is useful when recursively compiling values.
//
// TODO(someday): The Dynamic API should support something like this directly.
public:
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Field field)
: type(FIELD), struct_{structBuilder, field} {}
DynamicSlot(DynamicList::Builder listBuilder, uint index)
: type(ELEMENT), list{listBuilder, index} {}
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Field field,
StructSchema structFieldSchema)
: type(STRUCT_OBJECT_FIELD), struct_{structBuilder, field},
structFieldSchema(structFieldSchema) {}
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Field field,
ListSchema listFieldSchema)
: type(LIST_OBJECT_FIELD), struct_{structBuilder, field},
listFieldSchema(listFieldSchema) {}
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Field field,
EnumSchema enumFieldSchema)
: type(RAW_ENUM_FIELD), struct_{structBuilder, field},
enumFieldSchema(enumFieldSchema) {}
DynamicStruct::Builder initStruct() {
switch (type) {
case FIELD: return struct_.builder.init(struct_.field).as<DynamicStruct>();
case ELEMENT: return list.builder[list.index].as<DynamicStruct>();
case STRUCT_OBJECT_FIELD:
return struct_.builder.initObject(struct_.field, structFieldSchema);
case LIST_OBJECT_FIELD: KJ_FAIL_REQUIRE("Type mismatch.");
case RAW_ENUM_FIELD: KJ_FAIL_REQUIRE("Type mismatch.");
}
KJ_FAIL_ASSERT("can't get here");
}
DynamicList::Builder initList(uint size) {
switch (type) {
case FIELD: return struct_.builder.init(struct_.field, size).as<DynamicList>();
case ELEMENT: return list.builder.init(list.index, size).as<DynamicList>();
case STRUCT_OBJECT_FIELD: KJ_FAIL_REQUIRE("Type mismatch.");
case LIST_OBJECT_FIELD:
return struct_.builder.initObject(struct_.field, listFieldSchema, size);
case RAW_ENUM_FIELD: KJ_FAIL_REQUIRE("Type mismatch.");
}
KJ_FAIL_ASSERT("can't get here");
}
void set(DynamicValue::Reader value) {
switch (type) {
case FIELD: struct_.builder.set(struct_.field, value); return;
case ELEMENT: list.builder.set(list.index, value); return;
case STRUCT_OBJECT_FIELD: struct_.builder.set(struct_.field, value); return;
case LIST_OBJECT_FIELD: struct_.builder.set(struct_.field, value); return;
case RAW_ENUM_FIELD:
struct_.builder.set(struct_.field, value.as<DynamicEnum>().getRaw());
return;
}
KJ_FAIL_ASSERT("can't get here");
}
kj::Maybe<uint64_t> getEnumType() {
// If the slot type is an enum, get its type ID. Otherwise return nullptr.
//
// This is really ugly.
switch (type) {
case FIELD: return enumIdForField(struct_.field);
case ELEMENT: {
if (list.builder.getSchema().whichElementType() == schema::Type::ENUM) {
return list.builder.getSchema().getEnumElementType().getProto().getId();
}
return nullptr;
}
case STRUCT_OBJECT_FIELD: return nullptr;
case LIST_OBJECT_FIELD: return nullptr;
case RAW_ENUM_FIELD: return enumFieldSchema.getProto().getId();
}
KJ_FAIL_ASSERT("can't get here");
}
private:
enum Type {
FIELD, ELEMENT, STRUCT_OBJECT_FIELD, LIST_OBJECT_FIELD, RAW_ENUM_FIELD
};
Type type;
union {
struct {
DynamicStruct::Builder builder;
StructSchema::Field field;
} struct_;
struct {
DynamicList::Builder builder;
uint index;
} list;
};
union {
StructSchema structFieldSchema;
ListSchema listFieldSchema;
EnumSchema enumFieldSchema;
};
static kj::Maybe<uint64_t> enumIdForField(StructSchema::Field field) {
schema::Field::Reader proto = field.getProto();
if (proto.isNonGroup()) {
auto type = proto.getNonGroup().getType();
if (type.isEnum()) {
return type.getEnum();
}
}
return nullptr;
}
};
static kj::StringPtr getValueUnionFieldNameFor(schema::Type::Which type) {
switch (type) {
case schema::Type::VOID: return "void";
case schema::Type::BOOL: return "bool";
case schema::Type::INT8: return "int8";
case schema::Type::INT16: return "int16";
case schema::Type::INT32: return "int32";
case schema::Type::INT64: return "int64";
case schema::Type::UINT8: return "uint8";
case schema::Type::UINT16: return "uint16";
case schema::Type::UINT32: return "uint32";
case schema::Type::UINT64: return "uint64";
case schema::Type::FLOAT32: return "float32";
case schema::Type::FLOAT64: return "float64";
case schema::Type::TEXT: return "text";
case schema::Type::DATA: return "data";
case schema::Type::LIST: return "list";
case schema::Type::ENUM: return "enum";
case schema::Type::STRUCT: return "struct";
case schema::Type::INTERFACE: return "interface";
case schema::Type::OBJECT: return "object";
}
KJ_FAIL_ASSERT("Unknown type.");
}
void NodeTranslator::compileBootstrapValue(ValueExpression::Reader source,
schema::Type::Reader type,
schema::Value::Builder target) {
......@@ -1551,154 +1410,345 @@ void NodeTranslator::compileBootstrapValue(ValueExpression::Reader source,
void NodeTranslator::compileValue(ValueExpression::Reader source, schema::Type::Reader type,
schema::Value::Builder target, bool isBootstrap) {
auto valueUnion = toDynamic(target);
auto field = valueUnion.getSchema().getFieldByName(
getValueUnionFieldNameFor(type.which()));
switch (type.which()) {
case schema::Type::LIST:
KJ_IF_MAYBE(listSchema, makeListSchemaOf(type.getList())) {
DynamicSlot slot(valueUnion, field, *listSchema);
compileValue(source, slot, isBootstrap);
kj::StringPtr fieldName = KJ_ASSERT_NONNULL(toDynamic(type).which()).getProto().getName();
KJ_IF_MAYBE(value, compileValue(source, type, isBootstrap)) {
if (type.isEnum()) {
target.setEnum(value->getReader().as<DynamicEnum>().getRaw());
} else {
toDynamic(target).adopt(fieldName, kj::mv(*value));
}
}
}
kj::Maybe<Orphan<DynamicValue>> NodeTranslator::compileValue(
ValueExpression::Reader src, schema::Type::Reader type, bool isBootstrap) {
Orphan<DynamicValue> result = compileValueInner(src, type, isBootstrap);
switch (result.getType()) {
case DynamicValue::UNKNOWN:
// Error already reported.
return nullptr;
case DynamicValue::VOID:
if (type.isVoid()) {
return kj::mv(result);
}
break;
case schema::Type::STRUCT:
KJ_IF_MAYBE(structSchema, resolver.resolveBootstrapSchema(type.getStruct())) {
DynamicSlot slot(valueUnion, field, structSchema->asStruct());
compileValue(source, slot, isBootstrap);
case DynamicValue::BOOL:
if (type.isBool()) {
return kj::mv(result);
}
break;
case schema::Type::ENUM:
KJ_IF_MAYBE(enumSchema, resolver.resolveBootstrapSchema(type.getEnum())) {
DynamicSlot slot(valueUnion, field, enumSchema->asEnum());
compileValue(source, slot, isBootstrap);
case DynamicValue::INT: {
int64_t value = result.getReader().as<int64_t>();
if (value < 0) {
int64_t minValue = 1;
switch (type.which()) {
case schema::Type::INT8: minValue = std::numeric_limits<int8_t>::min(); break;
case schema::Type::INT16: minValue = std::numeric_limits<int16_t>::min(); break;
case schema::Type::INT32: minValue = std::numeric_limits<int32_t>::min(); break;
case schema::Type::INT64: minValue = std::numeric_limits<int64_t>::min(); break;
case schema::Type::UINT8: minValue = std::numeric_limits<uint8_t>::min(); break;
case schema::Type::UINT16: minValue = std::numeric_limits<uint16_t>::min(); break;
case schema::Type::UINT32: minValue = std::numeric_limits<uint32_t>::min(); break;
case schema::Type::UINT64: minValue = std::numeric_limits<uint64_t>::min(); break;
case schema::Type::FLOAT32:
case schema::Type::FLOAT64:
// Any integer is acceptable.
minValue = std::numeric_limits<int64_t>::min();
break;
default: break;
}
if (minValue == 1) break;
if (value < minValue) {
errorReporter.addErrorOn(src, "Integer value out of range.");
result = minValue;
}
return kj::mv(result);
}
// No break -- value is positive, so we can just go on to the uint case below.
}
case DynamicValue::UINT: {
uint64_t maxValue = 0;
switch (type.which()) {
case schema::Type::INT8: maxValue = std::numeric_limits<int8_t>::max(); break;
case schema::Type::INT16: maxValue = std::numeric_limits<int16_t>::max(); break;
case schema::Type::INT32: maxValue = std::numeric_limits<int32_t>::max(); break;
case schema::Type::INT64: maxValue = std::numeric_limits<int64_t>::max(); break;
case schema::Type::UINT8: maxValue = std::numeric_limits<uint8_t>::max(); break;
case schema::Type::UINT16: maxValue = std::numeric_limits<uint16_t>::max(); break;
case schema::Type::UINT32: maxValue = std::numeric_limits<uint32_t>::max(); break;
case schema::Type::UINT64: maxValue = std::numeric_limits<uint64_t>::max(); break;
case schema::Type::FLOAT32:
case schema::Type::FLOAT64:
// Any integer is acceptable.
maxValue = std::numeric_limits<uint64_t>::max();
break;
default: break;
}
if (maxValue == 0) break;
if (result.getReader().as<uint64_t>() > maxValue) {
errorReporter.addErrorOn(src, "Integer value out of range.");
result = maxValue;
}
return kj::mv(result);
}
case DynamicValue::FLOAT:
if (type.isFloat32() || type.isFloat64()) {
return kj::mv(result);
}
break;
default:
DynamicSlot slot(valueUnion, field);
compileValue(source, slot, isBootstrap);
case DynamicValue::TEXT:
if (type.isText()) {
return kj::mv(result);
}
break;
case DynamicValue::DATA:
if (type.isData()) {
return kj::mv(result);
}
break;
}
}
void NodeTranslator::compileValue(ValueExpression::Reader src, DynamicSlot& dst, bool isBootstrap) {
// We rely on the dynamic API to detect type errors and throw exceptions.
//
// TODO(cleanup): We should perhaps ensure that all exceptions that this might throw are
// recoverable, so that this doesn't crash if -fno-exceptions is enabled. Or create a better
// way to test for type compatibility without throwing.
KJ_IF_MAYBE(exception, kj::runCatchingExceptions(
[&]() { compileValueInner(src, dst, isBootstrap); })) {
errorReporter.addErrorOn(src, "Type mismatch.");
case DynamicValue::LIST:
if (type.isList()) {
KJ_IF_MAYBE(schema, makeListSchemaOf(type.getList())) {
if (result.getReader().as<DynamicList>().getSchema() == *schema) {
return kj::mv(result);
}
} else {
return nullptr;
}
}
break;
case DynamicValue::ENUM:
if (type.isEnum()) {
KJ_IF_MAYBE(schema, resolver.resolveBootstrapSchema(type.getEnum())) {
if (result.getReader().as<DynamicEnum>().getSchema() == *schema) {
return kj::mv(result);
}
} else {
return nullptr;
}
}
break;
case DynamicValue::STRUCT:
if (type.isStruct()) {
KJ_IF_MAYBE(schema, resolver.resolveBootstrapSchema(type.getStruct())) {
if (result.getReader().as<DynamicStruct>().getSchema() == *schema) {
return kj::mv(result);
}
} else {
return nullptr;
}
}
break;
case DynamicValue::INTERFACE:
KJ_FAIL_ASSERT("Interfaces can't have literal values.");
case DynamicValue::OBJECT:
KJ_FAIL_ASSERT("Objects can't have literal values.");
}
errorReporter.addErrorOn(src, kj::str("Type mismatch; expected ", makeTypeName(type), "."));
return nullptr;
}
void NodeTranslator::compileValueInner(
ValueExpression::Reader src, DynamicSlot& dst, bool isBootstrap) {
Orphan<DynamicValue> NodeTranslator::compileValueInner(
ValueExpression::Reader src, schema::Type::Reader type, bool isBootstrap) {
switch (src.which()) {
case ValueExpression::NAME: {
auto name = src.getName();
bool isBare = name.getBase().isRelativeName() &&
name.getMemberPath().size() == 0;
bool wasSet = false;
if (isBare) {
// The name is just a bare identifier. It may be a literal value or an enumerant.
kj::StringPtr id = name.getBase().getRelativeName().getValue();
KJ_IF_MAYBE(enumId, dst.getEnumType()) {
KJ_IF_MAYBE(enumSchema, resolver.resolveBootstrapSchema(*enumId)) {
if (type.isEnum()) {
KJ_IF_MAYBE(enumSchema, resolver.resolveBootstrapSchema(type.getEnum())) {
KJ_IF_MAYBE(enumerant, enumSchema->asEnum().findEnumerantByName(id)) {
dst.set(DynamicEnum(*enumerant));
wasSet = true;
return DynamicEnum(*enumerant);
}
} else {
// Enum type is broken. We don't want to report a redundant error here, so just assume
// we would have found a matching enumerant.
dst.set(kj::implicitCast<uint16_t>(0));
wasSet = true;
// Enum type is broken.
return nullptr;
}
} else {
// Interpret known constant values.
if (id == "void") {
dst.set(VOID);
wasSet = true;
return VOID;
} else if (id == "true") {
dst.set(true);
wasSet = true;
return true;
} else if (id == "false") {
dst.set(false);
wasSet = true;
return false;
} else if (id == "nan") {
dst.set(std::numeric_limits<double>::quiet_NaN());
wasSet = true;
return std::numeric_limits<double>::quiet_NaN();
} else if (id == "inf") {
dst.set(std::numeric_limits<double>::infinity());
wasSet = true;
return std::numeric_limits<double>::infinity();
}
}
}
if (!wasSet) {
// Haven't resolved the name yet. Try looking up a constant.
KJ_IF_MAYBE(constValue, readConstant(src.getName(), isBootstrap, src)) {
dst.set(*constValue);
}
// Haven't resolved the name yet. Try looking up a constant.
KJ_IF_MAYBE(constValue, readConstant(src.getName(), isBootstrap, src)) {
return orphanage.newOrphanCopy(*constValue);
}
break;
return nullptr;
}
case ValueExpression::POSITIVE_INT:
dst.set(src.getPositiveInt());
break;
return src.getPositiveInt();
case ValueExpression::NEGATIVE_INT: {
uint64_t nValue = src.getNegativeInt();
if (nValue > (std::numeric_limits<uint64_t>::max() >> 1) + 1) {
errorReporter.addErrorOn(src, "Integer is too big to be negative.");
return nullptr;
} else {
dst.set(kj::implicitCast<int64_t>(-nValue));
return kj::implicitCast<int64_t>(-nValue);
}
break;
}
case ValueExpression::FLOAT:
dst.set(src.getFloat());
return src.getFloat();
break;
case ValueExpression::STRING:
dst.set(src.getString());
if (type.isData()) {
Text::Reader text = src.getString();
return orphanage.newOrphanCopy(Data::Reader(
reinterpret_cast<const byte*>(text.begin()), text.size()));
} else {
return orphanage.newOrphanCopy(src.getString());
}
break;
case ValueExpression::LIST: {
auto srcList = src.getList();
auto dstList = dst.initList(srcList.size());
for (uint i = 0; i < srcList.size(); i++) {
DynamicSlot slot(dstList, i);
compileValue(srcList[i], slot, isBootstrap);
if (!type.isList()) {
errorReporter.addErrorOn(src, "Type mismatch.");
return nullptr;
}
auto elementType = type.getList();
KJ_IF_MAYBE(listSchema, makeListSchemaOf(elementType)) {
auto srcList = src.getList();
Orphan<DynamicList> result = orphanage.newOrphan(*listSchema, srcList.size());
auto dstList = result.get();
for (uint i = 0; i < srcList.size(); i++) {
KJ_IF_MAYBE(value, compileValue(srcList[i], elementType, isBootstrap)) {
dstList.adopt(i, kj::mv(*value));
}
}
return kj::mv(result);
} else {
return nullptr;
}
break;
}
case ValueExpression::STRUCT: {
auto srcStruct = src.getStruct();
auto dstStruct = dst.initStruct();
auto dstSchema = dstStruct.getSchema();
for (auto assignment: srcStruct) {
auto fieldName = assignment.getFieldName();
KJ_IF_MAYBE(field, dstSchema.findFieldByName(fieldName.getValue())) {
DynamicSlot slot(dstStruct, *field);
compileValue(assignment.getValue(), slot, isBootstrap);
} else {
errorReporter.addErrorOn(fieldName, kj::str(
"Struct has no field named '", fieldName.getValue(), "'."));
}
if (!type.isStruct()) {
errorReporter.addErrorOn(src, "Type mismatch.");
return nullptr;
}
KJ_IF_MAYBE(schema, resolver.resolveBootstrapSchema(type.getStruct())) {
auto structSchema = schema->asStruct();
Orphan<DynamicStruct> result = orphanage.newOrphan(structSchema);
fillStructValue(result.get(), src.getStruct(), isBootstrap);
return kj::mv(result);
} else {
return nullptr;
}
break;
}
case ValueExpression::UNKNOWN:
// Ignore earlier error.
break;
return nullptr;
}
KJ_UNREACHABLE;
}
void NodeTranslator::fillStructValue(DynamicStruct::Builder builder,
List<ValueExpression::FieldAssignment>::Reader assignments,
bool isBootstrap) {
for (auto assignment: assignments) {
auto fieldName = assignment.getFieldName();
KJ_IF_MAYBE(field, builder.getSchema().findFieldByName(fieldName.getValue())) {
auto fieldProto = field->getProto();
auto value = assignment.getValue();
switch (fieldProto.which()) {
case schema::Field::NON_GROUP:
KJ_IF_MAYBE(compiledValue,
compileValue(value, fieldProto.getNonGroup().getType(), isBootstrap)) {
builder.adopt(*field, kj::mv(*compiledValue));
}
break;
case schema::Field::GROUP:
if (value.isStruct()) {
fillStructValue(builder.init(*field).as<DynamicStruct>(), value.getStruct(),
isBootstrap);
} else {
errorReporter.addErrorOn(value, "Type mismatch.");
}
break;
}
} else {
errorReporter.addErrorOn(fieldName, kj::str(
"Struct has no field named '", fieldName.getValue(), "'."));
}
}
}
kj::String NodeTranslator::makeNodeName(uint64_t id) {
KJ_IF_MAYBE(schema, resolver.resolveBootstrapSchema(id)) {
schema::Node::Reader proto = schema->getProto();
return kj::str(proto.getDisplayName().slice(proto.getDisplayNamePrefixLength()));
} else {
return kj::str("@0x", kj::hex(id));
}
}
kj::String NodeTranslator::makeTypeName(schema::Type::Reader type) {
switch (type.which()) {
case schema::Type::VOID: return kj::str("Void");
case schema::Type::BOOL: return kj::str("Bool");
case schema::Type::INT8: return kj::str("Int8");
case schema::Type::INT16: return kj::str("Int16");
case schema::Type::INT32: return kj::str("Int32");
case schema::Type::INT64: return kj::str("Int64");
case schema::Type::UINT8: return kj::str("UInt8");
case schema::Type::UINT16: return kj::str("UInt16");
case schema::Type::UINT32: return kj::str("UInt32");
case schema::Type::UINT64: return kj::str("UInt64");
case schema::Type::FLOAT32: return kj::str("Float32");
case schema::Type::FLOAT64: return kj::str("Float64");
case schema::Type::TEXT: return kj::str("Text");
case schema::Type::DATA: return kj::str("Data");
case schema::Type::LIST: return kj::str("List(", makeTypeName(type.getList()), ")");
case schema::Type::ENUM: return makeNodeName(type.getEnum());
case schema::Type::STRUCT: return makeNodeName(type.getStruct());
case schema::Type::INTERFACE: return makeNodeName(type.getInterface());
case schema::Type::OBJECT: return kj::str("Object");
}
KJ_UNREACHABLE;
}
kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
......@@ -1824,7 +1874,6 @@ Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications(
return Orphan<List<schema::Annotation>>();
}
Orphanage orphanage = Orphanage::getForMessageContaining(wipNode.get());
auto result = orphanage.newOrphan<List<schema::Annotation>>(annotations.size());
auto builder = result.get();
......
......@@ -109,6 +109,7 @@ public:
private:
const Resolver& resolver;
const ErrorReporter& errorReporter;
Orphanage orphanage;
bool compileAnnotations;
Orphan<schema::Node> wipNode;
......@@ -168,15 +169,23 @@ private:
schema::Value::Builder target, bool isBootstrap);
// Interprets the value expression and initializes `target` with the result.
class DynamicSlot;
kj::Maybe<Orphan<DynamicValue>> compileValue(
ValueExpression::Reader src, schema::Type::Reader type, bool isBootstrap);
// Compile the given value as the given type. Returns null if there was an error, including
// if the value doesn't match the type.
void compileValue(ValueExpression::Reader src, DynamicSlot& dst, bool isBootstrap);
// Fill in `dst` (which effectively points to a struct field or list element) with the given
// value.
void compileValueInner(ValueExpression::Reader src, DynamicSlot& dst, bool isBootstrap);
Orphan<DynamicValue> compileValueInner(ValueExpression::Reader src, schema::Type::Reader type,
bool isBootstrap);
// Helper for compileValue().
void fillStructValue(DynamicStruct::Builder builder,
List<ValueExpression::FieldAssignment>::Reader assignments,
bool isBootstrap);
// Interprets the given assignments and uses them to fill in the given struct builder.
kj::String makeNodeName(uint64_t id);
kj::String makeTypeName(schema::Type::Reader type);
kj::Maybe<DynamicValue::Reader> readConstant(DeclName::Reader name, bool isBootstrap,
ValueExpression::Reader errorLocation);
// Get the value of the given constant. May return null if some error occurs, which will already
......
......@@ -558,10 +558,12 @@ void DynamicStruct::Builder::set(StructSchema::Field field, const DynamicValue::
return;
case schema::Type::LIST:
// TODO(soon): Type check.
builder.setListField(nonGroup.getOffset() * POINTERS, value.as<DynamicList>().reader);
return;
case schema::Type::STRUCT:
// TODO(soon): Type check.
builder.setStructField(
nonGroup.getOffset() * POINTERS, value.as<DynamicStruct>().reader);
return;
......@@ -697,11 +699,11 @@ void DynamicStruct::Builder::adopt(StructSchema::Field field, Orphan<DynamicValu
return;
case schema::Type::TEXT:
orphan.getReader().as<Text>(); // type check
KJ_REQUIRE(orphan.getType() == DynamicValue::TEXT, "Value type mismatch.");
break;
case schema::Type::DATA:
orphan.getReader().as<Data>(); // type check
KJ_REQUIRE(orphan.getType() == DynamicValue::DATA, "Value type mismatch.");
break;
case schema::Type::LIST: {
......@@ -869,7 +871,7 @@ void DynamicStruct::Builder::clear(StructSchema::Field field) {
case schema::Type::LIST:
case schema::Type::STRUCT:
case schema::Type::OBJECT:
builder.disown(nonGroup.getOffset() * POINTERS);
builder.clearPointer(nonGroup.getOffset() * POINTERS);
return;
case schema::Type::INTERFACE:
......@@ -1199,17 +1201,16 @@ void DynamicList::Builder::set(uint index, const DynamicValue::Reader& value) {
return;
case schema::Type::LIST: {
// TODO(soon): Type check.
builder.setListElement(index * ELEMENTS, value.as<DynamicList>().reader);
return;
}
case schema::Type::STRUCT:
// 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.
KJ_FAIL_ASSERT("DynamicList of structs does not support set().") {
return;
}
case schema::Type::STRUCT: {
// TODO(soon): Type check.
builder.getStructElement(index * ELEMENTS).copyContentFrom(value.as<DynamicStruct>().reader);
return;
}
case schema::Type::ENUM: {
uint16_t rawValue;
......@@ -1297,6 +1298,114 @@ DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) {
return nullptr;
}
void DynamicList::Builder::adopt(uint index, Orphan<DynamicValue>&& orphan) {
switch (schema.whichElementType()) {
case schema::Type::VOID:
case schema::Type::BOOL:
case schema::Type::INT8:
case schema::Type::INT16:
case schema::Type::INT32:
case schema::Type::INT64:
case schema::Type::UINT8:
case schema::Type::UINT16:
case schema::Type::UINT32:
case schema::Type::UINT64:
case schema::Type::FLOAT32:
case schema::Type::FLOAT64:
case schema::Type::ENUM:
set(index, orphan.getReader());
return;
case schema::Type::TEXT:
KJ_REQUIRE(orphan.getType() == DynamicValue::TEXT, "Value type mismatch.");
builder.adopt(index * ELEMENTS, kj::mv(orphan.builder));
return;
case schema::Type::DATA:
KJ_REQUIRE(orphan.getType() == DynamicValue::DATA, "Value type mismatch.");
builder.adopt(index * ELEMENTS, kj::mv(orphan.builder));
return;
case schema::Type::LIST: {
ListSchema elementType = schema.getListElementType();
KJ_REQUIRE(orphan.getType() == DynamicValue::LIST && orphan.listSchema == elementType,
"Value type mismatch.");
builder.adopt(index * ELEMENTS, kj::mv(orphan.builder));
return;
}
case schema::Type::STRUCT: {
auto elementType = schema.getStructElementType();
KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == elementType,
"Value type mismatch.");
builder.getStructElement(index * ELEMENTS).transferContentFrom(
orphan.builder.asStruct(structSizeFromSchema(elementType)));
return;
}
case schema::Type::OBJECT:
KJ_FAIL_ASSERT("List(Object) not supported.");
case schema::Type::INTERFACE:
KJ_FAIL_ASSERT("Interfaces not yet implemented.");
}
KJ_UNREACHABLE;
}
Orphan<DynamicValue> DynamicList::Builder::disown(uint index) {
switch (schema.whichElementType()) {
case schema::Type::VOID:
case schema::Type::BOOL:
case schema::Type::INT8:
case schema::Type::INT16:
case schema::Type::INT32:
case schema::Type::INT64:
case schema::Type::UINT8:
case schema::Type::UINT16:
case schema::Type::UINT32:
case schema::Type::UINT64:
case schema::Type::FLOAT32:
case schema::Type::FLOAT64:
case schema::Type::ENUM: {
auto result = Orphan<DynamicValue>(operator[](index), _::OrphanBuilder());
switch (elementSizeFor(schema.whichElementType())) {
case _::FieldSize::VOID: break;
case _::FieldSize::BIT: builder.setDataElement<bool>(index * ELEMENTS, false); break;
case _::FieldSize::BYTE: builder.setDataElement<uint8_t>(index * ELEMENTS, 0); break;
case _::FieldSize::TWO_BYTES: builder.setDataElement<uint16_t>(index * ELEMENTS, 0); break;
case _::FieldSize::FOUR_BYTES: builder.setDataElement<uint32_t>(index * ELEMENTS, 0); break;
case _::FieldSize::EIGHT_BYTES: builder.setDataElement<uint64_t>(index * ELEMENTS, 0);break;
case _::FieldSize::POINTER:
case _::FieldSize::INLINE_COMPOSITE:
KJ_UNREACHABLE;
}
return kj::mv(result);
}
case schema::Type::TEXT:
case schema::Type::DATA:
case schema::Type::LIST:
case schema::Type::OBJECT:
case schema::Type::INTERFACE: {
auto value = operator[](index);
return Orphan<DynamicValue>(value, builder.disown(index * ELEMENTS));
}
case schema::Type::STRUCT: {
// We have to make a copy.
Orphan<DynamicStruct> result =
Orphanage::getForMessageContaining(*this).newOrphan(schema.getStructElementType());
auto element = builder.getStructElement(index * ELEMENTS);
result.get().builder.transferContentFrom(element);
element.clearAll();
return kj::mv(result);
}
}
KJ_UNREACHABLE;
}
void DynamicList::Builder::copyFrom(std::initializer_list<DynamicValue::Reader> value) {
KJ_REQUIRE(value.size() == size(), "DynamicList::copyFrom() argument had different size.");
uint i = 0;
......@@ -1793,12 +1902,50 @@ DynamicValue::Reader Orphan<DynamicValue>::getReader() const {
template <>
Orphan<DynamicStruct> Orphan<DynamicValue>::releaseAs<DynamicStruct>() {
KJ_REQUIRE(type == DynamicValue::STRUCT, "Value type mismatch.");
type = DynamicValue::UNKNOWN;
return Orphan<DynamicStruct>(structSchema, kj::mv(builder));
}
template <>
Orphan<DynamicList> Orphan<DynamicValue>::releaseAs<DynamicList>() {
KJ_REQUIRE(type == DynamicValue::LIST, "Value type mismatch.");
type = DynamicValue::UNKNOWN;
return Orphan<DynamicList>(listSchema, kj::mv(builder));
}
template <>
Orphan<DynamicObject> Orphanage::newOrphanCopy<DynamicObject::Reader>(
const DynamicObject::Reader& copyFrom) const {
switch (copyFrom.reader.kind) {
case _::ObjectKind::NULL_POINTER:
return Orphan<DynamicObject>();
case _::ObjectKind::STRUCT:
return Orphan<DynamicObject>(_::OrphanBuilder::copy(arena, copyFrom.reader.structReader));
case _::ObjectKind::LIST:
return Orphan<DynamicObject>(_::OrphanBuilder::copy(arena, copyFrom.reader.listReader));
}
KJ_UNREACHABLE;
}
template <>
Orphan<DynamicValue> Orphanage::newOrphanCopy<DynamicValue::Reader>(
const DynamicValue::Reader& copyFrom) const {
switch (copyFrom.getType()) {
case DynamicValue::UNKNOWN: return nullptr;
case DynamicValue::VOID: return copyFrom.voidValue;
case DynamicValue::BOOL: return copyFrom.boolValue;
case DynamicValue::INT: return copyFrom.intValue;
case DynamicValue::UINT: return copyFrom.uintValue;
case DynamicValue::FLOAT: return copyFrom.floatValue;
case DynamicValue::ENUM: return copyFrom.enumValue;
case DynamicValue::TEXT: return newOrphanCopy(copyFrom.textValue);
case DynamicValue::DATA: return newOrphanCopy(copyFrom.dataValue);
case DynamicValue::LIST: return newOrphanCopy(copyFrom.listValue);
case DynamicValue::STRUCT: return newOrphanCopy(copyFrom.structValue);
case DynamicValue::INTERFACE: KJ_FAIL_ASSERT("Interfaces not implemented.");
case DynamicValue::OBJECT: return newOrphanCopy(copyFrom.objectValue);
}
KJ_UNREACHABLE;
}
} // namespace capnp
......@@ -150,6 +150,8 @@ class DynamicObject::Reader {
// Represents an "Object" field of unknown type.
public:
typedef DynamicObject Reads;
Reader() = default;
template <typename T>
......@@ -171,6 +173,7 @@ private:
friend class DynamicObject::Builder;
friend class Orphan<DynamicObject>;
friend class Orphan<DynamicValue>;
friend class Orphanage;
};
class DynamicObject::Builder: public kj::DisallowConstCopy {
......@@ -182,6 +185,8 @@ class DynamicObject::Builder: public kj::DisallowConstCopy {
// DynamicStruct::Builder::{get,set,init}Object() and pass a type schema to build object fields.
public:
typedef DynamicObject Builds;
Builder() = default;
Builder(Builder&) = default;
Builder(Builder&&) = default;
......@@ -313,8 +318,6 @@ public:
// Clear a field, setting it to its default value. For pointer fields, this actually makes the
// field null.
// TODO(someday): Implement adopt() and disown().
DynamicStruct::Builder getObject(StructSchema::Field field, StructSchema type);
DynamicList::Builder getObject(StructSchema::Field field, ListSchema type);
Text::Builder getObjectAsText(StructSchema::Field field);
......@@ -437,7 +440,8 @@ public:
DynamicValue::Builder operator[](uint index);
void set(uint index, const DynamicValue::Reader& value);
DynamicValue::Builder init(uint index, uint size);
// TODO(someday): Implement adopt() and disown().
void adopt(uint index, Orphan<DynamicValue>&& orphan);
Orphan<DynamicValue> disown(uint index);
typedef _::IndexingIterator<Builder, DynamicStruct::Builder> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
......@@ -484,7 +488,7 @@ class DynamicValue::Reader {
public:
typedef DynamicValue Reads;
inline Reader(std::nullptr_t n = nullptr); // UNKNOWN
inline Reader(decltype(nullptr) n = nullptr); // UNKNOWN
inline Reader(Void value);
inline Reader(bool value);
inline Reader(char value);
......@@ -557,13 +561,15 @@ private:
template <typename T, Kind kind = kind<T>()> struct AsImpl;
// Implementation backing the as() method. Needs to be a struct to allow partial
// specialization. Has a method apply() which does the work.
friend class Orphanage; // to speed up newOrphanCopy(DynamicValue::Reader)
};
class DynamicValue::Builder {
public:
typedef DynamicValue Builds;
inline Builder(std::nullptr_t n = nullptr); // UNKNOWN
inline Builder(decltype(nullptr) n = nullptr); // UNKNOWN
inline Builder(Void value);
inline Builder(bool value);
inline Builder(char value);
......@@ -757,16 +763,34 @@ private:
template <typename, Kind>
friend struct _::PointerHelpers;
friend class Orphan<DynamicValue>;
friend class Orphanage;
};
template <>
class Orphan<DynamicValue> {
public:
Orphan() = default;
KJ_DISALLOW_COPY(Orphan);
inline Orphan(decltype(nullptr) n = nullptr): type(DynamicValue::UNKNOWN) {}
inline Orphan(Void value);
inline Orphan(bool value);
inline Orphan(char value);
inline Orphan(signed char value);
inline Orphan(short value);
inline Orphan(int value);
inline Orphan(long value);
inline Orphan(long long value);
inline Orphan(unsigned char value);
inline Orphan(unsigned short value);
inline Orphan(unsigned int value);
inline Orphan(unsigned long value);
inline Orphan(unsigned long long value);
inline Orphan(float value);
inline Orphan(double value);
inline Orphan(DynamicEnum value);
Orphan(Orphan&&) = default;
template <typename T>
Orphan(Orphan<T>&&);
KJ_DISALLOW_COPY(Orphan);
Orphan& operator=(Orphan&&) = default;
inline DynamicValue::Type getType() { return type; }
......@@ -780,9 +804,6 @@ public:
// the original Orphan<DynamicStruct> is no longer valid after this call; ownership is
// transferred to the returned Orphan<T>.
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
private:
DynamicValue::Type type;
union {
......@@ -851,6 +872,7 @@ Orphan<T> Orphan<DynamicList>::releaseAs() {
template <typename T>
Orphan<T> Orphan<DynamicValue>::releaseAs() {
get().as<T>(); // type check
type = DynamicValue::UNKNOWN;
return Orphan<T>(kj::mv(builder));
}
......@@ -886,6 +908,14 @@ inline Orphan<DynamicList> Orphanage::newOrphanCopy<DynamicList::Reader>(
return Orphan<DynamicList>(copyFrom.getSchema(), _::OrphanBuilder::copy(arena, copyFrom.reader));
}
template <>
Orphan<DynamicObject> Orphanage::newOrphanCopy<DynamicObject::Reader>(
const DynamicObject::Reader& copyFrom) const;
template <>
Orphan<DynamicValue> Orphanage::newOrphanCopy<DynamicValue::Reader>(
const DynamicValue::Reader& copyFrom) const;
// -------------------------------------------------------------------
// Inject the ability to use DynamicStruct for message roots and Dynamic{Struct,List} for
// generated Object accessors.
......@@ -1001,11 +1031,13 @@ DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value) {
inline DynamicValue::Reader::Reader(std::nullptr_t n): type(UNKNOWN) {}
inline DynamicValue::Builder::Builder(std::nullptr_t n): type(UNKNOWN) {}
#define CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \
#define CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \
inline DynamicValue::Reader::Reader(cppType value) \
: type(typeTag), fieldName##Value(value) {} \
inline DynamicValue::Builder::Builder(cppType value) \
: type(typeTag), fieldName##Value(value) {}
: type(typeTag), fieldName##Value(value) {} \
inline Orphan<DynamicValue>::Orphan(cppType value) \
: type(DynamicValue::typeTag), fieldName##Value(value) {}
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(Void, VOID, void);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(bool, BOOL, bool);
......
......@@ -1558,19 +1558,19 @@ struct WireHelpers {
WirePointer* tag = reinterpret_cast<WirePointer*>(ptr);
tag->setKindAndInlineCompositeListElementCount(WirePointer::STRUCT, value.elementCount);
tag->structRef.set(dataSize, pointerCount);
ptr += POINTER_SIZE_IN_WORDS;
word* dst = ptr + POINTER_SIZE_IN_WORDS;
const word* src = reinterpret_cast<const word*>(value.ptr);
for (uint i = 0; i < value.elementCount / ELEMENTS; i++) {
memcpy(ptr, src, value.structDataSize / BITS_PER_BYTE / BYTES);
ptr += dataSize;
memcpy(dst, src, value.structDataSize / BITS_PER_BYTE / BYTES);
dst += dataSize;
src += dataSize;
for (uint j = 0; j < pointerCount / POINTERS; j++) {
setObjectPointer(segment, reinterpret_cast<WirePointer*>(ptr), readObjectPointer(
setObjectPointer(segment, reinterpret_cast<WirePointer*>(dst), readObjectPointer(
value.segment, reinterpret_cast<const WirePointer*>(src), nullptr,
value.nestingLimit));
ptr += POINTER_SIZE_IN_WORDS;
dst += POINTER_SIZE_IN_WORDS;
src += POINTER_SIZE_IN_WORDS;
}
}
......@@ -2152,6 +2152,19 @@ void StructBuilder::clearPointer(WirePointerCount ptrIndex) {
memset(pointers + ptrIndex, 0, sizeof(WirePointer));
}
void StructBuilder::clearAll() {
if (dataSize == 1 * BITS) {
setDataField<bool>(1 * ELEMENTS, false);
} else {
memset(data, 0, dataSize / BITS_PER_BYTE / BYTES);
}
for (uint i = 0; i < pointerCount / POINTERS; i++) {
WireHelpers::zeroObject(segment, pointers + i);
}
memset(pointers, 0, pointerCount * BYTES_PER_POINTER / BYTES);
}
void StructBuilder::transferContentFrom(StructBuilder other) {
// Determine the amount of data the builders have in common.
BitCount sharedDataSize = kj::min(dataSize, other.dataSize);
......@@ -2679,6 +2692,8 @@ ListBuilder OrphanBuilder::asStructList(StructSize elementSize) {
// Watch out, the pointer could have been updated if the object had to be relocated.
if (tagAsPtr()->kind() == WirePointer::FAR) {
location = nullptr;
} else if (result.step * ELEMENTS <= BITS_PER_WORD * WORDS) {
location = reinterpret_cast<word*>(result.ptr);
} else {
location = reinterpret_cast<word*>(result.ptr) - POINTER_SIZE_IN_WORDS;
}
......@@ -2748,16 +2763,24 @@ ObjectReader OrphanBuilder::asObjectReader() const {
}
void OrphanBuilder::euthanize() {
auto ref = reinterpret_cast<WirePointer*>(&tag);
if (ref->kind() == WirePointer::FAR) {
WireHelpers::zeroObject(segment, ref);
} else {
WireHelpers::zeroObject(segment, reinterpret_cast<WirePointer*>(&tag), location);
}
// Carefully catch any exceptions and rethrow them as recoverable exceptions since we may be in
// a destructor.
auto exception = kj::runCatchingExceptions([&]() {
auto ref = reinterpret_cast<WirePointer*>(&tag);
if (ref->kind() == WirePointer::FAR) {
WireHelpers::zeroObject(segment, ref);
} else {
WireHelpers::zeroObject(segment, reinterpret_cast<WirePointer*>(&tag), location);
}
memset(ref, 0, sizeof(*ref));
segment = nullptr;
location = nullptr;
memset(ref, 0, sizeof(*ref));
segment = nullptr;
location = nullptr;
});
KJ_IF_MAYBE(e, exception) {
kj::getExceptionCallback().onRecoverableException(kj::mv(*e));
}
}
} // namespace _ (private)
......
......@@ -376,6 +376,9 @@ public:
void clearPointer(WirePointerCount ptrIndex);
// Equivalent to calling disown() and letting the result simply be destroyed.
void clearAll();
// Clear all pointers and data.
void transferContentFrom(StructBuilder other);
// Adopt all pointers from `other`, and also copy all data. If `other`'s sections are larger
// than this, the extra data is not transferred, meaning there is a risk of data loss when
......
......@@ -446,14 +446,14 @@ TEST(Orphans, DynamicStructAs) {
Orphan<DynamicValue> orphan =
root.disownObjectField<DynamicStruct>(Schema::from<TestAllTypes>());
EXPECT_FALSE(orphan == nullptr);
EXPECT_EQ(DynamicValue::STRUCT, orphan.getType());
checkTestMessage(orphan.getReader().as<TestAllTypes>());
checkTestMessage(orphan.get().as<TestAllTypes>());
{
Orphan<DynamicStruct> structOrphan = orphan.releaseAs<DynamicStruct>();
EXPECT_TRUE(orphan == nullptr);
EXPECT_EQ(DynamicValue::UNKNOWN, orphan.getType());
EXPECT_FALSE(structOrphan == nullptr);
checkDynamicTestMessage(structOrphan.getReader());
checkDynamicTestMessage(structOrphan.get());
......@@ -467,7 +467,7 @@ TEST(Orphans, DynamicStructAs) {
checkTestMessage(typedOrphan.getReader());
checkTestMessage(typedOrphan.get());
orphan = kj::mv(typedOrphan);
EXPECT_FALSE(orphan == nullptr);
EXPECT_EQ(DynamicValue::STRUCT, orphan.getType());
EXPECT_TRUE(typedOrphan == nullptr);
}
}
......@@ -487,14 +487,14 @@ TEST(Orphans, DynamicListAs) {
EXPECT_TRUE(root.hasObjectField());
Orphan<DynamicValue> orphan = root.disownObjectField<DynamicList>(Schema::from<List<uint32_t>>());
EXPECT_FALSE(orphan == nullptr);
EXPECT_EQ(DynamicValue::LIST, orphan.getType());
checkList(orphan.getReader().as<List<uint32_t>>(), {12, 34, 56});
checkList(orphan.get().as<List<uint32_t>>(), {12, 34, 56});
{
Orphan<DynamicList> listOrphan = orphan.releaseAs<DynamicList>();
EXPECT_TRUE(orphan == nullptr);
EXPECT_EQ(DynamicValue::UNKNOWN, orphan.getType());
EXPECT_FALSE(listOrphan == nullptr);
checkList<uint32_t>(listOrphan.getReader(), {12, 34, 56});
checkList<uint32_t>(listOrphan.get(), {12, 34, 56});
......@@ -508,7 +508,7 @@ TEST(Orphans, DynamicListAs) {
checkList(typedOrphan.getReader(), {12, 34, 56});
checkList(typedOrphan.get(), {12, 34, 56});
orphan = kj::mv(typedOrphan);
EXPECT_FALSE(orphan == nullptr);
EXPECT_EQ(DynamicValue::LIST, orphan.getType());
EXPECT_TRUE(typedOrphan == nullptr);
}
}
......@@ -528,7 +528,7 @@ TEST(Orphans, DynamicObject) {
EXPECT_TRUE(root.hasObjectField());
Orphan<DynamicValue> orphan = root.disownObjectField<DynamicObject>();
EXPECT_FALSE(orphan == nullptr);
EXPECT_EQ(DynamicValue::OBJECT, orphan.getType());
checkTestMessage(orphan.getReader().as<DynamicObject>().as<TestAllTypes>());
......
......@@ -326,7 +326,12 @@ public:
#if KJ_NO_EXCEPTIONS
logException(mv(exception));
#else
throw ExceptionImpl(mv(exception));
if (std::uncaught_exception()) {
// Bad time to throw an exception. Just log instead.
logException(mv(exception));
} else {
throw ExceptionImpl(mv(exception));
}
#endif
}
......
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