dom/bindings/Codegen.py
changeset 112789 1c1fa8af5978ed6bf0b038839750228a63d40a70
parent 112678 02a99623c5529ed02025385d67bf7e8aa800571b
child 112825 c348f36c317ba4e414f6de7d83aeacbf0b9d525d
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5074,92 +5074,100 @@ class CGProxyIndexedOperation(CGProxySpe
 
 class CGProxyIndexedGetter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed getter. If templateValues is not None
     the returned value will be wrapped with wrapForType using templateValues.
     """
     def __init__(self, descriptor, templateValues=None):
         self.templateValues = templateValues
-        CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter')
+        CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter')
 
 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
     """
     Class to generate a call that checks whether an indexed property exists.
 
     For now, we just delegate to CGProxyIndexedGetter
     """
     def __init__(self, descriptor):
         CGProxyIndexedGetter.__init__(self, descriptor)
 
 class CGProxyIndexedSetter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed setter.
     """
     def __init__(self, descriptor):
-        CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter')
+        CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter')
 
 class CGProxyNamedOperation(CGProxySpecialOperation):
     """
     Class to generate a call to a named operation.
-    """
-    def __init__(self, descriptor, name):
+
+    'value' is the jsval to use for the name; None indicates that it should be
+    gotten from the property id.
+    """
+    def __init__(self, descriptor, name, value=None):
         CGProxySpecialOperation.__init__(self, descriptor, name)
+        if value is None:
+            value = "js::IdToValue(id)"
+        self.value = value
     def define(self):
         # Our first argument is the id we're getting.
         argName = self.arguments[0].identifier.name
-        return (("JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n"
+        return (("JS::Value nameVal = %s;\n"
                  "FakeDependentString %s;\n"
                  "if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n"
                  "                            eStringify, eStringify, %s)) {\n"
                  "  return false;\n"
                  "}\n"
                  "\n"
                  "%s* self = UnwrapProxy(proxy);\n" %
-                 (argName, argName, self.descriptor.nativeType)) +
+                 (self.value, argName, argName, self.descriptor.nativeType)) +
                 CGProxySpecialOperation.define(self))
 
 class CGProxyNamedGetter(CGProxyNamedOperation):
     """
     Class to generate a call to an named getter. If templateValues is not None
     the returned value will be wrapped with wrapForType using templateValues.
-    """
-    def __init__(self, descriptor, templateValues=None):
+    'value' is the jsval to use for the name; None indicates that it should be
+    gotten from the property id.
+    """
+    def __init__(self, descriptor, templateValues=None, value=None):
         self.templateValues = templateValues
-        CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter')
+        CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value)
 
 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
     """
     Class to generate a call that checks whether a named property exists.
 
     For now, we just delegate to CGProxyNamedGetter
     """
     def __init__(self, descriptor):
         CGProxyNamedGetter.__init__(self, descriptor)
 
 class CGProxyNamedSetter(CGProxyNamedOperation):
     """
     Class to generate a call to a named setter.
     """
     def __init__(self, descriptor):
