mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 15:25:52 +00:00
Bug 779048 part 5. Make js-to-native conversion support doing something other than "return false" on JS exceptions. r=peterv
This commit is contained in:
parent
7752f218bb
commit
4779ff59ef
@ -1872,10 +1872,11 @@ class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
|
||||
"""
|
||||
As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
|
||||
"""
|
||||
def __init__(self, descriptor, source, target):
|
||||
CastableObjectUnwrapper.__init__(self, descriptor, source, target,
|
||||
"return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE," +
|
||||
'"%s");' % descriptor.name)
|
||||
def __init__(self, descriptor, source, target, exceptionCode):
|
||||
CastableObjectUnwrapper.__init__(
|
||||
self, descriptor, source, target,
|
||||
'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");\n'
|
||||
'%s' % (descriptor.name, exceptionCode))
|
||||
|
||||
class CallbackObjectUnwrapper:
|
||||
"""
|
||||
@ -1884,11 +1885,12 @@ class CallbackObjectUnwrapper:
|
||||
|source| is the JSObject we want to use in native code.
|
||||
|target| is an nsCOMPtr of the appropriate type in which we store the result.
|
||||
"""
|
||||
def __init__(self, descriptor, source, target, codeOnFailure=None):
|
||||
def __init__(self, descriptor, source, target, exceptionCode,
|
||||
codeOnFailure=None):
|
||||
if codeOnFailure is None:
|
||||
codeOnFailure = ("return ThrowErrorMessage(cx," +
|
||||
'MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' %
|
||||
descriptor.name)
|
||||
codeOnFailure = (
|
||||
'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");\n'
|
||||
'%s' % (descriptor.name, exceptionCode))
|
||||
self.descriptor = descriptor
|
||||
self.substitution = { "nativeType" : descriptor.nativeType,
|
||||
"source" : source,
|
||||
@ -1957,7 +1959,8 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
|
||||
treatUndefinedAs="Default",
|
||||
isEnforceRange=False,
|
||||
isClamp=False,
|
||||
isNullOrUndefined=False):
|
||||
isNullOrUndefined=False,
|
||||
exceptionCode=None):
|
||||
"""
|
||||
Get a template for converting a JS value to a native object based on the
|
||||
given type and descriptor. If failureCode is given, then we're actually
|
||||
@ -1966,7 +1969,10 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
|
||||
value need to use failureCode instead of throwing exceptions. Failures to
|
||||
convert that are due to JS exceptions (from toString or valueOf methods) or
|
||||
out of memory conditions need to throw exceptions no matter what
|
||||
failureCode is.
|
||||
failureCode is. However what actually happens when throwing an exception
|
||||
can be controlled by exceptionCode. The only requirement on that is that
|
||||
exceptionCode must end up doing a return, and every return from this
|
||||
function must happen via exceptionCode if exceptionCode is not None.
|
||||
|
||||
If isDefinitelyObject is True, that means we know the value
|
||||
isObject() and we have no need to recheck that.
|
||||
@ -2032,20 +2038,33 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
|
||||
# And we can't both be an object and be null or undefined
|
||||
assert not isDefinitelyObject or not isNullOrUndefined
|
||||
|
||||
# If exceptionCode is not set, we'll just rethrow the exception we got.
|
||||
# Note that we can't just set failureCode to exceptionCode, because setting
|
||||
# failureCode will prevent pending exceptions from being set in cases when
|
||||
# they really should be!
|
||||
if exceptionCode is None:
|
||||
exceptionCode = "return false;"
|
||||
# We often want exceptionCode to be indented, since it often appears in an
|
||||
# if body.
|
||||
exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode))
|
||||
|
||||
# Helper functions for dealing with failures due to the JS value being the
|
||||
# wrong type of value
|
||||
def onFailureNotAnObject(failureCode):
|
||||
return CGWrapper(CGGeneric(
|
||||
failureCode or
|
||||
'return ThrowErrorMessage(cx, MSG_NOT_OBJECT);'), post="\n")
|
||||
('ThrowErrorMessage(cx, MSG_NOT_OBJECT);\n'
|
||||
'%s' % exceptionCode)), post="\n")
|
||||
def onFailureBadType(failureCode, typeName):
|
||||
return CGWrapper(CGGeneric(
|
||||
failureCode or
|
||||
'return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % typeName), post="\n")
|
||||
('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");'
|
||||
'%s' % (typeName, exceptionCode))), post="\n")
|
||||
def onFailureNotCallable(failureCode):
|
||||
return CGWrapper(CGGeneric(
|
||||
failureCode or
|
||||
'return ThrowErrorMessage(cx, MSG_NOT_CALLABLE);'), post="\n")
|
||||
('ThrowErrorMessage(cx, MSG_NOT_CALLABLE);\n'
|
||||
'%s' % exceptionCode)), post="\n")
|
||||
|
||||
# A helper function for handling default values. Takes a template
|
||||
# body and the C++ code to set the default value and wraps the
|
||||
@ -2110,7 +2129,8 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
|
||||
assert not isEnforceRange and not isClamp
|
||||
|
||||
if failureCode is None:
|
||||
notSequence = "return ThrowErrorMessage(cx, MSG_NOT_SEQUENCE);"
|
||||
notSequence = ("ThrowErrorMessage(cx, MSG_NOT_SEQUENCE);\n"
|
||||
"%s" % exceptionCode)
|
||||
else:
|
||||
notSequence = failureCode
|
||||
|
||||
@ -2139,7 +2159,8 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
|
||||
|
||||
(elementTemplate, elementDeclType,
|
||||
elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
|
||||
elementType, descriptorProvider, isMember=True)
|
||||
elementType, descriptorProvider, isMember=True,
|
||||
exceptionCode=exceptionCode)
|
||||
if dealWithOptional:
|
||||
raise TypeError("Shouldn't have optional things in sequences")
|
||||
if elementHolderType is not None:
|
||||
@ -2163,22 +2184,25 @@ if (!IsArrayLike(cx, seq)) {
|
||||
uint32_t length;
|
||||
// JS_GetArrayLength actually works on all objects
|
||||
if (!JS_GetArrayLength(cx, seq, &length)) {
|
||||
return false;
|
||||
%s
|
||||
}
|
||||
Sequence< %s > &arr = const_cast< Sequence< %s >& >(%s);
|
||||
if (!arr.SetCapacity(length)) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
%s
|
||||
}
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
jsval temp;
|
||||
if (!JS_GetElement(cx, seq, i, &temp)) {
|
||||
return false;
|
||||
%s
|
||||
}
|
||||
""" % (CGIndenter(CGGeneric(notSequence)).define(),
|
||||
exceptionCodeIndented.define(),
|
||||
elementDeclType.define(),
|
||||
elementDeclType.define(),
|
||||
arrayRef))
|
||||
arrayRef,
|
||||
exceptionCodeIndented.define(),
|
||||
CGIndenter(exceptionCodeIndented).define()))
|
||||
|
||||
templateBody += CGIndenter(CGGeneric(
|
||||
string.Template(elementTemplate).substitute(
|
||||
@ -2346,11 +2370,14 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
|
||||
templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
|
||||
throw = CGGeneric("if (failed) {\n"
|
||||
" return false;\n"
|
||||
"%s\n"
|
||||
"}\n"
|
||||
"if (!done) {\n"
|
||||
" return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
|
||||
"}" % ", ".join(names))
|
||||
" ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
|
||||
"%s\n"
|
||||
"}" % (exceptionCodeIndented.define(),
|
||||
", ".join(names),
|
||||
exceptionCodeIndented.define()))
|
||||
templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
|
||||
|
||||
typeName = type.name
|
||||
@ -2466,12 +2493,14 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
templateBody += str(FailureFatalCastableObjectUnwrapper(
|
||||
descriptor,
|
||||
"&${val}.toObject()",
|
||||
"${declName}"))
|
||||
"${declName}",
|
||||
exceptionCode))
|
||||
elif descriptor.interface.isCallback():
|
||||
templateBody += str(CallbackObjectUnwrapper(
|
||||
descriptor,
|
||||
"&${val}.toObject()",
|
||||
"${declName}",
|
||||
exceptionCode,
|
||||
codeOnFailure=failureCode))
|
||||
elif descriptor.workers:
|
||||
templateBody += "${declName} = &${val}.toObject();"
|
||||
@ -2597,8 +2626,9 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
def getConversionCode(varName):
|
||||
conversionCode = (
|
||||
"if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
|
||||
" return false;\n"
|
||||
"}" % (nullBehavior, undefinedBehavior, varName))
|
||||
"%s\n"
|
||||
"}" % (nullBehavior, undefinedBehavior, varName,
|
||||
exceptionCodeIndented.define()))
|
||||
if defaultValue is None:
|
||||
return conversionCode
|
||||
|
||||
@ -2648,6 +2678,7 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
if invalidEnumValueFatal:
|
||||
handleInvalidEnumValueCode = " MOZ_ASSERT(index >= 0);\n"
|
||||
else:
|
||||
assert exceptionCode == "return false;"
|
||||
handleInvalidEnumValueCode = (
|
||||
" if (index < 0) {\n"
|
||||
" return true;\n"
|
||||
@ -2658,14 +2689,15 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
" bool ok;\n"
|
||||
" int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n"
|
||||
" if (!ok) {\n"
|
||||
" return false;\n"
|
||||
"%(exceptionCode)s\n"
|
||||
" }\n"
|
||||
"%(handleInvalidEnumValueCode)s"
|
||||
" ${declName} = static_cast<%(enumtype)s>(index);\n"
|
||||
"}" % { "enumtype" : enum,
|
||||
"values" : enum + "Values::strings",
|
||||
"invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal),
|
||||
"handleInvalidEnumValueCode" : handleInvalidEnumValueCode })
|
||||
"handleInvalidEnumValueCode" : handleInvalidEnumValueCode,
|
||||
"exceptionCode" : CGIndenter(exceptionCodeIndented).define() })
|
||||
|
||||
if defaultValue is not None:
|
||||
assert(defaultValue.type.tag() == IDLType.Tags.domstring)
|
||||
@ -2771,8 +2803,8 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
val = "${val}"
|
||||
|
||||
template = ("if (!%s.Init(cx, %s)) {\n"
|
||||
" return false;\n"
|
||||
"}" % (selfRef, val))
|
||||
"%s\n"
|
||||
"}" % (selfRef, val, exceptionCodeIndented.define()))
|
||||
|
||||
return (template, declType, None, False)
|
||||
|
||||
@ -2800,16 +2832,18 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
"if (%s) {\n"
|
||||
" const_cast< %s >(${declName}).SetNull();\n"
|
||||
"} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
|
||||
" return false;\n"
|
||||
"}" % (nullCondition, mutableType, typeName, conversionBehavior, dataLoc))
|
||||
"%s\n"
|
||||
"}" % (nullCondition, mutableType, typeName, conversionBehavior, dataLoc,
|
||||
exceptionCodeIndented.define()))
|
||||
else:
|
||||
assert(defaultValue is None or
|
||||
not isinstance(defaultValue, IDLNullValue))
|
||||
dataLoc = "${declName}"
|
||||
template = (
|
||||
"if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
|
||||
" return false;\n"
|
||||
"}" % (typeName, conversionBehavior, dataLoc))
|
||||
"%s\n"
|
||||
"}" % (typeName, conversionBehavior, dataLoc,
|
||||
exceptionCodeIndented.define()))
|
||||
declType = CGGeneric(typeName)
|
||||
if (defaultValue is not None and
|
||||
# We already handled IDLNullValue, so just deal with the other ones
|
||||
@ -3913,7 +3947,7 @@ class CGAbstractBindingMethod(CGAbstractStaticMethod):
|
||||
# Our descriptor might claim that we're not castable, simply because
|
||||
# we're someone's consequential interface. But for this-unwrapping, we
|
||||
# know that we're the real deal. So fake a descriptor here for
|
||||
# consumption by FailureFatalCastableObjectUnwrapper.
|
||||
# consumption by CastableObjectUnwrapper.
|
||||
getThis = CGGeneric("""js::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user