Bug 741125: Update WebIDL parser.

This commit is contained in:
Kyle Huey 2012-04-12 15:14:10 -07:00
Родитель 960820e223
Коммит 70daea2117
12 изменённых файлов: 412 добавлений и 143 удалений

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

@ -7,6 +7,9 @@ NSS_DIRS = (('dbm', 'mozilla/dbm'),
('security/dbm', 'mozilla/security/dbm')) ('security/dbm', 'mozilla/security/dbm'))
NSSCKBI_DIRS = (('security/nss/lib/ckfw/builtins', 'mozilla/security/nss/lib/ckfw/builtins'),) NSSCKBI_DIRS = (('security/nss/lib/ckfw/builtins', 'mozilla/security/nss/lib/ckfw/builtins'),)
LIBFFI_DIRS = (('js/ctypes/libffi', 'libffi'),) LIBFFI_DIRS = (('js/ctypes/libffi', 'libffi'),)
WEBIDLPARSER_DIR = 'dom/bindings/parser'
WEBIDLPARSER_REPO = 'https://hg.mozilla.org/users/khuey_mozilla.com/webidl-parser'
WEBIDLPARSER_EXCLUSIONS = ['.hgignore', '.gitignore', '.hg', 'ply']
CVSROOT_MOZILLA = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot' CVSROOT_MOZILLA = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot'
CVSROOT_LIBFFI = ':pserver:anoncvs@sources.redhat.com:/cvs/libffi' CVSROOT_LIBFFI = ':pserver:anoncvs@sources.redhat.com:/cvs/libffi'
@ -15,6 +18,7 @@ import os
import sys import sys
import datetime import datetime
import shutil import shutil
import glob
from optparse import OptionParser from optparse import OptionParser
from subprocess import check_call from subprocess import check_call
@ -30,7 +34,6 @@ def do_hg_pull(dir, repository, hg):
fulldir = os.path.join(topsrcdir, dir) fulldir = os.path.join(topsrcdir, dir)
# clone if the dir doesn't exist, pull if it does # clone if the dir doesn't exist, pull if it does
if not os.path.exists(fulldir): if not os.path.exists(fulldir):
fulldir = os.path.join(topsrcdir, dir)
check_call_noisy([hg, 'clone', repository, fulldir]) check_call_noisy([hg, 'clone', repository, fulldir])
else: else:
cmd = [hg, 'pull', '-u', '-R', fulldir] cmd = [hg, 'pull', '-u', '-R', fulldir]
@ -40,6 +43,25 @@ def do_hg_pull(dir, repository, hg):
check_call([hg, 'parent', '-R', fulldir, check_call([hg, 'parent', '-R', fulldir,
'--template=Updated to revision {node}.\n']) '--template=Updated to revision {node}.\n'])
def do_hg_replace(dir, repository, tag, exclusions, hg):
"""
Replace the contents of dir with the contents of repository, except for
files matching exclusions.
"""
fulldir = os.path.join(topsrcdir, dir)
if os.path.exists(fulldir):
shutil.rmtree(fulldir)
assert not os.path.exists(fulldir)
check_call_noisy([hg, 'clone', '-u', tag, repository, fulldir])
for thing in exclusions:
for excluded in glob.iglob(os.path.join(fulldir, thing)):
if os.path.isdir(excluded):
shutil.rmtree(excluded)
else:
os.remove(excluded)
def do_cvs_export(modules, tag, cvsroot, cvs): def do_cvs_export(modules, tag, cvsroot, cvs):
"""Check out a CVS directory without CVS metadata, using "export" """Check out a CVS directory without CVS metadata, using "export"
modules is a list of directories to check out and the corresponding modules is a list of directories to check out and the corresponding
@ -60,7 +82,7 @@ def do_cvs_export(modules, tag, cvsroot, cvs):
cwd=os.path.join(topsrcdir, parent)) cwd=os.path.join(topsrcdir, parent))
print "CVS export end: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC") print "CVS export end: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname") o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname | update_webidlparser tagname")
o.add_option("--skip-mozilla", dest="skip_mozilla", o.add_option("--skip-mozilla", dest="skip_mozilla",
action="store_true", default=False, action="store_true", default=False,
help="Obsolete") help="Obsolete")
@ -69,6 +91,8 @@ o.add_option("--cvs", dest="cvs", default=os.environ.get('CVS', 'cvs'),
help="The location of the cvs binary") help="The location of the cvs binary")
o.add_option("--cvsroot", dest="cvsroot", o.add_option("--cvsroot", dest="cvsroot",
help="The CVSROOT (default for mozilla checkouts: %s)" % CVSROOT_MOZILLA) help="The CVSROOT (default for mozilla checkouts: %s)" % CVSROOT_MOZILLA)
o.add_option("--hg", dest="hg", default=os.environ.get('HG', 'hg'),
help="The location of the hg binary")
try: try:
options, args = o.parse_args() options, args = o.parse_args()
@ -104,6 +128,9 @@ elif action in ('update_libffi'):
if not options.cvsroot: if not options.cvsroot:
options.cvsroot = CVSROOT_LIBFFI options.cvsroot = CVSROOT_LIBFFI
do_cvs_export(LIBFFI_DIRS, tag, options.cvsroot, options.cvs) do_cvs_export(LIBFFI_DIRS, tag, options.cvsroot, options.cvs)
elif action in ('update_webidlparser'):
tag, = args[1:]
do_hg_replace(WEBIDLPARSER_DIR, WEBIDLPARSER_REPO, tag, WEBIDLPARSER_EXCLUSIONS, options.hg)
else: else:
o.print_help() o.print_help()
sys.exit(2) sys.exit(2)

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

