1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 // Author: xiaofeng@google.com (Feng Xiao)
9
10 #include "google/protobuf/compiler/java/shared_code_generator.h"
11
12 #include <memory>
13 #include <utility>
14
15 #include "absl/strings/escaping.h"
16 #include "absl/strings/str_cat.h"
17 #include "google/protobuf/compiler/code_generator.h"
18 #include "google/protobuf/compiler/java/helpers.h"
19 #include "google/protobuf/compiler/java/name_resolver.h"
20 #include "google/protobuf/compiler/java/names.h"
21 #include "google/protobuf/compiler/java/options.h"
22 #include "google/protobuf/compiler/retention.h"
23 #include "google/protobuf/compiler/versions.h"
24 #include "google/protobuf/descriptor.h"
25 #include "google/protobuf/descriptor.pb.h"
26 #include "google/protobuf/io/printer.h"
27 #include "google/protobuf/io/zero_copy_stream.h"
28
29 namespace google {
30 namespace protobuf {
31 namespace compiler {
32 namespace java {
33
SharedCodeGenerator(const FileDescriptor * file,const Options & options)34 SharedCodeGenerator::SharedCodeGenerator(const FileDescriptor* file,
35 const Options& options)
36 : name_resolver_(new ClassNameResolver(options)),
37 file_(file),
38 options_(options) {}
39
~SharedCodeGenerator()40 SharedCodeGenerator::~SharedCodeGenerator() {}
41
Generate(GeneratorContext * context,std::vector<std::string> * file_list,std::vector<std::string> * annotation_file_list)42 void SharedCodeGenerator::Generate(
43 GeneratorContext* context, std::vector<std::string>* file_list,
44 std::vector<std::string>* annotation_file_list) {
45 std::string java_package = FileJavaPackage(file_, true, options_);
46 std::string package_dir = JavaPackageToDir(java_package);
47
48 if (HasDescriptorMethods(file_, options_.enforce_lite)) {
49 // Generate descriptors.
50 std::string classname = name_resolver_->GetDescriptorClassName(file_);
51 std::string filename = absl::StrCat(package_dir, classname, ".java");
52 file_list->push_back(filename);
53 std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
54 GeneratedCodeInfo annotations;
55 io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
56 &annotations);
57 std::unique_ptr<io::Printer> printer(new io::Printer(
58 output.get(), '$',
59 options_.annotate_code ? &annotation_collector : nullptr));
60 std::string info_relative_path = absl::StrCat(classname, ".java.pb.meta");
61 std::string info_full_path = absl::StrCat(filename, ".pb.meta");
62 printer->Print(
63 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
64 "// NO CHECKED-IN PROTOBUF "
65 // Intentional line breaker
66 "GENCODE\n"
67 "// source: $filename$\n",
68 "filename", file_->name());
69 if (options_.opensource_runtime) {
70 printer->Print("// Protobuf Java Version: $protobuf_java_version$\n",
71 "protobuf_java_version", PROTOBUF_JAVA_VERSION_STRING);
72 }
73 printer->Print("\n");
74 if (!java_package.empty()) {
75 printer->Print(
76 "package $package$;\n"
77 "\n",
78 "package", java_package);
79 }
80 PrintGeneratedAnnotation(printer.get(), '$',
81 options_.annotate_code ? info_relative_path : "",
82 options_);
83
84
85 printer->Print(
86
87 "public final class $classname$ {\n"
88 " /* This variable is to be called by generated code only. It "
89 "returns\n"
90 " * an incomplete descriptor for internal use only. */\n"
91 " public static com.google.protobuf.Descriptors.FileDescriptor\n"
92 " descriptor;\n",
93 "classname", classname);
94 printer->Annotate("classname", file_->name());
95
96
97 printer->Print(" static {\n");
98 printer->Indent();
99 printer->Indent();
100 GenerateDescriptors(printer.get());
101 PrintGencodeVersionValidator(printer.get(), options_.opensource_runtime,
102 classname);
103 printer->Outdent();
104 printer->Outdent();
105 printer->Print(
106 " }\n"
107 "}\n");
108
109 if (options_.annotate_code) {
110 std::unique_ptr<io::ZeroCopyOutputStream> info_output(
111 context->Open(info_full_path));
112 annotations.SerializeToZeroCopyStream(info_output.get());
113 annotation_file_list->push_back(info_full_path);
114 }
115
116 printer.reset();
117 output.reset();
118 }
119 }
120
GenerateDescriptors(io::Printer * printer)121 void SharedCodeGenerator::GenerateDescriptors(io::Printer* printer) {
122 // Embed the descriptor. We simply serialize the entire FileDescriptorProto
123 // and embed it as a string literal, which is parsed and built into real
124 // descriptors at initialization time. We unfortunately have to put it in
125 // a string literal, not a byte array, because apparently using a literal
126 // byte array causes the Java compiler to generate *instructions* to
127 // initialize each and every byte of the array, e.g. as if you typed:
128 // b[0] = 123; b[1] = 456; b[2] = 789;
129 // This makes huge bytecode files and can easily hit the compiler's internal
130 // code size limits (error "code to large"). String literals are apparently
131 // embedded raw, which is what we want.
132 FileDescriptorProto file_proto = StripSourceRetentionOptions(*file_);
133 // Skip serialized file descriptor proto, which contain non-functional
134 // deviation between editions and legacy syntax (e.g. syntax, features)
135 if (options_.strip_nonfunctional_codegen) {
136 file_proto.Clear();
137 }
138
139 std::string file_data;
140 file_proto.SerializeToString(&file_data);
141
142 printer->Print("java.lang.String[] descriptorData = {\n");
143 printer->Indent();
144
145 // Limit the number of bytes per line.
146 static const int kBytesPerLine = 40;
147 // Limit the number of lines per string part.
148 static const int kLinesPerPart = 400;
149 // Every block of bytes, start a new string literal, in order to avoid the
150 // 64k length limit. Note that this value needs to be <64k.
151 static const int kBytesPerPart = kBytesPerLine * kLinesPerPart;
152 for (int i = 0; i < file_data.size(); i += kBytesPerLine) {
153 if (i > 0) {
154 if (i % kBytesPerPart == 0) {
155 printer->Print(",\n");
156 } else {
157 printer->Print(" +\n");
158 }
159 }
160 printer->Print("\"$data$\"", "data",
161 absl::CEscape(file_data.substr(i, kBytesPerLine)));
162 }
163
164 printer->Outdent();
165 printer->Print("\n};\n");
166
167 // -----------------------------------------------------------------
168 // Find out all dependencies.
169 std::vector<std::pair<std::string, std::string> > dependencies;
170 for (int i = 0; i < file_->dependency_count(); i++) {
171 const absl::string_view filename = file_->dependency(i)->name();
172 std::string package = FileJavaPackage(file_->dependency(i), true, options_);
173 std::string classname =
174 name_resolver_->GetDescriptorClassName(file_->dependency(i));
175 std::string full_name;
176 if (package.empty()) {
177 full_name = classname;
178 } else {
179 full_name = absl::StrCat(package, ".", classname);
180 }
181 dependencies.push_back(std::make_pair(std::string(filename), full_name));
182 }
183
184 // -----------------------------------------------------------------
185 // Invoke internalBuildGeneratedFileFrom() to build the file.
186 printer->Print(
187 "descriptor = com.google.protobuf.Descriptors.FileDescriptor\n"
188 " .internalBuildGeneratedFileFrom(descriptorData,\n");
189 if (options_.opensource_runtime) {
190 printer->Print(
191 " new com.google.protobuf.Descriptors.FileDescriptor[] {\n");
192
193 for (int i = 0; i < dependencies.size(); i++) {
194 const std::string& dependency = dependencies[i].second;
195 printer->Print(" $dependency$.getDescriptor(),\n", "dependency",
196 dependency);
197 }
198 }
199
200 printer->Print(" });\n");
201 }
202
203 } // namespace java
204 } // namespace compiler
205 } // namespace protobuf
206 } // namespace google
207