Spaces Data

Minimal test - lines (19041, 19224)

path: .spaces[259].spaces[0].metrics.loc.blank
old: 7.0
new: 8.0

path: .spaces[259].spaces[0].metrics.loc.cloc
old: 24.0
new: 25.0

path: .spaces[259].spaces[0].metrics.loc.sloc
old: 182.0
new: 184.0

path: .spaces[259].spaces[0].metrics.mi.mi_sei
old: 6.686479476299365
new: 6.788387376531922

path: .spaces[259].spaces[0].metrics.mi.mi_visual_studio
old: 21.18250400414427
new: 21.078965441207732

path: .spaces[259].spaces[0].metrics.mi.mi_original
old: 36.22208184708671
new: 36.04503090446522

Code

    def __init__(
        self,
        descriptor,
        cgMethod,
        cgGetter,
        cgSetter,
        wantGetParent=True,
        wrapMethodName="WrapObject",
        skipStaticMethods=False,
    ):
        """
        cgMethod, cgGetter and cgSetter are classes used to codegen methods,
        getters and setters.
        """
        self.descriptor = descriptor
        self._deps = descriptor.interface.getDeps()

        iface = descriptor.interface

        self.methodDecls = []

        def appendMethod(m, isConstructor=False):
            sigs = m.signatures()
            for s in sigs[:-1]:
                # Don't put a blank line after overloads, until we
                # get to the last one.
                self.methodDecls.append(
                    cgMethod(descriptor, m, s, isConstructor, breakAfter=False)
                )
            self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], isConstructor))

        if iface.ctor():
            appendMethod(iface.ctor(), isConstructor=True)
        for n in iface.namedConstructors:
            appendMethod(n, isConstructor=True)
        for m in iface.members:
            if m.isMethod():
                if m.isIdentifierLess():
                    continue
                if m.isMaplikeOrSetlikeOrIterableMethod():
                    # Handled by generated code already
                    continue
                if not m.isStatic() or not skipStaticMethods:
                    appendMethod(m)
            elif m.isAttr():
                if m.isMaplikeOrSetlikeAttr():
                    # Handled by generated code already
                    continue
                self.methodDecls.append(cgGetter(descriptor, m))
                if not m.readonly:
                    self.methodDecls.append(cgSetter(descriptor, m))

        # Now do the special operations
        def appendSpecialOperation(name, op):
            if op is None:
                return
            assert len(op.signatures()) == 1
            returnType, args = op.signatures()[0]
            # Make a copy of the args, since we plan to modify them.
            args = list(args)
            if op.isGetter() or op.isDeleter():
                # This is a total hack.  The '&' belongs with the
                # type, not the name!  But it works, and is simpler
                # than trying to somehow make this pretty.
                args.append(
                    FakeArgument(
                        BuiltinTypes[IDLBuiltinType.Types.boolean], op, name="&found"
                    )
                )
            if name == "Stringifier":
                if op.isIdentifierLess():
                    # XXXbz I wish we were consistent about our renaming here.
                    name = "Stringify"
                else:
                    # We already added this method
                    return
            if name == "LegacyCaller":
                if op.isIdentifierLess():
                    # XXXbz I wish we were consistent about our renaming here.
                    name = "LegacyCall"
                else:
                    # We already added this method
                    return
            self.methodDecls.append(
                CGNativeMember(
                    descriptor,
                    op,
                    name,
                    (returnType, args),
                    descriptor.getExtendedAttributes(op),
                )
            )

        # Sort things by name so we get stable ordering in the output.
        ops = sorted(descriptor.operations.items(), key=lambda x: x[0])
        for name, op in ops:
            appendSpecialOperation(name, op)
        # If we support indexed properties, then we need a Length()
        # method so we know which indices are supported.
        if descriptor.supportsIndexedProperties():
            # But we don't need it if we already have an infallible
            # "length" attribute, which we often do.
            haveLengthAttr = any(
                m
                for m in iface.members
                if m.isAttr()
                and CGSpecializedGetter.makeNativeName(descriptor, m) == "Length"
            )
            if not haveLengthAttr:
                self.methodDecls.append(
                    CGNativeMember(
                        descriptor,
                        FakeMember(),
                        "Length",
                        (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], []),
                        [],
                    ),
                )
        # And if we support named properties we need to be able to
        # enumerate the supported names.
        if descriptor.supportsNamedProperties():
            self.methodDecls.append(
                CGNativeMember(
                    descriptor,
                    FakeMember(),
                    "GetSupportedNames",
                    (
                        IDLSequenceType(
                            None, BuiltinTypes[IDLBuiltinType.Types.domstring]
                        ),
                        [],
                    ),
                    [],
                )
            )

        if descriptor.concrete:
            wrapArgs = [
                Argument("JSContext*", "aCx"),
                Argument("JS::Handle", "aGivenProto"),
            ]
            if not descriptor.wrapperCache:
                wrapReturnType = "bool"
                wrapArgs.append(Argument("JS::MutableHandle", "aReflector"))
            else:
                wrapReturnType = "JSObject*"
            self.methodDecls.insert(
                0,
                ClassMethod(
                    wrapMethodName,
                    wrapReturnType,
                    wrapArgs,
                    virtual=descriptor.wrapperCache,
                    breakAfterReturnDecl=" ",
                    override=descriptor.wrapperCache,
                    body=self.getWrapObjectBody(),
                ),
            )
        if descriptor.hasCEReactions():
            self.methodDecls.insert(
                0,
                ClassMethod(
                    "GetDocGroup",
                    "DocGroup*",
                    [],
                    const=True,
                    breakAfterReturnDecl=" ",
                    body=self.getGetDocGroupBody(),
                ),
            )
        if wantGetParent:
            self.methodDecls.insert(
                0,
                ClassMethod(
                    "GetParentObject",
                    self.getGetParentObjectReturnType(),
                    [],
                    const=True,
                    breakAfterReturnDecl=" ",
                    body=self.getGetParentObjectBody(),
                ),
            )

        # Invoke  CGClass.__init__ in any subclasses afterwards to do the actual codegen.

