//===--- SyntaxSerialization.h - Swift Syntax Serialization -----*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file provides the serialization of RawSyntax nodes and their // constituent parts to JSON. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SYNTAX_SERIALIZATION_SYNTAXSERIALIZATION_H #define SWIFT_SYNTAX_SERIALIZATION_SYNTAXSERIALIZATION_H #include "swift/Basic/ByteTreeSerialization.h" #include "swift/Basic/JSONSerialization.h" #include "swift/Basic/StringExtras.h" #include "swift/Syntax/RawSyntax.h" #include "llvm/ADT/StringSwitch.h" #include #include namespace swift { namespace json { /// The associated value will be interpreted as \c bool. If \c true the node IDs /// will not be included in the serialized JSON. static void *DontSerializeNodeIdsUserInfoKey = &DontSerializeNodeIdsUserInfoKey; /// The user info key pointing to a std::unordered_set of IDs of nodes that /// shall be omitted when the tree gets serialized static void *OmitNodesUserInfoKey = &OmitNodesUserInfoKey; /// Serialization traits for SourcePresence. template <> struct ScalarReferenceTraits { static StringRef stringRef(const syntax::SourcePresence &value) { switch (value) { case syntax::SourcePresence::Present: return "\"Present\""; case syntax::SourcePresence::Missing: return "\"Missing\""; } llvm_unreachable("unhandled presence"); } static bool mustQuote(StringRef) { // The string is already quoted. This is more efficient since it does not // check for characters that need to be escaped return false; } }; /// Serialization traits for swift::tok. template <> struct ScalarReferenceTraits { static StringRef stringRef(const tok &value) { switch (value) { #define TOKEN(name) \ case tok::name: return "\"" #name "\""; #include "swift/Syntax/TokenKinds.def" default: llvm_unreachable("Unknown token kind"); } } static bool mustQuote(StringRef) { // The string is already quoted. This is more efficient since it does not // check for characters that need to be escaped return false; } }; /// Serialization traits for Trivia. /// Trivia will serialize as an array of the underlying TriviaPieces. template<> struct ArrayTraits> { static size_t size(Output &out, ArrayRef &seq) { return seq.size(); } static syntax::TriviaPiece & element(Output &out, ArrayRef &seq, size_t index) { return const_cast(seq[index]); } }; /// Serialization traits for RawSyntax list. template<> struct ArrayTraits>> { static size_t size(Output &out, ArrayRef> &seq) { return seq.size(); } static RC & element(Output &out, ArrayRef> &seq, size_t index) { return const_cast &>(seq[index]); } }; /// An adapter struct that provides a nested structure for token content. struct TokenDescription { tok Kind; StringRef Text; }; /// Serialization traits for TokenDescription. /// TokenDescriptions always serialized with a token kind, which is /// the stringified version of their name in the tok:: enum. /// ``` /// { /// "kind": , /// } /// ``` /// /// For tokens that have some kind of text attached, like literals or /// identifiers, the serialized form will also have a "text" key containing /// that text as the value. template<> struct ObjectTraits { static void mapping(Output &out, TokenDescription &value) { out.mapRequired("kind", value.Kind); if (!isTokenTextDetermined(value.Kind)) { out.mapRequired("text", value.Text); } } }; /// Serialization traits for RC. /// This will be different depending if the raw syntax node is a Token or not. /// Token nodes will always have this structure: /// ``` /// { /// "tokenKind": { "kind": , "text": }, /// "leadingTrivia": [ ], /// "trailingTrivia": [ ], /// "presence": <"Present" or "Missing"> /// } /// ``` /// All other raw syntax nodes will have this structure: /// ``` /// { /// "kind": , /// "layout": [ ], /// "presence": <"Present" or "Missing"> /// } /// ``` template<> struct ObjectTraits { static void mapping(Output &out, syntax::RawSyntax &value) { bool dontSerializeIds = (bool)out.getUserInfo()[DontSerializeNodeIdsUserInfoKey]; if (!dontSerializeIds) { auto nodeId = value.getId(); out.mapRequired("id", nodeId); } auto omitNodes = (std::unordered_set *)out.getUserInfo()[OmitNodesUserInfoKey]; if (omitNodes && omitNodes->count(value.getId()) > 0) { bool omitted = true; out.mapRequired("omitted", omitted); return; } if (value.isToken()) { auto tokenKind = value.getTokenKind(); auto text = value.getTokenText(); auto description = TokenDescription { tokenKind, text }; out.mapRequired("tokenKind", description); auto leadingTrivia = value.getLeadingTrivia(); out.mapRequired("leadingTrivia", leadingTrivia); auto trailingTrivia = value.getTrailingTrivia(); out.mapRequired("trailingTrivia", trailingTrivia); } else { auto kind = value.getKind(); out.mapRequired("kind", kind); auto layout = value.getLayout(); out.mapRequired("layout", layout); } auto presence = value.getPresence(); out.mapRequired("presence", presence); } }; template<> struct NullableTraits> { using value_type = syntax::RawSyntax; static bool isNull(RC &value) { return value == nullptr; } static syntax::RawSyntax &get(RC &value) { return *value; } }; } // end namespace json namespace byteTree { /// Increase the major version for every change that is not just adding a new /// field at the end of an object. Older swiftSyntax clients will no longer be /// able to deserialize the format. const uint16_t SYNTAX_TREE_VERSION_MAJOR = 1; // Last change: initial version /// Increase the minor version if only new field has been added at the end of /// an object. Older swiftSyntax clients will still be able to deserialize the /// format. const uint8_t SYNTAX_TREE_VERSION_MINOR = 0; // Last change: initial version // Combine the major and minor version into one. The first three bytes // represent the major version, the last byte the minor version. const uint32_t SYNTAX_TREE_VERSION = SYNTAX_TREE_VERSION_MAJOR << 8 | SYNTAX_TREE_VERSION_MINOR; /// The key for a ByteTree serializion user info of type /// `std::unordered_set *`. Specifies the IDs of syntax nodes that /// shall be omitted when the syntax tree gets serialized. static void *UserInfoKeyReusedNodeIds = &UserInfoKeyReusedNodeIds; /// The key for a ByteTree serializion user info interpreted as `bool`. /// If specified, additional fields will be added to objects in the ByteTree /// to test forward compatibility. static void *UserInfoKeyAddInvalidFields = &UserInfoKeyAddInvalidFields; template <> struct WrapperTypeTraits { static uint8_t numericValue(const tok &Value); static void write(ByteTreeWriter &Writer, const tok &Value, unsigned Index) { Writer.write(numericValue(Value), Index); } }; template <> struct WrapperTypeTraits { static uint8_t numericValue(const syntax::SourcePresence &Presence) { switch (Presence) { case syntax::SourcePresence::Missing: return 0; case syntax::SourcePresence::Present: return 1; } llvm_unreachable("unhandled presence"); } static void write(ByteTreeWriter &Writer, const syntax::SourcePresence &Presence, unsigned Index) { Writer.write(numericValue(Presence), Index); } }; template <> struct ObjectTraits> { static unsigned numFields(const ArrayRef &Trivia, UserInfoMap &UserInfo) { return Trivia.size(); } static void write(ByteTreeWriter &Writer, const ArrayRef &Trivia, UserInfoMap &UserInfo) { for (unsigned I = 0, E = Trivia.size(); I < E; ++I) { Writer.write(Trivia[I], /*Index=*/I); } } }; template <> struct ObjectTraits>> { static unsigned numFields(const ArrayRef> &Layout, UserInfoMap &UserInfo) { return Layout.size(); } static void write(ByteTreeWriter &Writer, const ArrayRef> &Layout, UserInfoMap &UserInfo); }; template <> struct ObjectTraits> { static unsigned numFields(const std::pair &Pair, UserInfoMap &UserInfo) { return 2; } static void write(ByteTreeWriter &Writer, const std::pair &Pair, UserInfoMap &UserInfo) { Writer.write(Pair.first, /*Index=*/0); Writer.write(Pair.second, /*Index=*/1); } }; template <> struct ObjectTraits { enum NodeKind { Token = 0, Layout = 1, Omitted = 2 }; static bool shouldOmitNode(const syntax::RawSyntax &Syntax, UserInfoMap &UserInfo) { if (auto ReusedNodeIds = static_cast *>( UserInfo[UserInfoKeyReusedNodeIds])) { return ReusedNodeIds->count(Syntax.getId()) > 0; } else { return false; } } static NodeKind nodeKind(const syntax::RawSyntax &Syntax, UserInfoMap &UserInfo) { if (shouldOmitNode(Syntax, UserInfo)) { return Omitted; } else if (Syntax.isToken()) { return Token; } else { return Layout; } } static unsigned numFields(const syntax::RawSyntax &Syntax, UserInfoMap &UserInfo) { // FIXME: We know this is never set in production builds. Should we // disable this code altogether in that case // (e.g. if assertions are not enabled?) if (UserInfo[UserInfoKeyAddInvalidFields]) { switch (nodeKind(Syntax, UserInfo)) { case Token: return 7; case Layout: return 6; case Omitted: return 2; } llvm_unreachable("unhandled kind"); } else { switch (nodeKind(Syntax, UserInfo)) { case Token: return 6; case Layout: return 5; case Omitted: return 2; } llvm_unreachable("unhandled kind"); } } static void write(ByteTreeWriter &Writer, const syntax::RawSyntax &Syntax, UserInfoMap &UserInfo) { auto Kind = nodeKind(Syntax, UserInfo); Writer.write(static_cast(Kind), /*Index=*/0); Writer.write(static_cast(Syntax.getId()), /*Index=*/1); switch (Kind) { case Token: Writer.write(Syntax.getPresence(), /*Index=*/2); Writer.write(std::make_pair(Syntax.getTokenKind(), Syntax.getTokenText()), /*Index=*/3); Writer.write(Syntax.getLeadingTrivia(), /*Index=*/4); Writer.write(Syntax.getTrailingTrivia(), /*Index=*/5); // FIXME: We know this is never set in production builds. Should we // disable this code altogether in that case // (e.g. if assertions are not enabled?) if (UserInfo[UserInfoKeyAddInvalidFields]) { // Test adding a new scalar field StringRef Str = "invalid forward compatible field"; Writer.write(Str, /*Index=*/6); } break; case Layout: Writer.write(Syntax.getPresence(), /*Index=*/2); Writer.write(Syntax.getKind(), /*Index=*/3); Writer.write(Syntax.getLayout(), /*Index=*/4); // FIXME: We know this is never set in production builds. Should we // disable this code altogether in that case // (e.g. if assertions are not enabled?) if (UserInfo[UserInfoKeyAddInvalidFields]) { // Test adding a new object auto Piece = syntax::TriviaPiece::spaces(2); ArrayRef SomeTrivia(Piece); Writer.write(SomeTrivia, /*Index=*/5); } break; case Omitted: // Nothing more to write break; } } }; } // end namespace byteTree } // end namespace swift #endif /* SWIFT_SYNTAX_SERIALIZATION_SYNTAXSERIALIZATION_H */