Changes to make flatbuffers javascript compatible with the closure compiler.
authorWouter van Oortmerssen <aardappel@gmail.com>
Thu, 9 Feb 2017 01:08:47 +0000 (17:08 -0800)
committerWouter van Oortmerssen <aardappel@gmail.com>
Thu, 9 Feb 2017 01:14:35 +0000 (17:14 -0800)
Change-Id: Iab8d66a8f34910029deb8a5ff5ec7ba50c5b3421

docs/source/Compiler.md
include/flatbuffers/idl.h
js/flatbuffers.js
src/flatc.cpp
src/idl_gen_js.cpp

index b71b8c4..b83aace 100755 (executable)
@@ -93,6 +93,12 @@ Additional options:
     output (by default the case for C++ and JS), all code will end up in
     this one file.
 
+-   `--no-js-exports` :  Removes Node.js style export lines (useful for JS)
+
+-   `--goog-js-export` :  Uses goog.exportsSymbol and goog.exportsProperty
+    instead of Node.js style exporting.  Needed for compatibility with the
+    Google closure compiler (useful for JS).
+
 -   `--raw-binary` : Allow binaries without a file_indentifier to be read.
     This may crash flatc given a mismatched schema.
 
index 0956d18..186cae0 100644 (file)
@@ -337,6 +337,7 @@ struct ServiceDef : public Definition {
 struct IDLOptions {
   bool strict_json;
   bool skip_js_exports;
+  bool use_goog_js_export_format;
   bool output_default_scalars_in_json;
   int indent_step;
   bool output_enum_identifiers;
@@ -378,6 +379,7 @@ struct IDLOptions {
   IDLOptions()
     : strict_json(false),
       skip_js_exports(false),
+      use_goog_js_export_format(false),
       output_default_scalars_in_json(false),
       indent_step(2),
       output_enum_identifiers(true), prefixed_enums(true), scoped_enums(false),
index c1296d5..3ea1adf 100644 (file)
@@ -2,6 +2,15 @@
 /// @addtogroup flatbuffers_javascript_api
 /// @{
 /// @cond FLATBUFFERS_INTERNAL
+
+/**
+ * @fileoverview
+ *
+ * Need to suppress 'global this' error so the Node.js export line doesn't cause
+ * closure compile to error out.
+ * @suppress {globalThis}
+ */
+
 /**
  * @const
  * @namespace
@@ -530,6 +539,10 @@ flatbuffers.Builder.prototype.offset = function() {
  * @param {flatbuffers.ByteBuffer} bb The current buffer with the existing data
  * @returns {flatbuffers.ByteBuffer} A new byte buffer with the old data copied
  * to it. The data is located at the end of the buffer.
+ *
+ * uint8Array.set() formally takes {Array<number>|ArrayBufferView}, so to pass
+ * it a uint8Array we need to suppress the type check:
+ * @suppress {checkTypes}
  */
 flatbuffers.Builder.growByteBuffer = function(bb) {
   var old_buf_size = bb.capacity();
index 5d426cc..5fbb8db 100644 (file)
@@ -86,6 +86,8 @@ std::string FlatCompiler::GetUsageString(const char* program_name) const {
       "  --escape-proto-ids Disable appending '_' in namespaces names.\n"
       "  --gen-object-api   Generate an additional object-based API.\n"
       "  --cpp-ptr-type T   Set object API pointer type (default std::unique_ptr)\n"
+      "  --no-js-exports    Removes Node.js style export lines in JS.\n"
+      "  --goog-js-export   Uses goog.exports* for closure compiler exporting in JS.\n"
       "  --raw-binary       Allow binaries without file_indentifier to be read.\n"
       "                     This may crash flatc given a mismatched schema.\n"
       "  --proto            Input is a .proto, translate to .fbs.\n"
@@ -146,6 +148,8 @@ int FlatCompiler::Compile(int argc, const char** argv) {
         opts.allow_non_utf8 = true;
       } else if(arg == "--no-js-exports") {
         opts.skip_js_exports = true;
+      } else if(arg == "--goog-js-export") {
+        opts.use_goog_js_export_format = true;
       } else if(arg == "--defaults-json") {
         opts.output_default_scalars_in_json = true;
       } else if (arg == "--unknown-json") {
index dd04bec..e1daf0a 100644 (file)
@@ -113,7 +113,11 @@ class JsGenerator : public BaseGenerator {
     code += "/**\n * @const\n * @namespace\n */\n";
     if (it->find('.') == std::string::npos) {
       code += "var ";
-      exports += "this." + *it + " = " + *it + ";\n";
+      if(parser_.opts.use_goog_js_export_format) {
+        exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n";
+      } else {
+        exports += "this." + *it + " = " + *it + ";\n";
+      }
     }
     code += *it + " = " + *it + " || {};\n\n";
   }
@@ -172,7 +176,12 @@ void GenEnum(EnumDef &enum_def, std::string *code_ptr,
   GenDocComment(enum_def.doc_comment, code_ptr, "@enum");
   if (enum_def.defined_namespace->components.empty()) {
     code += "var ";
-    exports += "this." + enum_def.name + " = " + enum_def.name + ";\n";
+    if(parser_.opts.use_goog_js_export_format) {
+      exports += "goog.exportSymbol('" + enum_def.name + "', " + enum_def.name +
+        ");\n";
+    } else {
+      exports += "this." + enum_def.name + " = " + enum_def.name + ";\n";
+    }
   }
   code += WrapInNameSpace(enum_def) + " = {\n";
   for (auto it = enum_def.vals.vec.begin();
@@ -236,6 +245,9 @@ std::string GenDefaultValue(const Value &value, const std::string &context) {
     if (auto val = value.type.enum_def->ReverseLookup(
         atoi(value.constant.c_str()), false)) {
       return WrapInNameSpace(*value.type.enum_def) + "." + val->name;
+    } else {
+      return "/** @type {" + WrapInNameSpace(*value.type.enum_def) + "} */ ("
+        + value.constant + ")";
     }
   }
 
@@ -371,7 +383,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt
   std::string object_name = WrapInNameSpace(struct_def);
   GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
   if (isStatement) {
-    exports += "this." + struct_def.name + " = " + struct_def.name + ";\n";
+    if(parser_.opts.use_goog_js_export_format) {
+      exports += "goog.exportSymbol('" + struct_def.name + "', " +
+        struct_def.name + ");\n";
+    } else {
+      exports += "this." + struct_def.name + " = " + struct_def.name + ";\n";
+    }
     code += "function " + object_name;
   } else {
     code += object_name + " = function";
@@ -526,7 +543,13 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt
               field.value.type.element == BASE_TYPE_ULONG) {
             code += "this.bb.createLong(0, 0)";
           } else if (IsScalar(field.value.type.element)) {
-            code += "0";
+            if (field.value.type.enum_def) {
+              code += "/** @type {" +
+                WrapInNameSpace(*field.value.type.enum_def) + "} */ (" +
+                field.value.constant + ")";
+            } else {
+              code += "0";
+            }
           } else {
             code += "null";
           }
@@ -550,6 +573,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt
     }
     code += "};\n\n";
 
+    if(parser_.opts.use_goog_js_export_format) {
+      exports += "goog.exportProperty(" + object_name + ".prototype, '" +
+        MakeCamel(field.name, false) + "', " + object_name + ".prototype." +
+        MakeCamel(field.name, false) + ");\n";
+    }
+
     // Adds the mutable scalar value to the output
     if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer) {
       std::string annotations = "@param {" + GenTypeName(field.value.type, true) + "} value\n";
@@ -564,6 +593,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt
       code += "  this.bb.write" + MakeCamel(GenType(field.value.type)) + "(this.bb_pos + offset, value);\n";
       code += "  return true;\n";
       code += "};\n\n";
+
+      if(parser_.opts.use_goog_js_export_format) {
+        exports += "goog.exportProperty(" + object_name +
+          ".prototype, 'mutate_" + field.name + "', " + object_name +
+          ".prototype.mutate_" + field.name + ");\n";
+      }
     }
 
     // Emit vector helpers
@@ -574,6 +609,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt
       code += "Length = function() {\n" + offset_prefix;
       code += "this.bb.__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
 
+      if(parser_.opts.use_goog_js_export_format) {
+        exports += "goog.exportProperty(" + object_name + ".prototype, '" +
+          MakeCamel(field.name, false) + "Length', " + object_name +
+          ".prototype." + MakeCamel(field.name, false) + "Length);\n";
+      }
+
       // For scalar types, emit a typed array helper
       auto vectorType = field.value.type.VectorType();
       if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
@@ -583,6 +624,12 @@ void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_pt
         code += "new " + GenType(vectorType) + "Array(this.bb.bytes().buffer, "
           "this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), "
           "this.bb.__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
+
+        if(parser_.opts.use_goog_js_export_format) {
+          exports += "goog.exportProperty(" + object_name + ".prototype, '" +
+            MakeCamel(field.name, false) + "Array', " + object_name +
+            ".prototype." + MakeCamel(field.name, false) + "Array);\n";
+        }
       }
     }
   }