diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index c43994e621f9..073957794dda 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2196,6 +2196,7 @@ def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None): if not isOptional: typeName = CGWrapper(typeName, pre="const ") + # NOTE: Keep this in sync with variadic conversions as needed templateBody = ("""JSObject* seq = &${val}.toObject();\n if (!IsArrayLike(cx, seq)) { %s @@ -3042,9 +3043,6 @@ def __init__(self, argument, index, argv, argc, descriptorProvider, invalidEnumValueFatal=True, lenientFloatCode=None): CGThing.__init__(self) self.argument = argument - if argument.variadic: - raise TypeError("We don't support variadic arguments yet " + - str(argument.location)) assert(not argument.defaultValue or argument.optional) replacer = { @@ -3074,19 +3072,70 @@ def __init__(self, argument, index, argv, argc, descriptorProvider, self.lenientFloatCode = lenientFloatCode def define(self): - return instantiateJSToNativeConversionTemplate( - getJSToNativeConversionTemplate(self.argument.type, - self.descriptorProvider, - isOptional=(self.argcAndIndex is not None), - invalidEnumValueFatal=self.invalidEnumValueFatal, - defaultValue=self.argument.defaultValue, - treatNullAs=self.argument.treatNullAs, - treatUndefinedAs=self.argument.treatUndefinedAs, - isEnforceRange=self.argument.enforceRange, - isClamp=self.argument.clamp, - lenientFloatCode=self.lenientFloatCode), - self.replacementVariables, - self.argcAndIndex).define() + typeConversion = getJSToNativeConversionTemplate( + self.argument.type, + self.descriptorProvider, + isOptional=(self.argcAndIndex is not None and + not self.argument.variadic), + invalidEnumValueFatal=self.invalidEnumValueFatal, + defaultValue=self.argument.defaultValue, + treatNullAs=self.argument.treatNullAs, + treatUndefinedAs=self.argument.treatUndefinedAs, + isEnforceRange=self.argument.enforceRange, + isClamp=self.argument.clamp, + lenientFloatCode=self.lenientFloatCode, + isMember=self.argument.variadic) + + if not self.argument.variadic: + return instantiateJSToNativeConversionTemplate( + typeConversion, + self.replacementVariables, + self.argcAndIndex).define() + + # Variadic arguments get turned into a sequence. + (elementTemplate, elementDeclType, + elementHolderType, dealWithOptional) = typeConversion + if dealWithOptional: + raise TypeError("Shouldn't have optional things in variadics") + if elementHolderType is not None: + raise TypeError("Shouldn't need holders for variadics") + + replacer = dict(self.argcAndIndex, **self.replacementVariables) + replacer["seqType"] = CGWrapper(elementDeclType, pre="Sequence< ", post=" >").define() + replacer["elemType"] = elementDeclType.define() + + # NOTE: Keep this in sync with sequence conversions as needed + variadicConversion = string.Template("""const ${seqType} ${declName}; +if (${argc} > ${index}) { + ${seqType}& arr = const_cast< ${seqType}& >(${declName}); + if (!arr.SetCapacity(${argc} - ${index})) { + JS_ReportOutOfMemory(cx); + return false; + } + for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) { + ${elemType}& slot = *arr.AppendElement(); +""").substitute(replacer) + + val = string.Template("${argv}[variadicArg]").substitute(replacer) + variadicConversion += CGIndenter(CGGeneric( + string.Template(elementTemplate).substitute( + { + "val" : val, + "valPtr": "&" + val, + "declName" : "slot", + # We only need holderName here to handle isExternal() + # interfaces, which use an internal holder for the + # conversion even when forceOwningType ends up true. + "holderName": "tempHolder", + # Use the same ${obj} as for the variadic arg itself + "obj": replacer["obj"] + } + )), 4).define() + + variadicConversion += ("\n" + " }\n" + "}") + return variadicConversion def getWrapTemplateForType(type, descriptorProvider, result, successCode, isCreator, exceptionCode): @@ -3777,16 +3826,44 @@ def getPerSignatureCall(signature, argConversionStartsAt=0): distinguishingIndex = method.distinguishingIndexForArgCount(argCount) - for (_, args) in possibleSignatures: + def distinguishingArgument(signature): + args = signature[1] + if distinguishingIndex < len(args): + return args[distinguishingIndex] + assert args[-1].variadic + return args[-1] + + def distinguishingType(signature): + return distinguishingArgument(signature).type + + for sig in possibleSignatures: # We should not have "any" args at distinguishingIndex, # since we have multiple possible signatures remaining, # but "any" is never distinguishable from anything else. - assert not args[distinguishingIndex].type.isAny() + assert not distinguishingType(sig).isAny() # We can't handle unions at the distinguishing index. - if args[distinguishingIndex].type.isUnion(): + if distinguishingType(sig).isUnion(): raise TypeError("No support for unions as distinguishing " "arguments yet: %s", - args[distinguishingIndex].location) + distinguishingArgument(sig).location) + # We don't support variadics as the distinguishingArgument yet. + # If you want to add support, consider this case: + # + # void(long... foo); + # void(long bar, Int32Array baz); + # + # in which we have to convert argument 0 to long before picking + # an overload... but all the variadic stuff needs to go into a + # single array in case we pick that overload, so we have to have + # machinery for converting argument 0 to long and then either + # placing it in the variadic bit or not. Or something. We may + # be able to loosen this restriction if the variadic arg is in + # fact at distinguishingIndex, perhaps. Would need to + # double-check. + if distinguishingArgument(sig).variadic: + raise TypeError("No support for variadics as distinguishing " + "arguments yet: %s", + distinguishingArgument(sig).location) # Convert all our arguments up to the distinguishing index. # Doesn't matter which of the possible signatures we use, since @@ -3816,9 +3893,6 @@ def pickFirstSignature(condition, filterLambda): return True return False - def distinguishingType(signature): - return signature[1][distinguishingIndex].type - def tryCall(signature, indent, isDefinitelyObject=False, isNullOrUndefined=False): assert not isDefinitelyObject or not isNullOrUndefined @@ -6818,7 +6892,7 @@ def define(self): class CGNativeMember(ClassMethod): def __init__(self, descriptor, member, name, signature, extendedAttrs, breakAfter=True, passCxAsNeeded=True, visibility="public", - jsObjectsArePtr=False): + jsObjectsArePtr=False, variadicIsSequence=False): """ If jsObjectsArePtr is true, typed arrays and "object" will be passed as JSObject* @@ -6830,6 +6904,7 @@ def __init__(self, descriptor, member, name, signature, extendedAttrs, self.extendedAttrs) self.passCxAsNeeded = passCxAsNeeded self.jsObjectsArePtr = jsObjectsArePtr + self.variadicIsSequence = variadicIsSequence breakAfterSelf = "\n" if breakAfter else "" ClassMethod.__init__(self, name, self.getReturnType(signature[0], False), @@ -7110,7 +7185,8 @@ def getArgType(self, type, optional, variadic, isMember): decl = CGWrapper(decl, pre="Nullable< ", post=" >") ref = True if variadic: - decl = CGWrapper(decl, pre="nsTArray< ", post=" >") + arrayType = "Sequence" if self.variadicIsSequence else "nsTArray" + decl = CGWrapper(decl, pre="%s< " % arrayType, post=" >") ref = True elif optional: # Note: All variadic args claim to be optional, but we can just use @@ -7140,7 +7216,8 @@ def __init__(self, descriptor, method, signature, breakAfter=True): method), signature, descriptor.getExtendedAttributes(method), - breakAfter) + breakAfter=breakAfter, + variadicIsSequence=True) def define(self, cgClass): return '' diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 469290b4610c..3f6ad656fcb3 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -2607,24 +2607,32 @@ def overloadsForArgCount(self, argc): return [overload for overload in self._overloads if len(overload.arguments) == argc or (len(overload.arguments) > argc and - overload.arguments[argc].optional)] + overload.arguments[argc].optional) or + (len(overload.arguments) < argc and + len(overload.arguments) > 0 and + overload.arguments[-1].variadic)] def signaturesForArgCount(self, argc): return [(overload.returnType, overload.arguments) for overload in self.overloadsForArgCount(argc)] def locationsForArgCount(self, argc): - return [overload.location for overload in self._overloads if - len(overload.arguments) == argc or - (len(overload.arguments) > argc and - overload.arguments[argc].optional)] + return [overload.location for overload in self.overloadsForArgCount(argc)] def distinguishingIndexForArgCount(self, argc): def isValidDistinguishingIndex(idx, signatures): for (firstSigIndex, (firstRetval, firstArgs)) in enumerate(signatures[:-1]): for (secondRetval, secondArgs) in signatures[firstSigIndex+1:]: - firstType = firstArgs[idx].type - secondType = secondArgs[idx].type + if idx < len(firstArgs): + firstType = firstArgs[idx].type + else: + assert(firstArgs[-1].variadic) + firstType = firstArgs[-1].type + if idx < len(secondArgs): + secondType = secondArgs[idx].type + else: + assert(secondArgs[-1].variadic) + secondType = secondArgs[-1].type if not firstType.isDistinguishableFrom(secondType): return False return True diff --git a/dom/bindings/parser/tests/test_overload.py b/dom/bindings/parser/tests/test_overload.py index 59d9be54e534..e9a03958b6f5 100644 --- a/dom/bindings/parser/tests/test_overload.py +++ b/dom/bindings/parser/tests/test_overload.py @@ -8,6 +8,9 @@ def WebIDLTest(parser, harness): boolean abitharder(TestOverloads foo); boolean abitharder(boolean foo); void abitharder(ArrayBuffer? foo); + void withVariadics(long... numbers); + void withVariadics(TestOverloads iface); + void withVariadics(long num, TestOverloads iface); }; """) @@ -20,7 +23,7 @@ def WebIDLTest(parser, harness): "Should be an IDLInterface") harness.check(iface.identifier.QName(), "::TestOverloads", "Interface has the right QName") harness.check(iface.identifier.name, "TestOverloads", "Interface has the right name") - harness.check(len(iface.members), 2, "Expect %s members" % 2) + harness.check(len(iface.members), 3, "Expect %s members" % 3) member = iface.members[0] harness.check(member.identifier.QName(), "::TestOverloads::basic", "Method has the right QName") diff --git a/dom/bindings/parser/tests/test_variadic_constraints.py b/dom/bindings/parser/tests/test_variadic_constraints.py index 9cba22c58425..e9d060892e63 100644 --- a/dom/bindings/parser/tests/test_variadic_constraints.py +++ b/dom/bindings/parser/tests/test_variadic_constraints.py @@ -37,3 +37,16 @@ def WebIDLTest(parser, harness): threw = True harness.ok(threw, "Should have thrown.") + + threw = False + try: + results = parser.parse(""" + interface VariadicConstraints4 { + void foo(byte... arg1 = 0); + }; + """) + + except: + threw = True + + harness.ok(threw, "Should have thrown on variadic argument with default value.") diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index 0a68810477de..d2c872d0ad72 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -154,6 +154,7 @@ class TestInterface : public nsISupports, void PassOptionalByteWithDefault(int8_t); void PassNullableByte(const Nullable&); void PassOptionalNullableByte(const Optional< Nullable >&); + void PassVariadicByte(const Sequence&); int16_t ReadonlyShort(); int16_t WritableShort(); @@ -381,6 +382,7 @@ class TestInterface : public nsISupports, void PassOptionalStringWithDefaultValue(const nsAString&); void PassOptionalNullableString(const Optional&); void PassOptionalNullableStringWithDefaultValue(const nsAString&); + void PassVariadicString(const Sequence&); // Enumerated types void PassEnum(TestEnum); @@ -473,6 +475,14 @@ class TestInterface : public nsISupports, static bool StaticAttribute(nsISupports*); static void SetStaticAttribute(nsISupports*, bool); + // Overload resolution tests + bool Overload1(TestInterface&); + TestInterface* Overload1(const nsAString&, TestInterface&); + + // Variadic handling + void PassVariadicThirdArg(const nsAString&, int32_t, + const Sequence >&); + // Miscellania int32_t AttrWithLenientThis(); void SetAttrWithLenientThis(int32_t); @@ -522,6 +532,7 @@ class TestInterface : public nsISupports, void PassOptionalByte(const Optional&) MOZ_DELETE; template void PassOptionalByteWithDefault(T) MOZ_DELETE; + void PassVariadicByte(Sequence&) MOZ_DELETE; void SetReadonlyShort(int16_t) MOZ_DELETE; template @@ -631,7 +642,7 @@ class TestInterface : public nsISupports, void PassOptionalStringWithDefaultValue(nsAString&) MOZ_DELETE; void PassOptionalNullableString(Optional&) MOZ_DELETE; void PassOptionalNullableStringWithDefaultValue(nsAString&) MOZ_DELETE; - + void PassVariadicString(Sequence&) MOZ_DELETE; }; class TestIndexedGetterInterface : public nsISupports, diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index 8130791f84e2..d2e394fd4cdf 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -100,6 +100,7 @@ interface TestInterface { void passOptionalByteWithDefault(optional byte arg = 0); void passNullableByte(byte? arg); void passOptionalNullableByte(optional byte? arg); + void passVariadicByte(byte... arg); readonly attribute short readonlyShort; attribute short writableShort; @@ -333,6 +334,7 @@ interface TestInterface { void passOptionalStringWithDefaultValue(optional DOMString arg = "abc"); void passOptionalNullableString(optional DOMString? arg); void passOptionalNullableStringWithDefaultValue(optional DOMString? arg = null); + void passVariadicString(DOMString... arg); // Enumerated types void passEnum(TestEnum arg); @@ -424,6 +426,14 @@ interface TestInterface { static attribute boolean staticAttribute; static void staticMethod(boolean arg); + // Overload resolution tests + //void overload1(DOMString... strs); + boolean overload1(TestInterface arg); + TestInterface overload1(DOMString strs, TestInterface arg); + + // Variadic handling + void passVariadicThirdArg(DOMString arg1, long arg2, TestInterface... arg3); + // Miscellania [LenientThis] attribute long attrWithLenientThis; [Unforgeable] readonly attribute long unforgeableAttr; diff --git a/dom/bindings/test/TestExampleGen.webidl b/dom/bindings/test/TestExampleGen.webidl index 7edab7a28cd0..78673d58d054 100644 --- a/dom/bindings/test/TestExampleGen.webidl +++ b/dom/bindings/test/TestExampleGen.webidl @@ -21,6 +21,7 @@ interface TestExampleInterface { void passOptionalByteWithDefault(optional byte arg = 0); void passNullableByte(byte? arg); void passOptionalNullableByte(optional byte? arg); + void passVariadicByte(byte... arg); readonly attribute short readonlyShort; attribute short writableShort; @@ -254,6 +255,7 @@ interface TestExampleInterface { void passOptionalStringWithDefaultValue(optional DOMString arg = "abc"); void passOptionalNullableString(optional DOMString? arg); void passOptionalNullableStringWithDefaultValue(optional DOMString? arg = null); + void passVariadicString(DOMString... arg); // Enumerated types void passEnum(TestEnum arg); @@ -345,6 +347,14 @@ interface TestExampleInterface { static attribute boolean staticAttribute; static void staticMethod(boolean arg); + // Overload resolution tests + //void overload1(DOMString... strs); + boolean overload1(TestInterface arg); + TestInterface overload1(DOMString strs, TestInterface arg); + + // Variadic handling + void passVariadicThirdArg(DOMString arg1, long arg2, TestInterface... arg3); + // Miscellania [LenientThis] attribute long attrWithLenientThis; [Unforgeable] readonly attribute long unforgeableAttr;