Bug 1057541 - Part 3: Change codegen to support required dictionary members. r=khuey, a=lmandel
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 05 Sep 2014 14:28:43 -0400
changeset 225432 6b39085efdd96251b91c69b720d0e14995e37b30
parent 225431 e4605ac1576e3ac87bf8226e9df3d48a4e7f806b
child 225433 2bc3a755757300f719de801949f33570b59032da
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, lmandel
bugs1057541
milestone34.0a2
Bug 1057541 - Part 3: Change codegen to support required dictionary members. r=khuey, a=lmandel
dom/bindings/Codegen.py
dom/bindings/Errors.msg
dom/bindings/test/TestCodeGen.webidl
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -11115,20 +11115,19 @@ class CGDictionary(CGThing):
         self.memberInfo = [
             (member,
              getJSToNativeConversionInfo(
                  member.type,
                  descriptorProvider,
                  isEnforceRange=member.enforceRange,
                  isClamp=member.clamp,
                  isMember="Dictionary",
-                 isOptional=(not member.defaultValue),
+                 isOptional=member.canHaveMissingValue(),
                  defaultValue=member.defaultValue,
-                 sourceDescription=("'%s' member of %s" %
-                                    (member.identifier.name, dictionary.identifier.name))))
+                 sourceDescription=self.getMemberSourceDescription(member)))
             for member in dictionary.members]
 
         # If we have a union member containing something in the same
         # file as us, bail: the C++ includes won't work out.
         for member in dictionary.members:
             type = member.type.unroll()
             if type.isUnion():
                 for t in type.flatMemberTypes:
@@ -11318,17 +11317,17 @@ class CGDictionary(CGThing):
     def assignmentOperator(self):
         body = CGList([])
         if self.dictionary.parent:
             body.append(CGGeneric(
                 "%s::operator=(aOther);\n" %
                 self.makeClassName(self.dictionary.parent)))
         for m, _ in self.memberInfo:
             memberName = self.makeMemberName(m.identifier.name)