@ -75,7 +75,7 @@ bindinggen_dependencies := \
$(binding_header_files): %Binding.h: $(bindinggen_dependencies) \ $(binding_header_files): %Binding.h: $(bindinggen_dependencies) \
$(webidl_base)/%.webidl \ $(webidl_base)/%.webidl \
$(NULL) $(NULL)
$(PYTHON) $(topsrcdir)/config/pythonpath.py \ PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) -I$(srcdir)/parser \ $(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/BindingGen.py $(ACCESSOR_OPT) header \ $(srcdir)/BindingGen.py $(ACCESSOR_OPT) header \
$(srcdir)/Bindings.conf $*Binding \ $(srcdir)/Bindings.conf $*Binding \
@ -84,7 +84,7 @@ $(binding_header_files): %Binding.h: $(bindinggen_dependencies) \
$(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \ $(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \
$(webidl_base)/%.webidl \ $(webidl_base)/%.webidl \
$(NULL) $(NULL)
$(PYTHON) $(topsrcdir)/config/pythonpath.py \ PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) -I$(srcdir)/parser \ $(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/BindingGen.py $(ACCESSOR_OPT) cpp \ $(srcdir)/BindingGen.py $(ACCESSOR_OPT) cpp \
$(srcdir)/Bindings.conf $*Binding \ $(srcdir)/Bindings.conf $*Binding \
@ -109,7 +109,7 @@ $(CACHE_DIR)/.done:
ParserResults.pkl: $(globalgen_dependencies) \ ParserResults.pkl: $(globalgen_dependencies) \
$(addprefix $(webidl_base)/, $(webidl_files)) $(addprefix $(webidl_base)/, $(webidl_files))
$(PYTHON) $(topsrcdir)/config/pythonpath.py \ PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) -I$(srcdir)/parser \ $(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/GlobalGen.py $(ACCESSOR_OPT) $(srcdir)/Bindings.conf $(webidl_base) \ $(srcdir)/GlobalGen.py $(ACCESSOR_OPT) $(srcdir)/Bindings.conf $(webidl_base) \
--cachedir=$(CACHE_DIR) \ --cachedir=$(CACHE_DIR) \

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

@ -69,14 +69,16 @@ def parseInt(literal):
# Magic for creating enums # Magic for creating enums
def M_add_class_attribs(attribs): def M_add_class_attribs(attribs):
def foo(name, bases, dict_): def foo(name, bases, dict_):
for v, k in attribs: for v, k in enumerate(attribs):
dict_[k] = v dict_[k] = v
assert 'length' not in dict_
dict_['length'] = len(attribs)
return type(name, bases, dict_) return type(name, bases, dict_)
return foo return foo
def enum(*names): def enum(*names):
class Foo(object): class Foo(object):
__metaclass__ = M_add_class_attribs(enumerate(names)) __metaclass__ = M_add_class_attribs(names)
def __setattr__(self, name, value): # this makes it read-only def __setattr__(self, name, value): # this makes it read-only
raise NotImplementedError raise NotImplementedError
return Foo() return Foo()
@ -93,9 +95,8 @@ class WebIDLError(Exception):
self.location) self.location)
class Location(object): class Location(object):
_line = None
def __init__(self, lexer, lineno, lexpos, filename): def __init__(self, lexer, lineno, lexpos, filename):
self._line = None
self._lineno = lineno self._lineno = lineno
self._lexpos = lexpos self._lexpos = lexpos
self._lexdata = lexer.lexdata self._lexdata = lexer.lexdata
@ -105,36 +106,47 @@ class Location(object):
return self._lexpos == other._lexpos and \ return self._lexpos == other._lexpos and \
self._file == other._file self._file == other._file
def filename(self):
return self._file
def resolve(self): def resolve(self):
if self._line: if self._line:
return return
startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1 startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80) endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
self._line = self._lexdata[startofline:endofline] if endofline != -1:
self._line = self._lexdata[startofline:endofline]
else:
self._line = self._lexdata[startofline:]
self._colno = self._lexpos - startofline self._colno = self._lexpos - startofline
def pointerline(self):
def i():
for i in xrange(0, self._colno):
yield " "
yield "^"
return "".join(i())
def get(self): def get(self):
self.resolve() self.resolve()
return "%s line %s:%s" % (self._file, self._lineno, self._colno) return "%s line %s:%s" % (self._file, self._lineno, self._colno)
def _pointerline(self):
return " " * self._colno + "^"
def __str__(self): def __str__(self):
self.resolve() self.resolve()
return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno, return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
self._line, self.pointerline()) self._line, self._pointerline())
class BuiltinLocation(object): class BuiltinLocation(object):
def __init__(self, text): def __init__(self, text):
self.msg = text self.msg = text
def __eq__(self, other):
return isinstance(other, BuiltinLocation) and \
self.msg == other.msg
def filename(self):
return '<builtin>'
def resolve(self):
pass
def get(self): def get(self):
return self.msg return self.msg
@ -150,7 +162,7 @@ class IDLObject(object):
self.userData = dict() self.userData = dict()
def filename(self): def filename(self):
return self.location._file return self.location.filename()
def isInterface(self): def isInterface(self):
return False return False
@ -198,6 +210,11 @@ class IDLScope(IDLObject):
return "::" return "::"
def ensureUnique(self, identifier, object): def ensureUnique(self, identifier, object):
"""
Ensure that there is at most one 'identifier' in scope ('self').
Note that object can be None. This occurs if we end up here for an
interface type we haven't seen yet.
"""
assert isinstance(identifier, IDLUnresolvedIdentifier) assert isinstance(identifier, IDLUnresolvedIdentifier)
assert not object or isinstance(object, IDLObjectWithIdentifier) assert not object or isinstance(object, IDLObjectWithIdentifier)
assert not object or object.identifier == identifier assert not object or object.identifier == identifier
@ -300,6 +317,9 @@ class IDLUnresolvedIdentifier(IDLObject):
object.identifier = identifier object.identifier = identifier
return identifier return identifier
def finish(self):
assert False # Should replace with a resolved identifier first.
class IDLObjectWithIdentifier(IDLObject): class IDLObjectWithIdentifier(IDLObject):
def __init__(self, location, parentScope, identifier): def __init__(self, location, parentScope, identifier):
IDLObject.__init__(self, location) IDLObject.__init__(self, location)
@ -368,9 +388,8 @@ class IDLInterface(IDLObjectWithScope):
self.parent = parent self.parent = parent
self._callback = False self._callback = False
self._finished = False
self.members = list(members) # clone the list self.members = list(members) # clone the list
assert iter(self.members) # Assert it's iterable
IDLObjectWithScope.__init__(self, location, parentScope, name) IDLObjectWithScope.__init__(self, location, parentScope, name)
@ -404,7 +423,7 @@ class IDLInterface(IDLObjectWithScope):
return retval return retval
def finish(self, scope): def finish(self, scope):
if hasattr(self, "_finished"): if self._finished:
return return
self._finished = True self._finished = True
@ -416,7 +435,6 @@ class IDLInterface(IDLObjectWithScope):
self.parent = parent self.parent = parent
assert iter(self.members) assert iter(self.members)
members = None
if self.parent: if self.parent:
self.parent.finish(scope) self.parent.finish(scope)
@ -427,19 +445,6 @@ class IDLInterface(IDLObjectWithScope):
else: else:
members = list(self.members) members = list(self.members)
SpecialType = enum(
'NamedGetter',
'NamedSetter',
'NamedCreator',
'NamedDeleter',
'IndexedGetter',
'IndexedSetter',
'IndexedCreator',
'IndexedDeleter'
)
specialMembersSeen = [False for i in range(8)]
def memberNotOnParentChain(member, iface): def memberNotOnParentChain(member, iface):
assert iface assert iface
@ -451,59 +456,39 @@ class IDLInterface(IDLObjectWithScope):
return False return False
return memberNotOnParentChain(member, iface.parent) return memberNotOnParentChain(member, iface.parent)
# Ensure that there's at most one of each {named,indexed}
# {getter,setter,creator,deleter}.
specialMembersSeen = set()
for member in members: for member in members:
if memberNotOnParentChain(member, self): if memberNotOnParentChain(member, self):
member.resolve(self) member.resolve(self)
if member.tag == IDLInterfaceMember.Tags.Method: if member.tag != IDLInterfaceMember.Tags.Method:
if member.isGetter(): continue
if member.isNamed():
if specialMembersSeen[SpecialType.NamedGetter]: if member.isGetter():
raise WebIDLError("Multiple named getters on %s" % (self), memberType = "getters"
self.location) elif member.isSetter():
specialMembersSeen[SpecialType.NamedGetter] = True memberType = "setters"
else: elif member.isCreator():
assert member.isIndexed() memberType = "creators"
if specialMembersSeen[SpecialType.IndexedGetter]: elif member.isDeleter():
raise WebIDLError("Multiple indexed getters on %s" % (self), memberType = "deleters"
self.location) else:
specialMembersSeen[SpecialType.IndexedGetter] = True continue
if member.isSetter():
if member.isNamed(): if member.isNamed():
if specialMembersSeen[SpecialType.NamedSetter]: memberType = "named " + memberType
raise WebIDLError("Multiple named setters on %s" % (self), elif member.isIndexed():
self.location) memberType = "indexed " + memberType
specialMembersSeen[SpecialType.NamedSetter] = True else:
else: continue
assert member.isIndexed()
if specialMembersSeen[SpecialType.IndexedSetter]: if memberType in specialMembersSeen:
raise WebIDLError("Multiple indexed setters on %s" % (self), raise WebIDLError("Multiple " + memberType + " on %s" % (self),
self.location) self.location)
specialMembersSeen[SpecialType.IndexedSetter] = True
if member.isCreator(): specialMembersSeen.add(memberType)
if member.isNamed():
if specialMembersSeen[SpecialType.NamedCreator]:
raise WebIDLError("Multiple named creators on %s" % (self),
self.location)
specialMembersSeen[SpecialType.NamedCreator] = True
else:
assert member.isIndexed()
if specialMembersSeen[SpecialType.IndexedCreator]:
raise WebIDLError("Multiple indexed creators on %s" % (self),
self.location)
specialMembersSeen[SpecialType.IndexedCreator] = True
if member.isDeleter():
if member.isNamed():
if specialMembersSeen[SpecialType.NamedDeleter]:
raise WebIDLError("Multiple named deleters on %s" % (self),
self.location)
specialMembersSeen[SpecialType.NamedDeleter] = True
else:
assert member.isIndexed()
if specialMembersSeen[SpecialType.IndexedDeleter]:
raise WebIDLError("Multiple indexed Deleters on %s" % (self),
self.location)
specialMembersSeen[SpecialType.IndexedDeleter] = True
for member in self.members: for member in self.members:
member.finish(scope) member.finish(scope)
@ -529,7 +514,7 @@ class IDLInterface(IDLObjectWithScope):
return depth return depth
def hasConstants(self): def hasConstants(self):
return reduce(lambda b, m: b or m.isConst(), self.members, False) return any(m.isConst() for m in self.members)
def hasInterfaceObject(self): def hasInterfaceObject(self):
if self.isCallback(): if self.isCallback():
@ -567,10 +552,7 @@ class IDLInterface(IDLObjectWithScope):
identifier = IDLUnresolvedIdentifier(self.location, "constructor", identifier = IDLUnresolvedIdentifier(self.location, "constructor",
allowForbidden=True) allowForbidden=True)
method = IDLMethod(self.location, identifier, retType, args, method = IDLMethod(self.location, identifier, retType, args)
False, False, False, False, False, False,
False, False)
method.resolve(self) method.resolve(self)
self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
@ -763,6 +745,12 @@ class IDLNullableType(IDLType):
def isString(self): def isString(self):
return self.inner.isString() return self.inner.isString()
def isFloat(self):
return self.inner.isFloat()
def isInteger(self):
return self.inner.isInteger()
def isVoid(self): def isVoid(self):
return False return False
@ -772,6 +760,9 @@ class IDLNullableType(IDLType):
def isArray(self): def isArray(self):
return self.inner.isArray() return self.inner.isArray()
def isArrayBuffer(self):
return self.inner.isArrayBuffer()
def isDictionary(self): def isDictionary(self):
return self.inner.isDictionary() return self.inner.isDictionary()
@ -797,7 +788,7 @@ class IDLNullableType(IDLType):
return self return self
def unroll(self): def unroll(self):
return self.inner return self.inner.unroll()
def isDistinguishableFrom(self, other): def isDistinguishableFrom(self, other):
if other.nullable(): if other.nullable():
@ -835,18 +826,19 @@ class IDLSequenceType(IDLType):
return True return True
def isArray(self): def isArray(self):
return self.inner.isArray() return False
def isDictionary(self): def isDictionary(self):
return self.inner.isDictionary() return False
def isInterface(self): def isInterface(self):
return self.inner.isInterface() return False
def isEnum(self): def isEnum(self):
return self.inner.isEnum(); return False
def tag(self): def tag(self):
# XXXkhuey this is probably wrong.
return self.inner.tag() return self.inner.tag()
def resolveType(self, parentScope): def resolveType(self, parentScope):
@ -858,10 +850,11 @@ class IDLSequenceType(IDLType):
def complete(self, scope): def complete(self, scope):
self.inner = self.inner.complete(scope) self.inner = self.inner.complete(scope)
self.name = self.inner.name
return self return self
def unroll(self): def unroll(self):
return self.inner return self.inner.unroll()
def isDistinguishableFrom(self, other): def isDistinguishableFrom(self, other):
return (other.isPrimitive() or other.isString() or other.isEnum() or return (other.isPrimitive() or other.isString() or other.isEnum() or
@ -895,32 +888,33 @@ class IDLArrayType(IDLType):
return False return False
def isPrimitive(self): def isPrimitive(self):
return self.inner.isPrimitive() return False
def isString(self): def isString(self):
return self.inner.isString() return False
def isVoid(self): def isVoid(self):
return False return False
def isSequence(self): def isSequence(self):
assert not self.inner.isSequence() assert not self.inner.isSequence()
return self.inner.isSequence() return False
def isArray(self): def isArray(self):
return True return True
def isDictionary(self): def isDictionary(self):
assert not self.inner.isDictionary() assert not self.inner.isDictionary()
return self.inner.isDictionary() return False
def isInterface(self): def isInterface(self):
return self.inner.isInterface() return False
def isEnum(self): def isEnum(self):
return self.inner.isEnum() return False
def tag(self): def tag(self):
# XXXkhuey this is probably wrong.
return self.inner.tag() return self.inner.tag()
def resolveType(self, parentScope): def resolveType(self, parentScope):
@ -932,10 +926,11 @@ class IDLArrayType(IDLType):
def complete(self, scope): def complete(self, scope):
self.inner = self.inner.complete(scope) self.inner = self.inner.complete(scope)
self.name = self.inner.name
return self return self
def unroll(self): def unroll(self):
return self.inner return self.inner.unroll()
def isDistinguishableFrom(self, other): def isDistinguishableFrom(self, other):
return (other.isPrimitive() or other.isString() or other.isEnum() or return (other.isPrimitive() or other.isString() or other.isEnum() or
@ -995,7 +990,7 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
return self.inner.tag() return self.inner.tag()
def unroll(self): def unroll(self):
return self.inner return self.inner.unroll()
def isDistinguishableFrom(self, other): def isDistinguishableFrom(self, other):
return self.inner.isDistinguishableFrom(other) return self.inner.isDistinguishableFrom(other)
@ -1041,6 +1036,10 @@ class IDLWrapperType(IDLType):
def isEnum(self): def isEnum(self):
return isinstance(self.inner, IDLEnum) return isinstance(self.inner, IDLEnum)
def resolveType(self, parentScope):
assert isinstance(parentScope, IDLScope)
self.inner.resolve(parentScope)
def isComplete(self): def isComplete(self):
return True return True
@ -1122,32 +1121,32 @@ class IDLBuiltinType(IDLType):
def __init__(self, location, name, type): def __init__(self, location, name, type):
IDLType.__init__(self, location, name) IDLType.__init__(self, location, name)
self.builtin = True self.builtin = True
self.type = type self._typeTag = type
def isPrimitive(self): def isPrimitive(self):
return self.type <= IDLBuiltinType.Types.double return self._typeTag <= IDLBuiltinType.Types.double
def isString(self): def isString(self):
return self.type == IDLBuiltinType.Types.domstring return self._typeTag == IDLBuiltinType.Types.domstring
def isInteger(self): def isInteger(self):
return self.type <= IDLBuiltinType.Types.unsigned_long_long return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
def isArrayBuffer(self): def isArrayBuffer(self):
return self.type == IDLBuiltinType.Types.ArrayBuffer return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
def isInterface(self): def isInterface(self):
# ArrayBuffers are interface types per the TypedArray spec, # ArrayBuffers are interface types per the TypedArray spec,
# but we handle them as builtins because SpiderMonkey implements # but we handle them as builtins because SpiderMonkey implements
# ArrayBuffers. # ArrayBuffers.
return self.type == IDLBuiltinType.Types.ArrayBuffer return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
def isFloat(self): def isFloat(self):
return self.type == IDLBuiltinType.Types.float or \ return self._typeTag == IDLBuiltinType.Types.float or \
self.type == IDLBuiltinType.Types.double self._typeTag == IDLBuiltinType.Types.double
def tag(self): def tag(self):
return IDLBuiltinType.TagLookup[self.type] return IDLBuiltinType.TagLookup[self._typeTag]
def isDistinguishableFrom(self, other): def isDistinguishableFrom(self, other):
if self.isPrimitive() or self.isString(): if self.isPrimitive() or self.isString():
@ -1280,7 +1279,7 @@ class IDLValue(IDLObject):
# We're both integer types. See if we fit. # We're both integer types. See if we fit.
(min, max) = integerTypeSizes[type.type] (min, max) = integerTypeSizes[type._typeTag]
if self.value <= max and self.value >= min: if self.value <= max and self.value >= min:
# Promote # Promote
return IDLValue(self.location, type, self.value) return IDLValue(self.location, type, self.value)
@ -1492,8 +1491,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
) )
def __init__(self, location, identifier, returnType, arguments, def __init__(self, location, identifier, returnType, arguments,
static, getter, setter, creator, deleter, specialType, legacycaller, static=False, getter=False, setter=False, creator=False,
stringifier): deleter=False, specialType=NamedOrIndexed.Neither,
legacycaller=False, stringifier=False):
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.__init__(self, location, identifier,
IDLInterfaceMember.Tags.Method) IDLInterfaceMember.Tags.Method)
@ -1678,6 +1679,7 @@ class Tokenizer(object):
def t_INTEGER(self, t): def t_INTEGER(self, t):
r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)' r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)'
try: try:
# Can't use int(), because that doesn't handle octal properly.
t.value = parseInt(t.value) t.value = parseInt(t.value)
except: except:
raise WebIDLError("Invalid integer literal", raise WebIDLError("Invalid integer literal",
@ -2261,8 +2263,9 @@ class Parser(Tokenizer):
"legacycaller" if legacycaller else ""), allowDoubleUnderscore=True) "legacycaller" if legacycaller else ""), allowDoubleUnderscore=True)
method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments, method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
static, getter, setter, creator, deleter, specialType, static=static, getter=getter, setter=setter, creator=creator,
legacycaller, False) deleter=deleter, specialType=specialType,
legacycaller=legacycaller, stringifier=False)
p[0] = method p[0] = method
def p_QualifiersStatic(self, p): def p_QualifiersStatic(self, p):
@ -2861,7 +2864,14 @@ class Parser(Tokenizer):
for production in self._productions: for production in self._productions:
production.finish(self.globalScope()) production.finish(self.globalScope())
return set(self._productions) # De-duplicate self._productions, without modifying its order.
seen = set()
result = []
for p in self._productions:
if p not in seen:
seen.add(p)
result.append(p)
return result
def reset(self): def reset(self):
return Parser() return Parser()

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

