2008-08-20 06:38:24 +04:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# xpidl.py - A parser for cross-platform IDL (XPIDL) files.
|
|
|
|
#
|
2012-05-21 15:12:37 +04:00
|
|
|
# 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/.
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
"""A parser for cross-platform IDL (XPIDL) files."""
|
|
|
|
|
2020-05-01 00:49:10 +03:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2015-05-04 23:34:00 +03:00
|
|
|
import sys
|
|
|
|
import os.path
|
|
|
|
import re
|
|
|
|
from ply import lex
|
|
|
|
from ply import yacc
|
2020-04-24 19:17:26 +03:00
|
|
|
import six
|
2018-07-11 04:15:16 +03:00
|
|
|
from collections import namedtuple
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
"""A type conforms to the following pattern:
|
|
|
|
|
|
|
|
def nativeType(self, calltype):
|
|
|
|
'returns a string representation of the native type
|
2018-07-06 23:47:22 +03:00
|
|
|
calltype must be 'in', 'out', 'inout', or 'element'
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
Interface members const/method/attribute conform to the following pattern:
|
|
|
|
|
|
|
|
name = 'string'
|
|
|
|
|
|
|
|
def toIDL(self):
|
|
|
|
'returns the member signature as IDL'
|
|
|
|
"""
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
# XXX(nika): Fix the IDL files which do this so we can remove this list?
|
|
|
|
def rustBlacklistedForward(s):
|
|
|
|
"""These types are foward declared as interfaces, but never actually defined
|
|
|
|
in IDL files. We don't want to generate references to them in rust for that
|
|
|
|
reason."""
|
|
|
|
blacklisted = [
|
|
|
|
"nsIFrame",
|
|
|
|
"nsIObjectFrame",
|
|
|
|
"nsSubDocumentFrame",
|
|
|
|
]
|
|
|
|
return s in blacklisted
|
|
|
|
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def attlistToIDL(attlist):
|
|
|
|
if len(attlist) == 0:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
attlist = list(attlist)
|
2020-05-02 01:49:29 +03:00
|
|
|
attlist.sort(cmp=lambda a, b: cmp(a[0], b[0]))
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
return '[%s] ' % ','.join(["%s%s" % (name, value is not None and '(%s)' % value or '')
|
2018-05-26 07:29:52 +03:00
|
|
|
for name, value, aloc in attlist])
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
_paramsHardcode = {
|
|
|
|
2: ('array', 'shared', 'iid_is', 'size_is', 'retval'),
|
|
|
|
3: ('array', 'size_is', 'const'),
|
|
|
|
}
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def paramAttlistToIDL(attlist):
|
|
|
|
if len(attlist) == 0:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
# Hack alert: g_hash_table_foreach is pretty much unimitatable... hardcode
|
|
|
|
# quirk
|
|
|
|
attlist = list(attlist)
|
|
|
|
sorted = []
|
|
|
|
if len(attlist) in _paramsHardcode:
|
|
|
|
for p in _paramsHardcode[len(attlist)]:
|
|
|
|
i = 0
|
|
|
|
while i < len(attlist):
|
|
|
|
if attlist[i][0] == p:
|
|
|
|
sorted.append(attlist[i])
|
|
|
|
del attlist[i]
|
|
|
|
continue
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
sorted.extend(attlist)
|
|
|
|
|
|
|
|
return '[%s] ' % ', '.join(["%s%s" % (name, value is not None and ' (%s)' % value or '')
|
|
|
|
for name, value, aloc in sorted])
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2009-09-24 21:59:43 +04:00
|
|
|
def unaliasType(t):
|
|
|
|
while t.kind == 'typedef':
|
|
|
|
t = t.realtype
|
|
|
|
assert t is not None
|
|
|
|
return t
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2009-09-24 21:59:43 +04:00
|
|
|
def getBuiltinOrNativeTypeName(t):
|
|
|
|
t = unaliasType(t)
|
|
|
|
if t.kind == 'builtin':
|
|
|
|
return t.name
|
|
|
|
elif t.kind == 'native':
|
|
|
|
assert t.specialtype is not None
|
|
|
|
return '[%s]' % t.specialtype
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class BuiltinLocation(object):
|
|
|
|
def get(self):
|
|
|
|
return "<builtin type>"
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.get()
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Builtin(object):
|
|
|
|
kind = 'builtin'
|
|
|
|
location = BuiltinLocation
|
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def __init__(self, name, nativename, rustname, signed=False, maybeConst=False):
|
2008-08-20 06:38:24 +04:00
|
|
|
self.name = name
|
|
|
|
self.nativename = nativename
|
2018-01-05 01:32:48 +03:00
|
|
|
self.rustname = rustname
|
2008-08-20 06:38:24 +04:00
|
|
|
self.signed = signed
|
|
|
|
self.maybeConst = maybeConst
|
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def isPointer(self):
|
|
|
|
"""Check if this type is a pointer type - this will control how pointers act"""
|
|
|
|
return self.nativename.endswith('*')
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def nativeType(self, calltype, shared=False, const=False):
|
2018-07-24 22:08:04 +03:00
|
|
|
if self.name in ["string", "wstring"] and calltype == 'element':
|
2018-07-24 23:31:09 +03:00
|
|
|
raise IDLError("Use string class types for string Array elements", self.location)
|
2018-07-24 22:08:04 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
if const:
|
2018-05-26 07:29:52 +03:00
|
|
|
print >>sys.stderr, IDLError(
|
|
|
|
"[const] doesn't make sense on builtin types.", self.location, warning=True)
|
2008-08-20 06:38:24 +04:00
|
|
|
const = 'const '
|
2018-01-05 01:32:48 +03:00
|
|
|
elif calltype == 'in' and self.isPointer():
|
2008-08-20 06:38:24 +04:00
|
|
|
const = 'const '
|
|
|
|
elif shared:
|
2018-01-05 01:32:48 +03:00
|
|
|
if not self.isPointer():
|
2008-08-20 06:38:24 +04:00
|
|
|
raise IDLError("[shared] not applicable to non-pointer types.", self.location)
|
|
|
|
const = 'const '
|
|
|
|
else:
|
|
|
|
const = ''
|
|
|
|
return "%s%s %s" % (const, self.nativename,
|
2018-07-06 23:47:22 +03:00
|
|
|
'*' if 'out' in calltype else '')
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def rustType(self, calltype, shared=False, const=False):
|
|
|
|
# We want to rewrite any *mut pointers to *const pointers if constness
|
|
|
|
# was requested.
|
Bug 1535486 - Handle the element type correctly with rust XPCOM types, r=mccr8
Before the Array<T> type, the calltype argument could be in, out, or inout,
however with Array<T> the element type was added.
When I added Array<T>, I changed the checks in files which check calltype !=
'in' to instead check 'out' in calltype, such that element would act more like
in in most cases (not adding the outparam *).
However, I never made that change for rust code, as it didn't support Array<T>
at the time. When I turned on Array<T> support for rust code, I forgot to go
through and change the conditions, which lead to this bug.
Differential Revision: https://phabricator.services.mozilla.com/D24283
--HG--
extra : moz-landing-system : lando
2019-03-23 00:32:59 +03:00
|
|
|
const = const or ('out' not in calltype and self.isPointer()) or shared
|
2018-01-05 01:32:48 +03:00
|
|
|
rustname = self.rustname
|
|
|
|
if const and self.isPointer():
|
|
|
|
rustname = self.rustname.replace("*mut", "*const")
|
|
|
|
|
Bug 1535486 - Handle the element type correctly with rust XPCOM types, r=mccr8
Before the Array<T> type, the calltype argument could be in, out, or inout,
however with Array<T> the element type was added.
When I added Array<T>, I changed the checks in files which check calltype !=
'in' to instead check 'out' in calltype, such that element would act more like
in in most cases (not adding the outparam *).
However, I never made that change for rust code, as it didn't support Array<T>
at the time. When I turned on Array<T> support for rust code, I forgot to go
through and change the conditions, which lead to this bug.
Differential Revision: https://phabricator.services.mozilla.com/D24283
--HG--
extra : moz-landing-system : lando
2019-03-23 00:32:59 +03:00
|
|
|
return "%s%s" % ('*mut ' if 'out' in calltype else '', rustname)
|
2018-01-05 01:32:48 +03:00
|
|
|
|
2018-05-26 07:29:52 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
builtinNames = [
|
2018-01-05 01:32:48 +03:00
|
|
|
Builtin('boolean', 'bool', 'bool'),
|
|
|
|
Builtin('void', 'void', 'libc::c_void'),
|
2019-07-23 16:42:44 +03:00
|
|
|
Builtin('octet', 'uint8_t', 'u8', False, True),
|
|
|
|
Builtin('short', 'int16_t', 'i16', True, True),
|
|
|
|
Builtin('long', 'int32_t', 'i32', True, True),
|
|
|
|
Builtin('long long', 'int64_t', 'i64', True, False),
|
|
|
|
Builtin('unsigned short', 'uint16_t', 'u16', False, True),
|
|
|
|
Builtin('unsigned long', 'uint32_t', 'u32', False, True),
|
|
|
|
Builtin('unsigned long long', 'uint64_t', 'u64', False, False),
|
2018-01-05 01:32:48 +03:00
|
|
|
Builtin('float', 'float', 'libc::c_float', True, False),
|
|
|
|
Builtin('double', 'double', 'libc::c_double', True, False),
|
|
|
|
Builtin('char', 'char', 'libc::c_char', True, False),
|
|
|
|
Builtin('string', 'char *', '*const libc::c_char', False, False),
|
2019-07-23 16:42:44 +03:00
|
|
|
Builtin('wchar', 'char16_t', 'i16', False, False),
|
|
|
|
Builtin('wstring', 'char16_t *', '*const i16', False, False),
|
2008-08-20 06:38:24 +04:00
|
|
|
]
|
|
|
|
|
|
|
|
builtinMap = {}
|
|
|
|
for b in builtinNames:
|
|
|
|
builtinMap[b.name] = b
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Location(object):
|
|
|
|
_line = None
|
|
|
|
|
|
|
|
def __init__(self, lexer, lineno, lexpos):
|
|
|
|
self._lineno = lineno
|
|
|
|
self._lexpos = lexpos
|
|
|
|
self._lexdata = lexer.lexdata
|
|
|
|
self._file = getattr(lexer, 'filename', "<unknown>")
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
2015-05-20 22:55:00 +03:00
|
|
|
return (self._lexpos == other._lexpos and
|
|
|
|
self._file == other._file)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def resolve(self):
|
|
|
|
if self._line:
|
|
|
|
return
|
|
|
|
|
|
|
|
startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
|
|
|
|
endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
|
|
|
|
self._line = self._lexdata[startofline:endofline]
|
|
|
|
self._colno = self._lexpos - startofline
|
|
|
|
|
|
|
|
def pointerline(self):
|
|
|
|
def i():
|
2020-05-02 01:49:29 +03:00
|
|
|
for i in xrange(0, self._colno):
|
2008-08-20 06:38:24 +04:00
|
|
|
yield " "
|
|
|
|
yield "^"
|
|
|
|
|
|
|
|
return "".join(i())
|
|
|
|
|
|
|
|
def get(self):
|
|
|
|
self.resolve()
|
|
|
|
return "%s line %s:%s" % (self._file, self._lineno, self._colno)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
self.resolve()
|
|
|
|
return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
|
|
|
|
self._line, self.pointerline())
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class NameMap(object):
|
|
|
|
"""Map of name -> object. Each object must have a .name and .location property.
|
|
|
|
Setting the same name twice throws an error."""
|
2018-05-26 07:29:52 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def __init__(self):
|
|
|
|
self._d = {}
|
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
if key in builtinMap:
|
|
|
|
return builtinMap[key]
|
|
|
|
return self._d[key]
|
|
|
|
|
|
|
|
def __iter__(self):
|
2020-04-24 19:17:26 +03:00
|
|
|
return six.itervalues(self._d)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def __contains__(self, key):
|
|
|
|
return key in builtinMap or key in self._d
|
|
|
|
|
|
|
|
def set(self, object):
|
|
|
|
if object.name in builtinMap:
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError("name '%s' is a builtin and cannot be redeclared" %
|
|
|
|
(object.name), object.location)
|
2012-06-12 17:01:06 +04:00
|
|
|
if object.name.startswith("_"):
|
|
|
|
object.name = object.name[1:]
|
2008-08-20 06:38:24 +04:00
|
|
|
if object.name in self._d:
|
|
|
|
old = self._d[object.name]
|
2015-05-05 18:24:00 +03:00
|
|
|
if old == object:
|
|
|
|
return
|
2008-08-20 06:38:24 +04:00
|
|
|
if isinstance(old, Forward) and isinstance(object, Interface):
|
|
|
|
self._d[object.name] = object
|
|
|
|
elif isinstance(old, Interface) and isinstance(object, Forward):
|
|
|
|
pass
|
|
|
|
else:
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError("name '%s' specified twice. Previous location: %s" %
|
|
|
|
(object.name, self._d[object.name].location), object.location)
|
2008-08-20 06:38:24 +04:00
|
|
|
else:
|
|
|
|
self._d[object.name] = object
|
|
|
|
|
|
|
|
def get(self, id, location):
|
|
|
|
try:
|
|
|
|
return self[id]
|
|
|
|
except KeyError:
|
|
|
|
raise IDLError("Name '%s' not found", location)
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
class RustNoncompat(Exception):
|
2018-05-26 08:17:28 +03:00
|
|
|
"""
|
|
|
|
This exception is raised when a particular type or function cannot be safely exposed to
|
|
|
|
rust code
|
|
|
|
"""
|
2018-05-26 07:29:52 +03:00
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def __init__(self, reason):
|
|
|
|
self.reason = reason
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.reason
|
|
|
|
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class IDLError(Exception):
|
|
|
|
def __init__(self, message, location, warning=False):
|
|
|
|
self.message = message
|
|
|
|
self.location = location
|
|
|
|
self.warning = warning
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "%s: %s, %s" % (self.warning and 'warning' or 'error',
|
|
|
|
self.message, self.location)
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Include(object):
|
|
|
|
kind = 'include'
|
|
|
|
|
|
|
|
def __init__(self, filename, location):
|
|
|
|
self.filename = filename
|
|
|
|
self.location = location
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "".join(["include '%s'\n" % self.filename])
|
|
|
|
|
|
|
|
def resolve(self, parent):
|
|
|
|
def incfiles():
|
|
|
|
yield self.filename
|
|
|
|
for dir in parent.incdirs:
|
|
|
|
yield os.path.join(dir, self.filename)
|
|
|
|
|
|
|
|
for file in incfiles():
|
2015-05-05 18:24:00 +03:00
|
|
|
if not os.path.exists(file):
|
|
|
|
continue
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2020-05-02 01:49:29 +03:00
|
|
|
self.IDL = parent.parser.parse(open(file).read(), filename=file)
|
2018-04-07 01:20:49 +03:00
|
|
|
self.IDL.resolve(parent.incdirs, parent.parser, parent.webidlconfig)
|
2008-08-20 06:38:24 +04:00
|
|
|
for type in self.IDL.getNames():
|
|
|
|
parent.setName(type)
|
2011-07-20 01:46:25 +04:00
|
|
|
parent.deps.extend(self.IDL.deps)
|
2008-08-20 06:38:24 +04:00
|
|
|
return
|
|
|
|
|
|
|
|
raise IDLError("File '%s' not found" % self.filename, self.location)
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class IDL(object):
|
|
|
|
def __init__(self, productions):
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
self.hasSequence = False
|
2008-08-20 06:38:24 +04:00
|
|
|
self.productions = productions
|
2011-07-20 01:46:25 +04:00
|
|
|
self.deps = []
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def setName(self, object):
|
|
|
|
self.namemap.set(object)
|
|
|
|
|
|
|
|
def getName(self, id, location):
|
2018-07-24 23:31:09 +03:00
|
|
|
if id.name == 'Array':
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
if id.params is None or len(id.params) != 1:
|
2018-07-24 23:31:09 +03:00
|
|
|
raise IDLError("Array takes exactly 1 parameter", location)
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
self.hasSequence = True
|
2018-07-24 23:31:09 +03:00
|
|
|
return Array(self.getName(id.params[0], location), location)
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
|
2018-07-11 04:15:16 +03:00
|
|
|
if id.params is not None:
|
|
|
|
raise IDLError("Generic type '%s' unrecognized" % id.name, location)
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
try:
|
2018-07-11 04:15:16 +03:00
|
|
|
return self.namemap[id.name]
|
2008-08-20 06:38:24 +04:00
|
|
|
except KeyError:
|
2018-07-11 04:15:16 +03:00
|
|
|
raise IDLError("type '%s' not found" % id.name, location)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def hasName(self, id):
|
|
|
|
return id in self.namemap
|
|
|
|
|
|
|
|
def getNames(self):
|
|
|
|
return iter(self.namemap)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "".join([str(p) for p in self.productions])
|
|
|
|
|
2018-04-07 01:20:49 +03:00
|
|
|
def resolve(self, incdirs, parser, webidlconfig):
|
2008-08-20 06:38:24 +04:00
|
|
|
self.namemap = NameMap()
|
|
|
|
self.incdirs = incdirs
|
|
|
|
self.parser = parser
|
2018-04-07 01:20:49 +03:00
|
|
|
self.webidlconfig = webidlconfig
|
2008-08-20 06:38:24 +04:00
|
|
|
for p in self.productions:
|
|
|
|
p.resolve(self)
|
|
|
|
|
|
|
|
def includes(self):
|
|
|
|
for p in self.productions:
|
|
|
|
if p.kind == 'include':
|
|
|
|
yield p
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
if self.hasSequence:
|
|
|
|
yield Include("nsTArray.h", BuiltinLocation)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2011-08-08 19:14:34 +04:00
|
|
|
def needsJSTypes(self):
|
|
|
|
for p in self.productions:
|
|
|
|
if p.kind == 'interface' and p.needsJSTypes():
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class CDATA(object):
|
|
|
|
kind = 'cdata'
|
|
|
|
_re = re.compile(r'\n+')
|
|
|
|
|
|
|
|
def __init__(self, data, location):
|
|
|
|
self.data = self._re.sub('\n', data)
|
|
|
|
self.location = location
|
|
|
|
|
|
|
|
def resolve(self, parent):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "cdata: %s\n\t%r\n" % (self.location.get(), self.data)
|
|
|
|
|
2011-08-05 22:34:13 +04:00
|
|
|
def count(self):
|
|
|
|
return 0
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Typedef(object):
|
|
|
|
kind = 'typedef'
|
|
|
|
|
|
|
|
def __init__(self, type, name, location, doccomments):
|
|
|
|
self.type = type
|
|
|
|
self.name = name
|
|
|
|
self.location = location
|
|
|
|
self.doccomments = doccomments
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
return self.name == other.name and self.type == other.type
|
|
|
|
|
|
|
|
def resolve(self, parent):
|
|
|
|
parent.setName(self)
|
|
|
|
self.realtype = parent.getName(self.type, self.location)
|
|
|
|
|
2018-07-24 22:08:04 +03:00
|
|
|
if not isinstance(self.realtype, (Builtin, Native, Typedef)):
|
|
|
|
raise IDLError("Unsupported typedef target type", self.location)
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def nativeType(self, calltype):
|
2018-07-06 23:47:22 +03:00
|
|
|
return "%s %s" % (self.name, '*' if 'out' in calltype else '')
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def rustType(self, calltype):
|
2018-10-30 00:23:21 +03:00
|
|
|
if self.name == 'nsresult':
|
2019-02-26 23:51:19 +03:00
|
|
|
return "%s::nserror::nsresult" % ('*mut ' if 'out' in calltype else '')
|
2018-10-30 00:23:21 +03:00
|
|
|
|
2019-02-26 23:51:19 +03:00
|
|
|
return "%s%s" % ('*mut ' if 'out' in calltype else '', self.name)
|
2018-01-05 01:32:48 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def __str__(self):
|
|
|
|
return "typedef %s %s\n" % (self.type, self.name)
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Forward(object):
|
|
|
|
kind = 'forward'
|
|
|
|
|
|
|
|
def __init__(self, name, location, doccomments):
|
|
|
|
self.name = name
|
|
|
|
self.location = location
|
|
|
|
self.doccomments = doccomments
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
return other.kind == 'forward' and other.name == self.name
|
|
|
|
|
|
|
|
def resolve(self, parent):
|
|
|
|
# Hack alert: if an identifier is already present, move the doccomments
|
|
|
|
# forward.
|
|
|
|
if parent.hasName(self.name):
|
2020-05-02 01:49:29 +03:00
|
|
|
for i in xrange(0, len(parent.productions)):
|
2015-05-05 18:24:00 +03:00
|
|
|
if parent.productions[i] is self:
|
|
|
|
break
|
2020-05-02 01:49:29 +03:00
|
|
|
for i in xrange(i + 1, len(parent.productions)):
|
2008-08-20 06:38:24 +04:00
|
|
|
if hasattr(parent.productions[i], 'doccomments'):
|
|
|
|
parent.productions[i].doccomments[0:0] = self.doccomments
|
|
|
|
break
|
|
|
|
|
|
|
|
parent.setName(self)
|
|
|
|
|
|
|
|
def nativeType(self, calltype):
|
2018-07-24 22:08:04 +03:00
|
|
|
if calltype == 'element':
|
|
|
|
return 'RefPtr<%s>' % self.name
|
2018-07-06 23:47:22 +03:00
|
|
|
return "%s *%s" % (self.name, '*' if 'out' in calltype else '')
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def rustType(self, calltype):
|
|
|
|
if rustBlacklistedForward(self.name):
|
|
|
|
raise RustNoncompat("forward declaration %s is unsupported" % self.name)
|
2019-02-26 23:51:19 +03:00
|
|
|
if calltype == 'element':
|
|
|
|
return 'RefPtr<%s>' % self.name
|
Bug 1535486 - Handle the element type correctly with rust XPCOM types, r=mccr8
Before the Array<T> type, the calltype argument could be in, out, or inout,
however with Array<T> the element type was added.
When I added Array<T>, I changed the checks in files which check calltype !=
'in' to instead check 'out' in calltype, such that element would act more like
in in most cases (not adding the outparam *).
However, I never made that change for rust code, as it didn't support Array<T>
at the time. When I turned on Array<T> support for rust code, I forgot to go
through and change the conditions, which lead to this bug.
Differential Revision: https://phabricator.services.mozilla.com/D24283
--HG--
extra : moz-landing-system : lando
2019-03-23 00:32:59 +03:00
|
|
|
return "%s*const %s" % ('*mut' if 'out' in calltype else '',
|
2018-01-05 01:32:48 +03:00
|
|
|
self.name)
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def __str__(self):
|
|
|
|
return "forward-declared %s\n" % self.name
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Native(object):
|
|
|
|
kind = 'native'
|
|
|
|
|
|
|
|
modifier = None
|
|
|
|
specialtype = None
|
|
|
|
|
2018-07-06 23:47:22 +03:00
|
|
|
# A tuple type here means that a custom value is used for each calltype:
|
|
|
|
# (in, out/inout, array element) respectively.
|
|
|
|
# A `None` here means that the written type should be used as-is.
|
2008-08-20 06:38:24 +04:00
|
|
|
specialtypes = {
|
|
|
|
'nsid': None,
|
2018-07-06 23:47:22 +03:00
|
|
|
'utf8string': ('const nsACString&', 'nsACString&', 'nsCString'),
|
|
|
|
'cstring': ('const nsACString&', 'nsACString&', 'nsCString'),
|
|
|
|
'astring': ('const nsAString&', 'nsAString&', 'nsString'),
|
|
|
|
'jsval': ('JS::HandleValue', 'JS::MutableHandleValue', 'JS::Value'),
|
2018-04-19 08:20:49 +03:00
|
|
|
'promise': '::mozilla::dom::Promise',
|
|
|
|
}
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def __init__(self, name, nativename, attlist, location):
|
|
|
|
self.name = name
|
|
|
|
self.nativename = nativename
|
|
|
|
self.location = location
|
|
|
|
|
|
|
|
for name, value, aloc in attlist:
|
|
|
|
if value is not None:
|
|
|
|
raise IDLError("Unexpected attribute value", aloc)
|
|
|
|
if name in ('ptr', 'ref'):
|
|
|
|
if self.modifier is not None:
|
|
|
|
raise IDLError("More than one ptr/ref modifier", aloc)
|
|
|
|
self.modifier = name
|
|
|
|
elif name in self.specialtypes.keys():
|
|
|
|
if self.specialtype is not None:
|
|
|
|
raise IDLError("More than one special type", aloc)
|
|
|
|
self.specialtype = name
|
|
|
|
if self.specialtypes[name] is not None:
|
|
|
|
self.nativename = self.specialtypes[name]
|
|
|
|
else:
|
|
|
|
raise IDLError("Unexpected attribute", aloc)
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
2015-05-20 22:55:00 +03:00
|
|
|
return (self.name == other.name and
|
|
|
|
self.nativename == other.nativename and
|
|
|
|
self.modifier == other.modifier and
|
|
|
|
self.specialtype == other.specialtype)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def resolve(self, parent):
|
|
|
|
parent.setName(self)
|
|
|
|
|
2011-08-10 04:48:41 +04:00
|
|
|
def isPtr(self, calltype):
|
2014-01-09 21:39:36 +04:00
|
|
|
return self.modifier == 'ptr'
|
2011-08-10 04:48:41 +04:00
|
|
|
|
|
|
|
def isRef(self, calltype):
|
2014-01-09 21:39:36 +04:00
|
|
|
return self.modifier == 'ref'
|
2011-08-10 04:48:41 +04:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def nativeType(self, calltype, const=False, shared=False):
|
|
|
|
if shared:
|
|
|
|
if calltype != 'out':
|
2018-07-24 22:08:04 +03:00
|
|
|
raise IDLError("[shared] only applies to out parameters.", self.location)
|
2008-08-20 06:38:24 +04:00
|
|
|
const = True
|
|
|
|
|
2018-07-06 23:47:22 +03:00
|
|
|
if isinstance(self.nativename, tuple):
|
|
|
|
if calltype == 'in':
|
|
|
|
return self.nativename[0] + ' '
|
|
|
|
elif 'out' in calltype:
|
|
|
|
return self.nativename[1] + ' '
|
|
|
|
else:
|
|
|
|
return self.nativename[2] + ' '
|
2014-01-09 21:39:36 +04:00
|
|
|
|
2018-07-06 23:47:22 +03:00
|
|
|
# 'in' nsid parameters should be made 'const'
|
|
|
|
if self.specialtype == 'nsid' and calltype == 'in':
|
|
|
|
const = True
|
2018-07-24 22:08:04 +03:00
|
|
|
|
2018-07-24 22:08:04 +03:00
|
|
|
if calltype == 'element':
|
2019-02-14 00:42:00 +03:00
|
|
|
if self.specialtype == 'nsid':
|
|
|
|
if self.isPtr(calltype):
|
|
|
|
raise IDLError("Array<nsIDPtr> not yet supported. "
|
|
|
|
"File an XPConnect bug if you need it.", self.location)
|
|
|
|
|
|
|
|
# ns[CI]?IDs should be held directly in Array<T>s
|
|
|
|
return self.nativename
|
|
|
|
|
2018-07-24 22:08:04 +03:00
|
|
|
if self.isRef(calltype):
|
2018-07-24 23:31:09 +03:00
|
|
|
raise IDLError("[ref] qualified type unsupported in Array<T>", self.location)
|
2018-07-24 22:08:04 +03:00
|
|
|
|
2018-07-24 23:31:09 +03:00
|
|
|
# Promises should be held in RefPtr<T> in Array<T>s
|
2018-07-24 22:08:04 +03:00
|
|
|
if self.specialtype == 'promise':
|
|
|
|
return 'RefPtr<mozilla::dom::Promise>'
|
|
|
|
|
2011-08-10 04:48:41 +04:00
|
|
|
if self.isRef(calltype):
|
2018-07-06 23:47:22 +03:00
|
|
|
m = '& ' # [ref] is always passed with a single indirection
|
2008-08-20 06:38:24 +04:00
|
|
|
else:
|
2018-07-06 23:47:22 +03:00
|
|
|
m = '* ' if 'out' in calltype else ''
|
|
|
|
if self.isPtr(calltype):
|
|
|
|
m += '* '
|
2008-08-20 06:38:24 +04:00
|
|
|
return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
|
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def rustType(self, calltype, const=False, shared=False):
|
2018-07-06 23:47:22 +03:00
|
|
|
# For the most part, 'native' types don't make sense in rust, as they
|
|
|
|
# are native C++ types. However, we can support a few types here, as
|
|
|
|
# they're important.
|
|
|
|
#
|
|
|
|
# NOTE: This code doesn't try to perfectly match C++ constness, as
|
|
|
|
# constness doesn't affect ABI, and raw pointers are already unsafe.
|
2018-01-05 01:32:48 +03:00
|
|
|
|
2018-07-06 23:47:22 +03:00
|
|
|
if self.modifier not in ['ptr', 'ref']:
|
|
|
|
raise RustNoncompat('Rust only supports [ref] / [ptr] native types')
|
2018-01-05 01:32:48 +03:00
|
|
|
|
2018-07-06 23:47:22 +03:00
|
|
|
prefix = '*mut ' if 'out' in calltype else '*const '
|
|
|
|
if 'out' in calltype and self.modifier == 'ptr':
|
|
|
|
prefix += '*mut '
|
2018-01-05 01:32:48 +03:00
|
|
|
|
2018-07-06 23:47:22 +03:00
|
|
|
if self.specialtype == 'nsid':
|
2019-02-26 23:51:19 +03:00
|
|
|
if 'element' in calltype:
|
|
|
|
if self.isPtr(calltype):
|
|
|
|
raise IDLError("Array<nsIDPtr> not yet supported. "
|
|
|
|
"File an XPConnect bug if you need it.", self.location)
|
|
|
|
return self.nativename
|
2018-07-06 23:47:22 +03:00
|
|
|
return prefix + self.nativename
|
|
|
|
if self.specialtype in ['cstring', 'utf8string']:
|
2018-07-24 22:08:04 +03:00
|
|
|
if 'element' in calltype:
|
2018-07-06 23:47:22 +03:00
|
|
|
return '::nsstring::nsCString'
|
|
|
|
return prefix + '::nsstring::nsACString'
|
2018-09-11 08:14:28 +03:00
|
|
|
if self.specialtype == 'astring':
|
2018-07-24 22:08:04 +03:00
|
|
|
if 'element' in calltype:
|
2018-07-06 23:47:22 +03:00
|
|
|
return '::nsstring::nsString'
|
|
|
|
return prefix + '::nsstring::nsAString'
|
|
|
|
if self.nativename == 'void':
|
|
|
|
return prefix + 'libc::c_void'
|
|
|
|
|
|
|
|
if self.specialtype:
|
|
|
|
raise RustNoncompat("specialtype %s unsupported" % self.specialtype)
|
|
|
|
raise RustNoncompat("native type %s unsupported" % self.nativename)
|
2018-01-05 01:32:48 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def __str__(self):
|
|
|
|
return "native %s(%s)\n" % (self.name, self.nativename)
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2018-04-07 01:22:14 +03:00
|
|
|
class WebIDL(object):
|
|
|
|
kind = 'webidl'
|
|
|
|
|
|
|
|
def __init__(self, name, location):
|
|
|
|
self.name = name
|
|
|
|
self.location = location
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
return other.kind == 'webidl' and self.name == other.name
|
|
|
|
|
|
|
|
def resolve(self, parent):
|
|
|
|
# XXX(nika): We don't handle _every_ kind of webidl object here (as that
|
|
|
|
# would be hard). For example, we don't support nsIDOM*-defaulting
|
|
|
|
# interfaces.
|
|
|
|
# TODO: More explicit compile-time checks?
|
|
|
|
|
|
|
|
assert parent.webidlconfig is not None, \
|
|
|
|
"WebIDL declarations require passing webidlconfig to resolve."
|
|
|
|
|
|
|
|
# Resolve our native name according to the WebIDL configs.
|
|
|
|
config = parent.webidlconfig.get(self.name, {})
|
|
|
|
self.native = config.get('nativeType')
|
|
|
|
if self.native is None:
|
|
|
|
self.native = "mozilla::dom::%s" % self.name
|
|
|
|
self.headerFile = config.get('headerFile')
|
|
|
|
if self.headerFile is None:
|
|
|
|
self.headerFile = self.native.replace('::', '/') + '.h'
|
|
|
|
|
|
|
|
parent.setName(self)
|
|
|
|
|
2020-05-04 20:04:06 +03:00
|
|
|
def nativeType(self, calltype):
|
2018-07-24 22:08:04 +03:00
|
|
|
if calltype == 'element':
|
2020-05-04 20:04:06 +03:00
|
|
|
return 'RefPtr<%s>' % self.native
|
|
|
|
return "%s *%s" % (self.native, '*' if 'out' in calltype else '')
|
2018-04-07 01:22:14 +03:00
|
|
|
|
2020-05-04 20:04:06 +03:00
|
|
|
def rustType(self, calltype):
|
2018-04-07 01:22:14 +03:00
|
|
|
# Just expose the type as a void* - we can't do any better.
|
2018-07-06 23:47:22 +03:00
|
|
|
return "%s*const libc::c_void" % ('*mut ' if 'out' in calltype else '')
|
2018-04-07 01:22:14 +03:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "webidl %s\n" % self.name
|
|
|
|
|
|
|
|
|
2014-03-07 15:51:51 +04:00
|
|
|
class Interface(object):
|
|
|
|
kind = 'interface'
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def __init__(self, name, attlist, base, members, location, doccomments):
|
|
|
|
self.name = name
|
|
|
|
self.attributes = InterfaceAttributes(attlist, location)
|
|
|
|
self.base = base
|
|
|
|
self.members = members
|
|
|
|
self.location = location
|
|
|
|
self.namemap = NameMap()
|
|
|
|
self.doccomments = doccomments
|
|
|
|
self.nativename = name
|
|
|
|
|
|
|
|
for m in members:
|
|
|
|
if not isinstance(m, CDATA):
|
|
|
|
self.namemap.set(m)
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
return self.name == other.name and self.location == other.location
|
|
|
|
|
|
|
|
def resolve(self, parent):
|
|
|
|
self.idl = parent
|
|
|
|
|
|
|
|
# Hack alert: if an identifier is already present, libIDL assigns
|
|
|
|
# doc comments incorrectly. This is quirks-mode extraordinaire!
|
|
|
|
if parent.hasName(self.name):
|
|
|
|
for member in self.members:
|
|
|
|
if hasattr(member, 'doccomments'):
|
|
|
|
member.doccomments[0:0] = self.doccomments
|
|
|
|
break
|
2018-07-11 04:15:16 +03:00
|
|
|
self.doccomments = parent.getName(TypeId(self.name), None).doccomments
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2012-02-28 03:57:28 +04:00
|
|
|
if self.attributes.function:
|
|
|
|
has_method = False
|
|
|
|
for member in self.members:
|
2019-10-10 21:50:33 +03:00
|
|
|
if member.kind == 'method':
|
2012-02-28 03:57:28 +04:00
|
|
|
if has_method:
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError(
|
2018-05-26 08:17:28 +03:00
|
|
|
"interface '%s' has multiple methods, but marked 'function'" %
|
|
|
|
self.name, self.location)
|
2012-02-28 03:57:28 +04:00
|
|
|
else:
|
|
|
|
has_method = True
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
parent.setName(self)
|
2011-11-08 23:14:53 +04:00
|
|
|
if self.base is not None:
|
2018-07-11 04:15:16 +03:00
|
|
|
realbase = parent.getName(TypeId(self.base), self.location)
|
2014-03-07 15:51:51 +04:00
|
|
|
if realbase.kind != 'interface':
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError("interface '%s' inherits from non-interface type '%s'" %
|
|
|
|
(self.name, self.base), self.location)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
if self.attributes.scriptable and not realbase.attributes.scriptable:
|
2018-05-26 08:17:28 +03:00
|
|
|
raise IDLError("interface '%s' is scriptable but derives from "
|
|
|
|
"non-scriptable '%s'" %
|
2018-05-26 07:29:52 +03:00
|
|
|
(self.name, self.base), self.location, warning=True)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2018-05-26 08:17:28 +03:00
|
|
|
if (self.attributes.scriptable and realbase.attributes.builtinclass and
|
|
|
|
not self.attributes.builtinclass):
|
|
|
|
raise IDLError("interface '%s' is not builtinclass but derives from "
|
|
|
|
"builtinclass '%s'" %
|
|
|
|
(self.name, self.base), self.location)
|
2020-01-25 02:13:30 +03:00
|
|
|
elif self.name != 'nsISupports':
|
|
|
|
raise IDLError("Interface '%s' must inherit from nsISupports" %
|
|
|
|
self.name, self.location)
|
2012-06-06 20:30:58 +04:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
for member in self.members:
|
|
|
|
member.resolve(self)
|
|
|
|
|
2011-08-05 22:34:13 +04:00
|
|
|
# The number 250 is NOT arbitrary; this number is the maximum number of
|
2014-06-30 19:39:46 +04:00
|
|
|
# stub entries defined in xpcom/reflect/xptcall/genstubs.pl
|
2011-08-05 22:34:13 +04:00
|
|
|
# Do not increase this value without increasing the number in that
|
|
|
|
# location, or you WILL cause otherwise unknown problems!
|
|
|
|
if self.countEntries() > 250 and not self.attributes.builtinclass:
|
2015-05-20 22:55:00 +03:00
|
|
|
raise IDLError("interface '%s' has too many entries" % self.name, self.location)
|
2011-08-05 22:34:13 +04:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def nativeType(self, calltype, const=False):
|
2018-07-24 22:08:04 +03:00
|
|
|
if calltype == 'element':
|
|
|
|
return 'RefPtr<%s>' % self.name
|
2018-07-06 23:47:22 +03:00
|
|
|
return "%s%s *%s" % ('const ' if const else '', self.name,
|
|
|
|
'*' if 'out' in calltype else '')
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2018-07-06 23:47:22 +03:00
|
|
|
def rustType(self, calltype, const=False):
|
2019-02-26 23:51:19 +03:00
|
|
|
if calltype == 'element':
|
|
|
|
return 'RefPtr<%s>' % self.name
|
2018-07-06 23:47:22 +03:00
|
|
|
return "%s*const %s" % ('*mut ' if 'out' in calltype else '',
|
2018-01-05 01:32:48 +03:00
|
|
|
self.name)
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def __str__(self):
|
|
|
|
l = ["interface %s\n" % self.name]
|
|
|
|
if self.base is not None:
|
|
|
|
l.append("\tbase %s\n" % self.base)
|
|
|
|
l.append(str(self.attributes))
|
|
|
|
if self.members is None:
|
|
|
|
l.append("\tincomplete type\n")
|
|
|
|
else:
|
|
|
|
for m in self.members:
|
|
|
|
l.append(str(m))
|
|
|
|
return "".join(l)
|
|
|
|
|
|
|
|
def getConst(self, name, location):
|
2011-08-25 21:24:58 +04:00
|
|
|
# The constant may be in a base class
|
|
|
|
iface = self
|
|
|
|
while name not in iface.namemap and iface is not None:
|
2018-07-11 04:15:16 +03:00
|
|
|
iface = self.idl.getName(TypeId(self.base), self.location)
|
2011-08-25 21:24:58 +04:00
|
|
|
if iface is None:
|
2018-07-24 22:08:04 +03:00
|
|
|
raise IDLError("cannot find symbol '%s'" % name, self.location)
|
2011-08-25 21:24:58 +04:00
|
|
|
c = iface.namemap.get(name, location)
|
2008-08-20 06:38:24 +04:00
|
|
|
if c.kind != 'const':
|
|
|
|
raise IDLError("symbol '%s' is not a constant", c.location)
|
|
|
|
|
|
|
|
return c.getValue()
|
|
|
|
|
2011-08-08 19:14:34 +04:00
|
|
|
def needsJSTypes(self):
|
|
|
|
for m in self.members:
|
2018-07-11 04:15:16 +03:00
|
|
|
if m.kind == "attribute" and m.type == TypeId("jsval"):
|
2011-08-08 19:14:34 +04:00
|
|
|
return True
|
|
|
|
if m.kind == "method" and m.needsJSTypes():
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2011-08-05 22:34:13 +04:00
|
|
|
def countEntries(self):
|
|
|
|
''' Returns the number of entries in the vtable for this interface. '''
|
|
|
|
total = sum(member.count() for member in self.members)
|
|
|
|
if self.base is not None:
|
2018-07-11 04:15:16 +03:00
|
|
|
realbase = self.idl.getName(TypeId(self.base), self.location)
|
2011-08-05 22:34:13 +04:00
|
|
|
total += realbase.countEntries()
|
|
|
|
return total
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class InterfaceAttributes(object):
|
|
|
|
uuid = None
|
|
|
|
scriptable = False
|
2011-06-16 23:21:25 +04:00
|
|
|
builtinclass = False
|
2008-08-20 06:38:24 +04:00
|
|
|
function = False
|
2008-10-28 12:04:41 +03:00
|
|
|
noscript = False
|
2015-03-06 19:34:08 +03:00
|
|
|
main_process_scriptable_only = False
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def setuuid(self, value):
|
|
|
|
self.uuid = value.lower()
|
|
|
|
|
|
|
|
def setscriptable(self):
|
|
|
|
self.scriptable = True
|
|
|
|
|
|
|
|
def setfunction(self):
|
|
|
|
self.function = True
|
|
|
|
|
|
|
|
def setnoscript(self):
|
|
|
|
self.noscript = True
|
|
|
|
|
2011-06-16 23:21:25 +04:00
|
|
|
def setbuiltinclass(self):
|
|
|
|
self.builtinclass = True
|
|
|
|
|
2015-03-06 19:34:08 +03:00
|
|
|
def setmain_process_scriptable_only(self):
|
|
|
|
self.main_process_scriptable_only = True
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
actions = {
|
|
|
|
'uuid': (True, setuuid),
|
|
|
|
'scriptable': (False, setscriptable),
|
2011-06-16 23:21:25 +04:00
|
|
|
'builtinclass': (False, setbuiltinclass),
|
2008-08-20 06:38:24 +04:00
|
|
|
'function': (False, setfunction),
|
|
|
|
'noscript': (False, setnoscript),
|
|
|
|
'object': (False, lambda self: True),
|
2015-03-06 19:34:08 +03:00
|
|
|
'main_process_scriptable_only': (False, setmain_process_scriptable_only),
|
2018-05-26 07:29:52 +03:00
|
|
|
}
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def __init__(self, attlist, location):
|
|
|
|
def badattribute(self):
|
|
|
|
raise IDLError("Unexpected interface attribute '%s'" % name, location)
|
|
|
|
|
|
|
|
for name, val, aloc in attlist:
|
|
|
|
hasval, action = self.actions.get(name, (False, badattribute))
|
|
|
|
if hasval:
|
|
|
|
if val is None:
|
|
|
|
raise IDLError("Expected value for attribute '%s'" % name,
|
|
|
|
aloc)
|
|
|
|
|
|
|
|
action(self, val)
|
|
|
|
else:
|
|
|
|
if val is not None:
|
|
|
|
raise IDLError("Unexpected value for attribute '%s'" % name,
|
|
|
|
aloc)
|
|
|
|
|
|
|
|
action(self)
|
|
|
|
|
2014-03-07 15:51:51 +04:00
|
|
|
if self.uuid is None:
|
|
|
|
raise IDLError("interface has no uuid", location)
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def __str__(self):
|
|
|
|
l = []
|
|
|
|
if self.uuid:
|
|
|
|
l.append("\tuuid: %s\n" % self.uuid)
|
|
|
|
if self.scriptable:
|
|
|
|
l.append("\tscriptable\n")
|
2011-06-16 23:21:25 +04:00
|
|
|
if self.builtinclass:
|
|
|
|
l.append("\tbuiltinclass\n")
|
2008-08-20 06:38:24 +04:00
|
|
|
if self.function:
|
|
|
|
l.append("\tfunction\n")
|
2015-03-06 19:34:08 +03:00
|
|
|
if self.main_process_scriptable_only:
|
|
|
|
l.append("\tmain_process_scriptable_only\n")
|
2008-08-20 06:38:24 +04:00
|
|
|
return "".join(l)
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class ConstMember(object):
|
|
|
|
kind = 'const'
|
2015-05-20 22:56:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def __init__(self, type, name, value, location, doccomments):
|
|
|
|
self.type = type
|
|
|
|
self.name = name
|
|
|
|
self.value = value
|
|
|
|
self.location = location
|
|
|
|
self.doccomments = doccomments
|
|
|
|
|
|
|
|
def resolve(self, parent):
|
|
|
|
self.realtype = parent.idl.getName(self.type, self.location)
|
|
|
|
self.iface = parent
|
|
|
|
basetype = self.realtype
|
|
|
|
while isinstance(basetype, Typedef):
|
|
|
|
basetype = basetype.realtype
|
|
|
|
if not isinstance(basetype, Builtin) or not basetype.maybeConst:
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError("const may only be a short or long type, not %s" %
|
|
|
|
self.type, self.location)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
self.basetype = basetype
|
|
|
|
|
|
|
|
def getValue(self):
|
|
|
|
return self.value(self.iface)
|
|
|
|
|
2008-10-28 12:04:41 +03:00
|
|
|
def __str__(self):
|
|
|
|
return "\tconst %s %s = %s\n" % (self.type, self.name, self.getValue())
|
|
|
|
|
2011-08-05 22:34:13 +04:00
|
|
|
def count(self):
|
|
|
|
return 0
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2018-11-06 03:05:37 +03:00
|
|
|
# Represents a single name/value pair in a CEnum
|
|
|
|
class CEnumVariant(object):
|
|
|
|
# Treat CEnumVariants as consts in terms of value resolution, so we can
|
|
|
|
# do things like binary operation values for enum members.
|
|
|
|
kind = 'const'
|
|
|
|
|
|
|
|
def __init__(self, name, value, location):
|
|
|
|
self.name = name
|
|
|
|
self.value = value
|
|
|
|
self.location = location
|
|
|
|
|
|
|
|
def getValue(self):
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
|
|
|
|
class CEnum(object):
|
|
|
|
kind = 'cenum'
|
|
|
|
|
|
|
|
def __init__(self, width, name, variants, location, doccomments):
|
|
|
|
# We have to set a name here, otherwise we won't pass namemap checks on
|
|
|
|
# the interface. This name will change it in resolve(), in order to
|
|
|
|
# namespace the enum within the interface.
|
|
|
|
self.name = name
|
|
|
|
self.basename = name
|
|
|
|
self.width = width
|
|
|
|
self.location = location
|
|
|
|
self.namemap = NameMap()
|
|
|
|
self.doccomments = doccomments
|
|
|
|
self.variants = variants
|
|
|
|
if self.width not in (8, 16, 32):
|
|
|
|
raise IDLError("Width must be one of {8, 16, 32}", self.location)
|
|
|
|
|
|
|
|
def getValue(self):
|
|
|
|
return self.value(self.iface)
|
|
|
|
|
|
|
|
def resolve(self, iface):
|
|
|
|
self.iface = iface
|
|
|
|
# Renaming enum to faux-namespace the enum type to the interface in JS
|
|
|
|
# so we don't collide in the global namespace. Hacky/ugly but it does
|
|
|
|
# the job well enough, and the name will still be interface::variant in
|
|
|
|
# C++.
|
|
|
|
self.name = '%s_%s' % (self.iface.name, self.basename)
|
|
|
|
self.iface.idl.setName(self)
|
|
|
|
|
|
|
|
# Compute the value for each enum variant that doesn't set its own
|
|
|
|
# value
|
|
|
|
next_value = 0
|
|
|
|
for variant in self.variants:
|
|
|
|
# CEnum variants resolve to interface level consts in javascript,
|
|
|
|
# meaning their names could collide with other interface members.
|
|
|
|
# Iterate through all CEnum variants to make sure there are no
|
|
|
|
# collisions.
|
|
|
|
self.iface.namemap.set(variant)
|
|
|
|
# Value may be a lambda. If it is, resolve it.
|
|
|
|
if variant.value:
|
|
|
|
next_value = variant.value = variant.value(self.iface)
|
|
|
|
else:
|
|
|
|
variant.value = next_value
|
|
|
|
next_value += 1
|
|
|
|
|
|
|
|
def count(self):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def nativeType(self, calltype):
|
|
|
|
if 'out' in calltype:
|
|
|
|
return "%s::%s *" % (self.iface.name, self.basename)
|
|
|
|
return "%s::%s " % (self.iface.name, self.basename)
|
|
|
|
|
|
|
|
def rustType(self, calltype):
|
2020-01-07 22:35:02 +03:00
|
|
|
return "%s u%d" % ('*mut' if 'out' in calltype else '', self.width)
|
2018-11-06 03:05:37 +03:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
body = ', '.join('%s = %s' % v for v in self.variants)
|
|
|
|
return "\tcenum %s : %d { %s };\n" % (self.name, self.width, body)
|
|
|
|
|
|
|
|
|
2019-05-14 20:39:14 +03:00
|
|
|
# An interface cannot be implemented by JS if it has a notxpcom
|
|
|
|
# method or attribute, so it must be marked as builtinclass.
|
|
|
|
#
|
|
|
|
# XXX(nika): Why does nostdcall not imply builtinclass?
|
|
|
|
# It could screw up the shims as well...
|
|
|
|
def ensureBuiltinClassIfNeeded(methodOrAttribute):
|
|
|
|
iface = methodOrAttribute.iface
|
|
|
|
if not iface.attributes.scriptable or iface.attributes.builtinclass:
|
|
|
|
return
|
|
|
|
if iface.name == 'nsISupports':
|
|
|
|
return
|
|
|
|
if methodOrAttribute.notxpcom:
|
|
|
|
raise IDLError(
|
|
|
|
("scriptable interface '%s' must be marked [builtinclass] because it "
|
|
|
|
"contains a [notxpcom] %s '%s'") %
|
|
|
|
(iface.name, methodOrAttribute.kind, methodOrAttribute.name),
|
|
|
|
methodOrAttribute.location)
|
|
|
|
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Attribute(object):
|
|
|
|
kind = 'attribute'
|
|
|
|
noscript = False
|
2018-11-15 08:07:20 +03:00
|
|
|
notxpcom = False
|
2008-10-28 12:04:41 +03:00
|
|
|
readonly = False
|
2018-08-22 00:08:35 +03:00
|
|
|
symbol = False
|
2010-06-29 02:22:39 +04:00
|
|
|
implicit_jscontext = False
|
2011-06-24 06:17:58 +04:00
|
|
|
nostdcall = False
|
2016-08-17 04:08:14 +03:00
|
|
|
must_use = False
|
2008-10-28 12:04:41 +03:00
|
|
|
binaryname = None
|
2012-08-23 05:27:04 +04:00
|
|
|
infallible = False
|
2019-12-02 21:50:15 +03:00
|
|
|
# explicit_setter_can_run_script is true if the attribute is explicitly
|
|
|
|
# annotated as having a setter that can cause script to run.
|
|
|
|
explicit_setter_can_run_script = False
|
|
|
|
# explicit_getter_can_run_script is true if the attribute is explicitly
|
|
|
|
# annotated as having a getter that can cause script to run.
|
|
|
|
explicit_getter_can_run_script = False
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2014-03-07 15:51:51 +04:00
|
|
|
def __init__(self, type, name, attlist, readonly, location, doccomments):
|
2008-08-20 06:38:24 +04:00
|
|
|
self.type = type
|
|
|
|
self.name = name
|
|
|
|
self.attlist = attlist
|
|
|
|
self.readonly = readonly
|
|
|
|
self.location = location
|
|
|
|
self.doccomments = doccomments
|
|
|
|
|
|
|
|
for name, value, aloc in attlist:
|
|
|
|
if name == 'binaryname':
|
|
|
|
if value is None:
|
|
|
|
raise IDLError("binaryname attribute requires a value",
|
|
|
|
aloc)
|
|
|
|
|
|
|
|
self.binaryname = value
|
|
|
|
continue
|
|
|
|
|
2018-09-06 06:42:05 +03:00
|
|
|
if value is not None:
|
|
|
|
raise IDLError("Unexpected attribute value", aloc)
|
|
|
|
|
|
|
|
if name == 'noscript':
|
|
|
|
self.noscript = True
|
2018-11-15 08:07:20 +03:00
|
|
|
elif name == 'notxpcom':
|
|
|
|
self.notxpcom = True
|
2018-09-06 06:42:05 +03:00
|
|
|
elif name == 'symbol':
|
|
|
|
self.symbol = True
|
|
|
|
elif name == 'implicit_jscontext':
|
|
|
|
self.implicit_jscontext = True
|
|
|
|
elif name == 'nostdcall':
|
|
|
|
self.nostdcall = True
|
|
|
|
elif name == 'must_use':
|
|
|
|
self.must_use = True
|
|
|
|
elif name == 'infallible':
|
|
|
|
self.infallible = True
|
2019-03-11 18:16:57 +03:00
|
|
|
elif name == 'can_run_script':
|
2019-12-02 22:52:11 +03:00
|
|
|
if (self.explicit_setter_can_run_script or
|
|
|
|
self.explicit_getter_can_run_script):
|
|
|
|
raise IDLError("Redundant getter_can_run_script or "
|
|
|
|
"setter_can_run_script annotation on "
|
|
|
|
"attribute", aloc)
|
|
|
|
self.explicit_setter_can_run_script = True
|
|
|
|
self.explicit_getter_can_run_script = True
|
2019-12-02 21:50:15 +03:00
|
|
|
elif name == 'setter_can_run_script':
|
2019-12-02 22:52:11 +03:00
|
|
|
if self.explicit_setter_can_run_script:
|
|
|
|
raise IDLError("Redundant setter_can_run_script annotation "
|
|
|
|
"on attribute", aloc)
|
2019-12-02 21:50:15 +03:00
|
|
|
self.explicit_setter_can_run_script = True
|
|
|
|
elif name == 'getter_can_run_script':
|
2019-12-02 22:52:11 +03:00
|
|
|
if self.explicit_getter_can_run_script:
|
|
|
|
raise IDLError("Redundant getter_can_run_script annotation "
|
|
|
|
"on attribute", aloc)
|
2019-12-02 21:50:15 +03:00
|
|
|
self.explicit_getter_can_run_script = True
|
2008-08-20 06:38:24 +04:00
|
|
|
else:
|
2018-09-06 06:42:05 +03:00
|
|
|
raise IDLError("Unexpected attribute '%s'" % name, aloc)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def resolve(self, iface):
|
|
|
|
self.iface = iface
|
|
|
|
self.realtype = iface.idl.getName(self.type, self.location)
|
2018-05-26 08:17:28 +03:00
|
|
|
if self.infallible and self.realtype.kind not in ['builtin',
|
|
|
|
'interface',
|
|
|
|
'forward',
|
2018-11-08 23:46:27 +03:00
|
|
|
'webidl',
|
|
|
|
'cenum']:
|
2018-04-17 09:13:44 +03:00
|
|
|
raise IDLError('[infallible] only works on interfaces, domobjects, and builtin types '
|
2018-11-08 23:46:27 +03:00
|
|
|
'(numbers, booleans, cenum, and raw char types)',
|
2012-08-23 05:27:04 +04:00
|
|
|
self.location)
|
|
|
|
if self.infallible and not iface.attributes.builtinclass:
|
|
|
|
raise IDLError('[infallible] attributes are only allowed on '
|
|
|
|
'[builtinclass] interfaces',
|
|
|
|
self.location)
|
|
|
|
|
2019-05-14 20:39:14 +03:00
|
|
|
ensureBuiltinClassIfNeeded(self)
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def toIDL(self):
|
|
|
|
attribs = attlistToIDL(self.attlist)
|
|
|
|
readonly = self.readonly and 'readonly ' or ''
|
|
|
|
return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name)
|
2012-08-09 12:31:14 +04:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def isScriptable(self):
|
2015-05-05 18:24:00 +03:00
|
|
|
if not self.iface.attributes.scriptable:
|
|
|
|
return False
|
2018-11-15 08:07:20 +03:00
|
|
|
return not (self.noscript or self.notxpcom)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
|
|
|
|
self.type, self.name)
|
|
|
|
|
2011-08-05 22:34:13 +04:00
|
|
|
def count(self):
|
|
|
|
return self.readonly and 1 or 2
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Method(object):
|
|
|
|
kind = 'method'
|
|
|
|
noscript = False
|
|
|
|
notxpcom = False
|
2018-08-22 00:08:35 +03:00
|
|
|
symbol = False
|
2008-08-20 06:38:24 +04:00
|
|
|
binaryname = None
|
2010-06-29 02:22:39 +04:00
|
|
|
implicit_jscontext = False
|
2011-06-24 06:17:58 +04:00
|
|
|
nostdcall = False
|
2016-08-17 04:08:14 +03:00
|
|
|
must_use = False
|
2009-08-10 18:41:07 +04:00
|
|
|
optional_argc = False
|
2019-03-11 18:16:57 +03:00
|
|
|
# explicit_can_run_script is true if the method is explicitly annotated
|
|
|
|
# as being able to cause script to run.
|
|
|
|
explicit_can_run_script = False
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
|
|
|
|
self.type = type
|
|
|
|
self.name = name
|
|
|
|
self.attlist = attlist
|
|
|
|
self.params = paramlist
|
|
|
|
self.location = location
|
|
|
|
self.doccomments = doccomments
|
|
|
|
self.raises = raises
|
|
|
|
|
|
|
|
for name, value, aloc in attlist:
|
|
|
|
if name == 'binaryname':
|
|
|
|
if value is None:
|
|
|
|
raise IDLError("binaryname attribute requires a value",
|
|
|
|
aloc)
|
|
|
|
|
|
|
|
self.binaryname = value
|
|
|
|
continue
|
|
|
|
|
|
|
|
if value is not None:
|
|
|
|
raise IDLError("Unexpected attribute value", aloc)
|
|
|
|
|
|
|
|
if name == 'noscript':
|
|
|
|
self.noscript = True
|
|
|
|
elif name == 'notxpcom':
|
|
|
|
self.notxpcom = True
|
2018-08-22 00:08:35 +03:00
|
|
|
elif name == 'symbol':
|
|
|
|
self.symbol = True
|
2010-06-29 02:22:39 +04:00
|
|
|
elif name == 'implicit_jscontext':
|
|
|
|
self.implicit_jscontext = True
|
2009-08-10 18:41:07 +04:00
|
|
|
elif name == 'optional_argc':
|
|
|
|
self.optional_argc = True
|
2011-06-24 06:17:58 +04:00
|
|
|
elif name == 'nostdcall':
|
|
|
|
self.nostdcall = True
|
2016-08-17 04:08:14 +03:00
|
|
|
elif name == 'must_use':
|
|
|
|
self.must_use = True
|
2019-03-11 18:16:57 +03:00
|
|
|
elif name == 'can_run_script':
|
|
|
|
self.explicit_can_run_script = True
|
2008-08-20 06:38:24 +04:00
|
|
|
else:
|
2011-06-24 06:17:58 +04:00
|
|
|
raise IDLError("Unexpected attribute '%s'" % name, aloc)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
self.namemap = NameMap()
|
|
|
|
for p in paramlist:
|
|
|
|
self.namemap.set(p)
|
|
|
|
|
|
|
|
def resolve(self, iface):
|
|
|
|
self.iface = iface
|
|
|
|
self.realtype = self.iface.idl.getName(self.type, self.location)
|
2019-05-14 20:39:14 +03:00
|
|
|
|
|
|
|
ensureBuiltinClassIfNeeded(self)
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
for p in self.params:
|
|
|
|
p.resolve(self)
|
2012-03-10 02:06:13 +04:00
|
|
|
for p in self.params:
|
2012-03-14 00:50:45 +04:00
|
|
|
if p.retval and p != self.params[-1]:
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError("'retval' parameter '%s' is not the last parameter" %
|
|
|
|
p.name, self.location)
|
2012-03-10 02:06:13 +04:00
|
|
|
if p.size_is:
|
|
|
|
found_size_param = False
|
|
|
|
for size_param in self.params:
|
|
|
|
if p.size_is == size_param.name:
|
|
|
|
found_size_param = True
|
|
|
|
if getBuiltinOrNativeTypeName(size_param.realtype) != 'unsigned long':
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError(
|
|
|
|
"is_size parameter must have type 'unsigned long'", self.location)
|
2012-03-10 02:06:13 +04:00
|
|
|
if not found_size_param:
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError("could not find is_size parameter '%s'" %
|
|
|
|
p.size_is, self.location)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def isScriptable(self):
|
2015-05-05 18:24:00 +03:00
|
|
|
if not self.iface.attributes.scriptable:
|
|
|
|
return False
|
2017-01-31 01:17:34 +03:00
|
|
|
return not (self.noscript or self.notxpcom)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params]))
|
|
|
|
|
|
|
|
def toIDL(self):
|
|
|
|
if len(self.raises):
|
|
|
|
raises = ' raises (%s)' % ','.join(self.raises)
|
|
|
|
else:
|
|
|
|
raises = ''
|
|
|
|
|
|
|
|
return "%s%s %s (%s)%s;" % (attlistToIDL(self.attlist),
|
|
|
|
self.type,
|
|
|
|
self.name,
|
|
|
|
", ".join([p.toIDL()
|
|
|
|
for p in self.params]),
|
|
|
|
raises)
|
|
|
|
|
2011-08-08 19:14:34 +04:00
|
|
|
def needsJSTypes(self):
|
|
|
|
if self.implicit_jscontext:
|
|
|
|
return True
|
2018-07-11 04:15:16 +03:00
|
|
|
if self.type == TypeId("jsval"):
|
2011-08-14 21:12:21 +04:00
|
|
|
return True
|
2011-08-08 19:14:34 +04:00
|
|
|
for p in self.params:
|
|
|
|
t = p.realtype
|
|
|
|
if isinstance(t, Native) and t.specialtype == "jsval":
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2011-08-05 22:34:13 +04:00
|
|
|
def count(self):
|
|
|
|
return 1
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class Param(object):
|
|
|
|
size_is = None
|
|
|
|
iid_is = None
|
|
|
|
const = False
|
|
|
|
array = False
|
|
|
|
retval = False
|
|
|
|
shared = False
|
|
|
|
optional = False
|
2017-05-20 06:58:07 +03:00
|
|
|
default_value = None
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def __init__(self, paramtype, type, name, attlist, location, realtype=None):
|
|
|
|
self.paramtype = paramtype
|
|
|
|
self.type = type
|
|
|
|
self.name = name
|
|
|
|
self.attlist = attlist
|
|
|
|
self.location = location
|
|
|
|
self.realtype = realtype
|
|
|
|
|
|
|
|
for name, value, aloc in attlist:
|
|
|
|
# Put the value-taking attributes first!
|
|
|
|
if name == 'size_is':
|
|
|
|
if value is None:
|
|
|
|
raise IDLError("'size_is' must specify a parameter", aloc)
|
|
|
|
self.size_is = value
|
|
|
|
elif name == 'iid_is':
|
|
|
|
if value is None:
|
|
|
|
raise IDLError("'iid_is' must specify a parameter", aloc)
|
|
|
|
self.iid_is = value
|
2017-05-20 06:58:07 +03:00
|
|
|
elif name == 'default':
|
|
|
|
if value is None:
|
|
|
|
raise IDLError("'default' must specify a default value", aloc)
|
|
|
|
self.default_value = value
|
2008-08-20 06:38:24 +04:00
|
|
|
else:
|
|
|
|
if value is not None:
|
|
|
|
raise IDLError("Unexpected value for attribute '%s'" % name,
|
|
|
|
aloc)
|
|
|
|
|
|
|
|
if name == 'const':
|
|
|
|
self.const = True
|
|
|
|
elif name == 'array':
|
|
|
|
self.array = True
|
|
|
|
elif name == 'retval':
|
|
|
|
self.retval = True
|
|
|
|
elif name == 'shared':
|
|
|
|
self.shared = True
|
|
|
|
elif name == 'optional':
|
|
|
|
self.optional = True
|
|
|
|
else:
|
|
|
|
raise IDLError("Unexpected attribute '%s'" % name, aloc)
|
|
|
|
|
|
|
|
def resolve(self, method):
|
|
|
|
self.realtype = method.iface.idl.getName(self.type, self.location)
|
|
|
|
if self.array:
|
2018-07-24 22:16:13 +03:00
|
|
|
self.realtype = LegacyArray(self.realtype)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def nativeType(self):
|
|
|
|
kwargs = {}
|
2015-05-05 18:24:00 +03:00
|
|
|
if self.shared:
|
|
|
|
kwargs['shared'] = True
|
|
|
|
if self.const:
|
|
|
|
kwargs['const'] = True
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
try:
|
|
|
|
return self.realtype.nativeType(self.paramtype, **kwargs)
|
2018-05-26 08:17:28 +03:00
|
|
|
except IDLError as e:
|
2008-08-20 06:38:24 +04:00
|
|
|
raise IDLError(e.message, self.location)
|
2019-09-24 17:44:01 +03:00
|
|
|
except TypeError:
|
2008-08-20 06:38:24 +04:00
|
|
|
raise IDLError("Unexpected parameter attribute", self.location)
|
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def rustType(self):
|
|
|
|
kwargs = {}
|
|
|
|
if self.shared:
|
|
|
|
kwargs['shared'] = True
|
|
|
|
if self.const:
|
|
|
|
kwargs['const'] = True
|
|
|
|
|
|
|
|
try:
|
|
|
|
return self.realtype.rustType(self.paramtype, **kwargs)
|
2018-05-26 08:17:28 +03:00
|
|
|
except IDLError as e:
|
2018-01-05 01:32:48 +03:00
|
|
|
raise IDLError(e.message, self.location)
|
2019-09-24 17:44:01 +03:00
|
|
|
except TypeError:
|
2018-01-05 01:32:48 +03:00
|
|
|
raise IDLError("Unexpected parameter attribute", self.location)
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def toIDL(self):
|
|
|
|
return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
|
|
|
|
self.paramtype,
|
|
|
|
self.type,
|
|
|
|
self.name)
|
|
|
|
|
2015-05-04 23:35:00 +03:00
|
|
|
|
2018-07-24 22:16:13 +03:00
|
|
|
class LegacyArray(object):
|
2008-08-20 06:38:24 +04:00
|
|
|
def __init__(self, basetype):
|
|
|
|
self.type = basetype
|
2018-07-24 22:08:04 +03:00
|
|
|
self.location = self.type.location
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def nativeType(self, calltype, const=False):
|
2018-07-24 22:08:04 +03:00
|
|
|
if 'element' in calltype:
|
|
|
|
raise IDLError("nested [array] unsupported", self.location)
|
|
|
|
|
2018-07-06 23:47:22 +03:00
|
|
|
# For legacy reasons, we have to add a 'const ' to builtin pointer array
|
|
|
|
# types. (`[array] in string` and `[array] in wstring` parameters)
|
|
|
|
if calltype == 'in' and isinstance(self.type, Builtin) and self.type.isPointer():
|
|
|
|
const = True
|
|
|
|
|
|
|
|
return "%s%s*%s" % ('const ' if const else '',
|
2018-07-24 22:08:04 +03:00
|
|
|
self.type.nativeType('legacyelement'),
|
2018-07-06 23:47:22 +03:00
|
|
|
'*' if 'out' in calltype else '')
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2018-01-05 01:32:48 +03:00
|
|
|
def rustType(self, calltype, const=False):
|
2018-07-06 23:47:22 +03:00
|
|
|
return "%s%s%s" % ('*mut ' if 'out' in calltype else '',
|
|
|
|
'*const ' if const else '*mut ',
|
2018-07-24 22:08:04 +03:00
|
|
|
self.type.rustType('legacyelement'))
|
2018-07-11 04:15:16 +03:00
|
|
|
|
|
|
|
|
2018-07-24 23:31:09 +03:00
|
|
|
class Array(object):
|
|
|
|
kind = 'array'
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
|
|
|
|
def __init__(self, type, location):
|
|
|
|
self.type = type
|
|
|
|
self.location = location
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
2018-07-24 23:31:09 +03:00
|
|
|
return "Array<%s>" % self.type.name
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
|
|
|
|
def resolve(self, idl):
|
|
|
|
idl.getName(self.type, self.location)
|
|
|
|
|
|
|
|
def nativeType(self, calltype):
|
2018-07-24 22:08:04 +03:00
|
|
|
if calltype == 'legacyelement':
|
2018-07-24 23:31:09 +03:00
|
|
|
raise IDLError("[array] Array<T> is unsupported", self.location)
|
2018-07-24 22:08:04 +03:00
|
|
|
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
base = 'nsTArray<%s>' % self.type.nativeType('element')
|
|
|
|
if 'out' in calltype:
|
|
|
|
return '%s& ' % base
|
|
|
|
elif 'in' == calltype:
|
|
|
|
return 'const %s& ' % base
|
|
|
|
else:
|
|
|
|
return base
|
|
|
|
|
|
|
|
def rustType(self, calltype):
|
2019-02-26 23:51:19 +03:00
|
|
|
if calltype == 'legacyelement':
|
|
|
|
raise IDLError("[array] Array<T> is unsupported", self.location)
|
|
|
|
|
|
|
|
base = 'thin_vec::ThinVec<%s>' % self.type.rustType('element')
|
|
|
|
if 'out' in calltype:
|
|
|
|
return '*mut %s' % base
|
|
|
|
elif 'in' == calltype:
|
|
|
|
return '*const %s' % base
|
|
|
|
else:
|
|
|
|
return base
|
Bug 1474369 - Part 4: Add support for Sequence<T> types to xpidl and XPConnect, r=mccr8
Summary:
This patch adds support for the `Sequence<T>` type. This is largely a
straightforward type propagation patch, but there are a few notable things:
1. We allow `[iid_is(x)] Sequence<nsQIResult>`, so Sequence can be Dependent.
2. `Sequence<T>` is reflected into C++ as a `nsTArray<T>`, which is different
than WebIDL's `mozilla::dom::Sequence<T>` type. This decision was made for
general ergonomics reasons, as `nsTArray<T>` is more prevailent throughout
the codebase, and lengths in this case cannot be controlled by content, as
XPConnect is only exposed to Chrome JS.
3. Owned pointers in `Sequence<T>` are not reflected as their owned
counterparts. For example, `Sequence<nsISupports>` is reflected as
`nsTArray<nsISupports*>` rather than `nsTArray<RefPtr<nsISupports>>`. This
was done to avoid depending on `RefPtr<T>` and `T*` having the same
in-memory representation, however if that is considered an acceptable
dependency, it would be nice to support that.
4. We also don't reflect singly-owned pointers as their owned counterparts. For
example, `nsTArray<nsIIDPtr>` would be reflected as `nsTArray<nsIID*>`
rather than `nsTArray<mozilla::UniquePtr<nsIID>>`. If we are willing to
depend on `mozilla::UniquePtr<T>`'s in-memory representation, we could also
do this, however.
5. There are no restrictions on what types can appear inside of a `Sequence<T>`
or what can appear inside an `[array] T`. We may want to add restrictions
either at the xpidl level or in XPConnect.
Depends On D2109
Reviewers: mccr8!
Tags: #secure-revision
Bug #: 1474369
Differential Revision: https://phabricator.services.mozilla.com/D2110
2018-07-11 04:24:48 +03:00
|
|
|
|
|
|
|
|
2018-07-11 04:15:16 +03:00
|
|
|
TypeId = namedtuple('TypeId', 'name params')
|
|
|
|
|
|
|
|
|
|
|
|
# Make str(TypeId) produce a nicer value
|
|
|
|
TypeId.__str__ = lambda self: \
|
|
|
|
"%s<%s>" % (self.name, ', '.join(str(p) for p in self.params)) \
|
|
|
|
if self.params is not None \
|
|
|
|
else self.name
|
|
|
|
|
|
|
|
|
|
|
|
# Allow skipping 'params' in TypeId(..)
|
|
|
|
TypeId.__new__.__defaults__ = (None,)
|
|
|
|
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
class IDLParser(object):
|
|
|
|
keywords = {
|
2018-11-06 03:05:37 +03:00
|
|
|
'cenum': 'CENUM',
|
2008-08-20 06:38:24 +04:00
|
|
|
'const': 'CONST',
|
|
|
|
'interface': 'INTERFACE',
|
|
|
|
'in': 'IN',
|
|
|
|
'inout': 'INOUT',
|
|
|
|
'out': 'OUT',
|
|
|
|
'attribute': 'ATTRIBUTE',
|
|
|
|
'raises': 'RAISES',
|
|
|
|
'readonly': 'READONLY',
|
|
|
|
'native': 'NATIVE',
|
2013-01-08 21:36:05 +04:00
|
|
|
'typedef': 'TYPEDEF',
|
2018-04-07 01:22:14 +03:00
|
|
|
'webidl': 'WEBIDL',
|
2018-05-26 07:29:52 +03:00
|
|
|
}
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
tokens = [
|
|
|
|
'IDENTIFIER',
|
|
|
|
'CDATA',
|
|
|
|
'INCLUDE',
|
|
|
|
'IID',
|
|
|
|
'NUMBER',
|
|
|
|
'HEXNUM',
|
|
|
|
'LSHIFT',
|
|
|
|
'RSHIFT',
|
|
|
|
'NATIVEID',
|
2018-05-26 07:29:52 +03:00
|
|
|
]
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
tokens.extend(keywords.values())
|
|
|
|
|
|
|
|
states = (
|
|
|
|
('nativeid', 'exclusive'),
|
|
|
|
)
|
|
|
|
|
|
|
|
hexchar = r'[a-fA-F0-9]'
|
|
|
|
|
|
|
|
t_NUMBER = r'-?\d+'
|
|
|
|
t_HEXNUM = r'0x%s+' % hexchar
|
|
|
|
t_LSHIFT = r'<<'
|
2015-05-16 13:18:00 +03:00
|
|
|
t_RSHIFT = r'>>'
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2018-07-11 04:15:16 +03:00
|
|
|
literals = '"(){}[]<>,;:=|+-*'
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
t_ignore = ' \t'
|
|
|
|
|
|
|
|
def t_multilinecomment(self, t):
|
|
|
|
r'/\*(?s).*?\*/'
|
|
|
|
t.lexer.lineno += t.value.count('\n')
|
|
|
|
if t.value.startswith("/**"):
|
|
|
|
self._doccomments.append(t.value)
|
|
|
|
|
|
|
|
def t_singlelinecomment(self, t):
|
|
|
|
r'(?m)//.*?$'
|
|
|
|
|
|
|
|
def t_IID(self, t):
|
|
|
|
return t
|
|
|
|
t_IID.__doc__ = r'%(c)s{8}-%(c)s{4}-%(c)s{4}-%(c)s{4}-%(c)s{12}' % {'c': hexchar}
|
|
|
|
|
|
|
|
def t_IDENTIFIER(self, t):
|
2018-05-26 08:17:28 +03:00
|
|
|
r'(unsigned\ long\ long|unsigned\ short|unsigned\ long|long\ long)(?!_?[A-Za-z][A-Za-z_0-9])|_?[A-Za-z][A-Za-z_0-9]*' # NOQA: E501
|
2008-08-20 06:38:24 +04:00
|
|
|
t.type = self.keywords.get(t.value, 'IDENTIFIER')
|
|
|
|
return t
|
|
|
|
|
|
|
|
def t_LCDATA(self, t):
|
|
|
|
r'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
|
|
|
|
t.type = 'CDATA'
|
|
|
|
t.value = t.lexer.lexmatch.group('cdata')
|
|
|
|
t.lexer.lineno += t.value.count('\n')
|
|
|
|
return t
|
|
|
|
|
|
|
|
def t_INCLUDE(self, t):
|
|
|
|
r'\#include[ \t]+"[^"\n]+"'
|
|
|
|
inc, value, end = t.value.split('"')
|
|
|
|
t.value = value
|
|
|
|
return t
|
|
|
|
|
|
|
|
def t_directive(self, t):
|
|
|
|
r'\#(?P<directive>[a-zA-Z]+)[^\n]+'
|
2011-08-01 22:26:13 +04:00
|
|
|
raise IDLError("Unrecognized directive %s" % t.lexer.lexmatch.group('directive'),
|
|
|
|
Location(lexer=self.lexer, lineno=self.lexer.lineno,
|
|
|
|
lexpos=self.lexer.lexpos))
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def t_newline(self, t):
|
|
|
|
r'\n+'
|
|
|
|
t.lexer.lineno += len(t.value)
|
|
|
|
|
|
|
|
def t_nativeid_NATIVEID(self, t):
|
|
|
|
r'[^()\n]+(?=\))'
|
|
|
|
t.lexer.begin('INITIAL')
|
|
|
|
return t
|
|
|
|
|
|
|
|
t_nativeid_ignore = ''
|
|
|
|
|
|
|
|
def t_ANY_error(self, t):
|
|
|
|
raise IDLError("unrecognized input",
|
|
|
|
Location(lexer=self.lexer,
|
|
|
|
lineno=self.lexer.lineno,
|
|
|
|
lexpos=self.lexer.lexpos))
|
|
|
|
|
|
|
|
precedence = (
|
|
|
|
('left', '|'),
|
|
|
|
('left', 'LSHIFT', 'RSHIFT'),
|
|
|
|
('left', '+', '-'),
|
|
|
|
('left', '*'),
|
|
|
|
('left', 'UMINUS'),
|
|
|
|
)
|
|
|
|
|
|
|
|
def p_idlfile(self, p):
|
|
|
|
"""idlfile : productions"""
|
|
|
|
p[0] = IDL(p[1])
|
|
|
|
|
|
|
|
def p_productions_start(self, p):
|
|
|
|
"""productions : """
|
|
|
|
p[0] = []
|
|
|
|
|
|
|
|
def p_productions_cdata(self, p):
|
|
|
|
"""productions : CDATA productions"""
|
|
|
|
p[0] = list(p[2])
|
|
|
|
p[0].insert(0, CDATA(p[1], self.getLocation(p, 1)))
|
|
|
|
|
|
|
|
def p_productions_include(self, p):
|
|
|
|
"""productions : INCLUDE productions"""
|
|
|
|
p[0] = list(p[2])
|
|
|
|
p[0].insert(0, Include(p[1], self.getLocation(p, 1)))
|
|
|
|
|
|
|
|
def p_productions_interface(self, p):
|
|
|
|
"""productions : interface productions
|
|
|
|
| typedef productions
|
2018-04-07 01:22:14 +03:00
|
|
|
| native productions
|
|
|
|
| webidl productions"""
|
2008-08-20 06:38:24 +04:00
|
|
|
p[0] = list(p[2])
|
|
|
|
p[0].insert(0, p[1])
|
|
|
|
|
|
|
|
def p_typedef(self, p):
|
2018-07-11 04:15:16 +03:00
|
|
|
"""typedef : TYPEDEF type IDENTIFIER ';'"""
|
2008-08-20 06:38:24 +04:00
|
|
|
p[0] = Typedef(type=p[2],
|
|
|
|
name=p[3],
|
|
|
|
location=self.getLocation(p, 1),
|
|
|
|
doccomments=p.slice[1].doccomments)
|
|
|
|
|
|
|
|
def p_native(self, p):
|
|
|
|
"""native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
|
|
|
|
p[0] = Native(name=p[3],
|
|
|
|
nativename=p[6],
|
|
|
|
attlist=p[1]['attlist'],
|
|
|
|
location=self.getLocation(p, 2))
|
|
|
|
|
|
|
|
def p_afternativeid(self, p):
|
|
|
|
"""afternativeid : """
|
|
|
|
# this is a place marker: we switch the lexer into literal identifier
|
|
|
|
# mode here, to slurp up everything until the closeparen
|
|
|
|
self.lexer.begin('nativeid')
|
|
|
|
|
2018-04-07 01:22:14 +03:00
|
|
|
def p_webidl(self, p):
|
|
|
|
"""webidl : WEBIDL IDENTIFIER ';'"""
|
|
|
|
p[0] = WebIDL(name=p[2], location=self.getLocation(p, 2))
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def p_anyident(self, p):
|
|
|
|
"""anyident : IDENTIFIER
|
|
|
|
| CONST"""
|
|
|
|
p[0] = {'value': p[1],
|
|
|
|
'location': self.getLocation(p, 1)}
|
|
|
|
|
|
|
|
def p_attributes(self, p):
|
|
|
|
"""attributes : '[' attlist ']'
|
|
|
|
| """
|
|
|
|
if len(p) == 1:
|
|
|
|
p[0] = {'attlist': []}
|
|
|
|
else:
|
|
|
|
p[0] = {'attlist': p[2],
|
|
|
|
'doccomments': p.slice[1].doccomments}
|
|
|
|
|
|
|
|
def p_attlist_start(self, p):
|
|
|
|
"""attlist : attribute"""
|
|
|
|
p[0] = [p[1]]
|
|
|
|
|
|
|
|
def p_attlist_continue(self, p):
|
|
|
|
"""attlist : attribute ',' attlist"""
|
|
|
|
p[0] = list(p[3])
|
|
|
|
p[0].insert(0, p[1])
|
|
|
|
|
|
|
|
def p_attribute(self, p):
|
|
|
|
"""attribute : anyident attributeval"""
|
|
|
|
p[0] = (p[1]['value'], p[2], p[1]['location'])
|
|
|
|
|
|
|
|
def p_attributeval(self, p):
|
|
|
|
"""attributeval : '(' IDENTIFIER ')'
|
|
|
|
| '(' IID ')'
|
|
|
|
| """
|
|
|
|
if len(p) > 1:
|
|
|
|
p[0] = p[2]
|
|
|
|
|
|
|
|
def p_interface(self, p):
|
|
|
|
"""interface : attributes INTERFACE IDENTIFIER ifacebase ifacebody ';'"""
|
|
|
|
atts, INTERFACE, name, base, body, SEMI = p[1:]
|
|
|
|
attlist = atts['attlist']
|
|
|
|
doccomments = []
|
|
|
|
if 'doccomments' in atts:
|
|
|
|
doccomments.extend(atts['doccomments'])
|
|
|
|
doccomments.extend(p.slice[2].doccomments)
|
|
|
|
|
2018-05-26 08:17:28 +03:00
|
|
|
def loc(): return self.getLocation(p, 2)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
if body is None:
|
|
|
|
# forward-declared interface... must not have attributes!
|
|
|
|
if len(attlist) != 0:
|
|
|
|
raise IDLError("Forward-declared interface must not have attributes",
|
|
|
|
list[0][3])
|
|
|
|
|
|
|
|
if base is not None:
|
|
|
|
raise IDLError("Forward-declared interface must not have a base",
|
2018-05-26 08:17:28 +03:00
|
|
|
loc())
|
|
|
|
p[0] = Forward(name=name, location=loc(), doccomments=doccomments)
|
2008-08-20 06:38:24 +04:00
|
|
|
else:
|
|
|
|
p[0] = Interface(name=name,
|
|
|
|
attlist=attlist,
|
|
|
|
base=base,
|
|
|
|
members=body,
|
2018-05-26 08:17:28 +03:00
|
|
|
location=loc(),
|
2008-08-20 06:38:24 +04:00
|
|
|
doccomments=doccomments)
|
|
|
|
|
|
|
|
def p_ifacebody(self, p):
|
|
|
|
"""ifacebody : '{' members '}'
|
|
|
|
| """
|
|
|
|
if len(p) > 1:
|
|
|
|
p[0] = p[2]
|
|
|
|
|
|
|
|
def p_ifacebase(self, p):
|
|
|
|
"""ifacebase : ':' IDENTIFIER
|
|
|
|
| """
|
|
|
|
if len(p) == 3:
|
|
|
|
p[0] = p[2]
|
|
|
|
|
|
|
|
def p_members_start(self, p):
|
|
|
|
"""members : """
|
|
|
|
p[0] = []
|
|
|
|
|
|
|
|
def p_members_continue(self, p):
|
|
|
|
"""members : member members"""
|
|
|
|
p[0] = list(p[2])
|
|
|
|
p[0].insert(0, p[1])
|
|
|
|
|
|
|
|
def p_member_cdata(self, p):
|
|
|
|
"""member : CDATA"""
|
|
|
|
p[0] = CDATA(p[1], self.getLocation(p, 1))
|
|
|
|
|
|
|
|
def p_member_const(self, p):
|
2018-07-11 04:15:16 +03:00
|
|
|
"""member : CONST type IDENTIFIER '=' number ';' """
|
2008-08-20 06:38:24 +04:00
|
|
|
p[0] = ConstMember(type=p[2], name=p[3],
|
|
|
|
value=p[5], location=self.getLocation(p, 1),
|
|
|
|
doccomments=p.slice[1].doccomments)
|
|
|
|
|
|
|
|
# All "number" products return a function(interface)
|
|
|
|
|
|
|
|
def p_number_decimal(self, p):
|
|
|
|
"""number : NUMBER"""
|
|
|
|
n = int(p[1])
|
|
|
|
p[0] = lambda i: n
|
|
|
|
|
|
|
|
def p_number_hex(self, p):
|
|
|
|
"""number : HEXNUM"""
|
|
|
|
n = int(p[1], 16)
|
|
|
|
p[0] = lambda i: n
|
|
|
|
|
|
|
|
def p_number_identifier(self, p):
|
|
|
|
"""number : IDENTIFIER"""
|
|
|
|
id = p[1]
|
|
|
|
loc = self.getLocation(p, 1)
|
|
|
|
p[0] = lambda i: i.getConst(id, loc)
|
|
|
|
|
|
|
|
def p_number_paren(self, p):
|
|
|
|
"""number : '(' number ')'"""
|
|
|
|
p[0] = p[2]
|
|
|
|
|
|
|
|
def p_number_neg(self, p):
|
|
|
|
"""number : '-' number %prec UMINUS"""
|
|
|
|
n = p[2]
|
|
|
|
p[0] = lambda i: - n(i)
|
|
|
|
|
|
|
|
def p_number_add(self, p):
|
|
|
|
"""number : number '+' number
|
|
|
|
| number '-' number
|
|
|
|
| number '*' number"""
|
|
|
|
n1 = p[1]
|
|
|
|
n2 = p[3]
|
|
|
|
if p[2] == '+':
|
|
|
|
p[0] = lambda i: n1(i) + n2(i)
|
|
|
|
elif p[2] == '-':
|
|
|
|
p[0] = lambda i: n1(i) - n2(i)
|
|
|
|
else:
|
|
|
|
p[0] = lambda i: n1(i) * n2(i)
|
|
|
|
|
|
|
|
def p_number_shift(self, p):
|
|
|
|
"""number : number LSHIFT number
|
|
|
|
| number RSHIFT number"""
|
|
|
|
n1 = p[1]
|
|
|
|
n2 = p[3]
|
|
|
|
if p[2] == '<<':
|
|
|
|
p[0] = lambda i: n1(i) << n2(i)
|
|
|
|
else:
|
|
|
|
p[0] = lambda i: n1(i) >> n2(i)
|
|
|
|
|
|
|
|
def p_number_bitor(self, p):
|
|
|
|
"""number : number '|' number"""
|
|
|
|
n1 = p[1]
|
|
|
|
n2 = p[3]
|
|
|
|
p[0] = lambda i: n1(i) | n2(i)
|
|
|
|
|
2018-11-06 03:05:37 +03:00
|
|
|
def p_member_cenum(self, p):
|
|
|
|
"""member : CENUM IDENTIFIER ':' NUMBER '{' variants '}' ';'"""
|
|
|
|
p[0] = CEnum(name=p[2],
|
|
|
|
width=int(p[4]),
|
|
|
|
variants=p[6],
|
|
|
|
location=self.getLocation(p, 1),
|
|
|
|
doccomments=p.slice[1].doccomments)
|
|
|
|
|
|
|
|
def p_variants_start(self, p):
|
|
|
|
"""variants : """
|
|
|
|
p[0] = []
|
|
|
|
|
|
|
|
def p_variants_single(self, p):
|
|
|
|
"""variants : variant"""
|
|
|
|
p[0] = [p[1]]
|
|
|
|
|
|
|
|
def p_variants_continue(self, p):
|
|
|
|
"""variants : variant ',' variants"""
|
|
|
|
p[0] = [p[1]] + p[3]
|
|
|
|
|
|
|
|
def p_variant_implicit(self, p):
|
|
|
|
"""variant : IDENTIFIER"""
|
|
|
|
p[0] = CEnumVariant(p[1], None, self.getLocation(p, 1))
|
|
|
|
|
|
|
|
def p_variant_explicit(self, p):
|
|
|
|
"""variant : IDENTIFIER '=' number"""
|
|
|
|
p[0] = CEnumVariant(p[1], p[3], self.getLocation(p, 1))
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def p_member_att(self, p):
|
2018-07-11 04:15:16 +03:00
|
|
|
"""member : attributes optreadonly ATTRIBUTE type IDENTIFIER ';'"""
|
2008-08-20 06:38:24 +04:00
|
|
|
if 'doccomments' in p[1]:
|
|
|
|
doccomments = p[1]['doccomments']
|
|
|
|
elif p[2] is not None:
|
|
|
|
doccomments = p[2]
|
|
|
|
else:
|
|
|
|
doccomments = p.slice[3].doccomments
|
|
|
|
|
|
|
|
p[0] = Attribute(type=p[4],
|
|
|
|
name=p[5],
|
|
|
|
attlist=p[1]['attlist'],
|
|
|
|
readonly=p[2] is not None,
|
2008-10-28 12:04:41 +03:00
|
|
|
location=self.getLocation(p, 3),
|
2008-08-20 06:38:24 +04:00
|
|
|
doccomments=doccomments)
|
|
|
|
|
|
|
|
def p_member_method(self, p):
|
2018-07-11 04:15:16 +03:00
|
|
|
"""member : attributes type IDENTIFIER '(' paramlist ')' raises ';'"""
|
2008-08-20 06:38:24 +04:00
|
|
|
if 'doccomments' in p[1]:
|
|
|
|
doccomments = p[1]['doccomments']
|
|
|
|
else:
|
|
|
|
doccomments = p.slice[2].doccomments
|
|
|
|
|
|
|
|
p[0] = Method(type=p[2],
|
|
|
|
name=p[3],
|
|
|
|
attlist=p[1]['attlist'],
|
|
|
|
paramlist=p[5],
|
2008-10-28 12:04:41 +03:00
|
|
|
location=self.getLocation(p, 3),
|
2008-08-20 06:38:24 +04:00
|
|
|
doccomments=doccomments,
|
|
|
|
raises=p[7])
|
|
|
|
|
|
|
|
def p_paramlist(self, p):
|
|
|
|
"""paramlist : param moreparams
|
|
|
|
| """
|
|
|
|
if len(p) == 1:
|
|
|
|
p[0] = []
|
|
|
|
else:
|
|
|
|
p[0] = list(p[2])
|
|
|
|
p[0].insert(0, p[1])
|
|
|
|
|
|
|
|
def p_moreparams_start(self, p):
|
|
|
|
"""moreparams :"""
|
|
|
|
p[0] = []
|
|
|
|
|
|
|
|
def p_moreparams_continue(self, p):
|
|
|
|
"""moreparams : ',' param moreparams"""
|
|
|
|
p[0] = list(p[3])
|
|
|
|
p[0].insert(0, p[2])
|
|
|
|
|
|
|
|
def p_param(self, p):
|
2018-07-11 04:15:16 +03:00
|
|
|
"""param : attributes paramtype type IDENTIFIER"""
|
2008-08-20 06:38:24 +04:00
|
|
|
p[0] = Param(paramtype=p[2],
|
|
|
|
type=p[3],
|
|
|
|
name=p[4],
|
|
|
|
attlist=p[1]['attlist'],
|
|
|
|
location=self.getLocation(p, 3))
|
|
|
|
|
|
|
|
def p_paramtype(self, p):
|
|
|
|
"""paramtype : IN
|
|
|
|
| INOUT
|
|
|
|
| OUT"""
|
|
|
|
p[0] = p[1]
|
|
|
|
|
|
|
|
def p_optreadonly(self, p):
|
|
|
|
"""optreadonly : READONLY
|
|
|
|
| """
|
|
|
|
if len(p) > 1:
|
|
|
|
p[0] = p.slice[1].doccomments
|
|
|
|
else:
|
|
|
|
p[0] = None
|
|
|
|
|
|
|
|
def p_raises(self, p):
|
|
|
|
"""raises : RAISES '(' idlist ')'
|
|
|
|
| """
|
|
|
|
if len(p) == 1:
|
|
|
|
p[0] = []
|
|
|
|
else:
|
|
|
|
p[0] = p[3]
|
|
|
|
|
|
|
|
def p_idlist(self, p):
|
|
|
|
"""idlist : IDENTIFIER"""
|
|
|
|
p[0] = [p[1]]
|
|
|
|
|
|
|
|
def p_idlist_continue(self, p):
|
|
|
|
"""idlist : IDENTIFIER ',' idlist"""
|
|
|
|
p[0] = list(p[3])
|
|
|
|
p[0].insert(0, p[1])
|
|
|
|
|
2018-07-11 04:15:16 +03:00
|
|
|
def p_type_id(self, p):
|
|
|
|
"""type : IDENTIFIER"""
|
|
|
|
p[0] = TypeId(name=p[1])
|
|
|
|
p.slice[0].doccomments = p.slice[1].doccomments
|
|
|
|
|
|
|
|
def p_type_generic(self, p):
|
|
|
|
"""type : IDENTIFIER '<' typelist '>'"""
|
|
|
|
p[0] = TypeId(name=p[1], params=p[3])
|
|
|
|
p.slice[0].doccomments = p.slice[1].doccomments
|
|
|
|
|
|
|
|
def p_typelist(self, p):
|
|
|
|
"""typelist : type"""
|
|
|
|
p[0] = [p[1]]
|
|
|
|
|
|
|
|
def p_typelist_continue(self, p):
|
|
|
|
"""typelist : type ',' typelist"""
|
|
|
|
p[0] = list(p[3])
|
|
|
|
p[0].insert(0, p[1])
|
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
def p_error(self, t):
|
2011-11-20 15:13:40 +04:00
|
|
|
if not t:
|
2018-05-26 07:29:52 +03:00
|
|
|
raise IDLError(
|
2018-05-26 08:17:28 +03:00
|
|
|
"Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) "
|
|
|
|
"or both", None)
|
2011-11-20 15:13:40 +04:00
|
|
|
else:
|
|
|
|
location = Location(self.lexer, t.lineno, t.lexpos)
|
|
|
|
raise IDLError("invalid syntax", location)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
2011-11-06 16:32:25 +04:00
|
|
|
def __init__(self, outputdir=''):
|
2008-08-20 06:38:24 +04:00
|
|
|
self._doccomments = []
|
|
|
|
self.lexer = lex.lex(object=self,
|
|
|
|
outputdir=outputdir,
|
|
|
|
lextab='xpidllex',
|
2011-11-06 16:32:25 +04:00
|
|
|
optimize=1)
|
2008-08-20 06:38:24 +04:00
|
|
|
self.parser = yacc.yacc(module=self,
|
|
|
|
outputdir=outputdir,
|
2013-07-30 03:57:27 +04:00
|
|
|
debug=0,
|
2008-08-20 06:38:24 +04:00
|
|
|
tabmodule='xpidlyacc',
|
2011-11-06 16:32:25 +04:00
|
|
|
optimize=1)
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def clearComments(self):
|
|
|
|
self._doccomments = []
|
|
|
|
|
|
|
|
def token(self):
|
|
|
|
t = self.lexer.token()
|
|
|
|
if t is not None and t.type != 'CDATA':
|
|
|
|
t.doccomments = self._doccomments
|
|
|
|
self._doccomments = []
|
|
|
|
return t
|
|
|
|
|
|
|
|
def parse(self, data, filename=None):
|
|
|
|
if filename is not None:
|
|
|
|
self.lexer.filename = filename
|
|
|
|
self.lexer.lineno = 1
|
|
|
|
self.lexer.input(data)
|
2011-07-20 01:46:25 +04:00
|
|
|
idl = self.parser.parse(lexer=self)
|
|
|
|
if filename is not None:
|
|
|
|
idl.deps.append(filename)
|
|
|
|
return idl
|
2008-08-20 06:38:24 +04:00
|
|
|
|
|
|
|
def getLocation(self, p, i):
|
|
|
|
return Location(self.lexer, p.lineno(i), p.lexpos(i))
|
|
|
|
|
2018-05-26 07:29:52 +03:00
|
|
|
|
2008-08-20 06:38:24 +04:00
|
|
|
if __name__ == '__main__':
|
|
|
|
p = IDLParser()
|
|
|
|
for f in sys.argv[1:]:
|
2018-05-26 08:17:28 +03:00
|
|
|
print("Parsing %s" % f)
|
2020-05-02 01:49:29 +03:00
|
|
|
p.parse(open(f).read(), filename=f)
|