Bug 1561564 - Introduce a JSString WebIDL type. r=bzbarsky

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Henri Sivonen 2019-09-12 10:27:20 +00:00
parent 69618b0d2e
commit b9752dabba
9 changed files with 200 additions and 10 deletions

View File

@ -1800,6 +1800,14 @@ bool AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
enum StringificationBehavior { eStringify, eEmpty, eNull };
static inline JSString* ConvertJSValueToJSString(JSContext* cx,
JS::Handle<JS::Value> v) {
if (MOZ_LIKELY(v.isString())) {
return v.toString();
}
return JS::ToString(cx, v);
}
template <typename T>
static inline bool ConvertJSValueToString(
JSContext* cx, JS::Handle<JS::Value> v,

View File

@ -5075,7 +5075,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
})
keyType = recordKeyType(recordType)
if recordType.keyType.isByteString():
if recordType.keyType.isJSString():
raise TypeError("Have do deal with JSString record type, but don't know how")
elif recordType.keyType.isByteString():
keyConversionFunction = "ConvertJSValueToByteString"
hashKeyType = "nsCStringHashKey"
else:
@ -5876,6 +5878,45 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
declArgs=declArgs,
holderArgs=holderArgs)
if type.isJSString():
assert not isEnforceRange and not isClamp
if type.nullable():
raise TypeError("Nullable JSString not supported");
declArgs = "cx"
if isMember:
raise TypeError("JSString not supported as member")
else:
declType = "JS::Rooted<JSString*>"
if isOptional:
raise TypeError("JSString not supported as optional");
templateBody = fill("""
if (!($${declName} = ConvertJSValueToJSString(cx, $${val}))) {
$*{exceptionCode}
}
"""
,
exceptionCode=exceptionCode)
if defaultValue is not None:
assert not isinstance(defaultValue, IDLNullValue)
defaultCode = fill("""
static const char data[] = { ${data} };
$${declName} = JS_NewStringCopyN(cx, data, ArrayLength(data) - 1);
if (!$${declName}) {
$*{exceptionCode}
}
""",
data=", ".join(["'" + char + "'" for char in
defaultValue.value] + ["0"]),
exceptionCode=exceptionCode)
templateBody = handleDefault(templateBody, defaultCode)
return JSToNativeConversionInfo(templateBody,
declType=CGGeneric(declType),
declArgs=declArgs)
if type.isDOMString() or type.isUSVString():
assert not isEnforceRange and not isClamp
@ -6619,6 +6660,8 @@ class CGArgumentConverter(CGThing):
def getMaybeWrapValueFuncForType(type):
if type.isJSString():
return "MaybeWrapStringValue"
# Callbacks might actually be DOM objects; nothing prevents a page from
# doing that.
if type.isCallback() or type.isCallbackInterface() or type.isObject():
@ -6687,7 +6730,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
return _setValue(value, setter="setInt32")
def setString(value):
return _setValue(value, setter="setString")
return _setValue(value, wrapAsType=type, setter="setString")
def setObject(value, wrapAsType=None):
return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
@ -6949,6 +6992,9 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
wrappingCode += wrapAndSetPtr(wrap, failed)
return (wrappingCode, False)
if type.isJSString():
return (setString(result), False)
if type.isDOMString() or type.isUSVString():
if type.nullable():
return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False)
@ -7147,7 +7193,7 @@ def infallibleForMember(member, type, descriptorProvider):
def leafTypeNeedsCx(type, retVal):
return (type.isAny() or type.isObject() or
return (type.isAny() or type.isObject() or type.isJSString() or
(retVal and type.isSpiderMonkeyInterface()))
@ -7226,6 +7272,10 @@ def getRetvalDeclarationForType(returnType, descriptorProvider,
if returnType.nullable():
result = CGTemplatedType("Nullable", result)
return result, None, None, None, None
if returnType.isJSString():
if isMember:
raise TypeError("JSString not supported as return type member")
return CGGeneric("JS::Rooted<JSString*>"), "ptr", None, "cx", None
if returnType.isDOMString() or returnType.isUSVString():
if isMember:
return CGGeneric("nsString"), "ref", None, None, None
@ -10178,6 +10228,9 @@ def getUnionAccessorSignatureType(type, descriptorProvider):
typeName = CGGeneric(type.name)
return CGWrapper(typeName, post=" const &")
if type.isJSString():
raise TypeError("JSString not supported in unions")
if type.isDOMString() or type.isUSVString():
return CGGeneric("const nsAString&")
@ -14978,6 +15031,11 @@ class CGNativeMember(ClassMethod):
return (result.define(),
"%s(%s)" % (result.define(), defaultReturnArg),
"return ${declName};\n")
if type.isJSString():
if isMember:
raise TypeError("JSString not supported as return type member")
# Outparam
return "void", "", "aRetVal.set(${declName});\n"
if type.isDOMString() or type.isUSVString():
if isMember:
# No need for a third element in the isMember case
@ -15101,7 +15159,9 @@ class CGNativeMember(ClassMethod):
def getArgs(self, returnType, argList):
args = [self.getArg(arg) for arg in argList]
# Now the outparams
if returnType.isDOMString() or returnType.isUSVString():
if returnType.isJSString():
args.append(Argument("JS::MutableHandle<JSString*>", "aRetVal"))
elif returnType.isDOMString() or returnType.isUSVString():
args.append(Argument("nsString&", "aRetVal"))
elif returnType.isByteString():
args.append(Argument("nsCString&", "aRetVal"))
@ -15252,6 +15312,11 @@ class CGNativeMember(ClassMethod):
# Unroll for the name, in case we're nullable.
return type.unroll().name, True, True
if type.isJSString():
if isMember:
raise TypeError("JSString not supported as member")
return "JS::Handle<JSString*>", False, False
if type.isDOMString() or type.isUSVString():
if isMember:
declType = "nsString"
@ -18178,6 +18243,9 @@ class CGEventGetter(CGNativeMember):
type.isPromise() or
type.isGeckoInterface()):
return "return " + memberName + ";\n"
if type.isJSString():
# https://bugzilla.mozilla.org/show_bug.cgi?id=1580167
raise TypeError("JSString not supported as member of a generated event")
if type.isDOMString() or type.isByteString() or type.isUSVString():
return "aRetVal = " + memberName + ";\n"
if type.isSpiderMonkeyInterface() or type.isObject():
@ -18624,6 +18692,8 @@ class CGEventClass(CGBindingImplClass):
nativeType = CGGeneric(type.unroll().inner.identifier.name)
if type.nullable():
nativeType = CGTemplatedType("Nullable", nativeType)
elif type.isJSString():
nativeType = CGGeneric("JS::Heap<JSString*>")
elif type.isDOMString() or type.isUSVString():
nativeType = CGGeneric("nsString")
elif type.isByteString():

View File

@ -2157,6 +2157,7 @@ class IDLType(IDLObject):
'domstring',
'bytestring',
'usvstring',
'jsstring',
'object',
'date',
'void',
@ -2218,6 +2219,9 @@ class IDLType(IDLObject):
def isUSVString(self):
return False
def isJSString(self):
return False
def isVoid(self):
return self.name == "Void"
@ -2443,6 +2447,9 @@ class IDLNullableType(IDLParametrizedType):
def isUSVString(self):
return self.inner.isUSVString()
def isJSString(self):
return self.inner.isJSString()
def isFloat(self):
return self.inner.isFloat()
@ -2564,6 +2571,9 @@ class IDLSequenceType(IDLParametrizedType):
def isUSVString(self):
return False
def isJSString(self):
return False
def isVoid(self):
return False
@ -2817,6 +2827,9 @@ class IDLTypedefType(IDLType):
def isUSVString(self):
return self.inner.isUSVString()
def isJSString(self):
return self.inner.isJSString()
def isVoid(self):
return self.inner.isVoid()
@ -2947,6 +2960,9 @@ class IDLWrapperType(IDLType):
def isUSVString(self):
return False
def isJSString(self):
return False
def isVoid(self):
return False
@ -3146,6 +3162,7 @@ class IDLBuiltinType(IDLType):
'domstring',
'bytestring',
'usvstring',
'jsstring',
'object',
'date',
'void',
@ -3183,6 +3200,7 @@ class IDLBuiltinType(IDLType):
Types.domstring: IDLType.Tags.domstring,
Types.bytestring: IDLType.Tags.bytestring,
Types.usvstring: IDLType.Tags.usvstring,
Types.jsstring: IDLType.Tags.jsstring,
Types.object: IDLType.Tags.object,
Types.date: IDLType.Tags.date,
Types.void: IDLType.Tags.void,
@ -3267,7 +3285,8 @@ class IDLBuiltinType(IDLType):
def isString(self):
return (self._typeTag == IDLBuiltinType.Types.domstring or
self._typeTag == IDLBuiltinType.Types.bytestring or
self._typeTag == IDLBuiltinType.Types.usvstring)
self._typeTag == IDLBuiltinType.Types.usvstring or
self._typeTag == IDLBuiltinType.Types.jsstring)
def isByteString(self):
return self._typeTag == IDLBuiltinType.Types.bytestring
@ -3278,6 +3297,9 @@ class IDLBuiltinType(IDLType):
def isUSVString(self):
return self._typeTag == IDLBuiltinType.Types.usvstring
def isJSString(self):
return self._typeTag == IDLBuiltinType.Types.jsstring
def isInteger(self):
return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
@ -3480,6 +3502,9 @@ BuiltinTypes = {
IDLBuiltinType.Types.usvstring:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "USVString",
IDLBuiltinType.Types.usvstring),
IDLBuiltinType.Types.jsstring:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "JSString",
IDLBuiltinType.Types.jsstring),
IDLBuiltinType.Types.object:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object",
IDLBuiltinType.Types.object),
@ -3646,8 +3671,8 @@ class IDLValue(IDLObject):
# TreatNullAsEmpty is a different type for resolution reasons,
# however once you have a value it doesn't matter
return self
elif self.type.isString() and type.isByteString():
# Allow ByteStrings to use a default value like DOMString.
elif self.type.isString() and (type.isByteString() or type.isJSString()):
# Allow ByteStrings and JSStrings to use a default value like DOMString.
# No coercion is required as Codegen.py will handle the
# extra steps. We want to make sure that our string contains
# only valid characters, so we check that here.
@ -5671,6 +5696,7 @@ class Tokenizer(object):
"DOMString": "DOMSTRING",
"ByteString": "BYTESTRING",
"USVString": "USVSTRING",
"JSString": "JSSTRING",
"any": "ANY",
"boolean": "BOOLEAN",
"byte": "BYTE",
@ -6897,6 +6923,7 @@ class Parser(Tokenizer):
| DOMSTRING
| BYTESTRING
| USVSTRING
| JSSTRING
| ANY
| ATTRIBUTE
| BOOLEAN
@ -7179,6 +7206,12 @@ class Parser(Tokenizer):
"""
p[0] = IDLBuiltinType.Types.usvstring
def p_BuiltinStringTypeJSString(self, p):
"""
BuiltinStringType : JSSTRING
"""
p[0] = IDLBuiltinType.Types.jsstring
def p_UnsignedIntegerTypeUnsigned(self, p):
"""
UnsignedIntegerType : UNSIGNED IntegerType