@ -1 +0,0 @@
__all__ = ['WebIDL']

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

@ -37,36 +37,76 @@
import os, sys import os, sys
import glob import glob
import optparse
import traceback
import WebIDL import WebIDL
class TestHarness(object): class TestHarness(object):
def __init__(self, test, verbose):
self.test = test
self.verbose = verbose
self.printed_intro = False
def start(self):
if self.verbose:
self.maybe_print_intro()
def finish(self):
if self.verbose or self.printed_intro:
print "Finished test %s" % self.test
def maybe_print_intro(self):
if not self.printed_intro:
print "Starting test %s" % self.test
self.printed_intro = True
def test_pass(self, msg):
if self.verbose:
print "TEST-PASS | %s" % msg
def test_fail(self, msg):
self.maybe_print_intro()
print "TEST-UNEXPECTED-FAIL | %s" % msg
def ok(self, condition, msg): def ok(self, condition, msg):
if condition: if condition:
print "TEST-PASS | %s" % msg self.test_pass(msg)
else: else:
print "TEST-UNEXPECTED-FAIL | %s" % msg self.test_fail(msg)
def check(self, a, b, msg): def check(self, a, b, msg):
if a == b: if a == b:
print "TEST-PASS | %s" % msg self.test_pass(msg)
else: else:
print "TEST-UNEXPECTED-FAIL | %s" % msg self.test_fail(msg)
print "\tGot %s expected %s" % (a, b) print "\tGot %s expected %s" % (a, b)
def run_tests(): def run_tests(tests, verbose):
harness = TestHarness() testdir = os.path.join(os.path.dirname(__file__), 'tests')
if not tests:
tests = glob.iglob(os.path.join(testdir, "*.py"))
sys.path.append(testdir)
tests = glob.iglob("tests/*.py")
sys.path.append("./tests")
for test in tests: for test in tests:
(testpath, ext) = os.path.splitext(os.path.basename(test)) (testpath, ext) = os.path.splitext(os.path.basename(test))
_test = __import__(testpath, globals(), locals(), ['WebIDLTest']) _test = __import__(testpath, globals(), locals(), ['WebIDLTest'])
#try:
_test.WebIDLTest.__call__(WebIDL.Parser(), harness) harness = TestHarness(test, verbose)
#except: harness.start()
# print "TEST-UNEXPECTED-FAIL | Unhandled exception in Test %s" % testpath try:
# print sys.exc_info()[0] _test.WebIDLTest.__call__(WebIDL.Parser(), harness)
print "Test %s Complete\n" % testpath except:
print "TEST-UNEXPECTED-FAIL | Unhandled exception in test %s" % testpath
traceback.print_exc()
finally:
harness.finish()
if __name__ == '__main__': if __name__ == '__main__':
run_tests() usage = """%prog [OPTIONS] [TESTS]
Where TESTS are relative to the tests directory."""
parser = optparse.OptionParser(usage=usage)
parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
help="Don't print passing tests.")
options, tests = parser.parse_args()
run_tests(tests, verbose=options.verbose)

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