Minimal test - lines (17782, 17895)

path: .spaces[252].metrics.loc.cloc
old: 34.0
new: 36.0

path: .spaces[252].metrics.loc.sloc
old: 112.0
new: 114.0

path: .spaces[252].metrics.mi.mi_sei
old: 34.485119863789116
new: 34.624928885269746

path: .spaces[252].metrics.mi.mi_original
old: 48.49194345955861
new: 48.20521031054831

path: .spaces[252].metrics.mi.mi_visual_studio
old: 28.3578616722565
new: 28.190181468156904

Code

class ForwardDeclarationBuilder:
    """
    Create a canonical representation of a set of namespaced forward
    declarations.
    """

    def __init__(self):
        """
        The set of declarations is represented as a tree of nested namespaces.
        Each tree node has a set of declarations |decls| and a dict |children|.
        Each declaration is a pair consisting of the class name and a boolean
        that is true iff the class is really a struct. |children| maps the
        names of inner namespaces to the declarations in that namespace.
        """
        self.decls = set()
        self.children = {}

    def _ensureNonTemplateType(self, type):
        if "<" in type:
            # This is a templated type.  We don't really know how to
            # forward-declare those, and trying to do it naively is not going to
            # go well (e.g. we may have :: characters inside the type we're
            # templated on!).  Just bail out.
            raise TypeError(
                "Attempt to use ForwardDeclarationBuilder on "
                "templated type %s.  We don't know how to do that "
                "yet." % type
            )

    def _listAdd(self, namespaces, name, isStruct=False):
        """
        Add a forward declaration, where |namespaces| is a list of namespaces.
        |name| should not contain any other namespaces.
        """
        if namespaces:
            child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
            child._listAdd(namespaces[1:], name, isStruct)
        else:
            assert "::" not in name
            self.decls.add((name, isStruct))

    def addInMozillaDom(self, name, isStruct=False):
        """
        Add a forward declaration to the mozilla::dom:: namespace. |name| should not
        contain any other namespaces.
        """
        self._ensureNonTemplateType(name)
        self._listAdd(["mozilla", "dom"], name, isStruct)

    def add(self, nativeType, isStruct=False):
        """
        Add a forward declaration, where |nativeType| is a string containing
        the type and its namespaces, in the usual C++ way.
        """
        self._ensureNonTemplateType(nativeType)
        components = nativeType.split("::")
        self._listAdd(components[:-1], components[-1], isStruct)

    def _build(self, atTopLevel):
        """
        Return a codegenerator for the forward declarations.
        """
        decls = []
        if self.decls:
            decls.append(
                CGList(
                    [
                        CGClassForwardDeclare(cname, isStruct)
                        for cname, isStruct in sorted(self.decls)
                    ]
                )
            )
        for namespace, child in sorted(six.iteritems(self.children)):
            decls.append(
                CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True)
            )

        cg = CGList(decls, "\n")
        if not atTopLevel and len(decls) + len(self.decls) > 1:
            cg = CGWrapper(cg, pre="\n", post="\n")
        return cg

    def build(self):
        return self._build(atTopLevel=True)

    def forwardDeclareForType(self, t, config):
        t = t.unroll()
        if t.isGeckoInterface():
            name = t.inner.identifier.name
            try:
                desc = config.getDescriptor(name)
                self.add(desc.nativeType)
            except NoSuchDescriptorError:
                pass

        # Note: SpiderMonkey interfaces are typedefs, so can't be
        # forward-declared
        elif t.isPromise():
            self.addInMozillaDom("Promise")
        elif t.isCallback():
            self.addInMozillaDom(t.callback.identifier.name)
        elif t.isDictionary():
            self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
        elif t.isCallbackInterface():
            self.addInMozillaDom(t.inner.identifier.name)
        elif t.isUnion():
            # Forward declare both the owning and non-owning version,
            # since we don't know which one we might want
            self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
            self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
        elif t.isRecord():
            self.forwardDeclareForType(t.inner, config)
        # Don't need to do anything for void, primitive, string, any or object.
        # There may be some other cases we are missing.