-            if not m.defaultValue:
+            if m.canHaveMissingValue():
                 memberAssign = CGGeneric(fill(
                     """
                     if (aOther.${name}.WasPassed()) {
                       ${name}.Construct();
                       ${name}.Value() = aOther.${name}.Value();
                     } else {
                       ${name}.Reset();
                     }
@@ -11472,32 +11471,45 @@ class CGDictionary(CGThing):
             "convert": string.Template(conversionInfo.template).substitute(replacements),
             "propGet": propGet
         }
         conversion = ("if (!isNull && !${propGet}) {\n"
                       "  return false;\n"
                       "}\n")
         if member.defaultValue:
             conversion += "${convert}"
+        elif not conversionInfo.dealWithOptional:
+            # We're required, but have no default value.  Make sure
+            # that we throw if we have no value provided.
+            conversion += dedent(
+                """
+                // Skip the undefined check if we have no cx.  In that
+                // situation the caller is default-constructing us and we'll
+                // just assume they know what they're doing.
+                if (cx && (isNull || temp->isUndefined())) {
+                  return ThrowErrorMessage(cx, MSG_MISSING_REQUIRED_DICTIONARY_MEMBER,
+                                           "%s");
+                }
+                ${convert}""" % self.getMemberSourceDescription(member))
         else:
             conversion += (
                 "if (!isNull && !temp->isUndefined()) {\n"
                 "  ${prop}.Construct();\n"
                 "${convert}"
                 "}\n")
             conversionReplacements["convert"] = indent(conversionReplacements["convert"])
 
         return CGGeneric(
             string.Template(conversion).substitute(conversionReplacements))
 
     def getMemberDefinition(self, memberInfo):
         member = memberInfo[0]
         declType = memberInfo[1].declType
         memberLoc = self.makeMemberName(member.identifier.name)
-        if member.defaultValue:
+        if not member.canHaveMissingValue():
             memberData = memberLoc
         else:
             # The data is inside the Optional<>
             memberData = "%s.InternalValue()" % memberLoc
 
         # If you have to change this list (which you shouldn't!), make sure it
         # continues to match the list in test_Object.prototype_props.html
         if (member.identifier.name in
@@ -11543,26 +11555,26 @@ class CGDictionary(CGThing):
         # Now make sure that our successCode can actually break out of the
         # conversion.  This incidentally gives us a scope for 'temp' and
         # 'currentValue'.
         conversion = CGWrapper(
             CGIndenter(conversion),
             pre=("do {\n"
                  "  // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"),
             post="} while(0);\n")
-        if not member.defaultValue:
+        if member.canHaveMissingValue():
             # Only do the conversion if we have a value
             conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
         return conversion
 
     def getMemberTrace(self, member):
         type = member.type
         assert typeNeedsRooting(type)
         memberLoc = self.makeMemberName(member.identifier.name)
-        if member.defaultValue:
+        if not member.canHaveMissingValue():
             memberData = memberLoc
         else:
             # The data is inside the Optional<>
             memberData = "%s.Value()" % memberLoc
 
         memberName = "%s.%s" % (self.makeClassName(self.dictionary),
                                 memberLoc)
 
@@ -11593,40 +11605,44 @@ class CGDictionary(CGThing):
         elif type.isMozMap():
             # If you implement this, add a MozMap<object> to
             # TestInterfaceJSDictionary and test it in test_bug1036214.html
             # to make sure we end up with the correct security properties.
             assert False
         else:
             assert False  # unknown type
 
-        if not member.defaultValue:
+        if member.canHaveMissingValue():
             trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
 
         return trace.define()
 
     def getMemberInitializer(self, memberInfo):
         """
         Get the right initializer for the member.  Most members don't need one,
         but we need to pre-initialize 'any' and 'object' that have a default
         value, so they're safe to trace at all times.
         """
         member, _ = memberInfo
-        if not member.defaultValue:
-            # No default value means no need to set it up front, since it's
+        if member.canHaveMissingValue():
+            # Allowed missing value means no need to set it up front, since it's
             # inside an Optional and won't get traced until it's actually set
             # up.
             return None
         type = member.type
         if type.isAny():
             return "JS::UndefinedValue()"
         if type.isObject():
             return "nullptr"
         return None
 
+    def getMemberSourceDescription(self, member):
+        return ("'%s' member of %s" %
+                (member.identifier.name, self.dictionary.identifier.name))
+
     @staticmethod
     def makeIdName(name):
         return name.replace("-", "_") + "_id"
 
     @staticmethod
     def getDictionaryDependenciesFromType(type):
         if type.isDictionary():
             return set([type.unroll().inner])
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -54,8 +54,9 @@ MSG_DEF(MSG_DEFINEPROPERTY_ON_GSP, 0, "N
 MSG_DEF(MSG_INVALID_URL, 1, "{0} is not a valid URL.")
 MSG_DEF(MSG_METADATA_NOT_CONFIGURED, 0, "Either size or lastModified should be true.")
 MSG_DEF(MSG_INVALID_READ_SIZE, 0, "0 (Zero) is not a valid read size.")
 MSG_DEF(MSG_HEADERS_IMMUTABLE, 0, "Headers are immutable and cannot be modified.")
 MSG_DEF(MSG_INVALID_HEADER_NAME, 1, "{0} is an invalid header name.")
 MSG_DEF(MSG_INVALID_HEADER_VALUE, 1, "{0} is an invalid header value.")
 MSG_DEF(MSG_INVALID_HEADER_SEQUENCE, 0, "Headers require name/value tuples when being initialized by a sequence.")
 MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, "Permission denied to pass cross-origin object as {0}.")
+MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, "Missing required {0}.")
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -946,16 +946,19 @@ dictionary Dict : ParentDict {
 
   sequence<long> seq1;
   sequence<long> seq2 = [];
   sequence<long>? seq3;
   sequence<long>? seq4 = null;
   sequence<long>? seq5 = [];
 
   long dashed-name;
+
+  required long requiredLong;
+  required object requiredObject;
 };
 
 dictionary ParentDict : GrandparentDict {
   long c = 5;
   TestInterface someInterface;
   TestInterface? someNullableInterface = null;
   TestExternalInterface someExternalInterface;
   any parentAny;