Bug 1471726 - Part 1: Correct codegen for XPIDL arrays of JSVals, r=mccr8

This commit is contained in:
Nika Layzell 2018-07-06 16:47:22 -04:00
parent f4c55c8456
commit 345a0af828
3 changed files with 74 additions and 65 deletions

View File

@ -20,7 +20,7 @@ from ply import yacc
def nativeType(self, calltype): def nativeType(self, calltype):
'returns a string representation of the native type 'returns a string representation of the native type
calltype must be 'in', 'out', or 'inout' calltype must be 'in', 'out', 'inout', or 'element'
Interface members const/method/attribute conform to the following pattern: Interface members const/method/attribute conform to the following pattern:
@ -144,7 +144,7 @@ class Builtin(object):
else: else:
const = '' const = ''
return "%s%s %s" % (const, self.nativename, return "%s%s %s" % (const, self.nativename,
calltype != 'in' and '*' or '') '*' if 'out' in calltype else '')
def rustType(self, calltype, shared=False, const=False): def rustType(self, calltype, shared=False, const=False):
# We want to rewrite any *mut pointers to *const pointers if constness # We want to rewrite any *mut pointers to *const pointers if constness
@ -400,8 +400,7 @@ class Typedef(object):
return self.realtype.isScriptable() return self.realtype.isScriptable()
def nativeType(self, calltype): def nativeType(self, calltype):
return "%s %s" % (self.name, return "%s %s" % (self.name, '*' if 'out' in calltype else '')
calltype != 'in' and '*' or '')
def rustType(self, calltype): def rustType(self, calltype):
return "%s%s" % (calltype != 'in' and '*mut ' or '', return "%s%s" % (calltype != 'in' and '*mut ' or '',
@ -440,8 +439,7 @@ class Forward(object):
return True return True
def nativeType(self, calltype): def nativeType(self, calltype):
return "%s %s" % (self.name, return "%s *%s" % (self.name, '*' if 'out' in calltype else '')
calltype != 'in' and '* *' or '*')
def rustType(self, calltype): def rustType(self, calltype):
if rustBlacklistedForward(self.name): if rustBlacklistedForward(self.name):
@ -459,29 +457,19 @@ class Native(object):
modifier = None modifier = None
specialtype = None specialtype = None
# A tuple type here means that a custom value is used for each calltype:
# (in, out/inout, array element) respectively.
# A `None` here means that the written type should be used as-is.
specialtypes = { specialtypes = {
'nsid': None, 'nsid': None,
'domstring': 'nsAString', 'domstring': ('const nsAString&', 'nsAString&', 'nsString'),
'utf8string': 'nsACString', 'utf8string': ('const nsACString&', 'nsACString&', 'nsCString'),
'cstring': 'nsACString', 'cstring': ('const nsACString&', 'nsACString&', 'nsCString'),
'astring': 'nsAString', 'astring': ('const nsAString&', 'nsAString&', 'nsString'),
'jsval': 'JS::Value', 'jsval': ('JS::HandleValue', 'JS::MutableHandleValue', 'JS::Value'),
'promise': '::mozilla::dom::Promise', 'promise': '::mozilla::dom::Promise',
} }
# Mappings from C++ native name types to rust native names. Types which
# aren't listed here are incompatible with rust code.
rust_nativenames = {
'void': "libc::c_void",
'char': "u8",
'char16_t': "u16",
'nsID': "nsID",
'nsIID': "nsIID",
'nsCID': "nsCID",
'nsAString': "::nsstring::nsAString",
'nsACString': "::nsstring::nsACString",
}
def __init__(self, name, nativename, attlist, location): def __init__(self, name, nativename, attlist, location):
self.name = name self.name = name
self.nativename = nativename self.nativename = nativename
@ -536,44 +524,57 @@ class Native(object):
raise IDLError("[shared] only applies to out parameters.") raise IDLError("[shared] only applies to out parameters.")
const = True const = True
if self.specialtype not in [None, 'promise'] and calltype == 'in': if isinstance(self.nativename, tuple):
if calltype == 'in':
return self.nativename[0] + ' '
elif 'out' in calltype:
return self.nativename[1] + ' '
else:
return self.nativename[2] + ' '
# 'in' nsid parameters should be made 'const'
if self.specialtype == 'nsid' and calltype == 'in':
const = True const = True
if self.specialtype == 'jsval':
if calltype == 'out' or calltype == 'inout':
return "JS::MutableHandleValue "
return "JS::HandleValue "
if self.isRef(calltype): if self.isRef(calltype):
m = '& ' m = '& ' # [ref] is always passed with a single indirection
elif self.isPtr(calltype):
m = '*' + ((self.modifier == 'ptr' and calltype != 'in') and '*' or '')
else: else:
m = calltype != 'in' and '*' or '' m = '* ' if 'out' in calltype else ''
if self.isPtr(calltype):
m += '* '
return "%s%s %s" % (const and 'const ' or '', self.nativename, m) return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
def rustType(self, calltype, const=False, shared=False): def rustType(self, calltype, const=False, shared=False):
if shared: # For the most part, 'native' types don't make sense in rust, as they
if calltype != 'out': # are native C++ types. However, we can support a few types here, as
raise IDLError("[shared] only applies to out parameters.") # they're important.
const = True #
# NOTE: This code doesn't try to perfectly match C++ constness, as
# constness doesn't affect ABI, and raw pointers are already unsafe.
if self.specialtype is not None and calltype == 'in': if self.modifier not in ['ptr', 'ref']:
const = True raise RustNoncompat('Rust only supports [ref] / [ptr] native types')
if self.nativename not in self.rust_nativenames: prefix = '*mut ' if 'out' in calltype else '*const '
raise RustNoncompat("native type %s is unsupported" % self.nativename) if 'out' in calltype and self.modifier == 'ptr':
name = self.rust_nativenames[self.nativename] prefix += '*mut '
if self.isRef(calltype): if self.specialtype == 'nsid':
m = const and '&' or '&mut ' return prefix + self.nativename
elif self.isPtr(calltype): if self.specialtype in ['cstring', 'utf8string']:
m = (const and '*const ' or '*mut ') if calltype == 'element':
if self.modifier == 'ptr' and calltype != 'in': return '::nsstring::nsCString'
m += '*mut ' return prefix + '::nsstring::nsACString'
else: if self.specialtype in ['astring', 'domstring']:
m = calltype != 'in' and '*mut ' or '' if calltype == 'element':
return "%s%s" % (m, name) return '::nsstring::nsString'
return prefix + '::nsstring::nsAString'
if self.nativename == 'void':
return prefix + 'libc::c_void'
if self.specialtype:
raise RustNoncompat("specialtype %s unsupported" % self.specialtype)
raise RustNoncompat("native type %s unsupported" % self.nativename)
def __str__(self): def __str__(self):
return "native %s(%s)\n" % (self.name, self.nativename) return "native %s(%s)\n" % (self.name, self.nativename)
@ -613,11 +614,11 @@ class WebIDL(object):
return True # All DOM objects are script exposed. return True # All DOM objects are script exposed.
def nativeType(self, calltype): def nativeType(self, calltype):
return "%s %s" % (self.native, calltype != 'in' and '* *' or '*') return "%s *%s" % (self.native, '*' if 'out' in calltype else '')
def rustType(self, calltype): def rustType(self, calltype):
# Just expose the type as a void* - we can't do any better. # Just expose the type as a void* - we can't do any better.
return "%s*const libc::c_void" % (calltype != 'in' and '*mut ' or '') return "%s*const libc::c_void" % ('*mut ' if 'out' in calltype else '')
def __str__(self): def __str__(self):
return "webidl %s\n" % self.name return "webidl %s\n" % self.name
@ -713,12 +714,11 @@ class Interface(object):
return True return True
def nativeType(self, calltype, const=False): def nativeType(self, calltype, const=False):
return "%s%s %s" % (const and 'const ' or '', return "%s%s *%s" % ('const ' if const else '', self.name,
self.name, '*' if 'out' in calltype else '')
calltype != 'in' and '* *' or '*')
def rustType(self, calltype): def rustType(self, calltype, const=False):
return "%s*const %s" % (calltype != 'in' and '*mut ' or '', return "%s*const %s" % ('*mut ' if 'out' in calltype else '',
self.name) self.name)
def __str__(self): def __str__(self):
@ -1219,12 +1219,19 @@ class Array(object):
return self.type.isScriptable() return self.type.isScriptable()
def nativeType(self, calltype, const=False): def nativeType(self, calltype, const=False):
return "%s%s*" % (const and 'const ' or '', # For legacy reasons, we have to add a 'const ' to builtin pointer array
self.type.nativeType(calltype)) # types. (`[array] in string` and `[array] in wstring` parameters)
if calltype == 'in' and isinstance(self.type, Builtin) and self.type.isPointer():
const = True
return "%s%s*%s" % ('const ' if const else '',
self.type.nativeType('element'),
'*' if 'out' in calltype else '')
def rustType(self, calltype, const=False): def rustType(self, calltype, const=False):
return "%s %s" % (const and '*const' or '*mut', return "%s%s%s" % ('*mut ' if 'out' in calltype else '',
self.type.rustType(calltype)) '*const ' if const else '*mut ',
self.type.rustType('element'))
class IDLParser(object): class IDLParser(object):

View File

@ -11,7 +11,7 @@
// re-export libc so it can be used by the procedural macro. // re-export libc so it can be used by the procedural macro.
pub extern crate libc; pub extern crate libc;
pub use nsstring::{nsACString, nsAString}; pub use nsstring::{nsACString, nsAString, nsCString, nsString};
pub use nserror::{nsresult, NsresultExt, NS_ERROR_NO_INTERFACE, NS_OK}; pub use nserror::{nsresult, NsresultExt, NS_ERROR_NO_INTERFACE, NS_OK};

View File

@ -563,7 +563,9 @@ fn xpcom(init: DeriveInput) -> Result<Tokens, Box<Error>> {
#[allow(unused_imports)] #[allow(unused_imports)]
use ::xpcom::interfaces::*; use ::xpcom::interfaces::*;
#[allow(unused_imports)] #[allow(unused_imports)]
use ::xpcom::reexports::{libc, nsACString, nsAString, nsresult}; use ::xpcom::reexports::{
libc, nsACString, nsAString, nsCString, nsString, nsresult
};
unsafe { unsafe {
// NOTE: This is split into multiple lines to make the // NOTE: This is split into multiple lines to make the