Minimal test - lines (17867, 17895)

path: .spaces[252].spaces[7].metrics.loc.sloc
old: 27.0
new: 29.0

path: .spaces[252].spaces[7].metrics.loc.cloc
old: 4.0
new: 6.0

path: .spaces[252].spaces[7].metrics.mi.mi_original
old: 81.68394863186202
new: 80.52631341535127

path: .spaces[252].spaces[7].metrics.mi.mi_sei
old: 71.13916894473269
new: 73.7793443759281

path: .spaces[252].spaces[7].metrics.mi.mi_visual_studio
old: 47.76839101278481
new: 47.09141135400659

Code

    def forwardDeclareForType(self, t, config):
        t = t.unroll()
        if t.isGeckoInterface():
            name = t.inner.identifier.name
            try:
                desc = config.getDescriptor(name)
                self.add(desc.nativeType)
            except NoSuchDescriptorError:
                pass

        # Note: SpiderMonkey interfaces are typedefs, so can't be
        # forward-declared
        elif t.isPromise():
            self.addInMozillaDom("Promise")
        elif t.isCallback():
            self.addInMozillaDom(t.callback.identifier.name)
        elif t.isDictionary():
            self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
        elif t.isCallbackInterface():
            self.addInMozillaDom(t.inner.identifier.name)
        elif t.isUnion():
            # Forward declare both the owning and non-owning version,
            # since we don't know which one we might want
            self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
            self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
        elif t.isRecord():
            self.forwardDeclareForType(t.inner, config)
        # Don't need to do anything for void, primitive, string, any or object.
        # There may be some other cases we are missing.