dynamic.c++ 72.5 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
Kenton Varda's avatar
Kenton Varda committed
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// 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:
Kenton Varda's avatar
Kenton Varda committed
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
Kenton Varda's avatar
Kenton Varda committed
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// 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.
Kenton Varda's avatar
Kenton Varda committed
21 22

#include "dynamic.h"
Kenton Varda's avatar
Kenton Varda committed
23
#include <kj/debug.h>
Kenton Varda's avatar
Kenton Varda committed
24

25
namespace capnp {
Kenton Varda's avatar
Kenton Varda committed
26 27 28

namespace {

29 30 31 32
bool hasDiscriminantValue(const schema::Field::Reader& reader) {
  return reader.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT;
}

Kenton Varda's avatar
Kenton Varda committed
33
template <typename T, typename U>
34
KJ_ALWAYS_INLINE(T bitCast(U value));
Kenton Varda's avatar
Kenton Varda committed
35 36 37 38 39 40

template <typename T, typename U>
inline T bitCast(U value) {
  static_assert(sizeof(T) == sizeof(U), "Size must match.");
  return value;
}
Kenton Varda's avatar
Kenton Varda committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
template <>
inline float bitCast<float, uint32_t>(uint32_t value) KJ_UNUSED;
template <>
inline float bitCast<float, uint32_t>(uint32_t value) {
  float result;
  memcpy(&result, &value, sizeof(value));
  return result;
}
template <>
inline double bitCast<double, uint64_t>(uint64_t value) KJ_UNUSED;
template <>
inline double bitCast<double, uint64_t>(uint64_t value) {
  double result;
  memcpy(&result, &value, sizeof(value));
  return result;
}
Kenton Varda's avatar
Kenton Varda committed
57 58 59 60 61 62 63 64 65 66 67 68 69
template <>
inline uint32_t bitCast<uint32_t, float>(float value) {
  uint32_t result;
  memcpy(&result, &value, sizeof(value));
  return result;
}
template <>
inline uint64_t bitCast<uint64_t, double>(double value) {
  uint64_t result;
  memcpy(&result, &value, sizeof(value));
  return result;
}

70
ElementSize elementSizeFor(schema::Type::Which elementType) {
Kenton Varda's avatar
Kenton Varda committed
71
  switch (elementType) {
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    case schema::Type::VOID: return ElementSize::VOID;
    case schema::Type::BOOL: return ElementSize::BIT;
    case schema::Type::INT8: return ElementSize::BYTE;
    case schema::Type::INT16: return ElementSize::TWO_BYTES;
    case schema::Type::INT32: return ElementSize::FOUR_BYTES;
    case schema::Type::INT64: return ElementSize::EIGHT_BYTES;
    case schema::Type::UINT8: return ElementSize::BYTE;
    case schema::Type::UINT16: return ElementSize::TWO_BYTES;
    case schema::Type::UINT32: return ElementSize::FOUR_BYTES;
    case schema::Type::UINT64: return ElementSize::EIGHT_BYTES;
    case schema::Type::FLOAT32: return ElementSize::FOUR_BYTES;
    case schema::Type::FLOAT64: return ElementSize::EIGHT_BYTES;

    case schema::Type::TEXT: return ElementSize::POINTER;
    case schema::Type::DATA: return ElementSize::POINTER;
    case schema::Type::LIST: return ElementSize::POINTER;
    case schema::Type::ENUM: return ElementSize::TWO_BYTES;
    case schema::Type::STRUCT: return ElementSize::INLINE_COMPOSITE;
    case schema::Type::INTERFACE: return ElementSize::POINTER;
91
    case schema::Type::ANY_POINTER: KJ_FAIL_ASSERT("List(AnyPointer) not supported."); break;
Kenton Varda's avatar
Kenton Varda committed
92
  }
93 94

  // Unknown type.  Treat it as zero-size.
95
  return ElementSize::VOID;
Kenton Varda's avatar
Kenton Varda committed
96 97
}

98
inline _::StructSize structSizeFromSchema(StructSchema schema) {
Kenton Varda's avatar
Kenton Varda committed
99
  auto node = schema.getProto().getStruct();
100
  return _::StructSize(
101
      node.getDataWordCount() * WORDS,
102
      node.getPointerCount() * POINTERS);
Kenton Varda's avatar
Kenton Varda committed
103 104
}

Kenton Varda's avatar
Kenton Varda committed
105 106 107 108
}  // namespace

// =======================================================================================

109
kj::Maybe<EnumSchema::Enumerant> DynamicEnum::getEnumerant() const {
110
  auto enumerants = schema.getEnumerants();
Kenton Varda's avatar
Kenton Varda committed
111 112 113 114 115 116 117
  if (value < enumerants.size()) {
    return enumerants[value];
  } else {
    return nullptr;
  }
}

118
uint16_t DynamicEnum::asImpl(uint64_t requestedTypeId) const {
119 120
  KJ_REQUIRE(requestedTypeId == schema.getProto().getId(),
             "Type mismatch in DynamicEnum.as().") {
121
    // use it anyway
122
    break;
Kenton Varda's avatar
Kenton Varda committed
123 124 125 126 127 128
  }
  return value;
}

// =======================================================================================

Kenton Varda's avatar
Kenton Varda committed
129 130
bool DynamicStruct::Reader::isSetInUnion(StructSchema::Field field) const {
  auto proto = field.getProto();
131
  if (hasDiscriminantValue(proto)) {
Kenton Varda's avatar
Kenton Varda committed
132 133 134
    uint16_t discrim = reader.getDataField<uint16_t>(
        schema.getProto().getStruct().getDiscriminantOffset() * ELEMENTS);
    return discrim == proto.getDiscriminantValue();
135
  } else {
Kenton Varda's avatar
Kenton Varda committed
136
    return true;
137
  }
Kenton Varda's avatar
Kenton Varda committed
138 139
}

Kenton Varda's avatar
Kenton Varda committed
140 141 142 143
void DynamicStruct::Reader::verifySetInUnion(StructSchema::Field field) const {
  KJ_REQUIRE(isSetInUnion(field),
      "Tried to get() a union member which is not currently initialized.",
      field.getProto().getName(), schema.getProto().getDisplayName());
Kenton Varda's avatar
Kenton Varda committed
144 145
}

Kenton Varda's avatar
Kenton Varda committed
146 147
bool DynamicStruct::Builder::isSetInUnion(StructSchema::Field field) {
  auto proto = field.getProto();
148
  if (hasDiscriminantValue(proto)) {
Kenton Varda's avatar
Kenton Varda committed
149 150 151
    uint16_t discrim = builder.getDataField<uint16_t>(
        schema.getProto().getStruct().getDiscriminantOffset() * ELEMENTS);
    return discrim == proto.getDiscriminantValue();
152
  } else {
Kenton Varda's avatar
Kenton Varda committed
153
    return true;
154
  }
Kenton Varda's avatar
Kenton Varda committed
155 156
}

Kenton Varda's avatar
Kenton Varda committed
157 158 159 160
void DynamicStruct::Builder::verifySetInUnion(StructSchema::Field field) {
  KJ_REQUIRE(isSetInUnion(field),
      "Tried to get() a union member which is not currently initialized.",
      field.getProto().getName(), schema.getProto().getDisplayName());
161 162
}

Kenton Varda's avatar
Kenton Varda committed
163
void DynamicStruct::Builder::setInUnion(StructSchema::Field field) {
164
  // If a union member, set the discriminant to match.
Kenton Varda's avatar
Kenton Varda committed
165
  auto proto = field.getProto();
166
  if (hasDiscriminantValue(proto)) {
167
    builder.setDataField<uint16_t>(
Kenton Varda's avatar
Kenton Varda committed
168 169
        schema.getProto().getStruct().getDiscriminantOffset() * ELEMENTS,
        proto.getDiscriminantValue());
Kenton Varda's avatar
Kenton Varda committed
170
  }
171
}
Kenton Varda's avatar
Kenton Varda committed
172

Kenton Varda's avatar
Kenton Varda committed
173 174 175
DynamicValue::Reader DynamicStruct::Reader::get(StructSchema::Field field) const {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  verifySetInUnion(field);
Kenton Varda's avatar
Kenton Varda committed
176

177
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
178 179
  auto proto = field.getProto();
  switch (proto.which()) {
180 181
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
182 183 184 185 186

      // Note that the default value might be "anyPointer" even if the type is some poniter type
      // *other than* anyPointer. This happens with generics -- the field is actually a generic
      // parameter that has been bound, but the default value was of course compiled without any
      // binding available.
187
      auto dval = slot.getDefaultValue();
188 189

      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
190
        case schema::Type::VOID:
191
          return reader.getDataField<Void>(slot.getOffset() * ELEMENTS);
Kenton Varda's avatar
Kenton Varda committed
192 193

#define HANDLE_TYPE(discrim, titleCase, type) \
Kenton Varda's avatar
Kenton Varda committed
194
        case schema::Type::discrim: \
195
          return reader.getDataField<type>( \
196
              slot.getOffset() * ELEMENTS, \
197
              bitCast<_::Mask<type>>(dval.get##titleCase()));
198 199 200 201 202 203 204 205 206 207 208 209

        HANDLE_TYPE(BOOL, Bool, bool)
        HANDLE_TYPE(INT8, Int8, int8_t)
        HANDLE_TYPE(INT16, Int16, int16_t)
        HANDLE_TYPE(INT32, Int32, int32_t)
        HANDLE_TYPE(INT64, Int64, int64_t)
        HANDLE_TYPE(UINT8, Uint8, uint8_t)
        HANDLE_TYPE(UINT16, Uint16, uint16_t)
        HANDLE_TYPE(UINT32, Uint32, uint32_t)
        HANDLE_TYPE(UINT64, Uint64, uint64_t)
        HANDLE_TYPE(FLOAT32, Float32, float)
        HANDLE_TYPE(FLOAT64, Float64, double)
Kenton Varda's avatar
Kenton Varda committed
210 211 212

#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
213
        case schema::Type::ENUM: {
214 215
          uint16_t typedDval = dval.getEnum();
          return DynamicEnum(type.asEnum(),
216
              reader.getDataField<uint16_t>(slot.getOffset() * ELEMENTS, typedDval));
217 218
        }

Kenton Varda's avatar
Kenton Varda committed
219
        case schema::Type::TEXT: {
220
          Text::Reader typedDval = dval.isAnyPointer() ? Text::Reader() : dval.getText();
221 222
          return reader.getPointerField(slot.getOffset() * POINTERS)
                       .getBlob<Text>(typedDval.begin(), typedDval.size() * BYTES);
223 224
        }

Kenton Varda's avatar
Kenton Varda committed
225
        case schema::Type::DATA: {
226
          Data::Reader typedDval = dval.isAnyPointer() ? Data::Reader() : dval.getData();
227 228
          return reader.getPointerField(slot.getOffset() * POINTERS)
                       .getBlob<Data>(typedDval.begin(), typedDval.size() * BYTES);
229 230
        }

Kenton Varda's avatar
Kenton Varda committed
231
        case schema::Type::LIST: {
232 233
          auto elementType = type.asList().getElementType();
          return DynamicList::Reader(type.asList(),
234
              reader.getPointerField(slot.getOffset() * POINTERS)
235 236
                    .getList(elementSizeFor(elementType.which()), dval.isAnyPointer() ? nullptr :
                        dval.getList().getAs<_::UncheckedMessage>()));
237 238
        }

239
        case schema::Type::STRUCT:
240
          return DynamicStruct::Reader(type.asStruct(),
241
              reader.getPointerField(slot.getOffset() * POINTERS)
242 243
                    .getStruct(dval.isAnyPointer() ? nullptr :
                        dval.getStruct().getAs<_::UncheckedMessage>()));
244

245 246
        case schema::Type::ANY_POINTER:
          return AnyPointer::Reader(reader.getPointerField(slot.getOffset() * POINTERS));
247

Kenton Varda's avatar
Kenton Varda committed
248
        case schema::Type::INTERFACE:
249
          return DynamicCapability::Client(type.asInterface(),
250
              reader.getPointerField(slot.getOffset() * POINTERS).getCapability());
251
      }
Kenton Varda's avatar
Kenton Varda committed
252

Kenton Varda's avatar
Kenton Varda committed
253
      KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
254
    }
Kenton Varda's avatar
Kenton Varda committed
255

Kenton Varda's avatar
Kenton Varda committed
256
    case schema::Field::GROUP:
257
      return DynamicStruct::Reader(type.asStruct(), reader);
Kenton Varda's avatar
Kenton Varda committed
258 259
  }

Kenton Varda's avatar
Kenton Varda committed
260
  KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
261 262
}

Kenton Varda's avatar
Kenton Varda committed
263 264 265
DynamicValue::Builder DynamicStruct::Builder::get(StructSchema::Field field) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  verifySetInUnion(field);
Kenton Varda's avatar
Kenton Varda committed
266

Kenton Varda's avatar
Kenton Varda committed
267
  auto proto = field.getProto();
268
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
269
  switch (proto.which()) {
270 271
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
272 273 274 275 276

      // Note that the default value might be "anyPointer" even if the type is some poniter type
      // *other than* anyPointer. This happens with generics -- the field is actually a generic
      // parameter that has been bound, but the default value was of course compiled without any
      // binding available.
277
      auto dval = slot.getDefaultValue();
278 279

      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
280
        case schema::Type::VOID:
281
          return builder.getDataField<Void>(slot.getOffset() * ELEMENTS);
Kenton Varda's avatar
Kenton Varda committed
282 283

#define HANDLE_TYPE(discrim, titleCase, type) \
Kenton Varda's avatar
Kenton Varda committed
284
        case schema::Type::discrim: \
Kenton Varda's avatar
Kenton Varda committed
285
          return builder.getDataField<type>( \
286
              slot.getOffset() * ELEMENTS, \
Kenton Varda's avatar
Kenton Varda committed
287
              bitCast<_::Mask<type>>(dval.get##titleCase()));
288 289 290 291 292 293 294 295 296 297 298 299

        HANDLE_TYPE(BOOL, Bool, bool)
        HANDLE_TYPE(INT8, Int8, int8_t)
        HANDLE_TYPE(INT16, Int16, int16_t)
        HANDLE_TYPE(INT32, Int32, int32_t)
        HANDLE_TYPE(INT64, Int64, int64_t)
        HANDLE_TYPE(UINT8, Uint8, uint8_t)
        HANDLE_TYPE(UINT16, Uint16, uint16_t)
        HANDLE_TYPE(UINT32, Uint32, uint32_t)
        HANDLE_TYPE(UINT64, Uint64, uint64_t)
        HANDLE_TYPE(FLOAT32, Float32, float)
        HANDLE_TYPE(FLOAT64, Float64, double)
Kenton Varda's avatar
Kenton Varda committed
300 301 302

#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
303
        case schema::Type::ENUM: {
304 305
          uint16_t typedDval = dval.getEnum();
          return DynamicEnum(type.asEnum(),
306
              builder.getDataField<uint16_t>(slot.getOffset() * ELEMENTS, typedDval));
307 308
        }

Kenton Varda's avatar
Kenton Varda committed
309
        case schema::Type::TEXT: {
310
          Text::Reader typedDval = dval.isAnyPointer() ? Text::Reader() : dval.getText();
311 312
          return builder.getPointerField(slot.getOffset() * POINTERS)
                        .getBlob<Text>(typedDval.begin(), typedDval.size() * BYTES);
313 314
        }

Kenton Varda's avatar
Kenton Varda committed
315
        case schema::Type::DATA: {
316
          Data::Reader typedDval = dval.isAnyPointer() ? Data::Reader() : dval.getData();
317 318
          return builder.getPointerField(slot.getOffset() * POINTERS)
                        .getBlob<Data>(typedDval.begin(), typedDval.size() * BYTES);
319 320
        }

Kenton Varda's avatar
Kenton Varda committed
321
        case schema::Type::LIST: {
322
          ListSchema listType = type.asList();
Kenton Varda's avatar
Kenton Varda committed
323
          if (listType.whichElementType() == schema::Type::STRUCT) {
Kenton Varda's avatar
Kenton Varda committed
324
            return DynamicList::Builder(listType,
325 326
                builder.getPointerField(slot.getOffset() * POINTERS)
                       .getStructList(structSizeFromSchema(listType.getStructElementType()),
327 328
                                      dval.isAnyPointer() ? nullptr :
                                          dval.getList().getAs<_::UncheckedMessage>()));
329
          } else {
Kenton Varda's avatar
Kenton Varda committed
330
            return DynamicList::Builder(listType,
331 332
                builder.getPointerField(slot.getOffset() * POINTERS)
                       .getList(elementSizeFor(listType.whichElementType()),
333 334
                                dval.isAnyPointer() ? nullptr :
                                    dval.getList().getAs<_::UncheckedMessage>()));
335 336
          }
        }
337

Kenton Varda's avatar
Kenton Varda committed
338
        case schema::Type::STRUCT: {
339
          auto structSchema = type.asStruct();
Kenton Varda's avatar
Kenton Varda committed
340
          return DynamicStruct::Builder(structSchema,
341 342
              builder.getPointerField(slot.getOffset() * POINTERS)
                     .getStruct(structSizeFromSchema(structSchema),
343 344
                                dval.isAnyPointer() ? nullptr :
                                    dval.getStruct().getAs<_::UncheckedMessage>()));
345 346
        }

347 348
        case schema::Type::ANY_POINTER:
          return AnyPointer::Builder(builder.getPointerField(slot.getOffset() * POINTERS));
349

Kenton Varda's avatar
Kenton Varda committed
350
        case schema::Type::INTERFACE:
351
          return DynamicCapability::Client(type.asInterface(),
352
              builder.getPointerField(slot.getOffset() * POINTERS).getCapability());
353
      }
Kenton Varda's avatar
Kenton Varda committed
354

Kenton Varda's avatar
Kenton Varda committed
355
      KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
356
    }
Kenton Varda's avatar
Kenton Varda committed
357

Kenton Varda's avatar
Kenton Varda committed
358
    case schema::Field::GROUP:
359
      return DynamicStruct::Builder(type.asStruct(), builder);
Kenton Varda's avatar
Kenton Varda committed
360 361
  }

Kenton Varda's avatar
Kenton Varda committed
362
  KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
363
}
Kenton Varda's avatar
Kenton Varda committed
364