-        CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
+        CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter')
 
 class CGProxyIndexedDeleter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed deleter.
     """
     def __init__(self, descriptor):
-        CGProxySpecialOperation.__init__(self, descriptor, 'IndexedDeleter')
+        CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter')
 
 class CGProxyNamedDeleter(CGProxyNamedOperation):
     """
     Class to generate a call to a named deleter.
     """
     def __init__(self, descriptor):
-        CGProxySpecialOperation.__init__(self, descriptor, 'NamedDeleter')
+        CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter')
 
 class CGProxyIsProxy(CGAbstractMethod):
     def __init__(self, descriptor):
         args = [Argument('JSObject*', 'obj')]
         CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
     def declare(self):
         return ""
     def definition_body(self):
@@ -5209,38 +5217,44 @@ class CGDOMJSProxyHandler_getOwnProperty
 
         setOrIndexedGet = ""
         if self.descriptor.supportsIndexedProperties():
             setOrIndexedGet += "int32_t index = GetArrayIndexFromId(cx, id);\n"
             readonly = toStringBool(indexedSetter is None)
             fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
             templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
                               'obj': 'proxy', 'successCode': fillDescriptor}
-            get = ("if (index >= 0) {\n" +
+            get = ("if (IsArrayIndex(index)) {\n" +
                    CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
                    "}\n") % (self.descriptor.nativeType)
 
         if indexedSetter or self.descriptor.operations['NamedSetter']:
             setOrIndexedGet += "if (set) {\n"
             if indexedSetter:
-                setOrIndexedGet += ("  if (index >= 0) {\n")
+                setOrIndexedGet += ("  if (IsArrayIndex(index)) {\n")
                 if not 'IndexedCreator' in self.descriptor.operations:
-                    # FIXME need to check that this is a 'supported property index'
+                    # FIXME need to check that this is a 'supported property
+                    # index'.  But if that happens, watch out for the assumption
+                    # below that the name setter always returns for
+                    # IsArrayIndex(index).
                     assert False
                 setOrIndexedGet += ("    FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
                                     "    return true;\n" +
                                     "  }\n")
             if self.descriptor.operations['NamedSetter']:
-                setOrIndexedGet += "  if (JSID_IS_STRING(id)) {\n"
                 if not 'NamedCreator' in self.descriptor.operations:
                     # FIXME need to check that this is a 'supported property name'
                     assert False
-                setOrIndexedGet += ("    FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
-                                    "    return true;\n" +
-                                    "  }\n")
+                create = CGGeneric("FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n"
+                                   "return true;")
+                # If we have an indexed setter we've already returned
+                if (self.descriptor.supportsIndexedProperties() and
+                    not indexedSetter):
+                    create = CGIfWrapper(create, "!IsArrayIndex(index)")
+                setOrIndexedGet += CGIndenter(create).define() + "\n"
             setOrIndexedGet += "}"
             if indexedGetter:
                 setOrIndexedGet += (" else {\n" +
                                     CGIndenter(CGGeneric(get)).define() +
                                     "}")
             setOrIndexedGet += "\n\n"
         elif indexedGetter:
             setOrIndexedGet += ("if (!set) {\n" +
@@ -5250,20 +5264,23 @@ class CGDOMJSProxyHandler_getOwnProperty
         if self.descriptor.supportsNamedProperties():
             readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
             fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
             templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
                               'obj': 'proxy', 'successCode': fillDescriptor}
             # Once we start supporting OverrideBuiltins we need to make
             # ResolveOwnProperty or EnumerateOwnProperties filter out named
             # properties that shadow prototype properties.
+            condition = "!set && !HasPropertyOnPrototype(cx, proxy, this, id)"
+            if self.descriptor.supportsIndexedProperties():
+                condition = "!IsArrayIndex(index) && " + condition
             namedGet = ("\n" +
-                        "if (!set && JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
-                        CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
-                        "}\n")
+                        CGIfWrapper(CGProxyNamedGetter(self.descriptor, templateValues),
+                                    condition).define() +
+                        "\n")
         else:
             namedGet = ""
 
         return setOrIndexedGet + """JSObject* expando;
 if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
   unsigned flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED;
   if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc)) {
     return false;
@@ -5288,41 +5305,50 @@ class CGDOMJSProxyHandler_defineProperty
     def getBody(self):
         set = ""
 
         indexedSetter = self.descriptor.operations['IndexedSetter']
         if indexedSetter:
             if not (self.descriptor.operations['IndexedCreator'] is indexedSetter):
                 raise TypeError("Can't handle creator that's different from the setter")
             set += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
-                    "if (index >= 0) {\n" +
+                    "if (IsArrayIndex(index)) {\n" +
                     CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
                     "  return true;\n" +
                     "}\n") % (self.descriptor.nativeType)
         elif self.descriptor.supportsIndexedProperties():
