summaryrefslogtreecommitdiffstats
path: root/xpcom/idl-parser/xpidl
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/idl-parser/xpidl')
-rw-r--r--xpcom/idl-parser/xpidl/fixtures/xpctest.d.json2
-rwxr-xr-xxpcom/idl-parser/xpidl/xpidl.py97
2 files changed, 93 insertions, 6 deletions
diff --git a/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json b/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json
index 087a323dd4..b81760c0f5 100644
--- a/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json
+++ b/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json
@@ -1313,7 +1313,7 @@
"callable": false,
"consts": [],
"enums": [],
- "id": "nsIXPCTestNoScriptMembers",
+ "id": "nsIXPCTestTypeScript",
"members": [
{
"name": "exposedProp",
diff --git a/xpcom/idl-parser/xpidl/xpidl.py b/xpcom/idl-parser/xpidl/xpidl.py
index 11b5d05e58..59e4e355de 100755
--- a/xpcom/idl-parser/xpidl/xpidl.py
+++ b/xpcom/idl-parser/xpidl/xpidl.py
@@ -524,6 +524,8 @@ class Typedef(object):
return "%s%s" % ("*mut " if "out" in calltype else "", self.name)
def tsType(self):
+ # Make sure that underlying type is supported: doesn't throw TSNoncompat.
+ self.realtype.tsType()
return self.name
def __str__(self):
@@ -1214,19 +1216,24 @@ def ensureInfallibleIsSound(methodOrAttribute):
if methodOrAttribute.notxpcom:
raise IDLError(
- "[infallible] does not make sense for a [notxpcom] " "method or attribute",
+ "[infallible] does not make sense for a [notxpcom] method or attribute",
methodOrAttribute.location,
)
# An interface cannot be implemented by JS if it has a notxpcom or nostdcall
-# method or attribute, so it must be marked as builtinclass.
+# method or attribute, or uses a by-value native type, so it must be marked as
+# builtinclass.
def ensureBuiltinClassIfNeeded(methodOrAttribute):
iface = methodOrAttribute.iface
if not iface.attributes.scriptable or iface.attributes.builtinclass:
return
if iface.name == "nsISupports":
return
+
+ # notxpcom and nostdcall types change calling conventions, which breaks
+ # xptcall wrappers. We cannot allow XPCWrappedJS to be created for
+ # interfaces with these methods.
if methodOrAttribute.notxpcom:
raise IDLError(
(
@@ -1246,6 +1253,84 @@ def ensureBuiltinClassIfNeeded(methodOrAttribute):
methodOrAttribute.location,
)
+ # Methods with custom native parameters passed without indirection cannot be
+ # safely handled by xptcall (as it cannot know the calling stack/register
+ # layout), so require the interface to be builtinclass.
+ #
+ # Only "in" parameters and writable attributes are checked, as other
+ # parameters are always passed indirectly, so do not impact calling
+ # conventions.
+ def typeNeedsBuiltinclass(type):
+ inner = type
+ while inner.kind == "typedef":
+ inner = inner.realtype
+ return (
+ inner.kind == "native"
+ and inner.specialtype is None
+ and inner.modifier is None
+ )
+
+ if methodOrAttribute.kind == "method":
+ for p in methodOrAttribute.params:
+ if p.paramtype == "in" and typeNeedsBuiltinclass(p.realtype):
+ raise IDLError(
+ (
+ "scriptable interface '%s' must be marked [builtinclass] "
+ "because it contains method '%s' with a by-value custom native "
+ "parameter '%s'"
+ )
+ % (iface.name, methodOrAttribute.name, p.name),
+ methodOrAttribute.location,
+ )
+ elif methodOrAttribute.kind == "attribute" and not methodOrAttribute.readonly:
+ if typeNeedsBuiltinclass(methodOrAttribute.realtype):
+ raise IDLError(
+ (
+ "scriptable interface '%s' must be marked [builtinclass] because it "
+ "contains writable attribute '%s' with a by-value custom native type"
+ )
+ % (iface.name, methodOrAttribute.name),
+ methodOrAttribute.location,
+ )
+
+
+def ensureNoscriptIfNeeded(methodOrAttribute):
+ if not methodOrAttribute.isScriptable():
+ return
+
+ # NOTE: We can't check forward-declared interfaces to see if they're
+ # scriptable, as the information about whether they're scriptable is not
+ # known here.
+ def typeNeedsNoscript(type):
+ if type.kind in ["array", "legacyarray"]:
+ return typeNeedsNoscript(type.type)
+ if type.kind == "typedef":
+ return typeNeedsNoscript(type.realtype)
+ if type.kind == "native":
+ return type.specialtype is None
+ if type.kind == "interface":
+ return not type.attributes.scriptable
+ return False
+
+ if typeNeedsNoscript(methodOrAttribute.realtype):
+ raise IDLError(
+ "%s '%s' must be marked [noscript] because it has a non-scriptable type"
+ % (methodOrAttribute.kind, methodOrAttribute.name),
+ methodOrAttribute.location,
+ )
+ if methodOrAttribute.kind == "method":
+ for p in methodOrAttribute.params:
+ # iid_is arguments have their type ignored, so shouldn't be checked.
+ if not p.iid_is and typeNeedsNoscript(p.realtype):
+ raise IDLError(
+ (
+ "method '%s' must be marked [noscript] because it has a "
+ "non-scriptable parameter '%s'"
+ )
+ % (methodOrAttribute.name, p.name),
+ methodOrAttribute.location,
+ )
+
class Attribute(object):
kind = "attribute"
@@ -1334,6 +1419,7 @@ class Attribute(object):
ensureInfallibleIsSound(self)
ensureBuiltinClassIfNeeded(self)
+ ensureNoscriptIfNeeded(self)
def toIDL(self):
attribs = attlistToIDL(self.attlist)
@@ -1420,9 +1506,6 @@ class Method(object):
self.iface = iface
self.realtype = self.iface.idl.getName(self.type, self.location)
- ensureInfallibleIsSound(self)
- ensureBuiltinClassIfNeeded(self)
-
for p in self.params:
p.resolve(self)
for p in self.params:
@@ -1462,6 +1545,10 @@ class Method(object):
self.location,
)
+ ensureInfallibleIsSound(self)
+ ensureBuiltinClassIfNeeded(self)
+ ensureNoscriptIfNeeded(self)
+
def isScriptable(self):
if not self.iface.attributes.scriptable:
return False