@ -0,0 +1,13 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse("""
interface A {
attribute long a;
};
interface B {
attribute A[] b;
};
""");
parser.finish()

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

@ -0,0 +1,11 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse("""
interface Test {
attribute long b;
};
""");
attr = parser.finish()[0].members[0]
harness.check(attr.type.filename(), '<builtin>', 'Filename on builtin type')

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

@ -62,14 +62,14 @@ def WebIDLTest(parser, harness):
"Should be an IDLInterface") "Should be an IDLInterface")
checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor", checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
"constructor", [("TestConstructorNoArgs", [])]) "constructor", [("TestConstructorNoArgs (Wrapper)", [])])
checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor", checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
"constructor", "constructor",
[("TestConstructorWithArgs", [("TestConstructorWithArgs (Wrapper)",
[("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])]) [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor", checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
"constructor", "constructor",
[("TestConstructorOverloads", [("TestConstructorOverloads (Wrapper)",
[("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]), [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
("TestConstructorOverloads", ("TestConstructorOverloads (Wrapper)",
[("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])]) [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])

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

@ -0,0 +1,15 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse("""
interface Foo;
interface Bar;
interface Foo;
""");
results = parser.finish()
# There should be no duplicate interfaces in the result.
expectedNames = sorted(['Foo', 'Bar'])
actualNames = sorted(map(lambda iface: iface.identifier.name, results))
harness.check(actualNames, expectedNames, "Parser shouldn't output duplicate names.")

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

@ -47,7 +47,7 @@ def WebIDLTest(parser, harness):
harness.check(len(signatures), 1, "Expect one signature") harness.check(len(signatures), 1, "Expect one signature")
(returnType, arguments) = signatures[0] (returnType, arguments) = signatures[0]
harness.check(str(returnType), "TestEnum", "Method type is the correct name") harness.check(str(returnType), "TestEnum (Wrapper)", "Method type is the correct name")
harness.check(len(arguments), 1, "Method has the right number of arguments") harness.check(len(arguments), 1, "Method has the right number of arguments")
arg = arguments[0] arg = arguments[0]
harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument") harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument")
@ -58,4 +58,4 @@ def WebIDLTest(parser, harness):
"Attr has correct QName") "Attr has correct QName")
harness.check(attr.identifier.name, "foo", "Attr has correct name") harness.check(attr.identifier.name, "foo", "Attr has correct name")
harness.check(str(attr.type), "TestEnum", "Attr type is the correct name") harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name")

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

@ -0,0 +1,20 @@
import WebIDL
def WebIDLTest(parser, harness):
# Check that error messages put the '^' in the right place.
threw = False
input = 'interface ?'
try:
parser.parse(input)
results = parser.finish()
except WebIDL.WebIDLError as e:
threw = True
lines = str(e).split('\n')
harness.check(len(lines), 3, 'Expected number of lines in error message')
harness.check(lines[1], input, 'Second line shows error')
harness.check(lines[2], ' ' * (len(input) - 1) + '^',
'Correct column pointer in error message')
harness.ok(threw, "Should have thrown.")

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

@ -0,0 +1,134 @@
import WebIDL
def WebIDLTest(parser, harness):
parser.parse("""
interface TestNullableEquivalency1 {
attribute long a;
attribute long? b;
};
interface TestNullableEquivalency2 {
attribute ArrayBuffer a;
attribute ArrayBuffer? b;
};
/* Not implemented */
/*dictionary TestNullableEquivalency3Dict {
long foo = 42;
};
interface TestNullableEquivalency3 {
attribute Test3Dict a;
attribute Test3Dict? b;
};*/
enum TestNullableEquivalency4Enum {
"Foo",
"Bar"
};
interface TestNullableEquivalency4 {
attribute TestNullableEquivalency4Enum a;
attribute TestNullableEquivalency4Enum? b;
};
interface TestNullableEquivalency5 {
attribute TestNullableEquivalency4 a;
attribute TestNullableEquivalency4? b;
};
interface TestNullableEquivalency6 {
attribute boolean a;
attribute boolean? b;
};
interface TestNullableEquivalency7 {
attribute DOMString a;
attribute DOMString? b;
};
/* Not implemented. */
/*interface TestNullableEquivalency8 {
attribute float a;
attribute float? b;
};*/
interface TestNullableEquivalency8 {
attribute double a;
attribute double? b;
};
interface TestNullableEquivalency9 {
attribute object a;
attribute object? b;
};
interface TestNullableEquivalency10 {
attribute double[] a;
attribute double[]? b;
};
interface TestNullableEquivalency11 {
attribute TestNullableEquivalency9[] a;
attribute TestNullableEquivalency9[]? b;
};
""")
for decl in parser.finish():
if decl.isInterface():
checkEquivalent(decl, harness)
def checkEquivalent(iface, harness):
type1 = iface.members[0].type
type2 = iface.members[1].type
harness.check(type1.nullable(), False, 'attr1 should not be nullable')
harness.check(type2.nullable(), True, 'attr2 should be nullable')
# We don't know about type1, but type2, the nullable type, definitely
# shouldn't be builtin.
harness.check(type2.builtin, False, 'attr2 should not be builtin')
# Ensure that all attributes of type2 match those in type1, except for:
# - names on an ignore list,
# - names beginning with '_',
# - functions which throw when called with no args, and
# - class-level non-callables ("static variables").
#
# Yes, this is an ugly, fragile hack. But it finds bugs...
for attr in dir(type1):
if attr.startswith('_') or \
attr in ['nullable', 'builtin', 'filename', 'location',
'inner', 'QName'] or \
(hasattr(type(type1), attr) and not callable(getattr(type1, attr))):
continue
a1 = getattr(type1, attr)
if callable(a1):
try:
v1 = a1()
except:
# Can't call a1 with no args, so skip this attriute.
continue
try:
a2 = getattr(type2, attr)
except:
harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
continue
if not callable(a2):
harness.ok(False, "%s attribute on type %s in %s wasn't callable" % (attr, type2, iface))
continue
v2 = a2()
harness.check(v2, v1, '%s method return value' % attr)
else:
try:
a2 = getattr(type2, attr)
except:
harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
continue
harness.check(a2, a1, '%s attribute should match' % attr)