365
DynamicValue::Pipeline DynamicStruct::Pipeline::get(StructSchema::Field field) {
366 367 368
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");

  auto proto = field.getProto();
369
  KJ_REQUIRE(!hasDiscriminantValue(proto), "Can't pipeline on union members.");
370

371 372
  auto type = field.getType();

373 374 375 376 377 378
  switch (proto.which()) {
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();

      switch (type.which()) {
        case schema::Type::STRUCT:
379
          return DynamicStruct::Pipeline(type.asStruct(),
380 381 382
              typeless.getPointerField(slot.getOffset()));

        case schema::Type::INTERFACE:
383
          return DynamicCapability::Client(type.asInterface(),
384 385 386 387 388 389 390 391 392 393
              typeless.getPointerField(slot.getOffset()).asCap());

        default:
          KJ_FAIL_REQUIRE("Can only pipeline on struct and interface fields.");
      }

      KJ_UNREACHABLE;
    }

    case schema::Field::GROUP:
394
      return DynamicStruct::Pipeline(type.asStruct(), typeless.noop());
395 396 397 398 399
  }

  KJ_UNREACHABLE;
}

Kenton Varda's avatar
Kenton Varda committed
400 401 402 403
bool DynamicStruct::Reader::has(StructSchema::Field field) const {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");

  auto proto = field.getProto();
404
  if (hasDiscriminantValue(proto)) {
Kenton Varda's avatar
Kenton Varda committed
405 406 407 408 409 410
    uint16_t discrim = reader.getDataField<uint16_t>(
        schema.getProto().getStruct().getDiscriminantOffset() * ELEMENTS);
    if (discrim != proto.getDiscriminantValue()) {
      // Field is not active in the union.
      return false;
    }
411
  }
Kenton Varda's avatar
Kenton Varda committed
412 413

  switch (proto.which()) {
414
    case schema::Field::SLOT:
Kenton Varda's avatar
Kenton Varda committed
415 416 417
      // Continue to below.
      break;

418 419
    case schema::Field::GROUP:
      return true;
Kenton Varda's avatar
Kenton Varda committed
420 421
  }

422
  auto slot = proto.getSlot();
423
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
424 425

  switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
426
    case schema::Type::VOID:
427 428 429 430 431 432 433 434 435 436 437 438 439 440
    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:
      // Primitive types are always present.
      return true;
Kenton Varda's avatar
Kenton Varda committed
441

Kenton Varda's avatar
Kenton Varda committed
442 443 444 445
    case schema::Type::TEXT:
    case schema::Type::DATA:
    case schema::Type::LIST:
    case schema::Type::STRUCT:
446
    case schema::Type::ANY_POINTER:
Kenton Varda's avatar
Kenton Varda committed
447
    case schema::Type::INTERFACE:
448
      return !reader.getPointerField(slot.getOffset() * POINTERS).isNull();
Kenton Varda's avatar
Kenton Varda committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
  }

  // Unknown type.  As far as we know, it isn't set.
  return false;
}

kj::Maybe<StructSchema::Field> DynamicStruct::Reader::which() const {
  auto structProto = schema.getProto().getStruct();
  if (structProto.getDiscriminantCount() == 0) {
    return nullptr;
  }

  uint16_t discrim = reader.getDataField<uint16_t>(structProto.getDiscriminantOffset() * ELEMENTS);
  return schema.getFieldByDiscriminant(discrim);
}

kj::Maybe<StructSchema::Field> DynamicStruct::Builder::which() {
  auto structProto = schema.getProto().getStruct();
  if (structProto.getDiscriminantCount() == 0) {
    return nullptr;
  }

  uint16_t discrim = builder.getDataField<uint16_t>(structProto.getDiscriminantOffset() * ELEMENTS);
  return schema.getFieldByDiscriminant(discrim);
}

