797 lines
26 KiB
C++
797 lines
26 KiB
C++
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
|
// Licensed under the MIT License:
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
#include "schema.h"
|
|
#include "message.h"
|
|
#include <kj/debug.h>
|
|
|
|
namespace capnp {
|
|
|
|
namespace schema {
|
|
uint KJ_HASHCODE(Type::Which w) { return kj::hashCode(static_cast<uint16_t>(w)); }
|
|
// TODO(cleanup): Cap'n Proto does not declare stringifiers nor hashers for `Which` enums, unlike
|
|
// all other enums. Fix that and remove this.
|
|
}
|
|
|
|
namespace _ { // private
|
|
|
|
// Null schemas generated using the below schema file with:
|
|
//
|
|
// capnp eval -Isrc null-schemas.capnp node --flat |
|
|
// hexdump -v -e '8/1 "0x%02x, "' -e '1/8 "\n"'; echo
|
|
//
|
|
// I totally don't understand hexdump format strings and came up with this command based on trial
|
|
// and error.
|
|
//
|
|
// @0x879863d4b2cc4a1e;
|
|
//
|
|
// using Node = import "/capnp/schema.capnp".Node;
|
|
//
|
|
// const node :Node = (
|
|
// id = 0x0000000000000000,
|
|
// displayName = "(null schema)");
|
|
//
|
|
// const struct :Node = (
|
|
// id = 0x0000000000000001,
|
|
// displayName = "(null struct schema)",
|
|
// struct = (
|
|
// dataWordCount = 0,
|
|
// pointerCount = 0,
|
|
// preferredListEncoding = empty));
|
|
//
|
|
// const enum :Node = (
|
|
// id = 0x0000000000000002,
|
|
// displayName = "(null enum schema)",
|
|
// enum = ());
|
|
//
|
|
// const interface :Node = (
|
|
// id = 0x0000000000000003,
|
|
// displayName = "(null interface schema)",
|
|
// interface = ());
|
|
//
|
|
// const const :Node = (
|
|
// id = 0x0000000000000004,
|
|
// displayName = "(null const schema)",
|
|
// const = (type = (void = void), value = (void = void)));
|
|
|
|
static const AlignedData<13> NULL_SCHEMA_BYTES = {{
|
|
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, // union discriminant intentionally mangled
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x11, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x63,
|
|
0x68, 0x65, 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00,
|
|
}};
|
|
const RawSchema NULL_SCHEMA = {
|
|
0x0000000000000000, NULL_SCHEMA_BYTES.words, 13,
|
|
nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
|
|
{ &NULL_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
|
|
};
|
|
|
|
static const AlignedData<14> NULL_STRUCT_SCHEMA_BYTES = {{
|
|
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x11, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x74,
|
|
0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x63, 0x68,
|
|
0x65, 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00, 0x00,
|
|
}};
|
|
const RawSchema NULL_STRUCT_SCHEMA = {
|
|
0x0000000000000001, NULL_STRUCT_SCHEMA_BYTES.words, 14,
|
|
nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
|
|
{ &NULL_STRUCT_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
|
|
};
|
|
|
|
static const AlignedData<14> NULL_ENUM_SCHEMA_BYTES = {{
|
|
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x11, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x65, 0x6e,
|
|
0x75, 0x6d, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d,
|
|
0x61, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
}};
|
|
const RawSchema NULL_ENUM_SCHEMA = {
|
|
0x0000000000000002, NULL_ENUM_SCHEMA_BYTES.words, 14,
|
|
nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
|
|
{ &NULL_ENUM_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
|
|
};
|
|
|
|
static const AlignedData<20> NULL_CONST_SCHEMA_BYTES = {{
|
|
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
|
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x11, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
|
|
0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
|
|
0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x63, 0x6f,
|
|
0x6e, 0x73, 0x74, 0x20, 0x73, 0x63, 0x68, 0x65,
|
|
0x6d, 0x61, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
}};
|
|
const RawSchema NULL_CONST_SCHEMA = {
|
|
0x0000000000000004, NULL_CONST_SCHEMA_BYTES.words, 20,
|
|
nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
|
|
{ &NULL_CONST_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
|
|
};
|
|
|
|
} // namespace _ (private)
|
|
|
|
// =======================================================================================
|
|
|
|
schema::Node::Reader Schema::getProto() const {
|
|
return readMessageUnchecked<schema::Node>(raw->generic->encodedNode);
|
|
}
|
|
|
|
kj::ArrayPtr<const word> Schema::asUncheckedMessage() const {
|
|
return kj::arrayPtr(raw->generic->encodedNode, raw->generic->encodedSize);
|
|
}
|
|
|
|
Schema Schema::getDependency(uint64_t id, uint location) const {
|
|
{
|
|
// Binary search dependency list.
|
|
uint lower = 0;
|
|
uint upper = raw->dependencyCount;
|
|
|
|
while (lower < upper) {
|
|
uint mid = (lower + upper) / 2;
|
|
|
|
auto candidate = raw->dependencies[mid];
|
|
if (candidate.location == location) {
|
|
candidate.schema->ensureInitialized();
|
|
return Schema(candidate.schema);
|
|
} else if (candidate.location < location) {
|
|
lower = mid + 1;
|
|
} else {
|
|
upper = mid;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
uint lower = 0;
|
|
uint upper = raw->generic->dependencyCount;
|
|
|
|
while (lower < upper) {
|
|
uint mid = (lower + upper) / 2;
|
|
|
|
const _::RawSchema* candidate = raw->generic->dependencies[mid];
|
|
|
|
uint64_t candidateId = candidate->id;
|
|
if (candidateId == id) {
|
|
candidate->ensureInitialized();
|
|
return Schema(&candidate->defaultBrand);
|
|
} else if (candidateId < id) {
|
|
lower = mid + 1;
|
|
} else {
|
|
upper = mid;
|
|
}
|
|
}
|
|
}
|
|
|
|
KJ_FAIL_REQUIRE("Requested ID not found in dependency table.", kj::hex(id)) {
|
|
return Schema();
|
|
}
|
|
}
|
|
|
|
Schema::BrandArgumentList Schema::getBrandArgumentsAtScope(uint64_t scopeId) const {
|
|
KJ_REQUIRE(getProto().getIsGeneric(), "Not a generic type.", getProto().getDisplayName());
|
|
|
|
for (auto scope: kj::range(raw->scopes, raw->scopes + raw->scopeCount)) {
|
|
if (scope->typeId == scopeId) {
|
|
// OK, this scope matches the scope we're looking for.
|
|
if (scope->isUnbound) {
|
|
return BrandArgumentList(scopeId, true);
|
|
} else {
|
|
return BrandArgumentList(scopeId, scope->bindingCount, scope->bindings);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This scope is not listed in the scopes list.
|
|
return BrandArgumentList(scopeId, raw->isUnbound());
|
|
}
|
|
|
|
kj::Array<uint64_t> Schema::getGenericScopeIds() const {
|
|
if (!getProto().getIsGeneric())
|
|
return nullptr;
|
|
|
|
auto result = kj::heapArray<uint64_t>(raw->scopeCount);
|
|
for (auto iScope: kj::indices(result)) {
|
|
result[iScope] = raw->scopes[iScope].typeId;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
StructSchema Schema::asStruct() const {
|
|
KJ_REQUIRE(getProto().isStruct(), "Tried to use non-struct schema as a struct.",
|
|
getProto().getDisplayName()) {
|
|
return StructSchema();
|
|
}
|
|
return StructSchema(*this);
|
|
}
|
|
|
|
EnumSchema Schema::asEnum() const {
|
|
KJ_REQUIRE(getProto().isEnum(), "Tried to use non-enum schema as an enum.",
|
|
getProto().getDisplayName()) {
|
|
return EnumSchema();
|
|
}
|
|
return EnumSchema(*this);
|
|
}
|
|
|
|
|
|
|
|
ConstSchema Schema::asConst() const {
|
|
KJ_REQUIRE(getProto().isConst(), "Tried to use non-constant schema as a constant.",
|
|
getProto().getDisplayName()) {
|
|
return ConstSchema();
|
|
}
|
|
return ConstSchema(*this);
|
|
}
|
|
|
|
kj::StringPtr Schema::getShortDisplayName() const {
|
|
auto proto = getProto();
|
|
return proto.getDisplayName().slice(proto.getDisplayNamePrefixLength());
|
|
}
|
|
|
|
const kj::StringPtr Schema::getUnqualifiedName() const {
|
|
auto proto = getProto();
|
|
return proto.getDisplayName().slice(proto.getDisplayNamePrefixLength());
|
|
}
|
|
|
|
void Schema::requireUsableAs(const _::RawSchema* expected) const {
|
|
KJ_REQUIRE(raw->generic == expected ||
|
|
(expected != nullptr && raw->generic->canCastTo == expected),
|
|
"This schema is not compatible with the requested native type.");
|
|
}
|
|
|
|
uint32_t Schema::getSchemaOffset(const schema::Value::Reader& value) const {
|
|
const word* ptr;
|
|
|
|
switch (value.which()) {
|
|
case schema::Value::TEXT:
|
|
ptr = reinterpret_cast<const word*>(value.getText().begin());
|
|
break;
|
|
case schema::Value::DATA:
|
|
ptr = reinterpret_cast<const word*>(value.getData().begin());
|
|
break;
|
|
case schema::Value::STRUCT:
|
|
ptr = value.getStruct().getAs<_::UncheckedMessage>();
|
|
break;
|
|
case schema::Value::LIST:
|
|
ptr = value.getList().getAs<_::UncheckedMessage>();
|
|
break;
|
|
case schema::Value::ANY_POINTER:
|
|
ptr = value.getAnyPointer().getAs<_::UncheckedMessage>();
|
|
break;
|
|
default:
|
|
KJ_FAIL_ASSERT("getDefaultValueSchemaOffset() can only be called on struct, list, "
|
|
"and any-pointer fields.");
|
|
}
|
|
|
|
return ptr - raw->generic->encodedNode;
|
|
}
|
|
|
|
Type Schema::getBrandBinding(uint64_t scopeId, uint index) const {
|
|
return getBrandArgumentsAtScope(scopeId)[index];
|
|
}
|
|
|
|
Type Schema::interpretType(schema::Type::Reader proto, uint location) const {
|
|
switch (proto.which()) {
|
|
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::TEXT:
|
|
case schema::Type::DATA:
|
|
return proto.which();
|
|
|
|
case schema::Type::STRUCT: {
|
|
auto structType = proto.getStruct();
|
|
return getDependency(structType.getTypeId(), location).asStruct();
|
|
}
|
|
|
|
case schema::Type::ENUM: {
|
|
auto enumType = proto.getEnum();
|
|
return getDependency(enumType.getTypeId(), location).asEnum();
|
|
}
|
|
|
|
case schema::Type::INTERFACE:
|
|
KJ_FAIL_REQUIRE("Interfaces are not supported.");
|
|
|
|
case schema::Type::LIST:
|
|
return ListSchema::of(interpretType(proto.getList().getElementType(), location));
|
|
|
|
case schema::Type::ANY_POINTER: {
|
|
auto anyPointer = proto.getAnyPointer();
|
|
switch (anyPointer.which()) {
|
|
case schema::Type::AnyPointer::UNCONSTRAINED:
|
|
return anyPointer.getUnconstrained().which();
|
|
case schema::Type::AnyPointer::PARAMETER: {
|
|
auto param = anyPointer.getParameter();
|
|
return getBrandBinding(param.getScopeId(), param.getParameterIndex());
|
|
}
|
|
case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER:
|
|
return Type(Type::ImplicitParameter {
|
|
anyPointer.getImplicitMethodParameter().getParameterIndex() });
|
|
}
|
|
|
|
KJ_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
Type Schema::BrandArgumentList::operator[](uint index) const {
|
|
if (isUnbound) {
|
|
return Type::BrandParameter { scopeId, index };
|
|
}
|
|
|
|
if (index >= size_) {
|
|
// Binding index out-of-range. Treat as AnyPointer. This is important to allow new
|
|
// type parameters to be added to existing types without breaking dependent
|
|
// schemas.
|
|
return schema::Type::ANY_POINTER;
|
|
}
|
|
|
|
auto& binding = bindings[index];
|
|
Type result;
|
|
if (binding.which == (uint)schema::Type::ANY_POINTER) {
|
|
if (binding.scopeId != 0) {
|
|
result = Type::BrandParameter { binding.scopeId, binding.paramIndex };
|
|
} else if (binding.isImplicitParameter) {
|
|
result = Type::ImplicitParameter { binding.paramIndex };
|
|
} else {
|
|
result = static_cast<schema::Type::AnyPointer::Unconstrained::Which>(binding.paramIndex);
|
|
}
|
|
} else if (binding.schema == nullptr) {
|
|
// Builtin / primitive type.
|
|
result = static_cast<schema::Type::Which>(binding.which);
|
|
} else {
|
|
binding.schema->ensureInitialized();
|
|
result = Type(static_cast<schema::Type::Which>(binding.which), binding.schema);
|
|
}
|
|
|
|
return result.wrapInList(binding.listDepth);
|
|
}
|
|
|
|
kj::StringPtr KJ_STRINGIFY(const Schema& schema) {
|
|
return schema.getProto().getDisplayName();
|
|
}
|
|
|
|
// =======================================================================================
|
|
|
|
namespace {
|
|
|
|
template <typename List>
|
|
auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name, List&& list)
|
|
-> kj::Maybe<decltype(list[0])> {
|
|
uint lower = 0;
|
|
uint upper = raw->memberCount;
|
|
|
|
while (lower < upper) {
|
|
uint mid = (lower + upper) / 2;
|
|
|
|
uint16_t memberIndex = raw->membersByName[mid];
|
|
|
|
auto candidate = list[memberIndex];
|
|
kj::StringPtr candidateName = candidate.getProto().getName();
|
|
if (candidateName == name) {
|
|
return candidate;
|
|
} else if (candidateName < name) {
|
|
lower = mid + 1;
|
|
} else {
|
|
upper = mid;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
StructSchema::FieldList StructSchema::getFields() const {
|
|
return FieldList(*this, getProto().getStruct().getFields());
|
|
}
|
|
|
|
StructSchema::FieldSubset StructSchema::getUnionFields() const {
|
|
auto proto = getProto().getStruct();
|
|
return FieldSubset(*this, proto.getFields(),
|
|
raw->generic->membersByDiscriminant, proto.getDiscriminantCount());
|
|
}
|
|
|
|
StructSchema::FieldSubset StructSchema::getNonUnionFields() const {
|
|
auto proto = getProto().getStruct();
|
|
auto fields = proto.getFields();
|
|
auto offset = proto.getDiscriminantCount();
|
|
auto size = fields.size() - offset;
|
|
return FieldSubset(*this, fields, raw->generic->membersByDiscriminant + offset, size);
|
|
}
|
|
|
|
kj::Maybe<StructSchema::Field> StructSchema::findFieldByName(kj::StringPtr name) const {
|
|
return findSchemaMemberByName(raw->generic, name, getFields());
|
|
}
|
|
|
|
StructSchema::Field StructSchema::getFieldByName(kj::StringPtr name) const {
|
|
KJ_IF_MAYBE(member, findFieldByName(name)) {
|
|
return *member;
|
|
} else {
|
|
KJ_FAIL_REQUIRE("struct has no such member", name);
|
|
}
|
|
}
|
|
|
|
kj::Maybe<StructSchema::Field> StructSchema::getFieldByDiscriminant(uint16_t discriminant) const {
|
|
auto unionFields = getUnionFields();
|
|
|
|
if (discriminant >= unionFields.size()) {
|
|
return nullptr;
|
|
} else {
|
|
return unionFields[discriminant];
|
|
}
|
|
}
|
|
|
|
Type StructSchema::Field::getType() const {
|
|
auto proto = getProto();
|
|
uint location = _::RawBrandedSchema::makeDepLocation(_::RawBrandedSchema::DepKind::FIELD, index);
|
|
|
|
switch (proto.which()) {
|
|
case schema::Field::SLOT:
|
|
return parent.interpretType(proto.getSlot().getType(), location);
|
|
|
|
case schema::Field::GROUP:
|
|
return parent.getDependency(proto.getGroup().getTypeId(), location).asStruct();
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
uint32_t StructSchema::Field::getDefaultValueSchemaOffset() const {
|
|
return parent.getSchemaOffset(proto.getSlot().getDefaultValue());
|
|
}
|
|
|
|
kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field) {
|
|
return field.getProto().getName();
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
EnumSchema::EnumerantList EnumSchema::getEnumerants() const {
|
|
return EnumerantList(*this, getProto().getEnum().getEnumerants());
|
|
}
|
|
|
|
kj::Maybe<EnumSchema::Enumerant> EnumSchema::findEnumerantByName(kj::StringPtr name) const {
|
|
return findSchemaMemberByName(raw->generic, name, getEnumerants());
|
|
}
|
|
|
|
EnumSchema::Enumerant EnumSchema::getEnumerantByName(kj::StringPtr name) const {
|
|
KJ_IF_MAYBE(enumerant, findEnumerantByName(name)) {
|
|
return *enumerant;
|
|
} else {
|
|
KJ_FAIL_REQUIRE("enum has no such enumerant", name);
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
uint32_t ConstSchema::getValueSchemaOffset() const {
|
|
return getSchemaOffset(getProto().getConst().getValue());
|
|
}
|
|
|
|
Type ConstSchema::getType() const {
|
|
return interpretType(getProto().getConst().getType(),
|
|
_::RawBrandedSchema::makeDepLocation(_::RawBrandedSchema::DepKind::CONST_TYPE, 0));
|
|
}
|
|
|
|
// =======================================================================================
|
|
|
|
ListSchema ListSchema::of(schema::Type::Which primitiveType) {
|
|
switch (primitiveType) {
|
|
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::TEXT:
|
|
case schema::Type::DATA:
|
|
break;
|
|
|
|
case schema::Type::STRUCT:
|
|
case schema::Type::ENUM:
|
|
case schema::Type::INTERFACE:
|
|
case schema::Type::LIST:
|
|
KJ_FAIL_REQUIRE("Must use one of the other ListSchema::of() overloads for complex types.");
|
|
break;
|
|
|
|
case schema::Type::ANY_POINTER:
|
|
KJ_FAIL_REQUIRE("List(AnyPointer) not supported.");
|
|
break;
|
|
}
|
|
|
|
return ListSchema(primitiveType);
|
|
}
|
|
|
|
ListSchema ListSchema::of(schema::Type::Reader elementType, Schema context) {
|
|
// This method is deprecated because it can only be implemented in terms of other deprecated
|
|
// methods. Temporarily disable warnings for those other deprecated methods.
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
switch (elementType.which()) {
|
|
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::TEXT:
|
|
case schema::Type::DATA:
|
|
return of(elementType.which());
|
|
|
|
case schema::Type::STRUCT:
|
|
return of(context.getDependency(elementType.getStruct().getTypeId()).asStruct());
|
|
|
|
case schema::Type::ENUM:
|
|
return of(context.getDependency(elementType.getEnum().getTypeId()).asEnum());
|
|
|
|
case schema::Type::INTERFACE:
|
|
KJ_FAIL_REQUIRE("Interfaces are not supported.");
|
|
|
|
case schema::Type::LIST:
|
|
return of(of(elementType.getList().getElementType(), context));
|
|
|
|
case schema::Type::ANY_POINTER:
|
|
KJ_FAIL_REQUIRE("List(AnyPointer) not supported.");
|
|
return ListSchema();
|
|
}
|
|
|
|
// Unknown type is acceptable.
|
|
return ListSchema(elementType.which());
|
|
#pragma GCC diagnostic pop
|
|
}
|
|
|
|
// =======================================================================================
|
|
|
|
StructSchema Type::asStruct() const {
|
|
KJ_REQUIRE(isStruct(), "Tried to interpret a non-struct type as a struct.") {
|
|
return StructSchema();
|
|
}
|
|
KJ_ASSERT(schema != nullptr);
|
|
return StructSchema(Schema(schema));
|
|
}
|
|
EnumSchema Type::asEnum() const {
|
|
KJ_REQUIRE(isEnum(), "Tried to interpret a non-enum type as an enum.") {
|
|
return EnumSchema();
|
|
}
|
|
KJ_ASSERT(schema != nullptr);
|
|
return EnumSchema(Schema(schema));
|
|
}
|
|
|
|
ListSchema Type::asList() const {
|
|
KJ_REQUIRE(isList(), "Type::asList(): Not a list.") {
|
|
return ListSchema::of(schema::Type::VOID);
|
|
}
|
|
Type elementType = *this;
|
|
--elementType.listDepth;
|
|
return ListSchema::of(elementType);
|
|
}
|
|
|
|
kj::Maybe<Type::BrandParameter> Type::getBrandParameter() const {
|
|
KJ_REQUIRE(isAnyPointer(), "Type::getBrandParameter() can only be called on AnyPointer types.");
|
|
|
|
if (scopeId == 0) {
|
|
return nullptr;
|
|
} else {
|
|
return BrandParameter { scopeId, paramIndex };
|
|
}
|
|
}
|
|
|
|
kj::Maybe<Type::ImplicitParameter> Type::getImplicitParameter() const {
|
|
KJ_REQUIRE(isAnyPointer(),
|
|
"Type::getImplicitParameter() can only be called on AnyPointer types.");
|
|
|
|
if (isImplicitParam) {
|
|
return ImplicitParameter { paramIndex };
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool Type::operator==(const Type& other) const {
|
|
if (baseType != other.baseType || listDepth != other.listDepth) {
|
|
return false;
|
|
}
|
|
|
|
switch (baseType) {
|
|
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::TEXT:
|
|
case schema::Type::DATA:
|
|
return true;
|
|
|
|
case schema::Type::STRUCT:
|
|
case schema::Type::ENUM:
|
|
case schema::Type::INTERFACE:
|
|
return schema == other.schema;
|
|
|
|
case schema::Type::LIST:
|
|
KJ_UNREACHABLE;
|
|
|
|
case schema::Type::ANY_POINTER:
|
|
return scopeId == other.scopeId && isImplicitParam == other.isImplicitParam &&
|
|
// Trying to comply with strict aliasing rules. Hopefully the compiler realizes that
|
|
// both branches compile to the same instructions and can optimize it away.
|
|
(scopeId != 0 || isImplicitParam ? paramIndex == other.paramIndex
|
|
: anyPointerKind == other.anyPointerKind);
|
|
}
|
|
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
uint Type::hashCode() const {
|
|
switch (baseType) {
|
|
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::TEXT:
|
|
case schema::Type::DATA:
|
|
if (listDepth == 0) {
|
|
// Make sure that hashCode(Type(baseType)) == hashCode(baseType), otherwise HashMap lookups
|
|
// keyed by `Type` won't work when the caller passes `baseType` as the key.
|
|
return kj::hashCode(baseType);
|
|
} else {
|
|
return kj::hashCode(baseType, listDepth);
|
|
}
|
|
|
|
case schema::Type::STRUCT:
|
|
case schema::Type::ENUM:
|
|
case schema::Type::INTERFACE:
|
|
if (listDepth == 0) {
|
|
// Make sure that hashCode(Type(schema)) == hashCode(schema), otherwise HashMap lookups
|
|
// keyed by `Type` won't work when the caller passes `schema` as the key.
|
|
return kj::hashCode(schema);
|
|
} else {
|
|
return kj::hashCode(schema, listDepth);
|
|
}
|
|
|
|
case schema::Type::LIST:
|
|
KJ_UNREACHABLE;
|
|
|
|
case schema::Type::ANY_POINTER: {
|
|
// Trying to comply with strict aliasing rules. Hopefully the compiler realizes that
|
|
// both branches compile to the same instructions and can optimize it away.
|
|
uint16_t val = scopeId != 0 || isImplicitParam ?
|
|
paramIndex : static_cast<uint16_t>(anyPointerKind);
|
|
return kj::hashCode(val, isImplicitParam, scopeId, listDepth);
|
|
}
|
|
}
|
|
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
void Type::requireUsableAs(Type expected) const {
|
|
KJ_REQUIRE(baseType == expected.baseType && listDepth == expected.listDepth,
|
|
"This type is not compatible with the requested native type.");
|
|
|
|
switch (baseType) {
|
|
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::TEXT:
|
|
case schema::Type::DATA:
|
|
case schema::Type::ANY_POINTER:
|
|
break;
|
|
|
|
case schema::Type::STRUCT:
|
|
case schema::Type::ENUM:
|
|
case schema::Type::INTERFACE:
|
|
Schema(schema).requireUsableAs(expected.schema->generic);
|
|
break;
|
|
|
|
case schema::Type::LIST:
|
|
KJ_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
} // namespace capnp
|