Skip to content

Commit 2b62570

Browse files
authored
Merge pull request #33022 from HassanElDesouky/localization-serialization
[Diag] Create a serialized format for Localized Diagnostics
2 parents 1e95349 + 5e13a53 commit 2b62570

File tree

8 files changed

+326
-27
lines changed

8 files changed

+326
-27
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/TypeLoc.h"
2525
#include "llvm/ADT/StringSet.h"
2626
#include "llvm/Support/Allocator.h"
27+
#include "llvm/Support/Path.h"
2728
#include "llvm/Support/VersionTuple.h"
2829

2930
namespace swift {
@@ -742,8 +743,26 @@ namespace swift {
742743
void setLocalization(std::string locale, std::string path) {
743744
assert(!locale.empty());
744745
assert(!path.empty());
745-
localization =
746-
std::make_unique<diag::YAMLLocalizationProducer>(locale, path);
746+
llvm::SmallString<128> filePath(path);
747+
llvm::sys::path::append(filePath, locale);
748+
llvm::sys::path::replace_extension(filePath, ".db");
749+
750+
// If the serialized diagnostics file not available,
751+
// fallback to the `YAML` file.
752+
if (llvm::sys::fs::exists(filePath)) {
753+
if (auto file = llvm::MemoryBuffer::getFile(filePath)) {
754+
localization = std::make_unique<diag::SerializedLocalizationProducer>(
755+
std::move(file.get()));
756+
}
757+
} else {
758+
llvm::sys::path::replace_extension(filePath, ".yaml");
759+
// In case of missing localization files, we should fallback to messages
760+
// from `.def` files.
761+
if (llvm::sys::fs::exists(filePath)) {
762+
localization =
763+
std::make_unique<diag::YAMLLocalizationProducer>(filePath.str());
764+
}
765+
}
747766
}
748767

749768
void ignoreDiagnostic(DiagID id) {

include/swift/AST/LocalizationFormat.h

Lines changed: 134 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
//===--- LocalizationFormat.h - YAML format for Diagnostic Messages ---*-
2-
// C++ -*-===//
1+
//===--- LocalizationFormat.h - Format for Diagnostic Messages --*- C++ -*-===//
32
//
43
// This source file is part of the Swift.org open source project
54
//
@@ -18,16 +17,125 @@
1817
#ifndef SWIFT_LOCALIZATIONFORMAT_H
1918
#define SWIFT_LOCALIZATIONFORMAT_H
2019

20+
#include "llvm/ADT/Hashing.h"
21+
#include "llvm/ADT/STLExtras.h"
2122
#include "llvm/ADT/StringRef.h"
23+
#include "llvm/Bitstream/BitstreamReader.h"
24+
#include "llvm/Support/EndianStream.h"
25+
#include "llvm/Support/MemoryBuffer.h"
26+
#include "llvm/Support/OnDiskHashTable.h"
2227
#include "llvm/Support/YAMLParser.h"
2328
#include "llvm/Support/YAMLTraits.h"
29+
#include "llvm/Support/raw_ostream.h"
30+
#include <cstdint>
31+
#include <memory>
2432
#include <string>
2533
#include <type_traits>
34+
#include <utility>
35+
#include <vector>
2636

2737
namespace swift {
2838
enum class DiagID : uint32_t;
2939

3040
namespace diag {
41+
using namespace llvm::support;
42+
43+
class LocalizationWriterInfo {
44+
public:
45+
using key_type = uint32_t;
46+
using key_type_ref = const uint32_t &;
47+
using data_type = std::string;
48+
using data_type_ref = llvm::StringRef;
49+
using hash_value_type = uint32_t;
50+
using offset_type = uint32_t;
51+
52+
hash_value_type ComputeHash(key_type_ref key) { return llvm::hash_code(key); }
53+
54+
std::pair<offset_type, offset_type> EmitKeyDataLength(llvm::raw_ostream &out,
55+
key_type_ref key,
56+
data_type_ref data) {
57+
offset_type dataLength = static_cast<offset_type>(data.size());
58+
endian::write<offset_type>(out, dataLength, little);
59+
// No need to write the key length; it's constant.
60+
return {sizeof(key_type), dataLength};
61+
}
62+
63+
void EmitKey(llvm::raw_ostream &out, key_type_ref key, unsigned len) {
64+
assert(len == sizeof(key_type));
65+
endian::write<key_type>(out, key, little);
66+
}
67+
68+
void EmitData(llvm::raw_ostream &out, key_type_ref key, data_type_ref data,
69+
unsigned len) {
70+
out << data;
71+
}
72+
};
73+
74+
class LocalizationReaderInfo {
75+
public:
76+
using internal_key_type = uint32_t;
77+
using external_key_type = swift::DiagID;
78+
using data_type = llvm::StringRef;
79+
using hash_value_type = uint32_t;
80+
using offset_type = uint32_t;
81+
82+
internal_key_type GetInternalKey(external_key_type key) {
83+
return static_cast<internal_key_type>(key);
84+
}
85+
86+
external_key_type GetExternalKey(internal_key_type key) {
87+
return static_cast<external_key_type>(key);
88+
}
89+
90+
static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
91+
return lhs == rhs;
92+
}
93+
94+
hash_value_type ComputeHash(internal_key_type key) {
95+
return llvm::hash_code(key);
96+
}
97+
98+
static std::pair<offset_type, offset_type>
99+
ReadKeyDataLength(const unsigned char *&data) {
100+
offset_type dataLength =
101+
endian::readNext<offset_type, little, unaligned>(data);
102+
return {sizeof(uint32_t), dataLength};
103+
}
104+
105+
internal_key_type ReadKey(const unsigned char *data, offset_type length) {
106+
return endian::readNext<internal_key_type, little, unaligned>(data);
107+
}
108+
109+
data_type ReadData(internal_key_type Key, const unsigned char *data,
110+
offset_type length) {
111+
return data_type((const char *)data, length);
112+
}
113+
};
114+
115+
class SerializedLocalizationWriter {
116+
using offset_type = LocalizationWriterInfo::offset_type;
117+
llvm::OnDiskChainedHashTableGenerator<LocalizationWriterInfo> generator;
118+
119+
public:
120+
/// Enqueue the given diagnostic to be included in a serialized translations
121+
/// file.
122+
///
123+
/// \param id The identifier associated with the given diagnostic message e.g.
124+
/// 'cannot_convert_argument'.
125+
/// \param translation The localized diagnostic message for the given
126+
/// identifier.
127+
void insert(swift::DiagID id, llvm::StringRef translation);
128+
129+
/// Write out previously inserted diagnostic translations into the given
130+
/// location.
131+
///
132+
/// \param filePath The location of the serialized diagnostics file. It's
133+
/// supposed to be a file with '.db' postfix.
134+
/// \returns true if all diagnostic
135+
/// messages have been successfully serialized, false otherwise.
136+
bool emit(llvm::StringRef filePath);
137+
};
138+
31139
class LocalizationProducer {
32140
public:
33141
/// If the message isn't available/localized in the current `yaml` file,
@@ -41,9 +149,31 @@ class LocalizationProducer {
41149
};
42150

43151
class YAMLLocalizationProducer final : public LocalizationProducer {
44-
public:
45152
std::vector<std::string> diagnostics;
46-
explicit YAMLLocalizationProducer(std::string locale, std::string path);
153+
154+
public:
155+
explicit YAMLLocalizationProducer(llvm::StringRef filePath);
156+
llvm::StringRef getMessageOr(swift::DiagID id,
157+
llvm::StringRef defaultMessage) const override;
158+
159+
/// Iterate over all of the available (non-empty) translations
160+
/// maintained by this producer, callback gets each translation
161+
/// with its unique identifier.
162+
void forEachAvailable(
163+
llvm::function_ref<void(swift::DiagID, llvm::StringRef)> callback) const;
164+
};
165+
166+
class SerializedLocalizationProducer final : public LocalizationProducer {
167+
using SerializedLocalizationTable =
168+
llvm::OnDiskIterableChainedHashTable<LocalizationReaderInfo>;
169+
using offset_type = LocalizationReaderInfo::offset_type;
170+
std::unique_ptr<llvm::MemoryBuffer> Buffer;
171+
std::unique_ptr<SerializedLocalizationTable> SerializedTable;
172+
173+
public:
174+
explicit SerializedLocalizationProducer(
175+
std::unique_ptr<llvm::MemoryBuffer> buffer);
176+
47177
llvm::StringRef getMessageOr(swift::DiagID id,
48178
llvm::StringRef defaultMessage) const override;
49179
};

lib/AST/LocalizationFormat.cpp

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
//===--- LocalizationFormat.cpp - YAML format for Diagnostic Messages ---*-
2-
// C++ -*-===//
1+
//===-- LocalizationFormat.cpp - Format for Diagnostic Messages -*- C++ -*-===//
32
//
43
// This source file is part of the Swift.org open source project
54
//
@@ -18,11 +17,14 @@
1817
#include "swift/AST/LocalizationFormat.h"
1918
#include "llvm/ADT/SmallString.h"
2019
#include "llvm/ADT/StringRef.h"
20+
#include "llvm/Bitstream/BitstreamReader.h"
2121
#include "llvm/Support/CommandLine.h"
2222
#include "llvm/Support/MemoryBuffer.h"
2323
#include "llvm/Support/YAMLParser.h"
2424
#include "llvm/Support/YAMLTraits.h"
25+
#include <cstdint>
2526
#include <string>
27+
#include <system_error>
2628
#include <type_traits>
2729

2830
namespace {
@@ -68,15 +70,54 @@ template <> struct MappingTraits<DiagnosticNode> {
6870
namespace swift {
6971
namespace diag {
7072

71-
YAMLLocalizationProducer::YAMLLocalizationProducer(std::string locale,
72-
std::string path) {
73-
llvm::SmallString<128> DiagnosticsFilePath(path);
74-
llvm::sys::path::append(DiagnosticsFilePath, locale);
75-
llvm::sys::path::replace_extension(DiagnosticsFilePath, ".yaml");
76-
auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(DiagnosticsFilePath);
77-
// Absence of localizations shouldn't crash the compiler.
78-
if (!FileBufOrErr)
79-
return;
73+
void SerializedLocalizationWriter::insert(swift::DiagID id,
74+
llvm::StringRef translation) {
75+
generator.insert(static_cast<uint32_t>(id), translation);
76+
}
77+
78+
bool SerializedLocalizationWriter::emit(llvm::StringRef filePath) {
79+
assert(llvm::sys::path::extension(filePath) == ".db");
80+
std::error_code error;
81+
llvm::raw_fd_ostream OS(filePath, error, llvm::sys::fs::F_None);
82+
if (OS.has_error()) {
83+
return true;
84+
}
85+
86+
offset_type offset;
87+
{
88+
llvm::support::endian::write<offset_type>(OS, 0, llvm::support::little);
89+
offset = generator.Emit(OS);
90+
}
91+
OS.seek(0);
92+
llvm::support::endian::write(OS, offset, llvm::support::little);
93+
OS.close();
94+
95+
return OS.has_error();
96+
}
97+
98+
SerializedLocalizationProducer::SerializedLocalizationProducer(
99+
std::unique_ptr<llvm::MemoryBuffer> buffer)
100+
: Buffer(std::move(buffer)) {
101+
auto base =
102+
reinterpret_cast<const unsigned char *>(Buffer.get()->getBufferStart());
103+
auto tableOffset = endian::read<offset_type>(base, little);
104+
SerializedTable.reset(SerializedLocalizationTable::Create(
105+
base + tableOffset, base + sizeof(offset_type), base));
106+
}
107+
108+
llvm::StringRef SerializedLocalizationProducer::getMessageOr(
109+
swift::DiagID id, llvm::StringRef defaultMessage) const {
110+
auto value = SerializedTable.get()->find(id);
111+
llvm::StringRef diagnosticMessage((const char *)value.getDataPtr(),
112+
value.getDataLen());
113+
if (diagnosticMessage.empty())
114+
return defaultMessage;
115+
116+
return diagnosticMessage;
117+
}
118+
119+
YAMLLocalizationProducer::YAMLLocalizationProducer(llvm::StringRef filePath) {
120+
auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(filePath);
80121
llvm::MemoryBuffer *document = FileBufOrErr->get();
81122
diag::LocalizationInput yin(document->getBuffer());
82123
yin >> diagnostics;
@@ -93,6 +134,15 @@ YAMLLocalizationProducer::getMessageOr(swift::DiagID id,
93134
return diagnosticMessage;
94135
}
95136

137+
void YAMLLocalizationProducer::forEachAvailable(
138+
llvm::function_ref<void(swift::DiagID, llvm::StringRef)> callback) const {
139+
for (uint32_t i = 0, n = diagnostics.size(); i != n; ++i) {
140+
auto translation = diagnostics[i];
141+
if (!translation.empty())
142+
callback(static_cast<swift::DiagID>(i), translation);
143+
}
144+
}
145+
96146
template <typename T, typename Context>
97147
typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value, void>::type
98148
readYAML(llvm::yaml::IO &io, T &Seq, bool, Context &Ctx) {

localization/CMakeLists.txt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
add_custom_target(diagnostic-translation-database)
1+
add_custom_target(diagnostic-database)
22

3-
add_custom_command(
4-
TARGET diagnostic-translation-database
5-
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
6-
DEPENDS diagnostics
7-
COMMAND "${CMAKE_COMMAND}" -E copy_directory diagnostics/ "${SWIFT_BINARY_DIR}/share/swift/diagnostics/")
3+
add_custom_command(TARGET diagnostic-database
4+
COMMAND
5+
${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/diagnostics/ ${CMAKE_BINARY_DIR}/share/swift/diagnostics/
6+
COMMAND
7+
swift-serialize-diagnostics
8+
--input-file-path ${CMAKE_BINARY_DIR}/share/swift/diagnostics/en.yaml
9+
--output-directory ${CMAKE_BINARY_DIR}/share/swift/diagnostics/
10+
)
811

9-
add_dependencies(swift-frontend diagnostic-translation-database)
12+
add_dependencies(swift-frontend diagnostic-database)
1013

1114
swift_install_in_component(
12-
DIRECTORY diagnostics
13-
DESTINATION "share/swift"
15+
DIRECTORY ${CMAKE_BINARY_DIR}/share/swift/diagnostics/
16+
DESTINATION "share/swift/diagnostics"
1417
COMPONENT compiler)

test/diagnostics/Localization/fr_localization.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// RUN: %target-typecheck-verify-swift -localization-path %S/Inputs -locale fr
1+
// RUN: %empty-directory(%t)
2+
// RUN: swift-serialize-diagnostics --input-file-path=%S/Inputs/fr.yaml --output-directory=%t/
3+
// RUN: swift-serialize-diagnostics --input-file-path=%S/Inputs/en.yaml --output-directory=%t/
4+
// RUN: %target-typecheck-verify-swift -localization-path %t -locale fr
25

36
_ = "HI!
47
// expected-error@-1{{chaîne non terminée littérale}}

tools/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_swift_tool_subdirectory(swift-ide-test)
2222
add_swift_tool_subdirectory(swift-remoteast-test)
2323
add_swift_tool_subdirectory(swift-demangle)
2424
add_swift_tool_subdirectory(swift-demangle-yamldump)
25+
add_swift_tool_subdirectory(swift-serialize-diagnostics)
2526
add_swift_tool_subdirectory(lldb-moduleimport-test)
2627
add_swift_tool_subdirectory(sil-func-extractor)
2728
add_swift_tool_subdirectory(sil-llvm-gen)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
add_swift_host_tool(swift-serialize-diagnostics
2+
swift-serialize-diagnostics.cpp
3+
SWIFT_COMPONENT tools
4+
)
5+
target_link_libraries(swift-serialize-diagnostics
6+
PRIVATE
7+
swiftAST)

0 commit comments

Comments
 (0)