void DynamicStruct::Builder::set(StructSchema::Field field, const DynamicValue::Reader& value) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
480
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
481
  switch (proto.which()) {
482 483 484
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
      auto dval = slot.getDefaultValue();
Kenton Varda's avatar
Kenton Varda committed
485

486
      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
487
        case schema::Type::VOID:
488
          builder.setDataField<Void>(slot.getOffset() * ELEMENTS, value.as<Void>());
489
          return;
Kenton Varda's avatar
Kenton Varda committed
490

491
#define HANDLE_TYPE(discrim, titleCase, type) \
Kenton Varda's avatar
Kenton Varda committed
492
        case schema::Type::discrim: \
493
          builder.setDataField<type>( \
494
              slot.getOffset() * ELEMENTS, value.as<type>(), \
Kenton Varda's avatar
Kenton Varda committed
495
              bitCast<_::Mask<type> >(dval.get##titleCase())); \
496 497 498 499 500 501 502 503 504 505 506 507 508
          return;

        HANDLE_TYPE(BOOL, Bool, bool)
        HANDLE_TYPE(INT8, Int8, int8_t)
        HANDLE_TYPE(INT16, Int16, int16_t)
        HANDLE_TYPE(INT32, Int32, int32_t)
        HANDLE_TYPE(INT64, Int64, int64_t)
        HANDLE_TYPE(UINT8, Uint8, uint8_t)
        HANDLE_TYPE(UINT16, Uint16, uint16_t)
        HANDLE_TYPE(UINT32, Uint32, uint32_t)
        HANDLE_TYPE(UINT64, Uint64, uint64_t)
        HANDLE_TYPE(FLOAT32, Float32, float)
        HANDLE_TYPE(FLOAT64, Float64, double)
Kenton Varda's avatar
Kenton Varda committed
509 510 511

#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
512
        case schema::Type::ENUM: {
513
          uint16_t rawValue;
514
          auto enumSchema = type.asEnum();
515 516 517
          if (value.getType() == DynamicValue::TEXT) {
            // Convert from text.
            rawValue = enumSchema.getEnumerantByName(value.as<Text>()).getOrdinal();
518 519
          } else if (value.getType() == DynamicValue::INT ||
                     value.getType() == DynamicValue::UINT) {
520
            rawValue = value.as<uint16_t>();
Kenton Varda's avatar
Kenton Varda committed
521
          } else {
522
            DynamicEnum enumValue = value.as<DynamicEnum>();
523
            KJ_REQUIRE(enumValue.getSchema() == enumSchema, "Value type mismatch.") {
524 525 526 527
              return;
            }
            rawValue = enumValue.getRaw();
          }
528
          builder.setDataField<uint16_t>(slot.getOffset() * ELEMENTS, rawValue,
Kenton Varda's avatar
Kenton Varda committed
529
                                         dval.getEnum());
530
          return;
531
        }
532

Kenton Varda's avatar
Kenton Varda committed
533
        case schema::Type::TEXT:
534
          builder.getPointerField(slot.getOffset() * POINTERS).setBlob<Text>(value.as<Text>());
535 536
          return;

Kenton Varda's avatar
Kenton Varda committed
537
        case schema::Type::DATA:
538
          builder.getPointerField(slot.getOffset() * POINTERS).setBlob<Data>(value.as<Data>());
539 540
          return;

541
        case schema::Type::LIST: {
542
          ListSchema listType = type.asList();
543 544 545 546
          auto listValue = value.as<DynamicList>();
          KJ_REQUIRE(listValue.getSchema() == listType, "Value type mismatch.") {
            return;
          }
547
          builder.getPointerField(slot.getOffset() * POINTERS).setList(listValue.reader);
548
          return;
549
        }
550

551
        case schema::Type::STRUCT: {
552
          auto structType = type.asStruct();
553 554 555 556
          auto structValue = value.as<DynamicStruct>();
          KJ_REQUIRE(structValue.getSchema() == structType, "Value type mismatch.") {
            return;
          }
557
          builder.getPointerField(slot.getOffset() * POINTERS).setStruct(structValue.reader);
558
          return;
559
        }
560

561 562 563
        case schema::Type::ANY_POINTER:
          AnyPointer::Builder(builder.getPointerField(slot.getOffset() * POINTERS))
                 .set(value.as<AnyPointer>());
564 565
          return;

Kenton Varda's avatar
Kenton Varda committed
566
        case schema::Type::INTERFACE:
567
          auto interfaceType = type.asInterface();
568 569 570 571 572 573
          auto capability = value.as<DynamicCapability>();
          KJ_REQUIRE(capability.getSchema().extends(interfaceType), "Value type mismatch.") {
            return;
          }
          builder.getPointerField(slot.getOffset() * POINTERS).setCapability(
              kj::mv(capability.hook));
574 575
          return;
      }
Kenton Varda's avatar
Kenton Varda committed
576

Kenton Varda's avatar
Kenton Varda committed
577 578 579
      KJ_UNREACHABLE;
    }

Kenton Varda's avatar
Kenton Varda committed
580
    case schema::Field::GROUP: {
Kenton Varda's avatar
Kenton Varda committed
581 582 583 584 585 586 587 588 589 590 591
      auto src = value.as<DynamicStruct>();
      auto dst = init(field).as<DynamicStruct>();

      KJ_IF_MAYBE(unionField, src.which()) {
        dst.set(*unionField, src.get(*unionField));
      }

      for (auto field: src.schema.getNonUnionFields()) {
        if (src.has(field)) {
          dst.set(field, src.get(field));
        }
592
      }
Kenton Varda's avatar
Kenton Varda committed
593
    }
594
  }
Kenton Varda's avatar
Kenton Varda committed
595

Kenton Varda's avatar
Kenton Varda committed
596
  KJ_UNREACHABLE;
597
}
Kenton Varda's avatar
Kenton Varda committed
598

Kenton Varda's avatar
Kenton Varda committed
599 600 601 602 603
DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Field field) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
604
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
605 606

  switch (proto.which()) {
607 608
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
609 610
      switch (type.which()) {
        case schema::Type::STRUCT: {
611
          auto subSchema = type.asStruct();
612 613 614 615
          return DynamicStruct::Builder(subSchema,
              builder.getPointerField(slot.getOffset() * POINTERS)
                     .initStruct(structSizeFromSchema(subSchema)));
        }
616
        case schema::Type::ANY_POINTER: {
617 618
          auto pointer = builder.getPointerField(slot.getOffset() * POINTERS);
          pointer.clear();
619
          return AnyPointer::Builder(pointer);
620 621 622 623
        }
        default:
          KJ_FAIL_REQUIRE("init() without a size is only valid for struct and object fields.");
      }
Kenton Varda's avatar
Kenton Varda committed
624 625
    }

Kenton Varda's avatar
Kenton Varda committed
626
    case schema::Field::GROUP: {
Kenton Varda's avatar
Kenton Varda committed
627
      clear(field);
628
      return DynamicStruct::Builder(type.asStruct(), builder);
Kenton Varda's avatar
Kenton Varda committed
629 630 631 632 633
    }
  }

  KJ_UNREACHABLE;
}
Kenton Varda's avatar
Kenton Varda committed
634

Kenton Varda's avatar
Kenton Varda committed
635 636 637 638 639
DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Field field, uint size) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
640
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
641 642

  switch (proto.which()) {
643 644
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
645
      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
646
        case schema::Type::LIST: {
647
          auto listType = type.asList();
Kenton Varda's avatar
Kenton Varda committed
648
          if (listType.whichElementType() == schema::Type::STRUCT) {
Kenton Varda's avatar
Kenton Varda committed
649
            return DynamicList::Builder(listType,
650 651 652
                builder.getPointerField(slot.getOffset() * POINTERS)
                       .initStructList(size * ELEMENTS,
                                       structSizeFromSchema(listType.getStructElementType())));
Kenton Varda's avatar
Kenton Varda committed
653 654
          } else {
            return DynamicList::Builder(listType,
655 656
                builder.getPointerField(slot.getOffset() * POINTERS)
                       .initList(elementSizeFor(listType.whichElementType()), size * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
657 658
          }
        }
Kenton Varda's avatar
Kenton Varda committed
659
        case schema::Type::TEXT:
660 661
          return builder.getPointerField(slot.getOffset() * POINTERS)
                        .initBlob<Text>(size * BYTES);
Kenton Varda's avatar
Kenton Varda committed
662
        case schema::Type::DATA:
663 664
          return builder.getPointerField(slot.getOffset() * POINTERS)
                        .initBlob<Data>(size * BYTES);
665
        default:
666
          KJ_FAIL_REQUIRE(
667 668
              "init() with size is only valid for list, text, or data fields.",
              (uint)type.which());
669 670
          break;
      }
671
    }
Kenton Varda's avatar
Kenton Varda committed
672

Kenton Varda's avatar
Kenton Varda committed
673
    case schema::Field::GROUP:
Kenton Varda's avatar
Kenton Varda committed
674
      KJ_FAIL_REQUIRE("init() with size is only valid for list, text, or data fields.");
Kenton Varda's avatar
Kenton Varda committed
675 676
  }

Kenton Varda's avatar
Kenton Varda committed
677
  KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
678 679
}

680 681 682 683 684 685
void DynamicStruct::Builder::adopt(StructSchema::Field field, Orphan<DynamicValue>&& orphan) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
  switch (proto.which()) {
686 687
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
688
      auto type = field.getType();
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707

      switch (type.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::ENUM:
          set(field, orphan.getReader());
          return;

        case schema::Type::TEXT:
708
          KJ_REQUIRE(orphan.getType() == DynamicValue::TEXT, "Value type mismatch.");
709 710 711
          break;

        case schema::Type::DATA:
712
          KJ_REQUIRE(orphan.getType() == DynamicValue::DATA, "Value type mismatch.");
713 714 715
          break;

        case schema::Type::LIST: {
716
          ListSchema listType = type.asList();
717
          KJ_REQUIRE(orphan.getType() == DynamicValue::LIST && orphan.listSchema == listType,
718 719 720
                     "Value type mismatch.") {
            return;
          }
721 722 723 724
          break;
        }

        case schema::Type::STRUCT: {
725
          auto structType = type.asStruct();
726
          KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == structType,
727 728 729
                     "Value type mismatch.") {
            return;
          }
730 731 732
          break;
        }

733
        case schema::Type::ANY_POINTER:
734 735
          KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT ||
                     orphan.getType() == DynamicValue::LIST ||
736 737
                     orphan.getType() == DynamicValue::TEXT ||
                     orphan.getType() == DynamicValue::DATA ||
738
                     orphan.getType() == DynamicValue::CAPABILITY ||
739
                     orphan.getType() == DynamicValue::ANY_POINTER,
740 741 742
                     "Value type mismatch.") {
            return;
          }
743 744 745
          break;

        case schema::Type::INTERFACE:
746
          auto interfaceType = type.asInterface();
747 748 749 750 751
          KJ_REQUIRE(orphan.getType() == DynamicValue::CAPABILITY &&
                     orphan.interfaceSchema.extends(interfaceType),
                     "Value type mismatch.") {
            return;
          }
752 753 754
          break;
      }

755
      builder.getPointerField(slot.getOffset() * POINTERS).adopt(kj::mv(orphan.builder));
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
      return;
    }

    case schema::Field::GROUP:
      // Have to transfer fields.
      auto src = orphan.get().as<DynamicStruct>();
      auto dst = init(field).as<DynamicStruct>();

      KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == dst.getSchema(),
                 "Value type mismatch.");

      KJ_IF_MAYBE(unionField, src.which()) {
        dst.adopt(*unionField, src.disown(*unionField));
      }

      for (auto field: src.schema.getNonUnionFields()) {
        if (src.has(field)) {
          dst.adopt(field, src.disown(field));
        }
      }

      return;
  }

  KJ_UNREACHABLE;
}

