mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1293362 - Part 4: Generate runtime bindings for calling xpcom methods from rust, r=froydnj
MozReview-Commit-ID: K37KyHkKsSl
This commit is contained in:
parent
3fe123346a
commit
c33284aec0
@ -28,6 +28,7 @@ idl_deps_dir := .deps
|
||||
|
||||
dist_idl_dir := $(DIST)/idl
|
||||
dist_include_dir := $(DIST)/include
|
||||
dist_xpcrs_dir := $(DIST)/xpcrs
|
||||
process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py
|
||||
|
||||
# TODO we should use py_action, but that would require extra directories to be
|
||||
@ -36,7 +37,7 @@ process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py
|
||||
$(REPORT_BUILD)
|
||||
$(PYTHON_PATH) $(PLY_INCLUDE) -I$(topsrcdir)/xpcom/idl-parser -I$(DEPTH)/xpcom/idl-parser/xpidl \
|
||||
$(process_py) --cache-dir $(DEPTH)/xpcom/idl-parser/xpidl --depsdir $(idl_deps_dir) \
|
||||
$(dist_idl_dir) $(dist_include_dir) $(@D) $(libxul_sdk_includes) \
|
||||
$(dist_idl_dir) $(dist_include_dir) $(dist_xpcrs_dir) $(@D) $(libxul_sdk_includes) \
|
||||
$(basename $(notdir $@)) $($(basename $(notdir $@))_deps)
|
||||
# When some IDL is added or removed, if the actual IDL file was already, or
|
||||
# still is, in the tree, simple dependencies can't detect that the XPT needs
|
||||
@ -76,7 +77,7 @@ ifdef COMPILE_ENVIRONMENT
|
||||
xpidl:: $(xpt_files) $(chrome_manifests) $(interfaces_manifests)
|
||||
endif
|
||||
|
||||
$(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir))
|
||||
$(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir) $(dist_xpcrs_dir))
|
||||
|
||||
-include $(depends_files)
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "nsISupports.h"
|
||||
#include "Units.h"
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_ISCROLLOBSERVER_IID \
|
||||
{ 0xaa5026eb, 0x2f88, 0x4026, \
|
||||
{ 0xa4, 0x6b, 0xf4, 0x59, 0x6b, 0x4e, 0xdf, 0x00 } }
|
||||
|
@ -41,6 +41,7 @@ enum nsLinkState {
|
||||
};
|
||||
|
||||
// IID for the nsIContent interface
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_ICONTENT_IID \
|
||||
{ 0x8e1bab9d, 0x8815, 0x4d2c, \
|
||||
{ 0xa2, 0x4d, 0x7a, 0xba, 0x52, 0x39, 0xdc, 0x22 } }
|
||||
|
@ -182,6 +182,7 @@ enum class CallerType : uint32_t;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_IDOCUMENT_IID \
|
||||
{ 0xce1f7627, 0x7109, 0x4977, \
|
||||
{ 0xba, 0x77, 0x49, 0x0f, 0xfd, 0xe0, 0x7a, 0xaa } }
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nsTArray.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_IGLOBALOBJECT_IID \
|
||||
{ 0x11afa8be, 0xd997, 0x4e07, \
|
||||
{ 0xa6, 0xa3, 0x6f, 0x87, 0x2e, 0xc3, 0xee, 0x7f } }
|
||||
|
@ -279,6 +279,7 @@ private:
|
||||
#define DOM_USER_DATA 1
|
||||
|
||||
// IID for the nsINode interface
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_INODE_IID \
|
||||
{ 0x70ba4547, 0x7699, 0x44fc, \
|
||||
{ 0xb3, 0x20, 0x52, 0xdb, 0xe3, 0xd1, 0xf9, 0x0a } }
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
class nsIScriptGlobalObject;
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_ISCRIPTCONTEXT_IID \
|
||||
{ 0x54cbe9cf, 0x7282, 0x421a, \
|
||||
{ 0x91, 0x6f, 0xd0, 0x70, 0x73, 0xde, 0xb8, 0xc0 } }
|
||||
|
@ -32,6 +32,7 @@ NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
|
||||
nsEventStatus *aStatus);
|
||||
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_ISCRIPTGLOBALOBJECT_IID \
|
||||
{ 0x876f83bd, 0x6314, 0x460a, \
|
||||
{ 0xa0, 0x45, 0x1c, 0x8f, 0x46, 0x2f, 0xb8, 0xe1 } }
|
||||
|
@ -123,10 +123,12 @@ enum class LargeAllocStatus : uint8_t
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_PIDOMWINDOWINNER_IID \
|
||||
{ 0x775dabc9, 0x8f43, 0x4277, \
|
||||
{ 0x9a, 0xdb, 0xf1, 0x99, 0x0d, 0x77, 0xcf, 0xfb } }
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_PIDOMWINDOWOUTER_IID \
|
||||
{ 0x769693d4, 0xb009, 0x4fe2, \
|
||||
{ 0xaf, 0x18, 0x7d, 0xc8, 0xdf, 0x74, 0x96, 0xdf } }
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
class nsIDocument;
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_NSICONSOLEREPORTCOLLECTOR_IID \
|
||||
{0xdd98a481, 0xd2c4, 0x4203, {0x8d, 0xfa, 0x85, 0xbf, 0xd7, 0xdc, 0xd7, 0x05}}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_ISCRIPTELEMENT_IID \
|
||||
{ 0xe60fca9b, 0x1b96, 0x4e4e, \
|
||||
{ 0xa9, 0xb4, 0xdc, 0x98, 0x4f, 0x88, 0x3f, 0x9c } }
|
||||
|
@ -17,6 +17,7 @@ from io import BytesIO
|
||||
|
||||
from buildconfig import topsrcdir
|
||||
from xpidl.header import print_header
|
||||
from xpidl.rust import print_rust_bindings
|
||||
from xpidl.typelib import write_typelib
|
||||
from xpidl.xpidl import IDLParser
|
||||
from xpt import xpt_link
|
||||
@ -26,7 +27,8 @@ from mozbuild.pythonutil import iter_modules_in_path
|
||||
from mozbuild.util import FileAvoidWrite
|
||||
|
||||
|
||||
def process(input_dir, inc_paths, cache_dir, header_dir, xpt_dir, deps_dir, module, stems):
|
||||
def process(input_dir, inc_paths, cache_dir, header_dir, xpcrs_dir,
|
||||
xpt_dir, deps_dir, module, stems):
|
||||
p = IDLParser(outputdir=cache_dir)
|
||||
|
||||
xpts = {}
|
||||
@ -45,6 +47,7 @@ def process(input_dir, inc_paths, cache_dir, header_dir, xpt_dir, deps_dir, modu
|
||||
idl.resolve([input_dir] + inc_paths, p)
|
||||
|
||||
header_path = os.path.join(header_dir, '%s.h' % stem)
|
||||
rs_rt_path = os.path.join(xpcrs_dir, 'rt', '%s.rs' % stem)
|
||||
|
||||
xpt = BytesIO()
|
||||
write_typelib(idl, xpt, path)
|
||||
@ -56,6 +59,9 @@ def process(input_dir, inc_paths, cache_dir, header_dir, xpt_dir, deps_dir, modu
|
||||
with FileAvoidWrite(header_path) as fh:
|
||||
print_header(idl, fh, path)
|
||||
|
||||
with FileAvoidWrite(rs_rt_path) as fh:
|
||||
print_rust_bindings(idl, fh, path)
|
||||
|
||||
# TODO use FileAvoidWrite once it supports binary mode.
|
||||
xpt_path = os.path.join(xpt_dir, '%s.xpt' % module)
|
||||
xpt_link(xpts.values()).write(xpt_path)
|
||||
@ -77,6 +83,8 @@ def main(argv):
|
||||
help='Directory in which to find source .idl files.')
|
||||
parser.add_argument('headerdir',
|
||||
help='Directory in which to write header files.')
|
||||
parser.add_argument('xpcrsdir',
|
||||
help='Directory in which to write rust xpcom binding files.')
|
||||
parser.add_argument('xptdir',
|
||||
help='Directory in which to write xpt file.')
|
||||
parser.add_argument('module',
|
||||
@ -88,7 +96,7 @@ def main(argv):
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
process(args.inputdir, args.incpath, args.cache_dir, args.headerdir,
|
||||
args.xptdir, args.depsdir, args.module, args.idls)
|
||||
args.xpcrsdir, args.xptdir, args.depsdir, args.module, args.idls)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
|
@ -327,6 +327,7 @@ class CommonBackend(BuildBackend):
|
||||
|
||||
def consume_finished(self):
|
||||
if len(self._idl_manager.idls):
|
||||
self._write_rust_xpidl_summary(self._idl_manager)
|
||||
self._handle_idl_manager(self._idl_manager)
|
||||
self._handle_generated_sources(mozpath.join(self.environment.topobjdir, 'dist/include/%s.h' % idl['root']) for idl in self._idl_manager.idls.values())
|
||||
|
||||
@ -556,3 +557,15 @@ class CommonBackend(BuildBackend):
|
||||
m.replace('%', mozpath.basename(jarinfo.name) + '/'))
|
||||
self.consume_object(ChromeManifestEntry(
|
||||
jar_context, '%s.manifest' % jarinfo.name, entry))
|
||||
|
||||
def _write_rust_xpidl_summary(self, manager):
|
||||
"""Write out a rust file which includes the generated xpcom rust modules"""
|
||||
topobjdir = self.environment.topobjdir
|
||||
|
||||
include_tmpl = "include!(concat!(env!(\"MOZ_TOPOBJDIR\"), \"/dist/xpcrs/%s/%s.rs\"))"
|
||||
|
||||
with self._write_file(mozpath.join(topobjdir, 'dist', 'xpcrs', 'rt', 'all.rs')) as fh:
|
||||
fh.write("// THIS FILE IS GENERATED - DO NOT EDIT\n\n")
|
||||
for idl in manager.idls.values():
|
||||
fh.write(include_tmpl % ("rt", idl['root']))
|
||||
fh.write(";\n")
|
||||
|
@ -150,6 +150,7 @@ typedef void* nsNativeWidget;
|
||||
#define NS_PRESENTATION_SURFACE 102
|
||||
#endif
|
||||
|
||||
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
|
||||
#define NS_IWIDGET_IID \
|
||||
{ 0x06396bf6, 0x2dd8, 0x45e5, \
|
||||
{ 0xac, 0x45, 0x75, 0x26, 0x53, 0xb1, 0xc9, 0x80 } }
|
||||
|
557
xpcom/idl-parser/xpidl/rust.py
Normal file
557
xpcom/idl-parser/xpidl/rust.py
Normal file
@ -0,0 +1,557 @@
|
||||
# rust.py - Generate rust bindings from IDL.
|
||||
#
|
||||
# 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/.
|
||||
|
||||
"""Print a runtime Rust bindings file for the IDL file specified"""
|
||||
|
||||
# --- Safety Hazards ---
|
||||
|
||||
# We currently don't generate some bindings for some IDL methods in rust code,
|
||||
# due to there being ABI safety hazards if we were to do so. This is the
|
||||
# documentation for the reasons why we don't generate certain types of bindings,
|
||||
# so that we don't accidentally start generating them in the future.
|
||||
|
||||
# notxpcom methods return their results directly by value. The x86 windows
|
||||
# stdcall ABI returns aggregates by value differently for methods than
|
||||
# functions, and rust only exposes the function ABI, so that's the one we're
|
||||
# using. The correct ABI can be emulated for notxpcom methods returning
|
||||
# aggregates by passing an &mut ReturnType parameter as the second parameter.
|
||||
# This strategy is used by the winapi-rs crate.
|
||||
# https://github.com/retep998/winapi-rs/blob/7338a5216a6a7abeefcc6bb1bc34381c81d3e247/src/macros.rs#L220-L231
|
||||
#
|
||||
# Right now we can generate code for notxpcom methods, as we don't support
|
||||
# passing aggregates by value over these APIs ever (the types which are allowed
|
||||
# in xpidl.py shouldn't include any aggregates), so the code is correct. In the
|
||||
# future if we want to start supporting returning aggregates by value, we will
|
||||
# need to use a workaround such as the one used by winapi.rs.
|
||||
|
||||
# nostdcall methods on x86 windows will use the thiscall ABI, which is not
|
||||
# stable in rust right now, so we cannot generate bindings to them.
|
||||
|
||||
# In general, passing C++ objects by value over the C ABI is not a good idea,
|
||||
# and when possible we should avoid doing so. We don't generate bindings for
|
||||
# these methods here currently.
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
import re
|
||||
import xpidl
|
||||
|
||||
|
||||
class AutoIndent(object):
|
||||
"""A small autoindenting wrapper around a fd.
|
||||
Used to make the code output more readable."""
|
||||
|
||||
def __init__(self, fd):
|
||||
self.fd = fd
|
||||
self.indent = 0
|
||||
|
||||
def write(self, string):
|
||||
"""A smart write function which automatically adjusts the
|
||||
indentation of each line as it is written by counting braces"""
|
||||
for s in string.split('\n'):
|
||||
s = s.strip()
|
||||
indent = self.indent
|
||||
if len(s) == 0:
|
||||
indent = 0
|
||||
elif s[0] == '}':
|
||||
indent -= 1
|
||||
|
||||
self.fd.write(" " * indent + s + "\n")
|
||||
for c in s:
|
||||
if c == '(' or c == '{' or c == '[':
|
||||
self.indent += 1
|
||||
elif c == ')' or c == '}' or c == ']':
|
||||
self.indent -= 1
|
||||
|
||||
|
||||
def rustSanitize(s):
|
||||
keywords = [
|
||||
"abstract", "alignof", "as", "become", "box",
|
||||
"break", "const", "continue", "crate", "do",
|
||||
"else", "enum", "extern", "false", "final",
|
||||
"fn", "for", "if", "impl", "in",
|
||||
"let", "loop", "macro", "match", "mod",
|
||||
"move", "mut", "offsetof", "override", "priv",
|
||||
"proc", "pub", "pure", "ref", "return",
|
||||
"Self", "self", "sizeof", "static", "struct",
|
||||
"super", "trait", "true", "type", "typeof",
|
||||
"unsafe", "unsized", "use", "virtual", "where",
|
||||
"while", "yield"
|
||||
]
|
||||
if s in keywords:
|
||||
return s + "_"
|
||||
return s
|
||||
|
||||
|
||||
# printdoccomments = False
|
||||
printdoccomments = True
|
||||
|
||||
if printdoccomments:
|
||||
def printComments(fd, clist, indent):
|
||||
fd.write("%s%s" % (indent, doccomments(clist)))
|
||||
|
||||
def doccomments(clist):
|
||||
if len(clist) == 0:
|
||||
return ""
|
||||
s = "/// ```text"
|
||||
for c in clist:
|
||||
for cc in c.splitlines():
|
||||
s += "\n/// " + cc
|
||||
s += "\n/// ```\n///\n"
|
||||
return s
|
||||
|
||||
else:
|
||||
def printComments(fd, clist, indent):
|
||||
pass
|
||||
|
||||
def doccomments(clist):
|
||||
return ""
|
||||
|
||||
|
||||
def firstCap(str):
|
||||
return str[0].upper() + str[1:]
|
||||
|
||||
|
||||
# Attribute VTable Methods
|
||||
def attributeNativeName(a, getter):
|
||||
binaryname = rustSanitize(a.binaryname if a.binaryname else firstCap(a.name))
|
||||
return "%s%s" % ('Get' if getter else 'Set', binaryname)
|
||||
|
||||
|
||||
def attributeParamName(a):
|
||||
return "a" + firstCap(a.name)
|
||||
|
||||
|
||||
def attributeRawParamList(iface, a, getter):
|
||||
l = [(attributeParamName(a),
|
||||
a.realtype.rustType('out' if getter else 'in'))]
|
||||
if a.implicit_jscontext:
|
||||
raise xpidl.RustNoncompat("jscontext is unsupported")
|
||||
if a.nostdcall:
|
||||
raise xpidl.RustNoncompat("nostdcall is unsupported")
|
||||
return l
|
||||
|
||||
|
||||
def attributeParamList(iface, a, getter):
|
||||
l = ["this: *const " + iface.name]
|
||||
l += ["%s: %s" % x for x in attributeRawParamList(iface, a, getter)]
|
||||
return ", ".join(l)
|
||||
|
||||
|
||||
def attrAsVTableEntry(iface, m, getter):
|
||||
try:
|
||||
return "pub %s: unsafe extern \"system\" fn (%s) -> nsresult" % \
|
||||
(attributeNativeName(m, getter),
|
||||
attributeParamList(iface, m, getter))
|
||||
except xpidl.RustNoncompat as reason:
|
||||
return """\
|
||||
/// Unable to generate binding because `%s`
|
||||
pub %s: *const ::libc::c_void""" % (reason, attributeNativeName(m, getter))
|
||||
|
||||
|
||||
# Method VTable generation functions
|
||||
def methodNativeName(m):
|
||||
binaryname = m.binaryname is not None and m.binaryname or firstCap(m.name)
|
||||
return rustSanitize(binaryname)
|
||||
|
||||
|
||||
def methodReturnType(m):
|
||||
if m.notxpcom:
|
||||
return m.realtype.rustType('in').strip()
|
||||
return "nsresult"
|
||||
|
||||
|
||||
def methodRawParamList(iface, m):
|
||||
l = [(rustSanitize(p.name), p.rustType()) for p in m.params]
|
||||
|
||||
if m.implicit_jscontext:
|
||||
raise xpidl.RustNoncompat("jscontext is unsupported")
|
||||
|
||||
if m.optional_argc:
|
||||
raise xpidl.RustNoncompat("optional_argc is unsupported")
|
||||
|
||||
if m.nostdcall:
|
||||
raise xpidl.RustNoncompat("nostdcall is unsupported")
|
||||
|
||||
if not m.notxpcom and m.realtype.name != 'void':
|
||||
l.append(("_retval", m.realtype.rustType('out')))
|
||||
|
||||
return l
|
||||
|
||||
|
||||
def methodParamList(iface, m):
|
||||
l = ["this: *const %s" % iface.name]
|
||||
l += ["%s: %s" % x for x in methodRawParamList(iface, m)]
|
||||
return ", ".join(l)
|
||||
|
||||
|
||||
def methodAsVTableEntry(iface, m):
|
||||
try:
|
||||
return "pub %s: unsafe extern \"system\" fn (%s) -> %s" % \
|
||||
(methodNativeName(m),
|
||||
methodParamList(iface, m),
|
||||
methodReturnType(m))
|
||||
except xpidl.RustNoncompat as reason:
|
||||
return """\
|
||||
/// Unable to generate binding because `%s`
|
||||
pub %s: *const ::libc::c_void""" % (reason, methodNativeName(m))
|
||||
|
||||
|
||||
method_impl_tmpl = """\
|
||||
#[inline]
|
||||
pub unsafe fn %(name)s(&self, %(params)s) -> %(ret_ty)s {
|
||||
((*self.vtable).%(name)s)(self, %(args)s)
|
||||
}
|
||||
"""
|
||||
|
||||
def methodAsWrapper(iface, m):
|
||||
try:
|
||||
param_list = methodRawParamList(iface, m)
|
||||
params = ["%s: %s" % x for x in param_list]
|
||||
args = [x[0] for x in param_list]
|
||||
|
||||
return method_impl_tmpl % {
|
||||
'name': methodNativeName(m),
|
||||
'params': ', '.join(params),
|
||||
'ret_ty': methodReturnType(m),
|
||||
'args': ', '.join(args),
|
||||
}
|
||||
except xpidl.RustNoncompat:
|
||||
# Dummy field for the doc comments to attach to.
|
||||
# Private so that it's not shown in rustdoc.
|
||||
return "const _%s: () = ();" % methodNativeName(m)
|
||||
|
||||
|
||||
infallible_impl_tmpl = """\
|
||||
#[inline]
|
||||
pub unsafe fn %(name)s(&self) -> %(realtype)s {
|
||||
let mut result = <%(realtype)s as ::std::default::Default>::default();
|
||||
let _rv = ((*self.vtable).%(name)s)(self, &mut result);
|
||||
debug_assert!(::nserror::NsresultExt::succeeded(_rv));
|
||||
result
|
||||
}
|
||||
"""
|
||||
|
||||
def attrAsWrapper(iface, m, getter):
|
||||
try:
|
||||
if m.implicit_jscontext:
|
||||
raise xpidl.RustNoncompat("jscontext is unsupported")
|
||||
|
||||
if m.nostdcall:
|
||||
raise xpidl.RustNoncompat("nostdcall is unsupported")
|
||||
|
||||
name = attributeParamName(m)
|
||||
|
||||
if getter and m.infallible:
|
||||
return infallible_impl_tmpl % {
|
||||
'name': attributeNativeName(m, getter),
|
||||
'realtype': m.realtype.rustType('in'),
|
||||
}
|
||||
|
||||
rust_type = m.realtype.rustType('out' if getter else 'in')
|
||||
return method_impl_tmpl % {
|
||||
'name': attributeNativeName(m, getter),
|
||||
'params': name + ': ' + rust_type,
|
||||
'ret_ty': 'nsresult',
|
||||
'args': name,
|
||||
}
|
||||
|
||||
except xpidl.RustNoncompat:
|
||||
# Dummy field for the doc comments to attach to.
|
||||
# Private so that it's not shown in rustdoc.
|
||||
return "const _%s: () = ();" % attributeNativeName(m, getter)
|
||||
|
||||
|
||||
header = """\
|
||||
//
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED FROM %(filename)s
|
||||
//
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def idl_basename(f):
|
||||
"""returns the base name of a file with the last extension stripped"""
|
||||
return os.path.splitext(os.path.basename(f))[0]
|
||||
|
||||
|
||||
def print_rust_bindings(idl, fd, filename):
|
||||
fd = AutoIndent(fd)
|
||||
|
||||
fd.write(header % {'filename': filename})
|
||||
|
||||
# All of the idl files will be included into the same rust module, as we
|
||||
# can't do forward declarations. Because of this, we want to ignore all
|
||||
# import statements
|
||||
|
||||
for p in idl.productions:
|
||||
if p.kind == 'include' or p.kind == 'cdata' or p.kind == 'forward':
|
||||
continue
|
||||
|
||||
if p.kind == 'interface':
|
||||
write_interface(p, fd)
|
||||
continue
|
||||
|
||||
if p.kind == 'typedef':
|
||||
try:
|
||||
# We have to skip the typedef of bool to bool (it doesn't make any sense anyways)
|
||||
if p.name == "bool":
|
||||
continue
|
||||
|
||||
if printdoccomments:
|
||||
fd.write("/// `typedef %s %s;`\n///\n" %
|
||||
(p.realtype.nativeType('in'), p.name))
|
||||
fd.write(doccomments(p.doccomments))
|
||||
fd.write("pub type %s = %s;\n\n" % (p.name, p.realtype.rustType('in')))
|
||||
except xpidl.RustNoncompat as reason:
|
||||
fd.write("/* unable to generate %s typedef because `%s` */\n\n" %
|
||||
(p.name, reason))
|
||||
|
||||
|
||||
base_vtable_tmpl = """
|
||||
/// We need to include the members from the base interface's vtable at the start
|
||||
/// of the VTable definition.
|
||||
pub __base: %sVTable,
|
||||
|
||||
"""
|
||||
|
||||
|
||||
vtable_tmpl = """\
|
||||
// This struct represents the interface's VTable. A pointer to a statically
|
||||
// allocated version of this struct is at the beginning of every %(name)s
|
||||
// object. It contains one pointer field for each method in the interface. In
|
||||
// the case where we can't generate a binding for a method, we include a void
|
||||
// pointer.
|
||||
#[doc(hidden)]
|
||||
#[repr(C)]
|
||||
pub struct %(name)sVTable {%(base)s%(entries)s}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# NOTE: This template is not generated for nsISupports, as it has no base interfaces.
|
||||
deref_tmpl = """\
|
||||
// Every interface struct type implements `Deref` to its base interface. This
|
||||
// causes methods on the base interfaces to be directly avaliable on the
|
||||
// object. For example, you can call `.AddRef` or `.QueryInterface` directly
|
||||
// on any interface which inherits from `nsISupports`.
|
||||
impl ::std::ops::Deref for %(name)s {
|
||||
type Target = %(base)s;
|
||||
#[inline]
|
||||
fn deref(&self) -> &%(base)s {
|
||||
unsafe {
|
||||
::std::mem::transmute(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we can use .coerce() to cast to our base types as well. Any type which
|
||||
// our base interface can coerce from should be coercable from us as well.
|
||||
impl<T: %(base)sCoerce> %(name)sCoerce for T {
|
||||
#[inline]
|
||||
fn coerce_from(v: &%(name)s) -> &Self {
|
||||
T::coerce_from(v)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
struct_tmpl = """\
|
||||
// The actual type definition for the interface. This struct has methods
|
||||
// declared on it which will call through its vtable. You never want to pass
|
||||
// this type around by value, always pass it behind a reference.
|
||||
|
||||
#[repr(C)]
|
||||
pub struct %(name)s {
|
||||
vtable: *const %(name)sVTable,
|
||||
|
||||
/// This field is a phantomdata to ensure that the VTable type and any
|
||||
/// struct containing it is not safe to send across threads, as XPCOM is
|
||||
/// generally not threadsafe.
|
||||
///
|
||||
/// XPCOM interfaces in general are not safe to send across threads.
|
||||
__nosync: ::std::marker::PhantomData<::std::rc::Rc<u8>>,
|
||||
}
|
||||
|
||||
// Implementing XpCom for an interface exposes its IID, which allows for easy
|
||||
// use of the `.query_interface<T>` helper method. This also defines that
|
||||
// method for %(name)s.
|
||||
unsafe impl XpCom for %(name)s {
|
||||
const IID: nsIID = nsID(0x%(m0)s, 0x%(m1)s, 0x%(m2)s,
|
||||
[%(m3joined)s]);
|
||||
}
|
||||
|
||||
// We need to implement the RefCounted trait so we can be used with `RefPtr`.
|
||||
// This trait teaches `RefPtr` how to manage our memory.
|
||||
unsafe impl RefCounted for %(name)s {
|
||||
#[inline]
|
||||
unsafe fn addref(&self) {
|
||||
self.AddRef();
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn release(&self) {
|
||||
self.Release();
|
||||
}
|
||||
}
|
||||
|
||||
// This trait is implemented on all types which can be coerced to from %(name)s.
|
||||
// It is used in the implementation of `fn coerce<T>`. We hide it from the
|
||||
// documentation, because it clutters it up a lot.
|
||||
#[doc(hidden)]
|
||||
pub trait %(name)sCoerce {
|
||||
/// Cheaply cast a value of this type from a `%(name)s`.
|
||||
fn coerce_from(v: &%(name)s) -> &Self;
|
||||
}
|
||||
|
||||
// The trivial implementation: We can obviously coerce ourselves to ourselves.
|
||||
impl %(name)sCoerce for %(name)s {
|
||||
#[inline]
|
||||
fn coerce_from(v: &%(name)s) -> &Self {
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl %(name)s {
|
||||
/// Cast this `%(name)s` to one of its base interfaces.
|
||||
#[inline]
|
||||
pub fn coerce<T: %(name)sCoerce>(&self) -> &T {
|
||||
T::coerce_from(self)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
wrapper_tmpl = """\
|
||||
// The implementations of the function wrappers which are exposed to rust code.
|
||||
// Call these methods rather than manually calling through the VTable struct.
|
||||
impl %(name)s {
|
||||
%(consts)s
|
||||
%(methods)s
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
vtable_entry_tmpl = """\
|
||||
/* %(idl)s */
|
||||
%(entry)s,
|
||||
"""
|
||||
|
||||
|
||||
const_wrapper_tmpl = """\
|
||||
%(docs)s
|
||||
pub const %(name)s: i64 = %(val)s;
|
||||
"""
|
||||
|
||||
|
||||
method_wrapper_tmpl = """\
|
||||
%(docs)s
|
||||
/// `%(idl)s`
|
||||
%(wrapper)s
|
||||
"""
|
||||
|
||||
|
||||
uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})-
|
||||
(?P<m1>[a-f0-9]{4})-
|
||||
(?P<m2>[a-f0-9]{4})-
|
||||
(?P<m3>[a-f0-9]{4})-
|
||||
(?P<m4>[a-f0-9]{12})$""", re.X)
|
||||
|
||||
|
||||
def write_interface(iface, fd):
|
||||
if iface.namemap is None:
|
||||
raise Exception("Interface was not resolved.")
|
||||
|
||||
# if we see a base class-less type other than nsISupports, we just need
|
||||
# to discard anything else about it other than its constants.
|
||||
if iface.base is None and iface.name != "nsISupports":
|
||||
assert len([m for m in iface.members
|
||||
if type(m) == xpidl.Attribute or type(m) == xpidl.Method]) == 0
|
||||
return
|
||||
|
||||
# Extract the UUID's information so that it can be written into the struct definition
|
||||
names = uuid_decoder.match(iface.attributes.uuid).groupdict()
|
||||
m3str = names['m3'] + names['m4']
|
||||
names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
|
||||
names['name'] = iface.name
|
||||
|
||||
if printdoccomments:
|
||||
if iface.base is not None:
|
||||
fd.write("/// `interface %s : %s`\n///\n" %
|
||||
(iface.name, iface.base))
|
||||
else:
|
||||
fd.write("/// `interface %s`\n///\n" %
|
||||
iface.name)
|
||||
printComments(fd, iface.doccomments, '')
|
||||
fd.write(struct_tmpl % names)
|
||||
|
||||
if iface.base is not None:
|
||||
fd.write(deref_tmpl % {
|
||||
'name': iface.name,
|
||||
'base': iface.base,
|
||||
})
|
||||
|
||||
entries = []
|
||||
for member in iface.members:
|
||||
if type(member) == xpidl.Attribute:
|
||||
entries.append(vtable_entry_tmpl % {
|
||||
'idl': member.toIDL(),
|
||||
'entry': attrAsVTableEntry(iface, member, True),
|
||||
})
|
||||
if not member.readonly:
|
||||
entries.append(vtable_entry_tmpl % {
|
||||
'idl': member.toIDL(),
|
||||
'entry': attrAsVTableEntry(iface, member, False),
|
||||
})
|
||||
|
||||
elif type(member) == xpidl.Method:
|
||||
entries.append(vtable_entry_tmpl % {
|
||||
'idl': member.toIDL(),
|
||||
'entry': methodAsVTableEntry(iface, member),
|
||||
})
|
||||
|
||||
fd.write(vtable_tmpl % {
|
||||
'name': iface.name,
|
||||
'base': base_vtable_tmpl % iface.base if iface.base is not None else "",
|
||||
'entries': '\n'.join(entries),
|
||||
})
|
||||
|
||||
# Get all of the constants
|
||||
consts = []
|
||||
for member in iface.members:
|
||||
if type(member) == xpidl.ConstMember:
|
||||
consts.append(const_wrapper_tmpl % {
|
||||
'docs': doccomments(member.doccomments),
|
||||
'name': member.name,
|
||||
'val': member.getValue(),
|
||||
})
|
||||
|
||||
methods = []
|
||||
for member in iface.members:
|
||||
if type(member) == xpidl.Attribute:
|
||||
methods.append(method_wrapper_tmpl % {
|
||||
'docs': doccomments(member.doccomments),
|
||||
'idl': member.toIDL(),
|
||||
'wrapper': attrAsWrapper(iface, member, True),
|
||||
})
|
||||
if not member.readonly:
|
||||
methods.append(method_wrapper_tmpl % {
|
||||
'docs': doccomments(member.doccomments),
|
||||
'idl': member.toIDL(),
|
||||
'wrapper': attrAsWrapper(iface, member, False),
|
||||
})
|
||||
|
||||
elif type(member) == xpidl.Method:
|
||||
methods.append(method_wrapper_tmpl % {
|
||||
'docs': doccomments(member.doccomments),
|
||||
'idl': member.toIDL(),
|
||||
'wrapper': methodAsWrapper(iface, member),
|
||||
})
|
||||
|
||||
fd.write(wrapper_tmpl % {
|
||||
'name': iface.name,
|
||||
'consts': '\n'.join(consts),
|
||||
'methods': '\n'.join(methods),
|
||||
})
|
48
xpcom/rust/xpcom/src/base.rs
Normal file
48
xpcom/rust/xpcom/src/base.rs
Normal file
@ -0,0 +1,48 @@
|
||||
/* 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/. */
|
||||
|
||||
use {
|
||||
RefCounted,
|
||||
RefPtr,
|
||||
GetterAddrefs
|
||||
};
|
||||
use interfaces::nsISupports;
|
||||
use nserror::NsresultExt;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
/// A "unique identifier". This is modeled after OSF DCE UUIDs.
|
||||
pub struct nsID(pub u32, pub u16, pub u16, pub [u8; 8]);
|
||||
|
||||
/// Interface IDs
|
||||
pub type nsIID = nsID;
|
||||
/// Class IDs
|
||||
pub type nsCID = nsID;
|
||||
|
||||
/// A type which implements XpCom must follow the following rules:
|
||||
///
|
||||
/// * It must be a legal XPCOM interface.
|
||||
/// * The result of a QueryInterface or similar call, passing IID, must return a
|
||||
/// valid reference to an object of the given type.
|
||||
/// * It must be valid to cast a &self reference to a &nsISupports reference.
|
||||
pub unsafe trait XpCom : RefCounted {
|
||||
const IID: nsIID;
|
||||
|
||||
/// Perform a QueryInterface call on this object, attempting to dynamically
|
||||
/// cast it to the requested interface type. Returns Some(RefPtr<T>) if the
|
||||
/// cast succeeded, and None otherwise.
|
||||
fn query_interface<T: XpCom>(&self) -> Option<RefPtr<T>> {
|
||||
let mut ga = GetterAddrefs::<T>::new();
|
||||
unsafe {
|
||||
if (*(self as *const Self as *const nsISupports)).QueryInterface(
|
||||
&T::IID,
|
||||
ga.void_ptr(),
|
||||
).succeeded() {
|
||||
ga.refptr()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
xpcom/rust/xpcom/src/interfaces/idl.rs
Normal file
12
xpcom/rust/xpcom/src/interfaces/idl.rs
Normal file
@ -0,0 +1,12 @@
|
||||
/* 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/. */
|
||||
|
||||
#![allow(bad_style)]
|
||||
|
||||
use *;
|
||||
use interfaces::*;
|
||||
|
||||
// NOTE: This file contains a series of `include!()` invocations, defining all
|
||||
// idl interfaces directly within this module.
|
||||
include!(concat!(env!("MOZ_TOPOBJDIR"), "/dist/xpcrs/rt/all.rs"));
|
31
xpcom/rust/xpcom/src/interfaces/mod.rs
Normal file
31
xpcom/rust/xpcom/src/interfaces/mod.rs
Normal file
@ -0,0 +1,31 @@
|
||||
/* 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/. */
|
||||
|
||||
//! This module contains the xpcom interfaces exposed to rust code.
|
||||
//!
|
||||
//! The items in this module come in a few flavours:
|
||||
//!
|
||||
//! 1. `nsI*`: These are the types for XPCOM interfaces. They should always be
|
||||
//! passed behind a reference, pointer, or `RefPtr`. They may be coerced to
|
||||
//! their base interfaces using the `coerce` method.
|
||||
//!
|
||||
//! 2. `nsI*Coerce`: These traits provide the implementation mechanics for the
|
||||
//! `coerce` method, and can usually be ignored. *These traits are hidden in
|
||||
//! rustdoc*
|
||||
//!
|
||||
//! 3. `nsI*VTable`: These structs are the vtable definitions for each type.
|
||||
//! They contain the base interface's vtable, followed by pointers for each
|
||||
//! of the vtable's methods. If direct access is needed, a `*const nsI*` can
|
||||
//! be safely transmuted to a `*const nsI*VTable`. *These structs are hidden
|
||||
//! in rustdoc*
|
||||
//!
|
||||
//! 4. Typedefs used in idl file definitions.
|
||||
|
||||
// Interfaces defined in .idl files
|
||||
mod idl;
|
||||
pub use self::idl::*;
|
||||
|
||||
// Other interfaces which are needed to compile
|
||||
mod nonidl;
|
||||
pub use self::nonidl::*;
|
110
xpcom/rust/xpcom/src/interfaces/nonidl.rs
Normal file
110
xpcom/rust/xpcom/src/interfaces/nonidl.rs
Normal file
@ -0,0 +1,110 @@
|
||||
/* 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/. */
|
||||
|
||||
//! This module contains definitions of interfaces which are used in idl files
|
||||
//! as forward declarations, but are not actually defined in an idl file.
|
||||
//!
|
||||
//! NOTE: The IIDs in these files must be kept in sync with the IDL definitions
|
||||
//! in the corresponding C++ files.
|
||||
|
||||
use nsID;
|
||||
|
||||
// XXX: This macro should have an option for a custom base interface instead of
|
||||
// nsISupports, such that nsIDocument can have nsINode as a base, etc. For now,
|
||||
// query_interface should be sufficient.
|
||||
macro_rules! nonidl {
|
||||
($name:ident, $iid:expr) => {
|
||||
/// This interface is referenced from idl files, but not defined in
|
||||
/// them. It exports no methods to rust code.
|
||||
#[repr(C)]
|
||||
pub struct $name {
|
||||
_vtable: *const $crate::interfaces::nsISupportsVTable,
|
||||
}
|
||||
|
||||
unsafe impl $crate::XpCom for $name {
|
||||
const IID: $crate::nsIID = $iid;
|
||||
}
|
||||
|
||||
unsafe impl $crate::RefCounted for $name {
|
||||
#[inline]
|
||||
unsafe fn addref(&self) {
|
||||
self.AddRef();
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn release(&self) {
|
||||
self.Release();
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for $name {
|
||||
type Target = $crate::interfaces::nsISupports;
|
||||
#[inline]
|
||||
fn deref(&self) -> &$crate::interfaces::nsISupports {
|
||||
unsafe {
|
||||
::std::mem::transmute(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must be kept in sync with nsIDocument.h
|
||||
nonidl!(nsIDocument,
|
||||
nsID(0xce1f7627, 0x7109, 0x4977,
|
||||
[0xba, 0x77, 0x49, 0x0f, 0xfd, 0xe0, 0x7a, 0xaa]));
|
||||
|
||||
// Must be kept in sync with nsINode.h
|
||||
nonidl!(nsINode,
|
||||
nsID(0x70ba4547, 0x7699, 0x44fc,
|
||||
[0xb3, 0x20, 0x52, 0xdb, 0xe3, 0xd1, 0xf9, 0x0a]));
|
||||
|
||||
// Must be kept in sync with nsIContent.h
|
||||
nonidl!(nsIContent,
|
||||
nsID(0x8e1bab9d, 0x8815, 0x4d2c,
|
||||
[0xa2, 0x4d, 0x7a, 0xba, 0x52, 0x39, 0xdc, 0x22]));
|
||||
|
||||
// Must be kept in sync with nsIConsoleReportCollector.h
|
||||
nonidl!(nsIConsoleReportCollector,
|
||||
nsID(0xdd98a481, 0xd2c4, 0x4203,
|
||||
[0x8d, 0xfa, 0x85, 0xbf, 0xd7, 0xdc, 0xd7, 0x05]));
|
||||
|
||||
// Must be kept in sync with nsIGlobalObject.h
|
||||
nonidl!(nsIGlobalObject,
|
||||
nsID(0x11afa8be, 0xd997, 0x4e07,
|
||||
[0xa6, 0xa3, 0x6f, 0x87, 0x2e, 0xc3, 0xee, 0x7f]));
|
||||
|
||||
// Must be kept in sync with nsIScriptElement.h
|
||||
nonidl!(nsIScriptElement,
|
||||
nsID(0xe60fca9b, 0x1b96, 0x4e4e,
|
||||
[0xa9, 0xb4, 0xdc, 0x98, 0x4f, 0x88, 0x3f, 0x9c]));
|
||||
|
||||
// Must be kept in sync with nsPIDOMWindow.h
|
||||
nonidl!(nsPIDOMWindowOuter,
|
||||
nsID(0x769693d4, 0xb009, 0x4fe2,
|
||||
[0xaf, 0x18, 0x7d, 0xc8, 0xdf, 0x74, 0x96, 0xdf]));
|
||||
|
||||
// Must be kept in sync with nsPIDOMWindow.h
|
||||
nonidl!(nsPIDOMWindowInner,
|
||||
nsID(0x775dabc9, 0x8f43, 0x4277,
|
||||
[0x9a, 0xdb, 0xf1, 0x99, 0x0d, 0x77, 0xcf, 0xfb]));
|
||||
|
||||
// Must be kept in sync with nsIScriptContext.h
|
||||
nonidl!(nsIScriptContext,
|
||||
nsID(0x54cbe9cf, 0x7282, 0x421a,
|
||||
[0x91, 0x6f, 0xd0, 0x70, 0x73, 0xde, 0xb8, 0xc0]));
|
||||
|
||||
// Must be kept in sync with nsIScriptGlobalObject.h
|
||||
nonidl!(nsIScriptGlobalObject,
|
||||
nsID(0x876f83bd, 0x6314, 0x460a,
|
||||
[0xa0, 0x45, 0x1c, 0x8f, 0x46, 0x2f, 0xb8, 0xe1]));
|
||||
|
||||
// Must be kept in sync with nsIScrollObserver.h
|
||||
nonidl!(nsIScrollObserver,
|
||||
nsID(0xaa5026eb, 0x2f88, 0x4026,
|
||||
[0xa4, 0x6b, 0xf4, 0x59, 0x6b, 0x4e, 0xdf, 0x00]));
|
||||
|
||||
// Must be kept in sync with nsIWidget.h
|
||||
nonidl!(nsIWidget,
|
||||
nsID(0x06396bf6, 0x2dd8, 0x45e5,
|
||||
[0xac, 0x45, 0x75, 0x26, 0x53, 0xb1, 0xc9, 0x80]));
|
@ -15,3 +15,13 @@ extern crate nserror;
|
||||
extern crate xpcom_macros;
|
||||
#[doc(hidden)]
|
||||
pub use xpcom_macros::*;
|
||||
|
||||
// Helper functions and data structures are exported in the root of the crate.
|
||||
mod base;
|
||||
pub use base::*;
|
||||
|
||||
mod refptr;
|
||||
pub use refptr::*;
|
||||
|
||||
// XPCOM interface definitions.
|
||||
pub mod interfaces;
|
||||
|
199
xpcom/rust/xpcom/src/refptr.rs
Normal file
199
xpcom/rust/xpcom/src/refptr.rs
Normal file
@ -0,0 +1,199 @@
|
||||
/* 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/. */
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::ops::Deref;
|
||||
use std::marker::PhantomData;
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::{self, AtomicUsize, Ordering};
|
||||
|
||||
use nserror::{NsresultExt, nsresult, NS_OK};
|
||||
|
||||
use libc;
|
||||
|
||||
/// A trait representing a type which can be reference counted invasively.
|
||||
/// The object is responsible for freeing its backing memory when its
|
||||
/// reference count reaches 0.
|
||||
pub unsafe trait RefCounted {
|
||||
/// Increment the reference count.
|
||||
unsafe fn addref(&self);
|
||||
/// Decrement the reference count, potentially freeing backing memory.
|
||||
unsafe fn release(&self);
|
||||
}
|
||||
|
||||
/// A smart pointer holding a RefCounted object. The object itself manages its
|
||||
/// own memory. RefPtr will invoke the addref and release methods at the
|
||||
/// appropriate times to facilitate the bookkeeping.
|
||||
pub struct RefPtr<T: RefCounted + 'static> {
|
||||
// We're going to cheat and store the internal reference as an &'static T
|
||||
// instead of an *const T or Shared<T>, because Shared and NonZero are
|
||||
// unstable, and we need to build on stable rust.
|
||||
// I believe that this is "safe enough", as this module is private and
|
||||
// no other module can read this reference.
|
||||
_ptr: &'static T,
|
||||
// As we aren't using Shared<T>, we need to add this phantomdata to
|
||||
// prevent unsoundness in dropck
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl <T: RefCounted + 'static> RefPtr<T> {
|
||||
/// Construct a new RefPtr from a reference to the refcounted object.
|
||||
#[inline]
|
||||
pub fn new(p: &T) -> RefPtr<T> {
|
||||
unsafe {
|
||||
p.addref();
|
||||
RefPtr {
|
||||
_ptr: mem::transmute(p),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a RefPtr from a raw pointer, addrefing it.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw(p: *const T) -> Option<RefPtr<T>> {
|
||||
if p.is_null() {
|
||||
return None;
|
||||
}
|
||||
(*p).addref();
|
||||
Some(RefPtr {
|
||||
_ptr: &*p,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a RefPtr from a raw pointer, without addrefing it.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw_dont_addref(p: *const T) -> Option<RefPtr<T>> {
|
||||
if p.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(RefPtr {
|
||||
_ptr: &*p,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Write this RefPtr's value into an outparameter.
|
||||
#[inline]
|
||||
pub unsafe fn forget(self, into: &mut *const T) {
|
||||
*into = &*self as *const T;
|
||||
mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: RefCounted + 'static> Deref for RefPtr<T> {
|
||||
type Target = T;
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
self._ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: RefCounted + 'static> Drop for RefPtr<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self._ptr.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: RefCounted + 'static> Clone for RefPtr<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> RefPtr<T> {
|
||||
RefPtr::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper struct for constructing `RefPtr<T>` from raw pointer outparameters.
|
||||
/// Holds a `*const T` internally which will be released if non null when
|
||||
/// destructed, and can be easily transformed into an `Option<RefPtr<T>>`.
|
||||
///
|
||||
/// It many cases it may be easier to use the `getter_addrefs` method.
|
||||
pub struct GetterAddrefs<T: RefCounted + 'static> {
|
||||
_ptr: *const T,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl <T: RefCounted + 'static> GetterAddrefs<T> {
|
||||
/// Create a `GetterAddrefs`, initializing it with the null pointer.
|
||||
#[inline]
|
||||
pub fn new() -> GetterAddrefs<T> {
|
||||
GetterAddrefs {
|
||||
_ptr: ptr::null(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the internal `*const T`. This method is unsafe,
|
||||
/// as the destructor of this class depends on the internal `*const T`
|
||||
/// being either a valid reference to a value of type `T`, or null.
|
||||
#[inline]
|
||||
pub unsafe fn ptr(&mut self) -> &mut *const T {
|
||||
&mut self._ptr
|
||||
}
|
||||
|
||||
/// Get a reference to the internal `*const T` as a `*mut libc::c_void`.
|
||||
/// This is useful to pass to functions like `GetInterface` which take a
|
||||
/// void pointer outparameter.
|
||||
#[inline]
|
||||
pub unsafe fn void_ptr(&mut self) -> *mut *mut libc::c_void {
|
||||
&mut self._ptr as *mut *const T as *mut *mut libc::c_void
|
||||
}
|
||||
|
||||
/// Transform this `GetterAddrefs` into an `Option<RefPtr<T>>`, without
|
||||
/// performing any addrefs or releases.
|
||||
#[inline]
|
||||
pub fn refptr(self) -> Option<RefPtr<T>> {
|
||||
let p = self._ptr;
|
||||
// Don't run the destructor because we don't want to release the stored
|
||||
// pointer.
|
||||
mem::forget(self);
|
||||
unsafe {
|
||||
RefPtr::from_raw_dont_addref(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: RefCounted + 'static> Drop for GetterAddrefs<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if !self._ptr.is_null() {
|
||||
unsafe {
|
||||
(*self._ptr).release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper method for calling XPCOM methods which return a reference counted
|
||||
/// value through an outparameter. Takes a lambda, which is called with a valid
|
||||
/// outparameter argument (`*mut *const T`), and returns a `nsresult`. Returns
|
||||
/// either a `RefPtr<T>` with the value returned from the outparameter, or a
|
||||
/// `nsresult`.
|
||||
///
|
||||
/// # NOTE:
|
||||
///
|
||||
/// Can return `Err(NS_OK)` if the call succeeded, but the outparameter was set
|
||||
/// to NULL.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```
|
||||
/// let x: Result<RefPtr<T>, nsresult> =
|
||||
/// getter_addrefs(|p| iosvc.NewURI(uri, ptr::null(), ptr::null(), p));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn getter_addrefs<T: RefCounted, F>(f: F) -> Result<RefPtr<T>, nsresult>
|
||||
where F: FnOnce(*mut *const T) -> nsresult
|
||||
{
|
||||
let mut ga = GetterAddrefs::<T>::new();
|
||||
let rv = f(unsafe { ga.ptr() });
|
||||
if rv.failed() {
|
||||
return Err(rv);
|
||||
}
|
||||
ga.refptr().ok_or(NS_OK)
|
||||
}
|
Loading…
Reference in New Issue
Block a user