Fixed parsing for string->double maps. (#243)

Map parsing/serializing relies on map entries always
having a predictable order.  The code that generates
layout was not respecting this in the case of string
keys and primitive values.
diff --git a/BUILD b/BUILD
index ef2fcd2..9d3ae34 100644
--- a/BUILD
+++ b/BUILD
@@ -354,11 +354,24 @@
     ],
 )
 
+proto_library(
+    name = "test_proto",
+    testonly = 1,
+    srcs = ["tests/test.proto"],
+)
+
+upb_proto_library(
+    name = "test_upbproto",
+    testonly = 1,
+    deps = [":test_proto"],
+)
+
 cc_test(
     name = "test_generated_code",
     srcs = ["tests/test_generated_code.c"],
     deps = [
         ":test_messages_proto3_proto_upb",
+        ":test_upbproto",
         ":upb_test",
     ],
 )
@@ -657,6 +670,7 @@
         "@com_google_protobuf//:descriptor_proto",
         ":descriptor_proto_lua",
         ":test_messages_proto3_proto_lua",
+        ":test_proto_lua",
         "tests/bindings/lua/test_upb.lua",
         "third_party/lunit/console.lua",
         "third_party/lunit/lunit.lua",
@@ -683,8 +697,13 @@
 )
 
 lua_proto_library(
+    name = "test_proto_lua",
+    testonly = 1,
+    deps = [":test_proto"],
+)
+
+lua_proto_library(
     name = "descriptor_proto_lua",
-    visibility = ["//visibility:public"],
     deps = ["@com_google_protobuf//:descriptor_proto"],
 )
 
diff --git a/tests/bindings/lua/main.c b/tests/bindings/lua/main.c
index 5b50173..f3714b5 100644
--- a/tests/bindings/lua/main.c
+++ b/tests/bindings/lua/main.c
@@ -27,8 +27,10 @@
     "./third_party/lunit/?.lua;"
     "external/com_google_protobuf/?.lua;"
     "external/com_google_protobuf/src/?.lua;"
+    "bazel-bin/?.lua;"
     "bazel-bin/external/com_google_protobuf/src/?.lua;"
     "bazel-bin/external/com_google_protobuf/?.lua;"
+    "bazel-bin/external/com_google_protobuf/?.lua;"
     "upb/bindings/lua/?.lua"
   "'";
 
diff --git a/tests/bindings/lua/test.proto b/tests/bindings/lua/test.proto
new file mode 100644
index 0000000..322a967
--- /dev/null
+++ b/tests/bindings/lua/test.proto
@@ -0,0 +1,7 @@
+syntax = "proto2";
+
+package lua_test;
+
+message TestLua {
+  map<string, double> map_string_double = 1;
+}
diff --git a/tests/bindings/lua/test_upb.lua b/tests/bindings/lua/test_upb.lua
index 25ec0c1..fb76224 100644
--- a/tests/bindings/lua/test_upb.lua
+++ b/tests/bindings/lua/test_upb.lua
@@ -1,6 +1,7 @@
 
 local upb = require "lupb"
 local lunit = require "lunit"
+local upb_test = require "tests.test_pb"
 local test_messages_proto3 = require "google.protobuf.test_messages_proto3_pb"
 local descriptor = require "google.protobuf.descriptor_pb"
 
@@ -68,6 +69,32 @@
   assert_equal(12, msg2.map_int32_int32[6])
 end
 
