123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942 |
- // Protocol Buffers - Google's data interchange format
- // Copyright 2008 Google Inc. All rights reserved.
- // https://developers.google.com/protocol-buffers/
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- #include <assert.h>
- #include <google/protobuf/compiler/js/js_generator.h>
- #include <google/protobuf/compiler/js/well_known_types_embed.h>
- #include <google/protobuf/compiler/scc.h>
- #include <google/protobuf/descriptor.h>
- #include <google/protobuf/descriptor.pb.h>
- #include <google/protobuf/io/printer.h>
- #include <google/protobuf/io/zero_copy_stream.h>
- #include <google/protobuf/stubs/common.h>
- #include <google/protobuf/stubs/logging.h>
- #include <google/protobuf/stubs/stringprintf.h>
- #include <google/protobuf/stubs/strutil.h>
- #include <algorithm>
- #include <limits>
- #include <map>
- #include <memory>
- #include <string>
- #include <utility>
- #include <vector>
- namespace google {
- namespace protobuf {
- namespace compiler {
- namespace js {
- // Sorted list of JavaScript keywords. These cannot be used as names. If they
- // appear, we prefix them with "pb_".
- const char* kKeyword[] = {
- "abstract", "boolean", "break", "byte", "case",
- "catch", "char", "class", "const", "continue",
- "debugger", "default", "delete", "do", "double",
- "else", "enum", "export", "extends", "false",
- "final", "finally", "float", "for", "function",
- "goto", "if", "implements", "import", "in",
- "instanceof", "int", "interface", "long", "native",
- "new", "null", "package", "private", "protected",
- "public", "return", "short", "static", "super",
- "switch", "synchronized", "this", "throw", "throws",
- "transient", "try", "typeof", "var", "void",
- "volatile", "while", "with",
- };
- static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*);
- namespace {
- // The mode of operation for bytes fields. Historically JSPB always carried
- // bytes as JS {string}, containing base64 content by convention. With binary
- // and proto3 serialization the new convention is to represent it as binary
- // data in Uint8Array. See b/26173701 for background on the migration.
- enum BytesMode {
- BYTES_DEFAULT, // Default type for getBytesField to return.
- BYTES_B64, // Explicitly coerce to base64 string where needed.
- BYTES_U8, // Explicitly coerce to Uint8Array where needed.
- };
- bool IsReserved(const std::string& ident) {
- for (int i = 0; i < kNumKeyword; i++) {
- if (ident == kKeyword[i]) {
- return true;
- }
- }
- return false;
- }
- bool StrEndsWith(StringPiece sp, StringPiece x) {
- return sp.size() >= x.size() && sp.substr(sp.size() - x.size()) == x;
- }
- std::string GetSnakeFilename(const std::string& filename) {
- std::string snake_name = filename;
- ReplaceCharacters(&snake_name, "/", '_');
- return snake_name;
- }
- // Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
- // file foo/bar/baz.js.
- std::string GetJSFilename(const GeneratorOptions& options,
- const std::string& filename) {
- return StripProto(filename) + options.GetFileNameExtension();
- }
- // Given a filename like foo/bar/baz.proto, returns the root directory
- // path ../../
- std::string GetRootPath(const std::string& from_filename,
- const std::string& to_filename) {
- if (to_filename.find("google/protobuf") == 0) {
- // Well-known types (.proto files in the google/protobuf directory) are
- // assumed to come from the 'google-protobuf' npm package. We may want to
- // generalize this exception later by letting others put generated code in
- // their own npm packages.
- return "google-protobuf/";
- }
- size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
- if (slashes == 0) {
- return "./";
- }
- std::string result = "";
- for (size_t i = 0; i < slashes; i++) {
- result += "../";
- }
- return result;
- }
- // Returns the alias we assign to the module of the given .proto filename
- // when importing.
- std::string ModuleAlias(const std::string& filename) {
- // This scheme could technically cause problems if a file includes any 2 of:
- // foo/bar_baz.proto
- // foo_bar_baz.proto
- // foo_bar/baz.proto
- //
- // We'll worry about this problem if/when we actually see it. This name isn't
- // exposed to users so we can change it later if we need to.
- std::string basename = StripProto(filename);
- ReplaceCharacters(&basename, "-", '$');
- ReplaceCharacters(&basename, "/", '_');
- ReplaceCharacters(&basename, ".", '_');
- return basename + "_pb";
- }
- // Returns the fully normalized JavaScript namespace for the given
- // file descriptor's package.
- std::string GetNamespace(const GeneratorOptions& options,
- const FileDescriptor* file) {
- if (!options.namespace_prefix.empty()) {
- return options.namespace_prefix;
- } else if (!file->package().empty()) {
- return "proto." + file->package();
- } else {
- return "proto";
- }
- }
- // Returns the name of the message with a leading dot and taking into account
- // nesting, for example ".OuterMessage.InnerMessage", or returns empty if
- // descriptor is null. This function does not handle namespacing, only message
- // nesting.
- std::string GetNestedMessageName(const Descriptor* descriptor) {
- if (descriptor == NULL) {
- return "";
- }
- std::string result =
- StripPrefixString(descriptor->full_name(), descriptor->file()->package());
- // Add a leading dot if one is not already present.
- if (!result.empty() && result[0] != '.') {
- result = "." + result;
- }
- return result;
- }
- // Returns the path prefix for a message or enumeration that
- // lives under the given file and containing type.
- std::string GetPrefix(const GeneratorOptions& options,
- const FileDescriptor* file_descriptor,
- const Descriptor* containing_type) {
- std::string prefix = GetNamespace(options, file_descriptor) +
- GetNestedMessageName(containing_type);
- if (!prefix.empty()) {
- prefix += ".";
- }
- return prefix;
- }
- // Returns the fully normalized JavaScript path prefix for the given
- // message descriptor.
- std::string GetMessagePathPrefix(const GeneratorOptions& options,
- const Descriptor* descriptor) {
- return GetPrefix(options, descriptor->file(), descriptor->containing_type());
- }
- // Returns the fully normalized JavaScript path for the given
- // message descriptor.
- std::string GetMessagePath(const GeneratorOptions& options,
- const Descriptor* descriptor) {
- return GetMessagePathPrefix(options, descriptor) + descriptor->name();
- }
- // Returns the fully normalized JavaScript path prefix for the given
- // enumeration descriptor.
- std::string GetEnumPathPrefix(const GeneratorOptions& options,
- const EnumDescriptor* enum_descriptor) {
- return GetPrefix(options, enum_descriptor->file(),
- enum_descriptor->containing_type());
- }
- // Returns the fully normalized JavaScript path for the given
- // enumeration descriptor.
- std::string GetEnumPath(const GeneratorOptions& options,
- const EnumDescriptor* enum_descriptor) {
- return GetEnumPathPrefix(options, enum_descriptor) + enum_descriptor->name();
- }
- std::string MaybeCrossFileRef(const GeneratorOptions& options,
- const FileDescriptor* from_file,
- const Descriptor* to_message) {
- if ((options.import_style == GeneratorOptions::kImportCommonJs ||
- options.import_style == GeneratorOptions::kImportCommonJsStrict) &&
- from_file != to_message->file()) {
- // Cross-file ref in CommonJS needs to use the module alias instead of
- // the global name.
- return ModuleAlias(to_message->file()->name()) +
- GetNestedMessageName(to_message->containing_type()) + "." +
- to_message->name();
- } else {
- // Within a single file we use a full name.
- return GetMessagePath(options, to_message);
- }
- }
- std::string SubmessageTypeRef(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
- return MaybeCrossFileRef(options, field->file(), field->message_type());
- }
- // - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields
- // (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate,
- // and with reserved words triggering a "pb_" prefix.
- // - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields
- // (use the name directly), then append "List" if appropriate, then append "$"
- // if resulting name is equal to a reserved word.
- // - Enums: just uppercase.
- // Locale-independent version of ToLower that deals only with ASCII A-Z.
- char ToLowerASCII(char c) {
- if (c >= 'A' && c <= 'Z') {
- return (c - 'A') + 'a';
- } else {
- return c;
- }
- }
- std::vector<std::string> ParseLowerUnderscore(const std::string& input) {
- std::vector<std::string> words;
- std::string running = "";
- for (int i = 0; i < input.size(); i++) {
- if (input[i] == '_') {
- if (!running.empty()) {
- words.push_back(running);
- running.clear();
- }
- } else {
- running += ToLowerASCII(input[i]);
- }
- }
- if (!running.empty()) {
- words.push_back(running);
- }
- return words;
- }
- std::vector<std::string> ParseUpperCamel(const std::string& input) {
- std::vector<std::string> words;
- std::string running = "";
- for (int i = 0; i < input.size(); i++) {
- if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) {
- words.push_back(running);
- running.clear();
- }
- running += ToLowerASCII(input[i]);
- }
- if (!running.empty()) {
- words.push_back(running);
- }
- return words;
- }
- std::string ToLowerCamel(const std::vector<std::string>& words) {
- std::string result;
- for (int i = 0; i < words.size(); i++) {
- std::string word = words[i];
- if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) {
- word[0] = (word[0] - 'A') + 'a';
- } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) {
- word[0] = (word[0] - 'a') + 'A';
- }
- result += word;
- }
- return result;
- }
- std::string ToUpperCamel(const std::vector<std::string>& words) {
- std::string result;
- for (int i = 0; i < words.size(); i++) {
- std::string word = words[i];
- if (word[0] >= 'a' && word[0] <= 'z') {
- word[0] = (word[0] - 'a') + 'A';
- }
- result += word;
- }
- return result;
- }
- // Based on code from descriptor.cc (Thanks Kenton!)
- // Uppercases the entire string, turning ValueName into
- // VALUENAME.
- std::string ToEnumCase(const std::string& input) {
- std::string result;
- result.reserve(input.size());
- for (int i = 0; i < input.size(); i++) {
- if ('a' <= input[i] && input[i] <= 'z') {
- result.push_back(input[i] - 'a' + 'A');
- } else {
- result.push_back(input[i]);
- }
- }
- return result;
- }
- std::string ToLower(const std::string& input) {
- std::string result;
- result.reserve(input.size());
- for (int i = 0; i < input.size(); i++) {
- if ('A' <= input[i] && input[i] <= 'Z') {
- result.push_back(input[i] - 'A' + 'a');
- } else {
- result.push_back(input[i]);
- }
- }
- return result;
- }
- // When we're generating one output file per SCC, this is the filename
- // that top-level extensions should go in.
- // e.g. one proto file (test.proto):
- // package a;
- // extends Foo {
- // ...
- // }
- // If "with_filename" equals true, the extension filename will be
- // "proto.a_test_extensions.js", otherwise will be "proto.a.js"
- std::string GetExtensionFileName(const GeneratorOptions& options,
- const FileDescriptor* file,
- bool with_filename) {
- std::string snake_name = StripProto(GetSnakeFilename(file->name()));
- return options.output_dir + "/" + ToLower(GetNamespace(options, file)) +
- (with_filename ? ("_" + snake_name + "_extensions") : "") +
- options.GetFileNameExtension();
- }
- // When we're generating one output file per SCC, this is the filename
- // that all messages in the SCC should go in.
- // If with_package equals true, filename will have package prefix,
- // If the filename length is longer than 200, the filename will be the
- // SCC's proto filename with suffix "_long_sccs_(index)" (if with_package equals
- // true it still has package prefix)
- std::string GetMessagesFileName(const GeneratorOptions& options, const SCC* scc,
- bool with_package) {
- static std::map<const Descriptor*, std::string>* long_name_dict =
- new std::map<const Descriptor*, std::string>();
- std::string package_base =
- with_package
- ? ToLower(GetNamespace(options, scc->GetRepresentative()->file()) +
- "_")
- : "";
- std::string filename_base = "";
- std::vector<std::string> all_message_names;
- for (auto one_desc : scc->descriptors) {
- if (one_desc->containing_type() == nullptr) {
- all_message_names.push_back(ToLower(one_desc->name()));
- }
- }
- sort(all_message_names.begin(), all_message_names.end());
- for (auto one_message : all_message_names) {
- if (!filename_base.empty()) {
- filename_base += "_";
- }
- filename_base += one_message;
- }
- if (filename_base.size() + package_base.size() > 200) {
- if ((*long_name_dict).find(scc->GetRepresentative()) ==
- (*long_name_dict).end()) {
- std::string snake_name = StripProto(
- GetSnakeFilename(scc->GetRepresentative()->file()->name()));
- (*long_name_dict)[scc->GetRepresentative()] =
- StrCat(snake_name, "_long_sccs_",
- static_cast<uint64>((*long_name_dict).size()));
- }
- filename_base = (*long_name_dict)[scc->GetRepresentative()];
- }
- return options.output_dir + "/" + package_base + filename_base +
- options.GetFileNameExtension();
- }
- // When we're generating one output file per type name, this is the filename
- // that a top-level enum should go in.
- // If with_package equals true, filename will have package prefix.
- std::string GetEnumFileName(const GeneratorOptions& options,
- const EnumDescriptor* desc, bool with_package) {
- return options.output_dir + "/" +
- (with_package ? ToLower(GetNamespace(options, desc->file()) + "_")
- : "") +
- ToLower(desc->name()) + options.GetFileNameExtension();
- }
- // Returns the message/response ID, if set.
- std::string GetMessageId(const Descriptor* desc) { return std::string(); }
- bool IgnoreExtensionField(const FieldDescriptor* field) {
- // Exclude descriptor extensions from output "to avoid clutter" (from original
- // codegen).
- if (!field->is_extension()) return false;
- const FileDescriptor* file = field->containing_type()->file();
- return file->name() == "net/proto2/proto/descriptor.proto" ||
- file->name() == "google/protobuf/descriptor.proto";
- }
- // Used inside Google only -- do not remove.
- bool IsResponse(const Descriptor* desc) { return false; }
- bool IgnoreField(const FieldDescriptor* field) {
- return IgnoreExtensionField(field);
- }
- // Do we ignore this message type?
- bool IgnoreMessage(const Descriptor* d) { return d->options().map_entry(); }
- // Does JSPB ignore this entire oneof? True only if all fields are ignored.
- bool IgnoreOneof(const OneofDescriptor* oneof) {
- if (oneof->is_synthetic()) return true;
- for (int i = 0; i < oneof->field_count(); i++) {
- if (!IgnoreField(oneof->field(i))) {
- return false;
- }
- }
- return true;
- }
- std::string JSIdent(const GeneratorOptions& options,
- const FieldDescriptor* field, bool is_upper_camel,
- bool is_map, bool drop_list) {
- std::string result;
- if (field->type() == FieldDescriptor::TYPE_GROUP) {
- result = is_upper_camel
- ? ToUpperCamel(ParseUpperCamel(field->message_type()->name()))
- : ToLowerCamel(ParseUpperCamel(field->message_type()->name()));
- } else {
- result = is_upper_camel ? ToUpperCamel(ParseLowerUnderscore(field->name()))
- : ToLowerCamel(ParseLowerUnderscore(field->name()));
- }
- if (is_map || field->is_map()) {
- // JSPB-style or proto3-style map.
- result += "Map";
- } else if (!drop_list && field->is_repeated()) {
- // Repeated field.
- result += "List";
- }
- return result;
- }
- std::string JSObjectFieldName(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- std::string name = JSIdent(options, field,
- /* is_upper_camel = */ false,
- /* is_map = */ false,
- /* drop_list = */ false);
- if (IsReserved(name)) {
- name = "pb_" + name;
- }
- return name;
- }
- std::string JSByteGetterSuffix(BytesMode bytes_mode) {
- switch (bytes_mode) {
- case BYTES_DEFAULT:
- return "";
- case BYTES_B64:
- return "B64";
- case BYTES_U8:
- return "U8";
- default:
- assert(false);
- }
- return "";
- }
- // Returns the field name as a capitalized portion of a getter/setter method
- // name, e.g. MyField for .getMyField().
- std::string JSGetterName(const GeneratorOptions& options,
- const FieldDescriptor* field,
- BytesMode bytes_mode = BYTES_DEFAULT,
- bool drop_list = false) {
- std::string name = JSIdent(options, field,
- /* is_upper_camel = */ true,
- /* is_map = */ false, drop_list);
- if (field->type() == FieldDescriptor::TYPE_BYTES) {
- std::string suffix = JSByteGetterSuffix(bytes_mode);
- if (!suffix.empty()) {
- name += "_as" + suffix;
- }
- }
- if (name == "Extension" || name == "JsPbMessageId") {
- // Avoid conflicts with base-class names.
- name += "$";
- }
- return name;
- }
- std::string JSOneofName(const OneofDescriptor* oneof) {
- return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
- }
- // Returns the index corresponding to this field in the JSPB array (underlying
- // data storage array).
- std::string JSFieldIndex(const FieldDescriptor* field) {
- // Determine whether this field is a member of a group. Group fields are a bit
- // wonky: their "containing type" is a message type created just for the
- // group, and that type's parent type has a field with the group-message type
- // as its message type and TYPE_GROUP as its field type. For such fields, the
- // index we use is relative to the field number of the group submessage field.
- // For all other fields, we just use the field number.
- const Descriptor* containing_type = field->containing_type();
- const Descriptor* parent_type = containing_type->containing_type();
- if (parent_type != NULL) {
- for (int i = 0; i < parent_type->field_count(); i++) {
- if (parent_type->field(i)->type() == FieldDescriptor::TYPE_GROUP &&
- parent_type->field(i)->message_type() == containing_type) {
- return StrCat(field->number() - parent_type->field(i)->number());
- }
- }
- }
- return StrCat(field->number());
- }
- std::string JSOneofIndex(const OneofDescriptor* oneof) {
- int index = -1;
- for (int i = 0; i < oneof->containing_type()->oneof_decl_count(); i++) {
- const OneofDescriptor* o = oneof->containing_type()->oneof_decl(i);
- if (o->is_synthetic()) continue;
- // If at least one field in this oneof is not JSPB-ignored, count the oneof.
- for (int j = 0; j < o->field_count(); j++) {
- const FieldDescriptor* f = o->field(j);
- if (!IgnoreField(f)) {
- index++;
- break; // inner loop
- }
- }
- if (o == oneof) {
- break;
- }
- }
- return StrCat(index);
- }
- // Decodes a codepoint in \x0000 -- \xFFFF.
- uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) {
- if (*length == 0) {
- return 0;
- }
- size_t expected = 0;
- if ((*bytes & 0x80) == 0) {
- expected = 1;
- } else if ((*bytes & 0xe0) == 0xc0) {
- expected = 2;
- } else if ((*bytes & 0xf0) == 0xe0) {
- expected = 3;
- } else {
- // Too long -- don't accept.
- *length = 0;
- return 0;
- }
- if (*length < expected) {
- // Not enough bytes -- don't accept.
- *length = 0;
- return 0;
- }
- *length = expected;
- switch (expected) {
- case 1:
- return bytes[0];
- case 2:
- return ((bytes[0] & 0x1F) << 6) | ((bytes[1] & 0x3F) << 0);
- case 3:
- return ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) |
- ((bytes[2] & 0x3F) << 0);
- default:
- return 0;
- }
- }
- // Escapes the contents of a string to be included within double-quotes ("") in
- // JavaScript. The input data should be a UTF-8 encoded C++ string of chars.
- // Returns false if |out| was truncated because |in| contained invalid UTF-8 or
- // codepoints outside the BMP.
- // TODO(b/115551870): Support codepoints outside the BMP.
- bool EscapeJSString(const std::string& in, std::string* out) {
- size_t decoded = 0;
- for (size_t i = 0; i < in.size(); i += decoded) {
- uint16 codepoint = 0;
- // Decode the next UTF-8 codepoint.
- size_t have_bytes = in.size() - i;
- uint8 bytes[3] = {
- static_cast<uint8>(in[i]),
- static_cast<uint8>(((i + 1) < in.size()) ? in[i + 1] : 0),
- static_cast<uint8>(((i + 2) < in.size()) ? in[i + 2] : 0),
- };
- codepoint = DecodeUTF8Codepoint(bytes, &have_bytes);
- if (have_bytes == 0) {
- return false;
- }
- decoded = have_bytes;
- switch (codepoint) {
- case '\'':
- *out += "\\x27";
- break;
- case '"':
- *out += "\\x22";
- break;
- case '<':
- *out += "\\x3c";
- break;
- case '=':
- *out += "\\x3d";
- break;
- case '>':
- *out += "\\x3e";
- break;
- case '&':
- *out += "\\x26";
- break;
- case '\b':
- *out += "\\b";
- break;
- case '\t':
- *out += "\\t";
- break;
- case '\n':
- *out += "\\n";
- break;
- case '\f':
- *out += "\\f";
- break;
- case '\r':
- *out += "\\r";
- break;
- case '\\':
- *out += "\\\\";
- break;
- default:
- // TODO(b/115551870): Once we're supporting codepoints outside the BMP,
- // use a single Unicode codepoint escape if the output language is
- // ECMAScript 2015 or above. Otherwise, use a surrogate pair.
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#String_literals
- if (codepoint >= 0x20 && codepoint <= 0x7e) {
- *out += static_cast<char>(codepoint);
- } else if (codepoint >= 0x100) {
- *out += StringPrintf("\\u%04x", codepoint);
- } else {
- *out += StringPrintf("\\x%02x", codepoint);
- }
- break;
- }
- }
- return true;
- }
- std::string EscapeBase64(const std::string& in) {
- static const char* kAlphabet =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- std::string result;
- for (size_t i = 0; i < in.size(); i += 3) {
- int value = (in[i] << 16) | (((i + 1) < in.size()) ? (in[i + 1] << 8) : 0) |
- (((i + 2) < in.size()) ? (in[i + 2] << 0) : 0);
- result += kAlphabet[(value >> 18) & 0x3f];
- result += kAlphabet[(value >> 12) & 0x3f];
- if ((i + 1) < in.size()) {
- result += kAlphabet[(value >> 6) & 0x3f];
- } else {
- result += '=';
- }
- if ((i + 2) < in.size()) {
- result += kAlphabet[(value >> 0) & 0x3f];
- } else {
- result += '=';
- }
- }
- return result;
- }
- // Post-process the result of SimpleFtoa/SimpleDtoa to *exactly* match the
- // original codegen's formatting (which is just .toString() on java.lang.Double
- // or java.lang.Float).
- std::string PostProcessFloat(std::string result) {
- // If inf, -inf or nan, replace with +Infinity, -Infinity or NaN.
- if (result == "inf") {
- return "Infinity";
- } else if (result == "-inf") {
- return "-Infinity";
- } else if (result == "nan") {
- return "NaN";
- }
- // If scientific notation (e.g., "1e10"), (i) capitalize the "e", (ii)
- // ensure that the mantissa (portion prior to the "e") has at least one
- // fractional digit (after the decimal point), and (iii) strip any unnecessary
- // leading zeroes and/or '+' signs from the exponent.
- std::string::size_type exp_pos = result.find('e');
- if (exp_pos != std::string::npos) {
- std::string mantissa = result.substr(0, exp_pos);
- std::string exponent = result.substr(exp_pos + 1);
- // Add ".0" to mantissa if no fractional part exists.
- if (mantissa.find('.') == std::string::npos) {
- mantissa += ".0";
- }
- // Strip the sign off the exponent and store as |exp_neg|.
- bool exp_neg = false;
- if (!exponent.empty() && exponent[0] == '+') {
- exponent = exponent.substr(1);
- } else if (!exponent.empty() && exponent[0] == '-') {
- exp_neg = true;
- exponent = exponent.substr(1);
- }
- // Strip any leading zeroes off the exponent.
- while (exponent.size() > 1 && exponent[0] == '0') {
- exponent = exponent.substr(1);
- }
- return mantissa + "E" + std::string(exp_neg ? "-" : "") + exponent;
- }
- // Otherwise, this is an ordinary decimal number. Append ".0" if result has no
- // decimal/fractional part in order to match output of original codegen.
- if (result.find('.') == std::string::npos) {
- result += ".0";
- }
- return result;
- }
- std::string FloatToString(float value) {
- std::string result = SimpleFtoa(value);
- return PostProcessFloat(result);
- }
- std::string DoubleToString(double value) {
- std::string result = SimpleDtoa(value);
- return PostProcessFloat(result);
- }
- bool InRealOneof(const FieldDescriptor* field) {
- return field->containing_oneof() &&
- !field->containing_oneof()->is_synthetic();
- }
- // Return true if this is an integral field that should be represented as string
- // in JS.
- bool IsIntegralFieldWithStringJSType(const FieldDescriptor* field) {
- switch (field->cpp_type()) {
- case FieldDescriptor::CPPTYPE_INT64:
- case FieldDescriptor::CPPTYPE_UINT64:
- // The default value of JSType is JS_NORMAL, which behaves the same as
- // JS_NUMBER.
- return field->options().jstype() == FieldOptions::JS_STRING;
- default:
- return false;
- }
- }
- std::string MaybeNumberString(const FieldDescriptor* field,
- const std::string& orig) {
- return IsIntegralFieldWithStringJSType(field) ? ("\"" + orig + "\"") : orig;
- }
- std::string JSFieldDefault(const FieldDescriptor* field) {
- if (field->is_repeated()) {
- return "[]";
- }
- switch (field->cpp_type()) {
- case FieldDescriptor::CPPTYPE_INT32:
- return MaybeNumberString(field, StrCat(field->default_value_int32()));
- case FieldDescriptor::CPPTYPE_UINT32:
- // The original codegen is in Java, and Java protobufs store unsigned
- // integer values as signed integer values. In order to exactly match the
- // output, we need to reinterpret as base-2 signed. Ugh.
- return MaybeNumberString(
- field, StrCat(static_cast<int32>(field->default_value_uint32())));
- case FieldDescriptor::CPPTYPE_INT64:
- return MaybeNumberString(field, StrCat(field->default_value_int64()));
- case FieldDescriptor::CPPTYPE_UINT64:
- // See above note for uint32 -- reinterpreting as signed.
- return MaybeNumberString(
- field, StrCat(static_cast<int64>(field->default_value_uint64())));
- case FieldDescriptor::CPPTYPE_ENUM:
- return StrCat(field->default_value_enum()->number());
- case FieldDescriptor::CPPTYPE_BOOL:
- return field->default_value_bool() ? "true" : "false";
- case FieldDescriptor::CPPTYPE_FLOAT:
- return FloatToString(field->default_value_float());
- case FieldDescriptor::CPPTYPE_DOUBLE:
- return DoubleToString(field->default_value_double());
- case FieldDescriptor::CPPTYPE_STRING:
- if (field->type() == FieldDescriptor::TYPE_STRING) {
- std::string out;
- bool is_valid = EscapeJSString(field->default_value_string(), &out);
- if (!is_valid) {
- // TODO(b/115551870): Decide whether this should be a hard error.
- GOOGLE_LOG(WARNING)
- << "The default value for field " << field->full_name()
- << " was truncated since it contained invalid UTF-8 or"
- " codepoints outside the basic multilingual plane.";
- }
- return "\"" + out + "\"";
- } else { // Bytes
- return "\"" + EscapeBase64(field->default_value_string()) + "\"";
- }
- case FieldDescriptor::CPPTYPE_MESSAGE:
- return "null";
- }
- GOOGLE_LOG(FATAL) << "Shouldn't reach here.";
- return "";
- }
- std::string ProtoTypeName(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- switch (field->type()) {
- case FieldDescriptor::TYPE_BOOL:
- return "bool";
- case FieldDescriptor::TYPE_INT32:
- return "int32";
- case FieldDescriptor::TYPE_UINT32:
- return "uint32";
- case FieldDescriptor::TYPE_SINT32:
- return "sint32";
- case FieldDescriptor::TYPE_FIXED32:
- return "fixed32";
- case FieldDescriptor::TYPE_SFIXED32:
- return "sfixed32";
- case FieldDescriptor::TYPE_INT64:
- return "int64";
- case FieldDescriptor::TYPE_UINT64:
- return "uint64";
- case FieldDescriptor::TYPE_SINT64:
- return "sint64";
- case FieldDescriptor::TYPE_FIXED64:
- return "fixed64";
- case FieldDescriptor::TYPE_SFIXED64:
- return "sfixed64";
- case FieldDescriptor::TYPE_FLOAT:
- return "float";
- case FieldDescriptor::TYPE_DOUBLE:
- return "double";
- case FieldDescriptor::TYPE_STRING:
- return "string";
- case FieldDescriptor::TYPE_BYTES:
- return "bytes";
- case FieldDescriptor::TYPE_GROUP:
- return GetMessagePath(options, field->message_type());
- case FieldDescriptor::TYPE_ENUM:
- return GetEnumPath(options, field->enum_type());
- case FieldDescriptor::TYPE_MESSAGE:
- return GetMessagePath(options, field->message_type());
- default:
- return "";
- }
- }
- std::string JSIntegerTypeName(const FieldDescriptor* field) {
- return IsIntegralFieldWithStringJSType(field) ? "string" : "number";
- }
- std::string JSStringTypeName(const GeneratorOptions& options,
- const FieldDescriptor* field,
- BytesMode bytes_mode) {
- if (field->type() == FieldDescriptor::TYPE_BYTES) {
- switch (bytes_mode) {
- case BYTES_DEFAULT:
- return "(string|Uint8Array)";
- case BYTES_B64:
- return "string";
- case BYTES_U8:
- return "Uint8Array";
- default:
- assert(false);
- }
- }
- return "string";
- }
- std::string JSTypeName(const GeneratorOptions& options,
- const FieldDescriptor* field, BytesMode bytes_mode) {
- switch (field->cpp_type()) {
- case FieldDescriptor::CPPTYPE_BOOL:
- return "boolean";
- case FieldDescriptor::CPPTYPE_INT32:
- return JSIntegerTypeName(field);
- case FieldDescriptor::CPPTYPE_INT64:
- return JSIntegerTypeName(field);
- case FieldDescriptor::CPPTYPE_UINT32:
- return JSIntegerTypeName(field);
- case FieldDescriptor::CPPTYPE_UINT64:
- return JSIntegerTypeName(field);
- case FieldDescriptor::CPPTYPE_FLOAT:
- return "number";
- case FieldDescriptor::CPPTYPE_DOUBLE:
- return "number";
- case FieldDescriptor::CPPTYPE_STRING:
- return JSStringTypeName(options, field, bytes_mode);
- case FieldDescriptor::CPPTYPE_ENUM:
- return GetEnumPath(options, field->enum_type());
- case FieldDescriptor::CPPTYPE_MESSAGE:
- return GetMessagePath(options, field->message_type());
- default:
- return "";
- }
- }
- // Used inside Google only -- do not remove.
- bool UseBrokenPresenceSemantics(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- return false;
- }
- // Returns true for fields that return "null" from accessors when they are
- // unset. This should normally only be true for non-repeated submessages, but we
- // have legacy users who relied on old behavior where accessors behaved this
- // way.
- bool ReturnsNullWhenUnset(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
- field->is_optional()) {
- return true;
- }
- // TODO(haberman): remove this case and unconditionally return false.
- return UseBrokenPresenceSemantics(options, field) && !field->is_repeated() &&
- !field->has_default_value();
- }
- // In a sane world, this would be the same as ReturnsNullWhenUnset(). But in
- // the status quo, some fields declare that they never return null/undefined
- // even though they actually do:
- // * required fields
- // * optional enum fields
- // * proto3 primitive fields.
- bool DeclaredReturnTypeIsNullable(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- if (field->is_required() || field->type() == FieldDescriptor::TYPE_ENUM) {
- return false;
- }
- if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
- field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
- return false;
- }
- return ReturnsNullWhenUnset(options, field);
- }
- bool SetterAcceptsUndefined(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- if (ReturnsNullWhenUnset(options, field)) {
- return true;
- }
- // Broken presence semantics always accepts undefined for setters.
- return UseBrokenPresenceSemantics(options, field);
- }
- bool SetterAcceptsNull(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- if (ReturnsNullWhenUnset(options, field)) {
- return true;
- }
- // With broken presence semantics, fields with defaults accept "null" for
- // setters, but other fields do not. This is a strange quirk of the old
- // codegen.
- return UseBrokenPresenceSemantics(options, field) &&
- field->has_default_value();
- }
- // Returns types which are known to by non-nullable by default.
- // The style guide requires that we omit "!" in this case.
- bool IsPrimitive(const std::string& type) {
- return type == "undefined" || type == "string" || type == "number" ||
- type == "boolean";
- }
- std::string JSFieldTypeAnnotation(const GeneratorOptions& options,
- const FieldDescriptor* field,
- bool is_setter_argument, bool force_present,
- bool singular_if_not_packed,
- BytesMode bytes_mode = BYTES_DEFAULT,
- bool force_singular = false) {
- std::string jstype = JSTypeName(options, field, bytes_mode);
- if (!force_singular && field->is_repeated() &&
- (field->is_packed() || !singular_if_not_packed)) {
- if (field->type() == FieldDescriptor::TYPE_BYTES &&
- bytes_mode == BYTES_DEFAULT) {
- jstype = "(Array<!Uint8Array>|Array<string>)";
- } else {
- if (!IsPrimitive(jstype)) {
- jstype = "!" + jstype;
- }
- jstype = "Array<" + jstype + ">";
- }
- }
- bool is_null_or_undefined = false;
- if (is_setter_argument) {
- if (SetterAcceptsNull(options, field)) {
- jstype = "?" + jstype;
- is_null_or_undefined = true;
- }
- if (SetterAcceptsUndefined(options, field)) {
- jstype += "|undefined";
- is_null_or_undefined = true;
- }
- } else if (force_present) {
- // Don't add null or undefined.
- } else {
- if (DeclaredReturnTypeIsNullable(options, field)) {
- jstype = "?" + jstype;
- is_null_or_undefined = true;
- }
- }
- if (!is_null_or_undefined && !IsPrimitive(jstype)) {
- jstype = "!" + jstype;
- }
- return jstype;
- }
- std::string JSBinaryReaderMethodType(const FieldDescriptor* field) {
- std::string name = field->type_name();
- if (name[0] >= 'a' && name[0] <= 'z') {
- name[0] = (name[0] - 'a') + 'A';
- }
- return IsIntegralFieldWithStringJSType(field) ? (name + "String") : name;
- }
- std::string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
- bool is_writer) {
- std::string name = JSBinaryReaderMethodType(field);
- if (field->is_packed()) {
- name = "Packed" + name;
- } else if (is_writer && field->is_repeated()) {
- name = "Repeated" + name;
- }
- return name;
- }
- std::string JSBinaryReaderMethodName(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- return "jspb.BinaryReader.prototype.read" +
- JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
- }
- std::string JSBinaryWriterMethodName(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- if (field->containing_type() &&
- field->containing_type()->options().message_set_wire_format()) {
- return "jspb.BinaryWriter.prototype.writeMessageSet";
- }
- return "jspb.BinaryWriter.prototype.write" +
- JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
- }
- std::string JSTypeTag(const FieldDescriptor* desc) {
- switch (desc->type()) {
- case FieldDescriptor::TYPE_DOUBLE:
- case FieldDescriptor::TYPE_FLOAT:
- return "Float";
- case FieldDescriptor::TYPE_INT32:
- case FieldDescriptor::TYPE_UINT32:
- case FieldDescriptor::TYPE_INT64:
- case FieldDescriptor::TYPE_UINT64:
- case FieldDescriptor::TYPE_FIXED32:
- case FieldDescriptor::TYPE_FIXED64:
- case FieldDescriptor::TYPE_SINT32:
- case FieldDescriptor::TYPE_SINT64:
- case FieldDescriptor::TYPE_SFIXED32:
- case FieldDescriptor::TYPE_SFIXED64:
- if (IsIntegralFieldWithStringJSType(desc)) {
- return "StringInt";
- } else {
- return "Int";
- }
- case FieldDescriptor::TYPE_BOOL:
- return "Boolean";
- case FieldDescriptor::TYPE_STRING:
- return "String";
- case FieldDescriptor::TYPE_BYTES:
- return "Bytes";
- case FieldDescriptor::TYPE_ENUM:
- return "Enum";
- default:
- assert(false);
- }
- return "";
- }
- bool HasRepeatedFields(const GeneratorOptions& options,
- const Descriptor* desc) {
- for (int i = 0; i < desc->field_count(); i++) {
- if (desc->field(i)->is_repeated() && !desc->field(i)->is_map()) {
- return true;
- }
- }
- return false;
- }
- static const char* kRepeatedFieldArrayName = ".repeatedFields_";
- std::string RepeatedFieldsArrayName(const GeneratorOptions& options,
- const Descriptor* desc) {
- return HasRepeatedFields(options, desc)
- ? (GetMessagePath(options, desc) + kRepeatedFieldArrayName)
- : "null";
- }
- bool HasOneofFields(const Descriptor* desc) {
- for (int i = 0; i < desc->field_count(); i++) {
- if (InRealOneof(desc->field(i))) {
- return true;
- }
- }
- return false;
- }
- static const char* kOneofGroupArrayName = ".oneofGroups_";
- std::string OneofFieldsArrayName(const GeneratorOptions& options,
- const Descriptor* desc) {
- return HasOneofFields(desc)
- ? (GetMessagePath(options, desc) + kOneofGroupArrayName)
- : "null";
- }
- std::string RepeatedFieldNumberList(const GeneratorOptions& options,
- const Descriptor* desc) {
- std::vector<std::string> numbers;
- for (int i = 0; i < desc->field_count(); i++) {
- if (desc->field(i)->is_repeated() && !desc->field(i)->is_map()) {
- numbers.push_back(JSFieldIndex(desc->field(i)));
- }
- }
- return "[" + Join(numbers, ",") + "]";
- }
- std::string OneofGroupList(const Descriptor* desc) {
- // List of arrays (one per oneof), each of which is a list of field indices
- std::vector<std::string> oneof_entries;
- for (int i = 0; i < desc->oneof_decl_count(); i++) {
- const OneofDescriptor* oneof = desc->oneof_decl(i);
- if (IgnoreOneof(oneof)) {
- continue;
- }
- std::vector<std::string> oneof_fields;
- for (int j = 0; j < oneof->field_count(); j++) {
- if (IgnoreField(oneof->field(j))) {
- continue;
- }
- oneof_fields.push_back(JSFieldIndex(oneof->field(j)));
- }
- oneof_entries.push_back("[" + Join(oneof_fields, ",") + "]");
- }
- return "[" + Join(oneof_entries, ",") + "]";
- }
- std::string JSOneofArray(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- return OneofFieldsArrayName(options, field->containing_type()) + "[" +
- JSOneofIndex(field->containing_oneof()) + "]";
- }
- std::string RelativeTypeName(const FieldDescriptor* field) {
- assert(field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM ||
- field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
- // For a field with an enum or message type, compute a name relative to the
- // path name of the message type containing this field.
- std::string package = field->file()->package();
- std::string containing_type = field->containing_type()->full_name() + ".";
- std::string type = (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM)
- ? field->enum_type()->full_name()
- : field->message_type()->full_name();
- // |prefix| is advanced as we find separators '.' past the common package
- // prefix that yield common prefixes in the containing type's name and this
- // type's name.
- int prefix = 0;
- for (int i = 0; i < type.size() && i < containing_type.size(); i++) {
- if (type[i] != containing_type[i]) {
- break;
- }
- if (type[i] == '.' && i >= package.size()) {
- prefix = i + 1;
- }
- }
- return type.substr(prefix);
- }
- std::string JSExtensionsObjectName(const GeneratorOptions& options,
- const FileDescriptor* from_file,
- const Descriptor* desc) {
- if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
- // TODO(haberman): fix this for the kImportCommonJs case.
- return "jspb.Message.messageSetExtensions";
- } else {
- return MaybeCrossFileRef(options, from_file, desc) + ".extensions";
- }
- }
- static const int kMapKeyField = 1;
- static const int kMapValueField = 2;
- const FieldDescriptor* MapFieldKey(const FieldDescriptor* field) {
- assert(field->is_map());
- return field->message_type()->FindFieldByNumber(kMapKeyField);
- }
- const FieldDescriptor* MapFieldValue(const FieldDescriptor* field) {
- assert(field->is_map());
- return field->message_type()->FindFieldByNumber(kMapValueField);
- }
- std::string FieldDefinition(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- if (field->is_map()) {
- const FieldDescriptor* key_field = MapFieldKey(field);
- const FieldDescriptor* value_field = MapFieldValue(field);
- std::string key_type = ProtoTypeName(options, key_field);
- std::string value_type;
- if (value_field->type() == FieldDescriptor::TYPE_ENUM ||
- value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
- value_type = RelativeTypeName(value_field);
- } else {
- value_type = ProtoTypeName(options, value_field);
- }
- return StringPrintf("map<%s, %s> %s = %d;", key_type.c_str(),
- value_type.c_str(), field->name().c_str(),
- field->number());
- } else {
- std::string qualifier =
- field->is_repeated() ? "repeated"
- : (field->is_optional() ? "optional" : "required");
- std::string type, name;
- if (field->type() == FieldDescriptor::TYPE_ENUM ||
- field->type() == FieldDescriptor::TYPE_MESSAGE) {
- type = RelativeTypeName(field);
- name = field->name();
- } else if (field->type() == FieldDescriptor::TYPE_GROUP) {
- type = "group";
- name = field->message_type()->name();
- } else {
- type = ProtoTypeName(options, field);
- name = field->name();
- }
- return StringPrintf("%s %s %s = %d;", qualifier.c_str(), type.c_str(),
- name.c_str(), field->number());
- }
- }
- std::string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) {
- std::string comments;
- if (field->type() == FieldDescriptor::TYPE_BYTES && bytes_mode == BYTES_U8) {
- comments +=
- " * Note that Uint8Array is not supported on all browsers.\n"
- " * @see http://caniuse.com/Uint8Array\n";
- }
- return comments;
- }
- bool ShouldGenerateExtension(const FieldDescriptor* field) {
- return field->is_extension() && !IgnoreField(field);
- }
- bool HasExtensions(const Descriptor* desc) {
- for (int i = 0; i < desc->extension_count(); i++) {
- if (ShouldGenerateExtension(desc->extension(i))) {
- return true;
- }
- }
- for (int i = 0; i < desc->nested_type_count(); i++) {
- if (HasExtensions(desc->nested_type(i))) {
- return true;
- }
- }
- return false;
- }
- bool HasExtensions(const FileDescriptor* file) {
- for (int i = 0; i < file->extension_count(); i++) {
- if (ShouldGenerateExtension(file->extension(i))) {
- return true;
- }
- }
- for (int i = 0; i < file->message_type_count(); i++) {
- if (HasExtensions(file->message_type(i))) {
- return true;
- }
- }
- return false;
- }
- bool HasMap(const GeneratorOptions& options, const Descriptor* desc) {
- for (int i = 0; i < desc->field_count(); i++) {
- if (desc->field(i)->is_map()) {
- return true;
- }
- }
- for (int i = 0; i < desc->nested_type_count(); i++) {
- if (HasMap(options, desc->nested_type(i))) {
- return true;
- }
- }
- return false;
- }
- bool FileHasMap(const GeneratorOptions& options, const FileDescriptor* desc) {
- for (int i = 0; i < desc->message_type_count(); i++) {
- if (HasMap(options, desc->message_type(i))) {
- return true;
- }
- }
- return false;
- }
- bool IsExtendable(const Descriptor* desc) {
- return desc->extension_range_count() > 0;
- }
- // Returns the max index in the underlying data storage array beyond which the
- // extension object is used.
- std::string GetPivot(const Descriptor* desc) {
- static const int kDefaultPivot = 500;
- // Find the max field number
- int max_field_number = 0;
- for (int i = 0; i < desc->field_count(); i++) {
- if (!IgnoreField(desc->field(i)) &&
- desc->field(i)->number() > max_field_number) {
- max_field_number = desc->field(i)->number();
- }
- }
- int pivot = -1;
- if (IsExtendable(desc) || (max_field_number >= kDefaultPivot)) {
- pivot = ((max_field_number + 1) < kDefaultPivot) ? (max_field_number + 1)
- : kDefaultPivot;
- }
- return StrCat(pivot);
- }
- // Whether this field represents presence. For fields with presence, we
- // generate extra methods (clearFoo() and hasFoo()) for this field.
- bool HasFieldPresence(const GeneratorOptions& options,
- const FieldDescriptor* field) {
- // This returns false for repeated fields and maps, but we still do
- // generate clearFoo() methods for these through a special case elsewhere.
- return field->has_presence();
- }
- // We use this to implement the semantics that same file can be generated
- // multiple times, but only the last one keep the short name. Others all use
- // long name with extra information to distinguish (For message and enum, the
- // extra information is package name, for file level extension, the extra
- // information is proto's filename).
- // We never actually write the files, but we keep a set of which descriptors
- // were the final one for a given filename.
- class FileDeduplicator {
- public:
- explicit FileDeduplicator(const GeneratorOptions& options)
- : error_on_conflict_(options.error_on_name_conflict) {}
- // params:
- // filenames: a pair of {short filename, full filename}
- // (short filename don't have extra information, full filename
- // contains extra information)
- // desc: The Descriptor or SCC pointer or EnumDescriptor.
- // error: The returned error information.
- bool AddFile(const std::pair<std::string, std::string> filenames,
- const void* desc, std::string* error) {
- if (descs_by_shortname_.find(filenames.first) !=
- descs_by_shortname_.end()) {
- if (error_on_conflict_) {
- *error = "Name conflict: file name " + filenames.first +
- " would be generated by two descriptors";
- return false;
- }
- // Change old pointer's actual name to full name.
- auto short_name_desc = descs_by_shortname_[filenames.first];
- allowed_descs_actual_name_[short_name_desc] =
- allowed_descs_full_name_[short_name_desc];
- }
- descs_by_shortname_[filenames.first] = desc;
- allowed_descs_actual_name_[desc] = filenames.first;
- allowed_descs_full_name_[desc] = filenames.second;
- return true;
- }
- void GetAllowedMap(std::map<const void*, std::string>* allowed_set) {
- *allowed_set = allowed_descs_actual_name_;
- }
- private:
- bool error_on_conflict_;
- // The map that restores all the descs that are using short name as filename.
- std::map<std::string, const void*> descs_by_shortname_;
- // The final actual filename map.
- std::map<const void*, std::string> allowed_descs_actual_name_;
- // The full name map.
- std::map<const void*, std::string> allowed_descs_full_name_;
- };
- void DepthFirstSearch(const FileDescriptor* file,
- std::vector<const FileDescriptor*>* list,
- std::set<const FileDescriptor*>* seen) {
- if (!seen->insert(file).second) {
- return;
- }
- // Add all dependencies.
- for (int i = 0; i < file->dependency_count(); i++) {
- DepthFirstSearch(file->dependency(i), list, seen);
- }
- // Add this file.
- list->push_back(file);
- }
- // A functor for the predicate to remove_if() below. Returns true if a given
- // FileDescriptor is not in the given set.
- class NotInSet {
- public:
- explicit NotInSet(const std::set<const FileDescriptor*>& file_set)
- : file_set_(file_set) {}
- bool operator()(const FileDescriptor* file) {
- return file_set_.count(file) == 0;
- }
- private:
- const std::set<const FileDescriptor*>& file_set_;
- };
- // This function generates an ordering of the input FileDescriptors that matches
- // the logic of the old code generator. The order is significant because two
- // different input files can generate the same output file, and the last one
- // needs to win.
- void GenerateJspbFileOrder(const std::vector<const FileDescriptor*>& input,
- std::vector<const FileDescriptor*>* ordered) {
- // First generate an ordering of all reachable files (including dependencies)
- // with depth-first search. This mimics the behavior of --include_imports,
- // which is what the old codegen used.
- ordered->clear();
- std::set<const FileDescriptor*> seen;
- std::set<const FileDescriptor*> input_set;
- for (int i = 0; i < input.size(); i++) {
- DepthFirstSearch(input[i], ordered, &seen);
- input_set.insert(input[i]);
- }
- // Now remove the entries that are not actually in our input list.
- ordered->erase(
- std::remove_if(ordered->begin(), ordered->end(), NotInSet(input_set)),
- ordered->end());
- }
- // If we're generating code in file-per-type mode, avoid overwriting files
- // by choosing the last descriptor that writes each filename and permitting
- // only those to generate code.
- struct DepsGenerator {
- std::vector<const Descriptor*> operator()(const Descriptor* desc) const {
- std::vector<const Descriptor*> deps;
- auto maybe_add = [&](const Descriptor* d) {
- if (d) deps.push_back(d);
- };
- for (int i = 0; i < desc->field_count(); i++) {
- if (!IgnoreField(desc->field(i))) {
- maybe_add(desc->field(i)->message_type());
- }
- }
- for (int i = 0; i < desc->extension_count(); i++) {
- maybe_add(desc->extension(i)->message_type());
- maybe_add(desc->extension(i)->containing_type());
- }
- for (int i = 0; i < desc->nested_type_count(); i++) {
- maybe_add(desc->nested_type(i));
- }
- maybe_add(desc->containing_type());
- return deps;
- }
- };
- bool GenerateJspbAllowedMap(const GeneratorOptions& options,
- const std::vector<const FileDescriptor*>& files,
- std::map<const void*, std::string>* allowed_set,
- SCCAnalyzer<DepsGenerator>* analyzer,
- std::string* error) {
- std::vector<const FileDescriptor*> files_ordered;
- GenerateJspbFileOrder(files, &files_ordered);
- // Choose the last descriptor for each filename.
- FileDeduplicator dedup(options);
- std::set<const SCC*> added;
- for (int i = 0; i < files_ordered.size(); i++) {
- for (int j = 0; j < files_ordered[i]->message_type_count(); j++) {
- const Descriptor* desc = files_ordered[i]->message_type(j);
- if (added.insert(analyzer->GetSCC(desc)).second &&
- !dedup.AddFile(
- std::make_pair(
- GetMessagesFileName(options, analyzer->GetSCC(desc), false),
- GetMessagesFileName(options, analyzer->GetSCC(desc), true)),
- analyzer->GetSCC(desc), error)) {
- return false;
- }
- }
- for (int j = 0; j < files_ordered[i]->enum_type_count(); j++) {
- const EnumDescriptor* desc = files_ordered[i]->enum_type(j);
- if (!dedup.AddFile(std::make_pair(GetEnumFileName(options, desc, false),
- GetEnumFileName(options, desc, true)),
- desc, error)) {
- return false;
- }
- }
- // Pull out all free-floating extensions and generate files for those too.
- bool has_extension = false;
- for (int j = 0; j < files_ordered[i]->extension_count(); j++) {
- if (ShouldGenerateExtension(files_ordered[i]->extension(j))) {
- has_extension = true;
- }
- }
- if (has_extension) {
- if (!dedup.AddFile(
- std::make_pair(
- GetExtensionFileName(options, files_ordered[i], false),
- GetExtensionFileName(options, files_ordered[i], true)),
- files_ordered[i], error)) {
- return false;
- }
- }
- }
- dedup.GetAllowedMap(allowed_set);
- return true;
- }
- // Embeds base64 encoded GeneratedCodeInfo proto in a comment at the end of
- // file.
- void EmbedCodeAnnotations(const GeneratedCodeInfo& annotations,
- io::Printer* printer) {
- // Serialize annotations proto into base64 string.
- std::string meta_content;
- annotations.SerializeToString(&meta_content);
- std::string meta_64;
- Base64Escape(meta_content, &meta_64);
- // Print base64 encoded annotations at the end of output file in
- // a comment.
- printer->Print("\n// Below is base64 encoded GeneratedCodeInfo proto");
- printer->Print("\n// $encoded_proto$\n", "encoded_proto", meta_64);
- }
- bool IsWellKnownTypeFile(const FileDescriptor* file) {
- return HasPrefixString(file->name(), "google/protobuf/");
- }
- } // anonymous namespace
- void Generator::GenerateHeader(const GeneratorOptions& options,
- const FileDescriptor* file,
- io::Printer* printer) const {
- if (file != nullptr) {
- printer->Print("// source: $filename$\n", "filename", file->name());
- }
- printer->Print(
- "/**\n"
- " * @fileoverview\n"
- " * @enhanceable\n"
- // TODO(b/152440355): requireType/requires diverged from internal version.
- " * @suppress {missingRequire} reports error on implicit type usages.\n"
- " * @suppress {messageConventions} JS Compiler reports an "
- "error if a variable or\n"
- " * field starts with 'MSG_' and isn't a translatable "
- "message.\n"
- " * @public\n"
- " */\n"
- "// GENERATED CODE -- DO NOT EDIT!\n"
- "/* eslint-disable */\n"
- "// @ts-nocheck\n"
- "\n");
- }
- void Generator::FindProvidesForFile(const GeneratorOptions& options,
- io::Printer* printer,
- const FileDescriptor* file,
- std::set<std::string>* provided) const {
- for (int i = 0; i < file->message_type_count(); i++) {
- FindProvidesForMessage(options, printer, file->message_type(i), provided);
- }
- for (int i = 0; i < file->enum_type_count(); i++) {
- FindProvidesForEnum(options, printer, file->enum_type(i), provided);
- }
- }
- void Generator::FindProvides(const GeneratorOptions& options,
- io::Printer* printer,
- const std::vector<const FileDescriptor*>& files,
- std::set<std::string>* provided) const {
- for (int i = 0; i < files.size(); i++) {
- FindProvidesForFile(options, printer, files[i], provided);
- }
- printer->Print("\n");
- }
- void FindProvidesForOneOfEnum(const GeneratorOptions& options,
- const OneofDescriptor* oneof,
- std::set<std::string>* provided) {
- std::string name = GetMessagePath(options, oneof->containing_type()) + "." +
- JSOneofName(oneof) + "Case";
- provided->insert(name);
- }
- void FindProvidesForOneOfEnums(const GeneratorOptions& options,
- io::Printer* printer, const Descriptor* desc,
- std::set<std::string>* provided) {
- if (HasOneofFields(desc)) {
- for (int i = 0; i < desc->oneof_decl_count(); i++) {
- if (IgnoreOneof(desc->oneof_decl(i))) {
- continue;
- }
- FindProvidesForOneOfEnum(options, desc->oneof_decl(i), provided);
- }
- }
- }
- void Generator::FindProvidesForMessage(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc,
- std::set<std::string>* provided) const {
- if (IgnoreMessage(desc)) {
- return;
- }
- std::string name = GetMessagePath(options, desc);
- provided->insert(name);
- for (int i = 0; i < desc->enum_type_count(); i++) {
- FindProvidesForEnum(options, printer, desc->enum_type(i), provided);
- }
- FindProvidesForOneOfEnums(options, printer, desc, provided);
- for (int i = 0; i < desc->nested_type_count(); i++) {
- FindProvidesForMessage(options, printer, desc->nested_type(i), provided);
- }
- }
- void Generator::FindProvidesForEnum(const GeneratorOptions& options,
- io::Printer* printer,
- const EnumDescriptor* enumdesc,
- std::set<std::string>* provided) const {
- std::string name = GetEnumPath(options, enumdesc);
- provided->insert(name);
- }
- void Generator::FindProvidesForFields(
- const GeneratorOptions& options, io::Printer* printer,
- const std::vector<const FieldDescriptor*>& fields,
- std::set<std::string>* provided) const {
- for (int i = 0; i < fields.size(); i++) {
- const FieldDescriptor* field = fields[i];
- if (IgnoreField(field)) {
- continue;
- }
- std::string name = GetNamespace(options, field->file()) + "." +
- JSObjectFieldName(options, field);
- provided->insert(name);
- }
- }
- void Generator::GenerateProvides(const GeneratorOptions& options,
- io::Printer* printer,
- std::set<std::string>* provided) const {
- for (std::set<std::string>::iterator it = provided->begin();
- it != provided->end(); ++it) {
- if (options.import_style == GeneratorOptions::kImportClosure) {
- printer->Print("goog.provide('$name$');\n", "name", *it);
- } else {
- // We aren't using Closure's import system, but we use goog.exportSymbol()
- // to construct the expected tree of objects, eg.
- //
- // goog.exportSymbol('foo.bar.Baz', null, this);
- //
- // // Later generated code expects foo.bar = {} to exist:
- // foo.bar.Baz = function() { /* ... */ }
- // Do not use global scope in strict mode
- if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
- std::string namespaceObject = *it;
- // Remove "proto." from the namespace object
- GOOGLE_CHECK_EQ(0, namespaceObject.compare(0, 6, "proto."));
- namespaceObject.erase(0, 6);
- printer->Print("goog.exportSymbol('$name$', null, proto);\n", "name",
- namespaceObject);
- } else {
- printer->Print("goog.exportSymbol('$name$', null, global);\n", "name",
- *it);
- }
- }
- }
- }
- void Generator::GenerateRequiresForSCC(const GeneratorOptions& options,
- io::Printer* printer, const SCC* scc,
- std::set<std::string>* provided) const {
- std::set<std::string> required;
- std::set<std::string> forwards;
- bool have_message = false;
- bool has_extension = false;
- bool has_map = false;
- for (auto desc : scc->descriptors) {
- if (desc->containing_type() == nullptr) {
- FindRequiresForMessage(options, desc, &required, &forwards,
- &have_message);
- has_extension = (has_extension || HasExtensions(desc));
- has_map = (has_map || HasMap(options, desc));
- }
- }
- GenerateRequiresImpl(options, printer, &required, &forwards, provided,
- /* require_jspb = */ have_message,
- /* require_extension = */ has_extension,
- /* require_map = */ has_map);
- }
- void Generator::GenerateRequiresForLibrary(
- const GeneratorOptions& options, io::Printer* printer,
- const std::vector<const FileDescriptor*>& files,
- std::set<std::string>* provided) const {
- GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::kImportClosure);
- // For Closure imports we need to import every message type individually.
- std::set<std::string> required;
- std::set<std::string> forwards;
- bool have_extensions = false;
- bool have_map = false;
- bool have_message = false;
- for (int i = 0; i < files.size(); i++) {
- for (int j = 0; j < files[i]->message_type_count(); j++) {
- const Descriptor* desc = files[i]->message_type(j);
- if (!IgnoreMessage(desc)) {
- FindRequiresForMessage(options, desc, &required, &forwards,
- &have_message);
- }
- }
- if (!have_extensions && HasExtensions(files[i])) {
- have_extensions = true;
- }
- if (!have_map && FileHasMap(options, files[i])) {
- have_map = true;
- }
- for (int j = 0; j < files[i]->extension_count(); j++) {
- const FieldDescriptor* extension = files[i]->extension(j);
- if (IgnoreField(extension)) {
- continue;
- }
- if (extension->containing_type()->full_name() !=
- "google.protobuf.bridge.MessageSet") {
- required.insert(GetMessagePath(options, extension->containing_type()));
- }
- FindRequiresForField(options, extension, &required, &forwards);
- have_extensions = true;
- }
- }
- GenerateRequiresImpl(options, printer, &required, &forwards, provided,
- /* require_jspb = */ have_message,
- /* require_extension = */ have_extensions,
- /* require_map = */ have_map);
- }
- void Generator::GenerateRequiresForExtensions(
- const GeneratorOptions& options, io::Printer* printer,
- const std::vector<const FieldDescriptor*>& fields,
- std::set<std::string>* provided) const {
- std::set<std::string> required;
- std::set<std::string> forwards;
- for (int i = 0; i < fields.size(); i++) {
- const FieldDescriptor* field = fields[i];
- if (IgnoreField(field)) {
- continue;
- }
- FindRequiresForExtension(options, field, &required, &forwards);
- }
- GenerateRequiresImpl(options, printer, &required, &forwards, provided,
- /* require_jspb = */ false,
- /* require_extension = */ fields.size() > 0,
- /* require_map = */ false);
- }
- void Generator::GenerateRequiresImpl(const GeneratorOptions& options,
- io::Printer* printer,
- std::set<std::string>* required,
- std::set<std::string>* forwards,
- std::set<std::string>* provided,
- bool require_jspb, bool require_extension,
- bool require_map) const {
- if (require_jspb) {
- required->insert("jspb.Message");
- required->insert("jspb.BinaryReader");
- required->insert("jspb.BinaryWriter");
- }
- if (require_extension) {
- required->insert("jspb.ExtensionFieldBinaryInfo");
- required->insert("jspb.ExtensionFieldInfo");
- }
- if (require_map) {
- required->insert("jspb.Map");
- }
- std::set<std::string>::iterator it;
- for (it = required->begin(); it != required->end(); ++it) {
- if (provided->find(*it) != provided->end()) {
- continue;
- }
- printer->Print("goog.require('$name$');\n", "name", *it);
- }
- printer->Print("\n");
- for (it = forwards->begin(); it != forwards->end(); ++it) {
- if (provided->find(*it) != provided->end()) {
- continue;
- }
- printer->Print("goog.forwardDeclare('$name$');\n", "name", *it);
- }
- }
- bool NamespaceOnly(const Descriptor* desc) { return false; }
- void Generator::FindRequiresForMessage(const GeneratorOptions& options,
- const Descriptor* desc,
- std::set<std::string>* required,
- std::set<std::string>* forwards,
- bool* have_message) const {
- if (!NamespaceOnly(desc)) {
- *have_message = true;
- for (int i = 0; i < desc->field_count(); i++) {
- const FieldDescriptor* field = desc->field(i);
- if (IgnoreField(field)) {
- continue;
- }
- FindRequiresForField(options, field, required, forwards);
- }
- }
- for (int i = 0; i < desc->extension_count(); i++) {
- const FieldDescriptor* field = desc->extension(i);
- if (IgnoreField(field)) {
- continue;
- }
- FindRequiresForExtension(options, field, required, forwards);
- }
- for (int i = 0; i < desc->nested_type_count(); i++) {
- FindRequiresForMessage(options, desc->nested_type(i), required, forwards,
- have_message);
- }
- }
- void Generator::FindRequiresForField(const GeneratorOptions& options,
- const FieldDescriptor* field,
- std::set<std::string>* required,
- std::set<std::string>* forwards) const {
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
- // N.B.: file-level extensions with enum type do *not* create
- // dependencies, as per original codegen.
- !(field->is_extension() && field->extension_scope() == nullptr)) {
- if (options.add_require_for_enums) {
- required->insert(GetEnumPath(options, field->enum_type()));
- } else {
- forwards->insert(GetEnumPath(options, field->enum_type()));
- }
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- if (!IgnoreMessage(field->message_type())) {
- required->insert(GetMessagePath(options, field->message_type()));
- }
- }
- }
- void Generator::FindRequiresForExtension(
- const GeneratorOptions& options, const FieldDescriptor* field,
- std::set<std::string>* required, std::set<std::string>* forwards) const {
- if (field->containing_type()->full_name() !=
- "google.protobuf.bridge.MessageSet") {
- required->insert(GetMessagePath(options, field->containing_type()));
- }
- FindRequiresForField(options, field, required, forwards);
- }
- void Generator::GenerateTestOnly(const GeneratorOptions& options,
- io::Printer* printer) const {
- if (options.testonly) {
- printer->Print("goog.setTestOnly();\n\n");
- }
- printer->Print("\n");
- }
- void Generator::GenerateClassesAndEnums(const GeneratorOptions& options,
- io::Printer* printer,
- const FileDescriptor* file) const {
- for (int i = 0; i < file->message_type_count(); i++) {
- GenerateClassConstructorAndDeclareExtensionFieldInfo(options, printer,
- file->message_type(i));
- }
- for (int i = 0; i < file->message_type_count(); i++) {
- GenerateClass(options, printer, file->message_type(i));
- }
- for (int i = 0; i < file->enum_type_count(); i++) {
- GenerateEnum(options, printer, file->enum_type(i));
- }
- }
- void Generator::GenerateClass(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- if (IgnoreMessage(desc)) {
- return;
- }
- if (!NamespaceOnly(desc)) {
- printer->Print("\n");
- GenerateClassFieldInfo(options, printer, desc);
- GenerateClassToObject(options, printer, desc);
- // These must come *before* the extension-field info generation in
- // GenerateClassRegistration so that references to the binary
- // serialization/deserialization functions may be placed in the extension
- // objects.
- GenerateClassDeserializeBinary(options, printer, desc);
- GenerateClassSerializeBinary(options, printer, desc);
- }
- // Recurse on nested types. These must come *before* the extension-field
- // info generation in GenerateClassRegistration so that extensions that
- // reference nested types proceed the definitions of the nested types.
- for (int i = 0; i < desc->enum_type_count(); i++) {
- GenerateEnum(options, printer, desc->enum_type(i));
- }
- for (int i = 0; i < desc->nested_type_count(); i++) {
- GenerateClass(options, printer, desc->nested_type(i));
- }
- if (!NamespaceOnly(desc)) {
- GenerateClassRegistration(options, printer, desc);
- GenerateClassFields(options, printer, desc);
- if (options.import_style != GeneratorOptions::kImportClosure) {
- for (int i = 0; i < desc->extension_count(); i++) {
- GenerateExtension(options, printer, desc->extension(i));
- }
- }
- }
- }
- void Generator::GenerateClassConstructor(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- printer->Print(
- "/**\n"
- " * Generated by JsPbCodeGenerator.\n"
- " * @param {Array=} opt_data Optional initial data array, typically "
- "from a\n"
- " * server response, or constructed directly in Javascript. The array "
- "is used\n"
- " * in place and becomes part of the constructed object. It is not "
- "cloned.\n"
- " * If no data is provided, the constructed object will be empty, but "
- "still\n"
- " * valid.\n"
- " * @extends {jspb.Message}\n"
- " * @constructor\n"
- " */\n"
- "$classprefix$$classname$ = function(opt_data) {\n",
- "classprefix", GetMessagePathPrefix(options, desc), "classname",
- desc->name());
- printer->Annotate("classname", desc);
- std::string message_id = GetMessageId(desc);
- printer->Print(
- " jspb.Message.initialize(this, opt_data, $messageId$, $pivot$, "
- "$rptfields$, $oneoffields$);\n",
- "messageId",
- !message_id.empty() ? ("'" + message_id + "'")
- : (IsResponse(desc) ? "''" : "0"),
- "pivot", GetPivot(desc), "rptfields",
- RepeatedFieldsArrayName(options, desc), "oneoffields",
- OneofFieldsArrayName(options, desc));
- printer->Print(
- "};\n"
- "goog.inherits($classname$, jspb.Message);\n"
- "if (goog.DEBUG && !COMPILED) {\n"
- // displayName overrides Function.prototype.displayName
- // http://google3/javascript/externs/es3.js?l=511
- " /**\n"
- " * @public\n"
- " * @override\n"
- " */\n"
- " $classname$.displayName = '$classname$';\n"
- "}\n",
- "classname", GetMessagePath(options, desc));
- }
- void Generator::GenerateClassConstructorAndDeclareExtensionFieldInfo(
- const GeneratorOptions& options, io::Printer* printer,
- const Descriptor* desc) const {
- if (!NamespaceOnly(desc)) {
- GenerateClassConstructor(options, printer, desc);
- if (IsExtendable(desc) &&
- desc->full_name() != "google.protobuf.bridge.MessageSet") {
- GenerateClassExtensionFieldInfo(options, printer, desc);
- }
- }
- for (int i = 0; i < desc->nested_type_count(); i++) {
- if (!IgnoreMessage(desc->nested_type(i))) {
- GenerateClassConstructorAndDeclareExtensionFieldInfo(
- options, printer, desc->nested_type(i));
- }
- }
- }
- void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- if (HasRepeatedFields(options, desc)) {
- printer->Print(
- "/**\n"
- " * List of repeated fields within this message type.\n"
- " * @private {!Array<number>}\n"
- " * @const\n"
- " */\n"
- "$classname$$rptfieldarray$ = $rptfields$;\n"
- "\n",
- "classname", GetMessagePath(options, desc), "rptfieldarray",
- kRepeatedFieldArrayName, "rptfields",
- RepeatedFieldNumberList(options, desc));
- }
- if (HasOneofFields(desc)) {
- printer->Print(
- "/**\n"
- " * Oneof group definitions for this message. Each group defines the "
- "field\n"
- " * numbers belonging to that group. When of these fields' value is "
- "set, all\n"
- " * other fields in the group are cleared. During deserialization, if "
- "multiple\n"
- " * fields are encountered for a group, only the last value seen will "
- "be kept.\n"
- " * @private {!Array<!Array<number>>}\n"
- " * @const\n"
- " */\n"
- "$classname$$oneofgrouparray$ = $oneofgroups$;\n"
- "\n",
- "classname", GetMessagePath(options, desc), "oneofgrouparray",
- kOneofGroupArrayName, "oneofgroups", OneofGroupList(desc));
- for (int i = 0; i < desc->oneof_decl_count(); i++) {
- if (IgnoreOneof(desc->oneof_decl(i))) {
- continue;
- }
- GenerateOneofCaseDefinition(options, printer, desc->oneof_decl(i));
- }
- }
- }
- void Generator::GenerateClassXid(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- printer->Print(
- "\n"
- "\n"
- "$class$.prototype.messageXid = xid('$class$');\n",
- "class", GetMessagePath(options, desc));
- }
- void Generator::GenerateOneofCaseDefinition(
- const GeneratorOptions& options, io::Printer* printer,
- const OneofDescriptor* oneof) const {
- printer->Print(
- "/**\n"
- " * @enum {number}\n"
- " */\n"
- "$classname$.$oneof$Case = {\n"
- " $upcase$_NOT_SET: 0",
- "classname", GetMessagePath(options, oneof->containing_type()), "oneof",
- JSOneofName(oneof), "upcase", ToEnumCase(oneof->name()));
- for (int i = 0; i < oneof->field_count(); i++) {
- if (IgnoreField(oneof->field(i))) {
- continue;
- }
- printer->Print(
- ",\n"
- " $upcase$: $number$",
- "upcase", ToEnumCase(oneof->field(i)->name()), "number",
- JSFieldIndex(oneof->field(i)));
- printer->Annotate("upcase", oneof->field(i));
- }
- printer->Print(
- "\n"
- "};\n"
- "\n"
- "/**\n"
- " * @return {$class$.$oneof$Case}\n"
- " */\n"
- "$class$.prototype.get$oneof$Case = function() {\n"
- " return /** @type {$class$.$oneof$Case} */(jspb.Message."
- "computeOneofCase(this, $class$.oneofGroups_[$oneofindex$]));\n"
- "};\n"
- "\n",
- "class", GetMessagePath(options, oneof->containing_type()), "oneof",
- JSOneofName(oneof), "oneofindex", JSOneofIndex(oneof));
- }
- void Generator::GenerateClassToObject(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- printer->Print(
- "\n"
- "\n"
- "if (jspb.Message.GENERATE_TO_OBJECT) {\n"
- "/**\n"
- " * Creates an object representation of this proto.\n"
- " * Field names that are reserved in JavaScript and will be renamed to "
- "pb_name.\n"
- " * Optional fields that are not set will be set to undefined.\n"
- " * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.\n"
- " * For the list of reserved names please see:\n"
- " * net/proto2/compiler/js/internal/generator.cc#kKeyword.\n"
- " * @param {boolean=} opt_includeInstance Deprecated. whether to include "
- "the\n"
- " * JSPB instance for transitional soy proto support:\n"
- " * http://goto/soy-param-migration\n"
- " * @return {!Object}\n"
- " */\n"
- "$classname$.prototype.toObject = function(opt_includeInstance) {\n"
- " return $classname$.toObject(opt_includeInstance, this);\n"
- "};\n"
- "\n"
- "\n"
- "/**\n"
- " * Static version of the {@see toObject} method.\n"
- " * @param {boolean|undefined} includeInstance Deprecated. Whether to "
- "include\n"
- " * the JSPB instance for transitional soy proto support:\n"
- " * http://goto/soy-param-migration\n"
- " * @param {!$classname$} msg The msg instance to transform.\n"
- " * @return {!Object}\n"
- " * @suppress {unusedLocalVariables} f is only used for nested messages\n"
- " */\n"
- "$classname$.toObject = function(includeInstance, msg) {\n"
- " var f, obj = {",
- "classname", GetMessagePath(options, desc));
- bool first = true;
- for (int i = 0; i < desc->field_count(); i++) {
- const FieldDescriptor* field = desc->field(i);
- if (IgnoreField(field)) {
- continue;
- }
- if (!first) {
- printer->Print(",\n ");
- } else {
- printer->Print("\n ");
- first = false;
- }
- GenerateClassFieldToObject(options, printer, field);
- }
- if (!first) {
- printer->Print("\n };\n\n");
- } else {
- printer->Print("\n\n };\n\n");
- }
- if (IsExtendable(desc)) {
- printer->Print(
- " jspb.Message.toObjectExtension(/** @type {!jspb.Message} */ (msg), "
- "obj,\n"
- " $extObject$, $class$.prototype.getExtension,\n"
- " includeInstance);\n",
- "extObject", JSExtensionsObjectName(options, desc->file(), desc),
- "class", GetMessagePath(options, desc));
- }
- printer->Print(
- " if (includeInstance) {\n"
- " obj.$$jspbMessageInstance = msg;\n"
- " }\n"
- " return obj;\n"
- "};\n"
- "}\n"
- "\n"
- "\n",
- "classname", GetMessagePath(options, desc));
- }
- void Generator::GenerateFieldValueExpression(io::Printer* printer,
- const char* obj_reference,
- const FieldDescriptor* field,
- bool use_default) const {
- const bool is_float_or_double =
- field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
- field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE;
- const bool is_boolean = field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL;
- const std::string with_default = use_default ? "WithDefault" : "";
- const std::string default_arg =
- use_default ? StrCat(", ", JSFieldDefault(field)) : "";
- const std::string cardinality = field->is_repeated() ? "Repeated" : "";
- std::string type = "";
- if (is_float_or_double) {
- type = "FloatingPoint";
- }
- if (is_boolean) {
- type = "Boolean";
- }
- // Prints the appropriate function, among:
- // - getField
- // - getBooleanField
- // - getFloatingPointField => Replaced by getOptionalFloatingPointField to
- // preserve backward compatibility.
- // - getFieldWithDefault
- // - getBooleanFieldWithDefault
- // - getFloatingPointFieldWithDefault
- // - getRepeatedField
- // - getRepeatedBooleanField
- // - getRepeatedFloatingPointField
- if (is_float_or_double && !field->is_repeated() && !use_default) {
- printer->Print(
- "jspb.Message.getOptionalFloatingPointField($obj$, "
- "$index$$default$)",
- "obj", obj_reference, "index", JSFieldIndex(field), "default",
- default_arg);
- } else {
- printer->Print(
- "jspb.Message.get$cardinality$$type$Field$with_default$($obj$, "
- "$index$$default$)",
- "cardinality", cardinality, "type", type, "with_default", with_default,
- "obj", obj_reference, "index", JSFieldIndex(field), "default",
- default_arg);
- }
- }
- void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
- io::Printer* printer,
- const FieldDescriptor* field) const {
- printer->Print("$fieldname$: ", "fieldname",
- JSObjectFieldName(options, field));
- if (field->is_map()) {
- const FieldDescriptor* value_field = MapFieldValue(field);
- // If the map values are of a message type, we must provide their static
- // toObject() method; otherwise we pass undefined for that argument.
- std::string value_to_object;
- if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- value_to_object =
- GetMessagePath(options, value_field->message_type()) + ".toObject";
- } else {
- value_to_object = "undefined";
- }
- printer->Print(
- "(f = msg.get$name$()) ? f.toObject(includeInstance, $valuetoobject$) "
- ": []",
- "name", JSGetterName(options, field), "valuetoobject", value_to_object);
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- // Message field.
- if (field->is_repeated()) {
- {
- printer->Print(
- "jspb.Message.toObjectList(msg.get$getter$(),\n"
- " $type$.toObject, includeInstance)",
- "getter", JSGetterName(options, field), "type",
- SubmessageTypeRef(options, field));
- }
- } else {
- printer->Print(
- "(f = msg.get$getter$()) && "
- "$type$.toObject(includeInstance, f)",
- "getter", JSGetterName(options, field), "type",
- SubmessageTypeRef(options, field));
- }
- } else if (field->type() == FieldDescriptor::TYPE_BYTES) {
- // For bytes fields we want to always return the B64 data.
- printer->Print("msg.get$getter$()", "getter",
- JSGetterName(options, field, BYTES_B64));
- } else {
- bool use_default = field->has_default_value();
- if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
- // Repeated fields get initialized to their default in the constructor
- // (why?), so we emit a plain getField() call for them.
- !field->is_repeated()) {
- // Proto3 puts all defaults (including implicit defaults) in toObject().
- // But for proto2 we leave the existing semantics unchanged: unset fields
- // without default are unset.
- use_default = true;
- }
- // We don't implement this by calling the accessors, because the semantics
- // of the accessors are changing independently of the toObject() semantics.
- // We are migrating the accessors to return defaults instead of null, but
- // it may take longer to migrate toObject (or we might not want to do it at
- // all). So we want to generate independent code.
- // The accessor for unset optional values without default should return
- // null. Those are converted to undefined in the generated object.
- if (!use_default) {
- printer->Print("(f = ");
- }
- GenerateFieldValueExpression(printer, "msg", field, use_default);
- if (!use_default) {
- printer->Print(") == null ? undefined : f");
- }
- }
- }
- void Generator::GenerateObjectTypedef(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- // TODO(b/122687752): Consider renaming nested messages called ObjectFormat
- // to prevent collisions.
- const std::string type_name = GetMessagePath(options, desc) + ".ObjectFormat";
- printer->Print(
- "/**\n"
- " * The raw object form of $messageName$ as accepted by the `fromObject` "
- "method.\n"
- " * @record\n"
- " */\n"
- "$typeName$ = function() {\n",
- "messageName", desc->name(), "typeName", type_name);
- for (int i = 0; i < desc->field_count(); i++) {
- if (i > 0) {
- printer->Print("\n");
- }
- printer->Print(
- " /** @type {$fieldType$|undefined} */\n"
- " this.$fieldName$;\n",
- "fieldName", JSObjectFieldName(options, desc->field(i)),
- // TODO(b/121097361): Add type checking for field values.
- "fieldType", "?");
- }
- printer->Print("};\n\n");
- }
- void Generator::GenerateClassFromObject(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- printer->Print("if (jspb.Message.GENERATE_FROM_OBJECT) {\n\n");
- GenerateObjectTypedef(options, printer, desc);
- printer->Print(
- "/**\n"
- " * Loads data from an object into a new instance of this proto.\n"
- " * @param {!$classname$.ObjectFormat} obj\n"
- " * The object representation of this proto to load the data from.\n"
- " * @return {!$classname$}\n"
- " */\n"
- "$classname$.fromObject = function(obj) {\n"
- " var msg = new $classname$();\n",
- "classname", GetMessagePath(options, desc));
- for (int i = 0; i < desc->field_count(); i++) {
- const FieldDescriptor* field = desc->field(i);
- if (!IgnoreField(field)) {
- GenerateClassFieldFromObject(options, printer, field);
- }
- }
- printer->Print(
- " return msg;\n"
- "};\n"
- "}\n\n");
- }
- void Generator::GenerateClassFieldFromObject(
- const GeneratorOptions& options, io::Printer* printer,
- const FieldDescriptor* field) const {
- if (field->is_map()) {
- const FieldDescriptor* value_field = MapFieldValue(field);
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
- // Since the map values are of message type, we have to do some extra work
- // to recursively call fromObject() on them before setting the map field.
- printer->Print(
- " obj.$name$ && jspb.Message.setWrapperField(\n"
- " msg, $index$, jspb.Map.fromObject(obj.$name$, $fieldclass$, "
- "$fieldclass$.fromObject));\n",
- "name", JSObjectFieldName(options, field), "index",
- JSFieldIndex(field), "fieldclass",
- GetMessagePath(options, value_field->message_type()));
- } else {
- // `msg` is a newly-constructed message object that has not yet built any
- // map containers wrapping underlying arrays, so we can simply directly
- // set the array here without fear of a stale wrapper.
- printer->Print(
- " obj.$name$ && "
- "jspb.Message.setField(msg, $index$, obj.$name$);\n",
- "name", JSObjectFieldName(options, field), "index",
- JSFieldIndex(field));
- }
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- // Message field (singular or repeated)
- if (field->is_repeated()) {
- {
- printer->Print(
- " obj.$name$ && "
- "jspb.Message.setRepeatedWrapperField(\n"
- " msg, $index$, obj.$name$.map(\n"
- " $fieldclass$.fromObject));\n",
- "name", JSObjectFieldName(options, field), "index",
- JSFieldIndex(field), "fieldclass",
- SubmessageTypeRef(options, field));
- }
- } else {
- printer->Print(
- " obj.$name$ && jspb.Message.setWrapperField(\n"
- " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
- "name", JSObjectFieldName(options, field), "index",
- JSFieldIndex(field), "fieldclass", SubmessageTypeRef(options, field));
- }
- } else {
- // Simple (primitive) field.
- printer->Print(
- " obj.$name$ != null && jspb.Message.setField(msg, $index$, "
- "obj.$name$);\n",
- "name", JSObjectFieldName(options, field), "index",
- JSFieldIndex(field));
- }
- }
- void Generator::GenerateClassRegistration(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- // Register any extensions defined inside this message type.
- for (int i = 0; i < desc->extension_count(); i++) {
- const FieldDescriptor* extension = desc->extension(i);
- if (ShouldGenerateExtension(extension)) {
- GenerateExtension(options, printer, extension);
- }
- }
- }
- void Generator::GenerateClassFields(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- for (int i = 0; i < desc->field_count(); i++) {
- if (!IgnoreField(desc->field(i))) {
- GenerateClassField(options, printer, desc->field(i));
- }
- }
- }
- void GenerateBytesWrapper(const GeneratorOptions& options, io::Printer* printer,
- const FieldDescriptor* field, BytesMode bytes_mode) {
- std::string type =
- JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ false,
- /* force_present = */ false,
- /* singular_if_not_packed = */ false, bytes_mode);
- printer->Print(
- "/**\n"
- " * $fielddef$\n"
- "$comment$"
- " * This is a type-conversion wrapper around `get$defname$()`\n"
- " * @return {$type$}\n"
- " */\n"
- "$class$.prototype.get$name$ = function() {\n"
- " return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n"
- " this.get$defname$()));\n"
- "};\n"
- "\n"
- "\n",
- "fielddef", FieldDefinition(options, field), "comment",
- FieldComments(field, bytes_mode), "type", type, "class",
- GetMessagePath(options, field->containing_type()), "name",
- JSGetterName(options, field, bytes_mode), "list",
- field->is_repeated() ? "List" : "", "suffix",
- JSByteGetterSuffix(bytes_mode), "defname",
- JSGetterName(options, field, BYTES_DEFAULT));
- }
- void Generator::GenerateClassField(const GeneratorOptions& options,
- io::Printer* printer,
- const FieldDescriptor* field) const {
- if (field->is_map()) {
- const FieldDescriptor* key_field = MapFieldKey(field);
- const FieldDescriptor* value_field = MapFieldValue(field);
- // Map field: special handling to instantiate the map object on demand.
- std::string key_type =
- JSFieldTypeAnnotation(options, key_field,
- /* is_setter_argument = */ false,
- /* force_present = */ true,
- /* singular_if_not_packed = */ false);
- std::string value_type =
- JSFieldTypeAnnotation(options, value_field,
- /* is_setter_argument = */ false,
- /* force_present = */ true,
- /* singular_if_not_packed = */ false);
- printer->Print(
- "/**\n"
- " * $fielddef$\n"
- " * @param {boolean=} opt_noLazyCreate Do not create the map if\n"
- " * empty, instead returning `undefined`\n"
- " * @return {!jspb.Map<$keytype$,$valuetype$>}\n"
- " */\n",
- "fielddef", FieldDefinition(options, field), "keytype", key_type,
- "valuetype", value_type);
- printer->Print(
- "$class$.prototype.$gettername$ = function(opt_noLazyCreate) {\n"
- " return /** @type {!jspb.Map<$keytype$,$valuetype$>} */ (\n",
- "class", GetMessagePath(options, field->containing_type()),
- "gettername", "get" + JSGetterName(options, field), "keytype", key_type,
- "valuetype", value_type);
- printer->Annotate("gettername", field);
- printer->Print(
- " jspb.Message.getMapField(this, $index$, opt_noLazyCreate",
- "index", JSFieldIndex(field));
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
- printer->Print(
- ",\n"
- " $messageType$",
- "messageType", GetMessagePath(options, value_field->message_type()));
- } else {
- printer->Print(
- ",\n"
- " null");
- }
- printer->Print("));\n");
- printer->Print(
- "};\n"
- "\n"
- "\n");
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- // Message field: special handling in order to wrap the underlying data
- // array with a message object.
- printer->Print(
- "/**\n"
- " * $fielddef$\n"
- "$comment$"
- " * @return {$type$}\n"
- " */\n",
- "fielddef", FieldDefinition(options, field), "comment",
- FieldComments(field, BYTES_DEFAULT), "type",
- JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ false,
- /* force_present = */ false,
- /* singular_if_not_packed = */ false));
- printer->Print(
- "$class$.prototype.$gettername$ = function() {\n"
- " return /** @type{$type$} */ (\n"
- " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
- "$index$$required$));\n"
- "};\n"
- "\n"
- "\n",
- "class", GetMessagePath(options, field->containing_type()),
- "gettername", "get" + JSGetterName(options, field), "type",
- JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ false,
- /* force_present = */ false,
- /* singular_if_not_packed = */ false),
- "rpt", (field->is_repeated() ? "Repeated" : ""), "index",
- JSFieldIndex(field), "wrapperclass", SubmessageTypeRef(options, field),
- "required",
- (field->label() == FieldDescriptor::LABEL_REQUIRED ? ", 1" : ""));
- printer->Annotate("gettername", field);
- printer->Print(
- "/**\n"
- " * @param {$optionaltype$} value\n"
- " * @return {!$class$} returns this\n"
- "*/\n"
- "$class$.prototype.$settername$ = function(value) {\n"
- " return jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
- "optionaltype",
- JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ true,
- /* force_present = */ false,
- /* singular_if_not_packed = */ false),
- "class", GetMessagePath(options, field->containing_type()),
- "settername", "set" + JSGetterName(options, field), "oneoftag",
- (InRealOneof(field) ? "Oneof" : ""), "repeatedtag",
- (field->is_repeated() ? "Repeated" : ""));
- printer->Annotate("settername", field);
- printer->Print(
- "this, $index$$oneofgroup$, value);\n"
- "};\n"
- "\n"
- "\n",
- "index", JSFieldIndex(field), "oneofgroup",
- (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""));
- if (field->is_repeated()) {
- GenerateRepeatedMessageHelperMethods(options, printer, field);
- }
- } else {
- bool untyped = false;
- // Simple (primitive) field, either singular or repeated.
- // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type;
- // at this point we "lie" to non-binary users and tell the return
- // type is always base64 string, pending a LSC to migrate to typed getters.
- BytesMode bytes_mode =
- field->type() == FieldDescriptor::TYPE_BYTES && !options.binary
- ? BYTES_B64
- : BYTES_DEFAULT;
- std::string typed_annotation =
- JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ false,
- /* force_present = */ false,
- /* singular_if_not_packed = */ false,
- /* bytes_mode = */ bytes_mode);
- if (untyped) {
- printer->Print(
- "/**\n"
- " * @return {?} Raw field, untyped.\n"
- " */\n");
- } else {
- printer->Print(
- "/**\n"
- " * $fielddef$\n"
- "$comment$"
- " * @return {$type$}\n"
- " */\n",
- "fielddef", FieldDefinition(options, field), "comment",
- FieldComments(field, bytes_mode), "type", typed_annotation);
- }
- printer->Print("$class$.prototype.$gettername$ = function() {\n", "class",
- GetMessagePath(options, field->containing_type()),
- "gettername", "get" + JSGetterName(options, field));
- printer->Annotate("gettername", field);
- if (untyped) {
- printer->Print(" return ");
- } else {
- printer->Print(" return /** @type {$type$} */ (", "type",
- typed_annotation);
- }
- bool use_default = !ReturnsNullWhenUnset(options, field);
- // Raw fields with no default set should just return undefined.
- if (untyped && !field->has_default_value()) {
- use_default = false;
- }
- // Repeated fields get initialized to their default in the constructor
- // (why?), so we emit a plain getField() call for them.
- if (field->is_repeated()) {
- use_default = false;
- }
- GenerateFieldValueExpression(printer, "this", field, use_default);
- if (untyped) {
- printer->Print(
- ";\n"
- "};\n"
- "\n"
- "\n");
- } else {
- printer->Print(
- ");\n"
- "};\n"
- "\n"
- "\n");
- }
- if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) {
- GenerateBytesWrapper(options, printer, field, BYTES_B64);
- GenerateBytesWrapper(options, printer, field, BYTES_U8);
- }
- printer->Print(
- "/**\n"
- " * @param {$optionaltype$} value\n"
- " * @return {!$class$} returns this\n"
- " */\n",
- "class", GetMessagePath(options, field->containing_type()),
- "optionaltype",
- untyped ? "*"
- : JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ true,
- /* force_present = */ false,
- /* singular_if_not_packed = */ false));
- if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
- !field->is_repeated() && !field->is_map() &&
- !HasFieldPresence(options, field)) {
- // Proto3 non-repeated and non-map fields without presence use the
- // setProto3*Field function.
- printer->Print(
- "$class$.prototype.$settername$ = function(value) {\n"
- " return jspb.Message.setProto3$typetag$Field(this, $index$, "
- "value);"
- "\n"
- "};\n"
- "\n"
- "\n",
- "class", GetMessagePath(options, field->containing_type()),
- "settername", "set" + JSGetterName(options, field), "typetag",
- JSTypeTag(field), "index", JSFieldIndex(field));
- printer->Annotate("settername", field);
- } else {
- // Otherwise, use the regular setField function.
- printer->Print(
- "$class$.prototype.$settername$ = function(value) {\n"
- " return jspb.Message.set$oneoftag$Field(this, $index$",
- "class", GetMessagePath(options, field->containing_type()),
- "settername", "set" + JSGetterName(options, field), "oneoftag",
- (InRealOneof(field) ? "Oneof" : ""), "index", JSFieldIndex(field));
- printer->Annotate("settername", field);
- printer->Print(
- "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);\n"
- "};\n"
- "\n"
- "\n",
- "type",
- untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
- "typeclose", untyped ? ")" : "", "oneofgroup",
- (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""),
- "rptvalueinit", (field->is_repeated() ? " || []" : ""));
- }
- if (untyped) {
- printer->Print(
- "/**\n"
- " * Clears the value.\n"
- " * @return {!$class$} returns this\n"
- " */\n",
- "class", GetMessagePath(options, field->containing_type()));
- }
- if (field->is_repeated()) {
- GenerateRepeatedPrimitiveHelperMethods(options, printer, field, untyped);
- }
- }
- // Generate clearFoo() method for map fields, repeated fields, and other
- // fields with presence.
- if (field->is_map()) {
- // clang-format off
- printer->Print(
- "/**\n"
- " * Clears values from the map. The map will be non-null.\n"
- " * @return {!$class$} returns this\n"
- " */\n"
- "$class$.prototype.$clearername$ = function() {\n"
- " this.$gettername$().clear();\n"
- " return this;"
- "};\n"
- "\n"
- "\n",
- "class", GetMessagePath(options, field->containing_type()),
- "clearername", "clear" + JSGetterName(options, field),
- "gettername", "get" + JSGetterName(options, field));
- // clang-format on
- printer->Annotate("clearername", field);
- } else if (field->is_repeated() ||
- (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
- !field->is_required())) {
- // Fields where we can delegate to the regular setter.
- // clang-format off
- printer->Print(
- "/**\n"
- " * $jsdoc$\n"
- " * @return {!$class$} returns this\n"
- " */\n"
- "$class$.prototype.$clearername$ = function() {\n"
- " return this.$settername$($clearedvalue$);\n"
- "};\n"
- "\n"
- "\n",
- "jsdoc", field->is_repeated()
- ? "Clears the list making it empty but non-null."
- : "Clears the message field making it undefined.",
- "class", GetMessagePath(options, field->containing_type()),
- "clearername", "clear" + JSGetterName(options, field),
- "settername", "set" + JSGetterName(options, field),
- "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
- // clang-format on
- printer->Annotate("clearername", field);
- } else if (HasFieldPresence(options, field)) {
- // Fields where we can't delegate to the regular setter because it doesn't
- // accept "undefined" as an argument.
- // clang-format off
- printer->Print(
- "/**\n"
- " * Clears the field making it undefined.\n"
- " * @return {!$class$} returns this\n"
- " */\n"
- "$class$.prototype.$clearername$ = function() {\n"
- " return jspb.Message.set$maybeoneof$Field(this, "
- "$index$$maybeoneofgroup$, ",
- "class", GetMessagePath(options, field->containing_type()),
- "clearername", "clear" + JSGetterName(options, field),
- "maybeoneof", (InRealOneof(field) ? "Oneof" : ""),
- "maybeoneofgroup", (InRealOneof(field)
- ? (", " + JSOneofArray(options, field))
- : ""),
- "index", JSFieldIndex(field));
- // clang-format on
- printer->Annotate("clearername", field);
- printer->Print(
- "$clearedvalue$);\n"
- "};\n"
- "\n"
- "\n",
- "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
- }
- if (HasFieldPresence(options, field)) {
- printer->Print(
- "/**\n"
- " * Returns whether this field is set.\n"
- " * @return {boolean}\n"
- " */\n"
- "$class$.prototype.$hasername$ = function() {\n"
- " return jspb.Message.getField(this, $index$) != null;\n"
- "};\n"
- "\n"
- "\n",
- "class", GetMessagePath(options, field->containing_type()), "hasername",
- "has" + JSGetterName(options, field), "index", JSFieldIndex(field));
- printer->Annotate("hasername", field);
- }
- }
- void Generator::GenerateRepeatedPrimitiveHelperMethods(
- const GeneratorOptions& options, io::Printer* printer,
- const FieldDescriptor* field, bool untyped) const {
- // clang-format off
- printer->Print(
- "/**\n"
- " * @param {$optionaltype$} value\n"
- " * @param {number=} opt_index\n"
- " * @return {!$class$} returns this\n"
- " */\n"
- "$class$.prototype.$addername$ = function(value, opt_index) {\n"
- " return jspb.Message.addToRepeatedField(this, "
- "$index$",
- "class", GetMessagePath(options, field->containing_type()), "addername",
- "add" + JSGetterName(options, field, BYTES_DEFAULT,
- /* drop_list = */ true),
- "optionaltype",
- JSFieldTypeAnnotation(
- options, field,
- /* is_setter_argument = */ false,
- /* force_present = */ true,
- /* singular_if_not_packed = */ false,
- BYTES_DEFAULT,
- /* force_singular = */ true),
- "index", JSFieldIndex(field));
- printer->Annotate("addername", field);
- printer->Print(
- "$oneofgroup$, $type$value$rptvalueinit$$typeclose$, "
- "opt_index);\n"
- "};\n"
- "\n"
- "\n",
- "type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "",
- "typeclose", untyped ? ")" : "", "oneofgroup",
- (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""),
- "rptvalueinit", "");
- // clang-format on
- }
- void Generator::GenerateRepeatedMessageHelperMethods(
- const GeneratorOptions& options, io::Printer* printer,
- const FieldDescriptor* field) const {
- printer->Print(
- "/**\n"
- " * @param {!$optionaltype$=} opt_value\n"
- " * @param {number=} opt_index\n"
- " * @return {!$optionaltype$}\n"
- " */\n"
- "$class$.prototype.$addername$ = function(opt_value, opt_index) {\n"
- " return jspb.Message.addTo$repeatedtag$WrapperField(",
- "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "class",
- GetMessagePath(options, field->containing_type()), "addername",
- "add" + JSGetterName(options, field, BYTES_DEFAULT,
- /* drop_list = */ true),
- "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
- printer->Annotate("addername", field);
- printer->Print(
- "this, $index$$oneofgroup$, opt_value, $ctor$, opt_index);\n"
- "};\n"
- "\n"
- "\n",
- "index", JSFieldIndex(field), "oneofgroup",
- (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""), "ctor",
- GetMessagePath(options, field->message_type()));
- }
- void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- if (IsExtendable(desc)) {
- printer->Print(
- "\n"
- "/**\n"
- " * The extensions registered with this message class. This is a "
- "map of\n"
- " * extension field number to fieldInfo object.\n"
- " *\n"
- " * For example:\n"
- " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
- "ctor: proto.example.MyMessage} }\n"
- " *\n"
- " * fieldName contains the JsCompiler renamed field name property "
- "so that it\n"
- " * works in OPTIMIZED mode.\n"
- " *\n"
- " * @type {!Object<number, jspb.ExtensionFieldInfo>}\n"
- " */\n"
- "$class$.extensions = {};\n"
- "\n",
- "class", GetMessagePath(options, desc));
- printer->Print(
- "\n"
- "/**\n"
- " * The extensions registered with this message class. This is a "
- "map of\n"
- " * extension field number to fieldInfo object.\n"
- " *\n"
- " * For example:\n"
- " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
- "ctor: proto.example.MyMessage} }\n"
- " *\n"
- " * fieldName contains the JsCompiler renamed field name property "
- "so that it\n"
- " * works in OPTIMIZED mode.\n"
- " *\n"
- " * @type {!Object<number, jspb.ExtensionFieldBinaryInfo>}\n"
- " */\n"
- "$class$.extensionsBinary = {};\n"
- "\n",
- "class", GetMessagePath(options, desc));
- }
- }
- void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- // TODO(cfallin): Handle lazy decoding when requested by field option and/or
- // by default for 'bytes' fields and packed repeated fields.
- printer->Print(
- "/**\n"
- " * Deserializes binary data (in protobuf wire format).\n"
- " * @param {jspb.ByteSource} bytes The bytes to deserialize.\n"
- " * @return {!$class$}\n"
- " */\n"
- "$class$.deserializeBinary = function(bytes) {\n"
- " var reader = new jspb.BinaryReader(bytes);\n"
- " var msg = new $class$;\n"
- " return $class$.deserializeBinaryFromReader(msg, reader);\n"
- "};\n"
- "\n"
- "\n"
- "/**\n"
- " * Deserializes binary data (in protobuf wire format) from the\n"
- " * given reader into the given message object.\n"
- " * @param {!$class$} msg The message object to deserialize into.\n"
- " * @param {!jspb.BinaryReader} reader The BinaryReader to use.\n"
- " * @return {!$class$}\n"
- " */\n"
- "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
- " while (reader.nextField()) {\n",
- "class", GetMessagePath(options, desc));
- printer->Print(
- " if (reader.isEndGroup()) {\n"
- " break;\n"
- " }\n"
- " var field = reader.getFieldNumber();\n"
- " switch (field) {\n");
- for (int i = 0; i < desc->field_count(); i++) {
- if (!IgnoreField(desc->field(i))) {
- GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
- }
- }
- printer->Print(" default:\n");
- if (IsExtendable(desc)) {
- printer->Print(
- " jspb.Message.readBinaryExtension(msg, reader,\n"
- " $extobj$Binary,\n"
- " $class$.prototype.getExtension,\n"
- " $class$.prototype.setExtension);\n"
- " break;\n"
- " }\n",
- "extobj", JSExtensionsObjectName(options, desc->file(), desc), "class",
- GetMessagePath(options, desc));
- } else {
- printer->Print(
- " reader.skipField();\n"
- " break;\n"
- " }\n");
- }
- printer->Print(
- " }\n"
- " return msg;\n"
- "};\n"
- "\n"
- "\n");
- }
- void Generator::GenerateClassDeserializeBinaryField(
- const GeneratorOptions& options, io::Printer* printer,
- const FieldDescriptor* field) const {
- printer->Print(" case $num$:\n", "num", StrCat(field->number()));
- if (field->is_map()) {
- const FieldDescriptor* key_field = MapFieldKey(field);
- const FieldDescriptor* value_field = MapFieldValue(field);
- printer->Print(
- " var value = msg.get$name$();\n"
- " reader.readMessage(value, function(message, reader) {\n",
- "name", JSGetterName(options, field));
- printer->Print(
- " jspb.Map.deserializeBinary(message, reader, "
- "$keyReaderFn$, $valueReaderFn$",
- "keyReaderFn", JSBinaryReaderMethodName(options, key_field),
- "valueReaderFn", JSBinaryReaderMethodName(options, value_field));
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
- printer->Print(", $messageType$.deserializeBinaryFromReader",
- "messageType",
- GetMessagePath(options, value_field->message_type()));
- } else {
- printer->Print(", null");
- }
- printer->Print(", $defaultKey$", "defaultKey", JSFieldDefault(key_field));
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
- printer->Print(", new $messageType$()", "messageType",
- GetMessagePath(options, value_field->message_type()));
- } else {
- printer->Print(", $defaultValue$", "defaultValue",
- JSFieldDefault(value_field));
- }
- printer->Print(");\n");
- printer->Print(" });\n");
- } else {
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- printer->Print(
- " var value = new $fieldclass$;\n"
- " reader.read$msgOrGroup$($grpfield$value,"
- "$fieldclass$.deserializeBinaryFromReader);\n",
- "fieldclass", SubmessageTypeRef(options, field), "msgOrGroup",
- (field->type() == FieldDescriptor::TYPE_GROUP) ? "Group" : "Message",
- "grpfield",
- (field->type() == FieldDescriptor::TYPE_GROUP)
- ? (StrCat(field->number()) + ", ")
- : "");
- } else if (field->is_packable()) {
- printer->Print(
- " var values = /** @type {$fieldtype$} */ "
- "(reader.isDelimited() "
- "? reader.readPacked$reader$() : [reader.read$reader$()]);\n",
- "fieldtype",
- JSFieldTypeAnnotation(options, field, false, true,
- /* singular_if_not_packed */ false, BYTES_U8),
- "reader", JSBinaryReaderMethodType(field));
- } else {
- printer->Print(
- " var value = /** @type {$fieldtype$} */ "
- "(reader.read$reader$());\n",
- "fieldtype",
- JSFieldTypeAnnotation(options, field, false, true,
- /* singular_if_not_packed */ true, BYTES_U8),
- "reader",
- JSBinaryReadWriteMethodName(field, /* is_writer = */ false));
- }
- if (field->is_packable()) {
- printer->Print(
- " for (var i = 0; i < values.length; i++) {\n"
- " msg.add$name$(values[i]);\n"
- " }\n",
- "name",
- JSGetterName(options, field, BYTES_DEFAULT, /* drop_list = */ true));
- } else if (field->is_repeated()) {
- printer->Print(
- " msg.add$name$(value);\n", "name",
- JSGetterName(options, field, BYTES_DEFAULT, /* drop_list = */ true));
- } else {
- // Singular fields, and packed repeated fields, receive a |value| either
- // as the field's value or as the array of all the field's values; set
- // this as the field's value directly.
- printer->Print(" msg.set$name$(value);\n", "name",
- JSGetterName(options, field));
- }
- }
- printer->Print(" break;\n");
- }
- void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
- io::Printer* printer,
- const Descriptor* desc) const {
- printer->Print(
- "/**\n"
- " * Serializes the message to binary data (in protobuf wire format).\n"
- " * @return {!Uint8Array}\n"
- " */\n"
- "$class$.prototype.serializeBinary = function() {\n"
- " var writer = new jspb.BinaryWriter();\n"
- " $class$.serializeBinaryToWriter(this, writer);\n"
- " return writer.getResultBuffer();\n"
- "};\n"
- "\n"
- "\n"
- "/**\n"
- " * Serializes the given message to binary data (in protobuf wire\n"
- " * format), writing to the given BinaryWriter.\n"
- " * @param {!$class$} message\n"
- " * @param {!jspb.BinaryWriter} writer\n"
- " * @suppress {unusedLocalVariables} f is only used for nested messages\n"
- " */\n"
- "$class$.serializeBinaryToWriter = function(message, "
- "writer) {\n"
- " var f = undefined;\n",
- "class", GetMessagePath(options, desc));
- for (int i = 0; i < desc->field_count(); i++) {
- if (!IgnoreField(desc->field(i))) {
- GenerateClassSerializeBinaryField(options, printer, desc->field(i));
- }
- }
- if (IsExtendable(desc)) {
- printer->Print(
- " jspb.Message.serializeBinaryExtensions(message, writer,\n"
- " $extobj$Binary, $class$.prototype.getExtension);\n",
- "extobj", JSExtensionsObjectName(options, desc->file(), desc), "class",
- GetMessagePath(options, desc));
- }
- printer->Print(
- "};\n"
- "\n"
- "\n");
- }
- void Generator::GenerateClassSerializeBinaryField(
- const GeneratorOptions& options, io::Printer* printer,
- const FieldDescriptor* field) const {
- if (HasFieldPresence(options, field) &&
- field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
- std::string typed_annotation =
- JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ false,
- /* force_present = */ false,
- /* singular_if_not_packed = */ false,
- /* bytes_mode = */ BYTES_DEFAULT);
- printer->Print(
- " f = /** @type {$type$} */ "
- "(jspb.Message.getField(message, $index$));\n",
- "index", JSFieldIndex(field), "type", typed_annotation);
- } else {
- printer->Print(
- " f = message.get$name$($nolazy$);\n", "name",
- JSGetterName(options, field, BYTES_U8),
- // No lazy creation for maps containers -- fastpath the empty case.
- "nolazy", field->is_map() ? "true" : "");
- }
- // Print an `if (condition)` statement that evaluates to true if the field
- // goes on the wire.
- if (field->is_map()) {
- printer->Print(" if (f && f.getLength() > 0) {\n");
- } else if (field->is_repeated()) {
- printer->Print(" if (f.length > 0) {\n");
- } else {
- if (HasFieldPresence(options, field)) {
- printer->Print(" if (f != null) {\n");
- } else {
- // No field presence: serialize onto the wire only if value is
- // non-default. Defaults are documented here:
- // https://goto.google.com/lhdfm
- switch (field->cpp_type()) {
- case FieldDescriptor::CPPTYPE_INT32:
- case FieldDescriptor::CPPTYPE_INT64:
- case FieldDescriptor::CPPTYPE_UINT32:
- case FieldDescriptor::CPPTYPE_UINT64: {
- if (IsIntegralFieldWithStringJSType(field)) {
- // We can use `parseInt` here even though it will not be precise for
- // 64-bit quantities because we are only testing for zero/nonzero,
- // and JS numbers (64-bit floating point values, i.e., doubles) are
- // integer-precise in the range that includes zero.
- printer->Print(" if (parseInt(f, 10) !== 0) {\n");
- } else {
- printer->Print(" if (f !== 0) {\n");
- }
- break;
- }
- case FieldDescriptor::CPPTYPE_ENUM:
- case FieldDescriptor::CPPTYPE_FLOAT:
- case FieldDescriptor::CPPTYPE_DOUBLE:
- printer->Print(" if (f !== 0.0) {\n");
- break;
- case FieldDescriptor::CPPTYPE_BOOL:
- printer->Print(" if (f) {\n");
- break;
- case FieldDescriptor::CPPTYPE_STRING:
- printer->Print(" if (f.length > 0) {\n");
- break;
- default:
- assert(false);
- break;
- }
- }
- }
- // Write the field on the wire.
- if (field->is_map()) {
- const FieldDescriptor* key_field = MapFieldKey(field);
- const FieldDescriptor* value_field = MapFieldValue(field);
- printer->Print(
- " f.serializeBinary($index$, writer, "
- "$keyWriterFn$, $valueWriterFn$",
- "index", StrCat(field->number()), "keyWriterFn",
- JSBinaryWriterMethodName(options, key_field), "valueWriterFn",
- JSBinaryWriterMethodName(options, value_field));
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
- printer->Print(", $messageType$.serializeBinaryToWriter", "messageType",
- GetMessagePath(options, value_field->message_type()));
- }
- printer->Print(");\n");
- } else {
- printer->Print(
- " writer.write$method$(\n"
- " $index$,\n"
- " f",
- "method", JSBinaryReadWriteMethodName(field, /* is_writer = */ true),
- "index", StrCat(field->number()));
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
- !field->is_map()) {
- printer->Print(
- ",\n"
- " $submsg$.serializeBinaryToWriter\n",
- "submsg", SubmessageTypeRef(options, field));
- } else {
- printer->Print("\n");
- }
- printer->Print(" );\n");
- }
- // Close the `if`.
- printer->Print(" }\n");
- }
- void Generator::GenerateEnum(const GeneratorOptions& options,
- io::Printer* printer,
- const EnumDescriptor* enumdesc) const {
- printer->Print(
- "/**\n"
- " * @enum {number}\n"
- " */\n"
- "$enumprefix$$name$ = {\n",
- "enumprefix", GetEnumPathPrefix(options, enumdesc), "name",
- enumdesc->name());
- printer->Annotate("name", enumdesc);
- std::set<std::string> used_name;
- std::vector<int> valid_index;
- for (int i = 0; i < enumdesc->value_count(); i++) {
- if (enumdesc->options().allow_alias() &&
- !used_name.insert(ToEnumCase(enumdesc->value(i)->name())).second) {
- continue;
- }
- valid_index.push_back(i);
- }
- for (auto i : valid_index) {
- const EnumValueDescriptor* value = enumdesc->value(i);
- printer->Print(" $name$: $value$$comma$\n", "name",
- ToEnumCase(value->name()), "value", StrCat(value->number()),
- "comma", (i == valid_index.back()) ? "" : ",");
- printer->Annotate("name", value);
- }
- printer->Print(
- "};\n"
- "\n");
- }
- void Generator::GenerateExtension(const GeneratorOptions& options,
- io::Printer* printer,
- const FieldDescriptor* field) const {
- std::string extension_scope =
- (field->extension_scope()
- ? GetMessagePath(options, field->extension_scope())
- : GetNamespace(options, field->file()));
- const std::string extension_object_name = JSObjectFieldName(options, field);
- printer->Print(
- "\n"
- "/**\n"
- " * A tuple of {field number, class constructor} for the extension\n"
- " * field named `$nameInComment$`.\n"
- " * @type {!jspb.ExtensionFieldInfo<$extensionType$>}\n"
- " */\n"
- "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
- "nameInComment", extension_object_name, "name", extension_object_name,
- "class", extension_scope, "extensionType",
- JSFieldTypeAnnotation(options, field,
- /* is_setter_argument = */ false,
- /* force_present = */ true,
- /* singular_if_not_packed = */ false));
- printer->Annotate("name", field);
- printer->Print(
- " $index$,\n"
- " {$name$: 0},\n"
- " $ctor$,\n"
- " /** @type {?function((boolean|undefined),!jspb.Message=): "
- "!Object} */ (\n"
- " $toObject$),\n"
- " $repeated$);\n",
- "index", StrCat(field->number()), "name", extension_object_name, "ctor",
- (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
- ? SubmessageTypeRef(options, field)
- : std::string("null")),
- "toObject",
- (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
- ? (SubmessageTypeRef(options, field) + ".toObject")
- : std::string("null")),
- "repeated", (field->is_repeated() ? "1" : "0"));
- printer->Print(
- "\n"
- "$extendName$Binary[$index$] = new jspb.ExtensionFieldBinaryInfo(\n"
- " $class$.$name$,\n"
- " $binaryReaderFn$,\n"
- " $binaryWriterFn$,\n"
- " $binaryMessageSerializeFn$,\n"
- " $binaryMessageDeserializeFn$,\n",
- "extendName",
- JSExtensionsObjectName(options, field->file(), field->containing_type()),
- "index", StrCat(field->number()), "class", extension_scope, "name",
- extension_object_name, "binaryReaderFn",
- JSBinaryReaderMethodName(options, field), "binaryWriterFn",
- JSBinaryWriterMethodName(options, field), "binaryMessageSerializeFn",
- (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
- ? (SubmessageTypeRef(options, field) + ".serializeBinaryToWriter")
- : "undefined",
- "binaryMessageDeserializeFn",
- (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
- ? (SubmessageTypeRef(options, field) + ".deserializeBinaryFromReader")
- : "undefined");
- printer->Print(" $isPacked$);\n", "isPacked",
- (field->is_packed() ? "true" : "false"));
- printer->Print(
- "// This registers the extension field with the extended class, so that\n"
- "// toObject() will function correctly.\n"
- "$extendName$[$index$] = $class$.$name$;\n"
- "\n",
- "extendName",
- JSExtensionsObjectName(options, field->file(), field->containing_type()),
- "index", StrCat(field->number()), "class", extension_scope, "name",
- extension_object_name);
- }
- bool GeneratorOptions::ParseFromOptions(
- const std::vector<std::pair<std::string, std::string> >& options,
- std::string* error) {
- for (int i = 0; i < options.size(); i++) {
- if (options[i].first == "add_require_for_enums") {
- if (options[i].second != "") {
- *error = "Unexpected option value for add_require_for_enums";
- return false;
- }
- add_require_for_enums = true;
- } else if (options[i].first == "binary") {
- if (options[i].second != "") {
- *error = "Unexpected option value for binary";
- return false;
- }
- binary = true;
- } else if (options[i].first == "testonly") {
- if (options[i].second != "") {
- *error = "Unexpected option value for testonly";
- return false;
- }
- testonly = true;
- } else if (options[i].first == "error_on_name_conflict") {
- if (options[i].second != "") {
- *error = "Unexpected option value for error_on_name_conflict";
- return false;
- }
- error_on_name_conflict = true;
- } else if (options[i].first == "output_dir") {
- output_dir = options[i].second;
- } else if (options[i].first == "namespace_prefix") {
- namespace_prefix = options[i].second;
- } else if (options[i].first == "library") {
- library = options[i].second;
- } else if (options[i].first == "import_style") {
- if (options[i].second == "closure") {
- import_style = kImportClosure;
- } else if (options[i].second == "commonjs") {
- import_style = kImportCommonJs;
- } else if (options[i].second == "commonjs_strict") {
- import_style = kImportCommonJsStrict;
- } else if (options[i].second == "browser") {
- import_style = kImportBrowser;
- } else if (options[i].second == "es6") {
- import_style = kImportEs6;
- } else {
- *error = "Unknown import style " + options[i].second + ", expected " +
- "one of: closure, commonjs, browser, es6.";
- }
- } else if (options[i].first == "extension") {
- extension = options[i].second;
- } else if (options[i].first == "one_output_file_per_input_file") {
- if (!options[i].second.empty()) {
- *error = "Unexpected option value for one_output_file_per_input_file";
- return false;
- }
- one_output_file_per_input_file = true;
- } else if (options[i].first == "annotate_code") {
- if (!options[i].second.empty()) {
- *error = "Unexpected option value for annotate_code";
- return false;
- }
- annotate_code = true;
- } else {
- // Assume any other option is an output directory, as long as it is a bare
- // `key` rather than a `key=value` option.
- if (options[i].second != "") {
- *error = "Unknown option: " + options[i].first;
- return false;
- }
- output_dir = options[i].first;
- }
- }
- if (import_style != kImportClosure &&
- (add_require_for_enums || testonly || !library.empty() ||
- error_on_name_conflict || extension != ".js" ||
- one_output_file_per_input_file)) {
- *error =
- "The add_require_for_enums, testonly, library, error_on_name_conflict, "
- "extension, and one_output_file_per_input_file options should only be "
- "used for import_style=closure";
- return false;
- }
- return true;
- }
- GeneratorOptions::OutputMode GeneratorOptions::output_mode() const {
- // We use one output file per input file if we are not using Closure or if
- // this is explicitly requested.
- if (import_style != kImportClosure || one_output_file_per_input_file) {
- return kOneOutputFilePerInputFile;
- }
- // If a library name is provided, we put everything in that one file.
- if (!library.empty()) {
- return kEverythingInOneFile;
- }
- // Otherwise, we create one output file per SCC.
- return kOneOutputFilePerSCC;
- }
- void Generator::GenerateFilesInDepOrder(
- const GeneratorOptions& options, io::Printer* printer,
- const std::vector<const FileDescriptor*>& files) const {
- // Build a std::set over all files so that the DFS can detect when it recurses
- // into a dep not specified in the user's command line.
- std::set<const FileDescriptor*> all_files(files.begin(), files.end());
- // Track the in-progress set of files that have been generated already.
- std::set<const FileDescriptor*> generated;
- for (int i = 0; i < files.size(); i++) {
- GenerateFileAndDeps(options, printer, files[i], &all_files, &generated);
- }
- }
- void Generator::GenerateFileAndDeps(
- const GeneratorOptions& options, io::Printer* printer,
- const FileDescriptor* root, std::set<const FileDescriptor*>* all_files,
- std::set<const FileDescriptor*>* generated) const {
- // Skip if already generated.
- if (generated->find(root) != generated->end()) {
- return;
- }
- generated->insert(root);
- // Generate all dependencies before this file's content.
- for (int i = 0; i < root->dependency_count(); i++) {
- const FileDescriptor* dep = root->dependency(i);
- GenerateFileAndDeps(options, printer, dep, all_files, generated);
- }
- // Generate this file's content. Only generate if the file is part of the
- // original set requested to be generated; i.e., don't take all transitive
- // deps down to the roots.
- if (all_files->find(root) != all_files->end()) {
- GenerateClassesAndEnums(options, printer, root);
- }
- }
- bool Generator::GenerateFile(const FileDescriptor* file,
- const GeneratorOptions& options,
- GeneratorContext* context,
- bool use_short_name) const {
- std::string filename =
- options.output_dir + "/" +
- GetJSFilename(options, use_short_name
- ? file->name().substr(file->name().rfind('/'))
- : file->name());
- std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
- GOOGLE_CHECK(output);
- GeneratedCodeInfo annotations;
- io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
- &annotations);
- io::Printer printer(output.get(), '$',
- options.annotate_code ? &annotation_collector : nullptr);
- GenerateFile(options, &printer, file);
- if (printer.failed()) {
- return false;
- }
- if (options.annotate_code) {
- EmbedCodeAnnotations(annotations, &printer);
- }
- return true;
- }
- void Generator::GenerateFile(const GeneratorOptions& options,
- io::Printer* printer,
- const FileDescriptor* file) const {
- GenerateHeader(options, file, printer);
- // Generate "require" statements.
- if ((options.import_style == GeneratorOptions::kImportCommonJs ||
- options.import_style == GeneratorOptions::kImportCommonJsStrict)) {
- printer->Print("var jspb = require('google-protobuf');\n");
- printer->Print("var goog = jspb;\n");
- // Do not use global scope in strict mode
- if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
- printer->Print("var proto = {};\n\n");
- } else {
- printer->Print("var global = Function('return this')();\n\n");
- }
- for (int i = 0; i < file->dependency_count(); i++) {
- const std::string& name = file->dependency(i)->name();
- printer->Print(
- "var $alias$ = require('$file$');\n"
- "goog.object.extend(proto, $alias$);\n",
- "alias", ModuleAlias(name), "file",
- GetRootPath(file->name(), name) + GetJSFilename(options, name));
- }
- }
- std::set<std::string> provided;
- std::set<const FieldDescriptor*> extensions;
- for (int i = 0; i < file->extension_count(); i++) {
- // We honor the jspb::ignore option here only when working with
- // Closure-style imports. Use of this option is discouraged and so we want
- // to avoid adding new support for it.
- if (options.import_style == GeneratorOptions::kImportClosure &&
- IgnoreField(file->extension(i))) {
- continue;
- }
- provided.insert(GetNamespace(options, file) + "." +
- JSObjectFieldName(options, file->extension(i)));
- extensions.insert(file->extension(i));
- }
- FindProvidesForFile(options, printer, file, &provided);
- GenerateProvides(options, printer, &provided);
- std::vector<const FileDescriptor*> files;
- files.push_back(file);
- if (options.import_style == GeneratorOptions::kImportClosure) {
- GenerateRequiresForLibrary(options, printer, files, &provided);
- }
- GenerateClassesAndEnums(options, printer, file);
- // Generate code for top-level extensions. Extensions nested inside messages
- // are emitted inside GenerateClassesAndEnums().
- for (std::set<const FieldDescriptor*>::const_iterator it = extensions.begin();
- it != extensions.end(); ++it) {
- GenerateExtension(options, printer, *it);
- }
- // if provided is empty, do not export anything
- if (options.import_style == GeneratorOptions::kImportCommonJs &&
- !provided.empty()) {
- printer->Print("goog.object.extend(exports, $package$);\n", "package",
- GetNamespace(options, file));
- } else if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
- printer->Print("goog.object.extend(exports, proto);\n", "package",
- GetNamespace(options, file));
- }
- // Emit well-known type methods.
- for (FileToc* toc = well_known_types_js; toc->name != NULL; toc++) {
- std::string name = std::string("google/protobuf/") + toc->name;
- if (name == StripProto(file->name()) + ".js") {
- printer->Print(toc->data);
- }
- }
- }
- bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files,
- const std::string& parameter,
- GeneratorContext* context,
- std::string* error) const {
- std::vector<std::pair<std::string, std::string> > option_pairs;
- ParseGeneratorParameter(parameter, &option_pairs);
- GeneratorOptions options;
- if (!options.ParseFromOptions(option_pairs, error)) {
- return false;
- }
- if (options.output_mode() == GeneratorOptions::kEverythingInOneFile) {
- // All output should go in a single file.
- std::string filename = options.output_dir + "/" + options.library +
- options.GetFileNameExtension();
- std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
- GOOGLE_CHECK(output.get());
- GeneratedCodeInfo annotations;
- io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
- &annotations);
- io::Printer printer(
- output.get(), '$',
- options.annotate_code ? &annotation_collector : nullptr);
- // Pull out all extensions -- we need these to generate all
- // provides/requires.
- std::vector<const FieldDescriptor*> extensions;
- for (int i = 0; i < files.size(); i++) {
- for (int j = 0; j < files[i]->extension_count(); j++) {
- const FieldDescriptor* extension = files[i]->extension(j);
- extensions.push_back(extension);
- }
- }
- if (files.size() == 1) {
- GenerateHeader(options, files[0], &printer);
- } else {
- GenerateHeader(options, nullptr, &printer);
- }
- std::set<std::string> provided;
- FindProvides(options, &printer, files, &provided);
- FindProvidesForFields(options, &printer, extensions, &provided);
- GenerateProvides(options, &printer, &provided);
- GenerateTestOnly(options, &printer);
- GenerateRequiresForLibrary(options, &printer, files, &provided);
- GenerateFilesInDepOrder(options, &printer, files);
- for (int i = 0; i < extensions.size(); i++) {
- if (ShouldGenerateExtension(extensions[i])) {
- GenerateExtension(options, &printer, extensions[i]);
- }
- }
- if (printer.failed()) {
- return false;
- }
- if (options.annotate_code) {
- EmbedCodeAnnotations(annotations, &printer);
- }
- } else if (options.output_mode() == GeneratorOptions::kOneOutputFilePerSCC) {
- std::set<const Descriptor*> have_printed;
- SCCAnalyzer<DepsGenerator> analyzer;
- std::map<const void*, std::string> allowed_map;
- if (!GenerateJspbAllowedMap(options, files, &allowed_map, &analyzer,
- error)) {
- return false;
- }
- bool generated = false;
- for (int i = 0; i < files.size(); i++) {
- const FileDescriptor* file = files[i];
- // Force well known type to generate in a whole file.
- if (IsWellKnownTypeFile(file)) {
- if (!GenerateFile(file, options, context, true)) {
- return false;
- }
- generated = true;
- continue;
- }
- for (int j = 0; j < file->message_type_count(); j++) {
- const Descriptor* desc = file->message_type(j);
- if (have_printed.count(desc) ||
- allowed_map.count(analyzer.GetSCC(desc)) == 0) {
- continue;
- }
- generated = true;
- const SCC* scc = analyzer.GetSCC(desc);
- const std::string& filename = allowed_map[scc];
- std::unique_ptr<io::ZeroCopyOutputStream> output(
- context->Open(filename));
- GOOGLE_CHECK(output.get());
- GeneratedCodeInfo annotations;
- io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
- &annotations);
- io::Printer printer(
- output.get(), '$',
- options.annotate_code ? &annotation_collector : nullptr);
- GenerateHeader(options, file, &printer);
- std::set<std::string> provided;
- for (auto one_desc : scc->descriptors) {
- if (one_desc->containing_type() == nullptr) {
- FindProvidesForMessage(options, &printer, one_desc, &provided);
- }
- }
- GenerateProvides(options, &printer, &provided);
- GenerateTestOnly(options, &printer);
- GenerateRequiresForSCC(options, &printer, scc, &provided);
- for (auto one_desc : scc->descriptors) {
- if (one_desc->containing_type() == nullptr) {
- GenerateClassConstructorAndDeclareExtensionFieldInfo(
- options, &printer, one_desc);
- }
- }
- for (auto one_desc : scc->descriptors) {
- if (one_desc->containing_type() == nullptr) {
- GenerateClass(options, &printer, one_desc);
- }
- }
- for (auto one_desc : scc->descriptors) {
- have_printed.insert(one_desc);
- }
- if (printer.failed()) {
- return false;
- }
- if (options.annotate_code) {
- EmbedCodeAnnotations(annotations, &printer);
- }
- }
- for (int j = 0; j < file->enum_type_count(); j++) {
- const EnumDescriptor* enumdesc = file->enum_type(j);
- if (allowed_map.count(enumdesc) == 0) {
- continue;
- }
- generated = true;
- const std::string& filename = allowed_map[enumdesc];
- std::unique_ptr<io::ZeroCopyOutputStream> output(
- context->Open(filename));
- GOOGLE_CHECK(output.get());
- GeneratedCodeInfo annotations;
- io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
- &annotations);
- io::Printer printer(
- output.get(), '$',
- options.annotate_code ? &annotation_collector : nullptr);
- GenerateHeader(options, file, &printer);
- std::set<std::string> provided;
- FindProvidesForEnum(options, &printer, enumdesc, &provided);
- GenerateProvides(options, &printer, &provided);
- GenerateTestOnly(options, &printer);
- GenerateEnum(options, &printer, enumdesc);
- if (printer.failed()) {
- return false;
- }
- if (options.annotate_code) {
- EmbedCodeAnnotations(annotations, &printer);
- }
- }
- // File-level extensions (message-level extensions are generated under
- // the enclosing message).
- if (allowed_map.count(file) == 1) {
- generated = true;
- const std::string& filename = allowed_map[file];
- std::unique_ptr<io::ZeroCopyOutputStream> output(
- context->Open(filename));
- GOOGLE_CHECK(output.get());
- GeneratedCodeInfo annotations;
- io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
- &annotations);
- io::Printer printer(
- output.get(), '$',
- options.annotate_code ? &annotation_collector : nullptr);
- GenerateHeader(options, file, &printer);
- std::set<std::string> provided;
- std::vector<const FieldDescriptor*> fields;
- for (int j = 0; j < files[i]->extension_count(); j++) {
- if (ShouldGenerateExtension(files[i]->extension(j))) {
- fields.push_back(files[i]->extension(j));
- }
- }
- FindProvidesForFields(options, &printer, fields, &provided);
- GenerateProvides(options, &printer, &provided);
- GenerateTestOnly(options, &printer);
- GenerateRequiresForExtensions(options, &printer, fields, &provided);
- for (int j = 0; j < files[i]->extension_count(); j++) {
- if (ShouldGenerateExtension(files[i]->extension(j))) {
- GenerateExtension(options, &printer, files[i]->extension(j));
- }
- }
- if (options.annotate_code) {
- EmbedCodeAnnotations(annotations, &printer);
- }
- }
- }
- if (!generated) {
- std::string filename = options.output_dir + "/" +
- "empty_no_content_void_file" +
- options.GetFileNameExtension();
- std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
- }
- } else /* options.output_mode() == kOneOutputFilePerInputFile */ {
- // Generate one output file per input (.proto) file.
- for (int i = 0; i < files.size(); i++) {
- const FileDescriptor* file = files[i];
- if (!GenerateFile(file, options, context, false)) {
- return false;
- }
- }
- }
- return true;
- }
- } // namespace js
- } // namespace compiler
- } // namespace protobuf
- } // namespace google
|