Bug 748637. Refactor argument conversion to primitive values to make it easier to extend in the future. r=peterv

This commit is contained in:
Boris Zbarsky 2012-05-04 12:38:48 -04:00
parent ab17ec6502
commit a6dfa5d203
3 changed files with 146 additions and 81 deletions

View File

@ -1325,89 +1325,19 @@ def getArgumentConversionTemplate(type, descriptor):
if not type.isPrimitive(): if not type.isPrimitive():
raise TypeError("Need conversion for argument type '%s'" % type) raise TypeError("Need conversion for argument type '%s'" % type)
tag = type.tag() # XXXbz need to add support for [EnforceRange] and [Clamp]
replacements = dict()
if type.nullable(): if type.nullable():
replacements["declareArg"] = ( return (" Nullable<${typeName}> ${name};\n"
" Nullable<${typeName}> ${name};\n" " if (${argVal}.isNullOrUndefined()) {\n"
" if (${argVal}.isNullOrUndefined()) {\n" " ${name}.SetNull();\n"
" ${name}.SetNull();\n" " } else if (!ValueToPrimitive<${typeName}>(cx, ${argVal}, &${name}.SetValue())) {\n"
" } else" " return false;\n"
) " }\n")
replacements["finalValueSetter"] = "${name}.SetValue"
else: else:
replacements["declareArg"] = " ${typeName} ${name};\n" return (" ${typeName} ${name};\n"
replacements["finalValueSetter"] = "${name} = " " if (!ValueToPrimitive<${typeName}>(cx, ${argVal}, &${name})) {\n"
" return false;\n"
replacements["intermediateCast"] = "" " }\n")
if tag == IDLType.Tags.bool:
replacements["jstype"] = "JSBool"
replacements["converter"] = "JS_ValueToBoolean"
elif tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
IDLType.Tags.uint16, IDLType.Tags.int32, IDLType.Tags.uint32]:
# XXXbz need to add support for [EnforceRange] and [Clamp]
# The output of JS_ValueToECMAInt32 is determined as follows:
# 1) The value is converted to a double
# 2) Anything that's not a finite double returns 0
# 3) The double is rounded towards zero to the nearest integer
# 4) The resulting integer is reduced mod 2^32. The output of this
# operation is an integer in the range [0, 2^32).
# 5) If the resulting number is >= 2^31, 2^32 is subtracted from it.
#
# The result of all this is a number in the range [-2^31, 2^31)
#
# WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types
# are defined in the same way, except that step 4 uses reduction mod
# 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5
# is only done for the signed types.
#
# C/C++ define integer conversion semantics to unsigned types as taking
# your input integer mod (1 + largest value representable in the
# unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32,
# converting to the unsigned int of the relevant width will correctly
# perform step 4; in particular, the 2^32 possibly subtracted in step 5
# will become 0.
#
# Once we have step 4 done, we're just going to assume 2s-complement
# representation and cast directly to the type we really want.
#
# So we can cast directly for all unsigned types and for int32_t; for
# the smaller-width signed types we need to cast through the
# corresponding unsigned type.
replacements["jstype"] = "int32_t"
replacements["converter"] = "JS::ToInt32"
if tag is IDLType.Tags.int8:
replacements["intermediateCast"] = "(uint8_t)"
elif tag is IDLType.Tags.int16:
replacements["intermediateCast"] = "(uint16_t)"
else:
replacements["intermediateCast"] = ""
elif tag is IDLType.Tags.int64:
# XXXbz this may not match what WebIDL says to do in terms of reducing
# mod 2^64. Should we check?
replacements["jstype"] = "int64_t"
replacements["converter"] = "xpc::ValueToInt64"
elif tag is IDLType.Tags.uint64:
# XXXbz this may not match what WebIDL says to do in terms of reducing
# mod 2^64. Should we check?
replacements["jstype"] = "uint64_t"
replacements["converter"] = "xpc::ValueToUint64"
elif tag in [IDLType.Tags.float, IDLType.Tags.double]:
replacements["jstype"] = "double"
replacements["converter"] = "JS::ToNumber"
else:
raise TypeError("Unknown primitive type '%s'" % type);
# We substitute the %(name)s things here. Our caller will
# substitute the ${name} things.
return (" %(jstype)s ${name}_jstype;\n"
"%(declareArg)s" # No leading whitespace or newline here, on purpose
" if (%(converter)s(cx, ${argVal}, &${name}_jstype)) {\n"
" %(finalValueSetter)s((${typeName})%(intermediateCast)s${name}_jstype);\n"
" } else {\n"
" return false;\n"
" }\n" % replacements)
def convertConstIDLValueToJSVal(value): def convertConstIDLValueToJSVal(value):
if isinstance(value, IDLNullValue): if isinstance(value, IDLNullValue):
@ -2952,6 +2882,7 @@ class CGBindingRoot(CGThing):
['mozilla/dom/BindingUtils.h', ['mozilla/dom/BindingUtils.h',
'mozilla/dom/DOMJSClass.h'], 'mozilla/dom/DOMJSClass.h'],
['mozilla/dom/Nullable.h', ['mozilla/dom/Nullable.h',
'mozilla/dom/PrimitiveConversions.h',
'XPCQuickStubs.h', 'XPCQuickStubs.h',
'AccessCheck.h', 'AccessCheck.h',
'WorkerPrivate.h', 'WorkerPrivate.h',

View File

@ -52,6 +52,7 @@ EXPORTS_$(binding_include_path) = \
PrototypeList.h \ PrototypeList.h \
RegisterBindings.h \ RegisterBindings.h \
Nullable.h \ Nullable.h \
PrimitiveConversions.h \
TypedArray.h \ TypedArray.h \
BindingUtils.h \ BindingUtils.h \
$(binding_header_files) \ $(binding_header_files) \

View File

@ -0,0 +1,133 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* vim: set ts=2 sw=2 et tw=79: */
/* 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/. */
/**
* Conversions from jsval to primitive values
*/
#ifndef mozilla_dom_PrimitiveConversions_h
#define mozilla_dom_PrimitiveConversions_h
namespace mozilla {
namespace dom {
template<typename T>
struct PrimitiveConversionTraits {
};
struct PrimitiveConversionTraits_smallInt {
// The output of JS::ToInt32 is determined as follows:
// 1) The value is converted to a double
// 2) Anything that's not a finite double returns 0
// 3) The double is rounded towards zero to the nearest integer
// 4) The resulting integer is reduced mod 2^32. The output of this
// operation is an integer in the range [0, 2^32).
// 5) If the resulting number is >= 2^31, 2^32 is subtracted from it.
//
// The result of all this is a number in the range [-2^31, 2^31)
//
// WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types
// are defined in the same way, except that step 4 uses reduction mod
// 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5
// is only done for the signed types.
//
// C/C++ define integer conversion semantics to unsigned types as taking
// your input integer mod (1 + largest value representable in the
// unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32,
// converting to the unsigned int of the relevant width will correctly
// perform step 4; in particular, the 2^32 possibly subtracted in step 5
// will become 0.
//
// Once we have step 4 done, we're just going to assume 2s-complement
// representation and cast directly to the type we really want.
//
// So we can cast directly for all unsigned types and for int32_t; for
// the smaller-width signed types we need to cast through the
// corresponding unsigned type.
typedef int32_t jstype;
typedef int32_t intermediateType;
static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
return JS::ToInt32(cx, v, retval);
}
};
template<>
struct PrimitiveConversionTraits<int8_t> : PrimitiveConversionTraits_smallInt {
typedef uint8_t intermediateType;
};
template<>
struct PrimitiveConversionTraits<uint8_t> : PrimitiveConversionTraits_smallInt {
};
template<>
struct PrimitiveConversionTraits<int16_t> : PrimitiveConversionTraits_smallInt {
typedef uint16_t intermediateType;
};
template<>
struct PrimitiveConversionTraits<uint16_t> : PrimitiveConversionTraits_smallInt {
};
template<>
struct PrimitiveConversionTraits<int32_t> : PrimitiveConversionTraits_smallInt {
};
template<>
struct PrimitiveConversionTraits<uint32_t> : PrimitiveConversionTraits_smallInt {
};
template<>
struct PrimitiveConversionTraits<bool> {
typedef JSBool jstype;
typedef bool intermediateType;
static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
return JS_ValueToBoolean(cx, v, retval);
}
};
template<>
struct PrimitiveConversionTraits<int64_t> {
typedef int64_t jstype;
typedef int64_t intermediateType;
static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
return xpc::ValueToInt64(cx, v, retval);
}
};
template<>
struct PrimitiveConversionTraits<uint64_t> {
typedef uint64_t jstype;
typedef uint64_t intermediateType;
static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
return xpc::ValueToUint64(cx, v, retval);
}
};
struct PrimitiveConversionTraits_float {
typedef double jstype;
typedef double intermediateType;
static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
return JS::ToNumber(cx, v, retval);
}
};
template<>
struct PrimitiveConversionTraits<float> : PrimitiveConversionTraits_float {
};
template<>
struct PrimitiveConversionTraits<double> : PrimitiveConversionTraits_float {
};
template<typename T>
bool ValueToPrimitive(JSContext* cx, JS::Value v, T* retval)
{
typename PrimitiveConversionTraits<T>::jstype t;
if (!PrimitiveConversionTraits<T>::converter(cx, v, &t))
return false;
*retval =
static_cast<typename PrimitiveConversionTraits<T>::intermediateType>(t);
return true;
}
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_PrimitiveConversions_h */