-            set += ("if (GetArrayIndexFromId(cx, id) >= 0) {\n" +
+            # XXXbz Once this is fixed to only throw in strict mode, update the
+            # code that decides whether to do a
+            # CGDOMJSProxyHandler_defineProperty at all.
+            set += ("if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {\n" +
                     "  return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
                     "}\n") % self.descriptor.name
 
         namedSetter = self.descriptor.operations['NamedSetter']
         if namedSetter:
             if not self.descriptor.operations['NamedCreator'] is namedSetter:
                 raise TypeError("Can't handle creator that's different from the setter")
-            set += ("if (JSID_IS_STRING(id)) {\n" +
-                    CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
-                    "  return true;\n" +
-                    "}\n")
-        elif self.descriptor.supportsNamedProperties():
-            set += ("if (JSID_IS_STRING(id)) {\n" +
-                    CGIndenter(CGProxyNamedPresenceChecker(self.descriptor)).define() +
-                    "  if (found) {\n"
-                    "    return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
-                    "  }\n" +
-                    "}\n") % (self.descriptor.name)
-        return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args)
+            # If we support indexed properties, we won't get down here for
+            # indices, so we can just do our setter unconditionally here.
+            set += (CGProxyNamedSetter(self.descriptor).define() + "\n" +
+                    "return true;\n")
+        else:
+            if self.descriptor.supportsNamedProperties():
+                # XXXbz Once this is fixed to only throw in strict mode, update
+                # the code that decides whether to do a
+                # CGDOMJSProxyHandler_defineProperty at all.  If we support
+                # indexed properties, we won't get down here for indices, so we
+                # can just do our setter unconditionally here.
+                set += (CGProxyNamedPresenceChecker(self.descriptor).define() + "\n" +
+                        "if (found) {\n"
+                        "  return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n"
+                        "}" % self.descriptor.name)
+            set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);" %
+                    ", ".join(a.name for a in self.args))
+        return set
 
 class CGDOMJSProxyHandler_delete(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
                 Argument('jsid', 'id'),
                 Argument('bool*', 'bp')]
         ClassMethod.__init__(self, "delete_", "bool", args)
         self.descriptor = descriptor
@@ -5360,25 +5386,28 @@ class CGDOMJSProxyHandler_delete(ClassMe
                 body = None
             return body
 
         delete = ""
 
         indexedBody = getDeleterBody("Indexed")
         if indexedBody is not None:
             delete += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
-                       "if (index >= 0) {\n" +
+                       "if (IsArrayIndex(index)) {\n" +
                        CGIndenter(CGGeneric(indexedBody)).define() + "\n"
                        "  // We always return here, even if the property was not found\n"
                        "  return true;\n" +
                        "}\n") % self.descriptor.nativeType
 
         namedBody = getDeleterBody("Named")
         if namedBody is not None:
-            delete += ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+            # We always return above for an index id in the case when we support
+            # indexed properties, so we can just treat the id as a name
+            # unconditionally here.
+            delete += ("if (!HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
                        CGIndenter(CGGeneric(namedBody)).define() + "\n"
                        "  if (found) {\n"
                        "    return true;\n"
                        "  }\n"
                        "}\n")
 
         delete += "return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);";
 
@@ -5428,26 +5457,28 @@ class CGDOMJSProxyHandler_hasOwn(ClassMe
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
                 Argument('jsid', 'id'), Argument('bool*', 'bp')]
         ClassMethod.__init__(self, "hasOwn", "bool", args)
         self.descriptor = descriptor
     def getBody(self):
         if self.descriptor.supportsIndexedProperties():
             indexed = ("int32_t index = GetArrayIndexFromId(cx, id);\n" + 
-                       "if (index >= 0) {\n" +
+                       "if (IsArrayIndex(index)) {\n" +
                        CGIndenter(CGProxyIndexedPresenceChecker(self.descriptor)).define() + "\n" +
                        "  *bp = found;\n" +
                        "  return true;\n" +
                        "}\n\n") % (self.descriptor.nativeType)
         else:
             indexed = ""
 
         if self.descriptor.supportsNamedProperties():
-            named = ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+            # If we support indexed properties we always return above for index
+            # property names, so no need to check for those here.
+            named = ("if (!HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
                      CGIndenter(CGProxyNamedPresenceChecker(self.descriptor)).define() + "\n" +
                      "  *bp = found;\n"
                      "  return true;\n"
                      "}\n" +
                      "\n")
         else:
             named = ""
 
