summaryrefslogtreecommitdiff
path: root/xpcom/typelib
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2022-02-12 17:47:03 +0000
committerMatt A. Tobin <email@mattatobin.com>2022-02-12 14:23:18 -0600
commitf66babd8b8368ada3e5aa29cdef1c77291ee4ddd (patch)
treee3842e2a6bf19090185f9c475b3846e1bb79ac97 /xpcom/typelib
downloadGRE-f66babd8b8368ada3e5aa29cdef1c77291ee4ddd.tar.gz
Create the Goanna Runtime Environment
Diffstat (limited to 'xpcom/typelib')
-rw-r--r--xpcom/typelib/moz.build7
-rw-r--r--xpcom/typelib/xpt/moz.build39
-rw-r--r--xpcom/typelib/xpt/tools/Makefile.in10
-rw-r--r--xpcom/typelib/xpt/tools/moz.build12
-rw-r--r--xpcom/typelib/xpt/tools/runtests.py770
-rw-r--r--xpcom/typelib/xpt/tools/xpt.datbin0 -> 13001701 bytes
-rwxr-xr-xxpcom/typelib/xpt/tools/xpt.py1540
-rw-r--r--xpcom/typelib/xpt/xpt_arena.cpp196
-rw-r--r--xpcom/typelib/xpt/xpt_arena.h70
-rw-r--r--xpcom/typelib/xpt/xpt_struct.cpp432
-rw-r--r--xpcom/typelib/xpt/xpt_struct.h366
-rw-r--r--xpcom/typelib/xpt/xpt_xdr.cpp227
-rw-r--r--xpcom/typelib/xpt/xpt_xdr.h86
13 files changed, 3755 insertions, 0 deletions
diff --git a/xpcom/typelib/moz.build b/xpcom/typelib/moz.build
new file mode 100644
index 000000000..04178d8ee
--- /dev/null
+++ b/xpcom/typelib/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIRS += ['xpt']
+
diff --git a/xpcom/typelib/xpt/moz.build b/xpcom/typelib/xpt/moz.build
new file mode 100644
index 000000000..fc0469d9f
--- /dev/null
+++ b/xpcom/typelib/xpt/moz.build
@@ -0,0 +1,39 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+Library('xpt')
+
+DIRS += ['tools']
+
+UNIFIED_SOURCES += [
+ 'xpt_arena.cpp',
+ 'xpt_struct.cpp',
+ 'xpt_xdr.cpp',
+]
+
+EXPORTS += [
+ 'xpt_arena.h',
+ 'xpt_struct.h',
+ 'xpt_xdr.h',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '!/xpcom/base',
+ '/xpcom/base',
+]
+
+if CONFIG['_MSC_VER']:
+ CFLAGS += ['-Zl']
+
+# Code with FINAL_LIBRARY = 'xul' shouldn't do this, but the code
+# here doesn't use malloc functions anyways, while not setting
+# MOZ_NO_MOZALLOC makes the code include mozalloc.h, which includes
+# inline operator new definitions that MSVC linker doesn't strip
+# when linking the xpt tests.
+DEFINES['MOZ_NO_MOZALLOC'] = True
+
+DIST_INSTALL = True
diff --git a/xpcom/typelib/xpt/tools/Makefile.in b/xpcom/typelib/xpt/tools/Makefile.in
new file mode 100644
index 000000000..e2a6de1a4
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/Makefile.in
@@ -0,0 +1,10 @@
+# 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/.
+
+include $(topsrcdir)/config/rules.mk
+
+ifdef MOZ_PHOENIX
+libs::
+ cp -RL $(srcdir)/xpt.dat $(DIST)/bin/$(MOZ_APP_NAME).res
+endif \ No newline at end of file
diff --git a/xpcom/typelib/xpt/tools/moz.build b/xpcom/typelib/xpt/tools/moz.build
new file mode 100644
index 000000000..b1231abfa
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+PYTHON_UNIT_TESTS += [
+ 'runtests.py',
+]
+
+SDK_FILES.bin += [
+ 'xpt.py',
+]
diff --git a/xpcom/typelib/xpt/tools/runtests.py b/xpcom/typelib/xpt/tools/runtests.py
new file mode 100644
index 000000000..a86e6625d
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/runtests.py
@@ -0,0 +1,770 @@
+#!/usr/bin/env python
+# Copyright 2010,2011 Mozilla Foundation. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE MOZILLA FOUNDATION ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE MOZILLA FOUNDATION OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation
+# are those of the authors and should not be interpreted as representing
+# official policies, either expressed or implied, of the Mozilla
+# Foundation.
+
+import difflib
+import os
+from StringIO import StringIO
+import subprocess
+import tempfile
+import mozunit
+import unittest
+import xpt
+
+
+def get_output(bin, file):
+ p = subprocess.Popen([bin, file], stdout=subprocess.PIPE)
+ stdout, _ = p.communicate()
+ return stdout
+
+if "MOZILLA_OBJDIR" in os.environ:
+ class CheckXPTDump(unittest.TestCase):
+ def test_xpt_dump_diffs(self):
+ MOZILLA_OBJDIR = os.environ["MOZILLA_OBJDIR"]
+ xptdump = os.path.abspath(os.path.join(MOZILLA_OBJDIR,
+ "dist", "bin", "xpt_dump"))
+ components = os.path.abspath(os.path.join(MOZILLA_OBJDIR,
+ "dist", "bin", "components"))
+ for f in os.listdir(components):
+ if not f.endswith(".xpt"):
+ continue
+ fullpath = os.path.join(components, f)
+ # read a Typelib and dump it to a string
+ t = xpt.Typelib.read(fullpath)
+ self.assert_(t is not None)
+ outf = StringIO()
+ t.dump(outf)
+ out = outf.getvalue()
+ # now run xpt_dump on it
+ out2 = get_output(xptdump, fullpath)
+ if out != out2:
+ print "diff %s" % f
+ for line in difflib.unified_diff(out2.split("\n"), out.split("\n"), lineterm=""):
+ print line
+ self.assert_(out == out2, "xpt_dump output should be identical for %s" % f)
+
+
+class TestIIDString(unittest.TestCase):
+ def test_iid_str_roundtrip(self):
+ iid_str = "11223344-5566-7788-9900-aabbccddeeff"
+ iid = xpt.Typelib.string_to_iid(iid_str)
+ self.assertEqual(iid_str, xpt.Typelib.iid_to_string(iid))
+
+ def test_iid_roundtrip(self):
+ iid = "\x11\x22\x33\x44\x55\x66\x77\x88\x99\x00\xaa\xbb\xcc\xdd\xee\xff"
+ iid_str = xpt.Typelib.iid_to_string(iid)
+ self.assertEqual(iid, xpt.Typelib.string_to_iid(iid_str))
+
+
+class TypelibCompareMixin:
+ def assertEqualTypelibs(self, t1, t2):
+ self.assert_(t1 is not None, "Should not be None")
+ self.assert_(t2 is not None, "Should not be None")
+ self.assertEqual(t1.version, t2.version, "Versions should be equal")
+ self.assertEqual(len(t1.interfaces), len(t2.interfaces),
+ "Number of interfaces should be equal")
+ for i, j in zip(t1.interfaces, t2.interfaces):
+ self.assertEqualInterfaces(i, j)
+
+ def assertEqualInterfaces(self, i1, i2):
+ self.assert_(i1 is not None, "Should not be None")
+ self.assert_(i2 is not None, "Should not be None")
+ self.assertEqual(i1.name, i2.name, "Names should be equal")
+ self.assertEqual(i1.iid, i2.iid, "IIDs should be equal")
+ self.assertEqual(i1.namespace, i2.namespace,
+ "Namespaces should be equal")
+ self.assertEqual(i1.resolved, i2.resolved,
+ "Resolved status should be equal")
+ if i1.resolved:
+ if i1.parent or i2.parent:
+ # Can't test exact equality, probably different objects
+ self.assertEqualInterfaces(i1.parent, i2.parent)
+ self.assertEqual(len(i1.methods), len(i2.methods))
+ for m, n in zip(i1.methods, i2.methods):
+ self.assertEqualMethods(m, n)
+ self.assertEqual(len(i1.constants), len(i2.constants))
+ for c, d in zip(i1.constants, i2.constants):
+ self.assertEqualConstants(c, d)
+ self.assertEqual(i1.scriptable, i2.scriptable,
+ "Scriptable status should be equal")
+ self.assertEqual(i1.function, i2.function,
+ "Function status should be equal")
+
+ def assertEqualMethods(self, m1, m2):
+ self.assert_(m1 is not None, "Should not be None")
+ self.assert_(m2 is not None, "Should not be None")
+ self.assertEqual(m1.name, m2.name, "Names should be equal")
+ self.assertEqual(m1.getter, m2.getter, "Getter flag should be equal")
+ self.assertEqual(m1.setter, m2.setter, "Setter flag should be equal")
+ self.assertEqual(m1.notxpcom, m2.notxpcom,
+ "notxpcom flag should be equal")
+ self.assertEqual(m1.constructor, m2.constructor,
+ "constructor flag should be equal")
+ self.assertEqual(m1.hidden, m2.hidden, "hidden flag should be equal")
+ self.assertEqual(m1.optargc, m2.optargc, "optargc flag should be equal")
+ self.assertEqual(m1.implicit_jscontext, m2.implicit_jscontext,
+ "implicit_jscontext flag should be equal")
+ for p1, p2 in zip(m1.params, m2.params):
+ self.assertEqualParams(p1, p2)
+ self.assertEqualParams(m1.result, m2.result)
+
+ def assertEqualConstants(self, c1, c2):
+ self.assert_(c1 is not None, "Should not be None")
+ self.assert_(c2 is not None, "Should not be None")
+ self.assertEqual(c1.name, c2.name)
+ self.assertEqual(c1.value, c2.value)
+ self.assertEqualTypes(c1.type, c2.type)
+
+ def assertEqualParams(self, p1, p2):
+ self.assert_(p1 is not None, "Should not be None")
+ self.assert_(p2 is not None, "Should not be None")
+ self.assertEqualTypes(p1.type, p2.type)
+ self.assertEqual(p1.in_, p2.in_)
+ self.assertEqual(p1.out, p2.out)
+ self.assertEqual(p1.retval, p2.retval)
+ self.assertEqual(p1.shared, p2.shared)
+ self.assertEqual(p1.dipper, p2.dipper)
+ self.assertEqual(p1.optional, p2.optional)
+
+ def assertEqualTypes(self, t1, t2):
+ self.assert_(t1 is not None, "Should not be None")
+ self.assert_(t2 is not None, "Should not be None")
+ self.assertEqual(type(t1), type(t2), "type types should be equal")
+ self.assertEqual(t1.pointer, t2.pointer,
+ "pointer flag should be equal for %s and %s" % (t1, t2))
+ self.assertEqual(t1.reference, t2.reference)
+ if isinstance(t1, xpt.SimpleType):
+ self.assertEqual(t1.tag, t2.tag)
+ elif isinstance(t1, xpt.InterfaceType):
+ self.assertEqualInterfaces(t1.iface, t2.iface)
+ elif isinstance(t1, xpt.InterfaceIsType):
+ self.assertEqual(t1.param_index, t2.param_index)
+ elif isinstance(t1, xpt.ArrayType):
+ self.assertEqualTypes(t1.element_type, t2.element_type)
+ self.assertEqual(t1.size_is_arg_num, t2.size_is_arg_num)
+ self.assertEqual(t1.length_is_arg_num, t2.length_is_arg_num)
+ elif isinstance(t1, xpt.StringWithSizeType) or isinstance(t1, xpt.WideStringWithSizeType):
+ self.assertEqual(t1.size_is_arg_num, t2.size_is_arg_num)
+ self.assertEqual(t1.length_is_arg_num, t2.length_is_arg_num)
+
+
+class TestTypelibReadWrite(unittest.TestCase, TypelibCompareMixin):
+ def test_read_file(self):
+ """
+ Test that a Typelib can be read/written from/to a file.
+ """
+ t = xpt.Typelib()
+ # add an unresolved interface
+ t.interfaces.append(xpt.Interface("IFoo"))
+ fd, f = tempfile.mkstemp()
+ os.close(fd)
+ t.write(f)
+ t2 = xpt.Typelib.read(f)
+ os.remove(f)
+ self.assert_(t2 is not None)
+ self.assertEqualTypelibs(t, t2)
+
+
+# TODO: test flags in various combinations
+class TestTypelibRoundtrip(unittest.TestCase, TypelibCompareMixin):
+ def checkRoundtrip(self, t):
+ s = StringIO()
+ t.write(s)
+ s.seek(0)
+ t2 = xpt.Typelib.read(s)
+ self.assert_(t2 is not None)
+ self.assertEqualTypelibs(t, t2)
+
+ def test_simple(self):
+ t = xpt.Typelib()
+ # add an unresolved interface
+ t.interfaces.append(xpt.Interface("IFoo"))
+ self.checkRoundtrip(t)
+
+ t = xpt.Typelib()
+ # add an unresolved interface with an IID
+ t.interfaces.append(xpt.Interface("IBar", "11223344-5566-7788-9900-aabbccddeeff"))
+ self.checkRoundtrip(t)
+
+ def test_parent(self):
+ """
+ Test that an interface's parent property is correctly serialized
+ and deserialized.
+
+ """
+ t = xpt.Typelib()
+ pi = xpt.Interface("IParent")
+ t.interfaces.append(pi)
+ t.interfaces.append(xpt.Interface("IChild", iid="11223344-5566-7788-9900-aabbccddeeff",
+ parent=pi, resolved=True))
+ self.checkRoundtrip(t)
+
+ def test_ifaceFlags(self):
+ """
+ Test that an interface's flags are correctly serialized
+ and deserialized.
+
+ """
+ t = xpt.Typelib()
+ t.interfaces.append(xpt.Interface("IFlags", iid="11223344-5566-7788-9900-aabbccddeeff",
+ resolved=True,
+ scriptable=True,
+ function=True))
+ self.checkRoundtrip(t)
+
+ def test_constants(self):
+ c = xpt.Constant("X", xpt.SimpleType(xpt.Type.Tags.uint32),
+ 0xF000F000)
+ i = xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ constants=[c])
+ t = xpt.Typelib(interfaces=[i])
+ self.checkRoundtrip(t)
+ # tack on some more constants
+ i.constants.append(xpt.Constant("Y",
+ xpt.SimpleType(xpt.Type.Tags.int16),
+ -30000))
+ i.constants.append(xpt.Constant("Z",
+ xpt.SimpleType(xpt.Type.Tags.uint16),
+ 0xB0B0))
+ i.constants.append(xpt.Constant("A",
+ xpt.SimpleType(xpt.Type.Tags.int32),
+ -1000000))
+ self.checkRoundtrip(t)
+
+ def test_methods(self):
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ i = xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m])
+ t = xpt.Typelib(interfaces=[i])
+ self.checkRoundtrip(t)
+ # add some more methods
+ i.methods.append(xpt.Method("One", xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ params=[
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int64)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.float, pointer=True))
+ ]))
+ self.checkRoundtrip(t)
+ # test some other types (should really be more thorough)
+ i.methods.append(xpt.Method("Two", xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ params=[
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.UTF8String, pointer=True)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.wchar_t_ptr, pointer=True))
+ ]))
+ self.checkRoundtrip(t)
+ # add a method with an InterfaceType argument
+ bar = xpt.Interface("IBar")
+ t.interfaces.append(bar)
+ i.methods.append(xpt.Method("IFaceMethod", xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ params=[
+ xpt.Param(xpt.InterfaceType(bar))
+ ]))
+ self.checkRoundtrip(t)
+
+ # add a method with an InterfaceIsType argument
+ i.methods.append(xpt.Method("IFaceIsMethod", xpt.Param(xpt.SimpleType(xpt.Type.Tags.void)),
+ params=[
+ xpt.Param(xpt.InterfaceIsType(1)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.nsIID))
+ ]))
+ self.checkRoundtrip(t)
+
+ # add a method with an ArrayType argument
+ i.methods.append(xpt.Method("ArrayMethod", xpt.Param(xpt.SimpleType(xpt.Type.Tags.void)),
+ params=[
+ xpt.Param(xpt.ArrayType(
+ xpt.SimpleType(xpt.Type.Tags.int32),
+ 1, 2)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ ]))
+ self.checkRoundtrip(t)
+
+ # add a method with a StringWithSize and WideStringWithSize arguments
+ i.methods.append(xpt.Method("StringWithSizeMethod", xpt.Param(xpt.SimpleType(xpt.Type.Tags.void)),
+ params=[
+ xpt.Param(xpt.StringWithSizeType(
+ 1, 2)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ xpt.Param(xpt.WideStringWithSizeType(
+ 4, 5)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32)),
+ ]))
+ self.checkRoundtrip(t)
+
+
+class TestInterfaceCmp(unittest.TestCase):
+ def test_unresolvedName(self):
+ """
+ Test comparison function on xpt.Interface by name.
+
+ """
+ i1 = xpt.Interface("ABC")
+ i2 = xpt.Interface("DEF")
+ self.assert_(i1 < i2)
+ self.assert_(i1 != i2)
+
+ def test_unresolvedEqual(self):
+ """
+ Test comparison function on xpt.Interface with equal names and IIDs.
+
+ """
+ i1 = xpt.Interface("ABC")
+ i2 = xpt.Interface("ABC")
+ self.assert_(i1 == i2)
+
+ def test_unresolvedIID(self):
+ """
+ Test comparison function on xpt.Interface with different IIDs.
+
+ """
+ # IIDs sort before names
+ i1 = xpt.Interface("ABC", iid="22334411-5566-7788-9900-aabbccddeeff")
+ i2 = xpt.Interface("DEF", iid="11223344-5566-7788-9900-aabbccddeeff")
+ self.assert_(i2 < i1)
+ self.assert_(i2 != i1)
+
+ def test_unresolvedResolved(self):
+ """
+ Test comparison function on xpt.Interface with interfaces with
+ identical names and IIDs but different resolved status.
+
+ """
+ i1 = xpt.Interface("ABC", iid="11223344-5566-7788-9900-aabbccddeeff")
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ i2 = xpt.Interface("ABC", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m])
+ self.assert_(i2 < i1)
+ self.assert_(i2 != i1)
+
+ def test_resolvedIdentical(self):
+ """
+ Test comparison function on xpt.Interface with interfaces with
+ identical names and IIDs, both of which are resolved.
+
+ """
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ i1 = xpt.Interface("ABC", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m])
+ i2 = xpt.Interface("ABC", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m])
+ self.assert_(i2 == i1)
+
+
+class TestXPTLink(unittest.TestCase):
+ def test_mergeDifferent(self):
+ """
+ Test that merging two typelibs with completely different interfaces
+ produces the correctly merged typelib.
+
+ """
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo", scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ t2.interfaces.append(xpt.Interface("IBar", scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ # Interfaces should wind up sorted
+ self.assertEqual("IBar", t3.interfaces[0].name)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+
+ # Add some IID values
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff", scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ t2.interfaces.append(xpt.Interface("IBar", iid="44332211-6655-8877-0099-aabbccddeeff", scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ # Interfaces should wind up sorted
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("IBar", t3.interfaces[1].name)
+
+ def test_mergeConflict(self):
+ """
+ Test that merging two typelibs with conflicting interface definitions
+ raises an error.
+
+ """
+ # Same names, different IIDs
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff"))
+ t2 = xpt.Typelib()
+ # add an unresolved interface, same name different IID
+ t2.interfaces.append(xpt.Interface("IFoo", iid="44332211-6655-8877-0099-aabbccddeeff"))
+ self.assertRaises(xpt.DataError, xpt.xpt_link, [t1, t2])
+
+ # Same IIDs, different names
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff"))
+ t2 = xpt.Typelib()
+ # add an unresolved interface, same IID different name
+ t2.interfaces.append(xpt.Interface("IBar", iid="11223344-5566-7788-9900-aabbccddeeff"))
+ self.assertRaises(xpt.DataError, xpt.xpt_link, [t1, t2])
+
+ def test_mergeUnresolvedIID(self):
+ """
+ Test that merging a typelib with an unresolved definition of
+ an interface that's also unresolved in this typelib, but one
+ has a valid IID copies the IID value to the resulting typelib.
+
+ """
+ # Unresolved in both, but t1 has an IID value
+ t1 = xpt.Typelib()
+ # add an unresolved interface with a valid IID
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff", scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface, no IID
+ t2.interfaces.append(xpt.Interface("IFoo"))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(1, len(t3.interfaces))
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
+ # Unresolved in both, but t2 has an IID value
+ t1 = xpt.Typelib()
+ # add an unresolved interface, no IID
+ t1.interfaces.append(xpt.Interface("IFoo"))
+ t2 = xpt.Typelib()
+ # add an unresolved interface with a valid IID
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff", scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(1, len(t3.interfaces))
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
+
+ def test_mergeResolvedUnresolved(self):
+ """
+ Test that merging two typelibs, one of which contains an unresolved
+ reference to an interface, and the other of which contains a
+ resolved reference to the same interface results in keeping the
+ resolved reference.
+
+ """
+ # t1 has an unresolved interface, t2 has a resolved version
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ t1.interfaces.append(xpt.Interface("IFoo"))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(1, len(t3.interfaces))
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
+ self.assert_(t3.interfaces[0].resolved)
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assertEqual("Bar", t3.interfaces[0].methods[0].name)
+
+ # t1 has a resolved interface, t2 has an unresolved version
+ t1 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ t2.interfaces.append(xpt.Interface("IFoo"))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(1, len(t3.interfaces))
+ self.assertEqual("IFoo", t3.interfaces[0].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[0].iid)
+ self.assert_(t3.interfaces[0].resolved)
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assertEqual("Bar", t3.interfaces[0].methods[0].name)
+
+ def test_mergeReplaceParents(self):
+ """
+ Test that merging an interface results in other interfaces' parent
+ member being updated properly.
+
+ """
+ # t1 has an unresolved interface, t2 has a resolved version,
+ # but t1 also has another interface whose parent is the unresolved
+ # interface.
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ pi = xpt.Interface("IFoo")
+ t1.interfaces.append(pi)
+ # add a child of the unresolved interface
+ t1.interfaces.append(xpt.Interface("IChild", iid="11111111-1111-1111-1111-111111111111",
+ resolved=True, parent=pi, scriptable=True))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IChild", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[0].resolved)
+ # Ensure that IChild's parent has been updated
+ self.assertEqual(t3.interfaces[1], t3.interfaces[0].parent)
+ self.assert_(t3.interfaces[0].parent.resolved)
+
+ # t1 has a resolved interface, t2 has an unresolved version,
+ # but t2 also has another interface whose parent is the unresolved
+ # interface.
+ t1 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ pi = xpt.Interface("IFoo")
+ t2.interfaces.append(pi)
+ # add a child of the unresolved interface
+ t2.interfaces.append(xpt.Interface("IChild", iid="11111111-1111-1111-1111-111111111111",
+ resolved=True, parent=pi, scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IChild", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[0].resolved)
+ # Ensure that IChild's parent has been updated
+ self.assertEqual(t3.interfaces[1], t3.interfaces[0].parent)
+ self.assert_(t3.interfaces[0].parent.resolved)
+
+ def test_mergeReplaceRetval(self):
+ """
+ Test that merging an interface correctly updates InterfaceType
+ return values on methods of other interfaces.
+
+ """
+ # t1 has an unresolved interface and an interface that uses the
+ # unresolved interface as a return value from a method. t2
+ # has a resolved version of the unresolved interface.
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t1.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a return value in a method.
+ p = xpt.Param(xpt.InterfaceType(i))
+ m = xpt.Method("ReturnIface", p)
+ t1.interfaces.append(xpt.Interface("IRetval", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IRetval", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's return value type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].result.type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].result.type.iface)
+
+ # t1 has a resolved interface. t2 has an unresolved version and
+ # an interface that uses the unresolved interface as a return value
+ # from a method.
+ t1 = xpt.Typelib()
+ # add a resolved interface
+ p = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ m = xpt.Method("Bar", p)
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t2.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a return value in a method.
+ p = xpt.Param(xpt.InterfaceType(i))
+ m = xpt.Method("ReturnIface", p)
+ t2.interfaces.append(xpt.Interface("IRetval", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IRetval", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's return value type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].result.type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].result.type.iface)
+
+ def test_mergeReplaceParams(self):
+ """
+ Test that merging an interface correctly updates InterfaceType
+ params on methods of other interfaces.
+
+ """
+ # t1 has an unresolved interface and an interface that uses the
+ # unresolved interface as a param value in a method. t2
+ # has a resolved version of the unresolved interface.
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t1.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a param value in a method.
+ vp = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ p = xpt.Param(xpt.InterfaceType(i))
+ m = xpt.Method("IfaceParam", vp, params=[p])
+ t1.interfaces.append(xpt.Interface("IParam", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ m = xpt.Method("Bar", vp)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IParam", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's param type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].params[0].type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].params[0].type.iface)
+
+ # t1 has a resolved interface. t2 has an unresolved version
+ # and an interface that uses the unresolved interface as a
+ # param value in a method.
+ t1 = xpt.Typelib()
+ # add a resolved interface
+ m = xpt.Method("Bar", vp)
+ t1.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t2.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a param value in a method.
+ vp = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ p = xpt.Param(xpt.InterfaceType(i))
+ m = xpt.Method("IfaceParam", vp, params=[p])
+ t2.interfaces.append(xpt.Interface("IParam", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IParam", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's param type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].params[0].type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].params[0].type.iface)
+
+ def test_mergeReplaceArrayTypeParams(self):
+ """
+ Test that merging an interface correctly updates ArrayType
+ params whose element_type is an InterfaceType on methods
+ of other interfaces.
+
+ """
+ # t1 has an unresolved interface and an interface that uses the
+ # unresolved interface as a type in an ArrayType in a parameter
+ # of a method. t2 has a resolved version of the unresolved interface.
+ t1 = xpt.Typelib()
+ # add an unresolved interface
+ i = xpt.Interface("IFoo")
+ t1.interfaces.append(i)
+ # add an interface that uses the unresolved interface
+ # as a type in an ArrayType in a param value in a method.
+ vp = xpt.Param(xpt.SimpleType(xpt.Type.Tags.void))
+ intp = xpt.Param(xpt.SimpleType(xpt.Type.Tags.int32))
+ p = xpt.Param(xpt.ArrayType(xpt.InterfaceType(i), 1, 2))
+ m = xpt.Method("ArrayIfaceParam", vp, params=[p, intp, intp])
+ t1.interfaces.append(xpt.Interface("IParam", iid="11111111-1111-1111-1111-111111111111",
+ methods=[m], scriptable=True))
+ t2 = xpt.Typelib()
+ # add a resolved interface
+ m = xpt.Method("Bar", vp)
+ t2.interfaces.append(xpt.Interface("IFoo", iid="11223344-5566-7788-9900-aabbccddeeff",
+ methods=[m], scriptable=True))
+ t3 = xpt.xpt_link([t1, t2])
+
+ self.assertEqual(2, len(t3.interfaces))
+ self.assertEqual("IParam", t3.interfaces[0].name)
+ self.assertEqual("11111111-1111-1111-1111-111111111111", t3.interfaces[0].iid)
+ self.assertEqual("IFoo", t3.interfaces[1].name)
+ self.assertEqual("11223344-5566-7788-9900-aabbccddeeff", t3.interfaces[1].iid)
+ self.assert_(t3.interfaces[1].resolved)
+ # Ensure that IRetval's method's param type has been updated.
+ self.assertEqual(1, len(t3.interfaces[0].methods))
+ self.assert_(t3.interfaces[0].methods[0].params[0].type.element_type.iface.resolved)
+ self.assertEqual(t3.interfaces[1],
+ t3.interfaces[0].methods[0].params[0].type.element_type.iface)
+
+if __name__ == '__main__':
+ mozunit.main()
diff --git a/xpcom/typelib/xpt/tools/xpt.dat b/xpcom/typelib/xpt/tools/xpt.dat
new file mode 100644
index 000000000..69ffb7751
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/xpt.dat
Binary files differ
diff --git a/xpcom/typelib/xpt/tools/xpt.py b/xpcom/typelib/xpt/tools/xpt.py
new file mode 100755
index 000000000..67d40f7b2
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/xpt.py
@@ -0,0 +1,1540 @@
+#!/usr/bin/env python
+# Copyright 2010,2011 Mozilla Foundation. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE MOZILLA FOUNDATION ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE MOZILLA FOUNDATION OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation
+# are those of the authors and should not be interpreted as representing
+# official policies, either expressed or implied, of the Mozilla
+# Foundation.
+
+"""
+A module for working with XPCOM Type Libraries.
+
+The XPCOM Type Library File Format is described at:
+http://www.mozilla.org/scriptable/typelib_file.html . It is used
+to provide type information for calling methods on XPCOM objects
+from scripting languages such as JavaScript.
+
+This module provides a set of classes representing the parts of
+a typelib in a high-level manner, as well as methods for reading
+and writing them from files.
+
+The usable public interfaces are currently:
+Typelib.read(input_file) - read a typelib from a file on disk or file-like
+ object, return a Typelib object.
+
+xpt_dump(filename) - read a typelib from a file on disk, dump
+ the contents to stdout in a human-readable
+ format.
+
+Typelib() - construct a new Typelib object
+Interface() - construct a new Interface object
+Method() - construct a new object representing a method
+ defined on an Interface
+Constant() - construct a new object representing a constant
+ defined on an Interface
+Param() - construct a new object representing a parameter
+ to a method
+SimpleType() - construct a new object representing a simple
+ data type
+InterfaceType() - construct a new object representing a type that
+ is an IDL-defined interface
+
+"""
+
+from __future__ import with_statement
+import os
+import sys
+import struct
+import operator
+
+# header magic
+XPT_MAGIC = "XPCOM\nTypeLib\r\n\x1a"
+TYPELIB_VERSION = (1, 2)
+
+
+class FileFormatError(Exception):
+ pass
+
+
+class DataError(Exception):
+ pass
+
+
+# Magic for creating enums
+def M_add_class_attribs(attribs):
+ def foo(name, bases, dict_):
+ for v, k in attribs:
+ dict_[k] = v
+ return type(name, bases, dict_)
+ return foo
+
+
+def enum(*names):
+ class Foo(object):
+ __metaclass__ = M_add_class_attribs(enumerate(names))
+
+ def __setattr__(self, name, value): # this makes it read-only
+ raise NotImplementedError
+ return Foo()
+
+
+# Descriptor types as described in the spec
+class Type(object):
+ """
+ Data type of a method parameter or return value. Do not instantiate
+ this class directly. Rather, use one of its subclasses.
+
+ """
+ _prefixdescriptor = struct.Struct(">B")
+ Tags = enum(
+ # The first 18 entries are SimpleTypeDescriptor
+ 'int8',
+ 'int16',
+ 'int32',
+ 'int64',
+ 'uint8',
+ 'uint16',
+ 'uint32',
+ 'uint64',
+ 'float',
+ 'double',
+ 'boolean',
+ 'char',
+ 'wchar_t',
+ 'void',
+ # the following four values are only valid as pointers
+ 'nsIID',
+ 'DOMString',
+ 'char_ptr',
+ 'wchar_t_ptr',
+ # InterfaceTypeDescriptor
+ 'Interface',
+ # InterfaceIsTypeDescriptor
+ 'InterfaceIs',
+ # ArrayTypeDescriptor
+ 'Array',
+ # StringWithSizeTypeDescriptor
+ 'StringWithSize',
+ # WideStringWithSizeTypeDescriptor
+ 'WideStringWithSize',
+ # XXX: These are also SimpleTypes (but not in the spec)
+ # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/tools/xpt_dump.c#l69
+ 'UTF8String',
+ 'CString',
+ 'AString',
+ 'jsval',
+ )
+
+ def __init__(self, pointer=False, reference=False):
+ self.pointer = pointer
+ self.reference = reference
+ if reference and not pointer:
+ raise Exception("If reference is True pointer must be True too")
+
+ def __cmp__(self, other):
+ return (
+ # First make sure we have two Types of the same type (no pun intended!)
+ cmp(type(self), type(other)) or
+ cmp(self.pointer, other.pointer) or
+ cmp(self.reference, other.reference)
+ )
+
+ @staticmethod
+ def decodeflags(byte):
+ """
+ Given |byte|, an unsigned uint8 containing flag bits,
+ decode the flag bits as described in
+ http://www.mozilla.org/scriptable/typelib_file.html#TypeDescriptor
+ and return a dict of flagname: (True|False) suitable
+ for passing to Type.__init__ as **kwargs.
+
+ """
+ return {'pointer': bool(byte & 0x80),
+ 'reference': bool(byte & 0x20),
+ }
+
+ def encodeflags(self):
+ """
+ Encode the flag bits of this Type object. Returns a byte.
+
+ """
+ flags = 0
+ if self.pointer:
+ flags |= 0x80
+ if self.reference:
+ flags |= 0x20
+ return flags
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset):
+ """
+ Read a TypeDescriptor at |offset| from the mmaped file |map| with
+ data pool offset |data_pool|. Returns (Type, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this TypeDescriptor.
+
+ """
+ start = data_pool + offset - 1
+ (data,) = Type._prefixdescriptor.unpack_from(map, start)
+ # first three bits are the flags
+ flags = data & 0xE0
+ flags = Type.decodeflags(flags)
+ # last five bits is the tag
+ tag = data & 0x1F
+ offset += Type._prefixdescriptor.size
+ t = None
+ if tag <= Type.Tags.wchar_t_ptr or tag >= Type.Tags.UTF8String:
+ t = SimpleType.get(data, tag, flags)
+ elif tag == Type.Tags.Interface:
+ t, offset = InterfaceType.read(typelib, map, data_pool, offset, flags)
+ elif tag == Type.Tags.InterfaceIs:
+ t, offset = InterfaceIsType.read(typelib, map, data_pool, offset, flags)
+ elif tag == Type.Tags.Array:
+ t, offset = ArrayType.read(typelib, map, data_pool, offset, flags)
+ elif tag == Type.Tags.StringWithSize:
+ t, offset = StringWithSizeType.read(typelib, map, data_pool, offset, flags)
+ elif tag == Type.Tags.WideStringWithSize:
+ t, offset = WideStringWithSizeType.read(typelib, map, data_pool, offset, flags)
+ return t, offset
+
+ def write(self, typelib, file):
+ """
+ Write a TypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position. For types other than
+ SimpleType, this is not sufficient for writing the TypeDescriptor,
+ and the subclass method must be called.
+
+ """
+ file.write(Type._prefixdescriptor.pack(self.encodeflags() | self.tag))
+
+
+class SimpleType(Type):
+ """
+ A simple data type. (SimpleTypeDescriptor from the typelib specification.)
+
+ """
+ _cache = {}
+
+ def __init__(self, tag, **kwargs):
+ Type.__init__(self, **kwargs)
+ self.tag = tag
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def get(data, tag, flags):
+ """
+ Get a SimpleType object representing |data| (a TypeDescriptorPrefix).
+ May return an already-created object. If no cached object is found,
+ construct one with |tag| and |flags|.
+
+ """
+ if data not in SimpleType._cache:
+ SimpleType._cache[data] = SimpleType(tag, **flags)
+ return SimpleType._cache[data]
+
+ def __str__(self):
+ s = "unknown"
+ if self.tag == Type.Tags.char_ptr and self.pointer:
+ return "string"
+ if self.tag == Type.Tags.wchar_t_ptr and self.pointer:
+ return "wstring"
+ for t in dir(Type.Tags):
+ if self.tag == getattr(Type.Tags, t):
+ s = t
+ break
+
+ if self.pointer:
+ if self.reference:
+ s += " &"
+ else:
+ s += " *"
+ return s
+
+
+class InterfaceType(Type):
+ """
+ A type representing a pointer to an IDL-defined interface.
+ (InterfaceTypeDescriptor from the typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">H")
+
+ def __init__(self, iface, pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("InterfaceType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.iface = iface
+ self.tag = Type.Tags.Interface
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ # When comparing interface types, only look at the name.
+ cmp(self.iface.name, other.iface.name) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an InterfaceTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (InterfaceType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this InterfaceTypeDescriptor.
+
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (iface_index,) = InterfaceType._descriptor.unpack_from(map, start)
+ offset += InterfaceType._descriptor.size
+ iface = None
+ # interface indices are 1-based
+ if iface_index > 0 and iface_index <= len(typelib.interfaces):
+ iface = typelib.interfaces[iface_index - 1]
+ return InterfaceType(iface, **flags), offset
+
+ def write(self, typelib, file):
+ """
+ Write an InterfaceTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ # write out the interface index (1-based)
+ file.write(InterfaceType._descriptor.pack(typelib.interfaces.index(self.iface) + 1))
+
+ def __str__(self):
+ if self.iface:
+ return self.iface.name
+ return "unknown interface"
+
+
+class InterfaceIsType(Type):
+ """
+ A type representing an interface described by one of the other
+ arguments to the method. (InterfaceIsTypeDescriptor from the
+ typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">B")
+ _cache = {}
+
+ def __init__(self, param_index, pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("InterfaceIsType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.param_index = param_index
+ self.tag = Type.Tags.InterfaceIs
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.param_index, other.param_index) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an InterfaceIsTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (InterfaceIsType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this InterfaceIsTypeDescriptor.
+ May return a cached value.
+
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (param_index,) = InterfaceIsType._descriptor.unpack_from(map, start)
+ offset += InterfaceIsType._descriptor.size
+ if param_index not in InterfaceIsType._cache:
+ InterfaceIsType._cache[param_index] = InterfaceIsType(param_index, **flags)
+ return InterfaceIsType._cache[param_index], offset
+
+ def write(self, typelib, file):
+ """
+ Write an InterfaceIsTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ file.write(InterfaceIsType._descriptor.pack(self.param_index))
+
+ def __str__(self):
+ return "InterfaceIs *"
+
+
+class ArrayType(Type):
+ """
+ A type representing an Array of elements of another type, whose
+ size and length are passed as separate parameters to a method.
+ (ArrayTypeDescriptor from the typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">BB")
+
+ def __init__(self, element_type, size_is_arg_num, length_is_arg_num,
+ pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("ArrayType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.element_type = element_type
+ self.size_is_arg_num = size_is_arg_num
+ self.length_is_arg_num = length_is_arg_num
+ self.tag = Type.Tags.Array
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.element_type, other.element_type) or
+ cmp(self.size_is_arg_num, other.size_is_arg_num) or
+ cmp(self.length_is_arg_num, other.length_is_arg_num) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an ArrayTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (ArrayType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this ArrayTypeDescriptor.
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (size_is_arg_num, length_is_arg_num) = ArrayType._descriptor.unpack_from(map, start)
+ offset += ArrayType._descriptor.size
+ t, offset = Type.read(typelib, map, data_pool, offset)
+ return ArrayType(t, size_is_arg_num, length_is_arg_num, **flags), offset
+
+ def write(self, typelib, file):
+ """
+ Write an ArrayTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ file.write(ArrayType._descriptor.pack(self.size_is_arg_num,
+ self.length_is_arg_num))
+ self.element_type.write(typelib, file)
+
+ def __str__(self):
+ return "%s []" % str(self.element_type)
+
+
+class StringWithSizeType(Type):
+ """
+ A type representing a UTF-8 encoded string whose size and length
+ are passed as separate arguments to a method. (StringWithSizeTypeDescriptor
+ from the typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">BB")
+
+ def __init__(self, size_is_arg_num, length_is_arg_num,
+ pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("StringWithSizeType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.size_is_arg_num = size_is_arg_num
+ self.length_is_arg_num = length_is_arg_num
+ self.tag = Type.Tags.StringWithSize
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.size_is_arg_num, other.size_is_arg_num) or
+ cmp(self.length_is_arg_num, other.length_is_arg_num) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an StringWithSizeTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (StringWithSizeType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this StringWithSizeTypeDescriptor.
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (size_is_arg_num, length_is_arg_num) = StringWithSizeType._descriptor.unpack_from(map, start)
+ offset += StringWithSizeType._descriptor.size
+ return StringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
+
+ def write(self, typelib, file):
+ """
+ Write a StringWithSizeTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ file.write(StringWithSizeType._descriptor.pack(self.size_is_arg_num,
+ self.length_is_arg_num))
+
+ def __str__(self):
+ return "string_s"
+
+
+class WideStringWithSizeType(Type):
+ """
+ A type representing a UTF-16 encoded string whose size and length
+ are passed as separate arguments to a method.
+ (WideStringWithSizeTypeDescriptor from the typelib specification.)
+
+ """
+ _descriptor = struct.Struct(">BB")
+
+ def __init__(self, size_is_arg_num, length_is_arg_num,
+ pointer=True, **kwargs):
+ if not pointer:
+ raise DataError("WideStringWithSizeType is not valid with pointer=False")
+ Type.__init__(self, pointer=pointer, **kwargs)
+ self.size_is_arg_num = size_is_arg_num
+ self.length_is_arg_num = length_is_arg_num
+ self.tag = Type.Tags.WideStringWithSize
+
+ def __cmp__(self, other):
+ return (
+ Type.__cmp__(self, other) or
+ cmp(self.size_is_arg_num, other.size_is_arg_num) or
+ cmp(self.length_is_arg_num, other.length_is_arg_num) or
+ cmp(self.tag, other.tag)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset, flags):
+ """
+ Read an WideStringWithSizeTypeDescriptor at |offset| from the mmaped
+ file |map| with data pool offset |data_pool|.
+ Returns (WideStringWithSizeType, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this WideStringWithSizeTypeDescriptor.
+ """
+ if not flags['pointer']:
+ return None, offset
+ start = data_pool + offset - 1
+ (size_is_arg_num, length_is_arg_num) = WideStringWithSizeType._descriptor.unpack_from(map, start)
+ offset += WideStringWithSizeType._descriptor.size
+ return WideStringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
+
+ def write(self, typelib, file):
+ """
+ Write a WideStringWithSizeTypeDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ Type.write(self, typelib, file)
+ file.write(WideStringWithSizeType._descriptor.pack(self.size_is_arg_num,
+ self.length_is_arg_num))
+
+ def __str__(self):
+ return "wstring_s"
+
+
+class Param(object):
+ """
+ A parameter to a method, or the return value of a method.
+ (ParamDescriptor from the typelib specification.)
+
+ """
+ _descriptorstart = struct.Struct(">B")
+
+ def __init__(self, type, in_=True, out=False, retval=False,
+ shared=False, dipper=False, optional=False):
+ """
+ Construct a Param object with the specified |type| and
+ flags. Params default to "in".
+
+ """
+
+ self.type = type
+ self.in_ = in_
+ self.out = out
+ self.retval = retval
+ self.shared = shared
+ self.dipper = dipper
+ self.optional = optional
+
+ def __cmp__(self, other):
+ return (
+ cmp(self.type, other.type) or
+ cmp(self.in_, other.in_) or
+ cmp(self.out, other.out) or
+ cmp(self.retval, other.retval) or
+ cmp(self.shared, other.shared) or
+ cmp(self.dipper, other.dipper) or
+ cmp(self.optional, other.optional)
+ )
+
+ @staticmethod
+ def decodeflags(byte):
+ """
+ Given |byte|, an unsigned uint8 containing flag bits,
+ decode the flag bits as described in
+ http://www.mozilla.org/scriptable/typelib_file.html#ParamDescriptor
+ and return a dict of flagname: (True|False) suitable
+ for passing to Param.__init__ as **kwargs
+ """
+ return {'in_': bool(byte & 0x80),
+ 'out': bool(byte & 0x40),
+ 'retval': bool(byte & 0x20),
+ 'shared': bool(byte & 0x10),
+ 'dipper': bool(byte & 0x08),
+ # XXX: Not in the spec, see:
+ # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l456
+ 'optional': bool(byte & 0x04),
+ }
+
+ def encodeflags(self):
+ """
+ Encode the flags of this Param. Return a byte suitable for
+ writing to a typelib file.
+
+ """
+ flags = 0
+ if self.in_:
+ flags |= 0x80
+ if self.out:
+ flags |= 0x40
+ if self.retval:
+ flags |= 0x20
+ if self.shared:
+ flags |= 0x10
+ if self.dipper:
+ flags |= 0x08
+ if self.optional:
+ flags |= 0x04
+ return flags
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset):
+ """
+ Read a ParamDescriptor at |offset| from the mmaped file |map| with
+ data pool offset |data_pool|. Returns (Param, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this ParamDescriptor.
+ """
+ start = data_pool + offset - 1
+ (flags,) = Param._descriptorstart.unpack_from(map, start)
+ # only the first five bits are flags
+ flags &= 0xFC
+ flags = Param.decodeflags(flags)
+ offset += Param._descriptorstart.size
+ t, offset = Type.read(typelib, map, data_pool, offset)
+ p = Param(t, **flags)
+ return p, offset
+
+ def write(self, typelib, file):
+ """
+ Write a ParamDescriptor to |file|, which is assumed to be seeked
+ to the correct position.
+
+ """
+ file.write(Param._descriptorstart.pack(self.encodeflags()))
+ self.type.write(typelib, file)
+
+ def prefix(self):
+ """
+ Return a human-readable string representing the flags set
+ on this Param.
+
+ """
+ s = ""
+ if self.out:
+ if self.in_:
+ s = "inout "
+ else:
+ s = "out "
+ else:
+ s = "in "
+ if self.dipper:
+ s += "dipper "
+ if self.retval:
+ s += "retval "
+ if self.shared:
+ s += "shared "
+ if self.optional:
+ s += "optional "
+ return s
+
+ def __str__(self):
+ return self.prefix() + str(self.type)
+
+
+class Method(object):
+ """
+ A method of an interface, defining its associated parameters
+ and return value.
+ (MethodDescriptor from the typelib specification.)
+
+ """
+ _descriptorstart = struct.Struct(">BIB")
+
+ def __init__(self, name, result,
+ params=[], getter=False, setter=False, notxpcom=False,
+ constructor=False, hidden=False, optargc=False,
+ implicit_jscontext=False):
+ self.name = name
+ self._name_offset = 0
+ self.getter = getter
+ self.setter = setter
+ self.notxpcom = notxpcom
+ self.constructor = constructor
+ self.hidden = hidden
+ self.optargc = optargc
+ self.implicit_jscontext = implicit_jscontext
+ self.params = list(params)
+ if result and not isinstance(result, Param):
+ raise Exception("result must be a Param!")
+ self.result = result
+
+ def __cmp__(self, other):
+ return (
+ cmp(self.name, other.name) or
+ cmp(self.getter, other.getter) or
+ cmp(self.setter, other.setter) or
+ cmp(self.notxpcom, other.notxpcom) or
+ cmp(self.constructor, other.constructor) or
+ cmp(self.hidden, other.hidden) or
+ cmp(self.optargc, other.optargc) or
+ cmp(self.implicit_jscontext, other.implicit_jscontext) or
+ cmp(self.params, other.params) or
+ cmp(self.result, other.result)
+ )
+
+ def read_params(self, typelib, map, data_pool, offset, num_args):
+ """
+ Read |num_args| ParamDescriptors representing this Method's arguments
+ from the mmaped file |map| with data pool at the offset |data_pool|,
+ starting at |offset| into self.params. Returns the offset
+ suitable for reading the data following the ParamDescriptor array.
+
+ """
+ for i in range(num_args):
+ p, offset = Param.read(typelib, map, data_pool, offset)
+ self.params.append(p)
+ return offset
+
+ def read_result(self, typelib, map, data_pool, offset):
+ """
+ Read a ParamDescriptor representing this Method's return type
+ from the mmaped file |map| with data pool at the offset |data_pool|,
+ starting at |offset| into self.result. Returns the offset
+ suitable for reading the data following the ParamDescriptor.
+
+ """
+ self.result, offset = Param.read(typelib, map, data_pool, offset)
+ return offset
+
+ @staticmethod
+ def decodeflags(byte):
+ """
+ Given |byte|, an unsigned uint8 containing flag bits,
+ decode the flag bits as described in
+ http://www.mozilla.org/scriptable/typelib_file.html#MethodDescriptor
+ and return a dict of flagname: (True|False) suitable
+ for passing to Method.__init__ as **kwargs
+
+ """
+ return {'getter': bool(byte & 0x80),
+ 'setter': bool(byte & 0x40),
+ 'notxpcom': bool(byte & 0x20),
+ 'constructor': bool(byte & 0x10),
+ 'hidden': bool(byte & 0x08),
+ # Not in the spec, see
+ # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l489
+ 'optargc': bool(byte & 0x04),
+ 'implicit_jscontext': bool(byte & 0x02),
+ }
+
+ def encodeflags(self):
+ """
+ Encode the flags of this Method object, return a byte suitable
+ for writing to a typelib file.
+
+ """
+ flags = 0
+ if self.getter:
+ flags |= 0x80
+ if self.setter:
+ flags |= 0x40
+ if self.notxpcom:
+ flags |= 0x20
+ if self.constructor:
+ flags |= 0x10
+ if self.hidden:
+ flags |= 0x08
+ if self.optargc:
+ flags |= 0x04
+ if self.implicit_jscontext:
+ flags |= 0x02
+ return flags
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset):
+ """
+ Read a MethodDescriptor at |offset| from the mmaped file |map| with
+ data pool offset |data_pool|. Returns (Method, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this MethodDescriptor.
+
+ """
+ start = data_pool + offset - 1
+ flags, name_offset, num_args = Method._descriptorstart.unpack_from(map, start)
+ # only the first seven bits are flags
+ flags &= 0xFE
+ flags = Method.decodeflags(flags)
+ name = Typelib.read_string(map, data_pool, name_offset)
+ m = Method(name, None, **flags)
+ offset += Method._descriptorstart.size
+ offset = m.read_params(typelib, map, data_pool, offset, num_args)
+ offset = m.read_result(typelib, map, data_pool, offset)
+ return m, offset
+
+ def write(self, typelib, file):
+ """
+ Write a MethodDescriptor to |file|, which is assumed to be
+ seeked to the right position.
+
+ """
+ file.write(Method._descriptorstart.pack(self.encodeflags(),
+ self._name_offset,
+ len(self.params)))
+ for p in self.params:
+ p.write(typelib, file)
+ self.result.write(typelib, file)
+
+ def write_name(self, file, data_pool_offset):
+ """
+ Write this method's name to |file|.
+ Assumes that |file| is currently seeked to an unused portion
+ of the data pool.
+
+ """
+ if self.name:
+ self._name_offset = file.tell() - data_pool_offset + 1
+ file.write(self.name + "\x00")
+ else:
+ self._name_offset = 0
+
+
+class Constant(object):
+ """
+ A constant value of a specific type defined on an interface.
+ (ConstantDesciptor from the typelib specification.)
+
+ """
+ _descriptorstart = struct.Struct(">I")
+ # Actual value is restricted to this set of types
+ # XXX: the spec lies, the source allows a bunch more
+ # http://hg.mozilla.org/mozilla-central/annotate/9c85f9aaec8c/xpcom/typelib/xpt/src/xpt_struct.c#l689
+ typemap = {Type.Tags.int16: '>h',
+ Type.Tags.uint16: '>H',
+ Type.Tags.int32: '>i',
+ Type.Tags.uint32: '>I'}
+
+ def __init__(self, name, type, value):
+ self.name = name
+ self._name_offset = 0
+ self.type = type
+ self.value = value
+
+ def __cmp__(self, other):
+ return (
+ cmp(self.name, other.name) or
+ cmp(self.type, other.type) or
+ cmp(self.value, other.value)
+ )
+
+ @staticmethod
+ def read(typelib, map, data_pool, offset):
+ """
+ Read a ConstDescriptor at |offset| from the mmaped file |map| with
+ data pool offset |data_pool|. Returns (Constant, next offset),
+ where |next offset| is an offset suitable for reading the data
+ following this ConstDescriptor.
+
+ """
+ start = data_pool + offset - 1
+ (name_offset,) = Constant._descriptorstart.unpack_from(map, start)
+ name = Typelib.read_string(map, data_pool, name_offset)
+ offset += Constant._descriptorstart.size
+ # Read TypeDescriptor
+ t, offset = Type.read(typelib, map, data_pool, offset)
+ c = None
+ if isinstance(t, SimpleType) and t.tag in Constant.typemap:
+ tt = Constant.typemap[t.tag]
+ start = data_pool + offset - 1
+ (val,) = struct.unpack_from(tt, map, start)
+ offset += struct.calcsize(tt)
+ c = Constant(name, t, val)
+ return c, offset
+
+ def write(self, typelib, file):
+ """
+ Write a ConstDescriptor to |file|, which is assumed
+ to be seeked to the proper position.
+
+ """
+ file.write(Constant._descriptorstart.pack(self._name_offset))
+ self.type.write(typelib, file)
+ tt = Constant.typemap[self.type.tag]
+ file.write(struct.pack(tt, self.value))
+
+ def write_name(self, file, data_pool_offset):
+ """
+ Write this constants's name to |file|.
+ Assumes that |file| is currently seeked to an unused portion
+ of the data pool.
+
+ """
+ if self.name:
+ self._name_offset = file.tell() - data_pool_offset + 1
+ file.write(self.name + "\x00")
+ else:
+ self._name_offset = 0
+
+ def __repr__(self):
+ return "Constant(%s, %s, %d)" % (self.name, str(self.type), self.value)
+
+
+class Interface(object):
+ """
+ An Interface represents an object, with its associated methods
+ and constant values.
+ (InterfaceDescriptor from the typelib specification.)
+
+ """
+ _direntry = struct.Struct(">16sIII")
+ _descriptorstart = struct.Struct(">HH")
+
+ UNRESOLVED_IID = "00000000-0000-0000-0000-000000000000"
+
+ def __init__(self, name, iid=UNRESOLVED_IID, namespace="",
+ resolved=False, parent=None, methods=[], constants=[],
+ scriptable=False, function=False, builtinclass=False,
+ main_process_scriptable_only=False):
+ self.resolved = resolved
+ # TODO: should validate IIDs!
+ self.iid = iid
+ self.name = name
+ self.namespace = namespace
+ # if unresolved, all the members following this are unusable
+ self.parent = parent
+ self.methods = list(methods)
+ self.constants = list(constants)
+ self.scriptable = scriptable
+ self.function = function
+ self.builtinclass = builtinclass
+ self.main_process_scriptable_only = main_process_scriptable_only
+ # For sanity, if someone constructs an Interface and passes
+ # in methods or constants, then it's resolved.
+ if self.methods or self.constants:
+ # make sure it has a valid IID
+ if self.iid == Interface.UNRESOLVED_IID:
+ raise DataError("Cannot instantiate Interface %s containing methods or constants with an unresolved IID" % self.name)
+ self.resolved = True
+ # These are only used for writing out the interface
+ self._descriptor_offset = 0
+ self._name_offset = 0
+ self._namespace_offset = 0
+ self.xpt_filename = None
+
+ def __repr__(self):
+ return "Interface('%s', '%s', '%s', methods=%s)" % (self.name, self.iid, self.namespace, self.methods)
+
+ def __str__(self):
+ return "Interface(name='%s', iid='%s')" % (self.name, self.iid)
+
+ def __hash__(self):
+ return hash((self.name, self.iid))
+
+ def __cmp__(self, other):
+ c = cmp(self.iid, other.iid)
+ if c != 0:
+ return c
+ c = cmp(self.name, other.name)
+ if c != 0:
+ return c
+ c = cmp(self.namespace, other.namespace)
+ if c != 0:
+ return c
+ # names and IIDs are the same, check resolved
+ if self.resolved != other.resolved:
+ if self.resolved:
+ return -1
+ else:
+ return 1
+ else:
+ if not self.resolved:
+ # both unresolved, but names and IIDs are the same, so equal
+ return 0
+ # When comparing parents, only look at the name.
+ if (self.parent is None) != (other.parent is None):
+ if self.parent is None:
+ return -1
+ else:
+ return 1
+ elif self.parent is not None:
+ c = cmp(self.parent.name, other.parent.name)
+ if c != 0:
+ return c
+ return (
+ cmp(self.methods, other.methods) or
+ cmp(self.constants, other.constants) or
+ cmp(self.scriptable, other.scriptable) or
+ cmp(self.function, other.function) or
+ cmp(self.builtinclass, other.builtinclass) or
+ cmp(self.main_process_scriptable_only, other.main_process_scriptable_only)
+ )
+
+ def read_descriptor(self, typelib, map, data_pool):
+ offset = self._descriptor_offset
+ if offset == 0:
+ return
+ start = data_pool + offset - 1
+ parent, num_methods = Interface._descriptorstart.unpack_from(map, start)
+ if parent > 0 and parent <= len(typelib.interfaces):
+ self.parent = typelib.interfaces[parent - 1]
+ # Read methods
+ offset += Interface._descriptorstart.size
+ for i in range(num_methods):
+ m, offset = Method.read(typelib, map, data_pool, offset)
+ self.methods.append(m)
+ # Read constants
+ start = data_pool + offset - 1
+ (num_constants, ) = struct.unpack_from(">H", map, start)
+ offset = offset + struct.calcsize(">H")
+ for i in range(num_constants):
+ c, offset = Constant.read(typelib, map, data_pool, offset)
+ self.constants.append(c)
+ # Read flags
+ start = data_pool + offset - 1
+ (flags, ) = struct.unpack_from(">B", map, start)
+ offset = offset + struct.calcsize(">B")
+ # only the first two bits are flags
+ flags &= 0xf0
+ if flags & 0x80:
+ self.scriptable = True
+ if flags & 0x40:
+ self.function = True
+ if flags & 0x20:
+ self.builtinclass = True
+ if flags & 0x10:
+ self.main_process_scriptable_only = True
+ self.resolved = True
+
+ def write_directory_entry(self, file):
+ """
+ Write an InterfaceDirectoryEntry for this interface
+ to |file|, which is assumed to be seeked to the correct offset.
+
+ """
+ file.write(Interface._direntry.pack(Typelib.string_to_iid(self.iid),
+ self._name_offset,
+ self._namespace_offset,
+ self._descriptor_offset))
+
+ def write(self, typelib, file, data_pool_offset):
+ """
+ Write an InterfaceDescriptor to |file|, which is assumed
+ to be seeked to the proper position. If this interface
+ is not resolved, do not write any data.
+
+ """
+ if not self.resolved:
+ self._descriptor_offset = 0
+ return
+ self._descriptor_offset = file.tell() - data_pool_offset + 1
+ parent_idx = 0
+ if self.parent:
+ parent_idx = typelib.interfaces.index(self.parent) + 1
+ file.write(Interface._descriptorstart.pack(parent_idx, len(self.methods)))
+ for m in self.methods:
+ m.write(typelib, file)
+ file.write(struct.pack(">H", len(self.constants)))
+ for c in self.constants:
+ c.write(typelib, file)
+ flags = 0
+ if self.scriptable:
+ flags |= 0x80
+ if self.function:
+ flags |= 0x40
+ if self.builtinclass:
+ flags |= 0x20
+ if self.main_process_scriptable_only:
+ flags |= 0x10
+ file.write(struct.pack(">B", flags))
+
+ def write_names(self, file, data_pool_offset):
+ """
+ Write this interface's name and namespace to |file|,
+ as well as the names of all of its methods and constants.
+ Assumes that |file| is currently seeked to an unused portion
+ of the data pool.
+
+ """
+ if self.name:
+ self._name_offset = file.tell() - data_pool_offset + 1
+ file.write(self.name + "\x00")
+ else:
+ self._name_offset = 0
+ if self.namespace:
+ self._namespace_offset = file.tell() - data_pool_offset + 1
+ file.write(self.namespace + "\x00")
+ else:
+ self._namespace_offset = 0
+ for m in self.methods:
+ m.write_name(file, data_pool_offset)
+ for c in self.constants:
+ c.write_name(file, data_pool_offset)
+
+
+class Typelib(object):
+ """
+ A typelib represents one entire typelib file and all the interfaces
+ referenced within, whether defined entirely within the typelib or
+ merely referenced by name or IID.
+
+ Typelib objects may be instantiated directly and populated with data,
+ or the static Typelib.read method may be called to read one from a file.
+
+ """
+ _header = struct.Struct(">16sBBHIII")
+
+ def __init__(self, version=TYPELIB_VERSION, interfaces=[], annotations=[]):
+ """
+ Instantiate a new Typelib.
+
+ """
+ self.version = version
+ self.interfaces = list(interfaces)
+ self.annotations = list(annotations)
+ self.filename = None
+
+ @staticmethod
+ def iid_to_string(iid):
+ """
+ Convert a 16-byte IID into a UUID string.
+
+ """
+ def hexify(s):
+ return ''.join(["%02x" % ord(x) for x in s])
+
+ return "%s-%s-%s-%s-%s" % (hexify(iid[:4]), hexify(iid[4:6]),
+ hexify(iid[6:8]), hexify(iid[8:10]),
+ hexify(iid[10:]))
+
+ @staticmethod
+ def string_to_iid(iid_str):
+ """
+ Convert a UUID string into a 16-byte IID.
+
+ """
+ s = iid_str.replace('-', '')
+ return ''.join([chr(int(s[i:i+2], 16)) for i in range(0, len(s), 2)])
+
+ @staticmethod
+ def read_string(map, data_pool, offset):
+ if offset == 0:
+ return ""
+ sz = map.find('\x00', data_pool + offset - 1)
+ if sz == -1:
+ return ""
+ return map[data_pool + offset - 1:sz]
+
+ @staticmethod
+ def read(input_file):
+ """
+ Read a typelib from |input_file| and return
+ the constructed Typelib object. |input_file| can be a filename
+ or a file-like object.
+
+ """
+ filename = ""
+ data = None
+ expected_size = None
+ if isinstance(input_file, basestring):
+ filename = input_file
+ with open(input_file, "rb") as f:
+ st = os.fstat(f.fileno())
+ data = f.read(st.st_size)
+ expected_size = st.st_size
+ else:
+ data = input_file.read()
+
+ (magic,
+ major_ver,
+ minor_ver,
+ num_interfaces,
+ file_length,
+ interface_directory_offset,
+ data_pool_offset) = Typelib._header.unpack_from(data)
+ if magic != XPT_MAGIC:
+ raise FileFormatError("Bad magic: %s" % magic)
+ xpt = Typelib((major_ver, minor_ver))
+ xpt.filename = filename
+ if expected_size and file_length != expected_size:
+ raise FileFormatError("File is of wrong length, got %d bytes, expected %d" % (expected_size, file_length))
+ # XXX: by spec this is a zero-based file offset. however,
+ # the xpt_xdr code always subtracts 1 from data offsets
+ # (because that's what you do in the data pool) so it
+ # winds up accidentally treating this as 1-based.
+ # Filed as: https://bugzilla.mozilla.org/show_bug.cgi?id=575343
+ interface_directory_offset -= 1
+ # make a half-hearted attempt to read Annotations,
+ # since XPIDL doesn't produce any anyway.
+ start = Typelib._header.size
+ (anno, ) = struct.unpack_from(">B", data, start)
+ tag = anno & 0x7F
+ if tag == 0: # EmptyAnnotation
+ xpt.annotations.append(None)
+ # We don't bother handling PrivateAnnotations or anything
+
+ for i in range(num_interfaces):
+ # iid, name, namespace, interface_descriptor
+ start = interface_directory_offset + i * Interface._direntry.size
+ ide = Interface._direntry.unpack_from(data, start)
+ iid = Typelib.iid_to_string(ide[0])
+ name = Typelib.read_string(data, data_pool_offset, ide[1])
+ namespace = Typelib.read_string(data, data_pool_offset, ide[2])
+ iface = Interface(name, iid, namespace)
+ iface._descriptor_offset = ide[3]
+ iface.xpt_filename = xpt.filename
+ xpt.interfaces.append(iface)
+ for iface in xpt.interfaces:
+ iface.read_descriptor(xpt, data, data_pool_offset)
+ return xpt
+
+ def __repr__(self):
+ return "<Typelib with %d interfaces>" % len(self.interfaces)
+
+ def _sanityCheck(self):
+ """
+ Check certain assumptions about data contained in this typelib.
+ Sort the interfaces array by IID, check that all interfaces
+ referenced by methods exist in the array.
+
+ """
+ self.interfaces.sort()
+ for i in self.interfaces:
+ if i.parent and i.parent not in self.interfaces:
+ raise DataError("Interface %s has parent %s not present in typelib!" % (i.name, i.parent.name))
+ for m in i.methods:
+ for n, p in enumerate(m.params):
+ if isinstance(p, InterfaceType) and \
+ p.iface not in self.interfaces:
+ raise DataError("Interface method %s::%s, parameter %d references interface %s not present in typelib!" % (i.name, m.name, n, p.iface.name))
+ if isinstance(m.result, InterfaceType) and m.result.iface not in self.interfaces:
+ raise DataError("Interface method %s::%s, result references interface %s not present in typelib!" % (i.name, m.name, m.result.iface.name))
+
+ def writefd(self, fd):
+ # write out space for a header + one empty annotation,
+ # padded to 4-byte alignment.
+ headersize = (Typelib._header.size + 1)
+ if headersize % 4:
+ headersize += 4 - headersize % 4
+ fd.write("\x00" * headersize)
+ # save this offset, it's the interface directory offset.
+ interface_directory_offset = fd.tell()
+ # write out space for an interface directory
+ fd.write("\x00" * Interface._direntry.size * len(self.interfaces))
+ # save this offset, it's the data pool offset.
+ data_pool_offset = fd.tell()
+ # write out all the interface descriptors to the data pool
+ for i in self.interfaces:
+ i.write_names(fd, data_pool_offset)
+ i.write(self, fd, data_pool_offset)
+ # now, seek back and write the header
+ file_len = fd.tell()
+ fd.seek(0)
+ fd.write(Typelib._header.pack(XPT_MAGIC,
+ TYPELIB_VERSION[0],
+ TYPELIB_VERSION[1],
+ len(self.interfaces),
+ file_len,
+ interface_directory_offset,
+ data_pool_offset))
+ # write an empty annotation
+ fd.write(struct.pack(">B", 0x80))
+ # now write the interface directory
+ # XXX: bug-compatible with existing xpt lib, put it one byte
+ # ahead of where it's supposed to be.
+ fd.seek(interface_directory_offset - 1)
+ for i in self.interfaces:
+ i.write_directory_entry(fd)
+
+ def write(self, output_file):
+ """
+ Write the contents of this typelib to |output_file|,
+ which can be either a filename or a file-like object.
+
+ """
+ self._sanityCheck()
+ if isinstance(output_file, basestring):
+ with open(output_file, "wb") as f:
+ self.writefd(f)
+ else:
+ self.writefd(output_file)
+
+ def dump(self, out):
+ """
+ Print a human-readable listing of the contents of this typelib
+ to |out|, in the format of xpt_dump.
+
+ """
+ out.write("""Header:
+ Major version: %d
+ Minor version: %d
+ Number of interfaces: %d
+ Annotations:\n""" % (self.version[0], self.version[1], len(self.interfaces)))
+ for i, a in enumerate(self.annotations):
+ if a is None:
+ out.write(" Annotation #%d is empty.\n" % i)
+ out.write("\nInterface Directory:\n")
+ for i in self.interfaces:
+ out.write(" - %s::%s (%s):\n" % (i.namespace, i.name, i.iid))
+ if not i.resolved:
+ out.write(" [Unresolved]\n")
+ else:
+ if i.parent:
+ out.write(" Parent: %s::%s\n" % (i.parent.namespace,
+ i.parent.name))
+ out.write(""" Flags:
+ Scriptable: %s
+ BuiltinClass: %s
+ Function: %s\n""" % (i.scriptable and "TRUE" or "FALSE",
+ i.builtinclass and "TRUE" or "FALSE",
+ i.function and "TRUE" or "FALSE"))
+ out.write(" Methods:\n")
+ if len(i.methods) == 0:
+ out.write(" No Methods\n")
+ else:
+ for m in i.methods:
+ out.write(" %s%s%s%s%s%s%s %s %s(%s);\n" % (
+ m.getter and "G" or " ",
+ m.setter and "S" or " ",
+ m.hidden and "H" or " ",
+ m.notxpcom and "N" or " ",
+ m.constructor and "C" or " ",
+ m.optargc and "O" or " ",
+ m.implicit_jscontext and "J" or " ",
+ str(m.result.type),
+ m.name,
+ m.params and ", ".join(str(p) for p in m.params) or ""
+ ))
+ out.write(" Constants:\n")
+ if len(i.constants) == 0:
+ out.write(" No Constants\n")
+ else:
+ for c in i.constants:
+ out.write(" %s %s = %d;\n" % (c.type, c.name, c.value))
+
+
+def xpt_dump(file):
+ """
+ Dump the contents of |file| to stdout in the format of xpt_dump.
+
+ """
+ t = Typelib.read(file)
+ t.dump(sys.stdout)
+
+
+def xpt_link(inputs):
+ """
+ Link all of the xpt files in |inputs| together and return the result
+ as a Typelib object. All entries in inputs may be filenames or
+ file-like objects. Non-scriptable interfaces that are unreferenced
+ from scriptable interfaces will be removed during linking.
+
+ """
+ def read_input(i):
+ if isinstance(i, Typelib):
+ return i
+ return Typelib.read(i)
+
+ if not inputs:
+ print >>sys.stderr, "Usage: xpt_link <destination file> <input files>"
+ return None
+ # This is the aggregate list of interfaces.
+ interfaces = []
+ # This will be a dict of replaced interface -> replaced with
+ # containing interfaces that were replaced with interfaces from
+ # another typelib, and the interface that replaced them.
+ merged_interfaces = {}
+ for f in inputs:
+ t = read_input(f)
+ interfaces.extend(t.interfaces)
+ # Sort interfaces by name so we can merge adjacent duplicates
+ interfaces.sort(key=operator.attrgetter('name'))
+
+ Result = enum('Equal', # Interfaces the same, doesn't matter
+ 'NotEqual', # Interfaces differ, keep both
+ 'KeepFirst', # Replace second interface with first
+ 'KeepSecond') # Replace first interface with second
+
+ def compare(i, j):
+ """
+ Compare two interfaces, determine if they're equal or
+ completely different, or should be merged (and indicate which
+ one to keep in that case).
+
+ """
+ if i == j:
+ # Arbitrary, just pick one
+ return Result.Equal
+ if i.name != j.name:
+ if i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
+ # Same IID but different names: raise an exception.
+ raise DataError(
+ "Typelibs contain definitions of interface %s"
+ " with different names (%s (%s) vs %s (%s))!" %
+ (i.iid, i.name, i.xpt_filename, j.name, j.xpt_filename))
+ # Otherwise just different interfaces.
+ return Result.NotEqual
+ # Interfaces have the same name, so either they need to be merged
+ # or there's a data error. Sort out which one to keep
+ if i.resolved != j.resolved:
+ # prefer resolved interfaces over unresolved
+ if j.resolved:
+ assert i.iid == j.iid or i.iid == Interface.UNRESOLVED_IID
+ # keep j
+ return Result.KeepSecond
+ else:
+ assert i.iid == j.iid or j.iid == Interface.UNRESOLVED_IID
+ # replace j with i
+ return Result.KeepFirst
+ elif i.iid != j.iid:
+ # Prefer unresolved interfaces with valid IIDs
+ if j.iid == Interface.UNRESOLVED_IID:
+ # replace j with i
+ assert not j.resolved
+ return Result.KeepFirst
+ elif i.iid == Interface.UNRESOLVED_IID:
+ # keep j
+ assert not i.resolved
+ return Result.KeepSecond
+ else:
+ # Same name but different IIDs: raise an exception.
+ raise DataError(
+ "Typelibs contain definitions of interface %s"
+ " with different IIDs (%s (%s) vs %s (%s))!" %
+ (i.name, i.iid, i.xpt_filename,
+ j.iid, j.xpt_filename))
+ raise DataError("No idea what happened here: %s:%s (%s), %s:%s (%s)" %
+ (i.name, i.iid, i.xpt_filename, j.name, j.iid, j.xpt_filename))
+
+ # Compare interfaces pairwise to find duplicates that should be merged.
+ i = 1
+ while i < len(interfaces):
+ res = compare(interfaces[i-1], interfaces[i])
+ if res == Result.NotEqual:
+ i += 1
+ elif res == Result.Equal:
+ # Need to drop one but it doesn't matter which
+ del interfaces[i]
+ elif res == Result.KeepFirst:
+ merged_interfaces[interfaces[i]] = interfaces[i-1]
+ del interfaces[i]
+ elif res == Result.KeepSecond:
+ merged_interfaces[interfaces[i-1]] = interfaces[i]
+ del interfaces[i-1]
+
+ # Now fixup any merged interfaces
+ def checkType(t):
+ if isinstance(t, InterfaceType) and t.iface in merged_interfaces:
+ t.iface = merged_interfaces[t.iface]
+ elif isinstance(t, ArrayType) and \
+ isinstance(t.element_type, InterfaceType) and \
+ t.element_type.iface in merged_interfaces:
+ t.element_type.iface = merged_interfaces[t.element_type.iface]
+
+ for i in interfaces:
+ # Replace parent references
+ if i.parent in merged_interfaces:
+ i.parent = merged_interfaces[i.parent]
+ for m in i.methods:
+ # Replace InterfaceType params and return values
+ checkType(m.result.type)
+ for p in m.params:
+ checkType(p.type)
+
+ # There's no need to have non-scriptable interfaces in a typelib, and
+ # removing them saves memory when typelibs are loaded. But we can't
+ # just blindly remove all non-scriptable interfaces, since we still
+ # need to know about non-scriptable interfaces referenced from
+ # scriptable interfaces.
+ worklist = set(i for i in interfaces if i.scriptable)
+ required_interfaces = set()
+
+ def maybe_add_to_worklist(iface):
+ if iface in required_interfaces or iface in worklist:
+ return
+ worklist.add(iface)
+
+ while worklist:
+ i = worklist.pop()
+ required_interfaces.add(i)
+ if i.parent:
+ maybe_add_to_worklist(i.parent)
+ for m in i.methods:
+ if isinstance(m.result.type, InterfaceType):
+ maybe_add_to_worklist(m.result.type.iface)
+ for p in m.params:
+ if isinstance(p.type, InterfaceType):
+ maybe_add_to_worklist(p.type.iface)
+ elif isinstance(p.type, ArrayType) and isinstance(p.type.element_type, InterfaceType):
+ maybe_add_to_worklist(p.type.element_type.iface)
+
+ interfaces = list(required_interfaces)
+
+ # Re-sort interfaces (by IID)
+ interfaces.sort()
+ return Typelib(interfaces=interfaces)
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print >>sys.stderr, "xpt <dump|link> <files>"
+ sys.exit(1)
+ if sys.argv[1] == 'dump':
+ xpt_dump(sys.argv[2])
+ elif sys.argv[1] == 'link':
+ xpt_link(sys.argv[3:]).write(sys.argv[2])
diff --git a/xpcom/typelib/xpt/xpt_arena.cpp b/xpcom/typelib/xpt/xpt_arena.cpp
new file mode 100644
index 000000000..21be3c00b
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_arena.cpp
@@ -0,0 +1,196 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Quick arena hack for xpt. */
+
+/* XXX This exists because we don't want to drag in NSPR. It *seemed*
+* to make more sense to write a quick and dirty arena than to clone
+* plarena (like js/src did). This is not optimal, but it works.
+*/
+
+#include "xpt_arena.h"
+#include "mozilla/MemoryReporting.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/****************************************************/
+
+/* Block header for each block in the arena */
+struct BLK_HDR
+{
+ BLK_HDR *next;
+};
+
+#define XPT_MIN_BLOCK_SIZE 32
+
+/* XXX this is lame. Should clone the code to do this bitwise */
+#define ALIGN_RND(s,a) ((a)==1?(s):((((s)+(a)-1)/(a))*(a)))
+
+struct XPTSubArena
+{
+ BLK_HDR *first;
+ uint8_t *next;
+ size_t space;
+ size_t block_size;
+};
+
+struct XPTArena
+{
+ // We have one sub-arena with 8-byte alignment for most allocations, and
+ // one with 1-byte alignment for C string allocations. The latter sub-arena
+ // avoids significant amounts of unnecessary padding between C strings.
+ XPTSubArena subarena8;
+ XPTSubArena subarena1;
+};
+
+XPT_PUBLIC_API(XPTArena *)
+XPT_NewArena(size_t block_size8, size_t block_size1)
+{
+ XPTArena *arena = static_cast<XPTArena*>(calloc(1, sizeof(XPTArena)));
+ if (arena) {
+ if (block_size8 < XPT_MIN_BLOCK_SIZE)
+ block_size8 = XPT_MIN_BLOCK_SIZE;
+ arena->subarena8.block_size = ALIGN_RND(block_size8, 8);
+
+ if (block_size1 < XPT_MIN_BLOCK_SIZE)
+ block_size1 = XPT_MIN_BLOCK_SIZE;
+ arena->subarena1.block_size = block_size1;
+ }
+ return arena;
+}
+
+static void
+DestroySubArena(XPTSubArena *subarena)
+{
+ BLK_HDR* cur = subarena->first;
+ while (cur) {
+ BLK_HDR* next = cur->next;
+ free(cur);
+ cur = next;
+ }
+}
+
+XPT_PUBLIC_API(void)
+XPT_DestroyArena(XPTArena *arena)
+{
+ DestroySubArena(&arena->subarena8);
+ DestroySubArena(&arena->subarena1);
+ free(arena);
+}
+
+/*
+* Our alignment rule is that we always round up the size of each allocation
+* so that the 'arena->next' pointer one will point to properly aligned space.
+*/
+
+XPT_PUBLIC_API(void *)
+XPT_ArenaCalloc(XPTArena *arena, size_t size, size_t alignment)
+{
+ if (!size)
+ return NULL;
+
+ if (!arena) {
+ XPT_ASSERT(0);
+ return NULL;
+ }
+
+ XPTSubArena *subarena;
+ if (alignment == 8) {
+ subarena = &arena->subarena8;
+ } else if (alignment == 1) {
+ subarena = &arena->subarena1;
+ } else {
+ XPT_ASSERT(0);
+ return NULL;
+ }
+
+ size_t bytes = ALIGN_RND(size, alignment);
+
+ if (bytes > subarena->space) {
+ BLK_HDR* new_block;
+ size_t block_header_size = ALIGN_RND(sizeof(BLK_HDR), alignment);
+ size_t new_space = subarena->block_size;
+
+ while (bytes > new_space - block_header_size)
+ new_space += subarena->block_size;
+
+ new_block =
+ static_cast<BLK_HDR*>(calloc(new_space / alignment, alignment));
+ if (!new_block) {
+ subarena->next = NULL;
+ subarena->space = 0;
+ return NULL;
+ }
+
+ /* link block into the list of blocks for use when we destroy */
+ new_block->next = subarena->first;
+ subarena->first = new_block;
+
+ /* set info for current block */
+ subarena->next =
+ reinterpret_cast<uint8_t*>(new_block) + block_header_size;
+ subarena->space = new_space - block_header_size;
+
+#ifdef DEBUG
+ /* mark block for corruption check */
+ memset(subarena->next, 0xcd, subarena->space);
+#endif
+ }
+
+#ifdef DEBUG
+ {
+ /* do corruption check */
+ size_t i;
+ for (i = 0; i < bytes; ++i) {
+ XPT_ASSERT(subarena->next[i] == 0xcd);
+ }
+ /* we guarantee that the block will be filled with zeros */
+ memset(subarena->next, 0, bytes);
+ }
+#endif
+
+ uint8_t* p = subarena->next;
+ subarena->next += bytes;
+ subarena->space -= bytes;
+
+ return p;
+}
+
+/***************************************************************************/
+
+#ifdef DEBUG
+XPT_PUBLIC_API(void)
+XPT_AssertFailed(const char *s, const char *file, uint32_t lineno)
+{
+ fprintf(stderr, "Assertion failed: %s, file %s, line %d\n",
+ s, file, lineno);
+ abort();
+}
+#endif
+
+static size_t
+SizeOfSubArenaExcludingThis(XPTSubArena *subarena, MozMallocSizeOf mallocSizeOf)
+{
+ size_t n = 0;
+
+ BLK_HDR* cur = subarena->first;
+ while (cur) {
+ BLK_HDR* next = cur->next;
+ n += mallocSizeOf(cur);
+ cur = next;
+ }
+
+ return n;
+}
+
+XPT_PUBLIC_API(size_t)
+XPT_SizeOfArenaIncludingThis(XPTArena *arena, MozMallocSizeOf mallocSizeOf)
+{
+ size_t n = mallocSizeOf(arena);
+ n += SizeOfSubArenaExcludingThis(&arena->subarena8, mallocSizeOf);
+ n += SizeOfSubArenaExcludingThis(&arena->subarena1, mallocSizeOf);
+ return n;
+}
diff --git a/xpcom/typelib/xpt/xpt_arena.h b/xpcom/typelib/xpt/xpt_arena.h
new file mode 100644
index 000000000..6ac146ffe
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_arena.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Simple arena allocator for xpt (which avoids using NSPR).
+ */
+
+#ifndef __xpt_arena_h__
+#define __xpt_arena_h__
+
+#include <stdlib.h>
+#include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
+#include <stdint.h>
+
+
+/*
+ * The XPT library is statically linked: no functions are exported from
+ * shared libraries.
+ */
+#define XPT_PUBLIC_API(t) t
+#define XPT_PUBLIC_DATA(t) t
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Simple Arena support. Use with caution!
+ */
+
+typedef struct XPTArena XPTArena;
+
+XPT_PUBLIC_API(XPTArena *)
+XPT_NewArena(size_t block_size8, size_t block_size1);
+
+XPT_PUBLIC_API(void)
+XPT_DestroyArena(XPTArena *arena);
+
+XPT_PUBLIC_API(void *)
+XPT_ArenaCalloc(XPTArena *arena, size_t size, size_t alignment);
+
+XPT_PUBLIC_API(size_t)
+XPT_SizeOfArenaIncludingThis(XPTArena *arena, MozMallocSizeOf mallocSizeOf);
+
+/* --------------------------------------------------------- */
+
+#define XPT_CALLOC8(_arena, _bytes) XPT_ArenaCalloc((_arena), (_bytes), 8)
+#define XPT_CALLOC1(_arena, _bytes) XPT_ArenaCalloc((_arena), (_bytes), 1)
+#define XPT_NEWZAP(_arena, _struct) ((_struct *) XPT_CALLOC8((_arena), sizeof(_struct)))
+
+/* --------------------------------------------------------- */
+
+#ifdef DEBUG
+XPT_PUBLIC_API(void)
+XPT_AssertFailed(const char *s, const char *file, uint32_t lineno)
+ MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS;
+#define XPT_ASSERT(_expr) \
+ ((_expr)?((void)0):XPT_AssertFailed(# _expr, __FILE__, __LINE__))
+#else
+#define XPT_ASSERT(_expr) ((void)0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __xpt_arena_h__ */
diff --git a/xpcom/typelib/xpt/xpt_struct.cpp b/xpcom/typelib/xpt/xpt_struct.cpp
new file mode 100644
index 000000000..c2e09abf0
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_struct.cpp
@@ -0,0 +1,432 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implementation of XDR routines for typelib structures. */
+
+#include "xpt_xdr.h"
+#include "xpt_struct.h"
+#include <string.h>
+#include <stdio.h>
+
+using mozilla::WrapNotNull;
+
+/***************************************************************************/
+/* Forward declarations. */
+
+static bool
+DoInterfaceDirectoryEntry(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTInterfaceDirectoryEntry *ide);
+
+static bool
+DoConstDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTConstDescriptor *cd, XPTInterfaceDescriptor *id);
+
+static bool
+DoMethodDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTMethodDescriptor *md, XPTInterfaceDescriptor *id);
+
+static bool
+SkipAnnotation(NotNull<XPTCursor*> cursor, bool *isLast);
+
+static bool
+DoInterfaceDescriptor(XPTArena *arena, NotNull<XPTCursor*> outer,
+ XPTInterfaceDescriptor **idp);
+
+static bool
+DoTypeDescriptorPrefix(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTTypeDescriptorPrefix *tdp);
+
+static bool
+DoTypeDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTTypeDescriptor *td, XPTInterfaceDescriptor *id);
+
+static bool
+DoParamDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTParamDescriptor *pd, XPTInterfaceDescriptor *id);
+
+/***************************************************************************/
+
+XPT_PUBLIC_API(bool)
+XPT_DoHeader(XPTArena *arena, NotNull<XPTCursor*> cursor, XPTHeader **headerp)
+{
+ unsigned int i;
+ uint32_t file_length = 0;
+ uint32_t ide_offset;
+
+ XPTHeader* header = XPT_NEWZAP(arena, XPTHeader);
+ if (!header)
+ return false;
+ *headerp = header;
+
+ uint8_t magic[16];
+ for (i = 0; i < sizeof(magic); i++) {
+ if (!XPT_Do8(cursor, &magic[i]))
+ return false;
+ }
+
+ if (strncmp((const char*)magic, XPT_MAGIC, 16) != 0) {
+ /* Require that the header contain the proper magic */
+ fprintf(stderr,
+ "libxpt: bad magic header in input file; "
+ "found '%s', expected '%s'\n",
+ magic, XPT_MAGIC_STRING);
+ return false;
+ }
+
+ if (!XPT_Do8(cursor, &header->major_version) ||
+ !XPT_Do8(cursor, &header->minor_version)) {
+ return false;
+ }
+
+ if (header->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION) {
+ /* This file is newer than we are and set to an incompatible version
+ * number. We must set the header state thusly and return.
+ */
+ header->num_interfaces = 0;
+ return true;
+ }
+
+ if (!XPT_Do16(cursor, &header->num_interfaces) ||
+ !XPT_Do32(cursor, &file_length) ||
+ !XPT_Do32(cursor, &ide_offset)) {
+ return false;
+ }
+
+ /*
+ * Make sure the file length reported in the header is the same size as
+ * as our buffer unless it is zero (not set)
+ */
+ if (file_length != 0 &&
+ cursor->state->pool_allocated < file_length) {
+ fputs("libxpt: File length in header does not match actual length. File may be corrupt\n",
+ stderr);
+ return false;
+ }
+
+ uint32_t data_pool;
+ if (!XPT_Do32(cursor, &data_pool))
+ return false;
+
+ XPT_SetDataOffset(cursor->state, data_pool);
+
+ if (header->num_interfaces) {
+ size_t n = header->num_interfaces * sizeof(XPTInterfaceDirectoryEntry);
+ header->interface_directory =
+ static_cast<XPTInterfaceDirectoryEntry*>(XPT_CALLOC8(arena, n));
+ if (!header->interface_directory)
+ return false;
+ }
+
+ /*
+ * Iterate through the annotations rather than recurring, to avoid blowing
+ * the stack on large xpt files. We don't actually store annotations, we
+ * just skip over them.
+ */
+ bool isLast;
+ do {
+ if (!SkipAnnotation(cursor, &isLast))
+ return false;
+ } while (!isLast);
+
+ /* shouldn't be necessary now, but maybe later */
+ XPT_SeekTo(cursor, ide_offset);
+
+ for (i = 0; i < header->num_interfaces; i++) {
+ if (!DoInterfaceDirectoryEntry(arena, cursor,
+ &header->interface_directory[i]))
+ return false;
+ }
+
+ return true;
+}
+
+/* InterfaceDirectoryEntry records go in the header */
+bool
+DoInterfaceDirectoryEntry(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTInterfaceDirectoryEntry *ide)
+{
+ char* dummy_name_space;
+
+ /* write the IID in our cursor space */
+ if (!XPT_DoIID(cursor, &(ide->iid)) ||
+
+ /* write the name string in the data pool, and the offset in our
+ cursor space */
+ !XPT_DoCString(arena, cursor, &(ide->name)) ||
+
+ /* don't write the name_space string in the data pool, because we don't
+ * need it. Do write the offset in our cursor space */
+ !XPT_DoCString(arena, cursor, &dummy_name_space, /* ignore = */ true) ||
+
+ /* do InterfaceDescriptors */
+ !DoInterfaceDescriptor(arena, cursor, &ide->interface_descriptor)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+InterfaceDescriptorAddTypes(XPTArena *arena, XPTInterfaceDescriptor *id,
+ uint16_t num)
+{
+ XPTTypeDescriptor *old = id->additional_types;
+ XPTTypeDescriptor *new_;
+ size_t old_size = id->num_additional_types * sizeof(XPTTypeDescriptor);
+ size_t new_size = (num * sizeof(XPTTypeDescriptor)) + old_size;
+
+ /* XXX should grow in chunks to minimize alloc overhead */
+ new_ = static_cast<XPTTypeDescriptor*>(XPT_CALLOC8(arena, new_size));
+ if (!new_)
+ return false;
+ if (old) {
+ memcpy(new_, old, old_size);
+ }
+ id->additional_types = new_;
+
+ if (num + uint16_t(id->num_additional_types) > 256)
+ return false;
+
+ id->num_additional_types += num;
+ return true;
+}
+
+bool
+DoInterfaceDescriptor(XPTArena *arena, NotNull<XPTCursor*> outer,
+ XPTInterfaceDescriptor **idp)
+{
+ XPTInterfaceDescriptor *id;
+ XPTCursor curs;
+ NotNull<XPTCursor*> cursor = WrapNotNull(&curs);
+ uint32_t i, id_sz = 0;
+
+ id = XPT_NEWZAP(arena, XPTInterfaceDescriptor);
+ if (!id)
+ return false;
+ *idp = id;
+
+ if (!XPT_MakeCursor(outer->state, XPT_DATA, id_sz, cursor))
+ return false;
+
+ if (!XPT_Do32(outer, &cursor->offset))
+ return false;
+ if (!cursor->offset) {
+ *idp = NULL;
+ return true;
+ }
+ if(!XPT_Do16(cursor, &id->parent_interface) ||
+ !XPT_Do16(cursor, &id->num_methods)) {
+ return false;
+ }
+
+ if (id->num_methods) {
+ size_t n = id->num_methods * sizeof(XPTMethodDescriptor);
+ id->method_descriptors =
+ static_cast<XPTMethodDescriptor*>(XPT_CALLOC8(arena, n));
+ if (!id->method_descriptors)
+ return false;
+ }
+
+ for (i = 0; i < id->num_methods; i++) {
+ if (!DoMethodDescriptor(arena, cursor, &id->method_descriptors[i], id))
+ return false;
+ }
+
+ if (!XPT_Do16(cursor, &id->num_constants)) {
+ return false;
+ }
+
+ if (id->num_constants) {
+ size_t n = id->num_constants * sizeof(XPTConstDescriptor);
+ id->const_descriptors =
+ static_cast<XPTConstDescriptor*>(XPT_CALLOC8(arena, n));
+ if (!id->const_descriptors)
+ return false;
+ }
+
+ for (i = 0; i < id->num_constants; i++) {
+ if (!DoConstDescriptor(arena, cursor, &id->const_descriptors[i], id)) {
+ return false;
+ }
+ }
+
+ if (!XPT_Do8(cursor, &id->flags)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DoConstDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTConstDescriptor *cd, XPTInterfaceDescriptor *id)
+{
+ bool ok = false;
+
+ if (!XPT_DoCString(arena, cursor, &cd->name) ||
+ !DoTypeDescriptor(arena, cursor, &cd->type, id)) {
+
+ return false;
+ }
+
+ switch(XPT_TDP_TAG(cd->type.prefix)) {
+ case TD_INT8:
+ ok = XPT_Do8(cursor, (uint8_t*) &cd->value.i8);
+ break;
+ case TD_INT16:
+ ok = XPT_Do16(cursor, (uint16_t*) &cd->value.i16);
+ break;
+ case TD_INT32:
+ ok = XPT_Do32(cursor, (uint32_t*) &cd->value.i32);
+ break;
+ case TD_INT64:
+ ok = XPT_Do64(cursor, &cd->value.i64);
+ break;
+ case TD_UINT8:
+ ok = XPT_Do8(cursor, &cd->value.ui8);
+ break;
+ case TD_UINT16:
+ ok = XPT_Do16(cursor, &cd->value.ui16);
+ break;
+ case TD_UINT32:
+ ok = XPT_Do32(cursor, &cd->value.ui32);
+ break;
+ case TD_UINT64:
+ ok = XPT_Do64(cursor, (int64_t *)&cd->value.ui64);
+ break;
+ case TD_CHAR:
+ ok = XPT_Do8(cursor, (uint8_t*) &cd->value.ch);
+ break;
+ case TD_WCHAR:
+ ok = XPT_Do16(cursor, &cd->value.wch);
+ break;
+ /* fall-through */
+ default:
+ fprintf(stderr, "illegal type!\n");
+ break;
+ }
+
+ return ok;
+
+}
+
+bool
+DoMethodDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTMethodDescriptor *md, XPTInterfaceDescriptor *id)
+{
+ int i;
+
+ if (!XPT_Do8(cursor, &md->flags) ||
+ !XPT_DoCString(arena, cursor, &md->name) ||
+ !XPT_Do8(cursor, &md->num_args))
+ return false;
+
+ if (md->num_args) {
+ size_t n = md->num_args * sizeof(XPTParamDescriptor);
+ md->params = static_cast<XPTParamDescriptor*>(XPT_CALLOC8(arena, n));
+ if (!md->params)
+ return false;
+ }
+
+ for(i = 0; i < md->num_args; i++) {
+ if (!DoParamDescriptor(arena, cursor, &md->params[i], id))
+ return false;
+ }
+
+ if (!DoParamDescriptor(arena, cursor, &md->result, id))
+ return false;
+
+ return true;
+}
+
+bool
+DoParamDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTParamDescriptor *pd, XPTInterfaceDescriptor *id)
+{
+ if (!XPT_Do8(cursor, &pd->flags) ||
+ !DoTypeDescriptor(arena, cursor, &pd->type, id))
+ return false;
+
+ return true;
+}
+
+bool
+DoTypeDescriptorPrefix(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTTypeDescriptorPrefix *tdp)
+{
+ return XPT_Do8(cursor, &tdp->flags);
+}
+
+bool
+DoTypeDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
+ XPTTypeDescriptor *td, XPTInterfaceDescriptor *id)
+{
+ if (!DoTypeDescriptorPrefix(arena, cursor, &td->prefix)) {
+ return false;
+ }
+
+ switch (XPT_TDP_TAG(td->prefix)) {
+ case TD_INTERFACE_TYPE:
+ uint16_t iface;
+ if (!XPT_Do16(cursor, &iface))
+ return false;
+ td->u.iface.iface_hi8 = (iface >> 8) & 0xff;
+ td->u.iface.iface_lo8 = iface & 0xff;
+ break;
+ case TD_INTERFACE_IS_TYPE:
+ if (!XPT_Do8(cursor, &td->u.interface_is.argnum))
+ return false;
+ break;
+ case TD_ARRAY: {
+ // argnum2 appears in the on-disk format but it isn't used.
+ uint8_t argnum2 = 0;
+ if (!XPT_Do8(cursor, &td->u.array.argnum) ||
+ !XPT_Do8(cursor, &argnum2))
+ return false;
+
+ if (!InterfaceDescriptorAddTypes(arena, id, 1))
+ return false;
+ td->u.array.additional_type = id->num_additional_types - 1;
+
+ if (!DoTypeDescriptor(arena, cursor,
+ &id->additional_types[td->u.array.additional_type],
+ id))
+ return false;
+ break;
+ }
+ case TD_PSTRING_SIZE_IS:
+ case TD_PWSTRING_SIZE_IS: {
+ // argnum2 appears in the on-disk format but it isn't used.
+ uint8_t argnum2 = 0;
+ if (!XPT_Do8(cursor, &td->u.pstring_is.argnum) ||
+ !XPT_Do8(cursor, &argnum2))
+ return false;
+ break;
+ }
+ default:
+ /* nothing special */
+ break;
+ }
+ return true;
+}
+
+bool
+SkipAnnotation(NotNull<XPTCursor*> cursor, bool *isLast)
+{
+ uint8_t flags;
+ if (!XPT_Do8(cursor, &flags))
+ return false;
+
+ *isLast = XPT_ANN_IS_LAST(flags);
+
+ if (XPT_ANN_IS_PRIVATE(flags)) {
+ if (!XPT_SkipStringInline(cursor) ||
+ !XPT_SkipStringInline(cursor))
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/xpcom/typelib/xpt/xpt_struct.h b/xpcom/typelib/xpt/xpt_struct.h
new file mode 100644
index 000000000..b4da4a3fd
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_struct.h
@@ -0,0 +1,366 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Structures matching the in-memory representation of typelib structures.
+ * http://www.mozilla.org/scriptable/typelib_file.html
+ */
+
+#ifndef __xpt_struct_h__
+#define __xpt_struct_h__
+
+#include "xpt_arena.h"
+#include <stdint.h>
+
+extern "C" {
+
+/*
+ * Originally, I was going to have structures that exactly matched the on-disk
+ * representation, but that proved difficult: different compilers can pack
+ * their structs differently, and that makes overlaying them atop a
+ * read-from-disk byte buffer troublesome. So now I just have some structures
+ * that are used in memory, and we're going to write a nice XDR library to
+ * write them to disk and stuff. It is pure joy. -- shaver
+ */
+
+/* Structures for the typelib components */
+
+typedef struct XPTHeader XPTHeader;
+typedef struct XPTInterfaceDirectoryEntry XPTInterfaceDirectoryEntry;
+typedef struct XPTInterfaceDescriptor XPTInterfaceDescriptor;
+typedef struct XPTConstDescriptor XPTConstDescriptor;
+typedef struct XPTMethodDescriptor XPTMethodDescriptor;
+typedef struct XPTParamDescriptor XPTParamDescriptor;
+typedef struct XPTTypeDescriptor XPTTypeDescriptor;
+typedef struct XPTTypeDescriptorPrefix XPTTypeDescriptorPrefix;
+
+#ifndef nsID_h__
+/*
+ * We can't include nsID.h, because it's full of C++ goop and we're not doing
+ * C++ here, so we define our own minimal struct. We protect against multiple
+ * definitions of this struct, though, and use the same field naming.
+ */
+struct nsID {
+ uint32_t m0;
+ uint16_t m1;
+ uint16_t m2;
+ uint8_t m3[8];
+};
+
+typedef struct nsID nsID;
+#endif
+
+/*
+ * Every XPCOM typelib file begins with a header.
+ */
+struct XPTHeader {
+ // Some of these fields exists in the on-disk format but don't need to be
+ // stored in memory (other than very briefly, which can be done with local
+ // variables).
+
+ //uint8_t magic[16];
+ uint8_t major_version;
+ uint8_t minor_version;
+ uint16_t num_interfaces;
+ //uint32_t file_length;
+ XPTInterfaceDirectoryEntry *interface_directory;
+ //uint32_t data_pool;
+};
+
+#define XPT_MAGIC "XPCOM\nTypeLib\r\n\032"
+/* For error messages. */
+#define XPT_MAGIC_STRING "XPCOM\\nTypeLib\\r\\n\\032"
+#define XPT_MAJOR_VERSION 0x01
+#define XPT_MINOR_VERSION 0x02
+
+/* Any file with a major version number of XPT_MAJOR_INCOMPATIBLE_VERSION
+ * or higher is to be considered incompatible by this version of xpt and
+ * we will refuse to read it. We will return a header with magic, major and
+ * minor versions set from the file. num_interfaces will be set to zero to
+ * confirm our inability to read the file; i.e. even if some client of this
+ * library gets out of sync with us regarding the agreed upon value for
+ * XPT_MAJOR_INCOMPATIBLE_VERSION, anytime num_interfaces is zero we *know*
+ * that this library refused to read the file due to version incompatibility.
+ */
+#define XPT_MAJOR_INCOMPATIBLE_VERSION 0x02
+
+/*
+ * A contiguous array of fixed-size InterfaceDirectoryEntry records begins at
+ * the byte offset identified by the interface_directory field in the file
+ * header. The array is used to quickly locate an interface description
+ * using its IID. No interface should appear more than once in the array.
+ */
+struct XPTInterfaceDirectoryEntry {
+ nsID iid;
+ char *name;
+
+ // This field exists in the on-disk format. But it isn't used so we don't
+ // allocate space for it in memory.
+ //char *name_space;
+
+ XPTInterfaceDescriptor *interface_descriptor;
+};
+
+/*
+ * An InterfaceDescriptor describes a single XPCOM interface, including all of
+ * its methods.
+ */
+struct XPTInterfaceDescriptor {
+ /* This field ordering minimizes the size of this struct.
+ * The fields are serialized on disk in a different order.
+ * See DoInterfaceDescriptor().
+ */
+ XPTMethodDescriptor *method_descriptors;
+ XPTConstDescriptor *const_descriptors;
+ XPTTypeDescriptor *additional_types;
+ uint16_t parent_interface;
+ uint16_t num_methods;
+ uint16_t num_constants;
+ uint8_t flags;
+
+ /* additional_types are used for arrays where we may need multiple
+ * XPTTypeDescriptors for a single XPTMethodDescriptor. Since we still
+ * want to have a simple array of XPTMethodDescriptor (each with a single
+ * embedded XPTTypeDescriptor), a XPTTypeDescriptor can have a reference
+ * to an 'additional_type'. That reference is an index in this
+ * "additional_types" array. So a given XPTMethodDescriptor might have
+ * a whole chain of these XPTTypeDescriptors to represent, say, a multi
+ * dimensional array.
+ *
+ * Note that in the typelib file these additional types are stored 'inline'
+ * in the MethodDescriptor. But, in the typelib MethodDescriptors can be
+ * of varying sizes, where in XPT's in memory mapping of the data we want
+ * them to be of fixed size. This additional_types scheme is here to allow
+ * for that.
+ */
+ uint8_t num_additional_types;
+};
+
+#define XPT_ID_SCRIPTABLE 0x80
+#define XPT_ID_FUNCTION 0x40
+#define XPT_ID_BUILTINCLASS 0x20
+#define XPT_ID_MAIN_PROCESS_SCRIPTABLE_ONLY 0x10
+#define XPT_ID_FLAGMASK 0xf0
+
+#define XPT_ID_IS_SCRIPTABLE(flags) (!!(flags & XPT_ID_SCRIPTABLE))
+#define XPT_ID_IS_FUNCTION(flags) (!!(flags & XPT_ID_FUNCTION))
+#define XPT_ID_IS_BUILTINCLASS(flags) (!!(flags & XPT_ID_BUILTINCLASS))
+#define XPT_ID_IS_MAIN_PROCESS_SCRIPTABLE_ONLY(flags) (!!(flags & XPT_ID_MAIN_PROCESS_SCRIPTABLE_ONLY))
+
+/*
+ * A TypeDescriptor is a variable-size record used to identify the type of a
+ * method argument or return value.
+ *
+ * There are three types of TypeDescriptors:
+ *
+ * SimpleTypeDescriptor
+ * InterfaceTypeDescriptor
+ * InterfaceIsTypeDescriptor
+ *
+ * The tag field in the prefix indicates which of the variant TypeDescriptor
+ * records is being used, and hence the way any remaining fields should be
+ * parsed. Values from 0 to 17 refer to SimpleTypeDescriptors. The value 18
+ * designates an InterfaceTypeDescriptor, while 19 represents an
+ * InterfaceIsTypeDescriptor.
+ */
+
+/* why bother with a struct? - other code relies on this being a struct */
+struct XPTTypeDescriptorPrefix {
+ uint8_t flags;
+};
+
+/* flag bits */
+
+#define XPT_TDP_FLAGMASK 0xe0
+#define XPT_TDP_TAGMASK (~XPT_TDP_FLAGMASK)
+#define XPT_TDP_TAG(tdp) ((tdp).flags & XPT_TDP_TAGMASK)
+
+/*
+ * The following enum maps mnemonic names to the different numeric values
+ * of XPTTypeDescriptor->tag.
+ */
+enum XPTTypeDescriptorTags {
+ TD_INT8 = 0,
+ TD_INT16 = 1,
+ TD_INT32 = 2,
+ TD_INT64 = 3,
+ TD_UINT8 = 4,
+ TD_UINT16 = 5,
+ TD_UINT32 = 6,
+ TD_UINT64 = 7,
+ TD_FLOAT = 8,
+ TD_DOUBLE = 9,
+ TD_BOOL = 10,
+ TD_CHAR = 11,
+ TD_WCHAR = 12,
+ TD_VOID = 13,
+ TD_PNSIID = 14,
+ TD_DOMSTRING = 15,
+ TD_PSTRING = 16,
+ TD_PWSTRING = 17,
+ TD_INTERFACE_TYPE = 18,
+ TD_INTERFACE_IS_TYPE = 19,
+ TD_ARRAY = 20,
+ TD_PSTRING_SIZE_IS = 21,
+ TD_PWSTRING_SIZE_IS = 22,
+ TD_UTF8STRING = 23,
+ TD_CSTRING = 24,
+ TD_ASTRING = 25,
+ TD_JSVAL = 26
+};
+
+struct XPTTypeDescriptor {
+ XPTTypeDescriptorPrefix prefix;
+
+ // The memory layout here doesn't exactly match (for the appropriate types)
+ // the on-disk format. This is to save memory.
+ union {
+ // Used for TD_INTERFACE_IS_TYPE.
+ struct {
+ uint8_t argnum;
+ } interface_is;
+
+ // Used for TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS.
+ struct {
+ uint8_t argnum;
+ //uint8_t argnum2; // Present on disk, omitted here.
+ } pstring_is;
+
+ // Used for TD_ARRAY.
+ struct {
+ uint8_t argnum;
+ //uint8_t argnum2; // Present on disk, omitted here.
+ uint8_t additional_type; // uint16_t on disk, uint8_t here;
+ // in practice it never exceeds 20.
+ } array;
+
+ // Used for TD_INTERFACE_TYPE.
+ struct {
+ // We store the 16-bit iface value as two 8-bit values in order to
+ // avoid 16-bit alignment requirements for XPTTypeDescriptor, which
+ // reduces its size and also the size of XPTParamDescriptor.
+ uint8_t iface_hi8;
+ uint8_t iface_lo8;
+ } iface;
+ } u;
+};
+
+/*
+ * A ConstDescriptor is a variable-size record that records the name and
+ * value of a scoped interface constant.
+ *
+ * The types of the method parameter are restricted to the following subset
+ * of TypeDescriptors:
+ *
+ * int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+ * int64_t, uint64_t, wchar_t, char
+ *
+ * The type (and thus the size) of the value record is determined by the
+ * contents of the associated TypeDescriptor record. For instance, if type
+ * corresponds to int16_t, then value is a two-byte record consisting of a
+ * 16-bit signed integer.
+ */
+union XPTConstValue {
+ int8_t i8;
+ uint8_t ui8;
+ int16_t i16;
+ uint16_t ui16;
+ int32_t i32;
+ uint32_t ui32;
+ int64_t i64;
+ uint64_t ui64;
+ char ch;
+ uint16_t wch;
+}; /* varies according to type */
+
+struct XPTConstDescriptor {
+ char *name;
+ XPTTypeDescriptor type;
+ union XPTConstValue value;
+};
+
+/*
+ * A ParamDescriptor is a variable-size record used to describe either a
+ * single argument to a method or a method's result.
+ */
+struct XPTParamDescriptor {
+ uint8_t flags;
+ XPTTypeDescriptor type;
+};
+
+/* flag bits */
+#define XPT_PD_IN 0x80
+#define XPT_PD_OUT 0x40
+#define XPT_PD_RETVAL 0x20
+#define XPT_PD_SHARED 0x10
+#define XPT_PD_DIPPER 0x08
+#define XPT_PD_OPTIONAL 0x04
+#define XPT_PD_FLAGMASK 0xfc
+
+#define XPT_PD_IS_IN(flags) (flags & XPT_PD_IN)
+#define XPT_PD_IS_OUT(flags) (flags & XPT_PD_OUT)
+#define XPT_PD_IS_RETVAL(flags) (flags & XPT_PD_RETVAL)
+#define XPT_PD_IS_SHARED(flags) (flags & XPT_PD_SHARED)
+#define XPT_PD_IS_DIPPER(flags) (flags & XPT_PD_DIPPER)
+#define XPT_PD_IS_OPTIONAL(flags) (flags & XPT_PD_OPTIONAL)
+
+/*
+ * A MethodDescriptor is a variable-size record used to describe a single
+ * interface method.
+ */
+struct XPTMethodDescriptor {
+ char *name;
+ XPTParamDescriptor *params;
+ XPTParamDescriptor result;
+ uint8_t flags;
+ uint8_t num_args;
+};
+
+/* flag bits */
+#define XPT_MD_GETTER 0x80
+#define XPT_MD_SETTER 0x40
+#define XPT_MD_NOTXPCOM 0x20
+#define XPT_MD_HIDDEN 0x08
+#define XPT_MD_OPT_ARGC 0x04
+#define XPT_MD_CONTEXT 0x02
+#define XPT_MD_FLAGMASK 0xfe
+
+#define XPT_MD_IS_GETTER(flags) (flags & XPT_MD_GETTER)
+#define XPT_MD_IS_SETTER(flags) (flags & XPT_MD_SETTER)
+#define XPT_MD_IS_NOTXPCOM(flags) (flags & XPT_MD_NOTXPCOM)
+#define XPT_MD_IS_HIDDEN(flags) (flags & XPT_MD_HIDDEN)
+#define XPT_MD_WANTS_OPT_ARGC(flags) (flags & XPT_MD_OPT_ARGC)
+#define XPT_MD_WANTS_CONTEXT(flags) (flags & XPT_MD_CONTEXT)
+
+/*
+ * Annotation records are variable-size records used to store secondary
+ * information about the typelib, e.g. such as the name of the tool that
+ * generated the typelib file, the date it was generated, etc. The
+ * information is stored with very loose format requirements so as to
+ * allow virtually any private data to be stored in the typelib.
+ *
+ * There are two types of Annotations:
+ *
+ * EmptyAnnotation
+ * PrivateAnnotation
+ *
+ * The tag field of the prefix discriminates among the variant record
+ * types for Annotation's. If the tag is 0, this record is an
+ * EmptyAnnotation. EmptyAnnotation's are ignored - they're only used to
+ * indicate an array of Annotation's that's completely empty. If the tag
+ * is 1, the record is a PrivateAnnotation.
+ *
+ * We don't actually store annotations; we just skip over them if they are
+ * present.
+ */
+
+#define XPT_ANN_LAST 0x80
+#define XPT_ANN_IS_LAST(flags) (flags & XPT_ANN_LAST)
+#define XPT_ANN_PRIVATE 0x40
+#define XPT_ANN_IS_PRIVATE(flags) (flags & XPT_ANN_PRIVATE)
+
+}
+
+#endif /* __xpt_struct_h__ */
diff --git a/xpcom/typelib/xpt/xpt_xdr.cpp b/xpcom/typelib/xpt/xpt_xdr.cpp
new file mode 100644
index 000000000..04b90422a
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_xdr.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Implementation of XDR primitives. */
+
+#include "xpt_xdr.h"
+#include "nscore.h"
+#include <string.h> /* strchr */
+#include "mozilla/EndianUtils.h"
+
+#define CURS_POOL_OFFSET_RAW(cursor) \
+ ((cursor)->pool == XPT_HEADER \
+ ? (cursor)->offset \
+ : (XPT_ASSERT((cursor)->state->data_offset), \
+ (cursor)->offset + (cursor)->state->data_offset))
+
+#define CURS_POOL_OFFSET(cursor) \
+ (CURS_POOL_OFFSET_RAW(cursor) - 1)
+
+/* can be used as lvalue */
+#define CURS_POINT(cursor) \
+ ((cursor)->state->pool_data[CURS_POOL_OFFSET(cursor)])
+
+static bool
+CHECK_COUNT(NotNull<XPTCursor*> cursor, uint32_t space)
+{
+ // Fail if we're in the data area and about to exceed the allocation.
+ // XXX Also fail if we're in the data area and !state->data_offset
+ if (cursor->pool == XPT_DATA &&
+ (CURS_POOL_OFFSET(cursor) + space > (cursor)->state->pool_allocated)) {
+ XPT_ASSERT(0);
+ fprintf(stderr, "FATAL: no room for %d in cursor\n", space);
+ return false;
+ }
+
+ return true;
+}
+
+XPT_PUBLIC_API(void)
+XPT_InitXDRState(XPTState* state, char *data, uint32_t len)
+{
+ state->next_cursor[0] = state->next_cursor[1] = 1;
+ state->pool_data = data;
+ state->pool_allocated = len;
+}
+
+/* All offsets are 1-based */
+XPT_PUBLIC_API(void)
+XPT_SetDataOffset(XPTState *state, uint32_t data_offset)
+{
+ state->data_offset = data_offset;
+}
+
+XPT_PUBLIC_API(bool)
+XPT_MakeCursor(XPTState *state, XPTPool pool, uint32_t len,
+ NotNull<XPTCursor*> cursor)
+{
+ cursor->state = state;
+ cursor->pool = pool;
+ cursor->bits = 0;
+ cursor->offset = state->next_cursor[pool];
+
+ if (!(CHECK_COUNT(cursor, len)))
+ return false;
+
+ /* this check should be in CHECK_CURSOR */
+ if (pool == XPT_DATA && !state->data_offset) {
+ fprintf(stderr, "no data offset for XPT_DATA cursor!\n");
+ return false;
+ }
+
+ state->next_cursor[pool] += len;
+
+ return true;
+}
+
+XPT_PUBLIC_API(bool)
+XPT_SeekTo(NotNull<XPTCursor*> cursor, uint32_t offset)
+{
+ /* XXX do some real checking and update len and stuff */
+ cursor->offset = offset;
+ return true;
+}
+
+XPT_PUBLIC_API(bool)
+XPT_SkipStringInline(NotNull<XPTCursor*> cursor)
+{
+ uint16_t length;
+ if (!XPT_Do16(cursor, &length))
+ return false;
+
+ uint8_t byte;
+ for (uint16_t i = 0; i < length; i++)
+ if (!XPT_Do8(cursor, &byte))
+ return false;
+
+ return true;
+}
+
+XPT_PUBLIC_API(bool)
+XPT_DoCString(XPTArena *arena, NotNull<XPTCursor*> cursor, char **identp,
+ bool ignore)
+{
+ uint32_t offset = 0;
+ if (!XPT_Do32(cursor, &offset))
+ return false;
+
+ if (!offset) {
+ *identp = NULL;
+ return true;
+ }
+
+ XPTCursor my_cursor;
+ my_cursor.pool = XPT_DATA;
+ my_cursor.offset = offset;
+ my_cursor.state = cursor->state;
+ char* start = &CURS_POINT(&my_cursor);
+
+ char* end = strchr(start, 0); /* find the end of the string */
+ if (!end) {
+ fprintf(stderr, "didn't find end of string on decode!\n");
+ return false;
+ }
+ int len = end - start;
+ XPT_ASSERT(len > 0);
+
+ if (!ignore) {
+ char *ident = (char*)XPT_CALLOC1(arena, len + 1u);
+ if (!ident)
+ return false;
+
+ memcpy(ident, start, (size_t)len);
+ ident[len] = 0;
+ *identp = ident;
+ }
+
+ return true;
+}
+
+/*
+ * IIDs are written in struct order, in the usual big-endian way. From the
+ * typelib file spec:
+ *
+ * "For example, this IID:
+ * {00112233-4455-6677-8899-aabbccddeeff}
+ * is converted to the 128-bit value
+ * 0x00112233445566778899aabbccddeeff
+ * Note that the byte storage order corresponds to the layout of the nsIID
+ * C-struct on a big-endian architecture."
+ *
+ * (http://www.mozilla.org/scriptable/typelib_file.html#iid)
+ */
+XPT_PUBLIC_API(bool)
+XPT_DoIID(NotNull<XPTCursor*> cursor, nsID *iidp)
+{
+ int i;
+
+ if (!XPT_Do32(cursor, &iidp->m0) ||
+ !XPT_Do16(cursor, &iidp->m1) ||
+ !XPT_Do16(cursor, &iidp->m2))
+ return false;
+
+ for (i = 0; i < 8; i++)
+ if (!XPT_Do8(cursor, (uint8_t *)&iidp->m3[i]))
+ return false;
+
+ return true;
+}
+
+// MSVC apparently cannot handle functions as template parameters very well,
+// so we need to use a macro approach here.
+
+#define XPT_DOINT(T, func, valuep) \
+ do { \
+ const size_t sz = sizeof(T); \
+ \
+ if (!CHECK_COUNT(cursor, sz)) { \
+ return false; \
+ } \
+ \
+ *valuep = func(&CURS_POINT(cursor)); \
+ cursor->offset += sz; \
+ return true; \
+ } while(0)
+
+XPT_PUBLIC_API(bool)
+XPT_Do64(NotNull<XPTCursor*> cursor, int64_t *u64p)
+{
+ XPT_DOINT(int64_t, mozilla::BigEndian::readInt64, u64p);
+}
+
+/*
+ * When we're handling 32- or 16-bit quantities, we handle a byte at a time to
+ * avoid alignment issues. Someone could come and optimize this to detect
+ * well-aligned cases and do a single store, if they cared. I might care
+ * later.
+ */
+XPT_PUBLIC_API(bool)
+XPT_Do32(NotNull<XPTCursor*> cursor, uint32_t *u32p)
+{
+ XPT_DOINT(uint32_t, mozilla::BigEndian::readUint32, u32p);
+}
+
+XPT_PUBLIC_API(bool)
+XPT_Do16(NotNull<XPTCursor*> cursor, uint16_t *u16p)
+{
+ XPT_DOINT(uint16_t, mozilla::BigEndian::readUint16, u16p);
+}
+
+#undef XPT_DOINT
+
+XPT_PUBLIC_API(bool)
+XPT_Do8(NotNull<XPTCursor*> cursor, uint8_t *u8p)
+{
+ if (!CHECK_COUNT(cursor, 1))
+ return false;
+
+ *u8p = CURS_POINT(cursor);
+
+ cursor->offset++;
+
+ return true;
+}
+
+
diff --git a/xpcom/typelib/xpt/xpt_xdr.h b/xpcom/typelib/xpt/xpt_xdr.h
new file mode 100644
index 000000000..7bf23aa40
--- /dev/null
+++ b/xpcom/typelib/xpt/xpt_xdr.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Basic APIs for streaming typelib structures from disk.
+ */
+
+#ifndef __xpt_xdr_h__
+#define __xpt_xdr_h__
+
+#include "xpt_struct.h"
+#include "mozilla/NotNull.h"
+
+using mozilla::NotNull;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct XPTState XPTState;
+typedef struct XPTCursor XPTCursor;
+
+extern XPT_PUBLIC_API(bool)
+XPT_SkipStringInline(NotNull<XPTCursor*> cursor);
+
+extern XPT_PUBLIC_API(bool)
+XPT_DoCString(XPTArena *arena, NotNull<XPTCursor*> cursor, char **strp,
+ bool ignore = false);
+
+extern XPT_PUBLIC_API(bool)
+XPT_DoIID(NotNull<XPTCursor*> cursor, nsID *iidp);
+
+extern XPT_PUBLIC_API(bool)
+XPT_Do64(NotNull<XPTCursor*> cursor, int64_t *u64p);
+
+extern XPT_PUBLIC_API(bool)
+XPT_Do32(NotNull<XPTCursor*> cursor, uint32_t *u32p);
+
+extern XPT_PUBLIC_API(bool)
+XPT_Do16(NotNull<XPTCursor*> cursor, uint16_t *u16p);
+
+extern XPT_PUBLIC_API(bool)
+XPT_Do8(NotNull<XPTCursor*> cursor, uint8_t *u8p);
+
+extern XPT_PUBLIC_API(bool)
+XPT_DoHeader(XPTArena *arena, NotNull<XPTCursor*> cursor, XPTHeader **headerp);
+
+typedef enum {
+ XPT_HEADER = 0,
+ XPT_DATA = 1
+} XPTPool;
+
+struct XPTState {
+ uint32_t data_offset;
+ uint32_t next_cursor[2];
+ char *pool_data;
+ uint32_t pool_allocated;
+};
+
+struct XPTCursor {
+ XPTState *state;
+ XPTPool pool;
+ uint32_t offset;
+ uint8_t bits;
+};
+
+extern XPT_PUBLIC_API(void)
+XPT_InitXDRState(XPTState* state, char* data, uint32_t len);
+
+extern XPT_PUBLIC_API(bool)
+XPT_MakeCursor(XPTState *state, XPTPool pool, uint32_t len,
+ NotNull<XPTCursor*> cursor);
+
+extern XPT_PUBLIC_API(bool)
+XPT_SeekTo(NotNull<XPTCursor*> cursor, uint32_t offset);
+
+extern XPT_PUBLIC_API(void)
+XPT_SetDataOffset(XPTState *state, uint32_t data_offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __xpt_xdr_h__ */