From 57c2a362b2dd5caadc2858e389ad844ff6922d09 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 6 Sep 2012 10:23:51 -0400 Subject: [PATCH] Bug 742144. Implement support for typedefs in WebIDL. r=khuey --- dom/bindings/parser/WebIDL.py | 70 ++++++++++++++++---- dom/bindings/parser/tests/test_builtins.py | 2 +- dom/bindings/parser/tests/test_typedef.py | 76 ++++++++++++++++++++++ dom/bindings/test/TestBindingHeader.h | 5 ++ dom/bindings/test/TestCodeGen.webidl | 10 +++ dom/bindings/test/TestTypedef.webidl | 7 ++ dom/webidl/WebIDL.mk | 1 + 7 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 dom/bindings/parser/tests/test_typedef.py create mode 100644 dom/bindings/test/TestTypedef.webidl diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 874316625fd3..f552463414ac 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -999,6 +999,10 @@ class IDLUnresolvedType(IDLType): assert obj if obj.isType(): + # obj itself might not be complete; deal with that. + assert obj != self + if not obj.isComplete(): + obj = obj.complete(scope) return obj name = self.name.resolve(scope, None) @@ -1011,7 +1015,6 @@ class IDLUnresolvedType(IDLType): class IDLNullableType(IDLType): def __init__(self, location, innerType): assert not innerType.isVoid() - assert not innerType.nullable() assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any] IDLType.__init__(self, location, innerType.name) @@ -1090,6 +1093,10 @@ class IDLNullableType(IDLType): def complete(self, scope): self.inner = self.inner.complete(scope) + if self.inner.nullable(): + raise WebIDLError("The inner type of a nullable type must not be " + "a nullable type", + [self.location, self.inner.location]) if self.inner.isUnion(): if self.inner.hasNullableType: raise WebIDLError("The inner type of a nullable type must not " @@ -1407,9 +1414,24 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier): def isNonCallbackInterface(self): return self.inner.isNonCallbackInterface() - def resolve(self, parentScope): - assert isinstance(parentScope, IDLScope) - IDLObjectWithIdentifier.resolve(self, parentScope) + def isComplete(self): + return False + + def complete(self, parentScope): + if not self.inner.isComplete(): + self.inner = self.inner.complete(parentScope) + assert self.inner.isComplete() + return self.inner + + def finish(self, parentScope): + # Maybe the IDLObjectWithIdentifier for the typedef should be + # a separate thing from the type? + self.complete(parentScope) + + def validate(self): + pass + + # Do we need a resolveType impl? I don't think it's particularly useful.... def tag(self): return self.inner.tag() @@ -1883,18 +1905,28 @@ class IDLConst(IDLInterfaceMember): raise WebIDLError("A constant cannot be of a dictionary type", [self.location]) self.type = type - - # The value might not match the type - coercedValue = value.coerceToType(self.type, location) - assert coercedValue - - self.value = coercedValue + self.value = value def __str__(self): return "'%s' const '%s'" % (self.type, self.identifier) def finish(self, scope): - assert self.type.isComplete() + if not self.type.isComplete(): + type = self.type.complete(scope) + if not type.isPrimitive() and not type.isString(): + locations = [self.type.location, type.location] + try: + locations.append(type.inner.location) + except: + pass + raise WebIDLError("Incorrect type for constant", locations) + self.type = type + + # The value might not match the type + coercedValue = self.value.coerceToType(self.type, self.location) + assert coercedValue + + self.value = coercedValue def validate(self): pass @@ -1921,6 +1953,7 @@ class IDLAttribute(IDLInterfaceMember): t = self.type.complete(scope) assert not isinstance(t, IDLUnresolvedType) + assert not isinstance(t, IDLTypedefType) assert not isinstance(t.name, IDLUnresolvedIdentifier) self.type = t @@ -2016,6 +2049,7 @@ class IDLArgument(IDLObjectWithIdentifier): if not self.type.isComplete(): type = self.type.complete(scope) assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type, IDLTypedefType) assert not isinstance(type.name, IDLUnresolvedIdentifier) self.type = type @@ -2061,6 +2095,7 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): type = returnType.complete(scope) assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type, IDLTypedefType) assert not isinstance(type.name, IDLUnresolvedIdentifier) self._returnType = type @@ -2071,6 +2106,7 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): type = argument.type.complete(scope) assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type, IDLTypedefType) assert not isinstance(type.name, IDLUnresolvedIdentifier) argument.type = type @@ -2322,6 +2358,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope): type = returnType.complete(scope) assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type, IDLTypedefType) assert not isinstance(type.name, IDLUnresolvedIdentifier) overload.returnType = type @@ -3448,6 +3485,17 @@ class Parser(Tokenizer): type = IDLNullableType(self.getLocation(p, 1), type) p[0] = type + def p_ConstTypeIdentifier(self, p): + """ + ConstType : IDENTIFIER Null + """ + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) + + type = IDLUnresolvedType(self.getLocation(p, 1), identifier) + if p[2]: + type = IDLNullableType(self.getLocation(p, 1), type) + p[0] = type + def p_PrimitiveOrStringTypeUint(self, p): """ PrimitiveOrStringType : UnsignedIntegerType diff --git a/dom/bindings/parser/tests/test_builtins.py b/dom/bindings/parser/tests/test_builtins.py index 7f22f6cfbf34..f8563fc2d9b8 100644 --- a/dom/bindings/parser/tests/test_builtins.py +++ b/dom/bindings/parser/tests/test_builtins.py @@ -31,7 +31,7 @@ def WebIDLTest(parser, harness): harness.check(len(members), 10, "Should be one production") names = ["b", "s8", "u8", "s16", "u16", "s32", "u32", "s64", "u64", "ts"] - types = ["Boolean", "Byte", "Octet", "Short", "UnsignedShort", "Long", "UnsignedLong", "LongLong", "UnsignedLongLong", "DOMTimeStamp"] + types = ["Boolean", "Byte", "Octet", "Short", "UnsignedShort", "Long", "UnsignedLong", "LongLong", "UnsignedLongLong", "UnsignedLongLong"] for i in range(10): attr = members[i] harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute") diff --git a/dom/bindings/parser/tests/test_typedef.py b/dom/bindings/parser/tests/test_typedef.py new file mode 100644 index 000000000000..9d2f3b3c2ce3 --- /dev/null +++ b/dom/bindings/parser/tests/test_typedef.py @@ -0,0 +1,76 @@ +def WebIDLTest(parser, harness): + parser.parse(""" + typedef long mylong; + typedef long? mynullablelong; + interface Foo { + const mylong X = 5; + const mynullablelong Y = 7; + const mynullablelong Z = null; + void foo(mylong arg); + }; + """) + + results = parser.finish() + + harness.check(results[2].members[1].type.name, "Long", + "Should expand typedefs") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef long? mynullablelong; + interface Foo { + void foo(mynullablelong? Y); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown on nullable inside nullable arg.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef long? mynullablelong; + interface Foo { + const mynullablelong? X = 5; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown on nullable inside nullable const.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + const mynullablelong? X = 5; + }; + typedef long? mynullablelong; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Should have thrown on nullable inside nullable const typedef " + "after interface.") + + parser = parser.reset() + parser.parse(""" + interface Foo { + const mylong X = 5; + }; + typedef long mylong; + """) + + results = parser.finish() + + harness.check(results[0].members[0].type.name, "Long", + "Should expand typedefs that come before interface") diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index 7d57add69645..b14904380891 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -396,6 +396,11 @@ public: void PassDictionaryOrLong(int32_t); void PassDictContainingDict(const DictContainingDict&); + // Typedefs + void ExerciseTypedefInterfaces1(TestInterface&); + already_AddRefed ExerciseTypedefInterfaces2(TestInterface*); + void ExerciseTypedefInterfaces3(TestInterface&); + // Methods and properties imported via "implements" bool GetImplementedProperty(); void SetImplementedProperty(bool); diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index 74ff6463b034..8442d2bb8694 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -4,6 +4,10 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ +typedef long myLong; +typedef TestInterface AnotherNameForTestInterface; +typedef TestInterface? NullableTestInterface; + interface TestExternalInterface; interface TestNonCastableInterface { @@ -309,6 +313,12 @@ interface TestInterface { void dontEnforceRangeOrClamp(byte arg); void doEnforceRange([EnforceRange] byte arg); void doClamp([Clamp] byte arg); + + // Typedefs + const myLong myLongConstant = 5; + void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg); + AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg); + void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg); }; interface TestNonWrapperCacheInterface { diff --git a/dom/bindings/test/TestTypedef.webidl b/dom/bindings/test/TestTypedef.webidl new file mode 100644 index 000000000000..7f758c79e8f8 --- /dev/null +++ b/dom/bindings/test/TestTypedef.webidl @@ -0,0 +1,7 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +typedef TestInterface YetAnotherNameForTestInterface; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 0549f4f7ebc9..c6c081470f64 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -33,6 +33,7 @@ ifdef ENABLE_TESTS test_webidl_files := \ TestCodeGen.webidl \ TestDictionary.webidl \ + TestTypedef.webidl \ $(NULL) else test_webidl_files := $(NULL)