Bug 815502. Implement support for variadic arguments in WebIDL. r=peterv

This commit is contained in:
Boris Zbarsky 2012-12-11 17:50:56 -05:00
Родитель 05e5ef448b
Коммит d4de9b2c67
7 изменённых файлов: 167 добавлений и 35 удалений

Просмотреть файл

@ -2196,6 +2196,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, 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 @@ class CGArgumentConverter(CGThing):
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 @@ class CGArgumentConverter(CGThing):
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 @@ class CGMethodCall(CGThing):
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 @@ class CGMethodCall(CGThing):
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 @@ class CGBindingRoot(CGThing):
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 @@ class CGNativeMember(ClassMethod):
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 @@ class CGNativeMember(ClassMethod):
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 @@ class CGExampleMethod(CGNativeMember):
method),
signature,
descriptor.getExtendedAttributes(method),
breakAfter)
breakAfter=breakAfter,
variadicIsSequence=True)
def define(self, cgClass):
return ''

Просмотреть файл

@ -2607,24 +2607,32 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
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

Просмотреть файл

@ -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")

Просмотреть файл

@ -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.")

Просмотреть файл

@ -154,6 +154,7 @@ public:
void PassOptionalByteWithDefault(int8_t);
void PassNullableByte(const Nullable<int8_t>&);
void PassOptionalNullableByte(const Optional< Nullable<int8_t> >&);
void PassVariadicByte(const Sequence<int8_t>&);
int16_t ReadonlyShort();
int16_t WritableShort();
@ -381,6 +382,7 @@ public:
void PassOptionalStringWithDefaultValue(const nsAString&);
void PassOptionalNullableString(const Optional<nsAString>&);
void PassOptionalNullableStringWithDefaultValue(const nsAString&);
void PassVariadicString(const Sequence<nsString>&);
// Enumerated types
void PassEnum(TestEnum);
@ -473,6 +475,14 @@ public:
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<OwningNonNull<TestInterface> >&);
// Miscellania
int32_t AttrWithLenientThis();
void SetAttrWithLenientThis(int32_t);
@ -522,6 +532,7 @@ private:
void PassOptionalByte(const Optional<T>&) MOZ_DELETE;
template<typename T>
void PassOptionalByteWithDefault(T) MOZ_DELETE;
void PassVariadicByte(Sequence<int8_t>&) MOZ_DELETE;
void SetReadonlyShort(int16_t) MOZ_DELETE;
template<typename T>
@ -631,7 +642,7 @@ private:
void PassOptionalStringWithDefaultValue(nsAString&) MOZ_DELETE;
void PassOptionalNullableString(Optional<nsAString>&) MOZ_DELETE;
void PassOptionalNullableStringWithDefaultValue(nsAString&) MOZ_DELETE;
void PassVariadicString(Sequence<nsString>&) MOZ_DELETE;
};
class TestIndexedGetterInterface : public nsISupports,

Просмотреть файл

@ -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;

Просмотреть файл

@ -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;