Bug 1484496: Part 1 - Add support for symbol properties to XPIDL. r=nika

This patch allows us to define methods or getters/setters for any of the
current set of well-known symbols. Those are defined by adding the [symbol]
attribute to a method:

  [symbol]
  Iterator iterator();

which causes the method to define a property with the well-known symbol which
matches its method name (Symbol.iterator, in this case).

Due to the implementation details of the XPIDL parser, this currently does not
support defining a non-symbol function with the same name as a symbol
function:

  [symbol]
  Iterator iterator();

  [binaryname(OtherIterator)]
  Thing iterator(in nsIDRef aIID);

throws for a duplicate method name, even though there is no actual conflict.

Differential Revision: https://phabricator.services.mozilla.com/D3724

--HG--
extra : rebase_source : 1385e2da93113306730f7c087fe7385dbe668e91
extra : histedit_source : 3afd9fe38e7cbddc5576c2bd1673496dd623e489
This commit is contained in:
Kris Maglione 2018-08-21 14:08:35 -07:00
parent b219ce3bfc
commit 83db11134f
7 changed files with 97 additions and 15 deletions

View file

@ -943,7 +943,6 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
bool success;
bool readyToDoTheCall = false;
nsID param_iid;
const char* name = info->GetName();
bool foundDependentParam;
// We're about to call into script via an XPCWrappedJS, so we need an
@ -962,6 +961,19 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
if (!cx || !IsReflectable(methodIndex))
return NS_ERROR_FAILURE;
JS::RootedId id(cx);
const char* name;
nsAutoCString symbolName;
if (info->IsSymbol()) {
info->GetSymbolDescription(cx, symbolName);
name = symbolName.get();
id = SYMBOL_TO_JSID(info->GetSymbol(cx));
} else {
name = info->GetName();
if (!AtomizeAndPinJSString(cx, id.get(), name))
return NS_ERROR_FAILURE;
}
// We passed the unwrapped object's global to AutoEntryScript so we now need
// to enter the realm corresponding with the (maybe wrapper) object.
RootedObject scope(cx, wrapper->GetJSObjectGlobal());
@ -1030,7 +1042,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
fval = ObjectValue(*obj);
if (!isFunction || JS_TypeOfValue(ccx, fval) != JSTYPE_FUNCTION) {
if (!JS_GetProperty(cx, obj, name, &fval))
if (!JS_GetPropertyById(cx, obj, id, &fval))
goto pre_call_clean_up;
// XXX We really want to factor out the error reporting better and
// specifically report the failure to find a function with this name.

View file

@ -87,7 +87,13 @@ XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
callback = XPC_WN_GetterSetter;
}
JSFunction* fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, GetName());
JSFunction* fun;
jsid name = GetName();
if (JSID_IS_STRING(name)) {
fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, GetName());
} else {
fun = js::NewFunctionWithReserved(ccx, callback, argc, 0, nullptr);
}
if (!fun)
return false;
@ -292,13 +298,18 @@ XPCNativeInterface::NewInstance(const nsXPTInterfaceInfo* aInfo)
if (!XPCConvert::IsMethodReflectable(*info))
continue;
str = JS_AtomizeAndPinString(cx, info->GetName());
if (!str) {
NS_ERROR("bad method name");
failed = true;
break;
jsid name;
if (info->IsSymbol()) {
name = SYMBOL_TO_JSID(info->GetSymbol(cx));
} else {
str = JS_AtomizeAndPinString(cx, info->GetName());
if (!str) {
NS_ERROR("bad method name");
failed = true;
break;
}
name = INTERNED_STRING_TO_JSID(cx, str);
}
jsid name = INTERNED_STRING_TO_JSID(cx, str);
if (info->IsSetter()) {
MOZ_ASSERT(realTotalCount,"bad setter");

View file

@ -121,7 +121,8 @@ def mk_param(type, in_=0, out=0, optional=0):
def mk_method(name, params, getter=0, setter=0, notxpcom=0,
hidden=0, optargc=0, context=0, hasretval=0):
hidden=0, optargc=0, context=0, hasretval=0,
symbol=0):
return {
'name': name,
# NOTE: We don't include any return value information here, as we'll
@ -138,6 +139,7 @@ def mk_method(name, params, getter=0, setter=0, notxpcom=0,
('optargc', optargc),
('jscontext', context),
('hasretval', hasretval),
('symbol', symbol),
),
}
@ -186,19 +188,21 @@ def build_interface(iface):
methods.append(mk_method(
m.name, params, notxpcom=m.notxpcom, hidden=m.noscript,
optargc=m.optional_argc, context=m.implicit_jscontext,
hasretval=hasretval))
hasretval=hasretval, symbol=m.symbol))
def build_attr(a):
# Write the getter
param = mk_param(get_type(a.realtype, 'out'), out=1)
methods.append(mk_method(a.name, [param], getter=1, hidden=a.noscript,
context=a.implicit_jscontext, hasretval=1))
context=a.implicit_jscontext, hasretval=1,
symbol=a.symbol))
# And maybe the setter
if not a.readonly:
param = mk_param(get_type(a.realtype, 'in'), in_=1)
methods.append(mk_method(a.name, [param], setter=1, hidden=a.noscript,
context=a.implicit_jscontext))
context=a.implicit_jscontext,
symbol=a.symbol))
for member in iface.members:
if isinstance(member, xpidl.ConstMember):

