Skip to content
This repository has been archived by the owner on Aug 4, 2022. It is now read-only.

Commit

Permalink
Bug 815502. Implement support for variadic arguments in WebIDL. r=peterv
Browse files Browse the repository at this point in the history
  • Loading branch information
bzbarsky committed Dec 11, 2012
1 parent d6ad871 commit 86738d2
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 35 deletions.
129 changes: 103 additions & 26 deletions dom/bindings/Codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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*
Expand All @@ -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),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ''

Expand Down
22 changes: 15 additions & 7 deletions dom/bindings/parser/WebIDL.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion dom/bindings/parser/tests/test_overload.py
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
""")

Expand All @@ -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")
Expand Down
13 changes: 13 additions & 0 deletions dom/bindings/parser/tests/test_variadic_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
13 changes: 12 additions & 1 deletion dom/bindings/test/TestBindingHeader.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ class TestInterface : public nsISupports,
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();
Expand Down Expand Up @@ -381,6 +382,7 @@ class TestInterface : public nsISupports,
void PassOptionalStringWithDefaultValue(const nsAString&);
void PassOptionalNullableString(const Optional<nsAString>&);
void PassOptionalNullableStringWithDefaultValue(const nsAString&);
void PassVariadicString(const Sequence<nsString>&);

// Enumerated types
void PassEnum(TestEnum);
Expand Down Expand Up @@ -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<OwningNonNull<TestInterface> >&);

// Miscellania
int32_t AttrWithLenientThis();
void SetAttrWithLenientThis(int32_t);
Expand Down Expand Up @@ -522,6 +532,7 @@ class TestInterface : public nsISupports,
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>
Expand Down Expand Up @@ -631,7 +642,7 @@ class TestInterface : public nsISupports,
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,
Expand Down
10 changes: 10 additions & 0 deletions dom/bindings/test/TestCodeGen.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions dom/bindings/test/TestExampleGen.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 86738d2

Please sign in to comment.