@@ -5483,46 +5514,49 @@ if (expando) {
     return JS_GetPropertyById(cx, expando, id, vp);
   }
 }"""
 
         templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', 'obj': 'proxy'}
 
         if self.descriptor.supportsIndexedProperties():
             getIndexedOrExpando = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
-                                   "if (index >= 0) {\n" +
+                                   "if (IsArrayIndex(index)) {\n" +
                                    CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) % (self.descriptor.nativeType)
             getIndexedOrExpando += """
   // Even if we don't have this index, we don't forward the
   // get on to our expando object.
 } else {
   %s
 }
 """ % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n  ')))
         else:
             getIndexedOrExpando = getFromExpando + "\n"
 
         if self.descriptor.supportsNamedProperties():
-            getNamed = ("if (JSID_IS_STRING(id)) {\n" +
-                        CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
-                        "}\n")
+            getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
+            if self.descriptor.supportsIndexedProperties():
+                getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
+            getNamed = getNamed.define() + "\n"
         else:
             getNamed = ""
 
         return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
             "Should not have a XrayWrapper here");
 
 %s
-bool found;
-if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp)) {
-  return false;
-}
-
-if (found) {
-  return true;
+{  // Scope for this "found" so it doesn't leak to things below
+  bool found;
+  if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp)) {
+    return false;
+  }
+
+  if (found) {
+    return true;
+  }
 }
 %s
 vp->setUndefined();
 return true;""" % (getIndexedOrExpando, getNamed)
 
 class CGDOMJSProxyHandler_obj_toString(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy')]
@@ -5544,26 +5578,30 @@ class CGDOMJSProxyHandler_getElementIfPr
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
                 Argument('JSObject*', 'receiver'),
                 Argument('uint32_t', 'index'),
                 Argument('JS::Value*', 'vp'), Argument('bool*', 'present')]
         ClassMethod.__init__(self, "getElementIfPresent", "bool", args)
         self.descriptor = descriptor
     def getBody(self):
+        successCode = ("*present = found;\n"
+                       "return true;")
+        templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+                          'obj': 'proxy', 'successCode': successCode}
         if self.descriptor.supportsIndexedProperties():
-            successCode = """*present = found;
-return true;"""
-            templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp',
-                              'obj': 'proxy', 'successCode': successCode}
             get = (CGProxyIndexedGetter(self.descriptor, templateValues).define() + "\n"
-                   "// We skip the expando object if there is an indexed getter.\n" +
+                   "// We skip the expando object and any named getters if\n"
+                   "// there is an indexed getter.\n" +
                    "\n") % (self.descriptor.nativeType)
         else:
-            get = """
+            if self.descriptor.supportsNamedProperties():
+                get = CGProxyNamedGetter(self.descriptor, templateValues,
+                                         "UINT_TO_JSVAL(index)").define()
+            get += """
 
 JSObject* expando = GetExpandoObject(proxy);
 if (expando) {
   JSBool isPresent;
   if (!JS_GetElementIfPresent(cx, expando, index, expando, vp, &isPresent)) {
     return false;
   }
   if (isPresent) {
@@ -5572,18 +5610,16 @@ if (expando) {
   }
 }
 """
 
         return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
              "Should not have a XrayWrapper here");
 
 """ + get + """
-// No need to worry about name getters here, so just check the proto.
-
 JSObject *proto;
 if (!js::GetObjectProto(cx, proxy, &proto)) {
   return false;
 }
 if (proto) {
   JSBool isPresent;
   if (!JS_GetElementIfPresent(cx, proto, index, proxy, vp, &isPresent)) {
     return false;
@@ -5602,16 +5638,19 @@ class CGDOMJSProxyHandler_getInstance(Cl
     def getBody(self):
         return """static DOMProxyHandler instance;
 return &instance;"""
 
 class CGDOMJSProxyHandler(CGClass):
     def __init__(self, descriptor):
         constructors = [CGDOMJSProxyHandler_CGDOMJSProxyHandler()]
         methods = [CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)]
+        # XXXbz This should really just test supportsIndexedProperties() and
+        # supportsNamedProperties(), but that would make us throw in all cases
+        # because we don't know whether we're in strict mode.
         if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
             methods.append(CGDOMJSProxyHandler_defineProperty(descriptor))
         methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
                         CGDOMJSProxyHandler_hasOwn(descriptor),
                         CGDOMJSProxyHandler_get(descriptor),
                         CGDOMJSProxyHandler_obj_toString(descriptor),
                         CGDOMJSProxyHandler_finalize(descriptor),
                         CGDOMJSProxyHandler_getElementIfPresent(descriptor),