View File

@ -205,6 +205,18 @@ def WebIDLTest(parser, harness):
harness.ok(threw, "Should not allow [TreatNullAs] on long")
parser = parser.reset()
threw = False
try:
parser.parse("""
typedef [TreatNullAs=EmptyString] JSString Foo;
""")
parser.finish()
except:
threw = True
harness.ok(threw, "Should not allow [TreatNullAs] on JSString")
parser = parser.reset()
threw = False
try:

View File

@ -166,7 +166,7 @@ def WebIDLTest(parser, harness):
"record<ByteString, long>",
"Date", "Date?", "any",
"Promise<any>", "Promise<any>?",
"USVString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer",
"USVString", "JSString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer",
"Uint8Array", "Uint16Array",
"(long or Callback)", "(long or Dict)",
]
@ -183,7 +183,7 @@ def WebIDLTest(parser, harness):
primitives = numerics + booleans
nonNumerics = allBut(argTypes, numerics + unions)
nonBooleans = allBut(argTypes, booleans)
strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString" ]
strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString", "JSString" ]
nonStrings = allBut(argTypes, strings)
nonObjects = primitives + strings
objects = allBut(argTypes, nonObjects )
@ -202,7 +202,7 @@ def WebIDLTest(parser, harness):
notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] +
otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes)
records = [ "record<DOMString, object>", "record<USVString, Dict>",
"record<ByteString, long>" ]
"record<ByteString, long>" ] # JSString not supported in records
# Build a representation of the distinguishability table as a dict
# of dicts, holding True values where needed, holes elsewhere.
@ -222,6 +222,7 @@ def WebIDLTest(parser, harness):
setDistinguishable("DOMString", nonStrings)
setDistinguishable("ByteString", nonStrings)
setDistinguishable("USVString", nonStrings)
setDistinguishable("JSString", nonStrings)
setDistinguishable("Enum", nonStrings)
setDistinguishable("Enum2", nonStrings)
setDistinguishable("Interface", notRelatedInterfaces)
@ -244,6 +245,7 @@ def WebIDLTest(parser, harness):
allBut(argTypes, sequences + ["object"]))
setDistinguishable("record<DOMString, object>", nonUserObjects)
setDistinguishable("record<USVString, Dict>", nonUserObjects)
# JSString not supported in records
setDistinguishable("record<ByteString, long>", nonUserObjects)
setDistinguishable("Date", allBut(argTypes, dates + ["object"]))
setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"]))

