| //===--- DocStructureArray.cpp --------------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "sourcekitd/DocStructureArray.h" |
| #include "DictionaryKeys.h" |
| #include "SourceKit/Core/LLVM.h" |
| #include "SourceKit/Support/UIdent.h" |
| #include "sourcekitd/CompactArray.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace SourceKit; |
| using namespace sourcekitd; |
| |
| namespace { |
| struct Node { |
| // Scalars |
| unsigned Offset; |
| unsigned Length; |
| UIdent Kind; |
| UIdent AccessLevel; |
| UIdent SetterAccessLevel; |
| unsigned NameOffset; |
| unsigned NameLength; |
| unsigned BodyOffset; |
| unsigned BodyLength; |
| unsigned DocOffset; |
| unsigned DocLength; |
| std::string DisplayName; |
| std::string TypeName; |
| std::string RuntimeName; |
| std::string SelectorName; |
| // Arrays |
| unsigned InheritedTypesOffset; |
| unsigned AttrsOffset; |
| |
| // Children |
| struct Element { |
| UIdent Kind; |
| unsigned Offset; |
| unsigned Length; |
| }; |
| SmallVector<Element, 4> elements; |
| SmallVector<unsigned, 4> childIndices; |
| }; |
| } // end anonymous namespace |
| |
| struct DocStructureArrayBuilder::Implementation { |
| typedef CompactArrayBuilder<StringRef> InheritedTypesBuilder; |
| SmallVector<char, 256> inheritedTypesBuffer; |
| typedef CompactArrayBuilder<UIdent, unsigned, unsigned> AttrsBuilder; |
| SmallVector<char, 256> attrsBuffer; |
| typedef CompactArrayBuilder<UIdent, unsigned, unsigned> ElementsBuilder; |
| SmallVector<char, 256> elementsBuffer; |
| typedef CompactArrayBuilder<unsigned> StructureArrayBuilder; |
| SmallVector<char, 256> structureArrayBuffer; |
| |
| CompactArrayBuilder<unsigned, // Offset |
| unsigned, // Length |
| UIdent, // Kind |
| UIdent, // AccessLevel |
| UIdent, // SetterAccessLevel |
| unsigned, // NameOffset |
| unsigned, // NameLength |
| unsigned, // BodyOffset |
| unsigned, // BodyLength |
| unsigned, // DocOffset |
| unsigned, // DocLength |
| Optional<StringRef>, // DisplayName |
| Optional<StringRef>, // TypeName |
| Optional<StringRef>, // RuntimeName |
| Optional<StringRef>, // SelectorName |
| unsigned, // InheritedTypesOffset |
| unsigned, // AttrsOffset |
| unsigned, // ElementsOffset |
| unsigned // ChildrenOffset |
| > |
| structureBuilder; |
| |
| SmallVector<Node, 8> structure; |
| SmallVector<unsigned, 16> topIndices; |
| |
| unsigned addInheritedTypes(ArrayRef<StringRef> inheritedTypes); |
| unsigned addAttrs(ArrayRef<std::tuple<UIdent, unsigned, unsigned>> attrs); |
| unsigned addElements(ArrayRef<Node::Element> elements); |
| unsigned addChildren(ArrayRef<unsigned> offsets); |
| |
| Implementation(); |
| }; |
| |
| DocStructureArrayBuilder::Implementation::Implementation() { |
| // For each kind of compact array, fill in the first entry in the buffer as |
| // empty so we don't duplicate empty arrays anywhere. |
| InheritedTypesBuilder().appendTo(inheritedTypesBuffer); |
| AttrsBuilder().appendTo(attrsBuffer); |
| ElementsBuilder().appendTo(elementsBuffer); |
| StructureArrayBuilder().appendTo(structureArrayBuffer); |
| } |
| |
| unsigned DocStructureArrayBuilder::Implementation::addInheritedTypes( |
| ArrayRef<StringRef> inheritedTypes) { |
| if (inheritedTypes.empty()) |
| return 0; |
| |
| InheritedTypesBuilder builder; |
| for (StringRef type : inheritedTypes) |
| builder.addEntry(type); |
| |
| unsigned offset = inheritedTypesBuffer.size(); |
| builder.appendTo(inheritedTypesBuffer); |
| return offset; |
| } |
| |
| unsigned |
| DocStructureArrayBuilder::Implementation::addAttrs(ArrayRef<std::tuple<UIdent, unsigned, unsigned>> attrs) { |
| if (attrs.empty()) |
| return 0; |
| |
| AttrsBuilder builder; |
| for (auto attr : attrs) { |
| UIdent uid; |
| unsigned offset, length; |
| std::tie(uid, offset, length) = attr; |
| builder.addEntry(uid, offset, length); |
| } |
| |
| unsigned offset = attrsBuffer.size(); |
| builder.appendTo(attrsBuffer); |
| return offset; |
| } |
| |
| unsigned DocStructureArrayBuilder::Implementation::addElements( |
| ArrayRef<Node::Element> elements) { |
| if (elements.empty()) |
| return 0; |
| |
| ElementsBuilder builder; |
| for (auto &element : elements) |
| builder.addEntry(element.Kind, element.Offset, element.Length); |
| |
| unsigned offset = elementsBuffer.size(); |
| builder.appendTo(elementsBuffer); |
| return offset; |
| } |
| |
| unsigned DocStructureArrayBuilder::Implementation::addChildren( |
| ArrayRef<unsigned> offsets) { |
| if (offsets.empty()) |
| return 0; |
| |
| StructureArrayBuilder builder; |
| for (unsigned value : offsets) |
| builder.addEntry(value); |
| |
| unsigned offset = structureArrayBuffer.size(); |
| builder.appendTo(structureArrayBuffer); |
| return offset; |
| } |
| |
| DocStructureArrayBuilder::DocStructureArrayBuilder() |
| : impl(*new Implementation()) {} |
| DocStructureArrayBuilder::~DocStructureArrayBuilder() { delete &impl; } |
| |
| void DocStructureArrayBuilder::beginSubStructure( |
| unsigned Offset, unsigned Length, SourceKit::UIdent Kind, |
| SourceKit::UIdent AccessLevel, SourceKit::UIdent SetterAccessLevel, |
| unsigned NameOffset, unsigned NameLength, unsigned BodyOffset, |
| unsigned BodyLength, unsigned DocOffset, unsigned DocLength, |
| StringRef DisplayName, StringRef TypeName, |
| StringRef RuntimeName, StringRef SelectorName, |
| ArrayRef<StringRef> InheritedTypes, |
| ArrayRef<std::tuple<UIdent, unsigned, unsigned>> Attrs) { |
| |
| Node node = { |
| Offset, |
| Length, |
| Kind, |
| AccessLevel, |
| SetterAccessLevel, |
| NameOffset, |
| NameLength, |
| BodyOffset, |
| BodyLength, |
| DocOffset, |
| DocLength, |
| DisplayName, |
| TypeName, |
| RuntimeName, |
| SelectorName, |
| impl.addInheritedTypes(InheritedTypes), |
| impl.addAttrs(Attrs), |
| {}, // elements |
| {}, // children |
| }; |
| |
| impl.structure.push_back(std::move(node)); |
| } |
| |
| void DocStructureArrayBuilder::addElement(SourceKit::UIdent Kind, |
| unsigned Offset, unsigned Length) { |
| impl.structure.back().elements.push_back({Kind, Offset, Length}); |
| } |
| |
| void DocStructureArrayBuilder::endSubStructure() { |
| Node node = impl.structure.pop_back_val(); |
| unsigned index = impl.structureBuilder.size(); |
| if (!impl.structure.empty()) { |
| impl.structure.back().childIndices.push_back(index); |
| } else { |
| impl.topIndices.push_back(index); |
| } |
| |
| // Canonicalize empty strings to None for the CompactArray. |
| auto str = [](StringRef str) -> Optional<StringRef> { |
| return str.empty() ? None : Optional<StringRef>(str); |
| }; |
| |
| impl.structureBuilder.addEntry( |
| node.Offset, node.Length, node.Kind, node.AccessLevel, |
| node.SetterAccessLevel, node.NameOffset, node.NameLength, node.BodyOffset, |
| node.BodyLength, node.DocOffset, node.DocLength, str(node.DisplayName), |
| str(node.TypeName), str(node.RuntimeName), str(node.SelectorName), |
| node.InheritedTypesOffset, node.AttrsOffset, |
| impl.addElements(node.elements), impl.addChildren(node.childIndices)); |
| } |
| |
| std::unique_ptr<llvm::MemoryBuffer> DocStructureArrayBuilder::createBuffer() { |
| assert(impl.structure.empty()); |
| uint64_t topOffset = impl.addChildren(impl.topIndices); |
| |
| size_t inheritedTypesBufferSize = impl.inheritedTypesBuffer.size(); |
| size_t attrsBufferSize = impl.attrsBuffer.size(); |
| size_t elementsBufferSize = impl.elementsBuffer.size(); |
| size_t structureArrayBufferSize = impl.structureArrayBuffer.size(); |
| size_t structureBufferSize = impl.structureBuilder.sizeInBytes(); |
| |
| // Header: |
| // * offset of each section start (5) |
| // * offset of top structure array (relative to structure array section) (1) |
| size_t headerSize = sizeof(uint64_t) * 6; |
| |
| auto result = llvm::WritableMemoryBuffer::getNewUninitMemBuffer( |
| inheritedTypesBufferSize + attrsBufferSize + elementsBufferSize + |
| structureArrayBufferSize + structureBufferSize + headerSize); |
| |
| char *start = result->getBufferStart(); |
| char *headerPtr = start; |
| char *ptr = start + headerSize; |
| |
| auto addBuffer = [&](ArrayRef<char> buffer) { |
| uint64_t offset = ptr - start; |
| memcpy(headerPtr, &offset, sizeof(offset)); |
| headerPtr += sizeof(offset); |
| auto bytes = sizeof(buffer[0]) * buffer.size(); |
| memcpy(ptr, buffer.data(), bytes); |
| ptr += bytes; |
| }; |
| addBuffer(impl.structureArrayBuffer); |
| SmallVector<char, 256> structureBuffer; |
| impl.structureBuilder.appendTo(structureBuffer); |
| addBuffer(structureBuffer); |
| addBuffer(impl.elementsBuffer); |
| addBuffer(impl.attrsBuffer); |
| addBuffer(impl.inheritedTypesBuffer); |
| |
| assert(ptr == result->getBufferEnd()); |
| assert(headerPtr == start + (headerSize - sizeof(topOffset))); |
| memcpy(headerPtr, &topOffset, sizeof(topOffset)); |
| |
| return std::move(result); |
| } |
| |
| namespace { |
| struct OutNode { |
| // Scalars |
| unsigned Offset; |
| unsigned Length; |
| sourcekitd_uid_t Kind; |
| sourcekitd_uid_t AccessLevel; |
| sourcekitd_uid_t SetterAccessLevel; |
| unsigned NameOffset; |
| unsigned NameLength; |
| unsigned BodyOffset; |
| unsigned BodyLength; |
| unsigned DocOffset; |
| unsigned DocLength; |
| const char *DisplayName; |
| const char *TypeName; |
| const char *RuntimeName; |
| const char *SelectorName; |
| // Arrays |
| unsigned InheritedTypesOffset; |
| unsigned AttrsOffset; |
| unsigned ElementsOffset; |
| unsigned ChildIndicesOffset; |
| }; |
| |
| class DocStructureArrayReader { |
| public: |
| DocStructureArrayReader(void *buffer); |
| |
| OutNode readStructure(size_t index); |
| CompactArrayReader<unsigned> readStructureArray(size_t offset); |
| |
| void *getElementsBuffer(size_t offset) const { |
| return (char *)getElementsBufferStart() + offset; |
| } |
| void *getInheritedTypesBuffer(size_t offset) const { |
| return (char *)getInheritedTypesBufferStart() + offset; |
| } |
| void *getAttrsBuffer(size_t offset) const { |
| return (char *)getAttrsBufferStart() + offset; |
| } |
| |
| private: |
| void *getStructureArrayBufferStart() const { return getBufferStart(0); } |
| void *getStructureBufferStart() const { return getBufferStart(1); } |
| void *getElementsBufferStart() const { return getBufferStart(2); } |
| void *getAttrsBufferStart() const { return getBufferStart(3); } |
| void *getInheritedTypesBufferStart() const { return getBufferStart(4); } |
| size_t getTopStructureArrayOffset() const { return getHeaderValue(5); } |
| |
| uint64_t getHeaderValue(unsigned index) const; |
| void *getBufferStart(unsigned index) const; |
| |
| private: |
| void *buffer; |
| |
| typedef CompactArrayReader<unsigned, // Offset |
| unsigned, // Length |
| sourcekitd_uid_t, // Kind |
| sourcekitd_uid_t, // AccessLevel |
| sourcekitd_uid_t, // SetterAccessLevel |
| unsigned, // NameOffset |
| unsigned, // NameLength |
| unsigned, // BodyOffset |
| unsigned, // BodyLength |
| unsigned, // DocOffset |
| unsigned, // DocLength |
| const char *, // DisplayName |
| const char *, // TypeName |
| const char *, // RuntimeName |
| const char *, // SelectorName |
| unsigned, // InheritedTypesOffset |
| unsigned, // AttrsOffset |
| unsigned, // ElementsOffset |
| unsigned // ChildrenOffset |
| > |
| StructureReader; |
| }; |
| } // end anonymous namespace |
| |
| DocStructureArrayReader::DocStructureArrayReader(void *buffer) |
| : buffer(buffer) {} |
| |
| OutNode DocStructureArrayReader::readStructure(size_t index) { |
| OutNode result; |
| StructureReader reader(getStructureBufferStart()); |
| reader.readEntries( |
| index, result.Offset, result.Length, result.Kind, result.AccessLevel, |
| result.SetterAccessLevel, result.NameOffset, result.NameLength, |
| result.BodyOffset, result.BodyLength, result.DocOffset, result.DocLength, |
| result.DisplayName, result.TypeName, result.RuntimeName, |
| result.SelectorName, result.InheritedTypesOffset, |
| result.AttrsOffset, result.ElementsOffset, result.ChildIndicesOffset); |
| return result; |
| } |
| |
| CompactArrayReader<unsigned> |
| DocStructureArrayReader::readStructureArray(size_t offset) { |
| if (offset == ~size_t(0)) |
| offset = getTopStructureArrayOffset(); |
| void *arrayBuffer = (char *)getStructureArrayBufferStart() + offset; |
| return CompactArrayReader<unsigned>(arrayBuffer); |
| } |
| |
| void *DocStructureArrayReader::getBufferStart(unsigned index) const { |
| return (char *)buffer + getHeaderValue(index); |
| } |
| |
| uint64_t DocStructureArrayReader::getHeaderValue(unsigned index) const { |
| uint64_t headerField; |
| memcpy(&headerField, (uint64_t*)buffer + index, sizeof(headerField)); |
| return headerField; |
| } |
| |
| #define APPLY(K, Ty, Field) \ |
| do { \ |
| sourcekitd_uid_t key = SKDUIDFromUIdent(K); \ |
| sourcekitd_variant_t var = make##Ty##Variant(Field); \ |
| if (!applier(key, var)) \ |
| return false; \ |
| } while (0) |
| |
| namespace { |
| struct ElementReader { |
| typedef CompactArrayReader<sourcekitd_uid_t, unsigned, unsigned> |
| CompactArrayReaderTy; |
| |
| static bool |
| dictionary_apply(void *buffer, size_t index, |
| llvm::function_ref<bool(sourcekitd_uid_t, |
| sourcekitd_variant_t)> applier) { |
| |
| CompactArrayReaderTy reader(buffer); |
| sourcekitd_uid_t kind; |
| unsigned offset; |
| unsigned length; |
| reader.readEntries(index, kind, offset, length); |
| APPLY(KeyKind, UID, kind); |
| APPLY(KeyOffset, Int, offset); |
| APPLY(KeyLength, Int, length); |
| return true; |
| } |
| }; |
| |
| struct InheritedTypeReader { |
| typedef CompactArrayReader<const char *> CompactArrayReaderTy; |
| |
| static bool |
| dictionary_apply(void *buffer, size_t index, |
| llvm::function_ref<bool(sourcekitd_uid_t, |
| sourcekitd_variant_t)> applier) { |
| |
| CompactArrayReaderTy reader(buffer); |
| const char *value = nullptr; |
| reader.readEntries(index, value); |
| if (value) |
| APPLY(KeyName, String, value); |
| return true; |
| } |
| }; |
| |
| struct AttributesReader { |
| typedef CompactArrayReader<sourcekitd_uid_t, unsigned, unsigned> CompactArrayReaderTy; |
| |
| static bool |
| dictionary_apply(void *buffer, size_t index, |
| llvm::function_ref<bool(sourcekitd_uid_t, |
| sourcekitd_variant_t)> applier) { |
| |
| CompactArrayReaderTy reader(buffer); |
| sourcekitd_uid_t value; |
| unsigned offset; |
| unsigned length; |
| reader.readEntries(index, value, offset, length); |
| |
| APPLY(KeyAttribute, UID, value); |
| APPLY(KeyOffset, Int, offset); |
| APPLY(KeyLength, Int, length); |
| return true; |
| } |
| }; |
| |
| struct DocStructureReader { |
| static bool |
| dictionary_apply(void *buffer, size_t index, |
| llvm::function_ref<bool(sourcekitd_uid_t, |
| sourcekitd_variant_t)> applier) { |
| auto reader = DocStructureArrayReader(buffer); |
| auto node = reader.readStructure(index); |
| |
| APPLY(KeyOffset, Int, node.Offset); |
| APPLY(KeyLength, Int, node.Length); |
| APPLY(KeyKind, UID, node.Kind); |
| if (node.AccessLevel) |
| APPLY(KeyAccessLevel, UID, node.AccessLevel); |
| if (node.SetterAccessLevel) |
| APPLY(KeySetterAccessLevel, UID, node.SetterAccessLevel); |
| APPLY(KeyNameOffset, Int, node.NameOffset); |
| APPLY(KeyNameLength, Int, node.NameLength); |
| if (node.BodyOffset || node.BodyLength) { |
| APPLY(KeyBodyOffset, Int, node.BodyOffset); |
| APPLY(KeyBodyLength, Int, node.BodyLength); |
| } |
| if (node.DocOffset || node.DocLength) { |
| APPLY(KeyDocOffset, Int, node.DocOffset); |
| APPLY(KeyDocLength, Int, node.DocLength); |
| } |
| if (node.DisplayName) |
| APPLY(KeyName, String, node.DisplayName); |
| if (node.TypeName) |
| APPLY(KeyTypeName, String, node.TypeName); |
| if (node.RuntimeName) |
| APPLY(KeyRuntimeName, String, node.RuntimeName); |
| if (node.SelectorName) |
| APPLY(KeySelectorName, String, node.SelectorName); |
| |
| #define APPLY_ARRAY(Kind, Buf, Key, Off) \ |
| do { \ |
| sourcekitd_uid_t key = SKDUIDFromUIdent(Key); \ |
| sourcekitd_variant_t var = { \ |
| {(uintptr_t)getVariantFunctionsFor##Kind##Array(), (uintptr_t)Buf, \ |
| Off}}; \ |
| if (!applier(key, var)) \ |
| return false; \ |
| } while (0) |
| |
| if (node.InheritedTypesOffset) { |
| void *buf = reader.getInheritedTypesBuffer(node.InheritedTypesOffset); |
| APPLY_ARRAY(InheritedTypes, buf, KeyInheritedTypes, 0); |
| } |
| if (node.AttrsOffset) { |
| void *buf = reader.getAttrsBuffer(node.AttrsOffset); |
| APPLY_ARRAY(Attributes, buf, KeyAttributes, 0); |
| } |
| if (node.ElementsOffset) { |
| void *buf = reader.getElementsBuffer(node.ElementsOffset); |
| APPLY_ARRAY(DocStructureElement, buf, KeyElements, 0); |
| } |
| if (node.ChildIndicesOffset) { |
| APPLY_ARRAY(DocStructure, buffer, KeySubStructure, |
| node.ChildIndicesOffset); |
| } |
| return true; |
| } |
| }; |
| |
| // data[0] = DocStructureArrayFuncs::funcs |
| // data[1] = buffer for DocStructureArrayReader |
| // data[2] = structure array offset |
| struct DocStructureArrayFuncs { |
| static sourcekitd_variant_type_t get_type(sourcekitd_variant_t var) { |
| return SOURCEKITD_VARIANT_TYPE_ARRAY; |
| } |
| |
| static size_t array_get_count(sourcekitd_variant_t array) { |
| void *buffer = (void *)array.data[1]; |
| size_t offset = array.data[2]; |
| return DocStructureArrayReader(buffer) |
| .readStructureArray(offset) |
| .getCount(); |
| } |
| |
| static sourcekitd_variant_t array_get_value(sourcekitd_variant_t array, |
| size_t index) { |
| void *buffer = (void *)array.data[1]; |
| size_t offset = array.data[2]; |
| |
| auto reader = DocStructureArrayReader(buffer).readStructureArray(offset); |
| assert(index < reader.getCount()); |
| unsigned structureIndex; |
| reader.readEntries(index, structureIndex); |
| |
| return {{(uintptr_t)&CompactVariantFuncs<DocStructureReader>::Funcs, |
| (uintptr_t)buffer, structureIndex}}; |
| } |
| |
| static VariantFunctions funcs; |
| }; |
| } // end anonymous namespace |
| |
| VariantFunctions DocStructureArrayFuncs::funcs = { |
| get_type, |
| nullptr /*AnnotArray_array_apply*/, |
| nullptr /*AnnotArray_array_get_bool*/, |
| array_get_count, |
| nullptr /*AnnotArray_array_get_int64*/, |
| nullptr /*AnnotArray_array_get_string*/, |
| nullptr /*AnnotArray_array_get_uid*/, |
| array_get_value, |
| nullptr /*AnnotArray_bool_get_value*/, |
| nullptr /*AnnotArray_dictionary_apply*/, |
| nullptr /*AnnotArray_dictionary_get_bool*/, |
| nullptr /*AnnotArray_dictionary_get_int64*/, |
| nullptr /*AnnotArray_dictionary_get_string*/, |
| nullptr /*AnnotArray_dictionary_get_value*/, |
| nullptr /*AnnotArray_dictionary_get_uid*/, |
| nullptr /*AnnotArray_string_get_length*/, |
| nullptr /*AnnotArray_string_get_ptr*/, |
| nullptr /*AnnotArray_int64_get_value*/, |
| nullptr /*AnnotArray_uid_get_value*/ |
| }; |
| |
| VariantFunctions *sourcekitd::getVariantFunctionsForDocStructureElementArray() { |
| return &CompactArrayFuncs<ElementReader>::Funcs; |
| } |
| |
| VariantFunctions *sourcekitd::getVariantFunctionsForInheritedTypesArray() { |
| return &CompactArrayFuncs<InheritedTypeReader>::Funcs; |
| } |
| |
| VariantFunctions *sourcekitd::getVariantFunctionsForAttributesArray() { |
| return &CompactArrayFuncs<AttributesReader>::Funcs; |
| } |
| |
| VariantFunctions *sourcekitd::getVariantFunctionsForDocStructureArray() { |
| return &DocStructureArrayFuncs::funcs; |
| } |