Orphan<DynamicValue> DynamicStruct::Builder::disown(StructSchema::Field field) {
  // We end up calling get(field) below, so we don't need to validate `field` here.

  auto proto = field.getProto();
  switch (proto.which()) {
788 789
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
790

791
      switch (field.getType().which()) {
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
        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>(get(field), _::OrphanBuilder());
          clear(field);
          return kj::mv(result);
        }

        case schema::Type::TEXT:
        case schema::Type::DATA:
        case schema::Type::LIST:
        case schema::Type::STRUCT:
814
        case schema::Type::ANY_POINTER:
815 816
        case schema::Type::INTERFACE: {
          auto value = get(field);
817 818
          return Orphan<DynamicValue>(
              value, builder.getPointerField(slot.getOffset() * POINTERS).disown());
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
        }
      }
      KJ_UNREACHABLE;
    }

    case schema::Field::GROUP: {
      // We have to allocate new space for the group, unfortunately.
      auto src = get(field).as<DynamicStruct>();

      Orphan<DynamicStruct> result =
          Orphanage::getForMessageContaining(*this).newOrphan(src.getSchema());
      auto dst = result.get();

      KJ_IF_MAYBE(unionField, src.which()) {
        dst.adopt(*unionField, src.disown(*unionField));
      }

      // We need to explicitly reset the union to its default field.
      KJ_IF_MAYBE(unionField, src.schema.getFieldByDiscriminant(0)) {
        src.clear(*unionField);
      }

      for (auto field: src.schema.getNonUnionFields()) {
        if (src.has(field)) {
          dst.adopt(field, src.disown(field));
        }
      }

      return kj::mv(result);
    }
  }

  KJ_UNREACHABLE;
}
Kenton Varda's avatar
Kenton Varda committed
853 854 855 856 857 858

void DynamicStruct::Builder::clear(StructSchema::Field field) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
859
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
860
  switch (proto.which()) {
861 862
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
Kenton Varda's avatar
Kenton Varda committed
863 864

      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
865
        case schema::Type::VOID:
866
          builder.setDataField<Void>(slot.getOffset() * ELEMENTS, VOID);
Kenton Varda's avatar
Kenton Varda committed
867 868 869
          return;

#define HANDLE_TYPE(discrim, type) \
Kenton Varda's avatar
Kenton Varda committed
870
        case schema::Type::discrim: \
871
          builder.setDataField<type>(slot.getOffset() * ELEMENTS, 0); \
Kenton Varda's avatar
Kenton Varda committed
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
          return;

        HANDLE_TYPE(BOOL, bool)
        HANDLE_TYPE(INT8, uint8_t)
        HANDLE_TYPE(INT16, uint16_t)
        HANDLE_TYPE(INT32, uint32_t)
        HANDLE_TYPE(INT64, uint64_t)
        HANDLE_TYPE(UINT8, uint8_t)
        HANDLE_TYPE(UINT16, uint16_t)
        HANDLE_TYPE(UINT32, uint32_t)
        HANDLE_TYPE(UINT64, uint64_t)
        HANDLE_TYPE(FLOAT32, uint32_t)
        HANDLE_TYPE(FLOAT64, uint64_t)
        HANDLE_TYPE(ENUM, uint16_t)

#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
889 890 891 892
        case schema::Type::TEXT:
        case schema::Type::DATA:
        case schema::Type::LIST:
        case schema::Type::STRUCT:
893
        case schema::Type::ANY_POINTER:
Kenton Varda's avatar
Kenton Varda committed
894
        case schema::Type::INTERFACE:
895
          builder.getPointerField(slot.getOffset() * POINTERS).clear();
Kenton Varda's avatar
Kenton Varda committed
896 897 898 899 900 901
          return;
      }

      KJ_UNREACHABLE;
    }

Kenton Varda's avatar
Kenton Varda committed
902
    case schema::Field::GROUP: {
903
      DynamicStruct::Builder group(type.asStruct(), builder);
904 905 906

      // We clear the union field with discriminant 0 rather than the one that is set because
      // we want the union to end up with its default field active.
Kenton Varda's avatar
Kenton Varda committed
907 908 909
      KJ_IF_MAYBE(unionField, group.schema.getFieldByDiscriminant(0)) {
        group.clear(*unionField);
      }
910

Kenton Varda's avatar
Kenton Varda committed
911 912 913
      for (auto subField: group.schema.getNonUnionFields()) {
        group.clear(subField);
      }
914
      return;
Kenton Varda's avatar
Kenton Varda committed
915 916
    }
  }
917

Kenton Varda's avatar
Kenton Varda committed
918 919 920 921 922 923 924 925 926
  KJ_UNREACHABLE;
}

DynamicValue::Reader DynamicStruct::Reader::get(kj::StringPtr name) const {
  return get(schema.getFieldByName(name));
}
DynamicValue::Builder DynamicStruct::Builder::get(kj::StringPtr name) {
  return get(schema.getFieldByName(name));
}
927
DynamicValue::Pipeline DynamicStruct::Pipeline::get(kj::StringPtr name) {
928 929
  return get(schema.getFieldByName(name));
}
Kenton Varda's avatar
Kenton Varda committed
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
bool DynamicStruct::Reader::has(kj::StringPtr name) const {
  return has(schema.getFieldByName(name));
}
bool DynamicStruct::Builder::has(kj::StringPtr name) {
  return has(schema.getFieldByName(name));
}
void DynamicStruct::Builder::set(kj::StringPtr name, const DynamicValue::Reader& value) {
  set(schema.getFieldByName(name), value);
}
void DynamicStruct::Builder::set(kj::StringPtr name,
                                 std::initializer_list<DynamicValue::Reader> value) {
  auto list = init(name, value.size()).as<DynamicList>();
  uint i = 0;
  for (auto element: value) {
    list.set(i++, element);
  }
}
DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name) {
  return init(schema.getFieldByName(name));
}
DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name, uint size) {
  return init(schema.getFieldByName(name), size);
}
953 954 955 956 957 958
void DynamicStruct::Builder::adopt(kj::StringPtr name, Orphan<DynamicValue>&& orphan) {
  adopt(schema.getFieldByName(name), kj::mv(orphan));
}
Orphan<DynamicValue> DynamicStruct::Builder::disown(kj::StringPtr name) {
  return disown(schema.getFieldByName(name));
}
Kenton Varda's avatar
Kenton Varda committed
959
void DynamicStruct::Builder::clear(kj::StringPtr name) {
960
  clear(schema.getFieldByName(name));
Kenton Varda's avatar
Kenton Varda committed
961
}
Kenton Varda's avatar
Kenton Varda committed
962 963 964

// =======================================================================================