View File

@ -574,6 +574,14 @@ class TestInterface : public nsISupports, public nsWrapperCache {
void PassVariadicUSVS(const Sequence<nsString>&);
void ReceiveUSVS(DOMString&);
// JSString types
void PassJSString(JSContext*, JS::Handle<JSString*>);
void PassOptionalJSStringWithDefaultValue(JSContext*, JS::Handle<JSString*>);
void ReceiveJSString(JSContext*, JS::MutableHandle<JSString*>);
void GetReadonlyJSStringAttr(JSContext*, JS::MutableHandle<JSString*>);
void GetJsStringAttr(JSContext*, JS::MutableHandle<JSString*>);
void SetJsStringAttr(JSContext*, JS::Handle<JSString*>);
// Enumerated types
void PassEnum(TestEnum);
void PassNullableEnum(const Nullable<TestEnum>&);

View File

@ -513,6 +513,24 @@ interface TestInterface {
void passVariadicUSVS(USVString... arg);
USVString receiveUSVS();
// JSString types
void passJSString(JSString arg);
// void passNullableJSString(JSString? arg); // NOT SUPPORTED YET
// void passOptionalJSString(optional JSString arg); // NOT SUPPORTED YET
void passOptionalJSStringWithDefaultValue(optional JSString arg = "abc");
// void passOptionalNullableJSString(optional JSString? arg); // NOT SUPPORTED YET
// void passOptionalNullableJSStringWithDefaultValue(optional JSString? arg = null); // NOT SUPPORTED YET
// void passVariadicJSString(JSString... arg); // NOT SUPPORTED YET
// void passRecordOfJSString(record<DOMString, JSString> arg); // NOT SUPPORTED YET
// void passSequenceOfJSString(sequence<JSString> arg); // NOT SUPPORTED YET
// void passUnionJSString((JSString or long) arg); // NOT SUPPORTED YET
JSString receiveJSString();
// sequence<JSString> receiveJSStringSequence(); // NOT SUPPORTED YET
// (JSString or long) receiveJSStringUnion(); // NOT SUPPORTED YET
// record<DOMString, JSString> receiveJSStringRecord(); // NOT SUPPORTED YET
readonly attribute JSString readonlyJSStringAttr;
attribute JSString jsStringAttr;
// Enumerated types
void passEnum(TestEnum arg);
void passNullableEnum(TestEnum? arg);
@ -1093,6 +1111,7 @@ dictionary Dict : ParentDict {
ByteString byteStr;
ByteString emptyByteStr = "";
ByteString otherByteStr = "def";
// JSString jsStr; // NOT SUPPORTED YET
object someObj;
boolean prototype;
object? anotherObj = null;
@ -1356,6 +1375,7 @@ interface TestCEReactionsInterface {
typedef [EnforceRange] octet OctetRange;
typedef [Clamp] octet OctetClamp;
typedef [TreatNullAs=EmptyString] DOMString NullEmptyString;
// typedef [TreatNullAs=EmptyString] JSString NullEmptyJSString;
dictionary TestAttributesOnDictionaryMembers {
[Clamp] octet a;
@ -1370,6 +1390,7 @@ interface TestAttributesOnTypes {
void foo(OctetClamp thingy);
void bar(OctetRange thingy);
void baz(NullEmptyString thingy);
// void qux(NullEmptyJSString thingy);
attribute [Clamp] octet someAttr;
void argWithAttr([Clamp] octet arg0, optional [Clamp] octet arg1);
// There aren't any argument-only attributes that we can test here,

View File

@ -361,6 +361,24 @@ interface TestExampleInterface {
void passVariadicSVS(USVString... arg);
USVString receiveSVS();
// JSString types
void passJSString(JSString arg);
// void passNullableJSString(JSString? arg); // NOT SUPPORTED YET
// void passOptionalJSString(optional JSString arg); // NOT SUPPORTED YET
void passOptionalJSStringWithDefaultValue(optional JSString arg = "abc");
// void passOptionalNullableJSString(optional JSString? arg); // NOT SUPPORTED YET
// void passOptionalNullableJSStringWithDefaultValue(optional JSString? arg = null); // NOT SUPPORTED YET
// void passVariadicJSString(JSString... arg); // NOT SUPPORTED YET
// void passRecordOfJSString(record<DOMString, JSString> arg); // NOT SUPPORTED YET
// void passSequenceOfJSString(sequence<JSString> arg); // NOT SUPPORTED YET
// void passUnionJSString((JSString or long) arg); // NOT SUPPORTED YET
JSString receiveJSString();
// sequence<JSString> receiveJSStringSequence(); // NOT SUPPORTED YET
// (JSString or long) receiveJSStringUnion(); // NOT SUPPORTED YET
// record<DOMString, JSString> receiveJSStringRecord(); // NOT SUPPORTED YET
readonly attribute JSString readonlyJSStringAttr;
attribute JSString jsStringAttr;
// Enumerated types
void passEnum(TestEnum arg);
void passNullableEnum(TestEnum? arg);

View File

@ -375,6 +375,24 @@ interface TestJSImplInterface {
void passVariadicSVS(USVString... arg);
USVString receiveSVS();
// JSString types
void passJSString(JSString arg);
// void passNullableJSString(JSString? arg); // NOT SUPPORTED YET
// void passOptionalJSString(optional JSString arg); // NOT SUPPORTED YET
void passOptionalJSStringWithDefaultValue(optional JSString arg = "abc");
// void passOptionalNullableJSString(optional JSString? arg); // NOT SUPPORTED YET
// void passOptionalNullableJSStringWithDefaultValue(optional JSString? arg = null); // NOT SUPPORTED YET
// void passVariadicJSString(JSString... arg); // NOT SUPPORTED YET
// void passRecordOfJSString(record<DOMString, JSString> arg); // NOT SUPPORTED YET
// void passSequenceOfJSString(sequence<JSString> arg); // NOT SUPPORTED YET
// void passUnionJSString((JSString or long) arg); // NOT SUPPORTED YET
JSString receiveJSString();
// sequence<JSString> receiveJSStringSequence(); // NOT SUPPORTED YET
// (JSString or long) receiveJSStringUnion(); // NOT SUPPORTED YET
// record<DOMString, JSString> receiveJSStringRecord(); // NOT SUPPORTED YET
readonly attribute JSString readonlyJSStringAttr;
attribute JSString jsStringAttr;
// Enumerated types
void passEnum(MyTestEnum arg);
void passNullableEnum(MyTestEnum? arg);