+function test_string_double_map()
+  msg = upb_test.MapTest()
+  msg.map_string_double["one"] = 1.0
+  msg.map_string_double["two point five"] = 2.5
+  assert_equal(1, msg.map_string_double["one"])
+  assert_equal(2.5, msg.map_string_double["two point five"])
+
+  -- Test overwrite.
+  msg.map_string_double["one"] = 2
+  assert_equal(2, msg.map_string_double["one"])
+  assert_equal(2.5, msg.map_string_double["two point five"])
+  msg.map_string_double["one"] = 1.0
+
+  -- Test delete.
+  msg.map_string_double["one"] = nil
+  assert_nil(msg.map_string_double["one"])
+  assert_equal(2.5, msg.map_string_double["two point five"])
+  msg.map_string_double["one"] = 1
+
+  local serialized = upb.encode(msg)
+  assert_true(#serialized > 0)
+  local msg2 = upb.decode(upb_test.MapTest, serialized)
+  assert_equal(1, msg2.map_string_double["one"])
+  assert_equal(2.5, msg2.map_string_double["two point five"])
+end
+
 function test_msg_string_map()
   msg = test_messages_proto3.TestAllTypesProto3()
   msg.map_string_string["foo"] = "bar"
diff --git a/tests/test.proto b/tests/test.proto
index e790cbf..c4b7e9c 100644
--- a/tests/test.proto
+++ b/tests/test.proto
@@ -1,68 +1,8 @@
 
-// A series of messages with various kinds of cycles in them.
-//      +-+---+    +---+    +---+
-//      V |   |    V   |    V   |
-// A -> B-+-> C -> D---+--->E---+
-// ^          |`---|--------^
-// +----------+----+        F
-
 syntax = "proto2";
 
-message A {
-  optional B b = 1;
-}
+package upb_test;
 
-message B {
-  optional B b = 1;
-  optional C c = 2;
-}
-
-message C {
-  optional A a = 1;
-  optional B b = 2;
-  optional D d = 3;
-  optional E e = 4;
-}
-
-message D {
-  optional A a = 1;
-  optional D d = 2;
-  optional E e = 3;
-}
-
-message E {
-  optional E e = 1;
-}
-
-message F {
-  optional E e = 1;
-}
-
-// A proto with a bunch of simple primitives.
-message SimplePrimitives {
-  optional fixed64 u64 = 1;
-  optional fixed32 u32 = 2;
-  optional double dbl = 3;
-  optional float flt = 5;
-  optional sint64 i64 = 6;
-  optional sint32 i32 = 7;
-  optional bool b = 8;
-  optional string str = 9;
-
-  oneof foo {
-    int32 oneof_int32 = 10;
-    string oneof_string = 11;
-  }
-
-  oneof bar {
-    int64 oneof_int64 = 13;
-    bytes oneof_bytes = 14;
-  }
-
-  message Nested {
-    oneof foo {
-      int32 oneof_int32 = 10;
-      string b = 11;
-    }
-  }
+message MapTest {
+  map<string, double> map_string_double = 1;
 }
diff --git a/tests/test_generated_code.c b/tests/test_generated_code.c
index 3d1f518..ef8575c 100644
--- a/tests/test_generated_code.c
+++ b/tests/test_generated_code.c
@@ -5,6 +5,7 @@
 
 #include "src/google/protobuf/test_messages_proto3.upb.h"
 #include "tests/upb_test.h"
+#include "tests/test.upb.h"
 
 const char test_str[] = "abcdefg";
 const char test_str2[] = "12345678910";
@@ -114,6 +115,25 @@
   ASSERT(!const_ent);
 }
 
+static void test_string_double_map() {
+  upb_arena *arena = upb_arena_new();
+  upb_strview serialized;
+  upb_test_MapTest *msg = upb_test_MapTest_new(arena);
+  upb_test_MapTest *msg2;
+  double val;
+
+  upb_test_MapTest_map_string_double_set(msg, test_str_view, 1.5, arena);
+  serialized.data = upb_test_MapTest_serialize(msg, arena, &serialized.size);
+  ASSERT(serialized.data);
+
+  msg2 = upb_test_MapTest_parse(serialized.data, serialized.size, arena);
+  ASSERT(msg2);
+  ASSERT(upb_test_MapTest_map_string_double_get(msg2, test_str_view, &val));
+  ASSERT(val == 1.5);
+
+  upb_arena_free(arena);
+}
+
 static void test_string_map() {
   upb_arena *arena = upb_arena_new();
   protobuf_test_messages_proto3_TestAllTypesProto3 *msg =
@@ -323,6 +343,7 @@
 int run_tests(int argc, char *argv[]) {
   test_scalars();
   test_string_map();
+  test_string_double_map();
   test_int32_map();
   test_repeated();
   return 0;
diff --git a/upb/def.c b/upb/def.c
index f9de1a9..1b44aec 100644
--- a/upb/def.c
+++ b/upb/def.c
@@ -921,6 +921,32 @@
   l->fields = fields;
   l->submsgs = submsgs;
 
+  if (upb_msgdef_mapentry(m)) {
+    /* TODO(haberman): refactor this method so this special case is more
+     * elegant. */
+    const upb_fielddef *key = upb_msgdef_itof(m, 1);
+    const upb_fielddef *val = upb_msgdef_itof(m, 2);
+    fields[0].number = 1;
+    fields[1].number = 2;
+    fields[0].label = UPB_LABEL_OPTIONAL;
+    fields[1].label = UPB_LABEL_OPTIONAL;
+    fields[0].presence = 0;
+    fields[1].presence = 0;
+    fields[0].descriptortype = upb_fielddef_descriptortype(key);
+    fields[1].descriptortype = upb_fielddef_descriptortype(val);
+    fields[0].offset = 0;
+    fields[1].offset = sizeof(upb_strview);
+    fields[1].submsg_index = 0;
+
+    if (upb_fielddef_type(val) == UPB_TYPE_MESSAGE) {
+      submsgs[0] = upb_fielddef_msgsubdef(val)->layout;
+    }
+
+    l->field_count = 2;
+    l->size = 2 * sizeof(upb_strview);align_up(l->size, 8);
+    return true;
+  }
+
   /* Allocate data offsets in three stages:
    *
    * 1. hasbits.
diff --git a/upbc/message_layout.cc b/upbc/message_layout.cc
index bf3eb2b..cb7f7f9 100644
--- a/upbc/message_layout.cc
+++ b/upbc/message_layout.cc
@@ -32,12 +32,7 @@
 
 MessageLayout::SizeAndAlign MessageLayout::SizeOf(
     const protobuf::FieldDescriptor* field) {
-  if (field->containing_type()->options().map_entry()) {
-    // Map entries aren't actually stored, they are only used during parsing.
-    // For parsing, it helps a lot if all map entry messages have the same
-    // layout.
-    return {{8, 16}, {4, 8}};  // upb_stringview
-  } else if (field->is_repeated()) {
+  if (field->is_repeated()) {
     return {{4, 8}, {4, 8}};  // Pointer to array object.
   } else {
     return SizeOfUnwrapped(field);
@@ -50,7 +45,7 @@
     case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
       return {{4, 8}, {4, 8}};  // Pointer to message.
     case protobuf::FieldDescriptor::CPPTYPE_STRING:
-      return {{8, 16}, {4, 8}};  // upb_stringview
+      return {{8, 16}, {4, 8}};  // upb_strview
     case protobuf::FieldDescriptor::CPPTYPE_BOOL:
       return {{1, 1}, {1, 1}};
     case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
@@ -111,8 +106,18 @@
 void MessageLayout::ComputeLayout(const protobuf::Descriptor* descriptor) {
   size_ = Size{0, 0};
   maxalign_ = Size{0, 0};
-  PlaceNonOneofFields(descriptor);
-  PlaceOneofFields(descriptor);
+
+  if (descriptor->options().map_entry()) {
+    // Map entries aren't actually stored, they are only used during parsing.
+    // For parsing, it helps a lot if all map entry messages have the same
+    // layout.
+    SizeAndAlign size{{8, 16}, {4, 8}};  // upb_strview
+    field_offsets_[descriptor->FindFieldByNumber(1)] = Place(size);
+    field_offsets_[descriptor->FindFieldByNumber(2)] = Place(size);
+  } else {
+    PlaceNonOneofFields(descriptor);
+    PlaceOneofFields(descriptor);
+  }
 
   // Align overall size up to max size.
   size_.AlignUp(maxalign_);