965
DynamicValue::Reader DynamicList::Reader::operator[](uint index) const {
966
  KJ_REQUIRE(index < size(), "List index out-of-bounds.");
Kenton Varda's avatar
Kenton Varda committed
967

968
  switch (schema.whichElementType()) {
Kenton Varda's avatar
Kenton Varda committed
969
#define HANDLE_TYPE(name, discrim, typeName) \
Kenton Varda's avatar
Kenton Varda committed
970
    case schema::Type::discrim: \
971
      return reader.getDataElement<typeName>(index * ELEMENTS);
972 973 974 975 976 977 978 979 980 981 982 983 984

    HANDLE_TYPE(void, VOID, Void)
    HANDLE_TYPE(bool, BOOL, bool)
    HANDLE_TYPE(int8, INT8, int8_t)
    HANDLE_TYPE(int16, INT16, int16_t)
    HANDLE_TYPE(int32, INT32, int32_t)
    HANDLE_TYPE(int64, INT64, int64_t)
    HANDLE_TYPE(uint8, UINT8, uint8_t)
    HANDLE_TYPE(uint16, UINT16, uint16_t)
    HANDLE_TYPE(uint32, UINT32, uint32_t)
    HANDLE_TYPE(uint64, UINT64, uint64_t)
    HANDLE_TYPE(float32, FLOAT32, float)
    HANDLE_TYPE(float64, FLOAT64, double)
Kenton Varda's avatar
Kenton Varda committed
985 986
#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
987
    case schema::Type::TEXT:
988
      return reader.getPointerElement(index * ELEMENTS).getBlob<Text>(nullptr, 0 * BYTES);
Kenton Varda's avatar
Kenton Varda committed
989
    case schema::Type::DATA:
990
      return reader.getPointerElement(index * ELEMENTS).getBlob<Data>(nullptr, 0 * BYTES);
Kenton Varda's avatar
Kenton Varda committed
991

Kenton Varda's avatar
Kenton Varda committed
992
    case schema::Type::LIST: {
993
      auto elementType = schema.getListElementType();
994 995 996
      return DynamicList::Reader(elementType,
          reader.getPointerElement(index * ELEMENTS)
                .getList(elementSizeFor(elementType.whichElementType()), nullptr));
997
    }
Kenton Varda's avatar
Kenton Varda committed
998

Kenton Varda's avatar
Kenton Varda committed
999
    case schema::Type::STRUCT:
1000 1001
      return DynamicStruct::Reader(schema.getStructElementType(),
                                   reader.getStructElement(index * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1002

Kenton Varda's avatar
Kenton Varda committed
1003
    case schema::Type::ENUM:
1004 1005
      return DynamicEnum(schema.getEnumElementType(),
                         reader.getDataElement<uint16_t>(index * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1006

1007 1008
    case schema::Type::ANY_POINTER:
      return AnyPointer::Reader(reader.getPointerElement(index * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1009

Kenton Varda's avatar
Kenton Varda committed
1010
    case schema::Type::INTERFACE:
1011 1012
      return DynamicCapability::Client(schema.getInterfaceElementType(),
                                       reader.getPointerElement(index * ELEMENTS).getCapability());
Kenton Varda's avatar
Kenton Varda committed
1013
  }
1014

1015
  return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1016 1017
}

1018
DynamicValue::Builder DynamicList::Builder::operator[](uint index) {
1019
  KJ_REQUIRE(index < size(), "List index out-of-bounds.");
Kenton Varda's avatar
Kenton Varda committed
1020

1021
  switch (schema.whichElementType()) {
Kenton Varda's avatar
Kenton Varda committed
1022
#define HANDLE_TYPE(name, discrim, typeName) \
Kenton Varda's avatar
Kenton Varda committed
1023
    case schema::Type::discrim: \
1024
      return builder.getDataElement<typeName>(index * ELEMENTS);
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037

    HANDLE_TYPE(void, VOID, Void)
    HANDLE_TYPE(bool, BOOL, bool)
    HANDLE_TYPE(int8, INT8, int8_t)
    HANDLE_TYPE(int16, INT16, int16_t)
    HANDLE_TYPE(int32, INT32, int32_t)
    HANDLE_TYPE(int64, INT64, int64_t)
    HANDLE_TYPE(uint8, UINT8, uint8_t)
    HANDLE_TYPE(uint16, UINT16, uint16_t)
    HANDLE_TYPE(uint32, UINT32, uint32_t)
    HANDLE_TYPE(uint64, UINT64, uint64_t)
    HANDLE_TYPE(float32, FLOAT32, float)
    HANDLE_TYPE(float64, FLOAT64, double)
Kenton Varda's avatar
Kenton Varda committed
1038 1039
#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
1040
    case schema::Type::TEXT:
1041
      return builder.getPointerElement(index * ELEMENTS).getBlob<Text>(nullptr, 0 * BYTES);
Kenton Varda's avatar
Kenton Varda committed
1042
    case schema::Type::DATA:
1043
      return builder.getPointerElement(index * ELEMENTS).getBlob<Data>(nullptr, 0 * BYTES);
Kenton Varda's avatar
Kenton Varda committed
1044

Kenton Varda's avatar
Kenton Varda committed
1045
    case schema::Type::LIST: {
1046
      ListSchema elementType = schema.getListElementType();
Kenton Varda's avatar
Kenton Varda committed
1047
      if (elementType.whichElementType() == schema::Type::STRUCT) {
1048
        return DynamicList::Builder(elementType,
1049 1050 1051
            builder.getPointerElement(index * ELEMENTS)
                   .getStructList(structSizeFromSchema(elementType.getStructElementType()),
                                  nullptr));
1052
      } else {
1053
        return DynamicList::Builder(elementType,
1054 1055
            builder.getPointerElement(index * ELEMENTS)
                   .getList(elementSizeFor(elementType.whichElementType()), nullptr));
1056 1057
      }
    }
Kenton Varda's avatar
Kenton Varda committed
1058

Kenton Varda's avatar
Kenton Varda committed
1059
    case schema::Type::STRUCT:
1060 1061
      return DynamicStruct::Builder(schema.getStructElementType(),
                                    builder.getStructElement(index * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1062

Kenton Varda's avatar
Kenton Varda committed
1063
    case schema::Type::ENUM:
1064 1065
      return DynamicEnum(schema.getEnumElementType(),
                         builder.getDataElement<uint16_t>(index * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1066

1067 1068
    case schema::Type::ANY_POINTER:
      KJ_FAIL_ASSERT("List(AnyPointer) not supported.");
1069
      return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1070

Kenton Varda's avatar
Kenton Varda committed
1071
    case schema::Type::INTERFACE:
1072 1073
      return DynamicCapability::Client(schema.getInterfaceElementType(),
                                       builder.getPointerElement(index * ELEMENTS).getCapability());
Kenton Varda's avatar
Kenton Varda committed
1074
  }
1075

1076
  return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1077 1078
}

1079
void DynamicList::Builder::set(uint index, const DynamicValue::Reader& value) {
1080
  KJ_REQUIRE(index < size(), "List index out-of-bounds.") {
1081 1082
    return;
  }
Kenton Varda's avatar
Kenton Varda committed
1083

1084
  switch (schema.whichElementType()) {
Kenton Varda's avatar
Kenton Varda committed
1085
#define HANDLE_TYPE(name, discrim, typeName) \
Kenton Varda's avatar
Kenton Varda committed
1086
    case schema::Type::discrim: \
1087
      builder.setDataElement<typeName>(index * ELEMENTS, value.as<typeName>()); \
1088
      return;
Kenton Varda's avatar
Kenton Varda committed
1089

1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
    HANDLE_TYPE(void, VOID, Void)
    HANDLE_TYPE(bool, BOOL, bool)
    HANDLE_TYPE(int8, INT8, int8_t)
    HANDLE_TYPE(int16, INT16, int16_t)
    HANDLE_TYPE(int32, INT32, int32_t)
    HANDLE_TYPE(int64, INT64, int64_t)
    HANDLE_TYPE(uint8, UINT8, uint8_t)
    HANDLE_TYPE(uint16, UINT16, uint16_t)
    HANDLE_TYPE(uint32, UINT32, uint32_t)
    HANDLE_TYPE(uint64, UINT64, uint64_t)
    HANDLE_TYPE(float32, FLOAT32, float)
    HANDLE_TYPE(float64, FLOAT64, double)
Kenton Varda's avatar
Kenton Varda committed
1102 1103
#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
1104
    case schema::Type::TEXT:
1105
      builder.getPointerElement(index * ELEMENTS).setBlob<Text>(value.as<Text>());
1106
      return;
Kenton Varda's avatar
Kenton Varda committed
1107
    case schema::Type::DATA:
1108
      builder.getPointerElement(index * ELEMENTS).setBlob<Data>(value.as<Data>());
1109
      return;
Kenton Varda's avatar
Kenton Varda committed
1110

Kenton Varda's avatar
Kenton Varda committed
1111
    case schema::Type::LIST: {
1112 1113 1114 1115
      auto listValue = value.as<DynamicList>();
      KJ_REQUIRE(listValue.getSchema() == schema.getListElementType(), "Value type mismatch.") {
        return;
      }
1116
      builder.getPointerElement(index * ELEMENTS).setList(listValue.reader);
1117
      return;
1118
    }
Kenton Varda's avatar
Kenton Varda committed
1119

1120
    case schema::Type::STRUCT: {
1121 1122 1123 1124 1125
      auto structValue = value.as<DynamicStruct>();
      KJ_REQUIRE(structValue.getSchema() == schema.getStructElementType(), "Value type mismatch.") {
        return;
      }
      builder.getStructElement(index * ELEMENTS).copyContentFrom(structValue.reader);
1126 1127
      return;
    }
Kenton Varda's avatar
Kenton Varda committed
1128

Kenton Varda's avatar
Kenton Varda committed
1129
    case schema::Type::ENUM: {
1130 1131 1132 1133 1134 1135
      uint16_t rawValue;
      if (value.getType() == DynamicValue::TEXT) {
        // Convert from text.
        rawValue = schema.getEnumElementType().getEnumerantByName(value.as<Text>()).getOrdinal();
      } else {
        DynamicEnum enumValue = value.as<DynamicEnum>();
1136 1137
        KJ_REQUIRE(schema.getEnumElementType() == enumValue.getSchema(),
                   "Type mismatch when using DynamicList::Builder::set().") {
1138 1139 1140
          return;
        }
        rawValue = enumValue.getRaw();
1141
      }
1142
      builder.setDataElement<uint16_t>(index * ELEMENTS, rawValue);
1143
      return;
1144
    }
Kenton Varda's avatar
Kenton Varda committed
1145

1146 1147
    case schema::Type::ANY_POINTER:
      KJ_FAIL_ASSERT("List(AnyPointer) not supported.") {
1148 1149
        return;
      }
Kenton Varda's avatar
Kenton Varda committed
1150

1151 1152 1153 1154
    case schema::Type::INTERFACE: {
      auto capValue = value.as<DynamicCapability>();
      KJ_REQUIRE(capValue.getSchema().extends(schema.getInterfaceElementType()),
                 "Value type mismatch.") {
1155 1156
        return;
      }
1157 1158 1159
      builder.getPointerElement(index * ELEMENTS).setCapability(kj::mv(capValue.hook));
      return;
    }
Kenton Varda's avatar
Kenton Varda committed
1160
  }
1161

1162 1163 1164
  KJ_FAIL_REQUIRE("can't set element of unknown type", (uint)schema.whichElementType()) {
    return;
  }
Kenton Varda's avatar
Kenton Varda committed
1165 1166 1167
}

DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) {
1168
  KJ_REQUIRE(index < this->size(), "List index out-of-bounds.");
Kenton Varda's avatar
Kenton Varda committed
1169

1170
  switch (schema.whichElementType()) {
Kenton Varda's avatar
Kenton Varda committed
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
    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:
    case schema::Type::STRUCT:
    case schema::Type::INTERFACE:
1186
      KJ_FAIL_REQUIRE("Expected a list or blob.");
1187
      return nullptr;
1188

Kenton Varda's avatar
Kenton Varda committed
1189
    case schema::Type::TEXT:
1190
      return builder.getPointerElement(index * ELEMENTS).initBlob<Text>(size * BYTES);
1191

Kenton Varda's avatar
Kenton Varda committed
1192
    case schema::Type::DATA:
1193
      return builder.getPointerElement(index * ELEMENTS).initBlob<Data>(size * BYTES);
1194

Kenton Varda's avatar
Kenton Varda committed
1195
    case schema::Type::LIST: {
1196 1197
      auto elementType = schema.getListElementType();

Kenton Varda's avatar
Kenton Varda committed
1198
      if (elementType.whichElementType() == schema::Type::STRUCT) {
1199 1200 1201 1202
        return DynamicList::Builder(elementType,
            builder.getPointerElement(index * ELEMENTS)
                   .initStructList(size * ELEMENTS,
                                   structSizeFromSchema(elementType.getStructElementType())));
1203
      } else {
1204 1205 1206
        return DynamicList::Builder(elementType,
            builder.getPointerElement(index * ELEMENTS)
                   .initList(elementSizeFor(elementType.whichElementType()), size * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1207 1208 1209
      }
    }

1210 1211
    case schema::Type::ANY_POINTER: {
      KJ_FAIL_ASSERT("List(AnyPointer) not supported.");
1212
      return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1213 1214
    }
  }
1215

1216
  return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1217 1218
}

1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
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.");
1239
      builder.getPointerElement(index * ELEMENTS).adopt(kj::mv(orphan.builder));
1240 1241 1242 1243
      return;

    case schema::Type::DATA:
      KJ_REQUIRE(orphan.getType() == DynamicValue::DATA, "Value type mismatch.");
1244
      builder.getPointerElement(index * ELEMENTS).adopt(kj::mv(orphan.builder));
1245 1246 1247 1248 1249 1250
      return;

    case schema::Type::LIST: {
      ListSchema elementType = schema.getListElementType();
      KJ_REQUIRE(orphan.getType() == DynamicValue::LIST && orphan.listSchema == elementType,
                 "Value type mismatch.");
1251
      builder.getPointerElement(index * ELEMENTS).adopt(kj::mv(orphan.builder));
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
      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;
    }

1264 1265
    case schema::Type::ANY_POINTER:
      KJ_FAIL_ASSERT("List(AnyPointer) not supported.");
1266

1267 1268 1269 1270 1271 1272 1273 1274
    case schema::Type::INTERFACE: {
      auto elementType = schema.getInterfaceElementType();
      KJ_REQUIRE(orphan.getType() == DynamicValue::CAPABILITY &&
                 orphan.interfaceSchema.extends(elementType),
                 "Value type mismatch.");
      builder.getPointerElement(index * ELEMENTS).adopt(kj::mv(orphan.builder));
      return;
    }
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
  }

  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())) {
1297 1298 1299 1300 1301 1302 1303 1304 1305
        case ElementSize::VOID: break;
        case ElementSize::BIT: builder.setDataElement<bool>(index * ELEMENTS, false); break;
        case ElementSize::BYTE: builder.setDataElement<uint8_t>(index * ELEMENTS, 0); break;
        case ElementSize::TWO_BYTES: builder.setDataElement<uint16_t>(index * ELEMENTS, 0); break;
        case ElementSize::FOUR_BYTES: builder.setDataElement<uint32_t>(index * ELEMENTS, 0); break;
        case ElementSize::EIGHT_BYTES: builder.setDataElement<uint64_t>(index * ELEMENTS, 0);break;

        case ElementSize::POINTER:
        case ElementSize::INLINE_COMPOSITE:
1306 1307 1308 1309 1310 1311 1312 1313
          KJ_UNREACHABLE;
      }
      return kj::mv(result);
    }

    case schema::Type::TEXT:
    case schema::Type::DATA:
    case schema::Type::LIST:
1314
    case schema::Type::ANY_POINTER:
1315 1316
    case schema::Type::INTERFACE: {
      auto value = operator[](index);
1317
      return Orphan<DynamicValue>(value, builder.getPointerElement(index * ELEMENTS).disown());
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
    }

    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;
}

1333
void DynamicList::Builder::copyFrom(std::initializer_list<DynamicValue::Reader> value) {
1334
  KJ_REQUIRE(value.size() == size(), "DynamicList::copyFrom() argument had different size.");
1335 1336 1337 1338 1339 1340
  uint i = 0;
  for (auto element: value) {
    set(i++, element);
  }
}

1341
DynamicList::Reader DynamicList::Builder::asReader() const {
1342
  return DynamicList::Reader(schema, builder.asReader());
Kenton Varda's avatar
Kenton Varda committed
1343 1344
}

Kenton Varda's avatar
Kenton Varda committed
1345 1346
// =======================================================================================

Kenton Varda's avatar
Kenton Varda committed
1347
DynamicValue::Reader::Reader(ConstSchema constant): type(VOID) {
1348
  auto type = constant.getType();
1349
  auto value = constant.getProto().getConst().getValue();
1350
  switch (type.which()) {
1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
    case schema::Type::VOID: *this = capnp::VOID; break;
    case schema::Type::BOOL: *this = value.getBool(); break;
    case schema::Type::INT8: *this = value.getInt8(); break;
    case schema::Type::INT16: *this = value.getInt16(); break;
    case schema::Type::INT32: *this = value.getInt32(); break;
    case schema::Type::INT64: *this = value.getInt64(); break;
    case schema::Type::UINT8: *this = value.getUint8(); break;
    case schema::Type::UINT16: *this = value.getUint16(); break;
    case schema::Type::UINT32: *this = value.getUint32(); break;
    case schema::Type::UINT64: *this = value.getUint64(); break;
    case schema::Type::FLOAT32: *this = value.getFloat32(); break;
    case schema::Type::FLOAT64: *this = value.getFloat64(); break;
    case schema::Type::TEXT: *this = value.getText(); break;
    case schema::Type::DATA: *this = value.getData(); break;

    case schema::Type::ENUM:
1367
      *this = DynamicEnum(type.asEnum(), value.getEnum());
1368 1369 1370
      break;

    case schema::Type::STRUCT:
1371
      *this = value.getStruct().getAs<DynamicStruct>(type.asStruct());
1372 1373 1374
      break;

    case schema::Type::LIST:
1375
      *this = value.getList().getAs<DynamicList>(type.asList());
1376 1377
      break;

1378 1379
    case schema::Type::ANY_POINTER:
      *this = value.getAnyPointer();
1380 1381 1382 1383 1384 1385 1386
      break;

    case schema::Type::INTERFACE:
      KJ_FAIL_ASSERT("Constants can't have interface type.");
  }
}

1387
DynamicValue::Reader::Reader(const Reader& other) {
Kenton Varda's avatar
Kenton Varda committed
1388
  switch (other.type) {
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
    case UNKNOWN:
    case VOID:
    case BOOL:
    case INT:
    case UINT:
    case FLOAT:
    case TEXT:
    case DATA:
    case LIST:
    case ENUM:
    case STRUCT:
1400
    case ANY_POINTER:
1401 1402 1403 1404 1405 1406
      static_assert(kj::canMemcpy<Text::Reader>() &&
                    kj::canMemcpy<Data::Reader>() &&
                    kj::canMemcpy<DynamicList::Reader>() &&
                    kj::canMemcpy<DynamicEnum>() &&
                    kj::canMemcpy<DynamicStruct::Reader>() &&
                    kj::canMemcpy<AnyPointer::Reader>(),
1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417
                    "Assumptions here don't hold.");
      break;

    case CAPABILITY:
      type = CAPABILITY;
      kj::ctor(capabilityValue, other.capabilityValue);
      return;
  }

  memcpy(this, &other, sizeof(*this));
}
Kenton Varda's avatar
Kenton Varda committed
1418 1419
DynamicValue::Reader::Reader(Reader&& other) noexcept {
  switch (other.type) {
1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
    case UNKNOWN:
    case VOID:
    case BOOL:
    case INT:
    case UINT:
    case FLOAT:
    case TEXT:
    case DATA:
    case LIST:
    case ENUM:
    case STRUCT:
1431
    case ANY_POINTER:
1432 1433 1434 1435 1436 1437
      static_assert(kj::canMemcpy<Text::Reader>() &&
                    kj::canMemcpy<Data::Reader>() &&
                    kj::canMemcpy<DynamicList::Reader>() &&
                    kj::canMemcpy<DynamicEnum>() &&
                    kj::canMemcpy<DynamicStruct::Reader>() &&
                    kj::canMemcpy<AnyPointer::Reader>(),
1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
                    "Assumptions here don't hold.");
      break;

    case CAPABILITY:
      type = CAPABILITY;
      kj::ctor(capabilityValue, kj::mv(other.capabilityValue));
      return;
  }

  memcpy(this, &other, sizeof(*this));
}
Kenton Varda's avatar
Kenton Varda committed
1449
DynamicValue::Reader::~Reader() noexcept(false) {
1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
}

DynamicValue::Reader& DynamicValue::Reader::operator=(const Reader& other) {
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
  kj::ctor(*this, other);
  return *this;
}
DynamicValue::Reader& DynamicValue::Reader::operator=(Reader&& other) {
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
  kj::ctor(*this, kj::mv(other));
  return *this;
}

DynamicValue::Builder::Builder(Builder& other) {
Kenton Varda's avatar
Kenton Varda committed
1471
  switch (other.type) {
1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482
    case UNKNOWN:
    case VOID:
    case BOOL:
    case INT:
    case UINT:
    case FLOAT:
    case TEXT:
    case DATA:
    case LIST:
    case ENUM:
    case STRUCT:
1483
    case ANY_POINTER:
1484
      // Unfortunately canMemcpy() doesn't work on these types due to the use of
1485 1486 1487 1488 1489 1490 1491
      // DisallowConstCopy, but __has_trivial_destructor should detect if any of these types
      // become non-trivial.
      static_assert(__has_trivial_destructor(Text::Builder) &&
                    __has_trivial_destructor(Data::Builder) &&
                    __has_trivial_destructor(DynamicList::Builder) &&
                    __has_trivial_destructor(DynamicEnum) &&
                    __has_trivial_destructor(DynamicStruct::Builder) &&
1492
                    __has_trivial_destructor(AnyPointer::Builder),
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
                    "Assumptions here don't hold.");
      break;

    case CAPABILITY:
      type = CAPABILITY;
      kj::ctor(capabilityValue, other.capabilityValue);
      return;
  }

  memcpy(this, &other, sizeof(*this));
}
Kenton Varda's avatar
Kenton Varda committed
1504 1505
DynamicValue::Builder::Builder(Builder&& other) noexcept {
  switch (other.type) {
1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516
    case UNKNOWN:
    case VOID:
    case BOOL:
    case INT:
    case UINT:
    case FLOAT:
    case TEXT:
    case DATA:
    case LIST:
    case ENUM:
    case STRUCT:
1517
    case ANY_POINTER:
1518 1519 1520 1521 1522 1523 1524 1525
      // Unfortunately __has_trivial_copy doesn't work on these types due to the use of
      // DisallowConstCopy, but __has_trivial_destructor should detect if any of these types
      // become non-trivial.
      static_assert(__has_trivial_destructor(Text::Builder) &&
                    __has_trivial_destructor(Data::Builder) &&
                    __has_trivial_destructor(DynamicList::Builder) &&
                    __has_trivial_destructor(DynamicEnum) &&
                    __has_trivial_destructor(DynamicStruct::Builder) &&
1526
                    __has_trivial_destructor(AnyPointer::Builder),
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537
                    "Assumptions here don't hold.");
      break;

    case CAPABILITY:
      type = CAPABILITY;
      kj::ctor(capabilityValue, kj::mv(other.capabilityValue));
      return;
  }

  memcpy(this, &other, sizeof(*this));
}
Kenton Varda's avatar
Kenton Varda committed
1538
DynamicValue::Builder::~Builder() noexcept(false) {
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
}

DynamicValue::Builder& DynamicValue::Builder::operator=(Builder& other) {
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
  kj::ctor(*this, other);
  return *this;
}
DynamicValue::Builder& DynamicValue::Builder::operator=(Builder&& other) {
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
  kj::ctor(*this, kj::mv(other));
  return *this;
}

1559
DynamicValue::Reader DynamicValue::Builder::asReader() const {
Kenton Varda's avatar
Kenton Varda committed
1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
  switch (type) {
    case UNKNOWN: return Reader();
    case VOID: return Reader(voidValue);
    case BOOL: return Reader(boolValue);
    case INT: return Reader(intValue);
    case UINT: return Reader(uintValue);
    case FLOAT: return Reader(floatValue);
    case TEXT: return Reader(textValue.asReader());
    case DATA: return Reader(dataValue.asReader());
    case LIST: return Reader(listValue.asReader());
    case ENUM: return Reader(enumValue);
    case STRUCT: return Reader(structValue.asReader());
1572
    case CAPABILITY: return Reader(capabilityValue);
1573
    case ANY_POINTER: return Reader(anyPointerValue.asReader());
Kenton Varda's avatar
Kenton Varda committed
1574
  }
1575
  KJ_FAIL_ASSERT("Missing switch case.");
Kenton Varda's avatar
Kenton Varda committed
1576 1577 1578
  return Reader();
}

1579
DynamicValue::Pipeline::Pipeline(Pipeline&& other) noexcept: type(other.type) {
1580 1581 1582 1583 1584
  switch (type) {
    case UNKNOWN: break;
    case STRUCT: kj::ctor(structValue, kj::mv(other.structValue)); break;
    case CAPABILITY: kj::ctor(capabilityValue, kj::mv(other.capabilityValue)); break;
    default:
1585 1586
      KJ_LOG(ERROR, "Unexpected pipeline type.", (uint)type);
      type = UNKNOWN;
1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
      break;
  }
}
DynamicValue::Pipeline& DynamicValue::Pipeline::operator=(Pipeline&& other) {
  kj::dtor(*this);
  kj::ctor(*this, kj::mv(other));
  return *this;
}
DynamicValue::Pipeline::~Pipeline() noexcept(false) {
  switch (type) {
    case UNKNOWN: break;
    case STRUCT: kj::dtor(structValue); break;
    case CAPABILITY: kj::dtor(capabilityValue); break;
    default:
      KJ_FAIL_ASSERT("Unexpected pipeline type.", (uint)type) { type = UNKNOWN; break; }
      break;
  }
}

1606 1607 1608 1609
namespace {

template <typename T>
T signedToUnsigned(long long value) {
1610
  KJ_REQUIRE(value >= 0 && T(value) == value, "Value out-of-range for requested type.", value) {
1611
    // Use it anyway.
1612
    break;
1613 1614 1615 1616 1617 1618
  }
  return value;
}

template <>
uint64_t signedToUnsigned<uint64_t>(long long value) {
1619
  KJ_REQUIRE(value >= 0, "Value out-of-range for requested type.", value) {
1620
    // Use it anyway.
1621
    break;
1622 1623 1624 1625 1626 1627
  }
  return value;
}

template <typename T>
T unsignedToSigned(unsigned long long value) {
1628 1629
  KJ_REQUIRE(T(value) >= 0 && (unsigned long long)T(value) == value,
             "Value out-of-range for requested type.", value) {
1630
    // Use it anyway.
1631
    break;
1632 1633 1634 1635 1636 1637
  }
  return value;
}

template <>
int64_t unsignedToSigned<int64_t>(unsigned long long value) {
1638
  KJ_REQUIRE(int64_t(value) >= 0, "Value out-of-range for requested type.", value) {
1639
    // Use it anyway.
1640
    break;
1641 1642 1643 1644 1645 1646
  }
  return value;
}

template <typename T, typename U>
T checkRoundTrip(U value) {
1647
  KJ_REQUIRE(T(value) == value, "Value out-of-range for requested type.", value) {
1648
    // Use it anyway.
1649
    break;
1650 1651 1652 1653 1654 1655 1656
  }
  return value;
}

}  // namespace

#define HANDLE_NUMERIC_TYPE(typeName, ifInt, ifUint, ifFloat) \
1657
typeName DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \
1658 1659 1660 1661 1662 1663 1664 1665
  switch (reader.type) { \
    case INT: \
      return ifInt<typeName>(reader.intValue); \
    case UINT: \
      return ifUint<typeName>(reader.uintValue); \
    case FLOAT: \
      return ifFloat<typeName>(reader.floatValue); \
    default: \
Kenton Varda's avatar
Kenton Varda committed
1666
      KJ_FAIL_REQUIRE("Value type mismatch.") { \
1667
        return 0; \
1668
      } \
1669 1670
  } \
} \
1671
typeName DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \
1672 1673 1674 1675 1676 1677 1678 1679
  switch (builder.type) { \
    case INT: \
      return ifInt<typeName>(builder.intValue); \
    case UINT: \
      return ifUint<typeName>(builder.uintValue); \
    case FLOAT: \
      return ifFloat<typeName>(builder.floatValue); \
    default: \
Kenton Varda's avatar
Kenton Varda committed
1680
      KJ_FAIL_REQUIRE("Value type mismatch.") { \
1681
        return 0; \
1682
      } \
1683 1684 1685 1686 1687 1688
  } \
}

HANDLE_NUMERIC_TYPE(int8_t, checkRoundTrip, unsignedToSigned, checkRoundTrip)
HANDLE_NUMERIC_TYPE(int16_t, checkRoundTrip, unsignedToSigned, checkRoundTrip)
HANDLE_NUMERIC_TYPE(int32_t, checkRoundTrip, unsignedToSigned, checkRoundTrip)
1689
HANDLE_NUMERIC_TYPE(int64_t, kj::implicitCast, unsignedToSigned, checkRoundTrip)
1690 1691 1692
HANDLE_NUMERIC_TYPE(uint8_t, signedToUnsigned, checkRoundTrip, checkRoundTrip)
HANDLE_NUMERIC_TYPE(uint16_t, signedToUnsigned, checkRoundTrip, checkRoundTrip)
HANDLE_NUMERIC_TYPE(uint32_t, signedToUnsigned, checkRoundTrip, checkRoundTrip)
1693 1694 1695
HANDLE_NUMERIC_TYPE(uint64_t, signedToUnsigned, kj::implicitCast, checkRoundTrip)
HANDLE_NUMERIC_TYPE(float, kj::implicitCast, kj::implicitCast, kj::implicitCast)
HANDLE_NUMERIC_TYPE(double, kj::implicitCast, kj::implicitCast, kj::implicitCast)
1696 1697 1698

#undef HANDLE_NUMERIC_TYPE

Kenton Varda's avatar
Kenton Varda committed
1699
#define HANDLE_TYPE(name, discrim, typeName) \
1700
ReaderFor<typeName> DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \
Kenton Varda's avatar
Kenton Varda committed
1701
  KJ_REQUIRE(reader.type == discrim, "Value type mismatch.") { \
1702 1703
    return ReaderFor<typeName>(); \
  } \
Kenton Varda's avatar
Kenton Varda committed
1704 1705
  return reader.name##Value; \
} \
1706
BuilderFor<typeName> DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \
1707
  KJ_REQUIRE(builder.type == discrim, "Value type mismatch."); \
Kenton Varda's avatar
Kenton Varda committed
1708 1709 1710 1711 1712 1713 1714 1715 1716 1717
  return builder.name##Value; \
}

//HANDLE_TYPE(void, VOID, Void)
HANDLE_TYPE(bool, BOOL, bool)

HANDLE_TYPE(text, TEXT, Text)
HANDLE_TYPE(list, LIST, DynamicList)
HANDLE_TYPE(struct, STRUCT, DynamicStruct)
HANDLE_TYPE(enum, ENUM, DynamicEnum)
1718
HANDLE_TYPE(anyPointer, ANY_POINTER, AnyPointer)
Kenton Varda's avatar
Kenton Varda committed
1719 1720 1721

#undef HANDLE_TYPE

1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749
PipelineFor<DynamicStruct> DynamicValue::Pipeline::AsImpl<DynamicStruct>::apply(
    Pipeline& pipeline) {
  KJ_REQUIRE(pipeline.type == STRUCT, "Pipeline type mismatch.");
  return kj::mv(pipeline.structValue);
}

ReaderFor<DynamicCapability> DynamicValue::Reader::AsImpl<DynamicCapability>::apply(
    const Reader& reader) {
  KJ_REQUIRE(reader.type == CAPABILITY, "Value type mismatch.") {
    return DynamicCapability::Client();
  }
  return reader.capabilityValue;
}
BuilderFor<DynamicCapability> DynamicValue::Builder::AsImpl<DynamicCapability>::apply(
    Builder& builder) {
  KJ_REQUIRE(builder.type == CAPABILITY, "Value type mismatch.") {
    return DynamicCapability::Client();
  }
  return builder.capabilityValue;
}
PipelineFor<DynamicCapability> DynamicValue::Pipeline::AsImpl<DynamicCapability>::apply(
    Pipeline& pipeline) {
  KJ_REQUIRE(pipeline.type == CAPABILITY, "Pipeline type mismatch.") {
    return DynamicCapability::Client();
  }
  return kj::mv(pipeline.capabilityValue);
}

1750 1751 1752
Data::Reader DynamicValue::Reader::AsImpl<Data>::apply(const Reader& reader) {
  if (reader.type == TEXT) {
    // Coerce text to data.
1753
    return reader.textValue.asBytes();
1754 1755 1756 1757 1758 1759 1760 1761 1762
  }
  KJ_REQUIRE(reader.type == DATA, "Value type mismatch.") {
    return Data::Reader();
  }
  return reader.dataValue;
}
Data::Builder DynamicValue::Builder::AsImpl<Data>::apply(Builder& builder) {
  if (builder.type == TEXT) {
    // Coerce text to data.
1763
    return builder.textValue.asBytes();
1764 1765 1766 1767 1768 1769 1770
  }
  KJ_REQUIRE(builder.type == DATA, "Value type mismatch.") {
    return BuilderFor<Data>();
  }
  return builder.dataValue;
}

Kenton Varda's avatar
Kenton Varda committed
1771
// As in the header, HANDLE_TYPE(void, VOID, Void) crashes GCC 4.7.
1772
Void DynamicValue::Reader::AsImpl<Void>::apply(const Reader& reader) {
Kenton Varda's avatar
Kenton Varda committed
1773
  KJ_REQUIRE(reader.type == VOID, "Value type mismatch.") {
Kenton Varda's avatar
Kenton Varda committed
1774 1775 1776 1777
    return Void();
  }
  return reader.voidValue;
}
1778
Void DynamicValue::Builder::AsImpl<Void>::apply(Builder& builder) {
Kenton Varda's avatar
Kenton Varda committed
1779
  KJ_REQUIRE(builder.type == VOID, "Value type mismatch.") {
Kenton Varda's avatar
Kenton Varda committed
1780 1781 1782 1783 1784
    return Void();
  }
  return builder.voidValue;
}

Kenton Varda's avatar
Kenton Varda committed
1785 1786
// =======================================================================================

1787
namespace _ {  // private
Kenton Varda's avatar
Kenton Varda committed
1788

1789
DynamicStruct::Reader PointerHelpers<DynamicStruct, Kind::OTHER>::getDynamic(
1790
    PointerReader reader, StructSchema schema) {
1791 1792
  KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(),
             "Cannot form pointer to group type.");
1793
  return DynamicStruct::Reader(schema, reader.getStruct(nullptr));
Kenton Varda's avatar
Kenton Varda committed
1794
}
1795
DynamicStruct::Builder PointerHelpers<DynamicStruct, Kind::OTHER>::getDynamic(
1796
    PointerBuilder builder, StructSchema schema) {
1797 1798
  KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(),
             "Cannot form pointer to group type.");
1799
  return DynamicStruct::Builder(schema, builder.getStruct(
1800
      structSizeFromSchema(schema), nullptr));
Kenton Varda's avatar
Kenton Varda committed
1801
}
1802
void PointerHelpers<DynamicStruct, Kind::OTHER>::set(
1803
    PointerBuilder builder, const DynamicStruct::Reader& value) {
1804 1805
  KJ_REQUIRE(!value.schema.getProto().getStruct().getIsGroup(),
             "Cannot form pointer to group type.");
1806
  builder.setStruct(value.reader);
Kenton Varda's avatar
Kenton Varda committed
1807
}
1808
DynamicStruct::Builder PointerHelpers<DynamicStruct, Kind::OTHER>::init(
1809
    PointerBuilder builder, StructSchema schema) {
1810 1811
  KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(),
             "Cannot form pointer to group type.");
1812
  return DynamicStruct::Builder(schema,
1813
      builder.initStruct(structSizeFromSchema(schema)));
Kenton Varda's avatar
Kenton Varda committed
1814 1815
}

