Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
1dcb66b1
Commit
1dcb66b1
authored
Aug 20, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Continuing schema rewrite WIP.
parent
863afbe2
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
569 additions
and
463 deletions
+569
-463
compiler.c++
c++/src/capnp/compiler/compiler.c++
+21
-0
node-translator.c++
c++/src/capnp/compiler/node-translator.c++
+7
-2
parser.c++
c++/src/capnp/compiler/parser.c++
+28
-0
parser.h
c++/src/capnp/compiler/parser.h
+4
-1
dynamic.c++
c++/src/capnp/dynamic.c++
+15
-1
generated-header-support.h
c++/src/capnp/generated-header-support.h
+1
-0
message.h
c++/src/capnp/message.h
+2
-2
schema-loader.c++
c++/src/capnp/schema-loader.c++
+481
-457
schema-loader.h
c++/src/capnp/schema-loader.h
+10
-0
No files found.
c++/src/capnp/compiler/compiler.c++
View file @
1dcb66b1
...
...
@@ -903,6 +903,26 @@ Compiler::Impl::Impl(AnnotationFlag annotationFlag)
:
annotationFlag
(
annotationFlag
),
finalLoader
(
*
this
),
workspace
(
*
this
)
{
// Reflectively interpret the members of Declaration.body. Any member prefixed by "builtin"
// defines a builtin declaration visible in the global scope.
#warning "temporary hack for schema transition"
builtinDecls
[
"Void"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Void"
,
Declaration
::
Body
::
BUILTIN_VOID
);
builtinDecls
[
"Bool"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Bool"
,
Declaration
::
Body
::
BUILTIN_BOOL
);
builtinDecls
[
"Int8"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Int8"
,
Declaration
::
Body
::
BUILTIN_INT8
);
builtinDecls
[
"Int16"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Int16"
,
Declaration
::
Body
::
BUILTIN_INT16
);
builtinDecls
[
"Int32"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Int32"
,
Declaration
::
Body
::
BUILTIN_INT32
);
builtinDecls
[
"Int64"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Int64"
,
Declaration
::
Body
::
BUILTIN_INT64
);
builtinDecls
[
"UInt8"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"UInt8"
,
Declaration
::
Body
::
BUILTIN_U_INT8
);
builtinDecls
[
"UInt16"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"UInt16"
,
Declaration
::
Body
::
BUILTIN_U_INT16
);
builtinDecls
[
"UInt32"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"UInt32"
,
Declaration
::
Body
::
BUILTIN_U_INT32
);
builtinDecls
[
"UInt64"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"UInt64"
,
Declaration
::
Body
::
BUILTIN_U_INT64
);
builtinDecls
[
"Float32"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Float32"
,
Declaration
::
Body
::
BUILTIN_FLOAT32
);
builtinDecls
[
"Float64"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Float64"
,
Declaration
::
Body
::
BUILTIN_FLOAT64
);
builtinDecls
[
"Text"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Text"
,
Declaration
::
Body
::
BUILTIN_TEXT
);
builtinDecls
[
"Data"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Data"
,
Declaration
::
Body
::
BUILTIN_DATA
);
builtinDecls
[
"List"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"List"
,
Declaration
::
Body
::
BUILTIN_LIST
);
builtinDecls
[
"Object"
]
=
nodeArena
.
allocateOwn
<
Node
>
(
"Object"
,
Declaration
::
Body
::
BUILTIN_OBJECT
);
#if 0
StructSchema::Union declBodySchema =
Schema::from<Declaration>().getMemberByName("body").asUnion();
for (auto member: declBodySchema.getMembers()) {
...
...
@@ -913,6 +933,7 @@ Compiler::Impl::Impl(AnnotationFlag annotationFlag)
symbolName, static_cast<Declaration::Body::Which>(member.getIndex()));
}
}
#endif
}
Compiler
::
Impl
::~
Impl
()
noexcept
(
false
)
{}
...
...
c++/src/capnp/compiler/node-translator.c++
View file @
1dcb66b1
...
...
@@ -22,7 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "node-translator.h"
#include "parser.h" // only for generate
Child
Id()
#include "parser.h" // only for generate
Group
Id()
#include <kj/debug.h>
#include <kj/arena.h>
#include <set>
...
...
@@ -906,6 +906,7 @@ public:
case
Declaration
:
:
Body
::
GROUP_DECL
:
member
->
setDiscriminantOffsetInSchema
();
// in case it contains an unnamed union
member
->
node
.
setId
(
generateGroupId
(
member
->
parent
->
node
.
getId
(),
member
->
index
));
targetsFlagName
=
"targetsGroup"
;
break
;
...
...
@@ -964,6 +965,9 @@ private:
uint
codeOrder
;
// Code order within the parent.
uint
index
=
0
;
// Index within the parent.
uint
childCount
=
0
;
// Number of children this member has.
...
...
@@ -1017,6 +1021,7 @@ private:
KJ_IF_MAYBE
(
result
,
schema
)
{
return
*
result
;
}
else
{
index
=
parent
->
childInitializedCount
;
auto
builder
=
parent
->
addMemberSchema
();
if
(
isInUnion
)
{
builder
.
setDiscriminantValue
(
parent
->
unionDiscriminantCount
++
);
...
...
@@ -1189,7 +1194,7 @@ private:
.
newOrphan
<
schema2
::
Node
>
();
auto
node
=
orphan
.
get
();
node
.
setId
(
generateChildId
(
parent
.
getId
(),
name
));
// We'll set the ID later.
node
.
setDisplayName
(
kj
::
str
(
parent
.
getDisplayName
(),
'.'
,
name
));
node
.
setDisplayNamePrefixLength
(
node
.
getDisplayName
().
size
()
-
name
.
size
());
node
.
setScopeId
(
parent
.
getId
());
...
...
c++/src/capnp/compiler/parser.c++
View file @
1dcb66b1
...
...
@@ -70,6 +70,31 @@ uint64_t generateChildId(uint64_t parentId, kj::StringPtr childName) {
return
result
|
(
1ull
<<
63
);
}
uint64_t
generateGroupId
(
uint64_t
parentId
,
uint16_t
groupIndex
)
{
// Compute ID by MD5 hashing the concatenation of the parent ID and the group index, and
// then taking the first 8 bytes.
kj
::
byte
bytes
[
sizeof
(
uint64_t
)
+
sizeof
(
uint16_t
)];
for
(
uint
i
=
0
;
i
<
sizeof
(
uint64_t
);
i
++
)
{
bytes
[
i
]
=
(
parentId
>>
(
i
*
8
))
&
0xff
;
}
for
(
uint
i
=
0
;
i
<
sizeof
(
uint16_t
);
i
++
)
{
bytes
[
sizeof
(
uint64_t
)
+
i
]
=
(
groupIndex
>>
(
i
*
8
))
&
0xff
;
}
Md5
md5
;
md5
.
update
(
bytes
);
kj
::
ArrayPtr
<
const
kj
::
byte
>
resultBytes
=
md5
.
finish
();
uint64_t
result
=
0
;
for
(
uint
i
=
0
;
i
<
sizeof
(
uint64_t
);
i
++
)
{
result
=
(
result
<<
8
)
|
resultBytes
[
i
];
}
return
result
|
(
1ull
<<
63
);
}
void
parseFile
(
List
<
Statement
>::
Reader
statements
,
ParsedFile
::
Builder
result
,
const
ErrorReporter
&
errorReporter
)
{
CapnpParser
parser
(
Orphanage
::
getForMessageContaining
(
result
),
errorReporter
);
...
...
@@ -858,6 +883,8 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, const ErrorReporter& errorRep
DynamicStruct
::
Builder
dynamicBuilder
=
builder
;
for
(
auto
&
maybeTarget
:
targets
.
value
)
{
KJ_IF_MAYBE
(
target
,
maybeTarget
)
{
#warning "temporary hack for schema transition"
#if 0
if (target->value == "*") {
// Set all.
if (targets.value.size() > 1) {
...
...
@@ -892,6 +919,7 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, const ErrorReporter& errorRep
}
}
}
#endif
}
}
return
DeclParserResult
(
kj
::
mv
(
decl
));
...
...
c++/src/capnp/compiler/parser.h
View file @
1dcb66b1
...
...
@@ -45,8 +45,11 @@ uint64_t generateRandomId();
uint64_t
generateChildId
(
uint64_t
parentId
,
kj
::
StringPtr
childName
);
// Generate the ID for a child node given its parent ID and name.
uint64_t
generateGroupId
(
uint64_t
parentId
,
uint16_t
groupIndex
);
// Generate the ID for a group within a struct.
//
// TODO(cleanup): Move generate
RandomId() and generateChild
Id() somewhere more sensible.
// TODO(cleanup): Move generate
*
Id() somewhere more sensible.
class
CapnpParser
{
// Advanced parser interface. This interface exposes the inner parsers so that you can embed
...
...
c++/src/capnp/dynamic.c++
View file @
1dcb66b1
...
...
@@ -858,7 +858,7 @@ DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name, uint size
return
init
(
schema
.
getFieldByName
(
name
),
size
);
}
void
DynamicStruct
::
Builder
::
clear
(
kj
::
StringPtr
name
)
{
return
clear
(
schema
.
getFieldByName
(
name
));
clear
(
schema
.
getFieldByName
(
name
));
}
DynamicStruct
::
Builder
DynamicStruct
::
Builder
::
getObject
(
kj
::
StringPtr
name
,
StructSchema
type
)
{
...
...
@@ -1332,16 +1332,22 @@ Void DynamicValue::Builder::AsImpl<Void>::apply(Builder& builder) {
template
<>
DynamicStruct
::
Reader
MessageReader
::
getRoot
<
DynamicStruct
>
(
StructSchema
schema
)
{
KJ_REQUIRE
(
!
schema
.
getProto
().
getStruct
().
getIsGroup
(),
"Can't use group type as the root of a message."
);
return
DynamicStruct
::
Reader
(
schema
,
getRootInternal
());
}
template
<>
DynamicStruct
::
Builder
MessageBuilder
::
initRoot
<
DynamicStruct
>
(
StructSchema
schema
)
{
KJ_REQUIRE
(
!
schema
.
getProto
().
getStruct
().
getIsGroup
(),
"Can't use group type as the root of a message."
);
return
DynamicStruct
::
Builder
(
schema
,
initRoot
(
structSizeFromSchema
(
schema
)));
}
template
<>
DynamicStruct
::
Builder
MessageBuilder
::
getRoot
<
DynamicStruct
>
(
StructSchema
schema
)
{
KJ_REQUIRE
(
!
schema
.
getProto
().
getStruct
().
getIsGroup
(),
"Can't use group type as the root of a message."
);
return
DynamicStruct
::
Builder
(
schema
,
getRoot
(
structSizeFromSchema
(
schema
)));
}
...
...
@@ -1349,19 +1355,27 @@ namespace _ { // private
DynamicStruct
::
Reader
PointerHelpers
<
DynamicStruct
,
Kind
::
UNKNOWN
>::
getDynamic
(
StructReader
reader
,
WirePointerCount
index
,
StructSchema
schema
)
{
KJ_REQUIRE
(
!
schema
.
getProto
().
getStruct
().
getIsGroup
(),
"Cannot form pointer to group type."
);
return
DynamicStruct
::
Reader
(
schema
,
reader
.
getStructField
(
index
,
nullptr
));
}
DynamicStruct
::
Builder
PointerHelpers
<
DynamicStruct
,
Kind
::
UNKNOWN
>::
getDynamic
(
StructBuilder
builder
,
WirePointerCount
index
,
StructSchema
schema
)
{
KJ_REQUIRE
(
!
schema
.
getProto
().
getStruct
().
getIsGroup
(),
"Cannot form pointer to group type."
);
return
DynamicStruct
::
Builder
(
schema
,
builder
.
getStructField
(
index
,
structSizeFromSchema
(
schema
),
nullptr
));
}
void
PointerHelpers
<
DynamicStruct
,
Kind
::
UNKNOWN
>::
set
(
StructBuilder
builder
,
WirePointerCount
index
,
const
DynamicStruct
::
Reader
&
value
)
{
KJ_REQUIRE
(
!
value
.
schema
.
getProto
().
getStruct
().
getIsGroup
(),
"Cannot form pointer to group type."
);
builder
.
setStructField
(
index
,
value
.
reader
);
}
DynamicStruct
::
Builder
PointerHelpers
<
DynamicStruct
,
Kind
::
UNKNOWN
>::
init
(
StructBuilder
builder
,
WirePointerCount
index
,
StructSchema
schema
)
{
KJ_REQUIRE
(
!
schema
.
getProto
().
getStruct
().
getIsGroup
(),
"Cannot form pointer to group type."
);
return
DynamicStruct
::
Builder
(
schema
,
builder
.
initStructField
(
index
,
structSizeFromSchema
(
schema
)));
}
...
...
c++/src/capnp/generated-header-support.h
View file @
1dcb66b1
...
...
@@ -167,6 +167,7 @@ struct RawSchema {
uint16_t
value
;
inline
operator
uint16_t
()
const
{
return
value
;
}
MemberInfo
()
=
default
;
constexpr
MemberInfo
(
uint16_t
value
)
:
value
(
value
)
{}
constexpr
MemberInfo
(
uint16_t
value
,
uint16_t
dummy
)
:
value
(
value
)
{}
};
...
...
c++/src/capnp/message.h
View file @
1dcb66b1
...
...
@@ -234,7 +234,7 @@ template <typename Reader>
void
copyToUnchecked
(
Reader
&&
reader
,
kj
::
ArrayPtr
<
word
>
uncheckedBuffer
);
// Copy the content of the given reader into the given buffer, such that it can safely be passed to
// readMessageUnchecked(). The buffer's size must be exactly reader.totalSizeInWords() + 1,
// otherwise an exception will be thrown.
// otherwise an exception will be thrown.
The buffer must be zero'd before calling.
template
<
typename
Type
>
static
typename
Type
::
Reader
defaultValue
();
...
...
@@ -332,7 +332,7 @@ private:
class
FlatMessageBuilder
:
public
MessageBuilder
{
// A message builder implementation which allocates from a single flat array, throwing an
// exception if it runs out of space.
// exception if it runs out of space.
The array must be zero'd before use.
public
:
explicit
FlatMessageBuilder
(
kj
::
ArrayPtr
<
word
>
array
);
...
...
c++/src/capnp/schema-loader.c++
View file @
1dcb66b1
...
...
@@ -56,11 +56,11 @@ public:
inline
Impl
(
const
SchemaLoader
&
loader
,
const
LazyLoadCallback
&
callback
)
:
initializer
(
loader
,
callback
)
{}
_
::
RawSchema
*
load
(
const
schema
::
Node
::
Reader
&
reader
,
bool
isPlaceholder
);
_
::
RawSchema
*
load
(
const
schema
2
::
Node
::
Reader
&
reader
,
bool
isPlaceholder
);
_
::
RawSchema
*
loadNative
(
const
_
::
RawSchema
*
nativeSchema
);
_
::
RawSchema
*
loadEmpty
(
uint64_t
id
,
kj
::
StringPtr
name
,
schema
::
Node
::
Body
::
Which
kind
);
_
::
RawSchema
*
loadEmpty
(
uint64_t
id
,
kj
::
StringPtr
name
,
schema
2
::
Node
::
Which
kind
);
// Create a dummy empty schema of the given kind for the given id and load it.
struct
TryGetResult
{
...
...
@@ -71,45 +71,82 @@ public:
TryGetResult
tryGet
(
uint64_t
typeId
)
const
;
kj
::
Array
<
Schema
>
getAllLoaded
()
const
;
void
requireStructSize
(
uint64_t
id
,
uint
dataWordCount
,
uint
pointerCount
);
// Require any struct nodes loaded with this ID -- in the past and in the future -- to have at
// least the given sizes. Struct nodes that don't comply will simply be rewritten to comply.
// This is used to ensure that parents of group nodes have at least the size of the group node,
// so that allocating a struct that contains a group then getting the group node and setting
// its fields can't possibly write outside of the allocated space.
kj
::
Arena
arena
;
private
:
std
::
unordered_map
<
uint64_t
,
_
::
RawSchema
*>
schemas
;
struct
RequiredSize
{
uint16_t
dataWordCount
;
uint16_t
pointerCount
;
};
std
::
unordered_map
<
uint64_t
,
RequiredSize
>
structSizeRequirements
;
InitializerImpl
initializer
;
kj
::
ArrayPtr
<
word
>
makeUncheckedNode
(
schema2
::
Node
::
Reader
node
);
// Construct a copy of the given schema node, allocated as a single-segment ("unchecked") node
// within the loader's arena.
kj
::
ArrayPtr
<
word
>
makeUncheckedNodeEnforcingSizeRequirements
(
schema2
::
Node
::
Reader
node
);
// Like makeUncheckedNode() but if structSizeRequirements has a requirement for this node which
// is larger than the node claims to be, the size will be edited to comply. This should be rare.
// If the incoming node is not a struct, any struct size requirements will be ignored, but if
// such requirements exist, this indicates an inconsistency that could cause exceptions later on
// (but at least can't cause memory corruption).
kj
::
ArrayPtr
<
word
>
rewriteStructNodeWithSizes
(
schema2
::
Node
::
Reader
node
,
uint
dataWordCount
,
uint
pointerCount
);
// Make a copy of the given node (which must be a struct node) and set its sizes to be the max
// of what it said already and the given sizes.
// If the encoded node does not meet the given struct size requirements, make a new copy that
// does.
void
applyStructSizeRequirement
(
_
::
RawSchema
*
raw
,
uint
dataWordCount
,
uint
pointerCount
);
};
// =======================================================================================
inline
static
void
verifyVoid
(
Void
value
)
{}
// Calls to this will break if the parameter type changes to non-void. We use this to detect
// when the code needs updating.
class
SchemaLoader
::
Validator
{
public
:
Validator
(
SchemaLoader
::
Impl
&
loader
)
:
loader
(
loader
)
{}
bool
validate
(
const
schema
::
Node
::
Reader
&
node
)
{
bool
validate
(
const
schema
2
::
Node
::
Reader
&
node
)
{
isValid
=
true
;
nodeName
=
node
.
getDisplayName
();
dependencies
.
clear
();
KJ_CONTEXT
(
"validating schema node"
,
nodeName
,
(
uint
)
node
.
getBody
().
which
());
KJ_CONTEXT
(
"validating schema node"
,
nodeName
,
(
uint
)
node
.
which
());
switch
(
node
.
getBody
().
which
())
{
case
schema
:
:
Node
::
Body
::
FILE_NOD
E
:
v
alidate
(
node
.
getBody
().
getFileNod
e
());
switch
(
node
.
which
())
{
case
schema
2
:
:
Node
::
FIL
E
:
v
erifyVoid
(
node
.
getFil
e
());
break
;
case
schema
:
:
Node
::
Body
::
STRUCT_NODE
:
validate
(
node
.
get
Body
().
getStructNode
());
case
schema
2
:
:
Node
::
STRUCT
:
validate
(
node
.
get
Struct
(),
node
.
getScopeId
());
break
;
case
schema
:
:
Node
::
Body
::
ENUM_NODE
:
validate
(
node
.
get
Body
().
getEnumNode
());
case
schema
2
:
:
Node
::
ENUM
:
validate
(
node
.
get
Enum
());
break
;
case
schema
:
:
Node
::
Body
::
INTERFACE_NOD
E
:
validate
(
node
.
get
Body
().
getInterfaceNod
e
());
case
schema
2
:
:
Node
::
INTERFAC
E
:
validate
(
node
.
get
Interfac
e
());
break
;
case
schema
:
:
Node
::
Body
::
CONST_NODE
:
validate
(
node
.
get
Body
().
getConstNode
());
case
schema
2
:
:
Node
::
CONST
:
validate
(
node
.
get
Const
());
break
;
case
schema
:
:
Node
::
Body
::
ANNOTATION_NODE
:
validate
(
node
.
get
Body
().
getAnnotationNode
());
case
schema
2
:
:
Node
::
ANNOTATION
:
validate
(
node
.
get
Annotation
());
break
;
}
...
...
@@ -135,96 +172,76 @@ public:
loader
.
arena
.
allocateArray
<
_
::
RawSchema
::
MemberInfo
>
(
*
count
);
uint
pos
=
0
;
for
(
auto
&
member
:
members
)
{
result
[
pos
++
]
=
{
kj
::
implicitCast
<
uint16_t
>
(
member
.
first
.
first
),
kj
::
implicitCast
<
uint16_t
>
(
member
.
second
)};
result
[
pos
++
]
=
member
.
second
;
}
KJ_DASSERT
(
pos
==
*
count
);
return
result
.
begin
();
}
const
uint16_t
*
makeMembersByDiscriminantArray
()
{
return
membersByDiscriminant
.
begin
();
}
private
:
SchemaLoader
::
Impl
&
loader
;
Text
::
Reader
nodeName
;
bool
isValid
;
std
::
map
<
uint64_t
,
_
::
RawSchema
*>
dependencies
;
// Maps (scopeOrdinal, name) -> index for each member.
std
::
map
<
std
::
pair
<
uint
,
Text
::
Reader
>
,
uint
>
members
;
// Maps name -> index for each member.
std
::
map
<
Text
::
Reader
,
uint
>
members
;
kj
::
ArrayPtr
<
uint16_t
>
membersByDiscriminant
;
#define VALIDATE_SCHEMA(condition, ...) \
KJ_REQUIRE(condition, ##__VA_ARGS__) { isValid = false; return; }
#define FAIL_VALIDATE_SCHEMA(...) \
KJ_FAIL_REQUIRE(__VA_ARGS__) { isValid = false; return; }
void
validate
(
const
schema
::
FileNode
::
Reader
&
fileNode
)
{
// Nothing needs validation.
}
uint
countOrdinals
(
const
List
<
schema
::
StructNode
::
Member
>::
Reader
&
members
)
{
uint
result
=
0
;
for
(
auto
member
:
members
)
{
switch
(
member
.
getBody
().
which
())
{
case
schema
:
:
StructNode
::
Member
::
Body
::
FIELD_MEMBER
:
++
result
;
break
;
case
schema
:
:
StructNode
::
Member
::
Body
::
UNION_MEMBER
:
{
auto
uMembers
=
member
.
getBody
().
getUnionMember
().
getMembers
();
if
(
uMembers
.
size
()
==
0
||
member
.
getOrdinal
()
!=
uMembers
[
0
].
getOrdinal
())
{
// Union has explicit ordinal.
++
result
;
}
result
+=
countOrdinals
(
uMembers
);
break
;
}
case
schema
:
:
StructNode
::
Member
::
Body
::
GROUP_MEMBER
:
result
+=
countOrdinals
(
member
.
getBody
().
getGroupMember
().
getMembers
());
break
;
}
}
return
result
;
void
validateMemberName
(
kj
::
StringPtr
name
,
uint
index
)
{
bool
isNewName
=
members
.
insert
(
std
::
make_pair
(
name
,
index
)).
second
;
VALIDATE_SCHEMA
(
isNewName
,
"duplicate name"
,
name
);
}
void
validate
(
const
schema
::
StructNode
::
Reader
&
structNode
)
{
void
validate
(
const
schema
2
::
Node
::
Struct
::
Reader
&
structNode
,
uint64_t
scopeId
)
{
uint
dataSizeInBits
;
uint
pointerCount
;
switch
(
structNode
.
getPreferredListEncoding
())
{
case
schema
:
:
ElementSize
::
EMPTY
:
case
schema
2
:
:
ElementSize
::
EMPTY
:
dataSizeInBits
=
0
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
BIT
:
case
schema
2
:
:
ElementSize
::
BIT
:
dataSizeInBits
=
1
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
BYTE
:
case
schema
2
:
:
ElementSize
::
BYTE
:
dataSizeInBits
=
8
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
TWO_BYTES
:
case
schema
2
:
:
ElementSize
::
TWO_BYTES
:
dataSizeInBits
=
16
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
FOUR_BYTES
:
case
schema
2
:
:
ElementSize
::
FOUR_BYTES
:
dataSizeInBits
=
32
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
EIGHT_BYTES
:
case
schema
2
:
:
ElementSize
::
EIGHT_BYTES
:
dataSizeInBits
=
64
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
POINTER
:
case
schema
2
:
:
ElementSize
::
POINTER
:
dataSizeInBits
=
0
;
pointerCount
=
1
;
break
;
case
schema
:
:
ElementSize
::
INLINE_COMPOSITE
:
case
schema
2
:
:
ElementSize
::
INLINE_COMPOSITE
:
dataSizeInBits
=
structNode
.
getDataSectionWordSize
()
*
64
;
pointerCount
=
structNode
.
getPointerSectionSize
();
break
;
default:
FAIL_VALIDATE_SCHEMA
(
"
Invalid preferredListEncoding.
"
);
FAIL_VALIDATE_SCHEMA
(
"
invalid preferredListEncoding
"
);
dataSizeInBits
=
0
;
pointerCount
=
0
;
break
;
...
...
@@ -232,139 +249,111 @@ private:
VALIDATE_SCHEMA
(
structNode
.
getDataSectionWordSize
()
==
(
dataSizeInBits
+
63
)
/
64
&&
structNode
.
getPointerSectionSize
()
==
pointerCount
,
"
Struct size does not match preferredListEncoding.
"
);
"
struct size does not match preferredListEncoding
"
);
auto
members
=
structNode
.
getMembers
();
uint
ordinalCount
=
countOrdinals
(
members
);
auto
fields
=
structNode
.
getFields
();
KJ_STACK_ARRAY
(
bool
,
sawCodeOrder
,
member
s
.
size
(),
32
,
256
);
KJ_STACK_ARRAY
(
bool
,
sawCodeOrder
,
field
s
.
size
(),
32
,
256
);
memset
(
sawCodeOrder
.
begin
(),
0
,
sawCodeOrder
.
size
()
*
sizeof
(
sawCodeOrder
[
0
]));
KJ_STACK_ARRAY
(
bool
,
sawOrdinal
,
ordinalCount
,
32
,
256
);
memset
(
sawOrdinal
.
begin
(),
0
,
sawOrdinal
.
size
()
*
sizeof
(
sawOrdinal
[
0
]));
uint
index
=
0
;
for
(
auto
member
:
members
)
{
KJ_CONTEXT
(
"validating struct member"
,
member
.
getName
());
validate
(
member
,
sawCodeOrder
,
sawOrdinal
,
dataSizeInBits
,
pointerCount
,
0
,
members
.
size
(),
index
++
);
KJ_STACK_ARRAY
(
bool
,
sawDiscriminantValue
,
structNode
.
getDiscriminantCount
(),
32
,
256
);
memset
(
sawDiscriminantValue
.
begin
(),
0
,
sawDiscriminantValue
.
size
()
*
sizeof
(
sawDiscriminantValue
[
0
]));
if
(
structNode
.
getDiscriminantCount
()
>
0
)
{
VALIDATE_SCHEMA
(
structNode
.
getDiscriminantCount
()
!=
1
,
"union must have at least two members"
);
VALIDATE_SCHEMA
(
structNode
.
getDiscriminantCount
()
<=
fields
.
size
(),
"struct can't have more union fields than total fields"
);
VALIDATE_SCHEMA
((
structNode
.
getDiscriminantOffset
()
+
1
)
*
16
<=
dataSizeInBits
,
"union discriminant is out-of-bounds"
);
}
}
void
validateMemberName
(
kj
::
StringPtr
name
,
uint
scopeOrdinal
,
uint
adjustedIndex
)
{
bool
isNewName
=
members
.
insert
(
std
::
make_pair
(
std
::
pair
<
uint
,
Text
::
Reader
>
(
scopeOrdinal
,
name
),
adjustedIndex
)).
second
;
VALIDATE_SCHEMA
(
isNewName
,
"duplicate name"
,
name
);
}
membersByDiscriminant
=
loader
.
arena
.
allocateArray
<
uint16_t
>
(
fields
.
size
());
uint
discriminantPos
=
0
;
uint
nonDiscriminantPos
=
structNode
.
getDiscriminantCount
();
void
validate
(
const
schema
::
StructNode
::
Member
::
Reader
&
member
,
kj
::
ArrayPtr
<
bool
>
sawCodeOrder
,
kj
::
ArrayPtr
<
bool
>
sawOrdinal
,
uint
dataSizeInBits
,
uint
pointerCount
,
uint
scopeOrdinal
,
uint
scopeMemberCount
,
uint
adjustedIndex
)
{
validateMemberName
(
member
.
getName
(),
scopeOrdinal
,
adjustedIndex
);
VALIDATE_SCHEMA
(
member
.
getCodeOrder
()
<
sawCodeOrder
.
size
()
&&
!
sawCodeOrder
[
member
.
getCodeOrder
()],
"Invalid codeOrder."
);
sawCodeOrder
[
member
.
getCodeOrder
()]
=
true
;
switch
(
member
.
getBody
().
which
())
{
case
schema
:
:
StructNode
::
Member
::
Body
::
FIELD_MEMBER
:
{
VALIDATE_SCHEMA
(
member
.
getOrdinal
()
<
sawOrdinal
.
size
()
&&
!
sawOrdinal
[
member
.
getOrdinal
()],
"Invalid ordinal."
,
member
.
getOrdinal
());
sawOrdinal
[
member
.
getOrdinal
()]
=
true
;
auto
field
=
member
.
getBody
().
getFieldMember
();
uint
fieldBits
;
bool
fieldIsPointer
;
validate
(
field
.
getType
(),
field
.
getDefaultValue
(),
&
fieldBits
,
&
fieldIsPointer
);
VALIDATE_SCHEMA
(
fieldBits
*
(
field
.
getOffset
()
+
1
)
<=
dataSizeInBits
&&
fieldIsPointer
*
(
field
.
getOffset
()
+
1
)
<=
pointerCount
,
"field offset out-of-bounds"
,
field
.
getOffset
(),
dataSizeInBits
,
pointerCount
);
break
;
}
uint
index
=
0
;
uint
nextOrdinal
=
0
;
for
(
auto
field
:
fields
)
{
KJ_CONTEXT
(
"validating struct field"
,
field
.
getName
());
case
schema
:
:
StructNode
::
Member
::
Body
::
UNION_MEMBER
:
{
auto
u
=
member
.
getBody
().
getUnionMember
();
VALIDATE_SCHEMA
((
u
.
getDiscriminantOffset
()
+
1
)
*
16
<=
dataSizeInBits
,
"Schema invalid: Union discriminant out-of-bounds."
);
auto
uMembers
=
u
.
getMembers
();
VALIDATE_SCHEMA
(
uMembers
.
size
()
>=
2
,
"Union must have at least two members."
);
KJ_STACK_ARRAY
(
bool
,
uSawCodeOrder
,
uMembers
.
size
(),
32
,
256
);
memset
(
uSawCodeOrder
.
begin
(),
0
,
uSawCodeOrder
.
size
()
*
sizeof
(
uSawCodeOrder
[
0
]));
uint
subIndex
=
0
;
for
(
auto
uMember
:
uMembers
)
{
KJ_CONTEXT
(
"validating union member"
,
uMember
.
getName
());
VALIDATE_SCHEMA
(
uMember
.
getBody
().
which
()
!=
schema
::
StructNode
::
Member
::
Body
::
UNION_MEMBER
,
"Union members must be fields or groups."
);
uint
subScopeOrdinal
;
uint
indexAdjustment
;
if
(
member
.
getName
().
size
()
==
0
)
{
subScopeOrdinal
=
scopeOrdinal
;
indexAdjustment
=
scopeMemberCount
;
}
else
{
subScopeOrdinal
=
member
.
getOrdinal
()
+
1
;
indexAdjustment
=
0
;
}
validate
(
uMember
,
uSawCodeOrder
,
sawOrdinal
,
dataSizeInBits
,
pointerCount
,
subScopeOrdinal
,
uMembers
.
size
(),
subIndex
++
+
indexAdjustment
);
}
validateMemberName
(
field
.
getName
(),
index
);
VALIDATE_SCHEMA
(
field
.
getCodeOrder
()
<
sawCodeOrder
.
size
()
&&
!
sawCodeOrder
[
field
.
getCodeOrder
()],
"invalid codeOrder"
);
sawCodeOrder
[
field
.
getCodeOrder
()]
=
true
;
// Union ordinal may match the ordinal of its first member, meaning it was unspecified in
// the schema file. Otherwise, it must be unique.
if
(
member
.
getOrdinal
()
!=
uMembers
[
0
].
getOrdinal
())
{
VALIDATE_SCHEMA
(
member
.
getOrdinal
()
<
sawOrdinal
.
size
()
&&
!
sawOrdinal
[
member
.
getOrdinal
()],
"Invalid ordinal."
,
member
.
getOrdinal
());
sawOrdinal
[
member
.
getOrdinal
()]
=
true
;
}
break
;
auto
ordinal
=
field
.
getOrdinal
();
if
(
ordinal
.
which
()
==
schema2
::
Field
::
Ordinal
::
EXPLICIT
)
{
VALIDATE_SCHEMA
(
ordinal
.
getExplicit
()
>=
nextOrdinal
,
"fields were not ordered by ordinal"
);
nextOrdinal
=
ordinal
.
getExplicit
()
+
1
;
}
case
schema
:
:
StructNode
::
Member
::
Body
::
GROUP_MEMBER
:
{
auto
g
=
member
.
getBody
().
getGroupMember
();
auto
gMembers
=
g
.
getMembers
();
VALIDATE_SCHEMA
(
gMembers
.
size
()
>=
2
,
"Group must have at least two members."
);
if
(
field
.
hasDiscriminantValue
())
{
VALIDATE_SCHEMA
(
field
.
getDiscriminantValue
()
<
sawDiscriminantValue
.
size
()
&&
!
sawDiscriminantValue
[
field
.
getDiscriminantValue
()],
"invalid discriminantValue"
);
sawDiscriminantValue
[
field
.
getDiscriminantValue
()]
=
true
;
membersByDiscriminant
[
discriminantPos
++
]
=
index
;
}
else
{
VALIDATE_SCHEMA
(
nonDiscriminantPos
<=
fields
.
size
(),
"discriminantCount did not match fields"
);
membersByDiscriminant
[
nonDiscriminantPos
++
]
=
index
;
}
KJ_STACK_ARRAY
(
bool
,
uSawCodeOrder
,
gMembers
.
size
(),
32
,
256
);
memset
(
uSawCodeOrder
.
begin
(),
0
,
uSawCodeOrder
.
size
()
*
sizeof
(
uSawCodeOrder
[
0
]));
switch
(
field
.
which
())
{
case
schema2
:
:
Field
::
REGULAR
:
{
auto
regularField
=
field
.
getRegular
();
uint
subIndex
=
0
;
for
(
auto
gMember
:
gMembers
)
{
KJ_CONTEXT
(
"validating group member"
,
gMember
.
getName
());
VALIDATE_SCHEMA
(
gMember
.
getBody
().
which
()
!=
schema
::
StructNode
::
Member
::
Body
::
GROUP_MEMBER
,
"Group members must be fields or unions."
);
uint
fieldBits
;
bool
fieldIsPointer
;
validate
(
regularField
.
getType
(),
regularField
.
getDefaultValue
(),
&
fieldBits
,
&
fieldIsPointer
);
VALIDATE_SCHEMA
(
fieldBits
*
(
regularField
.
getOffset
()
+
1
)
<=
dataSizeInBits
&&
fieldIsPointer
*
(
regularField
.
getOffset
()
+
1
)
<=
pointerCount
,
"field offset out-of-bounds"
,
regularField
.
getOffset
(),
dataSizeInBits
,
pointerCount
);
validate
(
gMember
,
uSawCodeOrder
,
sawOrdinal
,
dataSizeInBits
,
pointerCount
,
member
.
getOrdinal
()
+
1
,
gMembers
.
size
(),
subIndex
++
);
break
;
}
// Group ordinal must match the ordinal of its first member.
VALIDATE_SCHEMA
(
member
.
getOrdinal
()
==
gMembers
[
0
].
getOrdinal
(),
"Invalid ordinal."
,
member
.
getOrdinal
()
);
break
;
case
schema2
:
:
Field
::
GROUP
:
// Require that the group is a struct node.
validateTypeId
(
field
.
getGroup
(),
schema2
::
Node
::
STRUCT
);
break
;
}
++
index
;
}
}
void
validate
(
const
schema
::
EnumNode
::
Reader
&
enumNode
)
{
auto
enumerants
=
enumNode
.
getEnumerants
();
// If the above code is correct, these should pass.
KJ_ASSERT
(
discriminantPos
==
structNode
.
getDiscriminantCount
());
KJ_ASSERT
(
nonDiscriminantPos
==
fields
.
size
());
if
(
structNode
.
getIsGroup
())
{
VALIDATE_SCHEMA
(
scopeId
!=
0
,
"group node missing scopeId"
);
// Require that the group's scope has at least the same size as the group, so that anyone
// constructing an instance of the outer scope can safely read/write the group.
loader
.
requireStructSize
(
scopeId
,
structNode
.
getDataSectionWordSize
(),
structNode
.
getPointerSectionSize
());
// Require that the parent type is a struct.
validateTypeId
(
scopeId
,
schema2
::
Node
::
STRUCT
);
}
}
void
validate
(
const
List
<
schema2
::
Enumerant
>::
Reader
&
enumerants
)
{
KJ_STACK_ARRAY
(
bool
,
sawCodeOrder
,
enumerants
.
size
(),
32
,
256
);
memset
(
sawCodeOrder
.
begin
(),
0
,
sawCodeOrder
.
size
()
*
sizeof
(
sawCodeOrder
[
0
]));
uint
index
=
0
;
for
(
auto
enumerant
:
enumerants
)
{
validateMemberName
(
enumerant
.
getName
(),
0
,
index
++
);
validateMemberName
(
enumerant
.
getName
(),
index
++
);
VALIDATE_SCHEMA
(
enumerant
.
getCodeOrder
()
<
enumerants
.
size
()
&&
!
sawCodeOrder
[
enumerant
.
getCodeOrder
()],
...
...
@@ -373,16 +362,14 @@ private:
}
}
void
validate
(
const
schema
::
InterfaceNode
::
Reader
&
interfaceNode
)
{
auto
methods
=
interfaceNode
.
getMethods
();
void
validate
(
const
List
<
schema2
::
Method
>::
Reader
&
methods
)
{
KJ_STACK_ARRAY
(
bool
,
sawCodeOrder
,
methods
.
size
(),
32
,
256
);
memset
(
sawCodeOrder
.
begin
(),
0
,
sawCodeOrder
.
size
()
*
sizeof
(
sawCodeOrder
[
0
]));
uint
index
=
0
;
for
(
auto
method
:
methods
)
{
KJ_CONTEXT
(
"validating method"
,
method
.
getName
());
validateMemberName
(
method
.
getName
(),
0
,
index
++
);
validateMemberName
(
method
.
getName
(),
index
++
);
VALIDATE_SCHEMA
(
method
.
getCodeOrder
()
<
methods
.
size
()
&&
!
sawCodeOrder
[
method
.
getCodeOrder
()],
...
...
@@ -403,26 +390,26 @@ private:
}
}
void
validate
(
const
schema
::
ConstNode
::
Reader
&
constNode
)
{
void
validate
(
const
schema
2
::
Node
::
Const
::
Reader
&
constNode
)
{
uint
dummy1
;
bool
dummy2
;
validate
(
constNode
.
getType
(),
constNode
.
getValue
(),
&
dummy1
,
&
dummy2
);
}
void
validate
(
const
schema
::
AnnotationNode
::
Reader
&
annotationNode
)
{
void
validate
(
const
schema
2
::
Node
::
Annotation
::
Reader
&
annotationNode
)
{
validate
(
annotationNode
.
getType
());
}
void
validate
(
const
schema
::
Type
::
Reader
&
type
,
const
schema
::
Value
::
Reader
&
value
,
void
validate
(
const
schema
2
::
Type
::
Reader
&
type
,
const
schema2
::
Value
::
Reader
&
value
,
uint
*
dataSizeInBits
,
bool
*
isPointer
)
{
validate
(
type
);
schema
::
Value
::
Body
::
Which
expectedValueType
=
schema
::
Value
::
Body
::
VOID_VALUE
;
schema
2
::
Value
::
Which
expectedValueType
=
schema2
::
Value
::
VOID
;
bool
hadCase
=
false
;
switch
(
type
.
getBody
().
which
())
{
switch
(
type
.
which
())
{
#define HANDLE_TYPE(name, bits, ptr) \
case schema
::Type::Body::name##_TYPE
: \
expectedValueType = schema
::Value::Body::name##_VALUE
; \
case schema
2::Type::name
: \
expectedValueType = schema
2::Value::name
; \
*dataSizeInBits = bits; *isPointer = ptr; \
hadCase = true; \
break;
...
...
@@ -449,54 +436,54 @@ private:
}
if
(
hadCase
)
{
VALIDATE_SCHEMA
(
value
.
getBody
().
which
()
==
expectedValueType
,
"Value did not match type."
);
VALIDATE_SCHEMA
(
value
.
which
()
==
expectedValueType
,
"Value did not match type."
);
}
}
void
validate
(
const
schema
::
Type
::
Reader
&
type
)
{
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
case
schema
:
:
Type
::
Body
::
INT16_TYPE
:
case
schema
:
:
Type
::
Body
::
INT32_TYPE
:
case
schema
:
:
Type
::
Body
::
INT64_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT16_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT32_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT64_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
case
schema
:
:
Type
::
Body
::
TEXT_TYPE
:
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
void
validate
(
const
schema
2
::
Type
::
Reader
&
type
)
{
switch
(
type
.
which
())
{
case
schema
2
:
:
Type
::
VOID
:
case
schema
2
:
:
Type
::
BOOL
:
case
schema
2
:
:
Type
::
INT8
:
case
schema
2
:
:
Type
::
INT16
:
case
schema
2
:
:
Type
::
INT32
:
case
schema
2
:
:
Type
::
INT64
:
case
schema
2
:
:
Type
::
UINT8
:
case
schema
2
:
:
Type
::
UINT16
:
case
schema
2
:
:
Type
::
UINT32
:
case
schema
2
:
:
Type
::
UINT64
:
case
schema
2
:
:
Type
::
FLOAT32
:
case
schema
2
:
:
Type
::
FLOAT64
:
case
schema
2
:
:
Type
::
TEXT
:
case
schema
2
:
:
Type
::
DATA
:
case
schema
2
:
:
Type
::
OBJECT
:
break
;
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
validateTypeId
(
type
.
get
Body
().
getStructType
(),
schema
::
Node
::
Body
::
STRUCT_NODE
);
case
schema
2
:
:
Type
::
STRUCT
:
validateTypeId
(
type
.
get
Struct
(),
schema2
::
Node
::
STRUCT
);
break
;
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
validateTypeId
(
type
.
get
Body
().
getEnumType
(),
schema
::
Node
::
Body
::
ENUM_NODE
);
case
schema
2
:
:
Type
::
ENUM
:
validateTypeId
(
type
.
get
Enum
(),
schema2
::
Node
::
ENUM
);
break
;
case
schema
:
:
Type
::
Body
::
INTERFACE_TYP
E
:
validateTypeId
(
type
.
get
Body
().
getInterfaceType
(),
schema
::
Node
::
Body
::
INTERFACE_NOD
E
);
case
schema
2
:
:
Type
::
INTERFAC
E
:
validateTypeId
(
type
.
get
Interface
(),
schema2
::
Node
::
INTERFAC
E
);
break
;
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
validate
(
type
.
get
Body
().
getListType
());
case
schema
2
:
:
Type
::
LIST
:
validate
(
type
.
get
List
());
break
;
}
// We intentionally allow unknown types.
}
void
validateTypeId
(
uint64_t
id
,
schema
::
Node
::
Body
::
Which
expectedKind
)
{
void
validateTypeId
(
uint64_t
id
,
schema
2
::
Node
::
Which
expectedKind
)
{
_
::
RawSchema
*
existing
=
loader
.
tryGet
(
id
).
schema
;
if
(
existing
!=
nullptr
)
{
auto
node
=
readMessageUnchecked
<
schema
::
Node
>
(
existing
->
encodedNode
);
VALIDATE_SCHEMA
(
node
.
getBody
().
which
()
==
expectedKind
,
auto
node
=
readMessageUnchecked
<
schema
2
::
Node
>
(
existing
->
encodedNode
);
VALIDATE_SCHEMA
(
node
.
which
()
==
expectedKind
,
"expected a different kind of node for this ID"
,
id
,
(
uint
)
expectedKind
,
(
uint
)
node
.
getBody
().
which
(),
node
.
getDisplayName
());
id
,
(
uint
)
expectedKind
,
(
uint
)
node
.
which
(),
node
.
getDisplayName
());
dependencies
.
insert
(
std
::
make_pair
(
id
,
existing
));
return
;
}
...
...
@@ -515,8 +502,8 @@ class SchemaLoader::CompatibilityChecker {
public
:
CompatibilityChecker
(
SchemaLoader
::
Impl
&
loader
)
:
loader
(
loader
)
{}
bool
shouldReplace
(
const
schema
::
Node
::
Reader
&
existingNode
,
const
schema
::
Node
::
Reader
&
replacement
,
bool
shouldReplace
(
const
schema
2
::
Node
::
Reader
&
existingNode
,
const
schema
2
::
Node
::
Reader
&
replacement
,
bool
preferReplacementIfEquivalent
)
{
KJ_CONTEXT
(
"checking compatibility with previously-loaded node of the same id"
,
existingNode
.
getDisplayName
());
...
...
@@ -581,53 +568,44 @@ private:
}
}
void
checkCompatibility
(
const
schema
::
Node
::
Reader
&
node
,
const
schema
::
Node
::
Reader
&
replacement
)
{
void
checkCompatibility
(
const
schema
2
::
Node
::
Reader
&
node
,
const
schema
2
::
Node
::
Reader
&
replacement
)
{
// Returns whether `replacement` is equivalent, older than, newer than, or incompatible with
// `node`. If exceptions are enabled, this will throw an exception on INCOMPATIBLE.
VALIDATE_SCHEMA
(
node
.
getBody
().
which
()
==
replacement
.
getBody
()
.
which
(),
VALIDATE_SCHEMA
(
node
.
which
()
==
replacement
.
which
(),
"kind of declaration changed"
);
// No need to check compatibility of the non-body parts of the node:
// - Arbitrary renaming and moving between scopes is allowed.
// - Annotations are ignored for compatibility purposes.
switch
(
node
.
getBody
().
which
())
{
case
schema
:
:
Node
::
Body
::
FILE_NODE
:
checkCompatibility
(
node
.
getBody
().
getFileNode
(),
replacement
.
getBody
().
getFileNode
());
switch
(
node
.
which
())
{
case
schema2
:
:
Node
::
FILE
:
verifyVoid
(
node
.
getFile
());
break
;
case
schema
:
:
Node
::
Body
::
STRUCT_NODE
:
checkCompatibility
(
node
.
get
Body
().
getStructNode
(),
replacement
.
getBody
().
getStructNode
());
case
schema
2
:
:
Node
::
STRUCT
:
checkCompatibility
(
node
.
get
Struct
(),
replacement
.
getStruct
(),
node
.
getScopeId
(),
replacement
.
getScopeId
());
break
;
case
schema
:
:
Node
::
Body
::
ENUM_NODE
:
checkCompatibility
(
node
.
getBody
().
getEnumNode
(),
replacement
.
getBody
().
getEnumNode
());
case
schema2
:
:
Node
::
ENUM
:
checkCompatibility
(
node
.
getEnum
(),
replacement
.
getEnum
());
break
;
case
schema
:
:
Node
::
Body
::
INTERFACE_NODE
:
checkCompatibility
(
node
.
getBody
().
getInterfaceNode
(),
replacement
.
getBody
().
getInterfaceNode
());
case
schema2
:
:
Node
::
INTERFACE
:
checkCompatibility
(
node
.
getInterface
(),
replacement
.
getInterface
());
break
;
case
schema
:
:
Node
::
Body
::
CONST_NODE
:
checkCompatibility
(
node
.
getBody
().
getConstNode
(),
replacement
.
getBody
().
getConstNode
());
case
schema2
:
:
Node
::
CONST
:
checkCompatibility
(
node
.
getConst
(),
replacement
.
getConst
());
break
;
case
schema
:
:
Node
::
Body
::
ANNOTATION_NODE
:
checkCompatibility
(
node
.
getBody
().
getAnnotationNode
(),
replacement
.
getBody
().
getAnnotationNode
());
case
schema2
:
:
Node
::
ANNOTATION
:
checkCompatibility
(
node
.
getAnnotation
(),
replacement
.
getAnnotation
());
break
;
}
}
void
checkCompatibility
(
const
schema
::
FileNode
::
Reader
&
file
,
const
schema
::
FileNode
::
Reader
&
replacement
)
{
// Nothing to compare.
}
void
checkCompatibility
(
const
schema
::
StructNode
::
Reader
&
structNode
,
const
schema
::
StructNode
::
Reader
&
replacement
)
{
void
checkCompatibility
(
const
schema2
::
Node
::
Struct
::
Reader
&
structNode
,
const
schema2
::
Node
::
Struct
::
Reader
&
replacement
,
uint64_t
scopeId
,
uint64_t
replacementScopeId
)
{
if
(
replacement
.
getDataSectionWordSize
()
>
structNode
.
getDataSectionWordSize
())
{
replacementIsNewer
();
}
else
if
(
replacement
.
getDataSectionWordSize
()
<
structNode
.
getDataSectionWordSize
())
{
...
...
@@ -651,87 +629,71 @@ private:
// The shared members should occupy corresponding positions in the member lists, since the
// lists are sorted by ordinal.
auto
members
=
structNode
.
getMember
s
();
auto
replacement
Members
=
replacement
.
getMember
s
();
uint
count
=
std
::
min
(
members
.
size
(),
replacementMember
s
.
size
());
auto
fields
=
structNode
.
getField
s
();
auto
replacement
Fields
=
replacement
.
getField
s
();
uint
count
=
std
::
min
(
fields
.
size
(),
replacementField
s
.
size
());
if
(
replacement
Members
.
size
()
>
member
s
.
size
())
{
if
(
replacement
Fields
.
size
()
>
field
s
.
size
())
{
replacementIsNewer
();
}
else
if
(
replacement
Members
.
size
()
<
member
s
.
size
())
{
}
else
if
(
replacement
Fields
.
size
()
<
field
s
.
size
())
{
replacementIsOlder
();
}
for
(
uint
i
=
0
;
i
<
count
;
i
++
)
{
checkCompatibility
(
members
[
i
],
replacementMembers
[
i
]);
checkCompatibility
(
fields
[
i
],
replacementFields
[
i
]);
}
// For the moment, we allow "upgrading" from non-group to group, mainly so that the
// placeholders we generate for group parents (which in the absence of more info, we assume to
// be non-groups) can be replaced with groups.
//
// TODO(cleanup): The placeholder approach is really breaking down. Maybe we need to maintain
// a list of expectations for nodes we haven't loaded yet.
if
(
structNode
.
getIsGroup
())
{
if
(
replacement
.
getIsGroup
())
{
VALIDATE_SCHEMA
(
replacementScopeId
==
scopeId
,
"group node's scope changed"
);
}
else
{
replacementIsOlder
();
}
}
else
{
if
(
replacement
.
getIsGroup
())
{
replacementIsNewer
();
}
}
}
void
checkCompatibility
(
const
schema
::
StructNode
::
Member
::
Reader
&
member
,
const
schema
::
StructNode
::
Member
::
Reader
&
replacement
)
{
KJ_CONTEXT
(
"comparing struct
member"
,
member
.
getName
());
void
checkCompatibility
(
const
schema
2
::
Field
::
Reader
&
field
,
const
schema
2
::
Field
::
Reader
&
replacement
)
{
KJ_CONTEXT
(
"comparing struct
field"
,
field
.
getName
());
switch
(
member
.
getBody
().
which
())
{
case
schema
:
:
StructNode
::
Member
::
Body
::
FIELD_MEMBER
:
{
auto
field
=
member
.
getBody
().
getFieldMember
();
auto
replacementField
=
replacement
.
getBody
().
getFieldMember
();
VALIDATE_SCHEMA
(
field
.
which
()
==
replacement
.
which
(),
"group field replaced with non-group or vice versa"
);
checkCompatibility
(
field
.
getType
(),
replacementField
.
getType
(),
switch
(
field
.
which
())
{
case
schema2
:
:
Field
::
REGULAR
:
{
auto
regularField
=
field
.
getRegular
();
auto
replacementRegularField
=
replacement
.
getRegular
();
checkCompatibility
(
regularField
.
getType
(),
replacementRegularField
.
getType
(),
NO_UPGRADE_TO_STRUCT
);
checkDefaultCompatibility
(
field
.
getDefaultValue
(),
replacementField
.
getDefaultValue
());
checkDefaultCompatibility
(
regularField
.
getDefaultValue
(),
replacementRegularField
.
getDefaultValue
());
VALIDATE_SCHEMA
(
field
.
getOffset
()
==
replacement
Field
.
getOffset
(),
VALIDATE_SCHEMA
(
regularField
.
getOffset
()
==
replacementRegular
Field
.
getOffset
(),
"field position changed"
);
break
;
}
case
schema
:
:
StructNode
::
Member
::
Body
::
UNION_MEMBER
:
{
auto
existingUnion
=
member
.
getBody
().
getUnionMember
();
auto
replacementUnion
=
replacement
.
getBody
().
getUnionMember
();
VALIDATE_SCHEMA
(
existingUnion
.
getDiscriminantOffset
()
==
replacementUnion
.
getDiscriminantOffset
(),
"union discriminant position changed"
);
auto
members
=
existingUnion
.
getMembers
();
auto
replacementMembers
=
replacementUnion
.
getMembers
();
uint
count
=
std
::
min
(
members
.
size
(),
replacementMembers
.
size
());
if
(
replacementMembers
.
size
()
>
members
.
size
())
{
replacementIsNewer
();
}
else
if
(
replacementMembers
.
size
()
<
members
.
size
())
{
replacementIsOlder
();
}
for
(
uint
i
=
0
;
i
<
count
;
i
++
)
{
checkCompatibility
(
members
[
i
],
replacementMembers
[
i
]);
}
break
;
}
case
schema
:
:
StructNode
::
Member
::
Body
::
GROUP_MEMBER
:
{
auto
existingGroup
=
member
.
getBody
().
getGroupMember
();
auto
replacementGroup
=
replacement
.
getBody
().
getGroupMember
();
auto
members
=
existingGroup
.
getMembers
();
auto
replacementMembers
=
replacementGroup
.
getMembers
();
uint
count
=
std
::
min
(
members
.
size
(),
replacementMembers
.
size
());
if
(
replacementMembers
.
size
()
>
members
.
size
())
{
replacementIsNewer
();
}
else
if
(
replacementMembers
.
size
()
<
members
.
size
())
{
replacementIsOlder
();
}
for
(
uint
i
=
0
;
i
<
count
;
i
++
)
{
checkCompatibility
(
members
[
i
],
replacementMembers
[
i
]);
}
case
schema2
:
:
Field
::
GROUP
:
VALIDATE_SCHEMA
(
field
.
getGroup
()
==
replacement
.
getGroup
(),
"group id changed"
);
break
;
}
}
}
void
checkCompatibility
(
const
schema
::
EnumNode
::
Reader
&
enumNode
,
const
schema
::
EnumNode
::
Reader
&
replacement
)
{
uint
size
=
enum
Node
.
getEnumerants
()
.
size
();
uint
replacementSize
=
replacement
.
getEnumerants
()
.
size
();
void
checkCompatibility
(
const
List
<
schema2
::
Enumerant
>::
Reader
&
enumerants
,
const
List
<
schema2
::
Enumerant
>::
Reader
&
replacementEnumerants
)
{
uint
size
=
enum
erants
.
size
();
uint
replacementSize
=
replacement
Enumerants
.
size
();
if
(
replacementSize
>
size
)
{
replacementIsNewer
();
}
else
if
(
replacementSize
<
size
)
{
...
...
@@ -739,11 +701,8 @@ private:
}
}
void
checkCompatibility
(
const
schema
::
InterfaceNode
::
Reader
&
interfaceNode
,
const
schema
::
InterfaceNode
::
Reader
&
replacement
)
{
auto
methods
=
interfaceNode
.
getMethods
();
auto
replacementMethods
=
replacement
.
getMethods
();
void
checkCompatibility
(
const
List
<
schema2
::
Method
>::
Reader
&
methods
,
const
List
<
schema2
::
Method
>::
Reader
&
replacementMethods
)
{
if
(
replacementMethods
.
size
()
>
methods
.
size
())
{
replacementIsNewer
();
}
else
if
(
replacementMethods
.
size
()
<
methods
.
size
())
{
...
...
@@ -757,8 +716,8 @@ private:
}
}
void
checkCompatibility
(
const
schema
::
InterfaceNode
::
Method
::
Reader
&
method
,
const
schema
::
InterfaceNode
::
Method
::
Reader
&
replacement
)
{
void
checkCompatibility
(
const
schema
2
::
Method
::
Reader
&
method
,
const
schema
2
::
Method
::
Reader
&
replacement
)
{
KJ_CONTEXT
(
"comparing method"
,
method
.
getName
());
auto
params
=
method
.
getParams
();
...
...
@@ -797,13 +756,13 @@ private:
ALLOW_UPGRADE_TO_STRUCT
);
}
void
checkCompatibility
(
const
schema
::
ConstNode
::
Reader
&
constNode
,
const
schema
::
ConstNode
::
Reader
&
replacement
)
{
void
checkCompatibility
(
const
schema
2
::
Node
::
Const
::
Reader
&
constNode
,
const
schema
2
::
Node
::
Const
::
Reader
&
replacement
)
{
// Who cares? These don't appear on the wire.
}
void
checkCompatibility
(
const
schema
::
AnnotationNode
::
Reader
&
annotationNode
,
const
schema
::
AnnotationNode
::
Reader
&
replacement
)
{
void
checkCompatibility
(
const
schema
2
::
Node
::
Annotation
::
Reader
&
annotationNode
,
const
schema
2
::
Node
::
Annotation
::
Reader
&
replacement
)
{
// Who cares? These don't appear on the wire.
}
...
...
@@ -812,35 +771,31 @@ private:
NO_UPGRADE_TO_STRUCT
};
void
checkCompatibility
(
const
schema
::
Type
::
Reader
&
type
,
const
schema
::
Type
::
Reader
&
replacement
,
void
checkCompatibility
(
const
schema
2
::
Type
::
Reader
&
type
,
const
schema
2
::
Type
::
Reader
&
replacement
,
UpgradeToStructMode
upgradeToStructMode
)
{
if
(
replacement
.
getBody
().
which
()
!=
type
.
getBody
()
.
which
())
{
if
(
replacement
.
which
()
!=
type
.
which
())
{
// Check for allowed "upgrade" to Data or Object.
if
(
replacement
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
DATA_TYPE
&&
canUpgradeToData
(
type
))
{
if
(
replacement
.
which
()
==
schema2
::
Type
::
DATA
&&
canUpgradeToData
(
type
))
{
replacementIsNewer
();
return
;
}
else
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
DATA_TYPE
&&
canUpgradeToData
(
replacement
))
{
}
else
if
(
type
.
which
()
==
schema2
::
Type
::
DATA
&&
canUpgradeToData
(
replacement
))
{
replacementIsOlder
();
return
;
}
else
if
(
replacement
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
OBJECT_TYPE
&&
canUpgradeToObject
(
type
))
{
}
else
if
(
replacement
.
which
()
==
schema2
::
Type
::
OBJECT
&&
canUpgradeToObject
(
type
))
{
replacementIsNewer
();
return
;
}
else
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
OBJECT_TYPE
&&
canUpgradeToObject
(
replacement
))
{
}
else
if
(
type
.
which
()
==
schema2
::
Type
::
OBJECT
&&
canUpgradeToObject
(
replacement
))
{
replacementIsOlder
();
return
;
}
if
(
upgradeToStructMode
==
ALLOW_UPGRADE_TO_STRUCT
)
{
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
STRUCT_TYPE
)
{
checkUpgradeToStruct
(
replacement
,
type
.
get
Body
().
getStructType
());
if
(
type
.
which
()
==
schema2
::
Type
::
STRUCT
)
{
checkUpgradeToStruct
(
replacement
,
type
.
get
Struct
());
return
;
}
else
if
(
replacement
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
STRUCT_TYPE
)
{
checkUpgradeToStruct
(
type
,
replacement
.
get
Body
().
getStructType
());
}
else
if
(
replacement
.
which
()
==
schema2
::
Type
::
STRUCT
)
{
checkUpgradeToStruct
(
type
,
replacement
.
get
Struct
());
return
;
}
}
...
...
@@ -848,36 +803,33 @@ private:
FAIL_VALIDATE_SCHEMA
(
"a type was changed"
);
}
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
case
schema
:
:
Type
::
Body
::
INT16_TYPE
:
case
schema
:
:
Type
::
Body
::
INT32_TYPE
:
case
schema
:
:
Type
::
Body
::
INT64_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT16_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT32_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT64_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
case
schema
:
:
Type
::
Body
::
TEXT_TYPE
:
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
switch
(
type
.
which
())
{
case
schema
2
:
:
Type
::
VOID
:
case
schema
2
:
:
Type
::
BOOL
:
case
schema
2
:
:
Type
::
INT8
:
case
schema
2
:
:
Type
::
INT16
:
case
schema
2
:
:
Type
::
INT32
:
case
schema
2
:
:
Type
::
INT64
:
case
schema
2
:
:
Type
::
UINT8
:
case
schema
2
:
:
Type
::
UINT16
:
case
schema
2
:
:
Type
::
UINT32
:
case
schema
2
:
:
Type
::
UINT64
:
case
schema
2
:
:
Type
::
FLOAT32
:
case
schema
2
:
:
Type
::
FLOAT64
:
case
schema
2
:
:
Type
::
TEXT
:
case
schema
2
:
:
Type
::
DATA
:
case
schema
2
:
:
Type
::
OBJECT
:
return
;
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
checkCompatibility
(
type
.
getBody
().
getListType
(),
replacement
.
getBody
().
getListType
(),
ALLOW_UPGRADE_TO_STRUCT
);
case
schema2
:
:
Type
::
LIST
:
checkCompatibility
(
type
.
getList
(),
replacement
.
getList
(),
ALLOW_UPGRADE_TO_STRUCT
);
return
;
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
VALIDATE_SCHEMA
(
replacement
.
getBody
().
getEnumType
()
==
type
.
getBody
().
getEnumType
(),
"type changed enum type"
);
case
schema2
:
:
Type
::
ENUM
:
VALIDATE_SCHEMA
(
replacement
.
getEnum
()
==
type
.
getEnum
(),
"type changed enum type"
);
return
;
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
case
schema
2
:
:
Type
::
STRUCT
:
// TODO(someday): If the IDs don't match, we should compare the two structs for
// compatibility. This is tricky, though, because the new type's target may not yet be
// loaded. In that case we could take the old type, make a copy of it, assign the new
...
...
@@ -885,21 +837,20 @@ private:
// be compatible. However, that has another problem, which is that it could be that the
// whole reason the type was replaced was to fork that type, and so an incompatibility
// could be very much expected. This could be a rat hole...
VALIDATE_SCHEMA
(
replacement
.
get
Body
().
getStructType
()
==
type
.
getBody
().
getStructType
(),
VALIDATE_SCHEMA
(
replacement
.
get
Struct
()
==
type
.
getStruct
(),
"type changed to incompatible struct type"
);
return
;
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
VALIDATE_SCHEMA
(
replacement
.
getBody
().
getInterfaceType
()
==
type
.
getBody
().
getInterfaceType
(),
"type changed to incompatible interface type"
);
case
schema2
:
:
Type
::
INTERFACE
:
VALIDATE_SCHEMA
(
replacement
.
getInterface
()
==
type
.
getInterface
(),
"type changed to incompatible interface type"
);
return
;
}
// We assume unknown types (from newer versions of Cap'n Proto?) are equivalent.
}
void
checkUpgradeToStruct
(
const
schema
::
Type
::
Reader
&
type
,
uint64_t
structTypeId
)
{
void
checkUpgradeToStruct
(
const
schema
2
::
Type
::
Reader
&
type
,
uint64_t
structTypeId
)
{
// We can't just look up the target struct and check it because it may not have been loaded
// yet. Instead, we contrive a struct that looks like what we want and load() that, which
// guarantees that any incompatibility will be caught either now or when the real version of
...
...
@@ -907,84 +858,84 @@ private:
word
scratch
[
32
];
memset
(
scratch
,
0
,
sizeof
(
scratch
));
MallocMessageBuilder
builder
(
kj
::
arrayPtr
(
scratch
,
sizeof
(
scratch
))
);
auto
node
=
builder
.
initRoot
<
schema
::
Node
>
();
MallocMessageBuilder
builder
(
scratch
);
auto
node
=
builder
.
initRoot
<
schema
2
::
Node
>
();
node
.
setId
(
structTypeId
);
node
.
setDisplayName
(
kj
::
str
(
"(unknown type used in "
,
nodeName
,
")"
));
auto
structNode
=
node
.
getBody
().
initStructNode
();
auto
structNode
=
node
.
initStruct
();
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
switch
(
type
.
which
())
{
case
schema
2
:
:
Type
::
VOID
:
structNode
.
setDataSectionWordSize
(
0
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
EMPTY
);
structNode
.
setPreferredListEncoding
(
schema
2
::
ElementSize
::
EMPTY
);
break
;
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
case
schema
2
:
:
Type
::
BOOL
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
BIT
);
structNode
.
setPreferredListEncoding
(
schema
2
::
ElementSize
::
BIT
);
break
;
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
case
schema
2
:
:
Type
::
INT8
:
case
schema
2
:
:
Type
::
UINT8
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
BYTE
);
structNode
.
setPreferredListEncoding
(
schema
2
::
ElementSize
::
BYTE
);
break
;
case
schema
:
:
Type
::
Body
::
INT16_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT16_TYPE
:
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
case
schema
2
:
:
Type
::
INT16
:
case
schema
2
:
:
Type
::
UINT16
:
case
schema
2
:
:
Type
::
ENUM
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
TWO_BYTES
);
structNode
.
setPreferredListEncoding
(
schema
2
::
ElementSize
::
TWO_BYTES
);
break
;
case
schema
:
:
Type
::
Body
::
INT32_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT32_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
case
schema
2
:
:
Type
::
INT32
:
case
schema
2
:
:
Type
::
UINT32
:
case
schema
2
:
:
Type
::
FLOAT32
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
FOUR_BYTES
);
structNode
.
setPreferredListEncoding
(
schema
2
::
ElementSize
::
FOUR_BYTES
);
break
;
case
schema
:
:
Type
::
Body
::
INT64_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT64_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
case
schema
2
:
:
Type
::
INT64
:
case
schema
2
:
:
Type
::
UINT64
:
case
schema
2
:
:
Type
::
FLOAT64
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
EIGHT_BYTES
);
structNode
.
setPreferredListEncoding
(
schema
2
::
ElementSize
::
EIGHT_BYTES
);
break
;
case
schema
:
:
Type
::
Body
::
TEXT_TYPE
:
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
case
schema
:
:
Type
::
Body
::
INTERFACE_TYP
E
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
case
schema
2
:
:
Type
::
TEXT
:
case
schema
2
:
:
Type
::
DATA
:
case
schema
2
:
:
Type
::
LIST
:
case
schema
2
:
:
Type
::
STRUCT
:
case
schema
2
:
:
Type
::
INTERFAC
E
:
case
schema
2
:
:
Type
::
OBJECT
:
structNode
.
setDataSectionWordSize
(
0
);
structNode
.
setPointerSectionSize
(
1
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
POINTER
);
structNode
.
setPreferredListEncoding
(
schema
2
::
ElementSize
::
POINTER
);
break
;
}
auto
member
=
structNode
.
initMember
s
(
1
)[
0
];
member
.
setName
(
"member0"
);
member
.
setOrdinal
(
0
);
member
.
setCodeOrder
(
0
);
member
.
getBody
().
initFieldMembe
r
().
setType
(
type
);
auto
field
=
structNode
.
initField
s
(
1
)[
0
];
field
.
setName
(
"member0"
);
field
.
getOrdinal
().
setExplicit
(
0
);
field
.
setCodeOrder
(
0
);
field
.
initRegula
r
().
setType
(
type
);
loader
.
load
(
node
,
true
);
}
bool
canUpgradeToData
(
const
schema
::
Type
::
Reader
&
type
)
{
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
TEXT_TYPE
)
{
bool
canUpgradeToData
(
const
schema
2
::
Type
::
Reader
&
type
)
{
if
(
type
.
which
()
==
schema2
::
Type
::
TEXT
)
{
return
true
;
}
else
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
LIST_TYPE
)
{
switch
(
type
.
get
Body
().
getListType
().
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
}
else
if
(
type
.
which
()
==
schema2
::
Type
::
LIST
)
{
switch
(
type
.
get
List
().
which
())
{
case
schema
2
:
:
Type
::
INT8
:
case
schema
2
:
:
Type
::
UINT8
:
return
true
;
default:
return
false
;
...
...
@@ -994,29 +945,29 @@ private:
}
}
bool
canUpgradeToObject
(
const
schema
::
Type
::
Reader
&
type
)
{
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
case
schema
:
:
Type
::
Body
::
INT16_TYPE
:
case
schema
:
:
Type
::
Body
::
INT32_TYPE
:
case
schema
:
:
Type
::
Body
::
INT64_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT16_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT32_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT64_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
bool
canUpgradeToObject
(
const
schema
2
::
Type
::
Reader
&
type
)
{
switch
(
type
.
which
())
{
case
schema
2
:
:
Type
::
VOID
:
case
schema
2
:
:
Type
::
BOOL
:
case
schema
2
:
:
Type
::
INT8
:
case
schema
2
:
:
Type
::
INT16
:
case
schema
2
:
:
Type
::
INT32
:
case
schema
2
:
:
Type
::
INT64
:
case
schema
2
:
:
Type
::
UINT8
:
case
schema
2
:
:
Type
::
UINT16
:
case
schema
2
:
:
Type
::
UINT32
:
case
schema
2
:
:
Type
::
UINT64
:
case
schema
2
:
:
Type
::
FLOAT32
:
case
schema
2
:
:
Type
::
FLOAT64
:
case
schema
2
:
:
Type
::
ENUM
:
return
false
;
case
schema
:
:
Type
::
Body
::
TEXT_TYPE
:
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
case
schema
:
:
Type
::
Body
::
INTERFACE_TYP
E
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
case
schema
2
:
:
Type
::
TEXT
:
case
schema
2
:
:
Type
::
DATA
:
case
schema
2
:
:
Type
::
LIST
:
case
schema
2
:
:
Type
::
STRUCT
:
case
schema
2
:
:
Type
::
INTERFAC
E
:
case
schema
2
:
:
Type
::
OBJECT
:
return
true
;
}
...
...
@@ -1024,21 +975,19 @@ private:
return
true
;
}
void
checkDefaultCompatibility
(
const
schema
::
Value
::
Reader
&
value
,
const
schema
::
Value
::
Reader
&
replacement
)
{
void
checkDefaultCompatibility
(
const
schema
2
::
Value
::
Reader
&
value
,
const
schema
2
::
Value
::
Reader
&
replacement
)
{
// Note that we test default compatibility only after testing type compatibility, and default
// values have already been validated as matching their types, so this should pass.
KJ_ASSERT
(
value
.
getBody
().
which
()
==
replacement
.
getBody
()
.
which
())
{
KJ_ASSERT
(
value
.
which
()
==
replacement
.
which
())
{
compatibility
=
INCOMPATIBLE
;
return
;
}
switch
(
value
.
getBody
().
which
())
{
switch
(
value
.
which
())
{
#define HANDLE_TYPE(discrim, name) \
case schema::Value::Body::discrim##_VALUE: \
VALIDATE_SCHEMA(value.getBody().get##name##Value() == \
replacement.getBody().get##name##Value(), \
"default value changed"); \
case schema2::Value::discrim: \
VALIDATE_SCHEMA(value.get##name() == replacement.get##name(), "default value changed"); \
break;
HANDLE_TYPE
(
VOID
,
Void
);
HANDLE_TYPE
(
BOOL
,
Bool
);
...
...
@@ -1055,12 +1004,12 @@ private:
HANDLE_TYPE
(
ENUM
,
Enum
);
#undef HANDLE_TYPE
case
schema
:
:
Value
::
Body
::
TEXT_VALUE
:
case
schema
:
:
Value
::
Body
::
DATA_VALUE
:
case
schema
:
:
Value
::
Body
::
LIST_VALUE
:
case
schema
:
:
Value
::
Body
::
STRUCT_VALUE
:
case
schema
:
:
Value
::
Body
::
INTERFACE_VALU
E
:
case
schema
:
:
Value
::
Body
::
OBJECT_VALUE
:
case
schema
2
:
:
Value
::
TEXT
:
case
schema
2
:
:
Value
::
DATA
:
case
schema
2
:
:
Value
::
LIST
:
case
schema
2
:
:
Value
::
STRUCT
:
case
schema
2
:
:
Value
::
INTERFAC
E
:
case
schema
2
:
:
Value
::
OBJECT
:
// It's not a big deal if default values for pointers change, and it would be difficult for
// us to compare these defaults here, so just let it slide.
break
;
...
...
@@ -1070,22 +1019,19 @@ private:
// =======================================================================================
_
::
RawSchema
*
SchemaLoader
::
Impl
::
load
(
const
schema
::
Node
::
Reader
&
reader
,
bool
isPlaceholder
)
{
_
::
RawSchema
*
SchemaLoader
::
Impl
::
load
(
const
schema
2
::
Node
::
Reader
&
reader
,
bool
isPlaceholder
)
{
// Make a copy of the node which can be used unchecked.
size_t
size
=
reader
.
totalSizeInWords
()
+
1
;
kj
::
ArrayPtr
<
word
>
validated
=
arena
.
allocateArray
<
word
>
(
size
);
memset
(
validated
.
begin
(),
0
,
size
*
sizeof
(
word
));
copyToUnchecked
(
reader
,
validated
);
kj
::
ArrayPtr
<
word
>
validated
=
makeUncheckedNodeEnforcingSizeRequirements
(
reader
);
// Validate the copy.
Validator
validator
(
*
this
);
auto
validatedReader
=
readMessageUnchecked
<
schema
::
Node
>
(
validated
.
begin
());
auto
validatedReader
=
readMessageUnchecked
<
schema
2
::
Node
>
(
validated
.
begin
());
if
(
!
validator
.
validate
(
validatedReader
))
{
// Not valid. Construct an empty schema of the same type and return that.
return
loadEmpty
(
validatedReader
.
getId
(),
validatedReader
.
getDisplayName
(),
validatedReader
.
getBody
().
which
());
validatedReader
.
which
());
}
// Check if we already have a schema for this ID.
...
...
@@ -1106,7 +1052,7 @@ _::RawSchema* SchemaLoader::Impl::load(const schema::Node::Reader& reader, bool
isPlaceholder
=
false
;
}
auto
existing
=
readMessageUnchecked
<
schema
::
Node
>
(
slot
->
encodedNode
);
auto
existing
=
readMessageUnchecked
<
schema
2
::
Node
>
(
slot
->
encodedNode
);
CompatibilityChecker
checker
(
*
this
);
// Prefer to replace the existing schema if the existing schema is a placeholder. Otherwise,
...
...
@@ -1121,6 +1067,7 @@ _::RawSchema* SchemaLoader::Impl::load(const schema::Node::Reader& reader, bool
slot
->
encodedSize
=
validated
.
size
();
slot
->
dependencies
=
validator
.
makeDependencyArray
(
&
slot
->
dependencyCount
);
slot
->
membersByName
=
validator
.
makeMemberInfoArray
(
&
slot
->
memberCount
);
slot
->
membersByDiscriminant
=
validator
.
makeMembersByDiscriminantArray
();
}
if
(
isPlaceholder
)
{
...
...
@@ -1147,12 +1094,12 @@ _::RawSchema* SchemaLoader::Impl::loadNative(const _::RawSchema* nativeSchema) {
KJ_REQUIRE
(
slot
->
canCastTo
==
nativeSchema
,
"two different compiled-in type have the same type ID"
,
nativeSchema
->
id
,
readMessageUnchecked
<
schema
::
Node
>
(
nativeSchema
->
encodedNode
).
getDisplayName
(),
readMessageUnchecked
<
schema
::
Node
>
(
slot
->
canCastTo
->
encodedNode
).
getDisplayName
());
readMessageUnchecked
<
schema
2
::
Node
>
(
nativeSchema
->
encodedNode
).
getDisplayName
(),
readMessageUnchecked
<
schema
2
::
Node
>
(
slot
->
canCastTo
->
encodedNode
).
getDisplayName
());
return
slot
;
}
else
{
auto
existing
=
readMessageUnchecked
<
schema
::
Node
>
(
slot
->
encodedNode
);
auto
native
=
readMessageUnchecked
<
schema
::
Node
>
(
nativeSchema
->
encodedNode
);
auto
existing
=
readMessageUnchecked
<
schema
2
::
Node
>
(
slot
->
encodedNode
);
auto
native
=
readMessageUnchecked
<
schema
2
::
Node
>
(
nativeSchema
->
encodedNode
);
CompatibilityChecker
checker
(
*
this
);
shouldReplace
=
checker
.
shouldReplace
(
existing
,
native
,
true
);
}
...
...
@@ -1162,20 +1109,30 @@ _::RawSchema* SchemaLoader::Impl::loadNative(const _::RawSchema* nativeSchema) {
_
::
RawSchema
*
result
=
slot
;
if
(
shouldReplace
)
{
// Set the schema to a copy of the native schema.
*
kj
::
implicitCast
<
_
::
RawSchema
*>
(
result
)
=
*
nativeSchema
;
// Set the schema to a copy of the native schema, but make sure not to null out lazyInitializer
// yet.
_
::
RawSchema
temp
=
*
nativeSchema
;
temp
.
lazyInitializer
=
result
->
lazyInitializer
;
*
result
=
temp
;
// Indicate that casting is safe. Note that it's important to set this before recursively
// loading dependencies, so that cycles don't cause infinite loops!
result
->
canCastTo
=
nativeSchema
;
//
Except that w
e need to set the dependency list to point at other loader-owned RawSchemas.
//
W
e need to set the dependency list to point at other loader-owned RawSchemas.
kj
::
ArrayPtr
<
const
_
::
RawSchema
*>
dependencies
=
arena
.
allocateArray
<
const
_
::
RawSchema
*>
(
result
->
dependencyCount
);
for
(
uint
i
=
0
;
i
<
nativeSchema
->
dependencyCount
;
i
++
)
{
dependencies
[
i
]
=
loadNative
(
nativeSchema
->
dependencies
[
i
]);
}
result
->
dependencies
=
dependencies
.
begin
();
// If there is a struct size requirement, we need to make sure that it is satisfied.
auto
reqIter
=
structSizeRequirements
.
find
(
nativeSchema
->
id
);
if
(
reqIter
!=
structSizeRequirements
.
end
())
{
applyStructSizeRequirement
(
result
,
reqIter
->
second
.
dataWordCount
,
reqIter
->
second
.
pointerCount
);
}
}
else
{
// The existing schema is newer.
...
...
@@ -1198,21 +1155,21 @@ _::RawSchema* SchemaLoader::Impl::loadNative(const _::RawSchema* nativeSchema) {
}
_
::
RawSchema
*
SchemaLoader
::
Impl
::
loadEmpty
(
uint64_t
id
,
kj
::
StringPtr
name
,
schema
::
Node
::
Body
::
Which
kind
)
{
uint64_t
id
,
kj
::
StringPtr
name
,
schema
2
::
Node
::
Which
kind
)
{
word
scratch
[
32
];
memset
(
scratch
,
0
,
sizeof
(
scratch
));
MallocMessageBuilder
builder
(
kj
::
arrayPtr
(
scratch
,
sizeof
(
scratch
))
);
auto
node
=
builder
.
initRoot
<
schema
::
Node
>
();
MallocMessageBuilder
builder
(
scratch
);
auto
node
=
builder
.
initRoot
<
schema
2
::
Node
>
();
node
.
setId
(
id
);
node
.
setDisplayName
(
name
);
switch
(
kind
)
{
case
schema
:
:
Node
::
Body
::
STRUCT_NODE
:
node
.
getBody
().
initStructNode
();
break
;
case
schema
:
:
Node
::
Body
::
ENUM_NODE
:
node
.
getBody
().
initEnumNode
(
);
break
;
case
schema
:
:
Node
::
Body
::
INTERFACE_NODE
:
node
.
getBody
().
initInterfaceNode
(
);
break
;
case
schema
2
:
:
Node
::
STRUCT
:
node
.
initStruct
();
break
;
case
schema
2
:
:
Node
::
ENUM
:
node
.
initEnum
(
0
);
break
;
case
schema
2
:
:
Node
::
INTERFACE
:
node
.
initInterface
(
0
);
break
;
case
schema
:
:
Node
::
Body
::
FILE_NOD
E
:
case
schema
:
:
Node
::
Body
::
CONST_NODE
:
case
schema
:
:
Node
::
Body
::
ANNOTATION_NODE
:
case
schema
2
:
:
Node
::
FIL
E
:
case
schema
2
:
:
Node
::
CONST
:
case
schema
2
:
:
Node
::
ANNOTATION
:
KJ_FAIL_REQUIRE
(
"Not a type."
);
break
;
}
...
...
@@ -1243,6 +1200,73 @@ kj::Array<Schema> SchemaLoader::Impl::getAllLoaded() const {
return
result
;
}
void
SchemaLoader
::
Impl
::
requireStructSize
(
uint64_t
id
,
uint
dataWordCount
,
uint
pointerCount
)
{
auto
&
slot
=
structSizeRequirements
[
id
];
slot
.
dataWordCount
=
kj
::
max
(
slot
.
dataWordCount
,
dataWordCount
);
slot
.
pointerCount
=
kj
::
max
(
slot
.
pointerCount
,
pointerCount
);
auto
iter
=
schemas
.
find
(
id
);
if
(
iter
!=
schemas
.
end
())
{
applyStructSizeRequirement
(
iter
->
second
,
dataWordCount
,
pointerCount
);
}
}
kj
::
ArrayPtr
<
word
>
SchemaLoader
::
Impl
::
makeUncheckedNode
(
schema2
::
Node
::
Reader
node
)
{
size_t
size
=
node
.
totalSizeInWords
()
+
1
;
kj
::
ArrayPtr
<
word
>
result
=
arena
.
allocateArray
<
word
>
(
size
);
memset
(
result
.
begin
(),
0
,
size
*
sizeof
(
word
));
copyToUnchecked
(
node
,
result
);
return
result
;
}
kj
::
ArrayPtr
<
word
>
SchemaLoader
::
Impl
::
makeUncheckedNodeEnforcingSizeRequirements
(
schema2
::
Node
::
Reader
node
)
{
if
(
node
.
which
()
==
schema2
::
Node
::
STRUCT
)
{
auto
iter
=
structSizeRequirements
.
find
(
node
.
getId
());
if
(
iter
!=
structSizeRequirements
.
end
())
{
auto
requirement
=
iter
->
second
;
auto
structNode
=
node
.
getStruct
();
if
(
structNode
.
getDataSectionWordSize
()
<
requirement
.
dataWordCount
||
structNode
.
getPointerSectionSize
()
<
requirement
.
pointerCount
)
{
return
rewriteStructNodeWithSizes
(
node
,
requirement
.
dataWordCount
,
requirement
.
pointerCount
);
}
}
}
return
makeUncheckedNode
(
node
);
}
kj
::
ArrayPtr
<
word
>
SchemaLoader
::
Impl
::
rewriteStructNodeWithSizes
(
schema2
::
Node
::
Reader
node
,
uint
dataWordCount
,
uint
pointerCount
)
{
MallocMessageBuilder
builder
;
builder
.
setRoot
(
node
);
auto
root
=
builder
.
getRoot
<
schema2
::
Node
>
();
auto
newStruct
=
root
.
getStruct
();
newStruct
.
setDataSectionWordSize
(
kj
::
max
(
newStruct
.
getDataSectionWordSize
(),
dataWordCount
));
newStruct
.
setPointerSectionSize
(
kj
::
max
(
newStruct
.
getPointerSectionSize
(),
pointerCount
));
return
makeUncheckedNode
(
root
);
}
void
SchemaLoader
::
Impl
::
applyStructSizeRequirement
(
_
::
RawSchema
*
raw
,
uint
dataWordCount
,
uint
pointerCount
)
{
auto
node
=
readMessageUnchecked
<
schema2
::
Node
>
(
raw
->
encodedNode
);
auto
structNode
=
node
.
getStruct
();
if
(
structNode
.
getDataSectionWordSize
()
<
dataWordCount
||
structNode
.
getPointerSectionSize
()
<
pointerCount
)
{
// Sizes need to be increased. Must rewrite.
kj
::
ArrayPtr
<
word
>
words
=
rewriteStructNodeWithSizes
(
node
,
dataWordCount
,
pointerCount
);
// We don't need to re-validate the node because we know this change could not possibly have
// invalidated it. Just remake the unchecked message.
raw
->
encodedNode
=
words
.
begin
();
raw
->
encodedSize
=
words
.
size
();
}
}
void
SchemaLoader
::
InitializerImpl
::
init
(
const
_
::
RawSchema
*
schema
)
const
{
KJ_IF_MAYBE
(
c
,
callback
)
{
c
->
load
(
loader
,
schema
->
id
);
...
...
@@ -1296,11 +1320,11 @@ kj::Maybe<Schema> SchemaLoader::tryGet(uint64_t id) const {
}
}
Schema
SchemaLoader
::
load
(
const
schema
::
Node
::
Reader
&
reader
)
{
Schema
SchemaLoader
::
load
(
const
schema
2
::
Node
::
Reader
&
reader
)
{
return
Schema
(
impl
.
lockExclusive
()
->
get
()
->
load
(
reader
,
false
));
}
Schema
SchemaLoader
::
loadOnce
(
const
schema
::
Node
::
Reader
&
reader
)
const
{
Schema
SchemaLoader
::
loadOnce
(
const
schema
2
::
Node
::
Reader
&
reader
)
const
{
auto
locked
=
impl
.
lockExclusive
();
auto
getResult
=
locked
->
get
()
->
tryGet
(
reader
.
getId
());
if
(
getResult
.
schema
==
nullptr
||
getResult
.
schema
->
lazyInitializer
!=
nullptr
)
{
...
...
c++/src/capnp/schema-loader.h
View file @
1dcb66b1
...
...
@@ -31,6 +31,16 @@
namespace
capnp
{
class
SchemaLoader
{
// Class which can be used to construct Schema objects from schema::Nodes as defined in
// schema.capnp.
//
// It is a bad idea to use this class on untrusted input with exceptions disabled -- you may
// be exposing yourself to denial-of-service attacks, as attackers can easily construct schemas
// that are subtly inconsistent in a way that causes exceptions to be thrown either by
// SchemaLoader or by the dynamic API when the schemas are subsequently used. If you enable and
// properly catch exceptions, you should be OK -- assuming no bugs in the Cap'n Proto
// implementation, of course.
public
:
class
LazyLoadCallback
{
public
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment