author | Boris Zbarsky <bzbarsky@mit.edu> |
Thu, 16 May 2013 12:36:54 -0400 | |
changeset 143615 | 603420c520cf1a24cd4dde3d76e8588f46967c53 |
parent 143614 | 55620ca9673c267ca3b97b39df9155e8bbe733ac |
child 143616 | f57bfa1109004bedb3e9424cb923aff7776724a1 |
push id | 2697 |
push user | bbajaj@mozilla.com |
push date | Mon, 05 Aug 2013 18:49:53 +0000 |
treeherder | mozilla-beta@dfec938c7b63 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | peterv |
bugs | 868715 |
milestone | 24.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2386,35 +2386,86 @@ if (NS_FAILED(rv) || !wrappedJS) { // Use a temp nsCOMPtr for the null-check, because ${target} might be // OwningNonNull, not an nsCOMPtr. nsCOMPtr<${nativeType}> tmp = do_QueryObject(wrappedJS.get()); if (!tmp) { ${codeOnFailure} } ${target} = tmp.forget();""").substitute(self.substitution) +class JSToNativeConversionInfo(): + """ + An object representing information about a JS-to-native conversion. + """ + def __init__(self, template, declType=None, holderType=None, + dealWithOptional=False): + """ + template: A string representing the conversion code. This will have + template substitution performed on it as follows: + + ${val} replaced by an expression for the JS::Value in question + ${valPtr} is a pointer to the JS::Value in question + ${valHandle} is a handle to the JS::Value in question + ${holderName} replaced by the holder's name, if any + ${declName} replaced by the declaration's name + ${haveValue} replaced by an expression that evaluates to a boolean + for whether we have a JS::Value. Only used when + defaultValue is not None. + + declType: A CGThing representing the native C++ type we're converting + to. This is allowed to be None if the conversion code is + supposed to be used as-is. + + holderType: A CGThing representing the type of a "holder" which will + hold a possible reference to the C++ thing whose type we + returned in #1, or None if no such holder is needed. + + dealWithOptional: A boolean indicating whether the caller has to do + optional-argument handling. This should only be set + to true if the JS-to-native conversion is being done + for an optional argument or dictionary member with no + default value and if the returned template expects + both declType and holderType to be wrapped in + Optional<>, with ${declName} and ${holderName} + adjusted to point to the Value() of the Optional, and + Construct() calls to be made on the Optional<>s as + needed. + + ${declName} must be in scope before the code from 'template' is entered. + + If holderType is not None then ${holderName} must be in scope before + the code from 'template' is entered. + """ + assert isinstance(template, str) + assert declType is None or isinstance(declType, CGThing) + assert holderType is None or isinstance(holderType, CGThing) + self.template = template + self.declType = declType + self.holderType = holderType + self.dealWithOptional = dealWithOptional + # If this function is modified, modify CGNativeMember.getArg and # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype # and holdertype we end up using, because it needs to be able to return the code # that will convert those to the actual return value of the callback function. -def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, - isDefinitelyObject=False, - isMember=False, - isOptional=False, - invalidEnumValueFatal=True, - defaultValue=None, - treatNullAs="Default", - treatUndefinedAs="Default", - isEnforceRange=False, - isClamp=False, - isNullOrUndefined=False, - exceptionCode=None, - lenientFloatCode=None, - allowTreatNonCallableAsNull=False, - isCallbackReturnValue=False): +def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, + isDefinitelyObject=False, + isMember=False, + isOptional=False, + invalidEnumValueFatal=True, + defaultValue=None, + treatNullAs="Default", + treatUndefinedAs="Default", + isEnforceRange=False, + isClamp=False, + isNullOrUndefined=False, + exceptionCode=None, + lenientFloatCode=None, + allowTreatNonCallableAsNull=False, + isCallbackReturnValue=False): """ 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 testing whether we can convert the argument to the desired type. That means that failures to convert due to the JS value being the wrong type of 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 @@ -2423,19 +2474,20 @@ def getJSToNativeConversionTemplate(type 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. if isMember is not False, we're being converted from a property of some JS object, not from an actual method argument, so we can't rely on our jsval - being rooted or outliving us in any way. Callers can pass "Dictionary" or - "Variadic" to indicate that the conversion is for something that is a - dictionary member or a variadic argument respectively. + being rooted or outliving us in any way. Callers can pass "Dictionary", + "Variadic", or "Sequence" to indicate that the conversion is for something + that is a dictionary member, a variadic argument, or a sequence + respectively. If isOptional is true, then we are doing conversion of an optional argument with no default value. invalidEnumValueFatal controls whether an invalid enum value conversion attempt will throw (if true) or simply return without doing anything (if false). @@ -2452,46 +2504,17 @@ def getJSToNativeConversionTemplate(type If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] extended attributes on nullable callback functions will be honored. If isCallbackReturnValue is true, then the declType may be adjusted to make it easier to return from a callback. Since that type is never directly observable by any consumers of the callback code, this is OK. - The return value from this function is a tuple consisting of four things: - - 1) A string representing the conversion code. This will have template - substitution performed on it as follows: - - ${val} replaced by an expression for the JS::Value in question - ${valPtr} is a pointer to the JS::Value in question - ${valHandle} is a handle to the JS::Value in question - ${holderName} replaced by the holder's name, if any - ${declName} replaced by the declaration's name - ${haveValue} replaced by an expression that evaluates to a boolean - for whether we have a JS::Value. Only used when - defaultValue is not None. - - 2) A CGThing representing the native C++ type we're converting to - (declType). This is allowed to be None if the conversion code is - supposed to be used as-is. - 3) A CGThing representing the type of a "holder" (holderType) which will - hold a possible reference to the C++ thing whose type we returned in #1, - or None if no such holder is needed. - 4) A boolean indicating whether the caller has to do optional-argument handling. - This will only be true if isOptional is true and if the returned template - expects both declType and holderType to be wrapped in Optional<>, with - ${declName} and ${holderName} adjusted to point to the Value() of the - Optional, and Construct() calls to be made on the Optional<>s as needed. - - ${declName} must be in scope before the generated code is entered. - - If holderType is not None then ${holderName} must be in scope - before the generated code is entered. + The return value from this function is a JSToNativeConversionInfo. """ # If we have a defaultValue then we're not actually optional for # purposes of what we need to be declared as. assert(defaultValue is None or not isOptional) # Also, we should not have a defaultValue if we know we're an object assert(not isDefinitelyObject or defaultValue is None) @@ -2599,17 +2622,18 @@ def getJSToNativeConversionTemplate(type # If cx is null then LazyRootedObject will not be # constructed and behave as nullptr. templateBody = CGIfWrapper(CGGeneric(templateBody), "cx").define() setToNullCode = CGIfWrapper(CGGeneric(setToNullCode), "cx").define() template = wrapObjectTemplate(templateBody, type, setToNullCode, failureCode) - return (template, declType, None, isOptional) + return JSToNativeConversionInfo(template, declType=declType, + dealWithOptional=isOptional) assert not (isEnforceRange and isClamp) # These are mutually exclusive if type.isArray(): raise TypeError("Can't handle array arguments yet") if type.isSequence(): @@ -2643,27 +2667,26 @@ def getJSToNativeConversionTemplate(type # nullable and optional cases because we don't want to leak the # AutoSequence type to consumers, which would be unavoidable with # Nullable<AutoSequence> or Optional<AutoSequence>. if isMember or isOptional or nullable or isCallbackReturnValue: sequenceClass = "Sequence" else: sequenceClass = "AutoSequence" - (elementTemplate, elementDeclType, - elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate( - elementType, descriptorProvider, isMember=True, + elementInfo = getJSToNativeConversionInfo( + elementType, descriptorProvider, isMember="Sequence", exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode, isCallbackReturnValue=isCallbackReturnValue) - if dealWithOptional: + if elementInfo.dealWithOptional: raise TypeError("Shouldn't have optional things in sequences") - if elementHolderType is not None: + if elementInfo.holderType is not None: raise TypeError("Shouldn't need holders for sequences") - typeName = CGTemplatedType(sequenceClass, elementDeclType) + typeName = CGTemplatedType(sequenceClass, elementInfo.declType) sequenceType = typeName.define() if nullable: typeName = CGTemplatedType("Nullable", typeName) arrayRef = "${declName}.SetValue()" else: arrayRef = "${declName}" # NOTE: Keep this in sync with variadic conversions as needed @@ -2688,36 +2711,37 @@ for (uint32_t i = 0; i < length; ++i) { } %s& slot = *arr.AppendElement(); """ % (CGIndenter(CGGeneric(notSequence)).define(), exceptionCodeIndented.define(), sequenceType, arrayRef, exceptionCodeIndented.define(), CGIndenter(exceptionCodeIndented).define(), - elementDeclType.define())) + elementInfo.declType.define())) templateBody += CGIndenter(CGGeneric( - string.Template(elementTemplate).substitute( + string.Template(elementInfo.template).substitute( { "val" : "temp", "valPtr": "temp.address()", "valHandle": "temp", "declName" : "slot", # We only need holderName here to handle isExternal() # interfaces, which use an internal holder for the # conversion even when forceOwningType ends up true. "holderName": "tempHolder", } ))).define() templateBody += "\n}" templateBody = wrapObjectTemplate(templateBody, type, "${declName}.SetNull()") - return (templateBody, typeName, None, isOptional) + return JSToNativeConversionInfo(templateBody, declType=typeName, + dealWithOptional=isOptional) if type.isUnion(): if isMember: raise TypeError("Can't handle unions as members, we have a " "holderType") nullable = type.nullable(); if nullable: type = type.inner @@ -2900,17 +2924,19 @@ for (uint32_t i = 0; i < length; ++i) { assert(isinstance(defaultValue, IDLNullValue)) valueMissing = "!(${haveValue}) || " else: valueMissing = "" templateBody = handleNull(templateBody, mutableDecl, extraConditionForNull=valueMissing) templateBody = CGList([constructDecl, templateBody], "\n") - return templateBody.define(), declType, holderType, False + return JSToNativeConversionInfo(templateBody.define(), + declType=declType, + holderType=holderType) if type.isGeckoInterface(): assert not isEnforceRange and not isClamp descriptor = descriptorProvider.getDescriptor( type.unroll().inner.identifier.name) if (descriptor.interface.isCallback() and @@ -2924,17 +2950,18 @@ for (uint32_t i = 0; i < length; ++i) { else: declType = CGGeneric("OwningNonNull<%s>" % name) conversion = ( " ${declName} = new %s(&${val}.toObject());\n" % name) template = wrapObjectTemplate(conversion, type, "${declName} = nullptr", failureCode) - return (template, declType, None, isOptional) + return JSToNativeConversionInfo(template, declType=declType, + dealWithOptional=isOptional) # This is an interface that we implement as a concrete class # or an XPCOM interface. # Allow null pointers for nullable types and old-binding classes, and # use an nsRefPtr or raw pointer for callback return values to make # them easier to return. argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or @@ -3032,17 +3059,20 @@ for (uint32_t i = 0; i < length; ++i) { templateBody = wrapObjectTemplate(templateBody, type, "${declName} = nullptr", failureCode) declType = CGGeneric(declType) if holderType is not None: holderType = CGGeneric(holderType) - return (templateBody, declType, holderType, isOptional) + return JSToNativeConversionInfo(templateBody, + declType=declType, + holderType=holderType, + dealWithOptional=isOptional) if type.isSpiderMonkeyInterface(): assert not isEnforceRange and not isClamp if isMember: raise TypeError("Can't handle member arraybuffers or " "arraybuffer views because making sure all the " "objects are properly rooted is hard") name = type.name @@ -3087,17 +3117,19 @@ for (uint32_t i = 0; i < length; ++i) { template += "${declName} = ${holderName}.addr();" template = wrapObjectTemplate(template, type, "%s = nullptr" % nullableTarget, failureCode) if holderType is not None: holderType = CGGeneric(holderType) # We handle all the optional stuff ourselves; no need for caller to do it. - return (template, CGGeneric(declType), holderType, False) + return JSToNativeConversionInfo(template, + declType=CGGeneric(declType), + holderType=holderType) if type.isString(): assert not isEnforceRange and not isClamp treatAs = { "Default": "eStringify", "EmptyString": "eEmpty", "Null": "eNull" @@ -3133,36 +3165,36 @@ for (uint32_t i = 0; i < length; ++i) { "%s.SetData(data, ArrayLength(data) - 1)" % (", ".join(["'" + char + "'" for char in defaultValue.value] + ["0"]), varName))) if isMember: # We have to make a copy, because our jsval may well not # live as long as our string needs to. declType = CGGeneric("nsString") - return ( + return JSToNativeConversionInfo( "{\n" " FakeDependentString str;\n" "%s\n" " ${declName} = str;\n" "}\n" % CGIndenter(CGGeneric(getConversionCode("str"))).define(), - declType, None, isOptional) + declType=declType, dealWithOptional=isOptional) if isOptional: declType = "Optional<nsAString>" else: declType = "NonNull<nsAString>" - return ( + # No need to deal with optional here; we handled it already + return JSToNativeConversionInfo( "%s\n" "${declName} = &${holderName};" % getConversionCode("${holderName}"), - CGGeneric(declType), CGGeneric("FakeDependentString"), - # No need to deal with Optional here; we have handled it already - False) + declType=CGGeneric(declType), + holderType=CGGeneric("FakeDependentString")) if type.isEnum(): assert not isEnforceRange and not isClamp enumName = type.unroll().inner.identifier.name declType = CGGeneric(enumName) if type.nullable(): declType = CGTemplatedType("Nullable", declType) @@ -3214,17 +3246,18 @@ for (uint32_t i = 0; i < length; ++i) { assert type.nullable() template = handleDefault(template, setNull) else: assert(defaultValue.type.tag() == IDLType.Tags.domstring) template = handleDefault(template, ("%s = %s::%s" % (enumLoc, enumName, getEnumValueName(defaultValue.value)))) - return (template, CGGeneric(declType), None, isOptional) + return JSToNativeConversionInfo(template, declType=CGGeneric(declType), + dealWithOptional=isOptional) if type.isCallback(): assert not isEnforceRange and not isClamp assert not type.treatNonCallableAsNull() or type.nullable() if descriptorProvider.workers: if isMember: raise NoSuchDescriptorError("Can't handle member callbacks in " @@ -3262,17 +3295,18 @@ for (uint32_t i = 0; i < length; ++i) { "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" + conversion + "} else {\n" "%s" "}" % CGIndenter(onFailureNotCallable(failureCode)).define(), type, "${declName} = nullptr", failureCode) - return (template, declType, None, isOptional) + return JSToNativeConversionInfo(template, declType=declType, + dealWithOptional=isOptional) if type.isAny(): assert not isEnforceRange and not isClamp if isMember == "Dictionary" or not isMember: declType = "LazyRootedValue" templateBody = "${declName}.construct(cx, ${val});" nullHandling = "${declName}.construct(cx, JS::NullValue());" @@ -3281,17 +3315,19 @@ for (uint32_t i = 0; i < length; ++i) { "out rooting issues") else: # Variadic arguments are rooted by being in argv declType = "JS::Value" templateBody = "${declName} = ${val};" nullHandling = "${declName} = JS::NullValue()" templateBody = handleDefaultNull(templateBody, nullHandling) - return (templateBody, CGGeneric(declType), None, isOptional) + return JSToNativeConversionInfo(templateBody, + declType=CGGeneric(declType), + dealWithOptional=isOptional) if type.isObject(): assert not isEnforceRange and not isClamp return handleJSObjectType(type, isMember, failureCode) if type.isDictionary(): if failureCode is not None and not isDefinitelyObject: raise TypeError("Can't handle dictionaries when failureCode is " @@ -3330,23 +3366,23 @@ for (uint32_t i = 0; i < length; ++i) { "!IsObjectValueConvertibleToDictionary(cx, ${valHandle})").define() + "\n\n" else: template = "" template += ("if (!${declName}.Init(cx, %s)) {\n" "%s\n" "}" % (val, exceptionCodeIndented.define())) - return (template, declType, None, False) + return JSToNativeConversionInfo(template, declType=declType) if type.isVoid(): assert not isOptional # This one only happens for return values, and its easy: Just # ignore the jsval. - return ("", None, None, False) + return JSToNativeConversionInfo("") if type.isDate(): assert not isEnforceRange and not isClamp declType = CGGeneric("Date") if type.nullable(): declType = CGTemplatedType("Nullable", declType) dateVal = "${declName}.SetValue()" @@ -3363,17 +3399,19 @@ for (uint32_t i = 0; i < length; ++i) { "if (!JS_ObjectIsDate(cx, &${val}.toObject()) ||\n" " !%s.SetTimeStamp(cx, &${val}.toObject())) {\n" "%s\n" "}" % (dateVal, CGIndenter(CGGeneric(notDate)).define())) conversion = wrapObjectTemplate(conversion, type, "${declName}.SetNull()") - return (conversion, declType, None, isOptional) + return JSToNativeConversionInfo(conversion, + declType=declType, + dealWithOptional=isOptional) if not type.isPrimitive(): raise TypeError("Need conversion for argument type '%s'" % str(type)) typeName = builtinNames[type.tag()] conversionBehavior = "eDefault" if isEnforceRange: @@ -3433,30 +3471,31 @@ for (uint32_t i = 0; i < length; ++i) { defaultStr = toStringBool(defaultValue.value) template = CGWrapper(CGIndenter(CGGeneric(template)), pre="if (${haveValue}) {\n", post=("\n" "} else {\n" " %s = %s;\n" "}" % (writeLoc, defaultStr))).define() - return (template, declType, None, isOptional) - -def instantiateJSToNativeConversionTemplate(templateTuple, replacements, - argcAndIndex=None): - """ - Take a tuple as returned by getJSToNativeConversionTemplate and a set of - replacements as required by the strings in such a tuple, and generate code - to convert into stack C++ types. + return JSToNativeConversionInfo(template, declType=declType, + dealWithOptional=isOptional) + +def instantiateJSToNativeConversion(info, replacements, argcAndIndex=None): + """ + Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo + and a set of replacements as required by the strings in such an object, and + generate code to convert into stack C++ types. If argcAndIndex is not None it must be a dict that can be used to replace ${argc} and ${index}, where ${index} is the index of this argument (0-based) and ${argc} is the total number of arguments. """ - (templateBody, declType, holderType, dealWithOptional) = templateTuple + (templateBody, declType, holderType, dealWithOptional) = ( + info.template, info.declType, info.holderType, info.dealWithOptional) if dealWithOptional and argcAndIndex is None: raise TypeError("Have to deal with optional things, but don't know how") if argcAndIndex is not None and declType is None: raise TypeError("Need to predeclare optional things, so they will be " "outside the check for big enough arg count!"); result = CGList([], "\n") @@ -3572,63 +3611,62 @@ class CGArgumentConverter(CGThing): self.argcAndIndex = replacer else: self.argcAndIndex = None self.invalidEnumValueFatal = invalidEnumValueFatal self.lenientFloatCode = lenientFloatCode self.allowTreatNonCallableAsNull = allowTreatNonCallableAsNull def define(self): - typeConversion = getJSToNativeConversionTemplate( + typeConversion = getJSToNativeConversionInfo( self.argument.type, self.descriptorProvider, isOptional=(self.argcAndIndex is not None and not self.argument.variadic), invalidEnumValueFatal=self.invalidEnumValueFatal, defaultValue=self.argument.defaultValue, treatNullAs=self.argument.treatNullAs, treatUndefinedAs=self.argument.treatUndefinedAs, isEnforceRange=self.argument.enforceRange, isClamp=self.argument.clamp, lenientFloatCode=self.lenientFloatCode, isMember="Variadic" if self.argument.variadic else False, allowTreatNonCallableAsNull=self.allowTreatNonCallableAsNull) if not self.argument.variadic: - return instantiateJSToNativeConversionTemplate( + return instantiateJSToNativeConversion( typeConversion, self.replacementVariables, self.argcAndIndex).define() # Variadic arguments get turned into a sequence. - (elementTemplate, elementDeclType, - elementHolderType, dealWithOptional) = typeConversion - if dealWithOptional: + if typeConversion.dealWithOptional: raise TypeError("Shouldn't have optional things in variadics") - if elementHolderType is not None: + if typeConversion.holderType is not None: raise TypeError("Shouldn't need holders for variadics") replacer = dict(self.argcAndIndex, **self.replacementVariables) - replacer["seqType"] = CGTemplatedType("AutoSequence", elementDeclType).define() - replacer["elemType"] = elementDeclType.define() + replacer["seqType"] = CGTemplatedType("AutoSequence", + typeConversion.declType).define() + replacer["elemType"] = typeConversion.declType.define() # NOTE: Keep this in sync with sequence conversions as needed variadicConversion = string.Template("""${seqType} ${declName}; if (${argc} > ${index}) { if (!${declName}.SetCapacity(${argc} - ${index})) { JS_ReportOutOfMemory(cx); return false; } for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) { ${elemType}& slot = *${declName}.AppendElement(); """).substitute(replacer) val = string.Template("${argv}[variadicArg]").substitute(replacer) variadicConversion += CGIndenter(CGGeneric( - string.Template(elementTemplate).substitute( + string.Template(typeConversion.template).substitute( { "val" : val, "valPtr": "&" + val, "valHandle" : ("JS::Handle<JS::Value>::fromMarkedLocation(&%s)" % val), "declName" : "slot", # We only need holderName here to handle isExternal() # interfaces, which use an internal holder for the @@ -4052,17 +4090,17 @@ def getRetvalDeclarationForType(returnTy nullable = returnType.nullable() if nullable: returnType = returnType.inner # If our result is already addrefed, use the right type in the # sequence argument here. (result, _) = getRetvalDeclarationForType(returnType.inner, descriptorProvider, resultAlreadyAddRefed, - isMember=True) + isMember="Sequence") result = CGTemplatedType("nsTArray", result) if nullable: result = CGTemplatedType("Nullable", result) return result, True if returnType.isDictionary(): nullable = returnType.nullable() result = CGGeneric( CGDictionary.makeDictionaryName(returnType.unroll().inner, @@ -4626,21 +4664,21 @@ class CGMethodCall(CGThing): if isDefinitelyObject: failureCode = "break;" else: failureCode = None type = distinguishingType(signature) # The argument at index distinguishingIndex can't possibly # be unset here, because we've already checked that argc is # large enough that we can examine this argument. - testCode = instantiateJSToNativeConversionTemplate( - getJSToNativeConversionTemplate(type, descriptor, - failureCode=failureCode, - isDefinitelyObject=isDefinitelyObject, - isNullOrUndefined=isNullOrUndefined), + testCode = instantiateJSToNativeConversion( + getJSToNativeConversionInfo(type, descriptor, + failureCode=failureCode, + isDefinitelyObject=isDefinitelyObject, + isNullOrUndefined=isNullOrUndefined), { "declName" : "arg%d" % distinguishingIndex, "holderName" : ("arg%d" % distinguishingIndex) + "_holder", "val" : distinguishingArg, "valHandle" : ("JS::Handle<JS::Value>::fromMarkedLocation(&%s)" % distinguishingArg), "obj" : "obj" }) @@ -5441,20 +5479,20 @@ def getUnionAccessorSignatureType(type, raise TypeError("Can't handle array arguments yet") if type.isSequence(): nullable = type.nullable(); if nullable: type = type.inner.inner else: type = type.inner - (elementTemplate, elementDeclType, - elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate( - type, descriptorProvider, isSequenceMember=True) - typeName = CGTemplatedType("Sequence", elementDeclType, isReference=True) + elementInfo = getJSToNativeConversionInfo(type, descriptorProvider, + isMember="Sequence") + typeName = CGTemplatedType("Sequence", elementInfo.declType, + isReference=True) if nullable: typeName = CGTemplatedType("Nullable", typeName, isReference=True) return typeName if type.isUnion(): typeName = CGGeneric(type.name) if type.nullable(): @@ -5517,17 +5555,17 @@ def getUnionAccessorSignatureType(type, typeName = CGGeneric(builtinNames[type.tag()]) if type.nullable(): typeName = CGTemplatedType("Nullable", typeName, isReference=True) return typeName def getUnionTypeTemplateVars(type, descriptorProvider): # For dictionaries and sequences we need to pass None as the failureCode - # for getJSToNativeConversionTemplate. + # for getJSToNativeConversionInfo. # Also, for dictionaries we would need to handle conversion of # null/undefined to the dictionary correctly. if type.isDictionary() or type.isSequence(): raise TypeError("Can't handle dictionaries or sequences in unions") if type.isGeckoInterface(): name = type.inner.identifier.name elif type.isEnum(): @@ -5538,36 +5576,35 @@ def getUnionTypeTemplateVars(type, descr name = type.name tryNextCode = """tryNext = true; return true;""" if type.isGeckoInterface(): tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) { mUnion.Destroy%s(); }""" % name) + tryNextCode - (template, declType, holderType, - dealWithOptional) = getJSToNativeConversionTemplate( + conversionInfo = getJSToNativeConversionInfo( type, descriptorProvider, failureCode=tryNextCode, isDefinitelyObject=True) # This is ugly, but UnionMember needs to call a constructor with no # arguments so the type can't be const. - structType = declType.define() + structType = conversionInfo.declType.define() if structType.startswith("const "): structType = structType[6:] externalType = getUnionAccessorSignatureType(type, descriptorProvider).define() if type.isObject(): setter = CGGeneric("void SetToObject(JSContext* cx, JSObject* obj)\n" "{\n" " mUnion.mValue.mObject.SetValue().construct(cx, obj);\n" " mUnion.mType = mUnion.eObject;\n" "}") else: - jsConversion = string.Template(template).substitute( + jsConversion = string.Template(conversionInfo.template).substitute( { "val": "value", "valHandle": "value", "valPtr": "pvalue", "declName": "SetAs" + name + "()", "holderName": "m" + name + "Holder", } ) @@ -5581,17 +5618,17 @@ return true;""" post="\n" "}") return { "name": name, "structType": structType, "externalType": externalType, "setter": CGIndenter(setter).define(), - "holderType": holderType.define() if holderType else None + "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None } def mapTemplate(template, templateVarArray): return map(lambda v: string.Template(template).substitute(v), templateVarArray) class CGUnionStruct(CGThing): def __init__(self, type, descriptorProvider): @@ -6371,28 +6408,28 @@ class CGProxySpecialOperation(CGPerSigna # CGPerSignatureCall won't do any argument conversion of its own. CGPerSignatureCall.__init__(self, returnType, arguments, nativeName, False, descriptor, operation, len(arguments)) if operation.isSetter() or operation.isCreator(): # arguments[0] is the index or name of the item that we're setting. argument = arguments[1] - template = getJSToNativeConversionTemplate(argument.type, descriptor, - treatNullAs=argument.treatNullAs, - treatUndefinedAs=argument.treatUndefinedAs) + info = getJSToNativeConversionInfo(argument.type, descriptor, + treatNullAs=argument.treatNullAs, + treatUndefinedAs=argument.treatUndefinedAs) templateValues = { "declName": argument.identifier.name, "holderName": argument.identifier.name + "_holder", "val": "desc->value", "valPtr": "&desc->value", "valHandle" : "JS::Handle<JS::Value>::fromMarkedLocation(&desc->value)", "obj": "obj" } - self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues)) + self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues)) elif operation.isGetter() or operation.isDeleter(): self.cgRoot.prepend(CGGeneric("bool found;")) def getArguments(self): args = [(a, a.identifier.name) for a in self.arguments] if self.idlNode.isGetter() or self.idlNode.isDeleter(): args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], self.idlNode), @@ -7348,21 +7385,21 @@ class CGNamespacedEnum(CGThing): class CGDictionary(CGThing): def __init__(self, dictionary, descriptorProvider): self.dictionary = dictionary self.descriptorProvider = descriptorProvider self.workers = descriptorProvider.workers self.needToInitIds = not self.workers and len(dictionary.members) > 0 self.memberInfo = [ (member, - getJSToNativeConversionTemplate(member.type, - descriptorProvider, - isMember="Dictionary", - isOptional=(not member.defaultValue), - defaultValue=member.defaultValue)) + getJSToNativeConversionInfo(member.type, + descriptorProvider, + isMember="Dictionary", + isOptional=(not member.defaultValue), + defaultValue=member.defaultValue)) for member in dictionary.members ] def declare(self): d = self.dictionary if d.parent: inheritance = ": public %s " % self.makeClassName(d.parent) elif not self.workers: inheritance = ": public MainThreadDictionaryBase " @@ -7518,38 +7555,37 @@ class CGDictionary(CGThing): def makeClassName(self, dictionary): return self.makeDictionaryName(dictionary, self.workers) @staticmethod def makeMemberName(name): return "m" + name[0].upper() + name[1:] def getMemberType(self, memberInfo): - (member, (templateBody, declType, - holderType, dealWithOptional)) = memberInfo + (_, conversionInfo) = memberInfo # We can't handle having a holderType here - assert holderType is None - if dealWithOptional: + assert conversionInfo.holderType is None + declType = conversionInfo.declType + if conversionInfo.dealWithOptional: declType = CGTemplatedType("Optional", declType) return declType.define() def getMemberConversion(self, memberInfo): - (member, (templateBody, declType, - holderType, dealWithOptional)) = memberInfo + (member, conversionInfo) = memberInfo replacements = { "val": "temp", "valPtr": "temp.address()", "valHandle": "temp", "declName": self.makeMemberName(member.identifier.name), # We need a holder name for external interfaces, but # it's scoped down to the conversion so we can just use # anything we want. "holderName": "holder" } # We can't handle having a holderType here - assert holderType is None - if dealWithOptional: + assert conversionInfo.holderType is None + if conversionInfo.dealWithOptional: replacements["declName"] = "(" + replacements["declName"] + ".Value())" if member.defaultValue: replacements["haveValue"] = "found" # NOTE: jsids are per-runtime, so don't use them in workers if self.workers: propName = member.identifier.name propCheck = ('JS_HasProperty(cx, &val.toObject(), "%s", &found)' % @@ -7560,17 +7596,17 @@ class CGDictionary(CGThing): propId = self.makeIdName(member.identifier.name); propCheck = ("JS_HasPropertyById(cx, &val.toObject(), %s, &found)" % propId) propGet = ("JS_GetPropertyById(cx, &val.toObject(), %s, temp.address())" % propId) conversionReplacements = { "prop": self.makeMemberName(member.identifier.name), - "convert": string.Template(templateBody).substitute(replacements), + "convert": string.Template(conversionInfo.template).substitute(replacements), "propCheck": propCheck, "propGet": propGet } conversion = ("if (isNull) {\n" " found = false;\n" "} else if (!${propCheck}) {\n" " return false;\n" "}\n") @@ -7595,17 +7631,17 @@ class CGDictionary(CGThing): CGGeneric(conversionReplacements["convert"])).define() return CGGeneric( string.Template(conversion).substitute(conversionReplacements) ) def getMemberDefinition(self, memberInfo): member = memberInfo[0] - declType = memberInfo[1][1] + declType = memberInfo[1].declType memberLoc = self.makeMemberName(member.identifier.name) if member.defaultValue: memberData = memberLoc else: # The data is inside the Optional<> memberData = "%s.Value()" % memberLoc if self.workers: @@ -9140,21 +9176,21 @@ class CallbackMember(CGNativeMember): "holderName" : "rvalHolder", "declName" : "rvalDecl", # We actually want to pass in a null scope object here, because # wrapping things into our current compartment (that of mCallback) # is what we want. "obj": "nullptr" } - convertType = instantiateJSToNativeConversionTemplate( - getJSToNativeConversionTemplate(self.retvalType, - self.descriptor, - exceptionCode=self.exceptionCode, - isCallbackReturnValue=True), + convertType = instantiateJSToNativeConversion( + getJSToNativeConversionInfo(self.retvalType, + self.descriptor, + exceptionCode=self.exceptionCode, + isCallbackReturnValue=True), replacements) assignRetval = string.Template( self.getRetvalInfo(self.retvalType, False)[2]).substitute(replacements) return convertType.define() + "\n" + assignRetval def getArgConversions(self): # Just reget the arglist from self.originalSig, because our superclasses