1816
DynamicList::Reader PointerHelpers<DynamicList, Kind::OTHER>::getDynamic(
1817
    PointerReader reader, ListSchema schema) {
1818
  return DynamicList::Reader(schema,
1819
      reader.getList(elementSizeFor(schema.whichElementType()), nullptr));
Kenton Varda's avatar
Kenton Varda committed
1820
}
1821
DynamicList::Builder PointerHelpers<DynamicList, Kind::OTHER>::getDynamic(
1822
    PointerBuilder builder, ListSchema schema) {
Kenton Varda's avatar
Kenton Varda committed
1823
  if (schema.whichElementType() == schema::Type::STRUCT) {
1824
    return DynamicList::Builder(schema,
1825
        builder.getStructList(
1826 1827 1828 1829
            structSizeFromSchema(schema.getStructElementType()),
            nullptr));
  } else {
    return DynamicList::Builder(schema,
1830
        builder.getList(elementSizeFor(schema.whichElementType()), nullptr));
1831
  }
Kenton Varda's avatar
Kenton Varda committed
1832
}
1833
void PointerHelpers<DynamicList, Kind::OTHER>::set(
1834 1835
    PointerBuilder builder, const DynamicList::Reader& value) {
  builder.setList(value.reader);
Kenton Varda's avatar
Kenton Varda committed
1836
}
1837
DynamicList::Builder PointerHelpers<DynamicList, Kind::OTHER>::init(
1838
    PointerBuilder builder, ListSchema schema, uint size) {
Kenton Varda's avatar
Kenton Varda committed
1839
  if (schema.whichElementType() == schema::Type::STRUCT) {
1840
    return DynamicList::Builder(schema,
1841
        builder.initStructList(size * ELEMENTS,
1842
            structSizeFromSchema(schema.getStructElementType())));
Kenton Varda's avatar
Kenton Varda committed
1843
  } else {
1844
    return DynamicList::Builder(schema,
1845
        builder.initList(elementSizeFor(schema.whichElementType()), size * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1846 1847 1848
  }
}

1849
DynamicCapability::Client PointerHelpers<DynamicCapability, Kind::OTHER>::getDynamic(
1850 1851 1852
    PointerReader reader, InterfaceSchema schema) {
  return DynamicCapability::Client(schema, reader.getCapability());
}
1853
DynamicCapability::Client PointerHelpers<DynamicCapability, Kind::OTHER>::getDynamic(
1854 1855 1856
    PointerBuilder builder, InterfaceSchema schema) {
  return DynamicCapability::Client(schema, builder.getCapability());
}
1857
void PointerHelpers<DynamicCapability, Kind::OTHER>::set(
1858
    PointerBuilder builder, DynamicCapability::Client& value) {
1859 1860
  builder.setCapability(value.hook->addRef());
}
1861
void PointerHelpers<DynamicCapability, Kind::OTHER>::set(
1862 1863 1864 1865
    PointerBuilder builder, DynamicCapability::Client&& value) {
  builder.setCapability(kj::mv(value.hook));
}

1866
}  // namespace _ (private)
Kenton Varda's avatar
Kenton Varda committed
1867

1868
template <>
1869
void AnyPointer::Builder::adopt<DynamicValue>(Orphan<DynamicValue>&& orphan) {
1870 1871 1872 1873 1874 1875 1876 1877
  switch (orphan.getType()) {
    case DynamicValue::UNKNOWN:
    case DynamicValue::VOID:
    case DynamicValue::BOOL:
    case DynamicValue::INT:
    case DynamicValue::UINT:
    case DynamicValue::FLOAT:
    case DynamicValue::ENUM:
1878
      KJ_FAIL_REQUIRE("AnyPointer cannot adopt primitive (non-object) value.");
1879 1880 1881 1882 1883

    case DynamicValue::STRUCT:
    case DynamicValue::LIST:
    case DynamicValue::TEXT:
    case DynamicValue::DATA:
1884
    case DynamicValue::CAPABILITY:
1885
    case DynamicValue::ANY_POINTER:
1886 1887 1888 1889 1890
      builder.adopt(kj::mv(orphan.builder));
      break;
  }
}

1891
template <>
1892
DynamicStruct::Builder Orphan<AnyPointer>::getAs<DynamicStruct>(StructSchema schema) {
1893 1894 1895
  return DynamicStruct::Builder(schema, builder.asStruct(structSizeFromSchema(schema)));
}
template <>
1896
DynamicStruct::Reader Orphan<AnyPointer>::getAsReader<DynamicStruct>(StructSchema schema) const {
1897 1898 1899
  return DynamicStruct::Reader(schema, builder.asStructReader(structSizeFromSchema(schema)));
}
template <>
1900
Orphan<DynamicStruct> Orphan<AnyPointer>::releaseAs<DynamicStruct>(StructSchema schema) {
1901 1902 1903 1904
  return Orphan<DynamicStruct>(schema, kj::mv(builder));
}

template <>
1905
DynamicList::Builder Orphan<AnyPointer>::getAs<DynamicList>(ListSchema schema) {
1906 1907 1908 1909 1910 1911 1912 1913
  if (schema.whichElementType() == schema::Type::STRUCT) {
    return DynamicList::Builder(schema, builder.asStructList(
        structSizeFromSchema(schema.getStructElementType())));
  } else {
    return DynamicList::Builder(schema, builder.asList(elementSizeFor(schema.whichElementType())));
  }
}
template <>
1914
DynamicList::Reader Orphan<AnyPointer>::getAsReader<DynamicList>(ListSchema schema) const {
1915 1916 1917 1918
  return DynamicList::Reader(schema, builder.asListReader(
      elementSizeFor(schema.whichElementType())));
}
template <>
1919
Orphan<DynamicList> Orphan<AnyPointer>::releaseAs<DynamicList>(ListSchema schema) {
1920 1921 1922
  return Orphan<DynamicList>(schema, kj::mv(builder));
}

1923
template <>
1924
DynamicCapability::Client Orphan<AnyPointer>::getAs<DynamicCapability>(InterfaceSchema schema) {
1925 1926 1927
  return DynamicCapability::Client(schema, builder.asCapability());
}
template <>
1928
DynamicCapability::Client Orphan<AnyPointer>::getAsReader<DynamicCapability>(
1929 1930 1931 1932
    InterfaceSchema schema) const {
  return DynamicCapability::Client(schema, builder.asCapability());
}
template <>
1933
Orphan<DynamicCapability> Orphan<AnyPointer>::releaseAs<DynamicCapability>(
1934 1935 1936 1937
    InterfaceSchema schema) {
  return Orphan<DynamicCapability>(schema, kj::mv(builder));
}

1938 1939
// -------------------------------------------------------------------

1940
Orphan<DynamicStruct> Orphanage::newOrphan(StructSchema schema) const {
1941
  return Orphan<DynamicStruct>(
1942
      schema, _::OrphanBuilder::initStruct(arena, capTable, structSizeFromSchema(schema)));
1943 1944
}

1945
Orphan<DynamicList> Orphanage::newOrphan(ListSchema schema, uint size) const {
Kenton Varda's avatar
Kenton Varda committed
1946
  if (schema.whichElementType() == schema::Type::STRUCT) {
1947
    return Orphan<DynamicList>(schema, _::OrphanBuilder::initStructList(
1948
        arena, capTable, size * ELEMENTS, structSizeFromSchema(schema.getStructElementType())));
1949 1950
  } else {
    return Orphan<DynamicList>(schema, _::OrphanBuilder::initList(
1951
        arena, capTable, size * ELEMENTS, elementSizeFor(schema.whichElementType())));
1952 1953 1954 1955 1956 1957 1958
  }
}

DynamicStruct::Builder Orphan<DynamicStruct>::get() {
  return DynamicStruct::Builder(schema, builder.asStruct(structSizeFromSchema(schema)));
}

1959 1960 1961 1962
DynamicStruct::Reader Orphan<DynamicStruct>::getReader() const {
  return DynamicStruct::Reader(schema, builder.asStructReader(structSizeFromSchema(schema)));
}

1963
DynamicList::Builder Orphan<DynamicList>::get() {
Kenton Varda's avatar
Kenton Varda committed
1964
  if (schema.whichElementType() == schema::Type::STRUCT) {
1965 1966 1967 1968 1969 1970 1971 1972
    return DynamicList::Builder(
        schema, builder.asStructList(structSizeFromSchema(schema.getStructElementType())));
  } else {
    return DynamicList::Builder(
        schema, builder.asList(elementSizeFor(schema.whichElementType())));
  }
}

1973 1974 1975 1976 1977
DynamicList::Reader Orphan<DynamicList>::getReader() const {
  return DynamicList::Reader(
      schema, builder.asListReader(elementSizeFor(schema.whichElementType())));
}

1978 1979 1980 1981 1982 1983 1984 1985
DynamicCapability::Client Orphan<DynamicCapability>::get() {
  return DynamicCapability::Client(schema, builder.asCapability());
}

DynamicCapability::Client Orphan<DynamicCapability>::getReader() const {
  return DynamicCapability::Client(schema, builder.asCapability());
}

1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000
Orphan<DynamicValue>::Orphan(DynamicValue::Builder value, _::OrphanBuilder&& builder)
    : type(value.getType()), builder(kj::mv(builder)) {
  switch (type) {
    case DynamicValue::UNKNOWN: break;
    case DynamicValue::VOID: voidValue = value.voidValue; break;
    case DynamicValue::BOOL: boolValue = value.boolValue; break;
    case DynamicValue::INT: intValue = value.intValue; break;
    case DynamicValue::UINT: uintValue = value.uintValue; break;
    case DynamicValue::FLOAT: floatValue = value.floatValue; break;
    case DynamicValue::ENUM: enumValue = value.enumValue; break;

    case DynamicValue::TEXT: break;
    case DynamicValue::DATA: break;
    case DynamicValue::LIST: listSchema = value.listValue.getSchema(); break;
    case DynamicValue::STRUCT: structSchema = value.structValue.getSchema(); break;
2001
    case DynamicValue::CAPABILITY: interfaceSchema = value.capabilityValue.getSchema(); break;
2002
    case DynamicValue::ANY_POINTER: break;
2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028
  }
}

DynamicValue::Builder Orphan<DynamicValue>::get() {
  switch (type) {
    case DynamicValue::UNKNOWN: return nullptr;
    case DynamicValue::VOID: return voidValue;
    case DynamicValue::BOOL: return boolValue;
    case DynamicValue::INT: return intValue;
    case DynamicValue::UINT: return uintValue;
    case DynamicValue::FLOAT: return floatValue;
    case DynamicValue::ENUM: return enumValue;

    case DynamicValue::TEXT: return builder.asText();
    case DynamicValue::DATA: return builder.asData();
    case DynamicValue::LIST:
      if (listSchema.whichElementType() == schema::Type::STRUCT) {
        return DynamicList::Builder(listSchema,
            builder.asStructList(structSizeFromSchema(listSchema.getStructElementType())));
      } else {
        return DynamicList::Builder(listSchema,
            builder.asList(elementSizeFor(listSchema.whichElementType())));
      }
    case DynamicValue::STRUCT:
      return DynamicStruct::Builder(structSchema,
          builder.asStruct(structSizeFromSchema(structSchema)));
2029 2030
    case DynamicValue::CAPABILITY:
      return DynamicCapability::Client(interfaceSchema, builder.asCapability());
2031 2032 2033
    case DynamicValue::ANY_POINTER:
      KJ_FAIL_REQUIRE("Can't get() an AnyPointer orphan; there is no underlying pointer to "
                      "wrap in an AnyPointer::Builder.");
2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054
  }
  KJ_UNREACHABLE;
}
DynamicValue::Reader Orphan<DynamicValue>::getReader() const {
  switch (type) {
    case DynamicValue::UNKNOWN: return nullptr;
    case DynamicValue::VOID: return voidValue;
    case DynamicValue::BOOL: return boolValue;
    case DynamicValue::INT: return intValue;
    case DynamicValue::UINT: return uintValue;
    case DynamicValue::FLOAT: return floatValue;
    case DynamicValue::ENUM: return enumValue;

    case DynamicValue::TEXT: return builder.asTextReader();
    case DynamicValue::DATA: return builder.asDataReader();
    case DynamicValue::LIST:
      return DynamicList::Reader(listSchema,
          builder.asListReader(elementSizeFor(listSchema.whichElementType())));
    case DynamicValue::STRUCT:
      return DynamicStruct::Reader(structSchema,
          builder.asStructReader(structSizeFromSchema(structSchema)));
2055 2056
    case DynamicValue::CAPABILITY:
      return DynamicCapability::Client(interfaceSchema, builder.asCapability());
2057 2058 2059
    case DynamicValue::ANY_POINTER:
      KJ_FAIL_ASSERT("Can't get() an AnyPointer orphan; there is no underlying pointer to "
                     "wrap in an AnyPointer::Builder.");
2060 2061 2062 2063
  }
  KJ_UNREACHABLE;
}

2064
template <>
2065 2066
Orphan<AnyPointer> Orphan<DynamicValue>::releaseAs<AnyPointer>() {
  KJ_REQUIRE(type == DynamicValue::ANY_POINTER, "Value type mismatch.");
2067
  type = DynamicValue::UNKNOWN;
2068
  return Orphan<AnyPointer>(kj::mv(builder));
2069
}
2070 2071 2072
template <>
Orphan<DynamicStruct> Orphan<DynamicValue>::releaseAs<DynamicStruct>() {
  KJ_REQUIRE(type == DynamicValue::STRUCT, "Value type mismatch.");
2073
  type = DynamicValue::UNKNOWN;
2074 2075 2076 2077 2078
  return Orphan<DynamicStruct>(structSchema, kj::mv(builder));
}
template <>
Orphan<DynamicList> Orphan<DynamicValue>::releaseAs<DynamicList>() {
  KJ_REQUIRE(type == DynamicValue::LIST, "Value type mismatch.");
2079
  type = DynamicValue::UNKNOWN;
2080 2081 2082
  return Orphan<DynamicList>(listSchema, kj::mv(builder));
}

2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098
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);
2099
    case DynamicValue::CAPABILITY: return newOrphanCopy(copyFrom.capabilityValue);
2100
    case DynamicValue::ANY_POINTER: return newOrphanCopy(copyFrom.anyPointerValue);
2101 2102 2103 2104
  }
  KJ_UNREACHABLE;
}

2105
}  // namespace capnp