View file

@ -912,6 +912,7 @@ class Attribute(object):
kind = 'attribute'
noscript = False
readonly = False
symbol = False
implicit_jscontext = False
nostdcall = False
must_use = False
@ -963,6 +964,8 @@ class Attribute(object):
if name == 'noscript':
self.noscript = True
elif name == 'symbol':
self.symbol = True
elif name == 'implicit_jscontext':
self.implicit_jscontext = True
elif name == 'nostdcall':
@ -1019,6 +1022,7 @@ class Method(object):
kind = 'method'
noscript = False
notxpcom = False
symbol = False
binaryname = None
implicit_jscontext = False
nostdcall = False
@ -1050,6 +1054,8 @@ class Method(object):
self.noscript = True
elif name == 'notxpcom':
self.notxpcom = True
elif name == 'symbol':
self.symbol = True
elif name == 'implicit_jscontext':
self.implicit_jscontext = True
elif name == 'optional_argc':

View file

@ -84,6 +84,7 @@ nsXPTMethodInfo = mkstruct(
"mOptArgc",
"mContext",
"mHasRetval",
"mIsSymbol",
)
##########################################################
@ -244,6 +245,9 @@ def link_to_cpp(interfaces, fd):
strings[s] = 0
return strings[s]
def lower_symbol(s):
return "uint32_t(JS::SymbolCode::%s)" % s
def lower_extra_type(type):
key = describe_type(type)
idx = type_cache.get(key)
@ -315,10 +319,16 @@ def link_to_cpp(interfaces, fd):
def lower_method(method, ifacename):
methodname = "%s::%s" % (ifacename, method['name'])
isSymbol = 'symbol' in method['flags']
if 'notxpcom' in method['flags'] or 'hidden' in method['flags']:
paramidx = name = numparams = 0 # hide parameters
else:
name = lower_string(method['name'])
if isSymbol:
name = lower_symbol(method['name'])
else:
name = lower_string(method['name'])
numparams = len(method['params'])
# Check cache for parameters
@ -346,6 +356,7 @@ def link_to_cpp(interfaces, fd):
mOptArgc='optargc' in method['flags'],
mContext='jscontext' in method['flags'],
mHasRetval='hasretval' in method['flags'],
mIsSymbol=isSymbol,
))
def lower_const(const, ifacename):

View file

@ -9,6 +9,8 @@
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/ArrayUtils.h"
#include "jsfriendapi.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace xpt::detail;
@ -168,3 +170,22 @@ nsXPTInterfaceInfo::IsMainProcessScriptableOnly(bool* aRetval) const
*aRetval = IsMainProcessScriptableOnly();
return NS_OK;
}
////////////////////////////////////
// nsXPTMethodInfo symbol helpers //
////////////////////////////////////
void
nsXPTMethodInfo::GetSymbolDescription(JSContext* aCx, nsACString& aID) const
{
JS::RootedSymbol symbol(aCx, GetSymbol(aCx));
JSString* desc = JS::GetSymbolDescription(symbol);
MOZ_ASSERT(JS_StringHasLatin1Chars(desc));
JS::AutoAssertNoGC nogc(aCx);
size_t length;
const JS::Latin1Char* chars = JS_GetLatin1StringCharsAndLength(
aCx, nogc, desc, &length);
aID.AssignASCII(reinterpret_cast<const char*>(chars), length);
}

View file

@ -16,6 +16,7 @@
#include <stdint.h>
#include "nsID.h"
#include "mozilla/Assertions.h"
#include "jsapi.h"
#include "js/Value.h"
#include "nsString.h"
#include "nsTArray.h"
@ -440,11 +441,13 @@ struct nsXPTMethodInfo
bool IsSetter() const { return mSetter; }
bool IsNotXPCOM() const { return mNotXPCOM; }
bool IsHidden() const { return mHidden; }
bool IsSymbol() const { return mIsSymbol; }
bool WantsOptArgc() const { return mOptArgc; }
bool WantsContext() const { return mContext; }
uint8_t ParamCount() const { return mNumParams; }
const char* Name() const {
MOZ_ASSERT(!IsSymbol());
return xpt::detail::GetString(mName);
}
const nsXPTParamInfo& Param(uint8_t aIndex) const {
@ -478,6 +481,20 @@ struct nsXPTMethodInfo
/////////////////////////////////////////////
const char* GetName() const { return Name(); }
JS::SymbolCode GetSymbolCode() const
{
MOZ_ASSERT(IsSymbol());
return JS::SymbolCode(mName);
}
JS::Symbol* GetSymbol(JSContext* aCx) const
{
return JS::GetWellKnownSymbol(aCx, GetSymbolCode());
}
void GetSymbolDescription(JSContext* aCx, nsACString& aID) const;
uint8_t GetParamCount() const { return ParamCount(); }
const nsXPTParamInfo& GetParam(uint8_t aIndex) const {
return Param(aIndex);
@ -498,7 +515,7 @@ struct nsXPTMethodInfo
uint8_t mOptArgc : 1;
uint8_t mContext : 1;
uint8_t mHasRetval : 1;
// uint8_t unused : 1;
uint8_t mIsSymbol : 1;
};
// The fields in nsXPTMethodInfo were carefully ordered to minimize size.