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:
Boris Zbarsky 2012-11-09 07:43:58 -08:00
parent 01c98be853
commit f7b9a5bdea

View File

@ -1872,10 +1872,11 @@ class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
""" """
As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
""" """
def __init__(self, descriptor, source, target): def __init__(self, descriptor, source, target, exceptionCode):
CastableObjectUnwrapper.__init__(self, descriptor, source, target, CastableObjectUnwrapper.__init__(
"return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE," + self, descriptor, source, target,
'"%s");' % descriptor.name) 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");\n'
'%s' % (descriptor.name, exceptionCode))
class CallbackObjectUnwrapper: class CallbackObjectUnwrapper:
""" """
@ -1884,11 +1885,12 @@ class CallbackObjectUnwrapper:
|source| is the JSObject we want to use in native code. |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. |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: if codeOnFailure is None:
codeOnFailure = ("return ThrowErrorMessage(cx," + codeOnFailure = (
'MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");\n'
descriptor.name) '%s' % (descriptor.name, exceptionCode))
self.descriptor = descriptor self.descriptor = descriptor
self.substitution = { "nativeType" : descriptor.nativeType, self.substitution = { "nativeType" : descriptor.nativeType,
"source" : source, "source" : source,
@ -1957,7 +1959,8 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
treatUndefinedAs="Default", treatUndefinedAs="Default",
isEnforceRange=False, isEnforceRange=False,
isClamp=False, isClamp=False,
isNullOrUndefined=False): isNullOrUndefined=False,
exceptionCode=None):
""" """
Get a template for converting a JS value to a native object based on the 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 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 value need to use failureCode instead of throwing exceptions. Failures to
convert that are due to JS exceptions (from toString or valueOf methods) or convert that are due to JS exceptions (from toString or valueOf methods) or
out of memory conditions need to throw exceptions no matter what 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 If isDefinitelyObject is True, that means we know the value
isObject() and we have no need to recheck that. 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 # And we can't both be an object and be null or undefined
assert not isDefinitelyObject or not isNullOrUndefined 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 # Helper functions for dealing with failures due to the JS value being the
# wrong type of value # wrong type of value
def onFailureNotAnObject(failureCode): def onFailureNotAnObject(failureCode):
return CGWrapper(CGGeneric( return CGWrapper(CGGeneric(
failureCode or failureCode or
'return ThrowErrorMessage(cx, MSG_NOT_OBJECT);'), post="\n") ('ThrowErrorMessage(cx, MSG_NOT_OBJECT);\n'
'%s' % exceptionCode)), post="\n")
def onFailureBadType(failureCode, typeName): def onFailureBadType(failureCode, typeName):
return CGWrapper(CGGeneric( return CGWrapper(CGGeneric(
failureCode or 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): def onFailureNotCallable(failureCode):
return CGWrapper(CGGeneric( return CGWrapper(CGGeneric(
failureCode or 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 # A helper function for handling default values. Takes a template
# body and the C++ code to set the default value and wraps the # 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 assert not isEnforceRange and not isClamp
if failureCode is None: if failureCode is None:
notSequence = "return ThrowErrorMessage(cx, MSG_NOT_SEQUENCE);" notSequence = ("ThrowErrorMessage(cx, MSG_NOT_SEQUENCE);\n"
"%s" % exceptionCode)
else: else:
notSequence = failureCode notSequence = failureCode
@ -2139,7 +2159,8 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
(elementTemplate, elementDeclType, (elementTemplate, elementDeclType,
elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate( elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
elementType, descriptorProvider, isMember=True) elementType, descriptorProvider, isMember=True,
exceptionCode=exceptionCode)
if dealWithOptional: if dealWithOptional:
raise TypeError("Shouldn't have optional things in sequences") raise TypeError("Shouldn't have optional things in sequences")
if elementHolderType is not None: if elementHolderType is not None:
@ -2163,22 +2184,25 @@ if (!IsArrayLike(cx, seq)) {
uint32_t length; uint32_t length;
// JS_GetArrayLength actually works on all objects // JS_GetArrayLength actually works on all objects
if (!JS_GetArrayLength(cx, seq, &length)) { if (!JS_GetArrayLength(cx, seq, &length)) {
return false; %s
} }
Sequence< %s > &arr = const_cast< Sequence< %s >& >(%s); Sequence< %s > &arr = const_cast< Sequence< %s >& >(%s);
if (!arr.SetCapacity(length)) { if (!arr.SetCapacity(length)) {
JS_ReportOutOfMemory(cx); JS_ReportOutOfMemory(cx);
return false; %s
} }
for (uint32_t i = 0; i < length; ++i) { for (uint32_t i = 0; i < length; ++i) {
jsval temp; jsval temp;
if (!JS_GetElement(cx, seq, i, &temp)) { if (!JS_GetElement(cx, seq, i, &temp)) {
return false; %s
} }
""" % (CGIndenter(CGGeneric(notSequence)).define(), """ % (CGIndenter(CGGeneric(notSequence)).define(),
exceptionCodeIndented.define(),
elementDeclType.define(), elementDeclType.define(),
elementDeclType.define(), elementDeclType.define(),
arrayRef)) arrayRef,
exceptionCodeIndented.define(),
CGIndenter(exceptionCodeIndented).define()))
templateBody += CGIndenter(CGGeneric( templateBody += CGIndenter(CGGeneric(
string.Template(elementTemplate).substitute( 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") templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
throw = CGGeneric("if (failed) {\n" throw = CGGeneric("if (failed) {\n"
" return false;\n" "%s\n"
"}\n" "}\n"
"if (!done) {\n" "if (!done) {\n"
" return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n" " ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
"}" % ", ".join(names)) "%s\n"
"}" % (exceptionCodeIndented.define(),
", ".join(names),
exceptionCodeIndented.define()))
templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}") templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
typeName = type.name typeName = type.name
@ -2466,12 +2493,14 @@ for (uint32_t i = 0; i < length; ++i) {
templateBody += str(FailureFatalCastableObjectUnwrapper( templateBody += str(FailureFatalCastableObjectUnwrapper(
descriptor, descriptor,
"&${val}.toObject()", "&${val}.toObject()",
"${declName}")) "${declName}",
exceptionCode))
elif descriptor.interface.isCallback(): elif descriptor.interface.isCallback():
templateBody += str(CallbackObjectUnwrapper( templateBody += str(CallbackObjectUnwrapper(
descriptor, descriptor,
"&${val}.toObject()", "&${val}.toObject()",
"${declName}", "${declName}",
exceptionCode,
codeOnFailure=failureCode)) codeOnFailure=failureCode))
elif descriptor.workers: elif descriptor.workers:
templateBody += "${declName} = &${val}.toObject();" templateBody += "${declName} = &${val}.toObject();"
@ -2597,8 +2626,9 @@ for (uint32_t i = 0; i < length; ++i) {
def getConversionCode(varName): def getConversionCode(varName):
conversionCode = ( conversionCode = (
"if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n" "if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
" return false;\n" "%s\n"
"}" % (nullBehavior, undefinedBehavior, varName)) "}" % (nullBehavior, undefinedBehavior, varName,
exceptionCodeIndented.define()))
if defaultValue is None: if defaultValue is None:
return conversionCode return conversionCode
@ -2648,6 +2678,7 @@ for (uint32_t i = 0; i < length; ++i) {
if invalidEnumValueFatal: if invalidEnumValueFatal:
handleInvalidEnumValueCode = " MOZ_ASSERT(index >= 0);\n" handleInvalidEnumValueCode = " MOZ_ASSERT(index >= 0);\n"
else: else:
assert exceptionCode == "return false;"
handleInvalidEnumValueCode = ( handleInvalidEnumValueCode = (
" if (index < 0) {\n" " if (index < 0) {\n"
" return true;\n" " return true;\n"
@ -2658,14 +2689,15 @@ for (uint32_t i = 0; i < length; ++i) {
" bool ok;\n" " bool ok;\n"
" int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n" " int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n"
" if (!ok) {\n" " if (!ok) {\n"
" return false;\n" "%(exceptionCode)s\n"
" }\n" " }\n"
"%(handleInvalidEnumValueCode)s" "%(handleInvalidEnumValueCode)s"
" ${declName} = static_cast<%(enumtype)s>(index);\n" " ${declName} = static_cast<%(enumtype)s>(index);\n"
"}" % { "enumtype" : enum, "}" % { "enumtype" : enum,
"values" : enum + "Values::strings", "values" : enum + "Values::strings",
"invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal), "invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal),
"handleInvalidEnumValueCode" : handleInvalidEnumValueCode }) "handleInvalidEnumValueCode" : handleInvalidEnumValueCode,
"exceptionCode" : CGIndenter(exceptionCodeIndented).define() })
if defaultValue is not None: if defaultValue is not None:
assert(defaultValue.type.tag() == IDLType.Tags.domstring) assert(defaultValue.type.tag() == IDLType.Tags.domstring)
@ -2771,8 +2803,8 @@ for (uint32_t i = 0; i < length; ++i) {
val = "${val}" val = "${val}"
template = ("if (!%s.Init(cx, %s)) {\n" template = ("if (!%s.Init(cx, %s)) {\n"
" return false;\n" "%s\n"
"}" % (selfRef, val)) "}" % (selfRef, val, exceptionCodeIndented.define()))
return (template, declType, None, False) return (template, declType, None, False)
@ -2800,16 +2832,18 @@ for (uint32_t i = 0; i < length; ++i) {
"if (%s) {\n" "if (%s) {\n"
" const_cast< %s >(${declName}).SetNull();\n" " const_cast< %s >(${declName}).SetNull();\n"
"} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n" "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
" return false;\n" "%s\n"
"}" % (nullCondition, mutableType, typeName, conversionBehavior, dataLoc)) "}" % (nullCondition, mutableType, typeName, conversionBehavior, dataLoc,
exceptionCodeIndented.define()))
else: else:
assert(defaultValue is None or assert(defaultValue is None or
not isinstance(defaultValue, IDLNullValue)) not isinstance(defaultValue, IDLNullValue))
dataLoc = "${declName}" dataLoc = "${declName}"
template = ( template = (
"if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n" "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
" return false;\n" "%s\n"
"}" % (typeName, conversionBehavior, dataLoc)) "}" % (typeName, conversionBehavior, dataLoc,
exceptionCodeIndented.define()))
declType = CGGeneric(typeName) declType = CGGeneric(typeName)
if (defaultValue is not None and if (defaultValue is not None and
# We already handled IDLNullValue, so just deal with the other ones # 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 # Our descriptor might claim that we're not castable, simply because
# we're someone's consequential interface. But for this-unwrapping, we # we're someone's consequential interface. But for this-unwrapping, we
# know that we're the real deal. So fake a descriptor here for # 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)); getThis = CGGeneric("""js::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
if (!obj) { if (!obj) {
return false; return false;