aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/codegen
diff options
context:
space:
mode:
authoryvt <i@yvt.jp>2021-07-10 17:24:27 +0900
committeryvt <i@yvt.jp>2021-07-10 17:55:42 +0900
commit01a7de50ab1843d85295f9dccad7f4c099e7208c (patch)
treeee53fb6e8889deb7b880ee969e6c662e6128d210 /components/script/dom/bindings/codegen
parentff8d2cdbbfc7a9dc7f38b7dd47cb350fde39388f (diff)
parent94b613fbdaa2b98f2179fc0bbda13c64e6fa0d38 (diff)
downloadservo-01a7de50ab1843d85295f9dccad7f4c099e7208c.tar.gz
servo-01a7de50ab1843d85295f9dccad7f4c099e7208c.zip
Merge remote-tracking branch 'upstream/master' into feat-cow-infra
`tests/wpt/web-platform-tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html` was reverted to the upstream version.
Diffstat (limited to 'components/script/dom/bindings/codegen')
-rw-r--r--components/script/dom/bindings/codegen/BindingGen.py54
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf133
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py2948
-rw-r--r--components/script/dom/bindings/codegen/Configuration.py138
-rw-r--r--components/script/dom/bindings/codegen/GlobalGen.py90
-rw-r--r--components/script/dom/bindings/codegen/parser/WebIDL.py3021
-rw-r--r--components/script/dom/bindings/codegen/parser/abstract.patch10
-rw-r--r--components/script/dom/bindings/codegen/parser/callback-location.patch16
-rw-r--r--components/script/dom/bindings/codegen/parser/debug.patch2
-rw-r--r--components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch11
-rw-r--r--components/script/dom/bindings/codegen/parser/inline.patch10
-rw-r--r--components/script/dom/bindings/codegen/parser/pref-main-thread.patch28
-rw-r--r--components/script/dom/bindings/codegen/parser/runtests.py4
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_argument_keywords.py17
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_attr.py8
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py415
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_callback.py3
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_callback_constructor.py63
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_cereactions.py133
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py12
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_const.py19
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_constructor.py377
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_constructor_global.py63
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py27
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_date.py15
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_dictionary.py213
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py88
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py28
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py2
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_error_colno.py2
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py2
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py86
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py8
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_float_types.py10
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py34
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_identifier_conflict.py39
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_implements.py216
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interface.py179
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py200
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py437
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_method.py92
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_namespace.py18
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_newobject.py70
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py2
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_promise.py102
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_record.py (renamed from components/script/dom/bindings/codegen/parser/tests/test_mozmap.py)28
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py17
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py70
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_methods.py52
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py17
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_stringifier.py100
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_toJSON.py195
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_typedef.py8
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py16
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py6
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py30
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_union.py2
-rw-r--r--components/script/dom/bindings/codegen/parser/union-typedef.patch18
-rwxr-xr-xcomponents/script/dom/bindings/codegen/parser/update.sh7
-rw-r--r--components/script/dom/bindings/codegen/ply/README2
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/__init__.py2
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/lex.py853
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/yacc.py2576
-rw-r--r--components/script/dom/bindings/codegen/pythonpath.py61
-rw-r--r--components/script/dom/bindings/codegen/run.py119
65 files changed, 8130 insertions, 5494 deletions
diff --git a/components/script/dom/bindings/codegen/BindingGen.py b/components/script/dom/bindings/codegen/BindingGen.py
deleted file mode 100644
index 79576b67e58..00000000000
--- a/components/script/dom/bindings/codegen/BindingGen.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# 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/.
-
-import sys
-import os
-sys.path.append(os.path.join(".", "parser"))
-sys.path.append(os.path.join(".", "ply"))
-import cPickle
-from Configuration import Configuration
-from CodegenRust import CGBindingRoot, replaceFileIfChanged
-
-
-def generate_binding_rs(config, outputprefix, webidlfile):
- """
- |config| Is the configuration object.
- |outputprefix| is a prefix to use for the header guards and filename.
- """
-
- filename = outputprefix + ".rs"
- module = CGBindingRoot(config, outputprefix, webidlfile).define()
- if not module:
- print "Skipping empty module: %s" % (filename)
- elif replaceFileIfChanged(filename, module):
- print "Generating binding implementation: %s" % (filename)
-
-
-def main():
- # Parse arguments.
- from optparse import OptionParser
- usagestring = "usage: %prog configFile outputdir outputPrefix webIDLFile"
- o = OptionParser(usage=usagestring)
- (options, args) = o.parse_args()
-
- if len(args) != 4:
- o.error(usagestring)
- configFile = os.path.normpath(args[0])
- outputdir = args[1]
- outputPrefix = args[2]
- webIDLFile = os.path.normpath(args[3])
-
- # Load the parsing results
- resultsPath = os.path.join(outputdir, 'ParserResults.pkl')
- with open(resultsPath, 'rb') as f:
- parserData = cPickle.load(f)
-
- # Create the configuration data.
- config = Configuration(configFile, parserData)
-
- # Generate the prototype classes.
- generate_binding_rs(config, outputPrefix, webIDLFile)
-
-if __name__ == '__main__':
- main()
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
index 5647679f446..6c6e0ed6618 100644
--- a/components/script/dom/bindings/codegen/Bindings.conf
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -1,6 +1,6 @@
# 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/.
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# DOM Bindings Configuration.
#
@@ -14,6 +14,14 @@
DOMInterfaces = {
+'Blob': {
+ 'weakReferenceable': True,
+},
+
+'File': {
+ 'weakReferenceable': True,
+},
+
'MediaQueryList': {
'weakReferenceable': True,
},
@@ -26,17 +34,134 @@ DOMInterfaces = {
'weakReferenceable': True,
},
+'EventSource': {
+ 'weakReferenceable': True,
+},
+
+'MessagePort': {
+ 'weakReferenceable': True,
+},
+
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
-'TestBinding': {},
+'TestBinding': {
+ 'inRealms': ['PromiseAttribute', 'PromiseNativeHandler'],
+},
+
+'DynamicModuleOwner': {
+ 'inRealms': ['PromiseAttribute'],
+},
'URL': {
'weakReferenceable': True,
},
'WindowProxy' : {
- 'nativeType': 'BrowsingContext',
- 'path': 'dom::browsingcontext::BrowsingContext',
+ 'path': 'crate::dom::windowproxy::WindowProxy',
'register': False,
+},
+
+'Window': {
+ 'inRealms': ['Fetch', 'Opener'],
+},
+
+'WorkerGlobalScope': {
+ 'inRealms': ['Fetch'],
+},
+
+'CustomElementRegistry': {
+ 'inRealms': ['WhenDefined'],
+},
+
+'AudioContext': {
+ 'inRealms': ['Suspend', 'Close'],
+},
+
+'NavigationPreloadManager': {
+ 'inRealms': ['Enable', 'Disable', 'SetHeaderValue', 'GetState'],
+},
+
+'HTMLMediaElement': {
+ 'inRealms': ['Play'],
+},
+
+'BluetoothRemoteGATTDescriptor': {
+ 'inRealms': ['ReadValue', 'WriteValue'],
+},
+
+'OfflineAudioContext': {
+ 'inRealms': ['StartRendering'],
+},
+
+'BluetoothRemoteGATTServer': {
+ 'inRealms': ['Connect'],
+},
+
+'ServiceWorkerContainer': {
+ 'inRealms': ['Register'],
+},
+
+'Navigator': {
+ 'inRealms': ['GetVRDisplays'],
+},
+
+'MediaDevices': {
+ 'inRealms': ['GetUserMedia'],
+},
+
+'XRSession': {
+ 'inRealms': ['UpdateRenderState', 'RequestReferenceSpace'],
+},
+
+'Bluetooth': {
+ 'inRealms': ['RequestDevice', 'GetAvailability'],
+},
+
+'BaseAudioContext': {
+ 'inRealms': ['Resume', 'DecodeAudioData'],
+},
+
+'RTCPeerConnection': {
+ 'inRealms': ['AddIceCandidate', 'CreateOffer', 'CreateAnswer', 'SetLocalDescription', 'SetRemoteDescription'],
+},
+
+'BluetoothRemoteGATTCharacteristic': {
+ 'inRealms': ['ReadValue', 'WriteValue', 'StartNotifications', 'StopNotifications'],
+},
+
+'VRDisplay': {
+ 'inRealms': ['RequestPresent', 'ExitPresent'],
+},
+
+'Worklet': {
+ 'inRealms': ['AddModule'],
+},
+
+'TestWorklet': {
+ 'inRealms': ['AddModule'],
+},
+
+'BluetoothDevice': {
+ 'inRealms': ['WatchAdvertisements'],
+},
+
+'XRSystem': {
+ 'inRealms': ['SupportsSessionMode', 'RequestSession'],
+},
+
+'GPU': {
+ 'inRealms': ['RequestAdapter'],
+},
+
+'GPUAdapter': {
+ 'inRealms': ['RequestDevice'],
+},
+
+'GPUBuffer': {
+ 'inRealms': ['MapAsync'],
+},
+
+'GPUDevice': {
+ 'inRealms': ['PopErrorScope', 'Lost'],
}
}
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index 09dc19ce411..4a102aacb45 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -1,6 +1,6 @@
# 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/.
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# Common codegen classes.
@@ -17,11 +17,14 @@ import functools
from WebIDL import (
BuiltinTypes,
IDLBuiltinType,
- IDLNullValue,
+ IDLDefaultDictionaryValue,
+ IDLEmptySequenceValue,
+ IDLInterfaceMember,
IDLNullableType,
+ IDLNullValue,
IDLObject,
+ IDLPromiseType,
IDLType,
- IDLInterfaceMember,
IDLUndefinedValue,
IDLWrapperType,
)
@@ -51,32 +54,6 @@ RUST_KEYWORDS = {"abstract", "alignof", "as", "become", "box", "break", "const",
"use", "virtual", "where", "while", "yield"}
-def replaceFileIfChanged(filename, newContents):
- """
- Read a copy of the old file, so that we don't touch it if it hasn't changed.
- Returns True if the file was updated, false otherwise.
- """
- # XXXjdm This doesn't play well with make right now.
- # Force the file to always be updated, or else changing CodegenRust.py
- # will cause many autogenerated bindings to be regenerated perpetually
- # until the result is actually different.
-
- # oldFileContents = ""
- # try:
- # with open(filename, 'rb') as oldFile:
- # oldFileContents = ''.join(oldFile.readlines())
- # except:
- # pass
-
- # if newContents == oldFileContents:
- # return False
-
- with open(filename, 'wb') as f:
- f.write(newContents)
-
- return True
-
-
def toStringBool(arg):
return str(not not arg).lower()
@@ -94,20 +71,19 @@ def stripTrailingWhitespace(text):
def innerContainerType(type):
- assert type.isSequence() or type.isMozMap()
+ assert type.isSequence() or type.isRecord()
return type.inner.inner if type.nullable() else type.inner
def wrapInNativeContainerType(type, inner):
if type.isSequence():
- containerType = "Vec"
- elif type.isMozMap():
- containerType = "MozMap"
+ return CGWrapper(inner, pre="Vec<", post=">")
+ elif type.isRecord():
+ key = type.inner.keyType if type.nullable() else type.keyType
+ return CGRecord(key, inner)
else:
raise TypeError("Unexpected container type %s", type)
- return CGWrapper(inner, pre=containerType + "<", post=">")
-
builtinNames = {
IDLType.Tags.bool: 'bool',
@@ -320,7 +296,7 @@ class CGMethodCall(CGThing):
if requiredArgs > 0:
code = (
"if argc < %d {\n"
- " throw_type_error(cx, \"Not enough arguments to %s.\");\n"
+ " throw_type_error(*cx, \"Not enough arguments to %s.\");\n"
" return false;\n"
"}" % (requiredArgs, methodName))
self.cgRoot.prepend(
@@ -343,12 +319,17 @@ class CGMethodCall(CGThing):
distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
- # We can't handle unions at the distinguishing index.
+ # We can't handle unions of non-object values at the distinguishing index.
for (returnType, args) in possibleSignatures:
- if args[distinguishingIndex].type.isUnion():
- raise TypeError("No support for unions as distinguishing "
- "arguments yet: %s",
- args[distinguishingIndex].location)
+ type = args[distinguishingIndex].type
+ if type.isUnion():
+ if type.nullable():
+ type = type.inner
+ for type in type.flatMemberTypes:
+ if not (type.isObject() or type.isNonCallbackInterface()):
+ raise TypeError("No support for unions with non-object variants "
+ "as distinguishing arguments yet: %s",
+ args[distinguishingIndex].location)
# Convert all our arguments up to the distinguishing index.
# Doesn't matter which of the possible signatures we use, since
@@ -360,10 +341,10 @@ class CGMethodCall(CGThing):
for i in range(0, distinguishingIndex)]
# Select the right overload from our set.
- distinguishingArg = "args.get(%d)" % distinguishingIndex
+ distinguishingArg = "HandleValue::from_raw(args.get(%d))" % distinguishingIndex
def pickFirstSignature(condition, filterLambda):
- sigs = filter(filterLambda, possibleSignatures)
+ sigs = list(filter(filterLambda, possibleSignatures))
assert len(sigs) < 2
if len(sigs) > 0:
call = getPerSignatureCall(sigs[0], distinguishingIndex)
@@ -378,16 +359,17 @@ class CGMethodCall(CGThing):
# First check for null or undefined
pickFirstSignature("%s.get().is_null_or_undefined()" % distinguishingArg,
- lambda s: (s[1][distinguishingIndex].type.nullable() or
- s[1][distinguishingIndex].type.isDictionary()))
+ lambda s: (s[1][distinguishingIndex].type.nullable()
+ or s[1][distinguishingIndex].type.isDictionary()))
# Now check for distinguishingArg being an object that implements a
# non-callback interface. That includes typed arrays and
# arraybuffers.
interfacesSigs = [
s for s in possibleSignatures
- if (s[1][distinguishingIndex].type.isObject() or
- s[1][distinguishingIndex].type.isNonCallbackInterface())]
+ if (s[1][distinguishingIndex].type.isObject()
+ or s[1][distinguishingIndex].type.isUnion()
+ or s[1][distinguishingIndex].type.isNonCallbackInterface())]
# There might be more than one of these; we need to check
# which ones we unwrap to.
@@ -421,7 +403,8 @@ class CGMethodCall(CGThing):
template,
{"val": distinguishingArg},
declType,
- "arg%d" % distinguishingIndex)
+ "arg%d" % distinguishingIndex,
+ needsAutoRoot=type_needs_auto_root(type))
# Indent by 4, since we need to indent further than our "do" statement
caseBody.append(CGIndenter(testCode, 4))
@@ -438,38 +421,27 @@ class CGMethodCall(CGThing):
# XXXbz Now we're supposed to check for distinguishingArg being
# an array or a platform object that supports indexed
# properties... skip that last for now. It's a bit of a pain.
- pickFirstSignature("%s.get().is_object() && is_array_like(cx, %s)" %
+ pickFirstSignature("%s.get().is_object() && is_array_like(*cx, %s)" %
(distinguishingArg, distinguishingArg),
lambda s:
- (s[1][distinguishingIndex].type.isSequence() or
- s[1][distinguishingIndex].type.isObject()))
-
- # Check for Date objects
- # XXXbz Do we need to worry about security wrappers around the Date?
- pickFirstSignature("%s.get().is_object() && "
- "{ rooted!(in(cx) let obj = %s.get().to_object()); "
- "let mut is_date = false; "
- "assert!(JS_ObjectIsDate(cx, obj.handle(), &mut is_date)); "
- "is_date }" %
- (distinguishingArg, distinguishingArg),
- lambda s: (s[1][distinguishingIndex].type.isDate() or
- s[1][distinguishingIndex].type.isObject()))
+ (s[1][distinguishingIndex].type.isSequence()
+ or s[1][distinguishingIndex].type.isObject()))
# Check for vanilla JS objects
# XXXbz Do we need to worry about security wrappers?
- pickFirstSignature("%s.get().is_object() && !is_platform_object(%s.get().to_object())" %
- (distinguishingArg, distinguishingArg),
- lambda s: (s[1][distinguishingIndex].type.isCallback() or
- s[1][distinguishingIndex].type.isCallbackInterface() or
- s[1][distinguishingIndex].type.isDictionary() or
- s[1][distinguishingIndex].type.isObject()))
+ pickFirstSignature("%s.get().is_object()" %
+ distinguishingArg,
+ lambda s: (s[1][distinguishingIndex].type.isCallback()
+ or s[1][distinguishingIndex].type.isCallbackInterface()
+ or s[1][distinguishingIndex].type.isDictionary()
+ or s[1][distinguishingIndex].type.isObject()))
# The remaining cases are mutually exclusive. The
# pickFirstSignature calls are what change caseBody
# Check for strings or enums
if pickFirstSignature(None,
- lambda s: (s[1][distinguishingIndex].type.isString() or
- s[1][distinguishingIndex].type.isEnum())):
+ lambda s: (s[1][distinguishingIndex].type.isString()
+ or s[1][distinguishingIndex].type.isEnum())):
pass
# Check for primitives
elif pickFirstSignature(None,
@@ -482,7 +454,8 @@ class CGMethodCall(CGThing):
else:
# Just throw; we have no idea what we're supposed to
# do with this.
- caseBody.append(CGGeneric("return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);"))
+ caseBody.append(CGGeneric("throw_type_error(*cx, \"Could not convert JavaScript argument\");\n"
+ "return false;"))
argCountCases.append(CGCase(str(argCount),
CGList(caseBody, "\n")))
@@ -494,7 +467,7 @@ class CGMethodCall(CGThing):
overloadCGThings.append(
CGSwitch("argcount",
argCountCases,
- CGGeneric("throw_type_error(cx, \"Not enough arguments to %s.\");\n"
+ CGGeneric("throw_type_error(*cx, \"Not enough arguments to %s.\");\n"
"return false;" % methodName)))
# XXXjdm Avoid unreachable statement warnings
# overloadCGThings.append(
@@ -509,9 +482,9 @@ class CGMethodCall(CGThing):
def dictionaryHasSequenceMember(dictionary):
return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in
- dictionary.members) or
- (dictionary.parent and
- dictionaryHasSequenceMember(dictionary.parent)))
+ dictionary.members)
+ or (dictionary.parent
+ and dictionaryHasSequenceMember(dictionary.parent)))
def typeIsSequenceOrHasSequenceMember(type):
@@ -560,11 +533,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
isDefinitelyObject=False,
isMember=False,
isArgument=False,
+ isAutoRooted=False,
invalidEnumValueFatal=True,
defaultValue=None,
- treatNullAs="Default",
- isEnforceRange=False,
- isClamp=False,
exceptionCode=None,
allowTreatNonObjectAsNull=False,
isCallbackReturnValue=False,
@@ -592,12 +563,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
If defaultValue is not None, it's the IDL default value for this conversion
- If isEnforceRange is true, we're converting an integer and throwing if the
- value is out of range.
-
- If isClamp is true, we're converting an integer and clamping if the
- value is out of range.
-
If allowTreatNonObjectAsNull is true, then [TreatNonObjectAsNull]
extended attributes on nullable callback functions will be honored.
@@ -620,6 +585,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# We should not have a defaultValue if we know we're an object
assert not isDefinitelyObject or defaultValue is None
+ isEnforceRange = type.hasEnforceRange()
+ isClamp = type.hasClamp()
+ if type.treatNullAsEmpty:
+ treatNullAs = "EmptyString"
+ else:
+ treatNullAs = "Default"
+
# 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
@@ -628,7 +600,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
exceptionCode = "return false;\n"
if failureCode is None:
- failOrPropagate = "throw_type_error(cx, &error);\n%s" % exceptionCode
+ failOrPropagate = "throw_type_error(*cx, &error);\n%s" % exceptionCode
else:
failOrPropagate = failureCode
@@ -646,34 +618,39 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
def onFailureNotAnObject(failureCode):
return CGWrapper(
CGGeneric(
- failureCode or
- ('throw_type_error(cx, "%s is not an object.");\n'
- '%s' % (firstCap(sourceDescription), exceptionCode))),
+ failureCode
+ or ('throw_type_error(*cx, "%s is not an object.");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode))),
post="\n")
def onFailureInvalidEnumValue(failureCode, passedVarName):
return CGGeneric(
- failureCode or
- ('throw_type_error(cx, &format!("\'{}\' is not a valid enum value for enumeration \'%s\'.", %s)); %s'
- % (type.name, passedVarName, exceptionCode)))
+ failureCode
+ or ('throw_type_error(*cx, &format!("\'{}\' is not a valid enum value for enumeration \'%s\'.", %s)); %s'
+ % (type.name, passedVarName, exceptionCode)))
def onFailureNotCallable(failureCode):
return CGGeneric(
- failureCode or
- ('throw_type_error(cx, \"%s is not callable.\");\n'
- '%s' % (firstCap(sourceDescription), exceptionCode)))
+ failureCode
+ or ('throw_type_error(*cx, \"%s is not callable.\");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode)))
- # A helper function for handling null default values. Checks that the
- # default value, if it exists, is null.
- def handleDefaultNull(nullValue):
+ # A helper function for handling default values.
+ def handleDefault(nullValue):
if defaultValue is None:
return None
- if not isinstance(defaultValue, IDLNullValue):
- raise TypeError("Can't handle non-null default value here")
+ if isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ return nullValue
+ elif isinstance(defaultValue, IDLDefaultDictionaryValue):
+ assert type.isDictionary()
+ return nullValue
+ elif isinstance(defaultValue, IDLEmptySequenceValue):
+ assert type.isSequence()
+ return "Vec::new()"
- assert type.nullable() or type.isDictionary()
- return nullValue
+ raise TypeError("Can't handle non-null, non-empty sequence or non-empty dictionary default value here")
# A helper function for wrapping up the template body for
# possibly-nullable objecty stuff
@@ -683,31 +660,32 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# Handle the non-object cases by wrapping up the whole
# thing in an if cascade.
templateBody = (
- "if ${val}.get().is_object() {\n" +
- CGIndenter(CGGeneric(templateBody)).define() + "\n")
+ "if ${val}.get().is_object() {\n"
+ + CGIndenter(CGGeneric(templateBody)).define() + "\n")
if type.nullable():
templateBody += (
"} else if ${val}.get().is_null_or_undefined() {\n"
" %s\n") % nullValue
templateBody += (
- "} else {\n" +
- CGIndenter(onFailureNotAnObject(failureCode)).define() +
- "}")
+ "} else {\n"
+ + CGIndenter(onFailureNotAnObject(failureCode)).define()
+ + "}")
return templateBody
assert not (isEnforceRange and isClamp) # These are mutually exclusive
- if type.isSequence() or type.isMozMap():
+ if type.isSequence() or type.isRecord():
innerInfo = getJSToNativeConversionInfo(innerContainerType(type),
descriptorProvider,
- isMember=isMember)
+ isMember="Sequence",
+ isAutoRooted=isAutoRooted)
declType = wrapInNativeContainerType(type, innerInfo.declType)
config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=" >")
- templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
+ templateBody = ("match FromJSValConvertible::from_jsval(*cx, ${val}, %s) {\n"
" Ok(ConversionResult::Success(value)) => value,\n"
" Ok(ConversionResult::Failure(error)) => {\n"
"%s\n"
@@ -715,17 +693,14 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
" _ => { %s },\n"
"}" % (config, indent(failOrPropagate, 8), exceptionCode))
- return handleOptional(templateBody, declType, handleDefaultNull("None"))
+ return handleOptional(templateBody, declType, handleDefault("None"))
if type.isUnion():
declType = CGGeneric(union_native_type(type))
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=" >")
- if isMember != "Dictionary" and type_needs_tracing(type):
- declType = CGTemplatedType("RootedTraceableBox", declType)
-
- templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ templateBody = ("match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n"
" Ok(ConversionResult::Success(value)) => value,\n"
" Ok(ConversionResult::Failure(error)) => {\n"
"%s\n"
@@ -738,11 +713,32 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
for memberType in type.unroll().flatMemberTypes
if memberType.isDictionary()
]
- if dictionaries:
+ if (defaultValue
+ and not isinstance(defaultValue, IDLNullValue)
+ and not isinstance(defaultValue, IDLDefaultDictionaryValue)):
+ tag = defaultValue.type.tag()
+ if tag is IDLType.Tags.bool:
+ default = "%s::Boolean(%s)" % (
+ union_native_type(type),
+ "true" if defaultValue.value else "false")
+ elif tag is IDLType.Tags.usvstring:
+ default = '%s::USVString(USVString("%s".to_owned()))' % (
+ union_native_type(type),
+ defaultValue.value)
+ elif defaultValue.type.isEnum():
+ enum = defaultValue.type.inner.identifier.name
+ default = "%s::%s(%s::%s)" % (
+ union_native_type(type),
+ enum,
+ enum,
+ getEnumValueName(defaultValue.value))
+ else:
+ raise("We don't currently support default values that aren't null, boolean or default dictionary")
+ elif dictionaries:
if defaultValue:
- assert isinstance(defaultValue, IDLNullValue)
+ assert isinstance(defaultValue, IDLDefaultDictionaryValue)
dictionary, = dictionaries
- default = "%s::%s(%s::%s::empty(cx))" % (
+ default = "%s::%s(%s::%s::empty())" % (
union_native_type(type),
dictionary.name,
CGDictionary.makeModuleName(dictionary.inner),
@@ -750,10 +746,60 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
else:
default = None
else:
- default = handleDefaultNull("None")
+ default = handleDefault("None")
return handleOptional(templateBody, declType, default)
+ if type.isPromise():
+ assert not type.nullable()
+ # Per spec, what we're supposed to do is take the original
+ # Promise.resolve and call it with the original Promise as this
+ # value to make a Promise out of whatever value we actually have
+ # here. The question is which global we should use. There are
+ # a couple cases to consider:
+ #
+ # 1) Normal call to API with a Promise argument. This is a case the
+ # spec covers, and we should be using the current Realm's
+ # Promise. That means the current realm.
+ # 2) Promise return value from a callback or callback interface.
+ # This is in theory a case the spec covers but in practice it
+ # really doesn't define behavior here because it doesn't define
+ # what Realm we're in after the callback returns, which is when
+ # the argument conversion happens. We will use the current
+ # realm, which is the realm of the callable (which
+ # may itself be a cross-realm wrapper itself), which makes
+ # as much sense as anything else. In practice, such an API would
+ # once again be providing a Promise to signal completion of an
+ # operation, which would then not be exposed to anyone other than
+ # our own implementation code.
+ templateBody = fill(
+ """
+ { // Scope for our JSAutoRealm.
+
+ rooted!(in(*cx) let globalObj = CurrentGlobalOrNull(*cx));
+ let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get(), *cx);
+
+ rooted!(in(*cx) let mut valueToResolve = $${val}.get());
+ if !JS_WrapValue(*cx, valueToResolve.handle_mut()) {
+ $*{exceptionCode}
+ }
+ match Promise::new_resolved(&promiseGlobal, cx, valueToResolve.handle()) {
+ Ok(value) => value,
+ Err(error) => {
+ throw_dom_exception(cx, &promiseGlobal, error);
+ $*{exceptionCode}
+ }
+ }
+ }
+ """,
+ exceptionCode=exceptionCode)
+
+ if isArgument:
+ declType = CGGeneric("&Promise")
+ else:
+ declType = CGGeneric("Rc<Promise>")
+ return handleOptional(templateBody, declType, handleDefault("None"))
+
if type.isGeckoInterface():
assert not isEnforceRange and not isClamp
@@ -770,7 +816,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
isDefinitelyObject, type,
failureCode)
- return handleOptional(template, declType, handleDefaultNull("None"))
+ return handleOptional(template, declType, handleDefault("None"))
conversionFunction = "root_from_handlevalue"
descriptorType = descriptor.returnType
@@ -779,99 +825,129 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
descriptorType = descriptor.nativeType
elif isArgument:
descriptorType = descriptor.argumentType
+ elif descriptor.interface.identifier.name == "WindowProxy":
+ conversionFunction = "windowproxy_from_handlevalue"
+
+ if failureCode is None:
+ substitutions = {
+ "sourceDescription": sourceDescription,
+ "interface": descriptor.interface.identifier.name,
+ "exceptionCode": exceptionCode,
+ }
+ unwrapFailureCode = string.Template(
+ 'throw_type_error(*cx, "${sourceDescription} does not '
+ 'implement interface ${interface}.");\n'
+ '${exceptionCode}').substitute(substitutions)
+ else:
+ unwrapFailureCode = failureCode
- templateBody = ""
- isPromise = descriptor.interface.identifier.name == "Promise"
- if isPromise:
- # Per spec, what we're supposed to do is take the original
- # Promise.resolve and call it with the original Promise as this
- # value to make a Promise out of whatever value we actually have
- # here. The question is which global we should use. There are
- # a couple cases to consider:
- #
- # 1) Normal call to API with a Promise argument. This is a case the
- # spec covers, and we should be using the current Realm's
- # Promise. That means the current compartment.
- # 2) Promise return value from a callback or callback interface.
- # This is in theory a case the spec covers but in practice it
- # really doesn't define behavior here because it doesn't define
- # what Realm we're in after the callback returns, which is when
- # the argument conversion happens. We will use the current
- # compartment, which is the compartment of the callable (which
- # may itself be a cross-compartment wrapper itself), which makes
- # as much sense as anything else. In practice, such an API would
- # once again be providing a Promise to signal completion of an
- # operation, which would then not be exposed to anyone other than
- # our own implementation code.
- templateBody = fill(
- """
- { // Scope for our JSAutoCompartment.
+ templateBody = fill(
+ """
+ match ${function}($${val}, *cx) {
+ Ok(val) => val,
+ Err(()) => {
+ $*{failureCode}
+ }
+ }
+ """,
+ failureCode=unwrapFailureCode + "\n",
+ function=conversionFunction)
- rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
- let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get());
+ declType = CGGeneric(descriptorType)
+ if type.nullable():
+ templateBody = "Some(%s)" % templateBody
+ declType = CGWrapper(declType, pre="Option<", post=">")
- rooted!(in(cx) let mut valueToResolve = $${val}.get());
- if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
- $*{exceptionCode}
- }
- match Promise::Resolve(&promiseGlobal, cx, valueToResolve.handle()) {
- Ok(value) => value,
- Err(error) => {
- throw_dom_exception(cx, &promiseGlobal, error);
- $*{exceptionCode}
- }
- }
- }
- """,
- exceptionCode=exceptionCode)
+ templateBody = wrapObjectTemplate(templateBody, "None",
+ isDefinitelyObject, type, failureCode)
+
+ return handleOptional(templateBody, declType, handleDefault("None"))
+
+ if is_typed_array(type):
+ if failureCode is None:
+ substitutions = {
+ "sourceDescription": sourceDescription,
+ "exceptionCode": exceptionCode,
+ }
+ unwrapFailureCode = string.Template(
+ 'throw_type_error(*cx, "${sourceDescription} is not a typed array.");\n'
+ '${exceptionCode}').substitute(substitutions)
else:
- if descriptor.interface.isConsequential():
- raise TypeError("Consequential interface %s being used as an "
- "argument" % descriptor.interface.identifier.name)
-
- if failureCode is None:
- substitutions = {
- "sourceDescription": sourceDescription,
- "interface": descriptor.interface.identifier.name,
- "exceptionCode": exceptionCode,
+ unwrapFailureCode = failureCode
+
+ typeName = type.unroll().name # unroll because it may be nullable
+
+ if isMember == "Union":
+ typeName = "Heap" + typeName
+
+ templateBody = fill(
+ """
+ match typedarray::${ty}::from($${val}.get().to_object()) {
+ Ok(val) => val,
+ Err(()) => {
+ $*{failureCode}
}
- unwrapFailureCode = string.Template(
- 'throw_type_error(cx, "${sourceDescription} does not '
- 'implement interface ${interface}.");\n'
- '${exceptionCode}').substitute(substitutions)
- else:
- unwrapFailureCode = failureCode
+ }
+ """,
+ ty=typeName,
+ failureCode=unwrapFailureCode + "\n",
+ )
- templateBody = fill(
- """
- match ${function}($${val}) {
+ if isMember == "Union":
+ templateBody = "RootedTraceableBox::new(%s)" % templateBody
+
+ declType = CGGeneric("typedarray::%s" % typeName)
+ if type.nullable():
+ templateBody = "Some(%s)" % templateBody
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ templateBody = wrapObjectTemplate(templateBody, "None",
+ isDefinitelyObject, type, failureCode)
+
+ return handleOptional(templateBody, declType, handleDefault("None"))
+
+ if type.isReadableStream():
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is None:
+ unwrapFailureCode = '''throw_type_error(*cx, "This object is not \
+ an instance of ReadableStream.");\n'''
+ else:
+ unwrapFailureCode = failureCode
+
+ templateBody = fill(
+ """
+ {
+ use crate::realms::{AlreadyInRealm, InRealm};
+ let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
+ match ReadableStream::from_js(cx, $${val}.get().to_object(), InRealm::Already(&in_realm_proof)) {
Ok(val) => val,
Err(()) => {
- $*{failureCode}
+ $*{failureCode}
}
}
- """,
- failureCode=unwrapFailureCode + "\n",
- function=conversionFunction)
- declType = CGGeneric(descriptorType)
- if type.nullable():
- templateBody = "Some(%s)" % templateBody
- declType = CGWrapper(declType, pre="Option<", post=">")
+ }
+ """,
+ failureCode=unwrapFailureCode + "\n",
+ )
templateBody = wrapObjectTemplate(templateBody, "None",
isDefinitelyObject, type, failureCode)
- return handleOptional(templateBody, declType, handleDefaultNull("None"))
+ declType = CGGeneric("DomRoot<ReadableStream>")
+
+ return handleOptional(templateBody, declType,
+ handleDefault("None"))
- if type.isSpiderMonkeyInterface():
- raise TypeError("Can't handle SpiderMonkey interface arguments yet")
+ elif type.isSpiderMonkeyInterface():
+ raise TypeError("Can't handle SpiderMonkey interface arguments other than typed arrays yet")
if type.isDOMString():
nullBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
conversionCode = (
- "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
+ "match FromJSValConvertible::from_jsval(*cx, ${val}, %s) {\n"
" Ok(ConversionResult::Success(strval)) => strval,\n"
" Ok(ConversionResult::Failure(error)) => {\n"
"%s\n"
@@ -900,7 +976,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert not isEnforceRange and not isClamp
conversionCode = (
- "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n"
" Ok(ConversionResult::Success(strval)) => strval,\n"
" Ok(ConversionResult::Failure(error)) => {\n"
"%s\n"
@@ -929,7 +1005,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert not isEnforceRange and not isClamp
conversionCode = (
- "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n"
" Ok(ConversionResult::Success(strval)) => strval,\n"
" Ok(ConversionResult::Failure(error)) => {\n"
"%s\n"
@@ -962,17 +1038,16 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
"yet")
enum = type.inner.identifier.name
if invalidEnumValueFatal:
- handleInvalidEnumValueCode = onFailureInvalidEnumValue(failureCode, 'search').define()
+ handleInvalidEnumValueCode = failureCode or "throw_type_error(*cx, &error); %s" % exceptionCode
else:
handleInvalidEnumValueCode = "return true;"
template = (
- "match find_enum_value(cx, ${val}, %(pairs)s) {\n"
+ "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {"
" Err(_) => { %(exceptionCode)s },\n"
- " Ok((None, search)) => { %(handleInvalidEnumValueCode)s },\n"
- " Ok((Some(&value), _)) => value,\n"
- "}" % {"pairs": enum + "Values::pairs",
- "exceptionCode": exceptionCode,
+ " Ok(ConversionResult::Success(v)) => v,\n"
+ " Ok(ConversionResult::Failure(error)) => { %(handleInvalidEnumValueCode)s },\n"
+ "}" % {"exceptionCode": exceptionCode,
"handleInvalidEnumValueCode": handleInvalidEnumValueCode})
if defaultValue is not None:
@@ -1034,20 +1109,28 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert not isEnforceRange and not isClamp
assert isMember != "Union"
- if isMember == "Dictionary":
- # TODO: Need to properly root dictionaries
- # https://github.com/servo/servo/issues/6381
- declType = CGGeneric("Heap<JSVal>")
+ if isMember in ("Dictionary", "Sequence") or isAutoRooted:
+ templateBody = "${val}.get()"
if defaultValue is None:
default = None
elif isinstance(defaultValue, IDLNullValue):
- default = "Heap::new(NullValue())"
+ default = "NullValue()"
elif isinstance(defaultValue, IDLUndefinedValue):
- default = "Heap::new(UndefinedValue())"
+ default = "UndefinedValue()"
else:
raise TypeError("Can't handle non-null, non-undefined default value here")
- return handleOptional("Heap::new(${val}.get())", declType, default)
+
+ if not isAutoRooted:
+ templateBody = "RootedTraceableBox::from_box(Heap::boxed(%s))" % templateBody
+ if default is not None:
+ default = "RootedTraceableBox::from_box(Heap::boxed(%s))" % default
+ declType = CGGeneric("RootedTraceableBox<Heap<JSVal>>")
+ # AutoRooter can trace properly inner raw GC thing pointers
+ else:
+ declType = CGGeneric("JSVal")
+
+ return handleOptional(templateBody, declType, default)
declType = CGGeneric("HandleValue")
@@ -1065,39 +1148,37 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.isObject():
assert not isEnforceRange and not isClamp
- # TODO: Need to root somehow
- # https://github.com/servo/servo/issues/6382
+ templateBody = "${val}.get().to_object()"
default = "ptr::null_mut()"
- templateBody = wrapObjectTemplate("${val}.get().to_object()",
- default,
- isDefinitelyObject, type, failureCode)
- if isMember in ("Dictionary", "Union"):
- declType = CGGeneric("Heap<*mut JSObject>")
- templateBody = "Heap::new(%s)" % templateBody
- default = "Heap::new(%s)" % default
+ if isMember in ("Dictionary", "Union", "Sequence") and not isAutoRooted:
+ templateBody = "RootedTraceableBox::from_box(Heap::boxed(%s))" % templateBody
+ default = "RootedTraceableBox::new(Heap::default())"
+ declType = CGGeneric("RootedTraceableBox<Heap<*mut JSObject>>")
else:
# TODO: Need to root somehow
# https://github.com/servo/servo/issues/6382
declType = CGGeneric("*mut JSObject")
+ templateBody = wrapObjectTemplate(templateBody, default,
+ isDefinitelyObject, type, failureCode)
+
return handleOptional(templateBody, declType,
- handleDefaultNull(default))
+ handleDefault(default))
if type.isDictionary():
# There are no nullable dictionaries
- assert not type.nullable()
+ assert not type.nullable() or (isMember and isMember != "Dictionary")
typeName = "%s::%s" % (CGDictionary.makeModuleName(type.inner),
CGDictionary.makeDictionaryName(type.inner))
declType = CGGeneric(typeName)
- empty = "%s::empty(cx)" % typeName
+ empty = "%s::empty()" % typeName
- if isMember != "Dictionary" and type_needs_tracing(type):
+ if type_needs_tracing(type):
declType = CGTemplatedType("RootedTraceableBox", declType)
- empty = "RootedTraceableBox::new(%s)" % empty
- template = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ template = ("match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n"
" Ok(ConversionResult::Success(dictionary)) => dictionary,\n"
" Ok(ConversionResult::Failure(error)) => {\n"
"%s\n"
@@ -1105,7 +1186,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
" _ => { %s },\n"
"}" % (indent(failOrPropagate, 8), exceptionCode))
- return handleOptional(template, declType, handleDefaultNull(empty))
+ return handleOptional(template, declType, handleDefault(empty))
if type.isVoid():
# This one only happens for return values, and its easy: Just
@@ -1125,7 +1206,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
declType = CGWrapper(declType, pre="Option<", post=">")
template = (
- "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
+ "match FromJSValConvertible::from_jsval(*cx, ${val}, %s) {\n"
" Ok(ConversionResult::Success(v)) => v,\n"
" Ok(ConversionResult::Failure(error)) => {\n"
"%s\n"
@@ -1156,7 +1237,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
def instantiateJSToNativeConversionTemplate(templateBody, replacements,
- declType, declName):
+ declType, declName,
+ needsAutoRoot=False):
"""
Take the templateBody and declType as returned by
getJSToNativeConversionInfo, a set of replacements as required by the
@@ -1181,6 +1263,8 @@ def instantiateJSToNativeConversionTemplate(templateBody, replacements,
else:
result.append(conversion)
+ if needsAutoRoot:
+ result.append(CGGeneric("auto_root!(in(*cx) let %s = %s);" % (declName, declName)))
# Add an empty CGGeneric to get an extra newline after the argument
# conversion.
result.append(CGGeneric(""))
@@ -1198,12 +1282,12 @@ def convertConstIDLValueToJSVal(value):
if tag == IDLType.Tags.uint32:
return "ConstantVal::UintVal(%s)" % (value.value)
if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
- return "ConstantVal::DoubleVal(%s)" % (value.value)
+ return "ConstantVal::DoubleVal(%s as f64)" % (value.value)
if tag == IDLType.Tags.bool:
return "ConstantVal::BoolVal(true)" if value.value else "ConstantVal::BoolVal(false)"
if tag in [IDLType.Tags.unrestricted_float, IDLType.Tags.float,
IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
- return "ConstantVal::DoubleVal(%s)" % (value.value)
+ return "ConstantVal::DoubleVal(%s as f64)" % (value.value)
raise TypeError("Const value of unhandled type: " + value.type)
@@ -1225,7 +1309,7 @@ class CGArgumentConverter(CGThing):
}
replacementVariables = {
- "val": string.Template("${args}.get(${index})").substitute(replacer),
+ "val": string.Template("HandleValue::from_raw(${args}.get(${index}))").substitute(replacer),
}
info = getJSToNativeConversionInfo(
@@ -1233,10 +1317,8 @@ class CGArgumentConverter(CGThing):
descriptorProvider,
invalidEnumValueFatal=invalidEnumValueFatal,
defaultValue=argument.defaultValue,
- treatNullAs=argument.treatNullAs,
- isEnforceRange=argument.enforceRange,
- isClamp=argument.clamp,
isMember="Variadic" if argument.variadic else False,
+ isAutoRooted=type_needs_auto_root(argument.type),
allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull())
template = info.template
default = info.default
@@ -1259,12 +1341,16 @@ class CGArgumentConverter(CGThing):
else:
assert not default
+ arg = "arg%d" % index
+
self.converter = instantiateJSToNativeConversionTemplate(
- template, replacementVariables, declType, "arg%d" % index)
+ template, replacementVariables, declType, arg,
+ needsAutoRoot=type_needs_auto_root(argument.type))
+
else:
assert argument.optional
variadicConversion = {
- "val": string.Template("${args}.get(variadicArg)").substitute(replacer),
+ "val": string.Template("HandleValue::from_raw(${args}.get(variadicArg))").substitute(replacer),
}
innerConverter = [instantiateJSToNativeConversionTemplate(
template, variadicConversion, declType, "slot")]
@@ -1272,7 +1358,7 @@ class CGArgumentConverter(CGThing):
arg = "arg%d" % index
if argument.type.isGeckoInterface():
init = "rooted_vec!(let mut %s)" % arg
- innerConverter.append(CGGeneric("%s.push(JS::from_ref(&*slot));" % arg))
+ innerConverter.append(CGGeneric("%s.push(Dom::from_ref(&*slot));" % arg))
else:
init = "let mut %s = vec![]" % arg
innerConverter.append(CGGeneric("%s.push(slot);" % arg))
@@ -1301,7 +1387,7 @@ def wrapForType(jsvalRef, result='result', successCode='return true;', pre=''):
* 'successCode': the code to run once we have done the conversion.
* 'pre': code to run before the conversion if rooting is necessary
"""
- wrap = "%s\n(%s).to_jsval(cx, %s);" % (pre, result, jsvalRef)
+ wrap = "%s\n(%s).to_jsval(*cx, %s);" % (pre, result, jsvalRef)
if successCode:
wrap += "\n%s" % successCode
return wrap
@@ -1323,7 +1409,7 @@ def typeNeedsCx(type, retVal=False):
# Returns a conversion behavior suitable for a type
def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs):
- if type.isSequence() or type.isMozMap():
+ if type.isSequence() or type.isRecord():
return getConversionConfigForType(innerContainerType(type), isEnforceRange, isClamp, treatNullAs)
if type.isDOMString():
assert not isEnforceRange and not isClamp
@@ -1381,6 +1467,9 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result
+ if returnType.isPromise():
+ assert not returnType.nullable()
+ return CGGeneric("Rc<Promise>")
if returnType.isGeckoInterface():
descriptor = descriptorProvider.getDescriptor(
returnType.unroll().inner.identifier.name)
@@ -1404,11 +1493,11 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
if returnType.isAny():
return CGGeneric("JSVal")
if returnType.isObject() or returnType.isSpiderMonkeyInterface():
- result = CGGeneric("NonZero<*mut JSObject>")
+ result = CGGeneric("NonNull<JSObject>")
if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result
- if returnType.isSequence() or returnType.isMozMap():
+ if returnType.isSequence() or returnType.isRecord():
result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider)
result = wrapInNativeContainerType(returnType, result)
if returnType.nullable():
@@ -1418,6 +1507,8 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
nullable = returnType.nullable()
dictName = returnType.inner.name if nullable else returnType.name
result = CGGeneric(dictName)
+ if type_needs_tracing(returnType):
+ result = CGWrapper(result, pre="RootedTraceableBox<", post=">")
if nullable:
result = CGWrapper(result, pre="Option<", post=">")
return result
@@ -1426,7 +1517,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
returnType)
-def MemberCondition(pref, func):
+def MemberCondition(pref, func, exposed, secure):
"""
A string representing the condition for a member to actually be exposed.
Any of the arguments can be None. If not None, they should have the
@@ -1434,14 +1525,21 @@ def MemberCondition(pref, func):
pref: The name of the preference.
func: The name of the function.
+ exposed: One or more names of an exposed global.
+ secure: Requires secure context.
"""
assert pref is None or isinstance(pref, str)
assert func is None or isinstance(func, str)
- assert func is None or pref is None
+ assert exposed is None or isinstance(exposed, set)
+ assert func is None or pref is None or exposed is None or secure is None
+ if secure:
+ return 'Condition::SecureContext()'
if pref:
return 'Condition::Pref("%s")' % pref
if func:
return 'Condition::Func(%s)' % func
+ if exposed:
+ return ["Condition::Exposed(InterfaceObjectMap::Globals::%s)" % camel_to_upper_snake(i) for i in exposed]
return "Condition::Satisfied"
@@ -1484,7 +1582,9 @@ class PropertyDefiner:
PropertyDefiner.getStringAttr(interfaceMember,
"Pref"),
PropertyDefiner.getStringAttr(interfaceMember,
- "Func"))
+ "Func"),
+ interfaceMember.exposureSet,
+ interfaceMember.getExtendedAttribute("SecureContext"))
def generateGuardedArray(self, array, name, specTemplate, specTerminator,
specType, getCondition, getDataTuple):
@@ -1516,22 +1616,30 @@ class PropertyDefiner:
specs = []
prefableSpecs = []
prefableTemplate = ' Guard::new(%s, %s[%d])'
+ origTemplate = specTemplate
+ if isinstance(specTemplate, str):
+ specTemplate = lambda _: origTemplate # noqa
for cond, members in groupby(array, lambda m: getCondition(m, self.descriptor)):
- currentSpecs = [specTemplate % getDataTuple(m) for m in members]
+ currentSpecs = [specTemplate(m) % getDataTuple(m) for m in members]
if specTerminator:
currentSpecs.append(specTerminator)
specs.append("&[\n" + ",\n".join(currentSpecs) + "]\n")
- prefableSpecs.append(
- prefableTemplate % (cond, name + "_specs", len(specs) - 1))
+ if isinstance(cond, list):
+ for i in cond:
+ prefableSpecs.append(
+ prefableTemplate % (i, name + "_specs", len(specs) - 1))
+ else:
+ prefableSpecs.append(
+ prefableTemplate % (cond, name + "_specs", len(specs) - 1))
- specsArray = ("const %s_specs: &'static [&'static[%s]] = &[\n" +
- ",\n".join(specs) + "\n" +
- "];\n") % (name, specType)
+ specsArray = ("const %s_specs: &'static [&'static[%s]] = &[\n"
+ + ",\n".join(specs) + "\n"
+ + "];\n") % (name, specType)
- prefArray = ("const %s: &'static [Guard<&'static [%s]>] = &[\n" +
- ",\n".join(prefableSpecs) + "\n" +
- "];\n") % (name, specType)
+ prefArray = ("const %s: &'static [Guard<&'static [%s]>] = &[\n"
+ + ",\n".join(prefableSpecs) + "\n"
+ + "];\n") % (name, specType)
return specsArray + prefArray
@@ -1559,45 +1667,70 @@ class MethodDefiner(PropertyDefiner):
# Ignore non-static methods for callback interfaces
if not descriptor.interface.isCallback() or static:
methods = [m for m in descriptor.interface.members if
- m.isMethod() and m.isStatic() == static and
- not m.isIdentifierLess() and
- MemberIsUnforgeable(m, descriptor) == unforgeable]
+ m.isMethod() and m.isStatic() == static
+ and not m.isIdentifierLess()
+ and MemberIsUnforgeable(m, descriptor) == unforgeable]
else:
methods = []
self.regular = [{"name": m.identifier.name,
"methodInfo": not m.isStatic(),
"length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m, descriptor)}
for m in methods]
- # FIXME Check for an existing iterator on the interface first.
- if any(m.isGetter() and m.isIndexed() for m in methods):
+ # TODO: Once iterable is implemented, use tiebreak rules instead of
+ # failing. Also, may be more tiebreak rules to implement once spec bug
+ # is resolved.
+ # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
+ def hasIterator(methods, regular):
+ return (any("@@iterator" in m.aliases for m in methods)
+ or any("@@iterator" == r["name"] for r in regular))
+
+ # Check whether we need to output an @@iterator due to having an indexed
+ # getter. We only do this while outputting non-static and
+ # non-unforgeable methods, since the @@iterator function will be
+ # neither.
+ if (not static
+ and not unforgeable
+ and descriptor.supportsIndexedProperties()): # noqa
+ if hasIterator(methods, self.regular): # noqa
+ raise TypeError("Cannot have indexed getter/attr on "
+ "interface %s with other members "
+ "that generate @@iterator, such as "
+ "maplike/setlike or aliased functions." %
+ self.descriptor.interface.identifier.name)
self.regular.append({"name": '@@iterator',
"methodInfo": False,
- "selfHostedName": "ArrayValues",
+ "selfHostedName": "$ArrayValues",
"length": 0,
+ "flags": "0", # Not enumerable, per spec.
"condition": "Condition::Satisfied"})
# Generate the keys/values/entries aliases for value iterables.
maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
- if (not static and not unforgeable and
- (maplikeOrSetlikeOrIterable and
- maplikeOrSetlikeOrIterable.isIterable() and
- maplikeOrSetlikeOrIterable.isValueIterator())):
+ if (not static and not unforgeable
+ and maplikeOrSetlikeOrIterable
+ and maplikeOrSetlikeOrIterable.isIterable()
+ and maplikeOrSetlikeOrIterable.isValueIterator()):
+ m = maplikeOrSetlikeOrIterable
+
# Add our keys/values/entries/forEach
self.regular.append({
"name": "keys",
"methodInfo": False,
"selfHostedName": "ArrayKeys",
"length": 0,
+ "flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
self.regular.append({
"name": "values",
"methodInfo": False,
- "selfHostedName": "ArrayValues",
+ "selfHostedName": "$ArrayValues",
"length": 0,
+ "flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
@@ -1606,6 +1739,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": False,
"selfHostedName": "ArrayEntries",
"length": 0,
+ "flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
@@ -1614,6 +1748,7 @@ class MethodDefiner(PropertyDefiner):
"methodInfo": False,
"selfHostedName": "ArrayForEach",
"length": 1,
+ "flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
@@ -1626,6 +1761,7 @@ class MethodDefiner(PropertyDefiner):
"name": "toString",
"nativeName": stringifier.identifier.name,
"length": 0,
+ "flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
})
self.unforgeable = unforgeable
@@ -1637,18 +1773,15 @@ class MethodDefiner(PropertyDefiner):
def condition(m, d):
return m["condition"]
- flags = "JSPROP_ENUMERATE"
- if self.unforgeable:
- flags += " | JSPROP_PERMANENT | JSPROP_READONLY"
-
def specData(m):
- # TODO: Use something like JS_FNSPEC
- # https://github.com/servo/servo/issues/6391
+ flags = m["flags"]
+ if self.unforgeable:
+ flags += " | JSPROP_PERMANENT | JSPROP_READONLY"
if "selfHostedName" in m:
selfHostedName = '%s as *const u8 as *const libc::c_char' % str_to_const_array(m["selfHostedName"])
assert not m.get("methodInfo", True)
accessor = "None"
- jitinfo = "0 as *const JSJitInfo"
+ jitinfo = "ptr::null()"
else:
selfHostedName = "0 as *const libc::c_char"
if m.get("methodInfo", True):
@@ -1660,28 +1793,30 @@ class MethodDefiner(PropertyDefiner):
jitinfo = "&%s_methodinfo as *const _ as *const JSJitInfo" % identifier
accessor = "Some(generic_method)"
else:
- jitinfo = "0 as *const JSJitInfo"
+ jitinfo = "ptr::null()"
accessor = 'Some(%s)' % m.get("nativeName", m["name"])
if m["name"].startswith("@@"):
- return ('(SymbolCode::%s as i32 + 1)'
- % m["name"][2:], accessor, jitinfo, m["length"], flags, selfHostedName)
- return (str_to_const_array(m["name"]), accessor, jitinfo, m["length"], flags, selfHostedName)
+ name = 'JSPropertySpec_Name { symbol_: SymbolCode::%s as usize + 1 }' % m["name"][2:]
+ else:
+ name = ('JSPropertySpec_Name { string_: %s as *const u8 as *const libc::c_char }'
+ % str_to_const_array(m["name"]))
+ return (name, accessor, jitinfo, m["length"], flags, selfHostedName)
return self.generateGuardedArray(
array, name,
' JSFunctionSpec {\n'
- ' name: %s as *const u8 as *const libc::c_char,\n'
+ ' name: %s,\n'
' call: JSNativeWrapper { op: %s, info: %s },\n'
' nargs: %s,\n'
' flags: (%s) as u16,\n'
' selfHostedName: %s\n'
' }',
' JSFunctionSpec {\n'
- ' name: 0 as *const libc::c_char,\n'
- ' call: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo },\n'
+ ' name: JSPropertySpec_Name { string_: ptr::null() },\n'
+ ' call: JSNativeWrapper { op: None, info: ptr::null() },\n'
' nargs: 0,\n'
' flags: 0,\n'
- ' selfHostedName: 0 as *const libc::c_char\n'
+ ' selfHostedName: ptr::null()\n'
' }',
'JSFunctionSpec',
condition, specData)
@@ -1694,23 +1829,35 @@ class AttrDefiner(PropertyDefiner):
self.name = name
self.descriptor = descriptor
self.regular = [
- m
+ {
+ "name": m.identifier.name,
+ "attr": m,
+ "flags": "JSPROP_ENUMERATE",
+ "is_accessor": "true",
+ }
for m in descriptor.interface.members if
- m.isAttr() and m.isStatic() == static and
- MemberIsUnforgeable(m, descriptor) == unforgeable
+ m.isAttr() and m.isStatic() == static
+ and MemberIsUnforgeable(m, descriptor) == unforgeable
]
self.static = static
self.unforgeable = unforgeable
+ if not static and not unforgeable and not (
+ descriptor.interface.isNamespace() or descriptor.interface.isCallback()
+ ):
+ self.regular.append({
+ "name": "@@toStringTag",
+ "attr": None,
+ "flags": "JSPROP_READONLY",
+ "is_accessor": "false",
+ })
+
def generateArray(self, array, name):
if len(array) == 0:
return ""
- flags = "JSPROP_ENUMERATE | JSPROP_SHARED"
- if self.unforgeable:
- flags += " | JSPROP_PERMANENT"
-
def getter(attr):
+ attr = attr['attr']
if self.static:
accessor = 'get_' + self.descriptor.internalNameFor(attr.identifier.name)
jitinfo = "0 as *const JSJitInfo"
@@ -1726,6 +1873,7 @@ class AttrDefiner(PropertyDefiner):
"native": accessor})
def setter(attr):
+ attr = attr['attr']
if (attr.readonly and not attr.getExtendedAttribute("PutForwards")
and not attr.getExtendedAttribute("Replaceable")):
return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }"
@@ -1744,26 +1892,61 @@ class AttrDefiner(PropertyDefiner):
% {"info": jitinfo,
"native": accessor})
+ def condition(m, d):
+ if m["name"] == "@@toStringTag":
+ return MemberCondition(pref=None, func=None, exposed=None, secure=None)
+ return PropertyDefiner.getControllingCondition(m["attr"], d)
+
def specData(attr):
- return (str_to_const_array(attr.identifier.name), flags, getter(attr),
+ if attr["name"] == "@@toStringTag":
+ return (attr["name"][2:], attr["flags"], attr["is_accessor"],
+ str_to_const_array(self.descriptor.interface.getClassName()))
+
+ flags = attr["flags"]
+ if self.unforgeable:
+ flags += " | JSPROP_PERMANENT"
+ return (str_to_const_array(attr["attr"].identifier.name), flags, attr["is_accessor"], getter(attr),
setter(attr))
+ def template(m):
+ if m["name"] == "@@toStringTag":
+ return """ JSPropertySpec {
+ name: JSPropertySpec_Name { symbol_: SymbolCode::%s as usize + 1 },
+ attributes_: (%s) as u8,
+ isAccessor_: (%s),
+ u: JSPropertySpec_AccessorsOrValue {
+ value: JSPropertySpec_ValueWrapper {
+ type_: JSPropertySpec_ValueWrapper_Type::String,
+ __bindgen_anon_1: JSPropertySpec_ValueWrapper__bindgen_ty_1 {
+ string: %s as *const u8 as *const libc::c_char,
+ }
+ }
+ }
+ }
+"""
+ return """ JSPropertySpec {
+ name: JSPropertySpec_Name { string_: %s as *const u8 as *const libc::c_char },
+ attributes_: (%s) as u8,
+ isAccessor_: (%s),
+ u: JSPropertySpec_AccessorsOrValue {
+ accessors: JSPropertySpec_AccessorsOrValue_Accessors {
+ getter: JSPropertySpec_Accessor {
+ native: %s,
+ },
+ setter: JSPropertySpec_Accessor {
+ native: %s,
+ }
+ }
+ }
+ }
+"""
+
return self.generateGuardedArray(
array, name,
- ' JSPropertySpec {\n'
- ' name: %s as *const u8 as *const libc::c_char,\n'
- ' flags: (%s) as u8,\n'
- ' getter: %s,\n'
- ' setter: %s\n'
- ' }',
- ' JSPropertySpec {\n'
- ' name: 0 as *const libc::c_char,\n'
- ' flags: 0,\n'
- ' getter: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo },\n'
- ' setter: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }\n'
- ' }',
+ template,
+ ' JSPropertySpec::ZERO',
'JSPropertySpec',
- PropertyDefiner.getControllingCondition, specData)
+ condition, specData)
class ConstDefiner(PropertyDefiner):
@@ -1790,6 +1973,7 @@ class ConstDefiner(PropertyDefiner):
'ConstantSpec',
PropertyDefiner.getControllingCondition, specData)
+
# We'll want to insert the indent at the beginnings of lines, but we
# don't want to indent empty lines. So only indent lines that have a
# non-newline character on them.
@@ -1835,11 +2019,36 @@ class CGWrapper(CGThing):
return self.pre + defn + self.post
+class CGRecord(CGThing):
+ """
+ CGThing that wraps value CGThing in record with key type equal to keyType parameter
+ """
+ def __init__(self, keyType, value):
+ CGThing.__init__(self)
+ assert keyType.isString()
+ self.keyType = keyType
+ self.value = value
+
+ def define(self):
+ if self.keyType.isByteString():
+ keyDef = "ByteString"
+ elif self.keyType.isDOMString():
+ keyDef = "DOMString"
+ elif self.keyType.isUSVString():
+ keyDef = "USVString"
+ else:
+ assert False
+
+ defn = keyDef + ", " + self.value.define()
+ return "Record<" + defn + ">"
+
+
class CGImports(CGWrapper):
"""
Generates the appropriate import/use statements.
"""
- def __init__(self, child, descriptors, callbacks, dictionaries, enums, imports, config, ignored_warnings=None):
+ def __init__(self, child, descriptors, callbacks, dictionaries, enums, typedefs, imports, config,
+ ignored_warnings=None):
"""
Adds a set of imports.
"""
@@ -1866,8 +2075,8 @@ class CGImports(CGWrapper):
def isImportable(type):
if not type.isType():
- assert (type.isInterface() or type.isDictionary() or
- type.isEnum() or type.isNamespace())
+ assert (type.isInterface() or type.isDictionary()
+ or type.isEnum() or type.isNamespace())
return True
return not (type.builtin or type.isSequence() or type.isUnion())
@@ -1912,7 +2121,7 @@ class CGImports(CGWrapper):
members += [constructor]
if d.proxy:
- members += [o for o in d.operations.values() if o]
+ members += [o for o in list(d.operations.values()) if o]
for m in members:
if m.isMethod():
@@ -1928,6 +2137,11 @@ class CGImports(CGWrapper):
for d in dictionaries:
types += componentTypes(d)
+ # Import the type names used in the typedefs that are being defined.
+ for t in typedefs:
+ if not t.innerType.isCallback():
+ types += componentTypes(t.innerType)
+
# Normalize the types we've collected and remove any ones which can't be imported.
types = removeWrapperAndNullableTypes(types)
@@ -1943,11 +2157,14 @@ class CGImports(CGWrapper):
if name != 'GlobalScope':
extras += [descriptor.path]
parentName = descriptor.getParentName()
- if parentName:
+ while parentName:
descriptor = descriptorProvider.getDescriptor(parentName)
extras += [descriptor.path, descriptor.bindingPath]
- elif t.isType() and t.isMozMap():
- extras += ['dom::bindings::mozmap::MozMap']
+ parentName = descriptor.getParentName()
+ elif t.isType() and t.isRecord():
+ extras += ['crate::dom::bindings::record::Record']
+ elif isinstance(t, IDLPromiseType):
+ extras += ['crate::dom::promise::Promise']
else:
if t.isEnum():
extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values']
@@ -1997,15 +2214,15 @@ def DOMClassTypeId(desc):
inner = ""
if desc.hasDescendants():
if desc.interface.getExtendedAttribute("Abstract"):
- return "::dom::bindings::codegen::InheritTypes::TopTypeId { abstract_: () }"
+ return "crate::dom::bindings::codegen::InheritTypes::TopTypeId { abstract_: () }"
name = desc.interface.identifier.name
- inner = "(::dom::bindings::codegen::InheritTypes::%sTypeId::%s)" % (name, name)
+ inner = "(crate::dom::bindings::codegen::InheritTypes::%sTypeId::%s)" % (name, name)
elif len(protochain) == 1:
- return "::dom::bindings::codegen::InheritTypes::TopTypeId { alone: () }"
+ return "crate::dom::bindings::codegen::InheritTypes::TopTypeId { alone: () }"
reversed_protochain = list(reversed(protochain))
for (child, parent) in zip(reversed_protochain, reversed_protochain[1:]):
- inner = "(::dom::bindings::codegen::InheritTypes::%sTypeId::%s%s)" % (parent, child, inner)
- return "::dom::bindings::codegen::InheritTypes::TopTypeId { %s: %s }" % (protochain[0].lower(), inner)
+ inner = "(crate::dom::bindings::codegen::InheritTypes::%sTypeId::%s%s)" % (parent, child, inner)
+ return "crate::dom::bindings::codegen::InheritTypes::TopTypeId { %s: %s }" % (protochain[0].lower(), inner)
def DOMClass(descriptor):
@@ -2016,7 +2233,7 @@ def DOMClass(descriptor):
# padding.
protoList.extend(['PrototypeList::ID::Last'] * (descriptor.config.maxProtoChainLength - len(protoList)))
prototypeChainString = ', '.join(protoList)
- heapSizeOf = 'heap_size_of_raw_self_and_children::<%s>' % descriptor.concreteType
+ mallocSizeOf = 'malloc_size_of_including_raw_self::<%s>' % descriptor.concreteType
if descriptor.isGlobal():
globals_ = camel_to_upper_snake(descriptor.name)
else:
@@ -2025,9 +2242,9 @@ def DOMClass(descriptor):
DOMClass {
interface_chain: [ %s ],
type_id: %s,
- heap_size_of: %s as unsafe fn(_) -> _,
- global: InterfaceObjectMap::%s,
-}""" % (prototypeChainString, DOMClassTypeId(descriptor), heapSizeOf, globals_)
+ malloc_size_of: %s as unsafe fn(&mut _, _) -> _,
+ global: InterfaceObjectMap::Globals::%s,
+}""" % (prototypeChainString, DOMClassTypeId(descriptor), mallocSizeOf, globals_)
class CGDOMJSClass(CGThing):
@@ -2039,11 +2256,15 @@ class CGDOMJSClass(CGThing):
self.descriptor = descriptor
def define(self):
+ parentName = self.descriptor.getParentName()
+ if not parentName:
+ parentName = "crate::dom::bindings::reflector::Reflector"
+
args = {
"domClass": DOMClass(self.descriptor),
"enumerateHook": "None",
"finalizeHook": FINALIZE_HOOK_NAME,
- "flags": "0",
+ "flags": "JSCLASS_FOREGROUND_FINALIZE",
"name": str_to_const_array(self.descriptor.interface.identifier.name),
"resolveHook": "None",
"slots": "1",
@@ -2052,7 +2273,7 @@ class CGDOMJSClass(CGThing):
if self.descriptor.isGlobal():
assert not self.descriptor.weakReferenceable
args["enumerateHook"] = "Some(enumerate_global)"
- args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
+ args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL | JSCLASS_FOREGROUND_FINALIZE"
args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
args["resolveHook"] = "Some(resolve_global)"
args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook"
@@ -2062,9 +2283,8 @@ class CGDOMJSClass(CGThing):
static CLASS_OPS: js::jsapi::JSClassOps = js::jsapi::JSClassOps {
addProperty: None,
delProperty: None,
- getProperty: None,
- setProperty: None,
- enumerate: %(enumerateHook)s,
+ enumerate: None,
+ newEnumerate: %(enumerateHook)s,
resolve: %(resolveHook)s,
mayResolve: None,
finalize: Some(%(finalizeHook)s),
@@ -2081,10 +2301,56 @@ static Class: DOMJSClass = DOMJSClass {
(((%(slots)s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT)
/* JSCLASS_HAS_RESERVED_SLOTS(%(slots)s) */,
cOps: &CLASS_OPS,
- reserved: [0 as *mut _; 3],
+ spec: ptr::null(),
+ ext: ptr::null(),
+ oOps: ptr::null(),
},
dom_class: %(domClass)s
-};""" % args
+};
+""" % args
+
+
+class CGAssertInheritance(CGThing):
+ """
+ Generate a type assertion for inheritance
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ parent = self.descriptor.interface.parent
+ parentName = ""
+ if parent:
+ parentName = parent.identifier.name
+ else:
+ parentName = "crate::dom::bindings::reflector::Reflector"
+
+ selfName = self.descriptor.interface.identifier.name
+
+ if selfName == "PaintRenderingContext2D":
+ # PaintRenderingContext2D embeds a CanvasRenderingContext2D
+ # instead of a Reflector as an optimization,
+ # but this is fine since CanvasRenderingContext2D
+ # also has a reflector
+ #
+ # FIXME *RenderingContext2D should use Inline
+ parentName = "crate::dom::canvasrenderingcontext2d::CanvasRenderingContext2D"
+ args = {
+ "parentName": parentName,
+ "selfName": selfName,
+ }
+
+ return """\
+impl %(selfName)s {
+ fn __assert_parent_type(&self) {
+ use crate::dom::bindings::inheritance::HasParent;
+ // If this type assertion fails, make sure the first field of your
+ // DOM struct is of the correct type -- it must be the parent class.
+ let _: &%(parentName)s = self.as_parent();
+ }
+}
+""" % args
def str_to_const_array(s):
@@ -2108,7 +2374,9 @@ static PrototypeClass: JSClass = JSClass {
// JSCLASS_HAS_RESERVED_SLOTS(%(slotCount)s)
(%(slotCount)s & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT,
cOps: 0 as *const _,
- reserved: [0 as *mut os::raw::c_void; 3]
+ spec: ptr::null(),
+ ext: ptr::null(),
+ oOps: ptr::null(),
};
""" % {'name': name, 'slotCount': slotCount}
@@ -2145,7 +2413,14 @@ static NAMESPACE_OBJECT_CLASS: NamespaceObjectClass = unsafe {
return """\
static INTERFACE_OBJECT_CLASS: NonCallbackInterfaceObjectClass =
NonCallbackInterfaceObjectClass::new(
- &%(constructorBehavior)s,
+ {
+ // Intermediate `const` because as of nightly-2018-10-05,
+ // rustc is conservative in promotion to `'static` of the return values of `const fn`s:
+ // https://github.com/rust-lang/rust/issues/54846
+ // https://github.com/rust-lang/rust/pull/53851
+ const BEHAVIOR: InterfaceConstructorBehavior = %(constructorBehavior)s;
+ &BEHAVIOR
+ },
%(representation)s,
PrototypeList::ID::%(id)s,
%(depth)s);
@@ -2209,20 +2484,19 @@ def getAllTypes(descriptors, dictionaries, callbacks, typedefs):
"""
Generate all the types we're dealing with. For each type, a tuple
containing type, descriptor, dictionary is yielded. The
- descriptor and dictionary can be None if the type does not come
- from a descriptor or dictionary; they will never both be non-None.
+ descriptor can be None if the type does not come from a descriptor.
"""
for d in descriptors:
for t in getTypesFromDescriptor(d):
- yield (t, d, None)
+ yield (t, d)
for dictionary in dictionaries:
for t in getTypesFromDictionary(dictionary):
- yield (t, None, dictionary)
+ yield (t, None)
for callback in callbacks:
for t in getTypesFromCallback(callback):
- yield (t, None, None)
+ yield (t, None)
for typedef in typedefs:
- yield (typedef.innerType, None, None)
+ yield (typedef.innerType, None)
def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
@@ -2231,40 +2505,53 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
"""
imports = [
- 'dom',
- 'dom::bindings::codegen::PrototypeList',
- 'dom::bindings::conversions::ConversionResult',
- 'dom::bindings::conversions::FromJSValConvertible',
- 'dom::bindings::conversions::ToJSValConvertible',
- 'dom::bindings::conversions::ConversionBehavior',
- 'dom::bindings::conversions::StringificationBehavior',
- 'dom::bindings::conversions::root_from_handlevalue',
- 'dom::bindings::error::throw_not_in_union',
- 'dom::bindings::js::Root',
- 'dom::bindings::mozmap::MozMap',
- 'dom::bindings::str::ByteString',
- 'dom::bindings::str::DOMString',
- 'dom::bindings::str::USVString',
- 'dom::types::*',
+ 'crate::dom',
+ 'crate::dom::bindings::codegen::PrototypeList',
+ 'crate::dom::bindings::conversions::ConversionResult',
+ 'crate::dom::bindings::conversions::FromJSValConvertible',
+ 'crate::dom::bindings::conversions::ToJSValConvertible',
+ 'crate::dom::bindings::conversions::ConversionBehavior',
+ 'crate::dom::bindings::conversions::StringificationBehavior',
+ 'crate::dom::bindings::conversions::root_from_handlevalue',
+ 'crate::dom::bindings::conversions::windowproxy_from_handlevalue',
+ 'std::ptr::NonNull',
+ 'std::rc::Rc',
+ 'crate::dom::bindings::record::Record',
+ 'crate::dom::bindings::num::Finite',
+ 'crate::dom::bindings::root::DomRoot',
+ 'crate::dom::bindings::str::ByteString',
+ 'crate::dom::bindings::str::DOMString',
+ 'crate::dom::bindings::str::USVString',
+ 'crate::dom::bindings::trace::RootedTraceableBox',
+ 'crate::dom::types::*',
+ 'crate::dom::windowproxy::WindowProxy',
+ 'crate::script_runtime::JSContext as SafeJSContext',
'js::error::throw_type_error',
- 'js::jsapi::HandleValue',
+ 'js::rust::HandleValue',
'js::jsapi::Heap',
+ 'js::jsapi::IsCallable',
'js::jsapi::JSContext',
'js::jsapi::JSObject',
- 'js::jsapi::MutableHandleValue',
+ 'js::rust::MutableHandleValue',
'js::jsval::JSVal',
+ 'js::typedarray'
]
# Now find all the things we'll need as arguments and return values because
# we need to wrap or unwrap them.
unionStructs = dict()
- for (t, descriptor, dictionary) in getAllTypes(descriptors, dictionaries, callbacks, typedefs):
- if dictionary:
- imports.append("%s::%s" % (CGDictionary.makeModuleName(dictionary),
- CGDictionary.makeDictionaryName(dictionary)))
+ for (t, descriptor) in getAllTypes(descriptors, dictionaries, callbacks, typedefs):
t = t.unroll()
if not t.isUnion():
continue
+ for memberType in t.flatMemberTypes:
+ if memberType.isDictionary() or memberType.isEnum() or memberType.isCallback():
+ memberModule = getModuleFromObject(memberType)
+ memberName = (memberType.callback.identifier.name
+ if memberType.isCallback() else memberType.inner.identifier.name)
+ imports.append("%s::%s" % (memberModule, memberName))
+ if memberType.isEnum():
+ imports.append("%s::%sValues" % (memberModule, memberName))
name = str(t)
if name not in unionStructs:
provider = descriptor or config.getDescriptorProvider()
@@ -2274,13 +2561,14 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
])
# Sort unionStructs by key, retrieve value
- unionStructs = (i[1] for i in sorted(unionStructs.items(), key=operator.itemgetter(0)))
+ unionStructs = (i[1] for i in sorted(list(unionStructs.items()), key=operator.itemgetter(0)))
return CGImports(CGList(unionStructs, "\n\n"),
descriptors=[],
callbacks=[],
dictionaries=[],
enums=[],
+ typedefs=[],
imports=imports,
config=config,
ignored_warnings=[])
@@ -2391,9 +2679,19 @@ class CGAbstractMethod(CGThing):
body = self.definition_body()
if self.catchPanic:
- body = CGWrapper(CGIndenter(body),
- pre="return wrap_panic(panic::AssertUnwindSafe(|| {\n",
- post=("""\n}), %s);""" % ("()" if self.returnType == "void" else "false")))
+ if self.returnType == "void":
+ pre = "wrap_panic(&mut || {\n"
+ post = "\n})"
+ else:
+ pre = (
+ "let mut result = false;\n"
+ "wrap_panic(&mut || result = (|| {\n"
+ )
+ post = (
+ "\n})());\n"
+ "return result"
+ )
+ body = CGWrapper(CGIndenter(body), pre=pre, post=post)
return CGWrapper(CGIndenter(body),
pre=self.definition_prologue(),
@@ -2420,23 +2718,22 @@ class CGConstructorEnabled(CGAbstractMethod):
def __init__(self, descriptor):
CGAbstractMethod.__init__(self, descriptor,
'ConstructorEnabled', 'bool',
- [Argument("*mut JSContext", "aCx"),
- Argument("HandleObject", "aObj")],
- unsafe=True)
+ [Argument("SafeJSContext", "aCx"),
+ Argument("HandleObject", "aObj")])
def definition_body(self):
conditions = []
iface = self.descriptor.interface
bits = " | ".join(sorted(
- "InterfaceObjectMap::" + camel_to_upper_snake(i) for i in iface.exposureSet
+ "InterfaceObjectMap::Globals::" + camel_to_upper_snake(i) for i in iface.exposureSet
))
conditions.append("is_exposed_in(aObj, %s)" % bits)
pref = iface.getExtendedAttribute("Pref")
if pref:
assert isinstance(pref, list) and len(pref) == 1
- conditions.append('PREFS.get("%s").as_boolean().unwrap_or(false)' % pref[0])
+ conditions.append('pref!(%s)' % pref[0])
func = iface.getExtendedAttribute("Func")
if func:
@@ -2446,33 +2743,6 @@ class CGConstructorEnabled(CGAbstractMethod):
return CGList((CGGeneric(cond) for cond in conditions), " &&\n")
-def CreateBindingJSObject(descriptor, parent=None):
- assert not descriptor.isGlobal()
- create = "let raw = Box::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n"
- if descriptor.proxy:
- create += """
-let handler = RegisterBindings::PROXY_HANDLERS[PrototypeList::Proxies::%s as usize];
-rooted!(in(cx) let private = PrivateValue(raw as *const libc::c_void));
-let obj = NewProxyObject(cx, handler,
- private.handle(),
- proto.get(), %s.get(),
- ptr::null_mut(), ptr::null_mut());
-assert!(!obj.is_null());
-rooted!(in(cx) let obj = obj);\
-""" % (descriptor.name, parent)
- else:
- create += ("rooted!(in(cx) let obj = JS_NewObjectWithGivenProto(\n"
- " cx, &Class.base as *const JSClass, proto.handle()));\n"
- "assert!(!obj.is_null());\n"
- "\n"
- "JS_SetReservedSlot(obj.get(), DOM_OBJECT_SLOT,\n"
- " PrivateValue(raw as *const libc::c_void));")
- if descriptor.weakReferenceable:
- create += """
-JS_SetReservedSlot(obj.get(), DOM_WEAK_SLOT, PrivateValue(ptr::null()));"""
- return create
-
-
def InitUnforgeablePropertiesOnHolder(descriptor, properties):
"""
Define the unforgeable properties on the unforgeable holder for
@@ -2482,8 +2752,8 @@ def InitUnforgeablePropertiesOnHolder(descriptor, properties):
"""
unforgeables = []
- defineUnforgeableAttrs = "define_guarded_properties(cx, unforgeable_holder.handle(), %s);"
- defineUnforgeableMethods = "define_guarded_methods(cx, unforgeable_holder.handle(), %s);"
+ defineUnforgeableAttrs = "define_guarded_properties(cx, unforgeable_holder.handle(), %s, global);"
+ defineUnforgeableMethods = "define_guarded_methods(cx, unforgeable_holder.handle(), %s, global);"
unforgeableMembers = [
(defineUnforgeableAttrs, properties.unforgeable_attrs),
@@ -2508,8 +2778,8 @@ def CopyUnforgeablePropertiesToInstance(descriptor):
# reflector, so we can make sure we don't get confused by named getters.
if descriptor.proxy:
copyCode += """\
-rooted!(in(cx) let mut expando = ptr::null_mut());
-ensure_expando_object(cx, obj.handle(), expando.handle_mut());
+rooted!(in(*cx) let mut expando = ptr::null_mut::<JSObject>());
+ensure_expando_object(*cx, obj.handle().into(), expando.handle_mut());
"""
obj = "expando"
else:
@@ -2519,14 +2789,15 @@ ensure_expando_object(cx, obj.handle(), expando.handle_mut());
# unforgeable holder for those with the right JSClass. Luckily, there
# aren't too many globals being created.
if descriptor.isGlobal():
- copyFunc = "JS_CopyPropertiesFrom"
+ copyFunc = "JS_CopyOwnPropertiesAndPrivateFields"
else:
copyFunc = "JS_InitializePropertiesFromCompatibleNativeObject"
copyCode += """\
-rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut());
-unforgeable_holder.handle_mut().set(
- JS_GetReservedSlot(proto.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT).to_object());
-assert!(%(copyFunc)s(cx, %(obj)s.handle(), unforgeable_holder.handle()));
+let mut slot = UndefinedValue();
+JS_GetReservedSlot(proto.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, &mut slot);
+rooted!(in(*cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>());
+unforgeable_holder.handle_mut().set(slot.to_object());
+assert!(%(copyFunc)s(*cx, %(obj)s.handle(), unforgeable_holder.handle()));
""" % {'copyFunc': copyFunc, 'obj': obj}
return copyCode
@@ -2540,32 +2811,74 @@ class CGWrapMethod(CGAbstractMethod):
def __init__(self, descriptor):
assert not descriptor.interface.isCallback()
assert not descriptor.isGlobal()
- args = [Argument('*mut JSContext', 'cx'),
+ args = [Argument('SafeJSContext', 'cx'),
Argument('&GlobalScope', 'scope'),
Argument("Box<%s>" % descriptor.concreteType, 'object')]
- retval = 'Root<%s>' % descriptor.concreteType
+ retval = 'DomRoot<%s>' % descriptor.concreteType
CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args,
pub=True, unsafe=True)
def definition_body(self):
unforgeable = CopyUnforgeablePropertiesToInstance(self.descriptor)
- create = CreateBindingJSObject(self.descriptor, "scope")
+ if self.descriptor.proxy:
+ create = """
+let handler: *const libc::c_void =
+ RegisterBindings::proxy_handlers::%(concreteType)s
+ .load(std::sync::atomic::Ordering::Acquire);
+rooted!(in(*cx) let obj = NewProxyObject(
+ *cx,
+ handler,
+ Handle::from_raw(UndefinedHandleValue),
+ proto.get(),
+ ptr::null(),
+));
+assert!(!obj.is_null());
+SetProxyReservedSlot(
+ obj.get(),
+ 0,
+ &PrivateValue(raw.as_ptr() as *const %(concreteType)s as *const libc::c_void),
+);
+"""
+ else:
+ create = """
+rooted!(in(*cx) let obj = JS_NewObjectWithGivenProto(
+ *cx,
+ &Class.base,
+ proto.handle(),
+));
+assert!(!obj.is_null());
+JS_SetReservedSlot(
+ obj.get(),
+ DOM_OBJECT_SLOT,
+ &PrivateValue(raw.as_ptr() as *const %(concreteType)s as *const libc::c_void),
+);
+"""
+ create = create % {"concreteType": self.descriptor.concreteType}
+ if self.descriptor.weakReferenceable:
+ create += """
+let val = PrivateValue(ptr::null());
+JS_SetReservedSlot(obj.get(), DOM_WEAK_SLOT, &val);
+"""
+
return CGGeneric("""\
+let raw = Root::new(MaybeUnreflectedDom::from_box(object));
+
let scope = scope.reflector().get_jsobject();
assert!(!scope.get().is_null());
assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0);
+let _ac = JSAutoRealm::new(*cx, scope.get());
-rooted!(in(cx) let mut proto = ptr::null_mut());
-let _ac = JSAutoCompartment::new(cx, scope.get());
+rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>());
GetProtoObject(cx, scope, proto.handle_mut());
assert!(!proto.is_null());
%(createObject)s
+let root = raw.reflect_with(obj.get());
%(copyUnforgeable)s
-(*raw).init_reflector(obj.get());
-Root::from_ref(&*raw)""" % {'copyUnforgeable': unforgeable, 'createObject': create})
+DomRoot::from_ref(&*root)\
+""" % {'copyUnforgeable': unforgeable, 'createObject': create})
class CGWrapGlobalMethod(CGAbstractMethod):
@@ -2575,15 +2888,16 @@ class CGWrapGlobalMethod(CGAbstractMethod):
def __init__(self, descriptor, properties):
assert not descriptor.interface.isCallback()
assert descriptor.isGlobal()
- args = [Argument('*mut JSContext', 'cx'),
+ args = [Argument('SafeJSContext', 'cx'),
Argument("Box<%s>" % descriptor.concreteType, 'object')]
- retval = 'Root<%s>' % descriptor.concreteType
+ retval = 'DomRoot<%s>' % descriptor.concreteType
CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args,
pub=True, unsafe=True)
self.properties = properties
def definition_body(self):
values = {
+ "concreteType": self.descriptor.concreteType,
"unforgeable": CopyUnforgeablePropertiesToInstance(self.descriptor)
}
@@ -2592,40 +2906,39 @@ class CGWrapGlobalMethod(CGAbstractMethod):
("define_guarded_methods", self.properties.methods),
("define_guarded_constants", self.properties.consts)
]
- members = ["%s(cx, obj.handle(), %s);" % (function, array.variableName())
+ members = ["%s(cx, obj.handle(), %s, obj.handle());" % (function, array.variableName())
for (function, array) in pairs if array.length() > 0]
values["members"] = "\n".join(members)
return CGGeneric("""\
let origin = object.origin().clone();
-let raw = Box::into_raw(object);
-let _rt = RootedTraceable::new(&*raw);
+let raw = Root::new(MaybeUnreflectedDom::from_box(object));
-rooted!(in(cx) let mut obj = ptr::null_mut());
+rooted!(in(*cx) let mut obj = ptr::null_mut::<JSObject>());
create_global_object(
cx,
&Class.base,
- raw as *const libc::c_void,
+ raw.as_ptr() as *const %(concreteType)s as *const libc::c_void,
_trace,
obj.handle_mut(),
&origin);
assert!(!obj.is_null());
-(*raw).init_reflector(obj.get());
+let root = raw.reflect_with(obj.get());
-let _ac = JSAutoCompartment::new(cx, obj.get());
-rooted!(in(cx) let mut proto = ptr::null_mut());
+let _ac = JSAutoRealm::new(*cx, obj.get());
+rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>());
GetProtoObject(cx, obj.handle(), proto.handle_mut());
-assert!(JS_SplicePrototype(cx, obj.handle(), proto.handle()));
+assert!(JS_SetPrototype(*cx, obj.handle(), proto.handle()));
let mut immutable = false;
-assert!(JS_SetImmutablePrototype(cx, obj.handle(), &mut immutable));
+assert!(JS_SetImmutablePrototype(*cx, obj.handle(), &mut immutable));
assert!(immutable);
%(members)s
%(unforgeable)s
-Root::from_ref(&*raw)\
+DomRoot::from_ref(&*root)\
""" % values)
@@ -2640,8 +2953,8 @@ class CGIDLInterface(CGThing):
def define(self):
interface = self.descriptor.interface
name = self.descriptor.concreteType
- if (interface.getUserData("hasConcreteDescendant", False) or
- interface.getUserData("hasProxyDescendant", False)):
+ if (interface.getUserData("hasConcreteDescendant", False)
+ or interface.getUserData("hasProxyDescendant", False)):
depth = self.descriptor.prototypeDepth
check = "class.interface_chain[%s] == PrototypeList::ID::%s" % (depth, name)
elif self.descriptor.proxy:
@@ -2664,6 +2977,50 @@ impl PartialEq for %(name)s {
""" % {'check': check, 'name': name}
+class CGDomObjectWrap(CGThing):
+ """
+ Class for codegen of an implementation of the DomObjectWrap trait.
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ name = self.descriptor.concreteType
+ name = "dom::%s::%s" % (name.lower(), name)
+ return """\
+impl DomObjectWrap for %s {
+ const WRAP: unsafe fn(
+ SafeJSContext,
+ &GlobalScope,
+ Box<Self>,
+ ) -> Root<Dom<Self>> = Wrap;
+}
+""" % (name)
+
+
+class CGDomObjectIteratorWrap(CGThing):
+ """
+ Class for codegen of an implementation of the DomObjectIteratorWrap trait.
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ assert self.descriptor.interface.isIteratorInterface()
+ name = self.descriptor.interface.iterableInterface.identifier.name
+ return """\
+impl DomObjectIteratorWrap for %s {
+ const ITER_WRAP: unsafe fn(
+ SafeJSContext,
+ &GlobalScope,
+ Box<IterableIterator<Self>>,
+ ) -> Root<Dom<IterableIterator<Self>>> = Wrap;
+}
+""" % (name)
+
+
class CGAbstractExternMethod(CGAbstractMethod):
"""
Abstract base class for codegen of implementation-only (no
@@ -2714,35 +3071,83 @@ class PropertyArrays():
return define
+class CGCollectJSONAttributesMethod(CGAbstractMethod):
+ """
+ Generate the CollectJSONAttributes method for an interface descriptor
+ """
+ def __init__(self, descriptor, toJSONMethod):
+ args = [Argument('*mut JSContext', 'cx'),
+ Argument('RawHandleObject', 'obj'),
+ Argument('*mut libc::c_void', 'this'),
+ Argument('&RootedGuard<*mut JSObject>', 'result')]
+ CGAbstractMethod.__init__(self, descriptor, 'CollectJSONAttributes',
+ 'bool', args, pub=True, unsafe=True)
+ self.toJSONMethod = toJSONMethod
+
+ def definition_body(self):
+ ret = """let incumbent_global = GlobalScope::incumbent().expect("no incumbent global");
+let global = incumbent_global.reflector().get_jsobject();\n"""
+ interface = self.descriptor.interface
+ for m in interface.members:
+ if m.isAttr() and not m.isStatic() and m.type.isJSONType():
+ name = m.identifier.name
+ conditions = MemberCondition(None, None, m.exposureSet, None)
+ ret_conditions = '&[' + ", ".join(conditions) + "]"
+ ret += fill(
+ """
+ let conditions = ${conditions};
+ let is_satisfied = conditions.iter().any(|c|
+ c.is_satisfied(
+ SafeJSContext::from_ptr(cx),
+ HandleObject::from_raw(obj),
+ global));
+ if is_satisfied {
+ rooted!(in(cx) let mut temp = UndefinedValue());
+ if !get_${name}(cx, obj, this, JSJitGetterCallArgs { _base: temp.handle_mut().into() }) {
+ return false;
+ }
+ if !JS_DefineProperty(cx, result.handle().into(),
+ ${nameAsArray} as *const u8 as *const libc::c_char,
+ temp.handle(), JSPROP_ENUMERATE as u32) {
+ return false;
+ }
+ }
+ """,
+ name=name, nameAsArray=str_to_const_array(name), conditions=ret_conditions)
+ ret += 'return true;\n'
+ return CGGeneric(ret)
+
+
class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
"""
Generate the CreateInterfaceObjects method for an interface descriptor.
properties should be a PropertyArrays instance.
"""
- def __init__(self, descriptor, properties, haveUnscopables):
- args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'global'),
+ def __init__(self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases):
+ args = [Argument('SafeJSContext', 'cx'), Argument('HandleObject', 'global'),
Argument('*mut ProtoOrIfaceArray', 'cache')]
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args,
unsafe=True)
self.properties = properties
self.haveUnscopables = haveUnscopables
+ self.haveLegacyWindowAliases = haveLegacyWindowAliases
def definition_body(self):
name = self.descriptor.interface.identifier.name
if self.descriptor.interface.isNamespace():
if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"):
- proto = "JS_GetObjectPrototype(cx, global)"
+ proto = "GetRealmObjectPrototype(*cx)"
else:
- proto = "JS_NewPlainObject(cx)"
+ proto = "JS_NewPlainObject(*cx)"
if self.properties.static_methods.length():
methods = self.properties.static_methods.variableName()
else:
methods = "&[]"
return CGGeneric("""\
-rooted!(in(cx) let proto = %(proto)s);
+rooted!(in(*cx) let proto = %(proto)s);
assert!(!proto.is_null());
-rooted!(in(cx) let mut namespace = ptr::null_mut());
+rooted!(in(*cx) let mut namespace = ptr::null_mut::<JSObject>());
create_namespace_object(cx, global, proto.handle(), &NAMESPACE_OBJECT_CLASS,
%(methods)s, %(name)s, namespace.handle_mut());
assert!(!namespace.is_null());
@@ -2755,7 +3160,7 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
if self.descriptor.interface.isCallback():
assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants()
return CGGeneric("""\
-rooted!(in(cx) let mut interface = ptr::null_mut());
+rooted!(in(*cx) let mut interface = ptr::null_mut::<JSObject>());
create_callback_interface_object(cx, global, sConstants, %(name)s, interface.handle_mut());
assert!(!interface.is_null());
assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
@@ -2768,23 +3173,25 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
parentName = self.descriptor.getParentName()
if not parentName:
if self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
- getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))"
+ protoGetter = "GetRealmErrorPrototype"
elif self.descriptor.interface.isIteratorInterface():
- getPrototypeProto = "prototype_proto.set(JS_GetIteratorPrototype(cx))"
+ protoGetter = "GetRealmIteratorPrototype"
else:
- getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))"
+ protoGetter = "GetRealmObjectPrototype"
+ getPrototypeProto = "prototype_proto.set(%s(*cx))" % protoGetter
else:
getPrototypeProto = ("%s::GetProtoObject(cx, global, prototype_proto.handle_mut())" %
toBindingNamespace(parentName))
code = [CGGeneric("""\
-rooted!(in(cx) let mut prototype_proto = ptr::null_mut());
+rooted!(in(*cx) let mut prototype_proto = ptr::null_mut::<JSObject>());
%s;
assert!(!prototype_proto.is_null());""" % getPrototypeProto)]
properties = {
"id": name,
- "unscopables": "unscopable_names" if self.haveUnscopables else "&[]"
+ "unscopables": "unscopable_names" if self.haveUnscopables else "&[]",
+ "legacyWindowAliases": "legacy_window_aliases" if self.haveLegacyWindowAliases else "&[]"
}
for arrayName in self.properties.arrayNames():
array = getattr(self.properties, arrayName)
@@ -2806,15 +3213,16 @@ assert!(!prototype_proto.is_null());""" % getPrototypeProto)]
proto_properties = properties
code.append(CGGeneric("""
-rooted!(in(cx) let mut prototype = ptr::null_mut());
+rooted!(in(*cx) let mut prototype = ptr::null_mut::<JSObject>());
create_interface_prototype_object(cx,
- prototype_proto.handle(),
+ global.into(),
+ prototype_proto.handle().into(),
&PrototypeClass,
%(methods)s,
%(attrs)s,
%(consts)s,
%(unscopables)s,
- prototype.handle_mut());
+ prototype.handle_mut().into());
assert!(!prototype.is_null());
assert!((*cache)[PrototypeList::ID::%(id)s as usize].is_null());
(*cache)[PrototypeList::ID::%(id)s as usize] = prototype.get();
@@ -2830,20 +3238,19 @@ assert!((*cache)[PrototypeList::ID::%(id)s as usize].is_null());
else:
properties["length"] = 0
parentName = self.descriptor.getParentName()
+ code.append(CGGeneric("rooted!(in(*cx) let mut interface_proto = ptr::null_mut::<JSObject>());"))
if parentName:
parentName = toBindingNamespace(parentName)
code.append(CGGeneric("""
-rooted!(in(cx) let mut interface_proto = ptr::null_mut());
%s::GetConstructorObject(cx, global, interface_proto.handle_mut());""" % parentName))
else:
- code.append(CGGeneric("""
-rooted!(in(cx) let interface_proto = JS_GetFunctionPrototype(cx, global));"""))
+ code.append(CGGeneric("interface_proto.set(GetRealmFunctionPrototype(*cx));"))
code.append(CGGeneric("""\
assert!(!interface_proto.is_null());
-rooted!(in(cx) let mut interface = ptr::null_mut());
+rooted!(in(*cx) let mut interface = ptr::null_mut::<JSObject>());
create_noncallback_interface_object(cx,
- global,
+ global.into(),
interface_proto.handle(),
&INTERFACE_OBJECT_CLASS,
%(static_methods)s,
@@ -2852,9 +3259,10 @@ create_noncallback_interface_object(cx,
prototype.handle(),
%(name)s,
%(length)s,
+ %(legacyWindowAliases)s,
interface.handle_mut());
assert!(!interface.is_null());""" % properties))
- if self.descriptor.hasDescendants():
+ if self.descriptor.shouldCacheConstructor():
code.append(CGGeneric("""\
assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
(*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.get();
@@ -2867,17 +3275,20 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
if aliasedMembers:
def defineAlias(alias):
if alias == "@@iterator":
- symbolJSID = "RUST_SYMBOL_TO_JSID(GetWellKnownSymbol(cx, SymbolCode::iterator))"
- getSymbolJSID = CGGeneric(fill("rooted!(in(cx) let iteratorId = ${symbolJSID});",
+ symbolJSID = "RUST_SYMBOL_TO_JSID(GetWellKnownSymbol(*cx, SymbolCode::iterator), \
+ iteratorId.handle_mut())"
+ getSymbolJSID = CGGeneric(fill("rooted!(in(*cx) let mut iteratorId: jsid);\n${symbolJSID};\n",
symbolJSID=symbolJSID))
defineFn = "JS_DefinePropertyById2"
prop = "iteratorId.handle()"
+ enumFlags = "0" # Not enumerable, per spec.
elif alias.startswith("@@"):
raise TypeError("Can't handle any well-known Symbol other than @@iterator")
else:
getSymbolJSID = None
defineFn = "JS_DefineProperty"
prop = '"%s"' % alias
+ enumFlags = "JSPROP_ENUMERATE"
return CGList([
getSymbolJSID,
# XXX If we ever create non-enumerable properties that can
@@ -2885,18 +3296,19 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
# match the enumerability of the property being aliased.
CGGeneric(fill(
"""
- assert!(${defineFn}(cx, prototype.handle(), ${prop}, aliasedVal.handle(),
- JSPROP_ENUMERATE, None, None));
+ assert!(${defineFn}(*cx, prototype.handle(), ${prop}, aliasedVal.handle(),
+ ${enumFlags} as u32));
""",
defineFn=defineFn,
- prop=prop))
+ prop=prop,
+ enumFlags=enumFlags))
], "\n")
def defineAliasesFor(m):
return CGList([
CGGeneric(fill(
"""
- assert!(JS_GetProperty(cx, prototype.handle(),
+ assert!(JS_GetProperty(*cx, prototype.handle(),
${prop} as *const u8 as *const _,
aliasedVal.handle_mut()));
""",
@@ -2908,7 +3320,7 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
// Set up aliases on the interface prototype object we just created.
""")),
- CGGeneric("rooted!(in(cx) let mut aliasedVal = UndefinedValue());\n\n")
+ CGGeneric("rooted!(in(*cx) let mut aliasedVal = UndefinedValue());\n\n")
] + [defineAliasesFor(m) for m in sorted(aliasedMembers)])
code.append(defineAliases)
@@ -2944,15 +3356,15 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
holderClass = "&Class.base as *const JSClass"
holderProto = "prototype.handle()"
code.append(CGGeneric("""
-rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut());
+rooted!(in(*cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>());
unforgeable_holder.handle_mut().set(
- JS_NewObjectWithoutMetadata(cx, %(holderClass)s, %(holderProto)s));
+ JS_NewObjectWithoutMetadata(*cx, %(holderClass)s, %(holderProto)s));
assert!(!unforgeable_holder.is_null());
""" % {'holderClass': holderClass, 'holderProto': holderProto}))
code.append(InitUnforgeablePropertiesOnHolder(self.descriptor, self.properties))
code.append(CGGeneric("""\
-JS_SetReservedSlot(prototype.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT,
- ObjectValue(unforgeable_holder.get()))"""))
+let val = ObjectValue(unforgeable_holder.get());
+JS_SetReservedSlot(prototype.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, &val)"""))
return CGList(code, "\n")
@@ -2963,27 +3375,29 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
constructor object).
"""
def __init__(self, descriptor, name, idPrefix="", pub=False):
- args = [Argument('*mut JSContext', 'cx'),
+ args = [Argument('SafeJSContext', 'cx'),
Argument('HandleObject', 'global'),
- Argument('MutableHandleObject', 'rval')]
+ Argument('MutableHandleObject', 'mut rval')]
CGAbstractMethod.__init__(self, descriptor, name,
- 'void', args, pub=pub, unsafe=True)
+ 'void', args, pub=pub)
self.id = idPrefix + "::" + MakeNativeName(self.descriptor.name)
def definition_body(self):
return CGGeneric("""
-assert!(((*get_object_class(global.get())).flags & JSCLASS_DOM_GLOBAL) != 0);
+unsafe {
+ assert!(((*get_object_class(global.get())).flags & JSCLASS_DOM_GLOBAL) != 0);
+
+ /* Check to see whether the interface objects are already installed */
+ let proto_or_iface_array = get_proto_or_iface_array(global.get());
+ rval.set((*proto_or_iface_array)[%(id)s as usize]);
+ if !rval.get().is_null() {
+ return;
+ }
-/* Check to see whether the interface objects are already installed */
-let proto_or_iface_array = get_proto_or_iface_array(global.get());
-rval.set((*proto_or_iface_array)[%(id)s as usize]);
-if !rval.get().is_null() {
- return;
+ CreateInterfaceObjects(cx, global, proto_or_iface_array);
+ rval.set((*proto_or_iface_array)[%(id)s as usize]);
+ assert!(!rval.get().is_null());
}
-
-CreateInterfaceObjects(cx, global, proto_or_iface_array);
-rval.set((*proto_or_iface_array)[%(id)s as usize]);
-assert!(!rval.get().is_null());
""" % {"id": self.id})
@@ -3072,7 +3486,6 @@ let traps = ProxyTraps {
set: None,
call: None,
construct: None,
- getPropertyDescriptor: Some(get_property_descriptor),
hasOwn: Some(hasOwn),
getOwnEnumerablePropertyKeys: Some(%(getOwnEnumerablePropertyKeys)s),
nativeCall: None,
@@ -3101,11 +3514,11 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
def __init__(self, descriptor):
assert descriptor.interface.hasInterfaceObject()
args = [
- Argument('*mut JSContext', 'cx'),
+ Argument('SafeJSContext', 'cx'),
Argument('HandleObject', 'global'),
]
CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface',
- 'void', args, pub=True, unsafe=True)
+ 'void', args, pub=True)
def define(self):
return CGAbstractMethod.define(self)
@@ -3122,15 +3535,15 @@ if !ConstructorEnabled(cx, global) {
return;
}
-rooted!(in(cx) let mut proto = ptr::null_mut());
+rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>());
%s(cx, global, proto.handle_mut());
assert!(!proto.is_null());""" % (function,))
def needCx(returnType, arguments, considerTypes):
- return (considerTypes and
- (typeNeedsCx(returnType, True) or
- any(typeNeedsCx(a.type) for a in arguments)))
+ return (considerTypes
+ and (typeNeedsCx(returnType, True)
+ or any(typeNeedsCx(a.type) for a in arguments)))
class CGCallGenerator(CGThing):
@@ -3143,7 +3556,7 @@ class CGCallGenerator(CGThing):
"""
def __init__(self, errorResult, arguments, argsPre, returnType,
extendedAttributes, descriptor, nativeMethodName,
- static, object="this"):
+ static, object="this", hasCEReactions=False):
CGThing.__init__(self)
assert errorResult is None or isinstance(errorResult, str)
@@ -3165,6 +3578,8 @@ class CGCallGenerator(CGThing):
if "cx" not in argsPre and needsCx:
args.prepend(CGGeneric("cx"))
+ if nativeMethodName in descriptor.inRealmMethods:
+ args.append(CGGeneric("InRealm::in_realm(&AlreadyInRealm::assert_for_cx(cx))"))
# Build up our actual call
self.cgRoot = CGList([], "\n")
@@ -3176,6 +3591,9 @@ class CGCallGenerator(CGThing):
call = CGWrapper(call, pre="%s." % object)
call = CGList([call, CGWrapper(args, pre="(", post=")")])
+ if hasCEReactions:
+ self.cgRoot.append(CGGeneric("push_new_element_queue();\n"))
+
self.cgRoot.append(CGList([
CGGeneric("let result: "),
result,
@@ -3184,6 +3602,9 @@ class CGCallGenerator(CGThing):
CGGeneric(";"),
]))
+ if hasCEReactions:
+ self.cgRoot.append(CGGeneric("pop_current_element_queue();\n"))
+
if isFallible:
if static:
glob = "global.upcast::<GlobalScope>()"
@@ -3258,11 +3679,12 @@ class CGPerSignatureCall(CGThing):
idlNode.maplikeOrSetlikeOrIterable,
idlNode.identifier.name))
else:
+ hasCEReactions = idlNode.getExtendedAttribute("CEReactions")
cgThings.append(CGCallGenerator(
errorResult,
self.getArguments(), self.argsPre, returnType,
self.extendedAttributes, descriptor, nativeMethodName,
- static))
+ static, hasCEReactions=hasCEReactions))
self.cgRoot = CGList(cgThings, "\n")
@@ -3279,7 +3701,7 @@ class CGPerSignatureCall(CGThing):
return 'infallible' not in self.extendedAttributes
def wrap_return_value(self):
- return wrapForType('args.rval()')
+ return wrapForType('MutableHandleValue::from_raw(args.rval())')
def define(self):
return (self.cgRoot.define() + "\n" + self.wrap_return_value())
@@ -3354,9 +3776,6 @@ class FakeArgument():
self.variadic = False
self.defaultValue = None
self._allowTreatNonObjectAsNull = allowTreatNonObjectAsNull
- self.treatNullAs = interfaceMember.treatNullAs
- self.enforceRange = False
- self.clamp = False
def allowTreatNonCallableAsNull(self):
return self._allowTreatNonObjectAsNull
@@ -3399,9 +3818,13 @@ class CGAbstractStaticBindingMethod(CGAbstractMethod):
self.exposureSet = descriptor.interface.exposureSet
def definition_body(self):
- preamble = "let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object());\n"
+ preamble = """\
+let args = CallArgs::from_vp(vp, argc);
+let global = GlobalScope::from_object(args.callee());
+"""
if len(self.exposureSet) == 1:
- preamble += "let global = Root::downcast::<dom::types::%s>(global).unwrap();\n" % list(self.exposureSet)[0]
+ preamble += ("let global = DomRoot::downcast::<dom::types::%s>(global).unwrap();\n" %
+ list(self.exposureSet)[0])
return CGList([CGGeneric(preamble), self.generate_code()])
def generate_code(self):
@@ -3416,8 +3839,9 @@ class CGSpecializedMethod(CGAbstractExternMethod):
def __init__(self, descriptor, method):
self.method = method
name = method.identifier.name
- args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_obj'),
- Argument('*const %s' % descriptor.concreteType, 'this'),
+ args = [Argument('*mut JSContext', 'cx'),
+ Argument('RawHandleObject', '_obj'),
+ Argument('*mut libc::c_void', 'this'),
Argument('*const JSJitMethodCallArgs', 'args')]
CGAbstractExternMethod.__init__(self, descriptor, name, 'bool', args)
@@ -3426,12 +3850,15 @@ class CGSpecializedMethod(CGAbstractExternMethod):
self.method)
return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(),
self.descriptor, self.method),
- pre="let this = &*this;\n"
- "let args = &*args;\n"
- "let argc = args._base.argc_;\n")
+ pre="let cx = SafeJSContext::from_ptr(cx);\n"
+ + ("let this = &*(this as *const %s);\n" % self.descriptor.concreteType)
+ + "let args = &*args;\n"
+ "let argc = args.argc_;\n")
@staticmethod
def makeNativeName(descriptor, method):
+ if method.underlyingAttr:
+ return CGSpecializedGetter.makeNativeName(descriptor, method.underlyingAttr)
name = method.identifier.name
nativeName = descriptor.binaryNameFor(name)
if nativeName == name:
@@ -3439,6 +3866,45 @@ class CGSpecializedMethod(CGAbstractExternMethod):
return MakeNativeName(nativeName)
+class CGDefaultToJSONMethod(CGSpecializedMethod):
+ def __init__(self, descriptor, method):
+ assert method.isDefaultToJSON()
+ CGSpecializedMethod.__init__(self, descriptor, method)
+
+ def definition_body(self):
+ ret = dedent("""
+ use crate::dom::bindings::inheritance::HasParent;
+ rooted!(in(cx) let result = JS_NewPlainObject(cx));
+ if result.is_null() {
+ return false;
+ }
+ """)
+
+ jsonDescriptors = [self.descriptor]
+ interface = self.descriptor.interface.parent
+ while interface:
+ descriptor = self.descriptor.getDescriptor(interface.identifier.name)
+ if descriptor.hasDefaultToJSON:
+ jsonDescriptors.append(descriptor)
+ interface = interface.parent
+
+ parents = len(jsonDescriptors) - 1
+ form = """
+ if !${parentclass}CollectJSONAttributes(cx, _obj, this, &result) {
+ return false;
+ }
+ """
+
+ # Iterate the array in reverse: oldest ancestor first
+ for descriptor in jsonDescriptors[:0:-1]:
+ ret += fill(form, parentclass=toBindingNamespace(descriptor.name) + "::")
+ parents -= 1
+ ret += fill(form, parentclass="")
+ ret += ('(*args).rval().set(ObjectValue(*result));\n'
+ 'return true;\n')
+ return CGGeneric(ret)
+
+
class CGStaticMethod(CGAbstractStaticBindingMethod):
"""
A class for generating the Rust code for an IDL static method.
@@ -3451,9 +3917,10 @@ class CGStaticMethod(CGAbstractStaticBindingMethod):
def generate_code(self):
nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
self.method)
+ safeContext = CGGeneric("let cx = SafeJSContext::from_ptr(cx);\n")
setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n")
call = CGMethodCall(["&global"], nativeName, True, self.descriptor, self.method)
- return CGList([setupArgs, call])
+ return CGList([safeContext, setupArgs, call])
class CGSpecializedGetter(CGAbstractExternMethod):
@@ -3465,8 +3932,8 @@ class CGSpecializedGetter(CGAbstractExternMethod):
self.attr = attr
name = 'get_' + descriptor.internalNameFor(attr.identifier.name)
args = [Argument('*mut JSContext', 'cx'),
- Argument('HandleObject', '_obj'),
- Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('RawHandleObject', '_obj'),
+ Argument('*mut libc::c_void', 'this'),
Argument('JSJitGetterCallArgs', 'args')]
CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args)
@@ -3476,7 +3943,8 @@ class CGSpecializedGetter(CGAbstractExternMethod):
return CGWrapper(CGGetterCall([], self.attr.type, nativeName,
self.descriptor, self.attr),
- pre="let this = &*this;\n")
+ pre="let cx = SafeJSContext::from_ptr(cx);\n"
+ + ("let this = &*(this as *const %s);\n" % self.descriptor.concreteType))
@staticmethod
def makeNativeName(descriptor, attr):
@@ -3505,10 +3973,11 @@ class CGStaticGetter(CGAbstractStaticBindingMethod):
def generate_code(self):
nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
self.attr)
+ safeContext = CGGeneric("let cx = SafeJSContext::from_ptr(cx);\n")
setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n")
call = CGGetterCall(["&global"], self.attr.type, nativeName, self.descriptor,
self.attr)
- return CGList([setupArgs, call])
+ return CGList([safeContext, setupArgs, call])
class CGSpecializedSetter(CGAbstractExternMethod):
@@ -3520,8 +3989,8 @@ class CGSpecializedSetter(CGAbstractExternMethod):
self.attr = attr
name = 'set_' + descriptor.internalNameFor(attr.identifier.name)
args = [Argument('*mut JSContext', 'cx'),
- Argument('HandleObject', 'obj'),
- Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('RawHandleObject', 'obj'),
+ Argument('*mut libc::c_void', 'this'),
Argument('JSJitSetterCallArgs', 'args')]
CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args)
@@ -3530,7 +3999,8 @@ class CGSpecializedSetter(CGAbstractExternMethod):
self.attr)
return CGWrapper(CGSetterCall([], self.attr.type, nativeName,
self.descriptor, self.attr),
- pre="let this = &*this;\n")
+ pre="let cx = SafeJSContext::from_ptr(cx);\n"
+ + ("let this = &*(this as *const %s);\n" % self.descriptor.concreteType))
@staticmethod
def makeNativeName(descriptor, attr):
@@ -3553,15 +4023,16 @@ class CGStaticSetter(CGAbstractStaticBindingMethod):
def generate_code(self):
nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
self.attr)
+ safeContext = CGGeneric("let cx = SafeJSContext::from_ptr(cx);\n")
checkForArg = CGGeneric(
"let args = CallArgs::from_vp(vp, argc);\n"
"if argc == 0 {\n"
- " throw_type_error(cx, \"Not enough arguments to %s setter.\");\n"
+ " throw_type_error(*cx, \"Not enough arguments to %s setter.\");\n"
" return false;\n"
"}" % self.attr.identifier.name)
call = CGSetterCall(["&global"], self.attr.type, nativeName, self.descriptor,
self.attr)
- return CGList([checkForArg, call])
+ return CGList([safeContext, checkForArg, call])
class CGSpecializedForwardingSetter(CGSpecializedSetter):
@@ -3578,16 +4049,17 @@ class CGSpecializedForwardingSetter(CGSpecializedSetter):
assert all(ord(c) < 128 for c in attrName)
assert all(ord(c) < 128 for c in forwardToAttrName)
return CGGeneric("""\
-rooted!(in(cx) let mut v = UndefinedValue());
-if !JS_GetProperty(cx, obj, %s as *const u8 as *const libc::c_char, v.handle_mut()) {
+let cx = SafeJSContext::from_ptr(cx);
+rooted!(in(*cx) let mut v = UndefinedValue());
+if !JS_GetProperty(*cx, HandleObject::from_raw(obj), %s as *const u8 as *const libc::c_char, v.handle_mut()) {
return false;
}
if !v.is_object() {
- throw_type_error(cx, "Value.%s is not an object.");
+ throw_type_error(*cx, "Value.%s is not an object.");
return false;
}
-rooted!(in(cx) let target_obj = v.to_object());
-JS_SetProperty(cx, target_obj.handle(), %s as *const u8 as *const libc::c_char, args.get(0))
+rooted!(in(*cx) let target_obj = v.to_object());
+JS_SetProperty(*cx, target_obj.handle(), %s as *const u8 as *const libc::c_char, HandleValue::from_raw(args.get(0)))
""" % (str_to_const_array(attrName), attrName, str_to_const_array(forwardToAttrName)))
@@ -3604,8 +4076,8 @@ class CGSpecializedReplaceableSetter(CGSpecializedSetter):
# JS_DefineProperty can only deal with ASCII.
assert all(ord(c) < 128 for c in name)
return CGGeneric("""\
-JS_DefineProperty(cx, obj, %s as *const u8 as *const libc::c_char,
- args.get(0), JSPROP_ENUMERATE, None, None)""" % name)
+JS_DefineProperty(cx, HandleObject::from_raw(obj), %s as *const u8 as *const libc::c_char,
+ HandleValue::from_raw(args.get(0)), JSPROP_ENUMERATE as u32)""" % name)
class CGMemberJITInfo(CGThing):
@@ -3634,11 +4106,16 @@ class CGMemberJITInfo(CGThing):
initializer = fill(
"""
JSJitInfo {
- call: ${opName} as *const os::raw::c_void,
- protoID: PrototypeList::ID::${name} as u16,
- depth: ${depth},
- _bitfield_1:
- JSJitInfo::new_bitfield_1(
+ __bindgen_anon_1: JSJitInfo__bindgen_ty_1 {
+ ${opKind}: Some(${opName})
+ },
+ __bindgen_anon_2: JSJitInfo__bindgen_ty_2 {
+ protoID: PrototypeList::ID::${name} as u16,
+ },
+ __bindgen_anon_3: JSJitInfo__bindgen_ty_3 { depth: ${depth} },
+ _bitfield_align_1: [],
+ _bitfield_1: __BindgenBitfieldUnit::new(
+ new_jsjitinfo_bitfield_1!(
JSJitInfo_OpType::${opType} as u8,
JSJitInfo_AliasSet::${aliasSet} as u8,
JSValueType::${returnType} as u8,
@@ -3648,17 +4125,19 @@ class CGMemberJITInfo(CGThing):
${isAlwaysInSlot},
${isLazilyCachedInSlot},
${isTypedMethod},
- ${slotIndex} as u16,
- )
+ ${slotIndex},
+ ).to_ne_bytes()
+ ),
}
""",
opName=opName,
name=self.descriptor.name,
depth=self.descriptor.interface.inheritanceDepth(),
opType=opType,
+ opKind=opType.lower(),
aliasSet=aliasSet,
- returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
- ""),
+ returnType=functools.reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
+ ""),
isInfallible=toStringBool(infallible),
isMovable=toStringBool(movable),
# FIXME(nox): https://github.com/servo/servo/issues/10991
@@ -3788,8 +4267,8 @@ class CGMemberJITInfo(CGThing):
# don't want them coalesced with each other or loop-hoisted, since
# their return value can change even if nothing is going on from our
# point of view.
- return (affects == "Nothing" and
- (dependsOn != "Everything" and dependsOn != "DeviceState"))
+ return (affects == "Nothing"
+ and (dependsOn != "Everything" and dependsOn != "DeviceState"))
def aliasSet(self):
"""Returns the alias set to store in the jitinfo. This may not be the
@@ -3821,7 +4300,9 @@ class CGMemberJITInfo(CGThing):
return "JSVAL_TYPE_UNDEFINED"
if t.isSequence():
return "JSVAL_TYPE_OBJECT"
- if t.isMozMap():
+ if t.isRecord():
+ return "JSVAL_TYPE_OBJECT"
+ if t.isPromise():
return "JSVAL_TYPE_OBJECT"
if t.isGeckoInterface():
return "JSVAL_TYPE_OBJECT"
@@ -3843,12 +4324,10 @@ class CGMemberJITInfo(CGThing):
if u.hasNullableType:
# Might be null or not
return "JSVAL_TYPE_UNKNOWN"
- return reduce(CGMemberJITInfo.getSingleReturnType,
- u.flatMemberTypes, "")
+ return functools.reduce(CGMemberJITInfo.getSingleReturnType,
+ u.flatMemberTypes, "")
if t.isDictionary():
return "JSVAL_TYPE_OBJECT"
- if t.isDate():
- return "JSVAL_TYPE_OBJECT"
if not t.isPrimitive():
raise TypeError("No idea what type " + str(t) + " is.")
tag = t.tag()
@@ -3879,10 +4358,10 @@ class CGMemberJITInfo(CGThing):
if type == existingType:
return existingType
- if ((type == "JSVAL_TYPE_DOUBLE" and
- existingType == "JSVAL_TYPE_INT32") or
- (existingType == "JSVAL_TYPE_DOUBLE" and
- type == "JSVAL_TYPE_INT32")):
+ if ((type == "JSVAL_TYPE_DOUBLE"
+ and existingType == "JSVAL_TYPE_INT32")
+ or (existingType == "JSVAL_TYPE_DOUBLE"
+ and type == "JSVAL_TYPE_INT32")):
# Promote INT32 to DOUBLE as needed
return "JSVAL_TYPE_DOUBLE"
# Different types
@@ -3913,13 +4392,11 @@ class CGMemberJITInfo(CGThing):
return "JSJitInfo_ArgType::Object as i32"
if t.isUnion():
u = t.unroll()
- type = "JSJitInfo::Null as i32" if u.hasNullableType else ""
- return reduce(CGMemberJITInfo.getSingleArgType,
- u.flatMemberTypes, type)
+ type = "JSJitInfo_ArgType::Null as i32" if u.hasNullableType else ""
+ return functools.reduce(CGMemberJITInfo.getSingleArgType,
+ u.flatMemberTypes, type)
if t.isDictionary():
return "JSJitInfo_ArgType::Object as i32"
- if t.isDate():
- return "JSJitInfo_ArgType::Object as i32"
if not t.isPrimitive():
raise TypeError("No idea what type " + str(t) + " is.")
tag = t.tag()
@@ -3963,7 +4440,7 @@ def getEnumValueName(value):
if re.match("[^\x20-\x7E]", value):
raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
if re.match("^[0-9]", value):
- raise SyntaxError('Enum value "' + value + '" starts with a digit')
+ value = '_' + value
value = re.sub(r'[^0-9A-Za-z_]', '_', value)
if re.match("^_[A-Z]|__", value):
raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
@@ -3981,35 +4458,66 @@ class CGEnum(CGThing):
ident = enum.identifier.name
decl = """\
#[repr(usize)]
-#[derive(JSTraceable, PartialEq, Copy, Clone, HeapSizeOf, Debug)]
+#[derive(Copy, Clone, Debug, JSTraceable, MallocSizeOf, PartialEq)]
pub enum %s {
%s
}
-""" % (ident, ",\n ".join(map(getEnumValueName, enum.values())))
-
- pairs = ",\n ".join(['("%s", super::%s::%s)' % (val, ident, getEnumValueName(val)) for val in enum.values()])
-
- inner = """\
-use dom::bindings::conversions::ToJSValConvertible;
-use js::jsapi::{JSContext, MutableHandleValue};
+""" % (ident, ",\n ".join(map(getEnumValueName, list(enum.values()))))
+
+ pairs = ",\n ".join(['("%s", super::%s::%s)' % (val, ident, getEnumValueName(val))
+ for val in list(enum.values())])
+
+ inner = string.Template("""\
+use crate::dom::bindings::conversions::ConversionResult;
+use crate::dom::bindings::conversions::FromJSValConvertible;
+use crate::dom::bindings::conversions::ToJSValConvertible;
+use crate::dom::bindings::utils::find_enum_value;
+use js::jsapi::JSContext;
+use js::rust::HandleValue;
+use js::rust::MutableHandleValue;
use js::jsval::JSVal;
-pub const pairs: &'static [(&'static str, super::%s)] = &[
- %s,
+pub const pairs: &'static [(&'static str, super::${ident})] = &[
+ ${pairs},
];
-impl super::%s {
+impl super::${ident} {
pub fn as_str(&self) -> &'static str {
pairs[*self as usize].0
}
}
-impl ToJSValConvertible for super::%s {
+impl Default for super::${ident} {
+ fn default() -> super::${ident} {
+ pairs[0].1
+ }
+}
+
+impl ToJSValConvertible for super::${ident} {
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
pairs[*self as usize].0.to_jsval(cx, rval);
}
}
- """ % (ident, pairs, ident, ident)
+
+impl FromJSValConvertible for super::${ident} {
+ type Config = ();
+ unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ())
+ -> Result<ConversionResult<super::${ident}>, ()> {
+ match find_enum_value(cx, value, pairs) {
+ Err(_) => Err(()),
+ Ok((None, search)) => {
+ Ok(ConversionResult::Failure(
+ format!("'{}' is not a valid enum value for enumeration '${ident}'.", search).into()
+ ))
+ }
+ Ok((Some(&value), _)) => Ok(ConversionResult::Success(value)),
+ }
+ }
+}
+ """).substitute({
+ 'ident': ident,
+ 'pairs': pairs
+ })
self.cgRoot = CGList([
CGGeneric(decl),
CGNamespace.build([ident + "Values"],
@@ -4044,7 +4552,17 @@ class CGConstant(CGThing):
def define(self):
name = self.constant.identifier.name
value = convertConstIDLValueToRust(self.constant.value)
- return "pub const %s: %s = %s;\n" % (name, builtinNames[self.constant.value.type.tag()], value)
+
+ tag = self.constant.value.type.tag()
+ const_type = builtinNames[self.constant.value.type.tag()]
+ # Finite<f32> or Finite<f64> cannot be used un a constant declaration.
+ # Remote the Finite type from restricted float and double tag declarations.
+ if tag == IDLType.Tags.float:
+ const_type = "f32"
+ elif tag == IDLType.Tags.double:
+ const_type = "f64"
+
+ return "pub const %s: %s = %s;\n" % (name, const_type, value)
def getUnionTypeTemplateVars(type, descriptorProvider):
@@ -4057,7 +4575,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
elif type.isDictionary():
name = type.name
typeName = name
- elif type.isSequence() or type.isMozMap():
+ elif type.isSequence() or type.isRecord():
name = type.name
inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider)
typeName = wrapInNativeContainerType(type, CGGeneric(inner["typeName"])).define()
@@ -4076,6 +4594,15 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
elif type.isObject():
name = type.name
typeName = "Heap<*mut JSObject>"
+ elif type.isReadableStream():
+ name = type.name
+ typeName = "DomRoot<ReadableStream>"
+ elif is_typed_array(type):
+ name = type.name
+ typeName = "typedarray::Heap" + name
+ elif type.isCallback():
+ name = type.name
+ typeName = name
else:
raise TypeError("Can't handle %s in unions yet" % type)
@@ -4107,15 +4634,29 @@ class CGUnionStruct(CGThing):
self.type = type
self.descriptorProvider = descriptorProvider
+ def membersNeedTracing(self):
+ for t in self.type.flatMemberTypes:
+ if type_needs_tracing(t):
+ return True
+ return False
+
def define(self):
- templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
- self.type.flatMemberTypes)
+ def getTypeWrapper(t):
+ if type_needs_tracing(t):
+ return "RootedTraceableBox"
+ if t.isCallback():
+ return "Rc"
+ return ""
+
+ templateVars = [(getUnionTypeTemplateVars(t, self.descriptorProvider),
+ getTypeWrapper(t)) for t in self.type.flatMemberTypes]
enumValues = [
- " %s(%s)," % (v["name"], v["typeName"]) for v in templateVars
+ " %s(%s)," % (v["name"], "%s<%s>" % (wrapper, v["typeName"]) if wrapper else v["typeName"])
+ for (v, wrapper) in templateVars
]
enumConversions = [
" %s::%s(ref inner) => inner.to_jsval(cx, rval),"
- % (self.type, v["name"]) for v in templateVars
+ % (self.type, v["name"]) for (v, _) in templateVars
]
return ("""\
#[derive(JSTraceable)]
@@ -4142,6 +4683,12 @@ class CGUnionConversionStruct(CGThing):
self.type = type
self.descriptorProvider = descriptorProvider
+ def membersNeedTracing(self):
+ for t in self.type.flatMemberTypes:
+ if type_needs_tracing(t):
+ return True
+ return False
+
def from_jsval(self):
memberTypes = self.type.flatMemberTypes
names = []
@@ -4155,13 +4702,13 @@ class CGUnionConversionStruct(CGThing):
def get_match(name):
return (
- "match %s::TryConvertTo%s(cx, value) {\n"
+ "match %s::TryConvertTo%s(SafeJSContext::from_ptr(cx), value) {\n"
" Err(_) => return Err(()),\n"
" Ok(Some(value)) => return Ok(ConversionResult::Success(%s::%s(value))),\n"
" Ok(None) => (),\n"
"}\n") % (self.type, name, self.type, name)
- interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ interfaceMemberTypes = [t for t in memberTypes if t.isNonCallbackInterface()]
if len(interfaceMemberTypes) > 0:
typeNames = [get_name(memberType) for memberType in interfaceMemberTypes]
interfaceObject = CGList(CGGeneric(get_match(typeName)) for typeName in typeNames)
@@ -4169,7 +4716,7 @@ class CGUnionConversionStruct(CGThing):
else:
interfaceObject = None
- arrayObjectMemberTypes = filter(lambda t: t.isSequence(), memberTypes)
+ arrayObjectMemberTypes = [t for t in memberTypes if t.isSequence()]
if len(arrayObjectMemberTypes) > 0:
assert len(arrayObjectMemberTypes) == 1
typeName = arrayObjectMemberTypes[0].name
@@ -4178,21 +4725,15 @@ class CGUnionConversionStruct(CGThing):
else:
arrayObject = None
- dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
- if len(dateObjectMemberTypes) > 0:
- assert len(dateObjectMemberTypes) == 1
- raise TypeError("Can't handle dates in unions.")
- else:
- dateObject = None
-
- callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
+ callbackMemberTypes = [t for t in memberTypes if t.isCallback() or t.isCallbackInterface()]
if len(callbackMemberTypes) > 0:
assert len(callbackMemberTypes) == 1
- raise TypeError("Can't handle callbacks in unions.")
+ typeName = callbackMemberTypes[0].name
+ callbackObject = CGGeneric(get_match(typeName))
else:
callbackObject = None
- dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
+ dictionaryMemberTypes = [t for t in memberTypes if t.isDictionary()]
if len(dictionaryMemberTypes) > 0:
assert len(dictionaryMemberTypes) == 1
typeName = dictionaryMemberTypes[0].name
@@ -4201,7 +4742,7 @@ class CGUnionConversionStruct(CGThing):
else:
dictionaryObject = None
- objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ objectMemberTypes = [t for t in memberTypes if t.isObject()]
if len(objectMemberTypes) > 0:
assert len(objectMemberTypes) == 1
typeName = objectMemberTypes[0].name
@@ -4210,7 +4751,7 @@ class CGUnionConversionStruct(CGThing):
else:
object = None
- mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
+ mozMapMemberTypes = [t for t in memberTypes if t.isRecord()]
if len(mozMapMemberTypes) > 0:
assert len(mozMapMemberTypes) == 1
typeName = mozMapMemberTypes[0].name
@@ -4219,21 +4760,29 @@ class CGUnionConversionStruct(CGThing):
else:
mozMapObject = None
- hasObjectTypes = interfaceObject or arrayObject or dateObject or object or mozMapObject
+ hasObjectTypes = object or interfaceObject or arrayObject or callbackObject or mozMapObject
if hasObjectTypes:
# "object" is not distinguishable from other types
- assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject or mozMapObject)
+ assert not object or not (interfaceObject or arrayObject or callbackObject or mozMapObject)
templateBody = CGList([], "\n")
+ if arrayObject or callbackObject:
+ # An object can be both an sequence object and a callback or
+ # dictionary, but we shouldn't have both in the union's members
+ # because they are not distinguishable.
+ assert not (arrayObject and callbackObject)
+ templateBody.append(arrayObject if arrayObject else callbackObject)
if interfaceObject:
+ assert not object
templateBody.append(interfaceObject)
- if arrayObject:
- templateBody.append(arrayObject)
+ elif object:
+ templateBody.append(object)
if mozMapObject:
templateBody.append(mozMapObject)
+
conversions.append(CGIfWrapper("value.get().is_object()", templateBody))
if dictionaryObject:
- assert not hasObjectTypes
+ assert not object
conversions.append(dictionaryObject)
stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
@@ -4248,9 +4797,9 @@ class CGUnionConversionStruct(CGThing):
typename = get_name(memberType)
return CGGeneric(get_match(typename))
other = []
- stringConversion = map(getStringOrPrimitiveConversion, stringTypes)
- numericConversion = map(getStringOrPrimitiveConversion, numericTypes)
- booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes)
+ stringConversion = list(map(getStringOrPrimitiveConversion, stringTypes))
+ numericConversion = list(map(getStringOrPrimitiveConversion, numericTypes))
+ booleanConversion = list(map(getStringOrPrimitiveConversion, booleanTypes))
if stringConversion:
if booleanConversion:
other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0]))
@@ -4266,8 +4815,8 @@ class CGUnionConversionStruct(CGThing):
other.append(booleanConversion[0])
conversions.append(CGList(other, "\n\n"))
conversions.append(CGGeneric(
- "throw_not_in_union(cx, \"%s\");\n"
- "Err(())" % ", ".join(names)))
+ "Ok(ConversionResult::Failure(\"argument could not be converted to any of: %s\".into()))" % ", ".join(names)
+ ))
method = CGWrapper(
CGIndenter(CGList(conversions, "\n\n")),
pre="unsafe fn from_jsval(cx: *mut JSContext,\n"
@@ -4285,13 +4834,17 @@ class CGUnionConversionStruct(CGThing):
def try_method(self, t):
templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider)
- returnType = "Result<Option<%s>, ()>" % templateVars["typeName"]
+ actualType = templateVars["typeName"]
+ if type_needs_tracing(t):
+ actualType = "RootedTraceableBox<%s>" % actualType
+ if t.isCallback():
+ actualType = "Rc<%s>" % actualType
+ returnType = "Result<Option<%s>, ()>" % actualType
jsConversion = templateVars["jsConversion"]
return CGWrapper(
CGIndenter(jsConversion, 4),
- # TryConvertToObject is unused, but not generating it while generating others is tricky.
- pre="#[allow(dead_code)] unsafe fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n"
+ pre="unsafe fn TryConvertTo%s(cx: SafeJSContext, value: HandleValue) -> %s {\n"
% (t.name, returnType),
post="\n}")
@@ -4338,7 +4891,7 @@ class ClassMethod(ClassItem):
def __init__(self, name, returnType, args, inline=False, static=False,
virtual=False, const=False, bodyInHeader=False,
templateArgs=None, visibility='public', body=None,
- breakAfterReturnDecl="\n",
+ breakAfterReturnDecl="\n", unsafe=False,
breakAfterSelf="\n", override=False):
"""
override indicates whether to flag the method as MOZ_OVERRIDE
@@ -4357,6 +4910,7 @@ class ClassMethod(ClassItem):
self.breakAfterReturnDecl = breakAfterReturnDecl
self.breakAfterSelf = breakAfterSelf
self.override = override
+ self.unsafe = unsafe
ClassItem.__init__(self, name, visibility)
def getDecorators(self, declaring):
@@ -4389,7 +4943,7 @@ class ClassMethod(ClassItem):
return string.Template(
"${decorators}%s"
- "${visibility}fn ${name}${templateClause}(${args})${returnType}${const}${override}${body}%s" %
+ "${visibility}${unsafe}fn ${name}${templateClause}(${args})${returnType}${const}${override}${body}%s" %
(self.breakAfterReturnDecl, self.breakAfterSelf)
).substitute({
'templateClause': templateClause,
@@ -4400,7 +4954,8 @@ class ClassMethod(ClassItem):
'override': ' MOZ_OVERRIDE' if self.override else '',
'args': args,
'body': body,
- 'visibility': self.visibility + ' ' if self.visibility != 'priv' else ''
+ 'visibility': self.visibility + ' ' if self.visibility != 'priv' else '',
+ 'unsafe': "unsafe " if self.unsafe else "",
})
def define(self, cgClass):
@@ -4470,7 +5025,7 @@ class ClassConstructor(ClassItem):
"});\n"
"// Note: callback cannot be moved after calling init.\n"
"match Rc::get_mut(&mut ret) {\n"
- " Some(ref mut callback) => unsafe { callback.parent.init(%s, %s) },\n"
+ " Some(ref mut callback) => callback.parent.init(%s, %s),\n"
" None => unreachable!(),\n"
"};\n"
"ret") % (cgClass.name, '\n'.join(initializers),
@@ -4485,7 +5040,7 @@ class ClassConstructor(ClassItem):
body = ' {\n' + body + '}'
return string.Template("""\
-pub fn ${decorators}new(${args}) -> Rc<${className}>${body}
+pub unsafe fn ${decorators}new(${args}) -> Rc<${className}>${body}
""").substitute({'decorators': self.getDecorators(True),
'className': cgClass.getNameString(),
'args': args,
@@ -4668,11 +5223,11 @@ class CGProxySpecialOperation(CGPerSignatureCall):
False, descriptor, operation,
len(arguments))
- if operation.isSetter() or operation.isCreator():
+ if operation.isSetter():
# arguments[0] is the index or name of the item that we're setting.
argument = arguments[1]
info = getJSToNativeConversionInfo(
- argument.type, descriptor, treatNullAs=argument.treatNullAs,
+ argument.type, descriptor,
exceptionCode="return false;")
template = info.template
declType = info.declType
@@ -4682,7 +5237,7 @@ class CGProxySpecialOperation(CGPerSignatureCall):
}
self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(
template, templateValues, declType, argument.identifier.name))
- self.cgRoot.prepend(CGGeneric("rooted!(in(cx) let value = desc.value);"))
+ self.cgRoot.prepend(CGGeneric("rooted!(in(*cx) let value = desc.value);"))
def getArguments(self):
args = [(a, process_arg(a.identifier.name, a)) for a in self.arguments]
@@ -4725,10 +5280,10 @@ class CGProxyNamedOperation(CGProxySpecialOperation):
def define(self):
# Our first argument is the id we're getting.
argName = self.arguments[0].identifier.name
- return ("let %s = string_jsid_to_string(cx, id);\n"
+ return ("let %s = jsid_to_string(*cx, Handle::from_raw(id)).expect(\"Not a string-convertible JSID?\");\n"
"let this = UnwrapProxy(proxy);\n"
- "let this = &*this;\n" % argName +
- CGProxySpecialOperation.define(self))
+ "let this = &*this;\n" % argName
+ + CGProxySpecialOperation.define(self))
class CGProxyNamedGetter(CGProxyNamedOperation):
@@ -4768,56 +5323,52 @@ class CGProxyNamedDeleter(CGProxyNamedOperation):
class CGProxyUnwrap(CGAbstractMethod):
def __init__(self, descriptor):
- args = [Argument('HandleObject', 'obj')]
+ args = [Argument('RawHandleObject', 'obj')]
CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy",
'*const ' + descriptor.concreteType, args,
alwaysInline=True, unsafe=True)
def definition_body(self):
return CGGeneric("""\
-/*if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
- obj = js::UnwrapObject(obj);
-}*/
-//MOZ_ASSERT(IsProxy(obj));
-let box_ = GetProxyPrivate(obj.get()).to_private() as *const %s;
+ let mut slot = UndefinedValue();
+ GetProxyReservedSlot(obj.get(), 0, &mut slot);
+ let box_ = slot.to_private() as *const %s;
return box_;""" % self.descriptor.concreteType)
class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
def __init__(self, descriptor):
- args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
- Argument('HandleId', 'id'),
- Argument('MutableHandle<PropertyDescriptor>', 'desc')]
+ args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'),
+ Argument('RawHandleId', 'id'),
+ Argument('RawMutableHandle<PropertyDescriptor>', 'mut desc')]
CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor",
"bool", args)
self.descriptor = descriptor
+ # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
def getBody(self):
indexedGetter = self.descriptor.operations['IndexedGetter']
- indexedSetter = self.descriptor.operations['IndexedSetter']
-
- get = ""
- if indexedGetter or indexedSetter:
- get = "let index = get_array_index_from_id(cx, id);\n"
+ get = "let cx = SafeJSContext::from_ptr(cx);\n"
if indexedGetter:
+ get += "let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
+
attrs = "JSPROP_ENUMERATE"
if self.descriptor.operations['IndexedSetter'] is None:
attrs += " | JSPROP_READONLY"
- # FIXME(#11868) Should assign to desc.value, desc.get() is a copy.
- fillDescriptor = ("desc.get().value = result_root.get();\n"
- "fill_property_descriptor(desc, proxy.get(), %s);\n"
+ fillDescriptor = ("desc.value = result_root.get();\n"
+ "fill_property_descriptor(MutableHandle::from_raw(desc), proxy.get(), (%s) as u32);\n"
"return true;" % attrs)
templateValues = {
'jsvalRef': 'result_root.handle_mut()',
'successCode': fillDescriptor,
- 'pre': 'rooted!(in(cx) let mut result_root = UndefinedValue());'
+ 'pre': 'rooted!(in(*cx) let mut result_root = UndefinedValue());'
}
- get += ("if let Some(index) = index {\n" +
- " let this = UnwrapProxy(proxy);\n" +
- " let this = &*this;\n" +
- CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
- "}\n")
+ get += ("if let Some(index) = index {\n"
+ + " let this = UnwrapProxy(proxy);\n"
+ + " let this = &*this;\n"
+ + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n"
+ + "}\n")
namedGetter = self.descriptor.operations['NamedGetter']
if namedGetter:
@@ -4830,49 +5381,54 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
attrs = " | ".join(attrs)
else:
attrs = "0"
- # FIXME(#11868) Should assign to desc.value, desc.get() is a copy.
- fillDescriptor = ("desc.get().value = result_root.get();\n"
- "fill_property_descriptor(desc, proxy.get(), %s);\n"
+ fillDescriptor = ("desc.value = result_root.get();\n"
+ "fill_property_descriptor(MutableHandle::from_raw(desc), proxy.get(), (%s) as u32);\n"
"return true;" % attrs)
templateValues = {
'jsvalRef': 'result_root.handle_mut()',
'successCode': fillDescriptor,
- 'pre': 'rooted!(in(cx) let mut result_root = UndefinedValue());'
+ 'pre': 'rooted!(in(*cx) let mut result_root = UndefinedValue());'
}
+
+ # See the similar-looking in CGDOMJSProxyHandler_get for the spec quote.
+ condition = "RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id)"
+ if indexedGetter:
+ condition = "index.is_none() && (%s)" % condition
# Once we start supporting OverrideBuiltins we need to make
# ResolveOwnProperty or EnumerateOwnProperties filter out named
# properties that shadow prototype properties.
namedGet = """
-if RUST_JSID_IS_STRING(id) {
+if %s {
let mut has_on_proto = false;
- if !has_property_on_prototype(cx, proxy, id, &mut has_on_proto) {
+ if !has_property_on_prototype(*cx, proxy_lt, id_lt, &mut has_on_proto) {
return false;
}
if !has_on_proto {
%s
}
}
-""" % CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues), 8).define()
+""" % (condition, CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues), 8).define())
else:
namedGet = ""
- # FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
return get + """\
-rooted!(in(cx) let mut expando = ptr::null_mut());
+rooted!(in(*cx) let mut expando = ptr::null_mut::<JSObject>());
get_expando_object(proxy, expando.handle_mut());
//if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+let proxy_lt = Handle::from_raw(proxy);
+let id_lt = Handle::from_raw(id);
if !expando.is_null() {
- if !JS_GetPropertyDescriptorById(cx, expando.handle(), id, desc) {
+ if !JS_GetPropertyDescriptorById(*cx, expando.handle().into(), id, desc) {
return false;
}
if !desc.obj.is_null() {
// Pretend the property lives on the wrapper.
- desc.get().obj = proxy.get();
+ desc.obj = proxy.get();
return true;
}
}
""" + namedGet + """\
-desc.get().obj = ptr::null_mut();
+desc.obj = ptr::null_mut();
return true;"""
def definition_body(self):
@@ -4881,28 +5437,28 @@ return true;"""
class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
def __init__(self, descriptor):
- args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
- Argument('HandleId', 'id'),
- Argument('Handle<PropertyDescriptor>', 'desc'),
+ args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'),
+ Argument('RawHandleId', 'id'),
+ Argument('RawHandle<PropertyDescriptor>', 'desc'),
Argument('*mut ObjectOpResult', 'opresult')]
CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "bool", args)
self.descriptor = descriptor
def getBody(self):
- set = ""
+ set = "let cx = SafeJSContext::from_ptr(cx);\n"
indexedSetter = self.descriptor.operations['IndexedSetter']
if indexedSetter:
- set += ("let index = get_array_index_from_id(cx, id);\n" +
- "if let Some(index) = index {\n" +
- " let this = UnwrapProxy(proxy);\n" +
- " let this = &*this;\n" +
- CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
- " return (*opresult).succeed();\n" +
- "}\n")
+ set += ("let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
+ + "if let Some(index) = index {\n"
+ + " let this = UnwrapProxy(proxy);\n"
+ + " let this = &*this;\n"
+ + CGIndenter(CGProxyIndexedSetter(self.descriptor)).define()
+ + " return (*opresult).succeed();\n"
+ + "}\n")
elif self.descriptor.operations['IndexedGetter']:
- set += ("if get_array_index_from_id(cx, id).is_some() {\n" +
- " return (*opresult).failNoIndexedSetter();\n" +
+ set += ("if get_array_index_from_id(*cx, Handle::from_raw(id)).is_some() {\n"
+ " return (*opresult).failNoIndexedSetter();\n"
"}\n")
namedSetter = self.descriptor.operations['NamedSetter']
@@ -4910,20 +5466,18 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
if self.descriptor.hasUnforgeableMembers:
raise TypeError("Can't handle a named setter on an interface that has "
"unforgeables. Figure out how that should work!")
- set += ("if RUST_JSID_IS_STRING(id) {\n" +
- CGIndenter(CGProxyNamedSetter(self.descriptor)).define() +
- " return (*opresult).succeed();\n" +
- "} else {\n" +
- " return false;\n" +
- "}\n")
- else:
- set += ("if RUST_JSID_IS_STRING(id) {\n" +
- CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
- " if result.is_some() {\n"
- " return (*opresult).failNoNamedSetter();\n"
- " }\n"
- "}\n")
- set += "return proxyhandler::define_property(%s);" % ", ".join(a.name for a in self.args)
+ set += ("if RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id) {\n"
+ + CGIndenter(CGProxyNamedSetter(self.descriptor)).define()
+ + " return (*opresult).succeed();\n"
+ + "}\n")
+ elif self.descriptor.operations['NamedGetter']:
+ set += ("if RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id) {\n"
+ + CGIndenter(CGProxyNamedGetter(self.descriptor)).define()
+ + " if result.is_some() {\n"
+ " return (*opresult).failNoNamedSetter();\n"
+ " }\n"
+ "}\n")
+ set += "return proxyhandler::define_property(*cx, %s);" % ", ".join(a.name for a in self.args[1:])
return set
def definition_body(self):
@@ -4932,20 +5486,20 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
class CGDOMJSProxyHandler_delete(CGAbstractExternMethod):
def __init__(self, descriptor):
- args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
- Argument('HandleId', 'id'),
+ args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'),
+ Argument('RawHandleId', 'id'),
Argument('*mut ObjectOpResult', 'res')]
CGAbstractExternMethod.__init__(self, descriptor, "delete", "bool", args)
self.descriptor = descriptor
def getBody(self):
- set = ""
+ set = "let cx = SafeJSContext::from_ptr(cx);\n"
if self.descriptor.operations['NamedDeleter']:
if self.descriptor.hasUnforgeableMembers:
raise TypeError("Can't handle a deleter on an interface that has "
"unforgeables. Figure out how that should work!")
set += CGProxyNamedDeleter(self.descriptor).define()
- set += "return proxyhandler::delete(%s);" % ", ".join(a.name for a in self.args)
+ set += "return proxyhandler::delete(*cx, %s);" % ", ".join(a.name for a in self.args[1:])
return set
def definition_body(self):
@@ -4955,14 +5509,15 @@ class CGDOMJSProxyHandler_delete(CGAbstractExternMethod):
class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
def __init__(self, descriptor):
args = [Argument('*mut JSContext', 'cx'),
- Argument('HandleObject', 'proxy'),
- Argument('*mut AutoIdVector', 'props')]
+ Argument('RawHandleObject', 'proxy'),
+ Argument('RawMutableHandleIdVector', 'props')]
CGAbstractExternMethod.__init__(self, descriptor, "own_property_keys", "bool", args)
self.descriptor = descriptor
def getBody(self):
body = dedent(
"""
+ let cx = SafeJSContext::from_ptr(cx);
let unwrapped_proxy = UnwrapProxy(proxy);
""")
@@ -4970,8 +5525,9 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
body += dedent(
"""
for i in 0..(*unwrapped_proxy).Length() {
- rooted!(in(cx) let rooted_jsid = int_to_jsid(i as i32));
- AppendToAutoIdVector(props, rooted_jsid.handle().get());
+ rooted!(in(*cx) let mut rooted_jsid: jsid);
+ int_to_jsid(i as i32, rooted_jsid.handle_mut());
+ AppendToIdVector(props, rooted_jsid.handle());
}
""")
@@ -4980,20 +5536,21 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
"""
for name in (*unwrapped_proxy).SupportedPropertyNames() {
let cstring = CString::new(name).unwrap();
- let jsstring = JS_AtomizeAndPinString(cx, cstring.as_ptr());
- rooted!(in(cx) let rooted = jsstring);
- let jsid = INTERNED_STRING_TO_JSID(cx, rooted.handle().get());
- rooted!(in(cx) let rooted_jsid = jsid);
- AppendToAutoIdVector(props, rooted_jsid.handle().get());
+ let jsstring = JS_AtomizeAndPinString(*cx, cstring.as_ptr());
+ rooted!(in(*cx) let rooted = jsstring);
+ rooted!(in(*cx) let mut rooted_jsid: jsid);
+ RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), rooted_jsid.handle_mut());
+ AppendToIdVector(props, rooted_jsid.handle());
}
""")
body += dedent(
"""
- rooted!(in(cx) let mut expando = ptr::null_mut());
+ rooted!(in(*cx) let mut expando = ptr::null_mut::<JSObject>());
get_expando_object(proxy, expando.handle_mut());
- if !expando.is_null() {
- GetPropertyKeys(cx, expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
+ if !expando.is_null() &&
+ !GetPropertyKeys(*cx, expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props) {
+ return false;
}
return true;
@@ -5007,11 +5564,11 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod):
def __init__(self, descriptor):
- assert (descriptor.operations["IndexedGetter"] and
- descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"))
+ assert (descriptor.operations["IndexedGetter"]
+ and descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"))
args = [Argument('*mut JSContext', 'cx'),
- Argument('HandleObject', 'proxy'),
- Argument('*mut AutoIdVector', 'props')]
+ Argument('RawHandleObject', 'proxy'),
+ Argument('RawMutableHandleIdVector', 'props')]
CGAbstractExternMethod.__init__(self, descriptor,
"getOwnEnumerablePropertyKeys", "bool", args)
self.descriptor = descriptor
@@ -5019,6 +5576,7 @@ class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod):
def getBody(self):
body = dedent(
"""
+ let cx = SafeJSContext::from_ptr(cx);
let unwrapped_proxy = UnwrapProxy(proxy);
""")
@@ -5026,17 +5584,19 @@ class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod):
body += dedent(
"""
for i in 0..(*unwrapped_proxy).Length() {
- rooted!(in(cx) let rooted_jsid = int_to_jsid(i as i32));
- AppendToAutoIdVector(props, rooted_jsid.handle().get());
+ rooted!(in(*cx) let mut rooted_jsid: jsid);
+ int_to_jsid(i as i32, rooted_jsid.handle_mut());
+ AppendToIdVector(props, rooted_jsid.handle());
}
""")
body += dedent(
"""
- rooted!(in(cx) let mut expando = ptr::null_mut());
+ rooted!(in(*cx) let mut expando = ptr::null_mut::<JSObject>());
get_expando_object(proxy, expando.handle_mut());
- if !expando.is_null() {
- GetPropertyKeys(cx, expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
+ if !expando.is_null() &&
+ !GetPropertyKeys(*cx, expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props) {
+ return false;
}
return true;
@@ -5050,31 +5610,33 @@ class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod):
class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
def __init__(self, descriptor):
- args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
- Argument('HandleId', 'id'), Argument('*mut bool', 'bp')]
+ args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'),
+ Argument('RawHandleId', 'id'), Argument('*mut bool', 'bp')]
CGAbstractExternMethod.__init__(self, descriptor, "hasOwn", "bool", args)
self.descriptor = descriptor
def getBody(self):
indexedGetter = self.descriptor.operations['IndexedGetter']
+ indexed = "let cx = SafeJSContext::from_ptr(cx);\n"
if indexedGetter:
- indexed = ("let index = get_array_index_from_id(cx, id);\n" +
- "if let Some(index) = index {\n" +
- " let this = UnwrapProxy(proxy);\n" +
- " let this = &*this;\n" +
- CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
- " *bp = result.is_some();\n" +
- " return true;\n" +
- "}\n\n")
- else:
- indexed = ""
+ indexed += ("let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
+ + "if let Some(index) = index {\n"
+ + " let this = UnwrapProxy(proxy);\n"
+ + " let this = &*this;\n"
+ + CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n"
+ + " *bp = result.is_some();\n"
+ + " return true;\n"
+ + "}\n\n")
namedGetter = self.descriptor.operations['NamedGetter']
+ condition = "RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id)"
+ if indexedGetter:
+ condition = "index.is_none() && (%s)" % condition
if namedGetter:
named = """\
-if RUST_JSID_IS_STRING(id) {
+if %s {
let mut has_on_proto = false;
- if !has_property_on_prototype(cx, proxy, id, &mut has_on_proto) {
+ if !has_property_on_prototype(*cx, proxy_lt, id_lt, &mut has_on_proto) {
return false;
}
if !has_on_proto {
@@ -5084,15 +5646,17 @@ if RUST_JSID_IS_STRING(id) {
}
}
-""" % CGIndenter(CGProxyNamedGetter(self.descriptor), 8).define()
+""" % (condition, CGIndenter(CGProxyNamedGetter(self.descriptor), 8).define())
else:
named = ""
return indexed + """\
-rooted!(in(cx) let mut expando = ptr::null_mut());
+rooted!(in(*cx) let mut expando = ptr::null_mut::<JSObject>());
+let proxy_lt = Handle::from_raw(proxy);
+let id_lt = Handle::from_raw(id);
get_expando_object(proxy, expando.handle_mut());
if !expando.is_null() {
- let ok = JS_HasPropertyById(cx, expando.handle(), id, bp);
+ let ok = JS_HasPropertyById(*cx, expando.handle().into(), id, bp);
if !ok || *bp {
return ok;
}
@@ -5107,39 +5671,40 @@ return true;"""
class CGDOMJSProxyHandler_get(CGAbstractExternMethod):
def __init__(self, descriptor):
- args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
- Argument('HandleValue', 'receiver'), Argument('HandleId', 'id'),
- Argument('MutableHandleValue', 'vp')]
+ args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'),
+ Argument('RawHandleValue', 'receiver'), Argument('RawHandleId', 'id'),
+ Argument('RawMutableHandleValue', 'vp')]
CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args)
self.descriptor = descriptor
+ # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
def getBody(self):
getFromExpando = """\
-rooted!(in(cx) let mut expando = ptr::null_mut());
+rooted!(in(*cx) let mut expando = ptr::null_mut::<JSObject>());
get_expando_object(proxy, expando.handle_mut());
if !expando.is_null() {
let mut hasProp = false;
- if !JS_HasPropertyById(cx, expando.handle(), id, &mut hasProp) {
+ if !JS_HasPropertyById(*cx, expando.handle().into(), id, &mut hasProp) {
return false;
}
if hasProp {
- return JS_ForwardGetPropertyTo(cx, expando.handle(), id, receiver, vp);
+ return JS_ForwardGetPropertyTo(*cx, expando.handle().into(), id, receiver, vp);
}
}"""
templateValues = {
- 'jsvalRef': 'vp',
+ 'jsvalRef': 'vp_lt',
'successCode': 'return true;',
}
indexedGetter = self.descriptor.operations['IndexedGetter']
if indexedGetter:
- getIndexedOrExpando = ("let index = get_array_index_from_id(cx, id);\n" +
- "if let Some(index) = index {\n" +
- " let this = UnwrapProxy(proxy);\n" +
- " let this = &*this;\n" +
- CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define())
+ getIndexedOrExpando = ("let index = get_array_index_from_id(*cx, id_lt);\n"
+ + "if let Some(index) = index {\n"
+ + " let this = UnwrapProxy(proxy);\n"
+ + " let this = &*this;\n"
+ + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define())
getIndexedOrExpando += """\
// Even if we don't have this index, we don't forward the
// get on to our expando object.
@@ -5152,19 +5717,31 @@ if !expando.is_null() {
namedGetter = self.descriptor.operations['NamedGetter']
if namedGetter:
- getNamed = ("if RUST_JSID_IS_STRING(id) {\n" +
- CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
- "}\n")
+ condition = "RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id)"
+ # From step 1:
+ # If O supports indexed properties and P is an array index, then:
+ #
+ # 3. Set ignoreNamedProps to true.
+ if indexedGetter:
+ condition = "index.is_none() && (%s)" % condition
+ getNamed = ("if %s {\n"
+ + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define()
+ + "}\n") % condition
else:
getNamed = ""
return """\
//MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
//"Should not have a XrayWrapper here");
+let cx = SafeJSContext::from_ptr(cx);
+let proxy_lt = Handle::from_raw(proxy);
+let vp_lt = MutableHandle::from_raw(vp);
+let id_lt = Handle::from_raw(id);
+let receiver_lt = Handle::from_raw(receiver);
%s
let mut found = false;
-if !get_property_on_prototype(cx, proxy, receiver, id, &mut found, vp) {
+if !get_property_on_prototype(*cx, proxy_lt, receiver_lt, id_lt, &mut found, vp_lt) {
return false;
}
@@ -5181,7 +5758,7 @@ return true;""" % (getIndexedOrExpando, getNamed)
class CGDOMJSProxyHandler_className(CGAbstractExternMethod):
def __init__(self, descriptor):
- args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_proxy')]
+ args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', '_proxy')]
CGAbstractExternMethod.__init__(self, descriptor, "className", "*const i8", args, doesNotPanic=True)
self.descriptor = descriptor
@@ -5203,7 +5780,7 @@ class CGAbstractClassHook(CGAbstractExternMethod):
def definition_body_prologue(self):
return CGGeneric("""
-let this = native_from_object::<%s>(obj).unwrap();
+let this = native_from_object_static::<%s>(obj).unwrap();
""" % self.descriptor.concreteType)
def definition_body(self):
@@ -5224,7 +5801,9 @@ finalize_global(obj);
"""
elif descriptor.weakReferenceable:
release += """\
-let weak_box_ptr = JS_GetReservedSlot(obj, DOM_WEAK_SLOT).to_private() as *mut WeakBox<%s>;
+let mut slot = UndefinedValue();
+JS_GetReservedSlot(obj, DOM_WEAK_SLOT, &mut slot);
+let weak_box_ptr = slot.to_private() as *mut WeakBox<%s>;
if !weak_box_ptr.is_null() {
let count = {
let weak_box = &*weak_box_ptr;
@@ -5285,16 +5864,30 @@ class CGClassConstructHook(CGAbstractExternMethod):
self.exposureSet = descriptor.interface.exposureSet
def definition_body(self):
- preamble = """let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object());\n"""
+ preamble = """let cx = SafeJSContext::from_ptr(cx);
+let global = GlobalScope::from_object(JS_CALLEE(*cx, vp).to_object());
+"""
if len(self.exposureSet) == 1:
- preamble += "let global = Root::downcast::<dom::types::%s>(global).unwrap();\n" % list(self.exposureSet)[0]
+ preamble += """\
+let global = DomRoot::downcast::<dom::types::%s>(global).unwrap();
+""" % list(self.exposureSet)[0]
preamble += """let args = CallArgs::from_vp(vp, argc);\n"""
preamble = CGGeneric(preamble)
- name = self.constructor.identifier.name
- nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
- callGenerator = CGMethodCall(["&global"], nativeName, True,
- self.descriptor, self.constructor)
- return CGList([preamble, callGenerator])
+ if self.constructor.isHTMLConstructor():
+ signatures = self.constructor.signatures()
+ assert len(signatures) == 1
+ constructorCall = CGGeneric("""dom::bindings::htmlconstructor::call_html_constructor::<dom::types::%s>(
+ cx,
+ &args,
+ &*global,
+ GetProtoObject,
+ )""" % self.descriptor.name)
+ else:
+ name = self.constructor.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
+ constructorCall = CGMethodCall(["&global"], nativeName, True,
+ self.descriptor, self.constructor)
+ return CGList([preamble, constructorCall])
class CGClassFinalizeHook(CGAbstractClassHook):
@@ -5323,29 +5916,37 @@ class CGInterfaceTrait(CGThing):
def __init__(self, descriptor):
CGThing.__init__(self)
- def attribute_arguments(needCx, argument=None):
+ def attribute_arguments(needCx, argument=None, inRealm=False):
if needCx:
- yield "cx", "*mut JSContext"
+ yield "cx", "SafeJSContext"
if argument:
yield "value", argument_type(descriptor, argument)
+ if inRealm:
+ yield "_comp", "InRealm"
+
def members():
for m in descriptor.interface.members:
- if (m.isMethod() and not m.isStatic() and
- not m.isMaplikeOrSetlikeOrIterableMethod() and
- (not m.isIdentifierLess() or m.isStringifier())):
+ if (m.isMethod() and not m.isStatic()
+ and not m.isMaplikeOrSetlikeOrIterableMethod()
+ and (not m.isIdentifierLess() or (m.isStringifier() and not m.underlyingAttr))
+ and not m.isDefaultToJSON()):
name = CGSpecializedMethod.makeNativeName(descriptor, m)
infallible = 'infallible' in descriptor.getExtendedAttributes(m)
for idx, (rettype, arguments) in enumerate(m.signatures()):
- arguments = method_arguments(descriptor, rettype, arguments)
+ arguments = method_arguments(descriptor, rettype, arguments,
+ inRealm=name in descriptor.inRealmMethods)
rettype = return_type(descriptor, rettype, infallible)
yield name + ('_' * idx), arguments, rettype
elif m.isAttr() and not m.isStatic():
name = CGSpecializedGetter.makeNativeName(descriptor, m)
infallible = 'infallible' in descriptor.getExtendedAttributes(m, getter=True)
yield (name,
- attribute_arguments(typeNeedsCx(m.type, True)),
+ attribute_arguments(
+ typeNeedsCx(m.type, True),
+ inRealm=name in descriptor.inRealmMethods
+ ),
return_type(descriptor, m.type, infallible))
if not m.readonly:
@@ -5355,10 +5956,16 @@ class CGInterfaceTrait(CGThing):
rettype = "()"
else:
rettype = "ErrorResult"
- yield name, attribute_arguments(typeNeedsCx(m.type, False), m.type), rettype
+ yield (name,
+ attribute_arguments(
+ typeNeedsCx(m.type, False),
+ m.type,
+ inRealm=name in descriptor.inRealmMethods
+ ),
+ rettype)
if descriptor.proxy:
- for name, operation in descriptor.operations.iteritems():
+ for name, operation in descriptor.operations.items():
if not operation or operation.isStringifier():
continue
@@ -5369,7 +5976,8 @@ class CGInterfaceTrait(CGThing):
if operation.isGetter():
if not rettype.nullable():
rettype = IDLNullableType(rettype.location, rettype)
- arguments = method_arguments(descriptor, rettype, arguments)
+ arguments = method_arguments(descriptor, rettype, arguments,
+ inRealm=name in descriptor.inRealmMethods)
# If this interface 'supports named properties', then we
# should be able to access 'supported property names'
@@ -5379,17 +5987,22 @@ class CGInterfaceTrait(CGThing):
if operation.isNamed():
yield "SupportedPropertyNames", [], "Vec<DOMString>"
else:
- arguments = method_arguments(descriptor, rettype, arguments)
+ arguments = method_arguments(descriptor, rettype, arguments,
+ inRealm=name in descriptor.inRealmMethods)
rettype = return_type(descriptor, rettype, infallible)
yield name, arguments, rettype
def fmt(arguments):
- return "".join(", %s: %s" % argument for argument in arguments)
+ keywords = {"async"}
+ return "".join(
+ ", %s: %s" % (name if name not in keywords else "r#" + name, type_)
+ for name, type_ in arguments
+ )
def contains_unsafe_arg(arguments):
if not arguments or len(arguments) == 0:
return False
- return reduce((lambda x, y: x or y[1] == '*mut JSContext'), arguments, False)
+ return functools.reduce((lambda x, y: x or y[1] == '*mut JSContext'), arguments, False)
methods = []
for name, arguments, rettype in members():
@@ -5421,16 +6034,17 @@ class CGWeakReferenceableTrait(CGThing):
return self.code
-def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None):
+def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None, typedefs=None):
if not callbacks:
callbacks = []
if not dictionaries:
dictionaries = []
if not enums:
enums = []
+ if not typedefs:
+ typedefs = []
- return CGImports(cgthings, descriptors, callbacks, dictionaries, enums, [
- 'core::nonzero::NonZero',
+ return CGImports(cgthings, descriptors, callbacks, dictionaries, enums, typedefs, [
'js',
'js::JSCLASS_GLOBAL_SLOT_COUNT',
'js::JSCLASS_IS_DOMJSCLASS',
@@ -5438,22 +6052,27 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::JSCLASS_RESERVED_SLOTS_MASK',
'js::JS_CALLEE',
'js::error::throw_type_error',
- 'js::jsapi::AutoIdVector',
- 'js::jsapi::Call',
+ 'js::error::throw_internal_error',
+ 'js::rust::wrappers::Call',
+ 'js::jsapi::__BindgenBitfieldUnit',
'js::jsapi::CallArgs',
'js::jsapi::CurrentGlobalOrNull',
- 'js::jsapi::FreeOp',
- 'js::jsapi::GetPropertyKeys',
+ 'js::rust::wrappers::GetPropertyKeys',
'js::jsapi::GetWellKnownSymbol',
- 'js::jsapi::Handle',
- 'js::jsapi::HandleId',
- 'js::jsapi::HandleObject',
- 'js::jsapi::HandleValue',
+ 'js::rust::Handle',
+ 'js::jsapi::Handle as RawHandle',
+ 'js::rust::HandleId',
+ 'js::jsapi::HandleId as RawHandleId',
+ 'js::rust::HandleObject',
+ 'js::jsapi::HandleObject as RawHandleObject',
+ 'js::rust::HandleValue',
+ 'js::jsapi::HandleValue as RawHandleValue',
'js::jsapi::HandleValueArray',
'js::jsapi::Heap',
- 'js::jsapi::INTERNED_STRING_TO_JSID',
+ 'js::rust::wrappers::RUST_INTERNED_STRING_TO_JSID',
'js::jsapi::IsCallable',
- 'js::jsapi::JSAutoCompartment',
+ 'js::jsapi::JSAutoRealm',
+ 'js::jsapi::JSCLASS_FOREGROUND_FINALIZE',
'js::jsapi::JSCLASS_RESERVED_SLOTS_SHIFT',
'js::jsapi::JSClass',
'js::jsapi::JSContext',
@@ -5464,6 +6083,9 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::jsapi::JSITER_SYMBOLS',
'js::jsapi::JSJitGetterCallArgs',
'js::jsapi::JSJitInfo',
+ 'js::jsapi::JSJitInfo__bindgen_ty_1',
+ 'js::jsapi::JSJitInfo__bindgen_ty_2',
+ 'js::jsapi::JSJitInfo__bindgen_ty_3',
'js::jsapi::JSJitInfo_AliasSet',
'js::jsapi::JSJitInfo_ArgType',
'js::jsapi::JSJitInfo_OpType',
@@ -5475,45 +6097,58 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::jsapi::JSPROP_ENUMERATE',
'js::jsapi::JSPROP_PERMANENT',
'js::jsapi::JSPROP_READONLY',
- 'js::jsapi::JSPROP_SHARED',
'js::jsapi::JSPropertySpec',
+ 'js::jsapi::JSPropertySpec_Accessor',
+ 'js::jsapi::JSPropertySpec_AccessorsOrValue',
+ 'js::jsapi::JSPropertySpec_AccessorsOrValue_Accessors',
+ 'js::jsapi::JSPropertySpec_Name',
+ 'js::jsapi::JSPropertySpec_ValueWrapper',
+ 'js::jsapi::JSPropertySpec_ValueWrapper_Type',
+ 'js::jsapi::JSPropertySpec_ValueWrapper__bindgen_ty_1',
'js::jsapi::JSString',
'js::jsapi::JSTracer',
'js::jsapi::JSType',
'js::jsapi::JSTypedMethodJitInfo',
'js::jsapi::JSValueType',
'js::jsapi::JS_AtomizeAndPinString',
- 'js::jsapi::JS_CallFunctionValue',
- 'js::jsapi::JS_CopyPropertiesFrom',
- 'js::jsapi::JS_DefineProperty',
- 'js::jsapi::JS_DefinePropertyById2',
+ 'js::rust::wrappers::JS_CallFunctionValue',
+ 'js::rust::wrappers::JS_CopyOwnPropertiesAndPrivateFields',
+ 'js::rust::wrappers::JS_DefineProperty',
+ 'js::rust::wrappers::JS_DefinePropertyById2',
'js::jsapi::JS_ForwardGetPropertyTo',
- 'js::jsapi::JS_GetErrorPrototype',
- 'js::jsapi::JS_GetFunctionPrototype',
- 'js::jsapi::JS_GetGlobalForObject',
- 'js::jsapi::JS_GetIteratorPrototype',
- 'js::jsapi::JS_GetObjectPrototype',
- 'js::jsapi::JS_GetProperty',
+ 'js::jsapi::GetRealmErrorPrototype',
+ 'js::jsapi::GetRealmFunctionPrototype',
+ 'js::jsapi::GetRealmIteratorPrototype',
+ 'js::jsapi::GetRealmObjectPrototype',
+ 'js::rust::wrappers::JS_GetProperty',
'js::jsapi::JS_GetPropertyById',
'js::jsapi::JS_GetPropertyDescriptorById',
- 'js::jsapi::JS_GetReservedSlot',
+ 'js::glue::JS_GetReservedSlot',
'js::jsapi::JS_HasProperty',
'js::jsapi::JS_HasPropertyById',
- 'js::jsapi::JS_InitializePropertiesFromCompatibleNativeObject',
+ 'js::rust::wrappers::JS_InitializePropertiesFromCompatibleNativeObject',
+ 'js::jsapi::JS_NewPlainObject',
'js::jsapi::JS_NewObject',
- 'js::jsapi::JS_NewObjectWithGivenProto',
- 'js::jsapi::JS_NewObjectWithoutMetadata',
- 'js::jsapi::JS_ObjectIsDate',
- 'js::jsapi::JS_SetImmutablePrototype',
- 'js::jsapi::JS_SetProperty',
+ 'js::rust::RootedGuard',
+ 'js::rust::wrappers::JS_NewObjectWithGivenProto',
+ 'js::rust::wrappers::JS_NewObjectWithoutMetadata',
+ 'js::rust::wrappers::ObjectIsDate',
+ 'js::rust::wrappers::JS_SetImmutablePrototype',
+ 'js::rust::wrappers::JS_SetProperty',
+ 'js::rust::wrappers::JS_SetPrototype',
'js::jsapi::JS_SetReservedSlot',
- 'js::jsapi::JS_SplicePrototype',
- 'js::jsapi::JS_WrapValue',
- 'js::jsapi::MutableHandle',
- 'js::jsapi::MutableHandleObject',
- 'js::jsapi::MutableHandleValue',
+ 'js::rust::wrappers::JS_WrapValue',
+ 'js::rust::wrappers::JS_WrapObject',
+ 'js::rust::MutableHandle',
+ 'js::jsapi::MutableHandle as RawMutableHandle',
+ 'js::rust::MutableHandleObject',
+ 'js::jsapi::MutableHandleObject as RawMutableHandleObject',
+ 'js::rust::MutableHandleValue',
+ 'js::jsapi::MutableHandleValue as RawMutableHandleValue',
+ 'js::jsapi::MutableHandleIdVector as RawMutableHandleIdVector',
'js::jsapi::ObjectOpResult',
'js::jsapi::PropertyDescriptor',
+ 'js::jsapi::Rooted',
'js::jsapi::RootedId',
'js::jsapi::RootedObject',
'js::jsapi::RootedString',
@@ -5525,126 +6160,143 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::jsval::ObjectOrNullValue',
'js::jsval::PrivateValue',
'js::jsval::UndefinedValue',
- 'js::glue::AppendToAutoIdVector',
+ 'js::jsapi::UndefinedHandleValue',
+ 'js::rust::wrappers::AppendToIdVector',
'js::glue::CallJitGetterOp',
'js::glue::CallJitMethodOp',
'js::glue::CallJitSetterOp',
'js::glue::CreateProxyHandler',
- 'js::glue::GetProxyPrivate',
- 'js::glue::NewProxyObject',
+ 'js::glue::GetProxyReservedSlot',
+ 'js::glue::SetProxyReservedSlot',
+ 'js::rust::wrappers::NewProxyObject',
'js::glue::ProxyTraps',
+ 'js::glue::RUST_JSID_IS_INT',
'js::glue::RUST_JSID_IS_STRING',
- 'js::glue::RUST_SYMBOL_TO_JSID',
- 'js::glue::int_to_jsid',
+ 'js::rust::wrappers::RUST_SYMBOL_TO_JSID',
+ 'js::rust::wrappers::int_to_jsid',
+ 'js::glue::UnwrapObjectDynamic',
'js::panic::maybe_resume_unwind',
'js::panic::wrap_panic',
'js::rust::GCMethods',
+ 'js::rust::CustomAutoRooterGuard',
'js::rust::define_methods',
'js::rust::define_properties',
'js::rust::get_object_class',
- 'dom',
- 'dom::bindings',
- 'dom::bindings::codegen::InterfaceObjectMap',
- 'dom::bindings::constant::ConstantSpec',
- 'dom::bindings::constant::ConstantVal',
- 'dom::bindings::interface::ConstructorClassHook',
- 'dom::bindings::interface::InterfaceConstructorBehavior',
- 'dom::bindings::interface::NonCallbackInterfaceObjectClass',
- 'dom::bindings::interface::create_callback_interface_object',
- 'dom::bindings::interface::create_global_object',
- 'dom::bindings::interface::create_interface_prototype_object',
- 'dom::bindings::interface::create_named_constructors',
- 'dom::bindings::interface::create_noncallback_interface_object',
- 'dom::bindings::interface::define_guarded_constants',
- 'dom::bindings::interface::define_guarded_methods',
- 'dom::bindings::interface::define_guarded_properties',
- 'dom::bindings::interface::is_exposed_in',
- 'dom::bindings::iterable::Iterable',
- 'dom::bindings::iterable::IteratorType',
- 'dom::bindings::js::JS',
- 'dom::bindings::js::Root',
- 'dom::bindings::js::RootedReference',
- 'dom::bindings::namespace::NamespaceObjectClass',
- 'dom::bindings::namespace::create_namespace_object',
- 'dom::bindings::reflector::MutDomObject',
- 'dom::bindings::reflector::DomObject',
- 'dom::bindings::utils::AsVoidPtr',
- 'dom::bindings::utils::DOMClass',
- 'dom::bindings::utils::DOMJSClass',
- 'dom::bindings::utils::DOM_PROTO_UNFORGEABLE_HOLDER_SLOT',
- 'dom::bindings::utils::JSCLASS_DOM_GLOBAL',
- 'dom::bindings::utils::ProtoOrIfaceArray',
- 'dom::bindings::utils::enumerate_global',
- 'dom::bindings::utils::finalize_global',
- 'dom::bindings::utils::find_enum_value',
- 'dom::bindings::utils::generic_getter',
- 'dom::bindings::utils::generic_lenient_getter',
- 'dom::bindings::utils::generic_lenient_setter',
- 'dom::bindings::utils::generic_method',
- 'dom::bindings::utils::generic_setter',
- 'dom::bindings::utils::get_array_index_from_id',
- 'dom::bindings::utils::get_dictionary_property',
- 'dom::bindings::utils::get_property_on_prototype',
- 'dom::bindings::utils::get_proto_or_iface_array',
- 'dom::bindings::utils::has_property_on_prototype',
- 'dom::bindings::utils::is_platform_object',
- 'dom::bindings::utils::resolve_global',
- 'dom::bindings::utils::set_dictionary_property',
- 'dom::bindings::utils::trace_global',
- 'dom::bindings::trace::JSTraceable',
- 'dom::bindings::trace::RootedTraceable',
- 'dom::bindings::trace::RootedTraceableBox',
- 'dom::bindings::callback::CallSetup',
- 'dom::bindings::callback::CallbackContainer',
- 'dom::bindings::callback::CallbackInterface',
- 'dom::bindings::callback::CallbackFunction',
- 'dom::bindings::callback::CallbackObject',
- 'dom::bindings::callback::ExceptionHandling',
- 'dom::bindings::callback::wrap_call_this_object',
- 'dom::bindings::conversions::ConversionBehavior',
- 'dom::bindings::conversions::ConversionResult',
- 'dom::bindings::conversions::DOM_OBJECT_SLOT',
- 'dom::bindings::conversions::FromJSValConvertible',
- 'dom::bindings::conversions::IDLInterface',
- 'dom::bindings::conversions::StringificationBehavior',
- 'dom::bindings::conversions::ToJSValConvertible',
- 'dom::bindings::conversions::is_array_like',
- 'dom::bindings::conversions::native_from_handlevalue',
- 'dom::bindings::conversions::native_from_object',
- 'dom::bindings::conversions::private_from_object',
- 'dom::bindings::conversions::root_from_handleobject',
- 'dom::bindings::conversions::root_from_handlevalue',
- 'dom::bindings::conversions::root_from_object',
- 'dom::bindings::conversions::string_jsid_to_string',
- 'dom::bindings::codegen::PrototypeList',
- 'dom::bindings::codegen::RegisterBindings',
- 'dom::bindings::codegen::UnionTypes',
- 'dom::bindings::error::Error',
- 'dom::bindings::error::ErrorResult',
- 'dom::bindings::error::Fallible',
- 'dom::bindings::error::Error::JSFailed',
- 'dom::bindings::error::throw_dom_exception',
- 'dom::bindings::guard::Condition',
- 'dom::bindings::guard::Guard',
- 'dom::bindings::inheritance::Castable',
- 'dom::bindings::proxyhandler',
- 'dom::bindings::proxyhandler::ensure_expando_object',
- 'dom::bindings::proxyhandler::fill_property_descriptor',
- 'dom::bindings::proxyhandler::get_expando_object',
- 'dom::bindings::proxyhandler::get_property_descriptor',
- 'dom::bindings::mozmap::MozMap',
- 'dom::bindings::num::Finite',
- 'dom::bindings::str::ByteString',
- 'dom::bindings::str::DOMString',
- 'dom::bindings::str::USVString',
- 'dom::bindings::weakref::DOM_WEAK_SLOT',
- 'dom::bindings::weakref::WeakBox',
- 'dom::bindings::weakref::WeakReferenceable',
- 'dom::browsingcontext::BrowsingContext',
- 'dom::globalscope::GlobalScope',
- 'mem::heap_size_of_raw_self_and_children',
+ 'js::typedarray',
+ 'crate::dom',
+ 'crate::dom::bindings',
+ 'crate::dom::bindings::codegen::InterfaceObjectMap',
+ 'crate::dom::bindings::constant::ConstantSpec',
+ 'crate::dom::bindings::constant::ConstantVal',
+ 'crate::dom::bindings::interface::ConstructorClassHook',
+ 'crate::dom::bindings::interface::InterfaceConstructorBehavior',
+ 'crate::dom::bindings::interface::NonCallbackInterfaceObjectClass',
+ 'crate::dom::bindings::interface::create_global_object',
+ 'crate::dom::bindings::interface::create_callback_interface_object',
+ 'crate::dom::bindings::interface::create_interface_prototype_object',
+ 'crate::dom::bindings::interface::create_named_constructors',
+ 'crate::dom::bindings::interface::create_noncallback_interface_object',
+ 'crate::dom::bindings::interface::define_guarded_constants',
+ 'crate::dom::bindings::interface::define_guarded_methods',
+ 'crate::dom::bindings::interface::define_guarded_properties',
+ 'crate::dom::bindings::interface::is_exposed_in',
+ 'crate::dom::bindings::htmlconstructor::pop_current_element_queue',
+ 'crate::dom::bindings::htmlconstructor::push_new_element_queue',
+ 'crate::dom::bindings::iterable::Iterable',
+ 'crate::dom::bindings::iterable::IteratorType',
+ 'crate::dom::bindings::namespace::NamespaceObjectClass',
+ 'crate::dom::bindings::namespace::create_namespace_object',
+ 'crate::dom::bindings::reflector::MutDomObject',
+ 'crate::dom::bindings::reflector::DomObject',
+ 'crate::dom::bindings::reflector::DomObjectWrap',
+ 'crate::dom::bindings::reflector::DomObjectIteratorWrap',
+ 'crate::dom::bindings::root::Dom',
+ 'crate::dom::bindings::root::DomRoot',
+ 'crate::dom::bindings::root::DomSlice',
+ 'crate::dom::bindings::root::MaybeUnreflectedDom',
+ 'crate::dom::bindings::root::OptionalHeapSetter',
+ 'crate::dom::bindings::root::Root',
+ 'crate::dom::bindings::utils::AsVoidPtr',
+ 'crate::dom::bindings::utils::DOMClass',
+ 'crate::dom::bindings::utils::DOMJSClass',
+ 'crate::dom::bindings::utils::DOM_PROTO_UNFORGEABLE_HOLDER_SLOT',
+ 'crate::dom::bindings::utils::JSCLASS_DOM_GLOBAL',
+ 'crate::dom::bindings::utils::ProtoOrIfaceArray',
+ 'crate::dom::bindings::utils::enumerate_global',
+ 'crate::dom::bindings::utils::finalize_global',
+ 'crate::dom::bindings::utils::generic_getter',
+ 'crate::dom::bindings::utils::generic_lenient_getter',
+ 'crate::dom::bindings::utils::generic_lenient_setter',
+ 'crate::dom::bindings::utils::generic_method',
+ 'crate::dom::bindings::utils::generic_setter',
+ 'crate::dom::bindings::utils::get_array_index_from_id',
+ 'crate::dom::bindings::utils::get_dictionary_property',
+ 'crate::dom::bindings::utils::get_property_on_prototype',
+ 'crate::dom::bindings::utils::get_proto_or_iface_array',
+ 'crate::dom::bindings::utils::has_property_on_prototype',
+ 'crate::dom::bindings::utils::is_platform_object_dynamic',
+ 'crate::dom::bindings::utils::is_platform_object_static',
+ 'crate::dom::bindings::utils::resolve_global',
+ 'crate::dom::bindings::utils::set_dictionary_property',
+ 'crate::dom::bindings::utils::trace_global',
+ 'crate::dom::bindings::trace::JSTraceable',
+ 'crate::dom::bindings::trace::RootedTraceableBox',
+ 'crate::dom::bindings::callback::CallSetup',
+ 'crate::dom::bindings::callback::CallbackContainer',
+ 'crate::dom::bindings::callback::CallbackInterface',
+ 'crate::dom::bindings::callback::CallbackFunction',
+ 'crate::dom::bindings::callback::CallbackObject',
+ 'crate::dom::bindings::callback::ExceptionHandling',
+ 'crate::dom::bindings::callback::wrap_call_this_object',
+ 'crate::dom::bindings::conversions::ConversionBehavior',
+ 'crate::dom::bindings::conversions::ConversionResult',
+ 'crate::dom::bindings::conversions::DOM_OBJECT_SLOT',
+ 'crate::dom::bindings::conversions::FromJSValConvertible',
+ 'crate::dom::bindings::conversions::IDLInterface',
+ 'crate::dom::bindings::conversions::StringificationBehavior',
+ 'crate::dom::bindings::conversions::ToJSValConvertible',
+ 'crate::dom::bindings::conversions::is_array_like',
+ 'crate::dom::bindings::conversions::native_from_handlevalue',
+ 'crate::dom::bindings::conversions::native_from_object',
+ 'crate::dom::bindings::conversions::native_from_object_static',
+ 'crate::dom::bindings::conversions::private_from_object',
+ 'crate::dom::bindings::conversions::root_from_handleobject',
+ 'crate::dom::bindings::conversions::root_from_handlevalue',
+ 'crate::dom::bindings::conversions::root_from_object',
+ 'crate::dom::bindings::conversions::jsid_to_string',
+ 'crate::dom::bindings::codegen::PrototypeList',
+ 'crate::dom::bindings::codegen::RegisterBindings',
+ 'crate::dom::bindings::codegen::UnionTypes',
+ 'crate::dom::bindings::error::Error',
+ 'crate::dom::bindings::error::ErrorResult',
+ 'crate::dom::bindings::error::Fallible',
+ 'crate::dom::bindings::error::Error::JSFailed',
+ 'crate::dom::bindings::error::throw_dom_exception',
+ 'crate::dom::bindings::guard::Condition',
+ 'crate::dom::bindings::guard::Guard',
+ 'crate::dom::bindings::inheritance::Castable',
+ 'crate::dom::bindings::proxyhandler',
+ 'crate::dom::bindings::proxyhandler::ensure_expando_object',
+ 'crate::dom::bindings::proxyhandler::fill_property_descriptor',
+ 'crate::dom::bindings::proxyhandler::get_expando_object',
+ 'crate::dom::bindings::record::Record',
+ 'std::ptr::NonNull',
+ 'crate::dom::bindings::num::Finite',
+ 'crate::dom::bindings::str::ByteString',
+ 'crate::dom::bindings::str::DOMString',
+ 'crate::dom::bindings::str::USVString',
+ 'crate::dom::bindings::weakref::DOM_WEAK_SLOT',
+ 'crate::dom::bindings::weakref::WeakBox',
+ 'crate::dom::bindings::weakref::WeakReferenceable',
+ 'crate::dom::windowproxy::WindowProxy',
+ 'crate::dom::globalscope::GlobalScope',
+ 'crate::mem::malloc_size_of_including_raw_self',
+ 'crate::realms::InRealm',
+ 'crate::realms::AlreadyInRealm',
+ 'crate::script_runtime::JSContext as SafeJSContext',
'libc',
- 'servo_config::prefs::PREFS',
+ 'servo_config::pref',
+ 'servo_config::prefs',
'std::borrow::ToOwned',
'std::cmp',
'std::mem',
@@ -5657,6 +6309,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'std::rc::Rc',
'std::default::Default',
'std::ffi::CString',
+ 'std::ops::Deref',
], config)
@@ -5677,24 +6330,23 @@ class CGDescriptor(CGThing):
cgThings = []
+ defaultToJSONMethod = None
unscopableNames = []
for m in descriptor.interface.members:
- if (m.isMethod() and
- (not m.isIdentifierLess() or m == descriptor.operations["Stringifier"])):
+ if (m.isMethod()
+ and (not m.isIdentifierLess() or m == descriptor.operations["Stringifier"])):
if m.getExtendedAttribute("Unscopable"):
assert not m.isStatic()
unscopableNames.append(m.identifier.name)
- if m.isStatic():
+ if m.isDefaultToJSON():
+ defaultToJSONMethod = m
+ elif m.isStatic():
assert descriptor.interface.hasInterfaceObject()
cgThings.append(CGStaticMethod(descriptor, m))
elif not descriptor.interface.isCallback():
cgThings.append(CGSpecializedMethod(descriptor, m))
cgThings.append(CGMemberJITInfo(descriptor, m))
elif m.isAttr():
- if m.stringifier:
- raise TypeError("Stringifier attributes not supported yet. "
- "See https://github.com/servo/servo/issues/7590\n"
- "%s" % m.location)
if m.getExtendedAttribute("Unscopable"):
assert not m.isStatic()
unscopableNames.append(m.identifier.name)
@@ -5718,6 +6370,10 @@ class CGDescriptor(CGThing):
if (not m.isStatic() and not descriptor.interface.isCallback()):
cgThings.append(CGMemberJITInfo(descriptor, m))
+ if defaultToJSONMethod:
+ cgThings.append(CGDefaultToJSONMethod(descriptor, defaultToJSONMethod))
+ cgThings.append(CGMemberJITInfo(descriptor, defaultToJSONMethod))
+
if descriptor.concrete:
cgThings.append(CGClassFinalizeHook(descriptor))
cgThings.append(CGClassTraceHook(descriptor))
@@ -5735,6 +6391,9 @@ class CGDescriptor(CGThing):
properties = PropertyArrays(descriptor)
+ if defaultToJSONMethod:
+ cgThings.append(CGCollectJSONAttributesMethod(descriptor, defaultToJSONMethod))
+
if descriptor.concrete:
if descriptor.proxy:
# cgThings.append(CGProxyIsProxy(descriptor))
@@ -5762,12 +6421,18 @@ class CGDescriptor(CGThing):
pass
else:
cgThings.append(CGDOMJSClass(descriptor))
+ if not descriptor.interface.isIteratorInterface():
+ cgThings.append(CGAssertInheritance(descriptor))
pass
if descriptor.isGlobal():
cgThings.append(CGWrapGlobalMethod(descriptor, properties))
else:
cgThings.append(CGWrapMethod(descriptor))
+ if descriptor.interface.isIteratorInterface():
+ cgThings.append(CGDomObjectIteratorWrap(descriptor))
+ else:
+ cgThings.append(CGDomObjectWrap(descriptor))
reexports.append('Wrap')
haveUnscopables = False
@@ -5790,6 +6455,15 @@ class CGDescriptor(CGThing):
if descriptor.weakReferenceable:
cgThings.append(CGWeakReferenceableTrait(descriptor))
+ legacyWindowAliases = descriptor.interface.legacyWindowAliases
+ haveLegacyWindowAliases = len(legacyWindowAliases) != 0
+ if haveLegacyWindowAliases:
+ cgThings.append(
+ CGList([CGGeneric("const legacy_window_aliases: &'static [&'static [u8]] = &["),
+ CGIndenter(CGList([CGGeneric(str_to_const_array(name)) for
+ name in legacyWindowAliases], ",\n")),
+ CGGeneric("];\n")], "\n"))
+
cgThings.append(CGGeneric(str(properties)))
if not descriptor.interface.getExtendedAttribute("Inline"):
@@ -5811,7 +6485,8 @@ class CGDescriptor(CGThing):
cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
reexports.append('DefineDOMInterface')
cgThings.append(CGConstructorEnabled(descriptor))
- cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables))
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables,
+ haveLegacyWindowAliases))
cgThings = generate_imports(config, CGList(cgThings, '\n'), [descriptor])
cgThings = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
@@ -5819,7 +6494,7 @@ class CGDescriptor(CGThing):
post='\n')
if reexports:
- reexports = ', '.join(map(lambda name: reexportedName(name), reexports))
+ reexports = ', '.join([reexportedName(name) for name in reexports])
cgThings = CGList([CGGeneric('pub use self::%s::{%s};' % (toBindingNamespace(descriptor.name), reexports)),
cgThings], '\n')
@@ -5879,7 +6554,7 @@ class CGDictionary(CGThing):
descriptorProvider,
isMember="Dictionary",
defaultValue=member.defaultValue,
- exceptionCode="return Err(());"))
+ exceptionCode="return Err(());\n"))
for member in dictionary.members]
def define(self):
@@ -5890,43 +6565,58 @@ class CGDictionary(CGThing):
def struct(self):
d = self.dictionary
if d.parent:
- inheritance = " pub parent: %s::%s,\n" % (self.makeModuleName(d.parent),
- self.makeClassName(d.parent))
+ typeName = "%s::%s" % (self.makeModuleName(d.parent),
+ self.makeClassName(d.parent))
+ if type_needs_tracing(d.parent):
+ typeName = "RootedTraceableBox<%s>" % typeName
+ inheritance = " pub parent: %s,\n" % typeName
else:
inheritance = ""
memberDecls = [" pub %s: %s," %
(self.makeMemberName(m[0].identifier.name), self.getMemberType(m))
for m in self.memberInfo]
+ derive = ["JSTraceable"]
+ mustRoot = ""
+ if self.membersNeedTracing():
+ mustRoot = "#[unrooted_must_root_lint::must_root]\n"
+ if not self.hasRequiredFields(self.dictionary):
+ derive += ["Default"]
+
return (string.Template(
- "#[derive(JSTraceable)]\n"
- "pub struct ${selfName} {\n" +
- "${inheritance}" +
- "\n".join(memberDecls) + "\n" +
- "}").substitute({"selfName": self.makeClassName(d),
- "inheritance": inheritance}))
+ "#[derive(${derive})]\n"
+ + "${mustRoot}"
+ + "pub struct ${selfName} {\n"
+ + "${inheritance}"
+ + "\n".join(memberDecls) + "\n"
+ + "}").substitute({"selfName": self.makeClassName(d),
+ "inheritance": inheritance,
+ "mustRoot": mustRoot,
+ "derive": ', '.join(derive)}))
def impl(self):
d = self.dictionary
if d.parent:
- initParent = ("parent: {\n"
- " match try!(%s::%s::new(cx, val)) {\n"
+ initParent = ("{\n"
+ " match %s::%s::new(cx, val)? {\n"
" ConversionResult::Success(v) => v,\n"
" ConversionResult::Failure(error) => {\n"
- " throw_type_error(cx, &error);\n"
+ " throw_type_error(*cx, &error);\n"
" return Err(());\n"
" }\n"
" }\n"
- "},\n" % (self.makeModuleName(d.parent),
- self.makeClassName(d.parent)))
+ "}" % (self.makeModuleName(d.parent),
+ self.makeClassName(d.parent)))
else:
initParent = ""
- def memberInit(memberInfo):
+ def memberInit(memberInfo, isInitial):
member, _ = memberInfo
name = self.makeMemberName(member.identifier.name)
conversion = self.getMemberConversion(memberInfo, member.type)
- return CGGeneric("%s: %s,\n" % (name, conversion.define()))
+ if isInitial:
+ return CGGeneric("%s: %s,\n" % (name, conversion.define()))
+ return CGGeneric("dictionary.%s = %s;\n" % (name, conversion.define()))
def varInsert(varName, dictionaryName):
insertion = ("rooted!(in(cx) let mut %s_js = UndefinedValue());\n"
@@ -5946,59 +6636,86 @@ class CGDictionary(CGThing):
(name, name, varInsert(name, member.identifier.name).define()))
return CGGeneric("%s\n" % insertion.define())
- memberInits = CGList([memberInit(m) for m in self.memberInfo])
- memberInserts = CGList([memberInsert(m) for m in self.memberInfo])
+ memberInserts = [memberInsert(m) for m in self.memberInfo]
+
+ if d.parent:
+ memberInserts = [CGGeneric("self.parent.to_jsobject(cx, obj);\n")] + memberInserts
+
+ selfName = self.makeClassName(d)
+ if self.membersNeedTracing():
+ actualType = "RootedTraceableBox<%s>" % selfName
+ preInitial = "let dictionary = RootedTraceableBox::new(%s {\n" % selfName
+ postInitial = "});\n"
+ else:
+ actualType = selfName
+ preInitial = "let dictionary = %s {\n" % selfName
+ postInitial = "};\n"
+ initParent = ("parent: %s,\n" % initParent) if initParent else ""
+ memberInits = CGList([memberInit(m, True) for m in self.memberInfo])
return string.Template(
"impl ${selfName} {\n"
- " pub unsafe fn empty(cx: *mut JSContext) -> ${selfName} {\n"
- " match ${selfName}::new(cx, HandleValue::null()) {\n"
- " Ok(ConversionResult::Success(v)) => v,\n"
- " _ => unreachable!(),\n"
- " }\n"
- " }\n"
- " pub unsafe fn new(cx: *mut JSContext, val: HandleValue) \n"
- " -> Result<ConversionResult<${selfName}>, ()> {\n"
- " let object = if val.get().is_null_or_undefined() {\n"
- " ptr::null_mut()\n"
- " } else if val.get().is_object() {\n"
- " val.get().to_object()\n"
- " } else {\n"
- " throw_type_error(cx, \"Value not an object.\");\n"
- " return Err(());\n"
- " };\n"
- " rooted!(in(cx) let object = object);\n"
- " Ok(ConversionResult::Success(${selfName} {\n"
+ "${empty}\n"
+ " pub fn new(cx: SafeJSContext, val: HandleValue) \n"
+ " -> Result<ConversionResult<${actualType}>, ()> {\n"
+ " unsafe {\n"
+ " let object = if val.get().is_null_or_undefined() {\n"
+ " ptr::null_mut()\n"
+ " } else if val.get().is_object() {\n"
+ " val.get().to_object()\n"
+ " } else {\n"
+ " return Ok(ConversionResult::Failure(\"Value is not an object.\".into()));\n"
+ " };\n"
+ " rooted!(in(*cx) let object = object);\n"
+ "${preInitial}"
"${initParent}"
"${initMembers}"
- " }))\n"
+ "${postInitial}"
+ " Ok(ConversionResult::Success(dictionary))\n"
+ " }\n"
" }\n"
"}\n"
"\n"
- "impl FromJSValConvertible for ${selfName} {\n"
+ "impl FromJSValConvertible for ${actualType} {\n"
" type Config = ();\n"
" unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ())\n"
- " -> Result<ConversionResult<${selfName}>, ()> {\n"
- " ${selfName}::new(cx, value)\n"
+ " -> Result<ConversionResult<${actualType}>, ()> {\n"
+ " ${selfName}::new(SafeJSContext::from_ptr(cx), value)\n"
" }\n"
"}\n"
"\n"
- "impl ToJSValConvertible for ${selfName} {\n"
- " unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n"
- " rooted!(in(cx) let obj = JS_NewObject(cx, ptr::null()));\n"
+ "impl ${selfName} {\n"
+ " pub(crate) unsafe fn to_jsobject(&self, cx: *mut JSContext, mut obj: MutableHandleObject) {\n"
"${insertMembers}"
+ " }\n"
+ "}\n"
+ "\n"
+ "impl ToJSValConvertible for ${selfName} {\n"
+ " unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {\n"
+ " rooted!(in(cx) let mut obj = JS_NewObject(cx, ptr::null()));\n"
+ " self.to_jsobject(cx, obj.handle_mut());\n"
" rval.set(ObjectOrNullValue(obj.get()))\n"
" }\n"
"}\n").substitute({
- "selfName": self.makeClassName(d),
- "initParent": CGIndenter(CGGeneric(initParent), indentLevel=12).define(),
- "initMembers": CGIndenter(memberInits, indentLevel=12).define(),
- "insertMembers": CGIndenter(memberInserts, indentLevel=8).define(),
+ "selfName": selfName,
+ "actualType": actualType,
+ "empty": CGIndenter(CGGeneric(self.makeEmpty()), indentLevel=4).define(),
+ "initParent": CGIndenter(CGGeneric(initParent), indentLevel=16).define(),
+ "initMembers": CGIndenter(memberInits, indentLevel=16).define(),
+ "insertMembers": CGIndenter(CGList(memberInserts), indentLevel=8).define(),
+ "preInitial": CGIndenter(CGGeneric(preInitial), indentLevel=8).define(),
+ "postInitial": CGIndenter(CGGeneric(postInitial), indentLevel=8).define(),
})
+ def membersNeedTracing(self):
+ return type_needs_tracing(self.dictionary)
+
@staticmethod
def makeDictionaryName(dictionary):
- return dictionary.identifier.name
+ if isinstance(dictionary, IDLWrapperType):
+ return CGDictionary.makeDictionaryName(dictionary.inner)
+ else:
+ return dictionary.identifier.name
def makeClassName(self, dictionary):
return self.makeDictionaryName(dictionary)
@@ -6027,7 +6744,7 @@ class CGDictionary(CGThing):
assert (member.defaultValue is None) == (default is None)
if not member.optional:
assert default is None
- default = ("throw_type_error(cx, \"Missing required member \\\"%s\\\".\");\n"
+ default = ("throw_type_error(*cx, \"Missing required member \\\"%s\\\".\");\n"
"return Err(());") % member.identifier.name
elif not default:
default = "None"
@@ -6035,19 +6752,61 @@ class CGDictionary(CGThing):
conversion = (
"{\n"
- " rooted!(in(cx) let mut rval = UndefinedValue());\n"
- " match try!(get_dictionary_property(cx, object.handle(), \"%s\", rval.handle_mut())) {\n"
- " true => {\n"
+ " rooted!(in(*cx) let mut rval = UndefinedValue());\n"
+ " if get_dictionary_property(*cx, object.handle(), \"%s\", rval.handle_mut())?"
+ " && !rval.is_undefined() {\n"
"%s\n"
- " },\n"
- " false => {\n"
+ " } else {\n"
"%s\n"
- " },\n"
" }\n"
"}") % (member.identifier.name, indent(conversion), indent(default))
return CGGeneric(conversion)
+ def makeEmpty(self):
+ if self.hasRequiredFields(self.dictionary):
+ return ""
+ parentTemplate = "parent: %s::%s::empty(),\n"
+ fieldTemplate = "%s: %s,\n"
+ functionTemplate = (
+ "pub fn empty() -> Self {\n"
+ " Self {\n"
+ "%s"
+ " }\n"
+ "}"
+ )
+ if self.membersNeedTracing():
+ parentTemplate = "dictionary.parent = %s::%s::empty();\n"
+ fieldTemplate = "dictionary.%s = %s;\n"
+ functionTemplate = (
+ "pub fn empty() -> RootedTraceableBox<Self> {\n"
+ " let mut dictionary = RootedTraceableBox::new(Self::default());\n"
+ "%s"
+ " dictionary\n"
+ "}"
+ )
+ s = ""
+ if self.dictionary.parent:
+ s += parentTemplate % (self.makeModuleName(self.dictionary.parent),
+ self.makeClassName(self.dictionary.parent))
+ for member, info in self.memberInfo:
+ if not member.optional:
+ return ""
+ default = info.default
+ if not default:
+ default = "None"
+ s += fieldTemplate % (self.makeMemberName(member.identifier.name), default)
+ return functionTemplate % CGIndenter(CGGeneric(s), 12).define()
+
+ def hasRequiredFields(self, dictionary):
+ if dictionary.parent:
+ if self.hasRequiredFields(dictionary.parent):
+ return True
+ for member in dictionary.members:
+ if not member.optional:
+ return True
+ return False
+
@staticmethod
def makeMemberName(name):
# Can't use Rust keywords as member names.
@@ -6075,7 +6834,10 @@ class CGRegisterProxyHandlersMethod(CGAbstractMethod):
def definition_body(self):
return CGList([
- CGGeneric("PROXY_HANDLERS[Proxies::%s as usize] = Bindings::%s::DefineProxyHandler();"
+ CGGeneric("proxy_handlers::%s.store(\n"
+ " Bindings::%s::DefineProxyHandler() as *mut _,\n"
+ " std::sync::atomic::Ordering::Release,\n"
+ ");"
% (desc.name, '::'.join([desc.name + 'Binding'] * 2)))
for desc in self.descriptors
], "\n")
@@ -6084,10 +6846,18 @@ class CGRegisterProxyHandlersMethod(CGAbstractMethod):
class CGRegisterProxyHandlers(CGThing):
def __init__(self, config):
descriptors = config.getDescriptors(proxy=True)
- length = len(descriptors)
self.root = CGList([
- CGGeneric("pub static mut PROXY_HANDLERS: [*const libc::c_void; %d] = [0 as *const libc::c_void; %d];"
- % (length, length)),
+ CGGeneric(
+ "#[allow(non_upper_case_globals)]\n"
+ + "pub mod proxy_handlers {\n"
+ + "".join(
+ " pub static %s: std::sync::atomic::AtomicPtr<libc::c_void> =\n"
+ " std::sync::atomic::AtomicPtr::new(std::ptr::null_mut());\n"
+ % desc.name
+ for desc in descriptors
+ )
+ + "}\n"
+ ),
CGRegisterProxyHandlersMethod(descriptors),
], "\n")
@@ -6097,7 +6867,7 @@ class CGRegisterProxyHandlers(CGThing):
class CGBindingRoot(CGThing):
"""
- Root codegen class for binding generation. Instantiate the class, and call
+ DomRoot codegen class for binding generation. Instantiate the class, and call
declare or define to generate header or cpp code (respectively).
"""
def __init__(self, config, prefix, webIDLFile):
@@ -6128,7 +6898,7 @@ class CGBindingRoot(CGThing):
# Do codegen for all the enums.
cgthings = [CGEnum(e) for e in enums]
- # Do codegen for all the typdefs
+ # Do codegen for all the typedefs
for t in typedefs:
typeName = getRetvalDeclarationForType(t.innerType, config.getDescriptorProvider())
substs = {
@@ -6166,7 +6936,7 @@ class CGBindingRoot(CGThing):
# Add imports
curr = generate_imports(config, curr, callbackDescriptors, mainCallbacks,
- dictionaries, enums)
+ dictionaries, enums, typedefs)
# Add the auto-generated comment.
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
@@ -6202,6 +6972,9 @@ def type_needs_tracing(t):
if t.isUnion():
return any(type_needs_tracing(member) for member in t.flatMemberTypes)
+ if is_typed_array(t):
+ return True
+
return False
if t.isDictionary():
@@ -6222,9 +6995,33 @@ def type_needs_tracing(t):
assert False, (t, type(t))
+def is_typed_array(t):
+ assert isinstance(t, IDLObject), (t, type(t))
+
+ return t.isTypedArray() or t.isArrayBuffer() or t.isArrayBufferView()
+
+
+def type_needs_auto_root(t):
+ """
+ Certain IDL types, such as `sequence<any>` or `sequence<object>` need to be
+ traced and wrapped via (Custom)AutoRooter
+ """
+ assert isinstance(t, IDLObject), (t, type(t))
+
+ if t.isType():
+ if t.isSequence() and (t.inner.isAny() or t.inner.isObject()):
+ return True
+ # SpiderMonkey interfaces, we currently don't support any other except typed arrays
+ if is_typed_array(t):
+ return True
+
+ return False
+
+
def argument_type(descriptorProvider, ty, optional=False, defaultValue=None, variadic=False):
info = getJSToNativeConversionInfo(
- ty, descriptorProvider, isArgument=True)
+ ty, descriptorProvider, isArgument=True,
+ isAutoRooted=type_needs_auto_root(ty))
declType = info.declType
if variadic:
@@ -6238,12 +7035,15 @@ def argument_type(descriptorProvider, ty, optional=False, defaultValue=None, var
if ty.isDictionary() and not type_needs_tracing(ty):
declType = CGWrapper(declType, pre="&")
+ if type_needs_auto_root(ty):
+ declType = CGTemplatedType("CustomAutoRooterGuard", declType)
+
return declType.define()
-def method_arguments(descriptorProvider, returnType, arguments, passJSBits=True, trailing=None):
+def method_arguments(descriptorProvider, returnType, arguments, passJSBits=True, trailing=None, inRealm=False):
if needCx(returnType, arguments, passJSBits):
- yield "cx", "*mut JSContext"
+ yield "cx", "SafeJSContext"
for argument in arguments:
ty = argument_type(descriptorProvider, argument.type, argument.optional,
@@ -6253,6 +7053,9 @@ def method_arguments(descriptorProvider, returnType, arguments, passJSBits=True,
if trailing:
yield trailing
+ if inRealm:
+ yield "_comp", "InRealm"
+
def return_type(descriptorProvider, rettype, infallible):
result = getRetvalDeclarationForType(rettype, descriptorProvider)
@@ -6263,7 +7066,8 @@ def return_type(descriptorProvider, rettype, infallible):
class CGNativeMember(ClassMethod):
def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
- breakAfter=True, passJSBitsAsNeeded=True, visibility="public"):
+ breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
+ unsafe=False):
"""
If passJSBitsAsNeeded is false, we don't automatically pass in a
JSContext* or a JSObject* based on the return and argument types.
@@ -6279,9 +7083,10 @@ class CGNativeMember(ClassMethod):
static=member.isStatic(),
# Mark our getters, which are attrs that
# have a non-void return type, as const.
- const=(not member.isStatic() and member.isAttr() and
- not signature[0].isVoid()),
+ const=(not member.isStatic() and member.isAttr()
+ and not signature[0].isVoid()),
breakAfterSelf=breakAfterSelf,
+ unsafe=unsafe,
visibility=visibility)
def getReturnType(self, type):
@@ -6297,8 +7102,7 @@ class CGNativeMember(ClassMethod):
class CGCallback(CGClass):
- def __init__(self, idlObject, descriptorProvider, baseName, methods,
- getters=[], setters=[]):
+ def __init__(self, idlObject, descriptorProvider, baseName, methods):
self.baseName = baseName
self._deps = idlObject.getDeps()
name = idlObject.identifier.name
@@ -6317,12 +7121,13 @@ class CGCallback(CGClass):
CGClass.__init__(self, name,
bases=[ClassBase(baseName)],
constructors=self.getConstructors(),
- methods=realMethods + getters + setters,
- decorators="#[derive(JSTraceable, PartialEq)]\n#[allow_unrooted_interior]")
+ methods=realMethods,
+ decorators="#[derive(JSTraceable, PartialEq)]\n"
+ "#[unrooted_must_root_lint::allow_unrooted_interior]")
def getConstructors(self):
return [ClassConstructor(
- [Argument("*mut JSContext", "aCx"), Argument("*mut JSObject", "aCallback")],
+ [Argument("SafeJSContext", "aCx"), Argument("*mut JSObject", "aCallback")],
bodyInHeader=True,
visibility="pub",
explicit=False,
@@ -6335,7 +7140,7 @@ class CGCallback(CGClass):
args = list(method.args)
# Strip out the JSContext*/JSObject* args
# that got added.
- assert args[0].name == "cx" and args[0].argType == "*mut JSContext"
+ assert args[0].name == "cx" and args[0].argType == "SafeJSContext"
assert args[1].name == "aThisObj" and args[1].argType == "HandleObject"
args = args[2:]
# Record the names of all the arguments, so we can use them when we call
@@ -6361,23 +7166,23 @@ class CGCallback(CGClass):
setupCall = "let s = CallSetup::new(self, aExceptionHandling);\n"
bodyWithThis = string.Template(
- setupCall +
- "rooted!(in(s.get_context()) let mut thisObjJS = ptr::null_mut());\n"
- "wrap_call_this_object(s.get_context(), thisObj, thisObjJS.handle_mut());\n"
- "if thisObjJS.is_null() {\n"
- " return Err(JSFailed);\n"
- "}\n"
- "return ${methodName}(${callArgs});").substitute({
- "callArgs": ", ".join(argnamesWithThis),
- "methodName": 'self.' + method.name,
- })
+ setupCall
+ + "rooted!(in(*s.get_context()) let mut thisObjJS = ptr::null_mut::<JSObject>());\n"
+ "wrap_call_this_object(s.get_context(), thisObj, thisObjJS.handle_mut());\n"
+ "if thisObjJS.is_null() {\n"
+ " return Err(JSFailed);\n"
+ "}\n"
+ "unsafe { ${methodName}(${callArgs}) }").substitute({
+ "callArgs": ", ".join(argnamesWithThis),
+ "methodName": 'self.' + method.name,
+ })
bodyWithoutThis = string.Template(
- setupCall +
- "rooted!(in(s.get_context()) let thisObjJS = ptr::null_mut());\n"
- "return ${methodName}(${callArgs});").substitute({
- "callArgs": ", ".join(argnamesWithoutThis),
- "methodName": 'self.' + method.name,
- })
+ setupCall
+ + "rooted!(in(*s.get_context()) let thisObjJS = ptr::null_mut::<JSObject>());\n"
+ "unsafe { ${methodName}(${callArgs}) }").substitute({
+ "callArgs": ", ".join(argnamesWithoutThis),
+ "methodName": 'self.' + method.name,
+ })
return [ClassMethod(method.name + '_', method.returnType, args,
bodyInHeader=True,
templateArgs=["T: DomObject"],
@@ -6418,7 +7223,7 @@ class CGCallbackFunctionImpl(CGGeneric):
def __init__(self, callback):
impl = string.Template("""\
impl CallbackContainer for ${type} {
- unsafe fn new(cx: *mut JSContext, callback: *mut JSObject) -> Rc<${type}> {
+ unsafe fn new(cx: SafeJSContext, callback: *mut JSObject) -> Rc<${type}> {
${type}::new(cx, callback)
}
@@ -6440,21 +7245,18 @@ class CGCallbackInterface(CGCallback):
def __init__(self, descriptor):
iface = descriptor.interface
attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
- getters = [CallbackGetter(a, descriptor) for a in attrs]
- setters = [CallbackSetter(a, descriptor) for a in attrs
- if not a.readonly]
+ assert not attrs
methods = [m for m in iface.members
if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
methods = [CallbackOperation(m, sig, descriptor) for m in methods
for sig in m.signatures()]
assert not iface.isJSImplemented() or not iface.ctor()
- CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
- methods, getters=getters, setters=setters)
+ CGCallback.__init__(self, iface, descriptor, "CallbackInterface", methods)
class FakeMember():
def __init__(self):
- self.treatNullAs = "Default"
+ pass
def isStatic(self):
return False
@@ -6500,6 +7302,7 @@ class CallbackMember(CGNativeMember):
name, (self.retvalType, args),
extendedAttrs={},
passJSBitsAsNeeded=False,
+ unsafe=needThisHandling,
visibility=visibility)
# We have to do all the generation of our body now, because
# the caller relies on us throwing if we can't manage it.
@@ -6518,7 +7321,7 @@ class CallbackMember(CGNativeMember):
replacements["argCount"] = self.argCountStr
replacements["argvDecl"] = string.Template(
"rooted_vec!(let mut argv);\n"
- "argv.extend((0..${argCount}).map(|_| Heap::new(UndefinedValue())));\n"
+ "argv.extend((0..${argCount}).map(|_| Heap::default()));\n"
).substitute(replacements)
else:
# Avoid weird 0-sized arrays
@@ -6533,10 +7336,7 @@ class CallbackMember(CGNativeMember):
"${convertArgs}"
"${doCall}"
"${returnResult}").substitute(replacements)
- return CGWrapper(CGIndenter(CGList([
- CGGeneric(pre),
- CGGeneric(body),
- ], "\n"), 4), pre="unsafe {\n", post="\n}").define()
+ return pre + "\n" + body
def getResultConversion(self):
replacements = {
@@ -6593,24 +7393,28 @@ class CallbackMember(CGNativeMember):
conversion = wrapForType(
"argv_root.handle_mut()", result=argval,
- successCode="argv[%s] = Heap::new(argv_root.get());" % jsvalIndex,
- pre="rooted!(in(cx) let mut argv_root = UndefinedValue());")
+ successCode=("{\n"
+ "let arg = &mut argv[%s];\n"
+ "*arg = Heap::default();\n"
+ "arg.set(argv_root.get());\n"
+ "}") % jsvalIndex,
+ pre="rooted!(in(*cx) let mut argv_root = UndefinedValue());")
if arg.variadic:
conversion = string.Template(
- "for idx in 0..${arg}.len() {\n" +
- CGIndenter(CGGeneric(conversion)).define() + "\n"
- "}"
+ "for idx in 0..${arg}.len() {\n"
+ + CGIndenter(CGGeneric(conversion)).define() + "\n"
+ + "}"
).substitute({"arg": arg.identifier.name})
elif arg.optional and not arg.defaultValue:
conversion = (
CGIfWrapper("%s.is_some()" % arg.identifier.name,
- CGGeneric(conversion)).define() +
- " else if argc == %d {\n"
- " // This is our current trailing argument; reduce argc\n"
- " argc -= 1;\n"
- "} else {\n"
- " argv[%d] = Heap::new(UndefinedValue());\n"
- "}" % (i + 1, i))
+ CGGeneric(conversion)).define()
+ + " else if argc == %d {\n"
+ " // This is our current trailing argument; reduce argc\n"
+ " argc -= 1;\n"
+ "} else {\n"
+ " argv[%d] = Heap::default();\n"
+ "}" % (i + 1, i))
return conversion
def getArgs(self, returnType, argList):
@@ -6623,7 +7427,7 @@ class CallbackMember(CGNativeMember):
return args
# We want to allow the caller to pass in a "this" object, as
# well as a JSContext.
- return [Argument("*mut JSContext", "cx"),
+ return [Argument("SafeJSContext", "cx"),
Argument("HandleObject", "aThisObj")] + args
def getCallSetup(self):
@@ -6632,7 +7436,7 @@ class CallbackMember(CGNativeMember):
return ""
return (
"CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n"
- "JSContext* cx = s.get_context();\n"
+ "JSContext* cx = *s.get_context();\n"
"if (!cx) {\n"
" return Err(JSFailed);\n"
"}\n")
@@ -6664,7 +7468,7 @@ class CallbackMethod(CallbackMember):
needThisHandling)
def getRvalDecl(self):
- return "rooted!(in(cx) let mut rval = UndefinedValue());\n"
+ return "rooted!(in(*cx) let mut rval = UndefinedValue());\n"
def getCall(self):
replacements = {
@@ -6680,9 +7484,9 @@ class CallbackMethod(CallbackMember):
replacements["argc"] = "0"
return string.Template(
"${getCallable}"
- "rooted!(in(cx) let rootedThis = ${thisObj});\n"
+ "rooted!(in(*cx) let rootedThis = ${thisObj});\n"
"let ok = ${callGuard}JS_CallFunctionValue(\n"
- " cx, rootedThis.handle(), callable.handle(),\n"
+ " *cx, rootedThis.handle(), callable.handle(),\n"
" &HandleValueArray {\n"
" length_: ${argc} as ::libc::size_t,\n"
" elements_: ${argv}\n"
@@ -6703,7 +7507,7 @@ class CallCallback(CallbackMethod):
return "aThisObj.get()"
def getCallableDecl(self):
- return "rooted!(in(cx) let callable = ObjectValue(self.callback()));\n"
+ return "rooted!(in(*cx) let callable = ObjectValue(self.callback()));\n"
def getCallGuard(self):
if self.callback._treatNonObjectAsNull:
@@ -6733,14 +7537,14 @@ class CallbackOperationBase(CallbackMethod):
"methodName": self.methodName
}
getCallableFromProp = string.Template(
- 'try!(self.parent.get_callable_property(cx, "${methodName}"))'
+ 'self.parent.get_callable_property(cx, "${methodName}")?'
).substitute(replacements)
if not self.singleOperation:
- return 'rooted!(in(cx) let callable =\n' + getCallableFromProp + ');\n'
+ return 'rooted!(in(*cx) let callable =\n' + getCallableFromProp + ');\n'
return (
'let isCallable = IsCallable(self.callback());\n'
- 'rooted!(in(cx) let callable =\n' +
- CGIndenter(
+ 'rooted!(in(*cx) let callable =\n'
+ + CGIndenter(
CGIfElseWrapper('isCallable',
CGGeneric('ObjectValue(self.callback())'),
CGGeneric(getCallableFromProp))).define() + ');\n')
@@ -6762,59 +7566,6 @@ class CallbackOperation(CallbackOperationBase):
descriptor, descriptor.interface.isSingleOperationInterface())
-class CallbackGetter(CallbackMember):
- def __init__(self, attr, descriptor):
- self.ensureASCIIName(attr)
- self.attrName = attr.identifier.name
- CallbackMember.__init__(self,
- (attr.type, []),
- callbackGetterName(attr),
- descriptor,
- needThisHandling=False)
-
- def getRvalDecl(self):
- return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
-
- def getCall(self):
- replacements = {
- "attrName": self.attrName
- }
- return string.Template(
- 'if (!JS_GetProperty(cx, mCallback, "${attrName}", &rval)) {\n'
- ' return Err(JSFailed);\n'
- '}\n').substitute(replacements)
-
-
-class CallbackSetter(CallbackMember):
- def __init__(self, attr, descriptor):
- self.ensureASCIIName(attr)
- self.attrName = attr.identifier.name
- CallbackMember.__init__(self,
- (BuiltinTypes[IDLBuiltinType.Types.void],
- [FakeArgument(attr.type, attr)]),
- callbackSetterName(attr),
- descriptor,
- needThisHandling=False)
-
- def getRvalDecl(self):
- # We don't need an rval
- return ""
-
- def getCall(self):
- replacements = {
- "attrName": self.attrName,
- "argv": "argv.handleAt(0)",
- }
- return string.Template(
- 'MOZ_ASSERT(argv.length() == 1);\n'
- 'if (!JS_SetProperty(cx, mCallback, "${attrName}", ${argv})) {\n'
- ' return Err(JSFailed);\n'
- '}\n').substitute(replacements)
-
- def getArgcDecl(self):
- return None
-
-
class CGIterableMethodGenerator(CGGeneric):
"""
Creates methods for iterable interfaces. Unwrapping/wrapping
@@ -6827,24 +7578,35 @@ class CGIterableMethodGenerator(CGGeneric):
CGGeneric.__init__(self, fill(
"""
if !IsCallable(arg0) {
- throw_type_error(cx, "Argument 1 of ${ifaceName}.forEach is not callable.");
+ throw_type_error(*cx, "Argument 1 of ${ifaceName}.forEach is not callable.");
return false;
}
- rooted!(in(cx) let arg0 = ObjectValue(arg0));
- rooted!(in(cx) let mut call_arg1 = UndefinedValue());
- rooted!(in(cx) let mut call_arg2 = UndefinedValue());
+ rooted!(in(*cx) let arg0 = ObjectValue(arg0));
+ rooted!(in(*cx) let mut call_arg1 = UndefinedValue());
+ rooted!(in(*cx) let mut call_arg2 = UndefinedValue());
let mut call_args = vec![UndefinedValue(), UndefinedValue(), ObjectValue(*_obj)];
- rooted!(in(cx) let mut ignoredReturnVal = UndefinedValue());
- for i in 0..(*this).get_iterable_length() {
- (*this).get_value_at_index(i).to_jsval(cx, call_arg1.handle_mut());
- (*this).get_key_at_index(i).to_jsval(cx, call_arg2.handle_mut());
+ rooted!(in(*cx) let mut ignoredReturnVal = UndefinedValue());
+
+ // This has to be a while loop since get_iterable_length() may change during
+ // the callback, and we need to avoid iterator invalidation.
+ //
+ // It is possible for this to loop infinitely, but that matches the spec
+ // and other browsers.
+ //
+ // https://heycam.github.io/webidl/#es-forEach
+ let mut i = 0;
+ while i < (*this).get_iterable_length() {
+ (*this).get_value_at_index(i).to_jsval(*cx, call_arg1.handle_mut());
+ (*this).get_key_at_index(i).to_jsval(*cx, call_arg2.handle_mut());
call_args[0] = call_arg1.handle().get();
call_args[1] = call_arg2.handle().get();
let call_args = HandleValueArray { length_: 3, elements_: call_args.as_ptr() };
- if !Call(cx, arg1, arg0.handle(), &call_args,
+ if !Call(*cx, arg1, arg0.handle(), &call_args,
ignoredReturnVal.handle_mut()) {
return false;
}
+
+ i += 1;
}
let result = ();
@@ -6853,9 +7615,7 @@ class CGIterableMethodGenerator(CGGeneric):
return
CGGeneric.__init__(self, fill(
"""
- let result = ${iterClass}::new(&*this,
- IteratorType::${itrMethod},
- super::${ifaceName}IteratorBinding::Wrap);
+ let result = ${iterClass}::new(&*this, IteratorType::${itrMethod});
""",
iterClass=iteratorNativeType(descriptor, True),
ifaceName=descriptor.interface.identifier.name,
@@ -6868,10 +7628,16 @@ def camel_to_upper_snake(s):
def process_arg(expr, arg):
if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
- if arg.type.nullable() or arg.type.isSequence() or arg.optional:
+ if arg.variadic or arg.type.isSequence():
expr += ".r()"
+ elif arg.type.nullable() and arg.optional and not arg.defaultValue:
+ expr += ".as_ref().map(Option::as_deref)"
+ elif arg.type.nullable() or arg.optional and not arg.defaultValue:
+ expr += ".as_deref()"
else:
expr = "&" + expr
+ elif isinstance(arg.type, IDLPromiseType):
+ expr = "&" + expr
return expr
@@ -6886,8 +7652,9 @@ class GlobalGenRoots():
@staticmethod
def InterfaceObjectMap(config):
mods = [
- "dom::bindings::codegen",
- "js::jsapi::{HandleObject, JSContext}",
+ "crate::dom::bindings::codegen",
+ "crate::script_runtime::JSContext",
+ "js::rust::HandleObject",
"phf",
]
imports = CGList([CGGeneric("use %s;" % mod) for mod in mods], "\n")
@@ -6899,9 +7666,9 @@ class GlobalGenRoots():
for (idx, d) in enumerate(global_descriptors)
)
global_flags = CGWrapper(CGIndenter(CGList([
- CGGeneric("const %s = %#x," % args)
+ CGGeneric("const %s = %#x;" % args)
for args in flags
- ], "\n")), pre="pub flags Globals: u8 {\n", post="\n}")
+ ], "\n")), pre="pub struct Globals: u8 {\n", post="\n}")
globals_ = CGWrapper(CGIndenter(global_flags), pre="bitflags! {\n", post="\n}")
phf = CGGeneric("include!(concat!(env!(\"OUT_DIR\"), \"/InterfaceObjectMapPhf.rs\"));")
@@ -6917,11 +7684,13 @@ class GlobalGenRoots():
for d in config.getDescriptors(hasInterfaceObject=True, isInline=False):
binding = toBindingNamespace(d.name)
pairs.append((d.name, binding, binding))
+ for alias in d.interface.legacyWindowAliases:
+ pairs.append((alias, binding, binding))
for ctor in d.interface.namedConstructors:
pairs.append((ctor.identifier.name, binding, binding))
pairs.sort(key=operator.itemgetter(0))
mappings = [
- CGGeneric('"%s": "codegen::Bindings::%s::%s::DefineDOMInterface as unsafe fn(_, _)"' % pair)
+ CGGeneric('"%s": "codegen::Bindings::%s::%s::DefineDOMInterface"' % pair)
for pair in pairs
]
return CGWrapper(
@@ -6938,8 +7707,6 @@ class GlobalGenRoots():
for d in config.getDescriptors(hasInterfaceObject=True)
if d.shouldHaveGetConstructorObjectMethod()])
- proxies = [d.name for d in config.getDescriptors(proxy=True)]
-
return CGList([
CGGeneric(AUTOGENERATED_WARNING_COMMENT),
CGGeneric("pub const PROTO_OR_IFACE_LENGTH: usize = %d;\n" % (len(protos) + len(constructors))),
@@ -6956,7 +7723,6 @@ class GlobalGenRoots():
" debug_assert!(proto_id < ID::Last as u16);\n"
" INTERFACES[proto_id as usize]\n"
"}\n\n"),
- CGNonNamespacedEnum('Proxies', proxies, 0, deriving="PartialEq, Copy, Clone"),
])
@staticmethod
@@ -6966,10 +7732,8 @@ class GlobalGenRoots():
CGRegisterProxyHandlers(config),
], "\n")
- return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], imports=[
- 'dom::bindings::codegen::Bindings',
- 'dom::bindings::codegen::PrototypeList::Proxies',
- 'libc',
+ return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], typedefs=[], imports=[
+ 'crate::dom::bindings::codegen::Bindings',
], config=config, ignored_warnings=[])
@staticmethod
@@ -6978,8 +7742,8 @@ class GlobalGenRoots():
for d in config.getDescriptors(register=True,
isCallback=False,
isIteratorInterface=False)])
- curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(),
- MakeNativeName(name)))
+ curr = CGList([CGGeneric("pub use crate::dom::%s::%s;\n" % (name.lower(),
+ MakeNativeName(name)))
for name in descriptors])
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
return curr
@@ -6991,9 +7755,9 @@ class GlobalGenRoots():
return getModuleFromObject(d).split('::')[-1]
descriptors = config.getDescriptors(register=True, isIteratorInterface=False)
- descriptors = (set(toBindingNamespace(d.name) for d in descriptors) |
- set(leafModule(d) for d in config.callbacks) |
- set(leafModule(d) for d in config.getDictionaries()))
+ descriptors = (set(toBindingNamespace(d.name) for d in descriptors)
+ | set(leafModule(d) for d in config.callbacks)
+ | set(leafModule(d) for d in config.getDictionaries()))
curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)])
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
return curr
@@ -7002,12 +7766,12 @@ class GlobalGenRoots():
def InheritTypes(config):
descriptors = config.getDescriptors(register=True, isCallback=False)
- imports = [CGGeneric("use dom::types::*;\n"),
- CGGeneric("use dom::bindings::conversions::{DerivedFrom, get_dom_class};\n"),
- CGGeneric("use dom::bindings::inheritance::Castable;\n"),
- CGGeneric("use dom::bindings::js::{JS, LayoutJS, Root};\n"),
- CGGeneric("use dom::bindings::trace::JSTraceable;\n"),
- CGGeneric("use dom::bindings::reflector::DomObject;\n"),
+ imports = [CGGeneric("use crate::dom::types::*;\n"),
+ CGGeneric("use crate::dom::bindings::conversions::{DerivedFrom, get_dom_class};\n"),
+ CGGeneric("use crate::dom::bindings::inheritance::Castable;\n"),
+ CGGeneric("use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom};\n"),
+ CGGeneric("use crate::dom::bindings::trace::JSTraceable;\n"),
+ CGGeneric("use crate::dom::bindings::reflector::DomObject;\n"),
CGGeneric("use js::jsapi::JSTracer;\n\n"),
CGGeneric("use std::mem;\n\n")]
allprotos = []
@@ -7066,7 +7830,7 @@ impl Clone for TopTypeId {
# TypeId enum.
return "%s(%sTypeId)" % (name, name) if name in hierarchy else name
- for base, derived in hierarchy.iteritems():
+ for base, derived in hierarchy.items():
variants = []
if config.getDescriptor(base).concrete:
variants.append(CGGeneric(base))
@@ -7113,7 +7877,7 @@ impl %(base)s {
def SupportedDomApis(config):
descriptors = config.getDescriptors(isExposedConditionally=False)
- base_path = os.path.join('dom', 'bindings', 'codegen')
+ base_path = os.path.dirname(__file__)
with open(os.path.join(base_path, 'apis.html.template')) as f:
base_template = f.read()
with open(os.path.join(base_path, 'api.html.template')) as f:
@@ -7130,6 +7894,8 @@ impl %(base)s {
for m in descriptor.interface.members:
if PropertyDefiner.getStringAttr(m, 'Pref') or \
PropertyDefiner.getStringAttr(m, 'Func') or \
+ PropertyDefiner.getStringAttr(m, 'Exposed') or \
+ m.getExtendedAttribute('SecureContext') or \
(m.isMethod() and m.isIdentifierLess()):
continue
display = m.identifier.name + ('()' if m.isMethod() else '')
diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py
index 0fe9bf6c004..b92f68af3b9 100644
--- a/components/script/dom/bindings/codegen/Configuration.py
+++ b/components/script/dom/bindings/codegen/Configuration.py
@@ -1,10 +1,11 @@
# 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/.
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+import functools
import os
-from WebIDL import IDLExternalInterface, IDLWrapperType, WebIDLError
+from WebIDL import IDLExternalInterface, IDLSequenceType, IDLWrapperType, WebIDLError
class Configuration:
@@ -15,7 +16,7 @@ class Configuration:
def __init__(self, filename, parseData):
# Read the configuration file.
glbl = {}
- execfile(filename, glbl)
+ exec(compile(open(filename).read(), filename, 'exec'), glbl)
config = glbl['DOMInterfaces']
# Build descriptors for all the interfaces we have in the parse data.
@@ -38,11 +39,6 @@ class Configuration:
iface = thing
self.interfaces[iface.identifier.name] = iface
if iface.identifier.name not in config:
- # Completely skip consequential interfaces with no descriptor
- # if they have no interface object because chances are we
- # don't need to do anything interesting with them.
- if iface.isConsequential() and not iface.hasInterfaceObject():
- continue
entry = {}
else:
entry = config[iface.identifier.name]
@@ -67,7 +63,9 @@ class Configuration:
c.isCallback() and not c.isInterface()]
# Keep the descriptor list sorted for determinism.
- self.descriptors.sort(lambda x, y: cmp(x.name, y.name))
+ def cmp(x, y):
+ return (x > y) - (x < y)
+ self.descriptors.sort(key=functools.cmp_to_key(lambda x, y: cmp(x.name, y.name)))
def getInterface(self, ifname):
return self.interfaces[ifname]
@@ -75,35 +73,45 @@ class Configuration:
def getDescriptors(self, **filters):
"""Gets the descriptors that match the given filters."""
curr = self.descriptors
- for key, val in filters.iteritems():
+ for key, val in filters.items():
if key == 'webIDLFile':
- getter = lambda x: x.interface.filename()
+ def getter(x):
+ return x.interface.filename()
elif key == 'hasInterfaceObject':
- getter = lambda x: x.interface.hasInterfaceObject()
+ def getter(x):
+ return x.interface.hasInterfaceObject()
elif key == 'isCallback':
- getter = lambda x: x.interface.isCallback()
+ def getter(x):
+ return x.interface.isCallback()
elif key == 'isNamespace':
- getter = lambda x: x.interface.isNamespace()
+ def getter(x):
+ return x.interface.isNamespace()
elif key == 'isJSImplemented':
- getter = lambda x: x.interface.isJSImplemented()
+ def getter(x):
+ return x.interface.isJSImplemented()
elif key == 'isGlobal':
- getter = lambda x: x.isGlobal()
+ def getter(x):
+ return x.isGlobal()
elif key == 'isInline':
- getter = lambda x: x.interface.getExtendedAttribute('Inline') is not None
+ def getter(x):
+ return x.interface.getExtendedAttribute('Inline') is not None
elif key == 'isExposedConditionally':
- getter = lambda x: x.interface.isExposedConditionally()
+ def getter(x):
+ return x.interface.isExposedConditionally()
elif key == 'isIteratorInterface':
- getter = lambda x: x.interface.isIteratorInterface()
+ def getter(x):
+ return x.interface.isIteratorInterface()
else:
- getter = lambda x: getattr(x, key)
- curr = filter(lambda x: getter(x) == val, curr)
+ def getter(x):
+ return getattr(x, key)
+ curr = [x for x in curr if getter(x) == val]
return curr
def getEnums(self, webIDLFile):
- return filter(lambda e: e.filename() == webIDLFile, self.enums)
+ return [e for e in self.enums if e.filename() == webIDLFile]
def getTypedefs(self, webIDLFile):
- return filter(lambda e: e.filename() == webIDLFile, self.typedefs)
+ return [e for e in self.typedefs if e.filename() == webIDLFile]
@staticmethod
def _filterForFile(items, webIDLFile=""):
@@ -111,7 +119,7 @@ class Configuration:
if not webIDLFile:
return items
- return filter(lambda x: x.filename() == webIDLFile, items)
+ return [x for x in items if x.filename() == webIDLFile]
def getDictionaries(self, webIDLFile=""):
return self._filterForFile(self.dictionaries, webIDLFile=webIDLFile)
@@ -128,8 +136,8 @@ class Configuration:
# We should have exactly one result.
if len(descriptors) != 1:
- raise NoSuchDescriptorError("For " + interfaceName + " found " +
- str(len(descriptors)) + " matches")
+ raise NoSuchDescriptorError("For " + interfaceName + " found "
+ + str(len(descriptors)) + " matches")
return descriptors[0]
def getDescriptorProvider(self):
@@ -160,10 +168,10 @@ class DescriptorProvider:
def MemberIsUnforgeable(member, descriptor):
- return ((member.isAttr() or member.isMethod()) and
- not member.isStatic() and
- (member.isUnforgeable() or
- bool(descriptor.interface.getExtendedAttribute("Unforgeable"))))
+ return ((member.isAttr() or member.isMethod())
+ and not member.isStatic()
+ and (member.isUnforgeable()
+ or bool(descriptor.interface.getExtendedAttribute("Unforgeable"))))
class Descriptor(DescriptorProvider):
@@ -204,40 +212,41 @@ class Descriptor(DescriptorProvider):
self.returnType = 'Rc<%s>' % typeName
self.argumentType = '&%s' % typeName
self.nativeType = typeName
- pathDefault = 'dom::types::%s' % typeName
+ pathDefault = 'crate::dom::types::%s' % typeName
elif self.interface.isCallback():
- ty = 'dom::bindings::codegen::Bindings::%sBinding::%s' % (ifaceName, ifaceName)
+ ty = 'crate::dom::bindings::codegen::Bindings::%sBinding::%s' % (ifaceName, ifaceName)
pathDefault = ty
self.returnType = "Rc<%s>" % ty
self.argumentType = "???"
self.nativeType = ty
else:
- self.returnType = "Root<%s>" % typeName
+ self.returnType = "DomRoot<%s>" % typeName
self.argumentType = "&%s" % typeName
self.nativeType = "*const %s" % typeName
if self.interface.isIteratorInterface():
- pathDefault = 'dom::bindings::iterable::IterableIterator'
+ pathDefault = 'crate::dom::bindings::iterable::IterableIterator'
else:
- pathDefault = 'dom::types::%s' % MakeNativeName(typeName)
+ pathDefault = 'crate::dom::types::%s' % MakeNativeName(typeName)
self.concreteType = typeName
self.register = desc.get('register', True)
self.path = desc.get('path', pathDefault)
- self.bindingPath = 'dom::bindings::codegen::Bindings::%s' % ('::'.join([ifaceName + 'Binding'] * 2))
+ self.inRealmMethods = [name for name in desc.get('inRealms', [])]
+ self.bindingPath = 'crate::dom::bindings::codegen::Bindings::%s' % ('::'.join([ifaceName + 'Binding'] * 2))
self.outerObjectHook = desc.get('outerObjectHook', 'None')
self.proxy = False
self.weakReferenceable = desc.get('weakReferenceable', False)
# If we're concrete, we need to crawl our ancestor interfaces and mark
# them as having a concrete descendant.
- self.concrete = (not self.interface.isCallback() and
- not self.interface.isNamespace() and
- not self.interface.getExtendedAttribute("Abstract") and
- not self.interface.getExtendedAttribute("Inline") and
- not spiderMonkeyInterface)
- self.hasUnforgeableMembers = (self.concrete and
- any(MemberIsUnforgeable(m, self) for m in
- self.interface.members))
+ self.concrete = (not self.interface.isCallback()
+ and not self.interface.isNamespace()
+ and not self.interface.getExtendedAttribute("Abstract")
+ and not self.interface.getExtendedAttribute("Inline")
+ and not spiderMonkeyInterface)
+ self.hasUnforgeableMembers = (self.concrete
+ and any(MemberIsUnforgeable(m, self) for m in
+ self.interface.members))
self.operations = {
'IndexedGetter': None,
@@ -249,6 +258,8 @@ class Descriptor(DescriptorProvider):
'Stringifier': None,
}
+ self.hasDefaultToJSON = False
+
def addOperation(operation, m):
if not self.operations[operation]:
self.operations[operation] = m
@@ -258,6 +269,8 @@ class Descriptor(DescriptorProvider):
for m in self.interface.members:
if m.isMethod() and m.isStringifier():
addOperation('Stringifier', m)
+ if m.isMethod() and m.isDefaultToJSON():
+ self.hasDefaultToJSON = True
if self.concrete:
iface = self.interface
@@ -279,8 +292,6 @@ class Descriptor(DescriptorProvider):
addIndexedOrNamedOperation('Getter', m)
if m.isSetter():
addIndexedOrNamedOperation('Setter', m)
- if m.isCreator():
- addIndexedOrNamedOperation('Creator', m)
if m.isDeleter():
addIndexedOrNamedOperation('Deleter', m)
@@ -316,7 +327,7 @@ class Descriptor(DescriptorProvider):
if config == '*':
iface = self.interface
while iface:
- add('all', map(lambda m: m.name, iface.members), attribute)
+ add('all', [m.name for m in iface.members], attribute)
iface = iface.parent
else:
add('all', [config], attribute)
@@ -390,15 +401,26 @@ class Descriptor(DescriptorProvider):
parent = parent.parent
return None
+ def supportsIndexedProperties(self):
+ return self.operations['IndexedGetter'] is not None
+
def hasDescendants(self):
- return (self.interface.getUserData("hasConcreteDescendant", False) or
- self.interface.getUserData("hasProxyDescendant", False))
+ return (self.interface.getUserData("hasConcreteDescendant", False)
+ or self.interface.getUserData("hasProxyDescendant", False))
+
+ def hasHTMLConstructor(self):
+ ctor = self.interface.ctor()
+ return ctor and ctor.isHTMLConstructor()
def shouldHaveGetConstructorObjectMethod(self):
assert self.interface.hasInterfaceObject()
if self.interface.getExtendedAttribute("Inline"):
return False
- return self.interface.isCallback() or self.interface.isNamespace() or self.hasDescendants()
+ return (self.interface.isCallback() or self.interface.isNamespace()
+ or self.hasDescendants() or self.hasHTMLConstructor())
+
+ def shouldCacheConstructor(self):
+ return self.hasDescendants() or self.hasHTMLConstructor()
def isExposedConditionally(self):
return self.interface.isExposedConditionally()
@@ -408,8 +430,8 @@ class Descriptor(DescriptorProvider):
Returns true if this is the primary interface for a global object
of some sort.
"""
- return bool(self.interface.getExtendedAttribute("Global") or
- self.interface.getExtendedAttribute("PrimaryGlobal"))
+ return bool(self.interface.getExtendedAttribute("Global")
+ or self.interface.getExtendedAttribute("PrimaryGlobal"))
# Some utility methods
@@ -420,8 +442,8 @@ def MakeNativeName(name):
def getModuleFromObject(object):
- return ('dom::bindings::codegen::Bindings::' +
- os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding')
+ return ('crate::dom::bindings::codegen::Bindings::'
+ + os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding')
def getTypesFromDescriptor(descriptor):
@@ -453,7 +475,7 @@ def getTypesFromDictionary(dictionary):
types = []
curDict = dictionary
while curDict:
- types.extend([m.type for m in curDict.members])
+ types.extend([getUnwrappedType(m.type) for m in curDict.members])
curDict = curDict.parent
return types
@@ -469,6 +491,12 @@ def getTypesFromCallback(callback):
return types
+def getUnwrappedType(type):
+ while isinstance(type, IDLSequenceType):
+ type = type.inner
+ return type
+
+
def iteratorNativeType(descriptor, infer=False):
assert descriptor.interface.isIterable()
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
diff --git a/components/script/dom/bindings/codegen/GlobalGen.py b/components/script/dom/bindings/codegen/GlobalGen.py
deleted file mode 100644
index 966e4bdbcd2..00000000000
--- a/components/script/dom/bindings/codegen/GlobalGen.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# 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/.
-
-# We do one global pass over all the WebIDL to generate our prototype enum
-# and generate information for subsequent phases.
-
-import sys
-import os
-sys.path.append(os.path.join(".", "parser"))
-sys.path.append(os.path.join(".", "ply"))
-import WebIDL
-import cPickle
-from Configuration import Configuration
-from CodegenRust import GlobalGenRoots, replaceFileIfChanged
-
-
-def generate_file(config, name, filename):
- root = getattr(GlobalGenRoots, name)(config)
- code = root.define()
-
- if replaceFileIfChanged(filename, code):
- print "Generating %s" % (filename)
- else:
- print "%s hasn't changed - not touching it" % (filename)
-
-
-def main():
- # Parse arguments.
- from optparse import OptionParser
- usageString = "usage: %prog [options] configFile outputdir webidldir [files]"
- o = OptionParser(usage=usageString)
- o.add_option("--cachedir", dest='cachedir', default=None,
- help="Directory in which to cache lex/parse tables.")
- o.add_option("--only-html", dest='only_html', action="store_true",
- help="Only generate HTML from WebIDL inputs")
- o.add_option("--filelist", dest='filelist', default=None,
- help="A file containing the list (one per line) of webidl files to process.")
- (options, args) = o.parse_args()
-
- if len(args) < 2:
- o.error(usageString)
-
- configFile = args[0]
- outputdir = args[1]
- baseDir = args[2]
- if options.filelist is not None:
- fileList = (l.strip() for l in open(options.filelist).xreadlines())
- else:
- fileList = args[3:]
-
- # Parse the WebIDL.
- parser = WebIDL.Parser(options.cachedir)
- for filename in fileList:
- fullPath = os.path.normpath(os.path.join(baseDir, filename))
- with open(fullPath, 'rb') as f:
- lines = f.readlines()
- parser.parse(''.join(lines), fullPath)
- parserResults = parser.finish()
-
- if not options.only_html:
- # Write the parser results out to a pickle.
- resultsPath = os.path.join(outputdir, 'ParserResults.pkl')
- with open(resultsPath, 'wb') as resultsFile:
- cPickle.dump(parserResults, resultsFile, -1)
-
- # Load the configuration.
- config = Configuration(configFile, parserResults)
-
- to_generate = [
- ('SupportedDomApis', 'apis.html'),
- ]
-
- if not options.only_html:
- to_generate = [
- ('PrototypeList', 'PrototypeList.rs'),
- ('RegisterBindings', 'RegisterBindings.rs'),
- ('InterfaceObjectMap', 'InterfaceObjectMap.rs'),
- ('InterfaceObjectMapData', 'InterfaceObjectMapData.json'),
- ('InterfaceTypes', 'InterfaceTypes.rs'),
- ('InheritTypes', 'InheritTypes.rs'),
- ('Bindings', os.path.join('Bindings', 'mod.rs')),
- ('UnionTypes', 'UnionTypes.rs'),
- ]
-
- for name, filename in to_generate:
- generate_file(config, name, os.path.join(outputdir, filename))
-
-if __name__ == '__main__':
- main()
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
index dc9fa036141..d74278c3e0c 100644
--- a/components/script/dom/bindings/codegen/parser/WebIDL.py
+++ b/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -4,13 +4,15 @@
""" A WebIDL parser. """
+
from ply import lex, yacc
import re
import os
import traceback
import math
import string
-from collections import defaultdict
+from collections import defaultdict, OrderedDict
+from itertools import chain
# Machinery
@@ -40,32 +42,22 @@ def parseInt(literal):
return value * sign
-# Magic for creating enums
-def M_add_class_attribs(attribs, start):
- def foo(name, bases, dict_):
- for v, k in enumerate(attribs):
- dict_[k] = start + v
- assert 'length' not in dict_
- dict_['length'] = start + len(attribs)
- return type(name, bases, dict_)
- return foo
-
-
def enum(*names, **kw):
- if len(kw) == 1:
- base = kw['base'].__class__
- start = base.length
- else:
- assert len(kw) == 0
- base = object
- start = 0
-
- class Foo(base):
- __metaclass__ = M_add_class_attribs(names, start)
-
+ class Foo(object):
+ attrs = OrderedDict()
+ def __init__(self, names):
+ for v, k in enumerate(names):
+ self.attrs[k] = v
+ def __getattr__(self, attr):
+ if attr in self.attrs:
+ return self.attrs[attr]
+ raise AttributeError
def __setattr__(self, name, value): # this makes it read-only
raise NotImplementedError
- return Foo()
+
+ if "base" not in kw:
+ return Foo(names)
+ return Foo(chain(list(kw["base"].attrs.keys()), names))
class WebIDLError(Exception):
@@ -132,6 +124,9 @@ class BuiltinLocation(object):
return (isinstance(other, BuiltinLocation) and
self.msg == other.msg)
+ def __hash__(self):
+ return hash(self.msg)
+
def filename(self):
return '<builtin>'
@@ -162,6 +157,9 @@ class IDLObject(object):
def isNamespace(self):
return False
+ def isInterfaceMixin(self):
+ return False
+
def isEnum(self):
return False
@@ -241,15 +239,19 @@ class IDLScope(IDLObject):
# A mapping from global name to the set of global interfaces
# that have that global name.
self.globalNameMapping = defaultdict(set)
- self.primaryGlobalAttr = None
- self.primaryGlobalName = None
def __str__(self):
return self.QName()
def QName(self):
- if self._name:
- return self._name.QName() + "::"
+ # It's possible for us to be called before __init__ has been called, for
+ # the IDLObjectWithScope case. In that case, self._name won't be set yet.
+ if hasattr(self, "_name"):
+ name = self._name
+ else:
+ name = None
+ if name:
+ return name.QName() + "::"
return "::"
def ensureUnique(self, identifier, object):
@@ -305,8 +307,8 @@ class IDLScope(IDLObject):
# because we need to merge overloads of NamedConstructors and we need to
# detect conflicts in those across interfaces. See also the comment in
# IDLInterface.addExtendedAttributes for "NamedConstructor".
- if (originalObject.tag == IDLInterfaceMember.Tags.Method and
- newObject.tag == IDLInterfaceMember.Tags.Method):
+ if (isinstance(originalObject, IDLMethod) and
+ isinstance(newObject, IDLMethod)):
return originalObject.addOverload(newObject)
# Default to throwing, derived classes can override.
@@ -316,7 +318,7 @@ class IDLScope(IDLObject):
newObject.location)
raise WebIDLError(
- "Multiple unresolvable definitions of identifier '%s' in scope '%s%s"
+ "Multiple unresolvable definitions of identifier '%s' in scope '%s'%s"
% (identifier.name, str(self), conflictdesc), [])
def _lookupIdentifier(self, identifier):
@@ -327,6 +329,13 @@ class IDLScope(IDLObject):
assert identifier.scope == self
return self._lookupIdentifier(identifier)
+ def addIfaceGlobalNames(self, interfaceName, globalNames):
+ """Record the global names (from |globalNames|) that can be used in
+ [Exposed] to expose things in a global named |interfaceName|"""
+ self.globalNames.update(globalNames)
+ for name in globalNames:
+ self.globalNameMapping[name].add(interfaceName)
+
class IDLIdentifier(IDLObject):
def __init__(self, location, scope, name):
@@ -367,8 +376,6 @@ class IDLUnresolvedIdentifier(IDLObject):
[location])
if name[0] == '_' and not allowDoubleUnderscore:
name = name[1:]
- # TODO: Bug 872377, Restore "toJSON" to below list.
- # We sometimes need custom serialization, so allow toJSON for now.
if (name in ["constructor", "toString"] and
not allowForbidden):
raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
@@ -409,48 +416,11 @@ class IDLObjectWithIdentifier(IDLObject):
if parentScope:
self.resolve(parentScope)
- self.treatNullAs = "Default"
-
def resolve(self, parentScope):
assert isinstance(parentScope, IDLScope)
assert isinstance(self.identifier, IDLUnresolvedIdentifier)
self.identifier.resolve(parentScope, self)
- def checkForStringHandlingExtendedAttributes(self, attrs,
- isDictionaryMember=False,
- isOptional=False):
- """
- A helper function to deal with TreatNullAs. Returns the list
- of attrs it didn't handle itself.
- """
- assert isinstance(self, IDLArgument) or isinstance(self, IDLAttribute)
- unhandledAttrs = list()
- for attr in attrs:
- if not attr.hasValue():
- unhandledAttrs.append(attr)
- continue
-
- identifier = attr.identifier()
- value = attr.value()
- if identifier == "TreatNullAs":
- if not self.type.isDOMString() or self.type.nullable():
- raise WebIDLError("[TreatNullAs] is only allowed on "
- "arguments or attributes whose type is "
- "DOMString",
- [self.location])
- if isDictionaryMember:
- raise WebIDLError("[TreatNullAs] is not allowed for "
- "dictionary members", [self.location])
- if value != 'EmptyString':
- raise WebIDLError("[TreatNullAs] must take the identifier "
- "'EmptyString', not '%s'" % value,
- [self.location])
- self.treatNullAs = value
- else:
- unhandledAttrs.append(attr)
-
- return unhandledAttrs
-
class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope):
def __init__(self, location, parentScope, identifier):
@@ -496,8 +466,17 @@ class IDLExposureMixins():
raise WebIDLError("Unknown [Exposed] value %s" % globalName,
[self._location])
- if len(self._exposureGlobalNames) == 0:
- self._exposureGlobalNames.add(scope.primaryGlobalName)
+ # Verify that we are exposed _somwhere_ if we have some place to be
+ # exposed. We don't want to assert that we're definitely exposed
+ # because a lot of our parser tests have small-enough IDL snippets that
+ # they don't include any globals, and we don't really want to go through
+ # and add global interfaces and [Exposed] annotations to all those
+ # tests.
+ if len(scope.globalNames) != 0:
+ if (len(self._exposureGlobalNames) == 0):
+ raise WebIDLError(("'%s' is not exposed anywhere even though we have "
+ "globals to be exposed to") % self,
+ [self.location])
globalNameSetToExposureSet(scope, self._exposureGlobalNames,
self.exposureSet)
@@ -505,18 +484,14 @@ class IDLExposureMixins():
def isExposedInWindow(self):
return 'Window' in self.exposureSet
- def isExposedOnMainThread(self):
- return (self.isExposedInWindow() or
- self.isExposedInSystemGlobals())
-
def isExposedInAnyWorker(self):
return len(self.getWorkerExposureSet()) > 0
def isExposedInWorkerDebugger(self):
return len(self.getWorkerDebuggerExposureSet()) > 0
- def isExposedInSystemGlobals(self):
- return 'BackstagePass' in self.exposureSet
+ def isExposedInAnyWorklet(self):
+ return len(self.getWorkletExposureSet()) > 0
def isExposedInSomeButNotAllWorkers(self):
"""
@@ -534,22 +509,24 @@ class IDLExposureMixins():
workerScopes = self._globalScope.globalNameMapping["Worker"]
return workerScopes.intersection(self.exposureSet)
+ def getWorkletExposureSet(self):
+ workletScopes = self._globalScope.globalNameMapping["Worklet"]
+ return workletScopes.intersection(self.exposureSet)
+
def getWorkerDebuggerExposureSet(self):
workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"]
return workerDebuggerScopes.intersection(self.exposureSet)
-class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
+class IDLExternalInterface(IDLObjectWithIdentifier):
def __init__(self, location, parentScope, identifier):
assert isinstance(identifier, IDLUnresolvedIdentifier)
assert isinstance(parentScope, IDLScope)
self.parent = None
IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
- IDLExposureMixins.__init__(self, location)
IDLObjectWithIdentifier.resolve(self, parentScope)
def finish(self, scope):
- IDLExposureMixins.finish(self, scope)
pass
def validate(self):
@@ -564,11 +541,11 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
def isInterface(self):
return True
- def isConsequential(self):
- return False
-
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ if len(attrs) != 0:
+ raise WebIDLError("There are no extended attributes that are "
+ "allowed on external interfaces",
+ [attrs[0].location, self.location])
def resolve(self, parentScope):
pass
@@ -579,16 +556,41 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
def isJSImplemented(self):
return False
- def isProbablyShortLivingObject(self):
- return False
-
- def isNavigatorProperty(self):
+ def hasProbablyShortLivingWrapper(self):
return False
def _getDependentObjects(self):
return set()
+class IDLPartialDictionary(IDLObject):
+ def __init__(self, location, name, members, nonPartialDictionary):
+ assert isinstance(name, IDLUnresolvedIdentifier)
+
+ IDLObject.__init__(self, location)
+ self.identifier = name
+ self.members = members
+ self._nonPartialDictionary = nonPartialDictionary
+ self._finished = False
+ nonPartialDictionary.addPartialDictionary(self)
+
+ def addExtendedAttributes(self, attrs):
+ pass
+
+ def finish(self, scope):
+ if self._finished:
+ return
+ self._finished = True
+
+ # Need to make sure our non-partial dictionary gets
+ # finished so it can report cases when we only have partial
+ # dictionaries.
+ self._nonPartialDictionary.finish(scope)
+
+ def validate(self):
+ pass
+
+
class IDLPartialInterfaceOrNamespace(IDLObject):
def __init__(self, location, name, members, nonPartialInterfaceOrNamespace):
assert isinstance(name, IDLUnresolvedIdentifier)
@@ -602,13 +604,13 @@ class IDLPartialInterfaceOrNamespace(IDLObject):
self._haveSecureContextExtendedAttribute = False
self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
self._finished = False
- nonPartialInterfaceOrNamespace.addPartialInterface(self)
+ nonPartialInterfaceOrNamespace.addPartial(self)
def addExtendedAttributes(self, attrs):
for attr in attrs:
identifier = attr.identifier()
- if identifier in ["Constructor", "NamedConstructor"]:
+ if identifier == "NamedConstructor":
self.propagatedExtendedAttrs.append(attr)
elif identifier == "SecureContext":
self._haveSecureContextExtendedAttribute = True
@@ -673,36 +675,225 @@ def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
for name in nameSet:
exposureSet.update(globalScope.globalNameMapping[name])
-
-class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
- def __init__(self, location, parentScope, name, parent, members,
- isKnownNonPartial):
+class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMixins):
+ def __init__(self, location, parentScope, name):
assert isinstance(parentScope, IDLScope)
assert isinstance(name, IDLUnresolvedIdentifier)
+
+ self._finished = False
+ self.members = []
+ self._partials = []
+ self._extendedAttrDict = {}
+ self._isKnownNonPartial = False
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+ IDLExposureMixins.__init__(self, location)
+
+ def finish(self, scope):
+ if not self._isKnownNonPartial:
+ raise WebIDLError("%s does not have a non-partial declaration" %
+ str(self), [self.location])
+
+ IDLExposureMixins.finish(self, scope)
+
+ # Now go ahead and merge in our partials.
+ for partial in self._partials:
+ partial.finish(scope)
+ self.addExtendedAttributes(partial.propagatedExtendedAttrs)
+ self.members.extend(partial.members)
+
+ def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
+ assert isinstance(scope, IDLScope)
+ assert isinstance(originalObject, IDLInterfaceMember)
+ assert isinstance(newObject, IDLInterfaceMember)
+
+ retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
+ originalObject, newObject)
+
+ # Might be a ctor, which isn't in self.members
+ if newObject in self.members:
+ self.members.remove(newObject)
+ return retval
+
+ def typeName(self):
+ if self.isInterface():
+ return "interface"
+ if self.isNamespace():
+ return "namespace"
+ assert self.isInterfaceMixin()
+ return "interface mixin"
+
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
+
+ def setNonPartial(self, location, members):
+ if self._isKnownNonPartial:
+ raise WebIDLError("Two non-partial definitions for the "
+ "same %s" % self.typeName(),
+ [location, self.location])
+ self._isKnownNonPartial = True
+ # Now make it look like we were parsed at this new location, since
+ # that's the place where the interface is "really" defined
+ self.location = location
+ # Put the new members at the beginning
+ self.members = members + self.members
+
+ def addPartial(self, partial):
+ assert self.identifier.name == partial.identifier.name
+ self._partials.append(partial)
+
+ def getPartials(self):
+ # Don't let people mutate our guts.
+ return list(self._partials)
+
+ def finishMembers(self, scope):
+ # Assuming we've merged in our partials, set the _exposureGlobalNames on
+ # any members that don't have it set yet. Note that any partial
+ # interfaces that had [Exposed] set have already set up
+ # _exposureGlobalNames on all the members coming from them, so this is
+ # just implementing the "members default to interface or interface mixin
+ # that defined them" and "partial interfaces or interface mixins default
+ # to interface or interface mixin they're a partial for" rules from the
+ # spec.
+ for m in self.members:
+ # If m, or the partial m came from, had [Exposed]
+ # specified, it already has a nonempty exposure global names set.
+ if len(m._exposureGlobalNames) == 0:
+ m._exposureGlobalNames.update(self._exposureGlobalNames)
+ if m.isAttr() and m.stringifier:
+ m.expand(self.members)
+
+ # resolve() will modify self.members, so we need to iterate
+ # over a copy of the member list here.
+ for member in list(self.members):
+ member.resolve(self)
+
+ for member in self.members:
+ member.finish(scope)
+
+ # Now that we've finished our members, which has updated their exposure
+ # sets, make sure they aren't exposed in places where we are not.
+ for member in self.members:
+ if not member.exposureSet.issubset(self.exposureSet):
+ raise WebIDLError("Interface or interface mixin member has "
+ "larger exposure set than its container",
+ [member.location, self.location])
+
+ def isExternal(self):
+ return False
+
+
+class IDLInterfaceMixin(IDLInterfaceOrInterfaceMixinOrNamespace):
+ def __init__(self, location, parentScope, name, members, isKnownNonPartial):
+ self.actualExposureGlobalNames = set()
+
+ assert isKnownNonPartial or len(members) == 0
+ IDLInterfaceOrInterfaceMixinOrNamespace.__init__(self, location, parentScope, name)
+
+ if isKnownNonPartial:
+ self.setNonPartial(location, members)
+
+ def __str__(self):
+ return "Interface mixin '%s'" % self.identifier.name
+
+ def isInterfaceMixin(self):
+ return True
+
+ def finish(self, scope):
+ if self._finished:
+ return
+ self._finished = True
+
+ # Expose to the globals of interfaces that includes this mixin if this
+ # mixin has no explicit [Exposed] so that its members can be exposed
+ # based on the base interface exposure set.
+ #
+ # Make sure this is done before IDLExposureMixins.finish call, since
+ # that converts our set of exposure global names to an actual exposure
+ # set.
+ hasImplicitExposure = len(self._exposureGlobalNames) == 0
+ if hasImplicitExposure:
+ self._exposureGlobalNames.update(self.actualExposureGlobalNames)
+
+ IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope)
+
+ self.finishMembers(scope)
+
+ def validate(self):
+ for member in self.members:
+
+ if member.isAttr():
+ if member.inherit:
+ raise WebIDLError("Interface mixin member cannot include "
+ "an inherited attribute",
+ [member.location, self.location])
+ if member.isStatic():
+ raise WebIDLError("Interface mixin member cannot include "
+ "a static member",
+ [member.location, self.location])
+
+ if member.isMethod():
+ if member.isStatic():
+ raise WebIDLError("Interface mixin member cannot include "
+ "a static operation",
+ [member.location, self.location])
+ if (member.isGetter() or
+ member.isSetter() or
+ member.isDeleter() or
+ member.isLegacycaller()):
+ raise WebIDLError("Interface mixin member cannot include a "
+ "special operation",
+ [member.location, self.location])
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ if identifier == "SecureContext":
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ # This gets propagated to all our members.
+ for member in self.members:
+ if member.getExtendedAttribute("SecureContext"):
+ raise WebIDLError("[SecureContext] specified on both "
+ "an interface mixin member and on"
+ "the interface mixin itself",
+ [member.location, attr.location])
+ member.addExtendedAttributes([attr])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr,
+ self._exposureGlobalNames)
+ else:
+ raise WebIDLError("Unknown extended attribute %s on interface" % identifier,
+ [attr.location])
+
+ attrlist = attr.listValue()
+ self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+
+ def _getDependentObjects(self):
+ return set(self.members)
+
+
+class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
+ def __init__(self, location, parentScope, name, parent, members,
+ isKnownNonPartial, toStringTag):
assert isKnownNonPartial or not parent
assert isKnownNonPartial or len(members) == 0
self.parent = None
self._callback = False
- self._finished = False
- self.members = []
self.maplikeOrSetlikeOrIterable = None
- self._partialInterfaces = []
- self._extendedAttrDict = {}
# namedConstructors needs deterministic ordering because bindings code
# outputs the constructs in the order that namedConstructors enumerates
# them.
self.namedConstructors = list()
- self.implementedInterfaces = set()
- self._consequential = False
- self._isKnownNonPartial = False
+ self.legacyWindowAliases = []
+ self.includedMixins = set()
# self.interfacesBasedOnSelf is the set of interfaces that inherit from
- # self or have self as a consequential interface, including self itself.
+ # self, including self itself.
# Used for distinguishability checking.
self.interfacesBasedOnSelf = set([self])
- # self.interfacesImplementingSelf is the set of interfaces that directly
- # have self as a consequential interface
- self.interfacesImplementingSelf = set()
self._hasChildInterfaces = False
self._isOnGlobalProtoChain = False
# Tracking of the number of reserved slots we need for our
@@ -713,9 +904,14 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
# If this is an iterator interface, we need to know what iterable
# interface we're iterating for in order to get its nativeType.
self.iterableInterface = None
+ # True if we have cross-origin members.
+ self.hasCrossOriginMembers = False
+ # True if some descendant (including ourselves) has cross-origin members
+ self.hasDescendantWithCrossOriginMembers = False
- IDLObjectWithScope.__init__(self, location, parentScope, name)
- IDLExposureMixins.__init__(self, location)
+ self.toStringTag = toStringTag
+
+ IDLInterfaceOrInterfaceMixinOrNamespace.__init__(self, location, parentScope, name)
if isKnownNonPartial:
self.setNonPartial(location, parent, members)
@@ -735,37 +931,23 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
def isIteratorInterface(self):
return self.iterableInterface is not None
- def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
- assert isinstance(scope, IDLScope)
- assert isinstance(originalObject, IDLInterfaceMember)
- assert isinstance(newObject, IDLInterfaceMember)
-
- retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
- originalObject, newObject)
-
- # Might be a ctor, which isn't in self.members
- if newObject in self.members:
- self.members.remove(newObject)
- return retval
-
def finish(self, scope):
if self._finished:
return
self._finished = True
- if not self._isKnownNonPartial:
- raise WebIDLError("Interface %s does not have a non-partial "
- "declaration" % self.identifier.name,
- [self.location])
-
- IDLExposureMixins.finish(self, scope)
+ IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope)
- # Now go ahead and merge in our partial interfaces.
- for partial in self._partialInterfaces:
- partial.finish(scope)
- self.addExtendedAttributes(partial.propagatedExtendedAttrs)
- self.members.extend(partial.members)
+ if len(self.legacyWindowAliases) > 0:
+ if not self.hasInterfaceObject():
+ raise WebIDLError("Interface %s unexpectedly has [LegacyWindowAlias] "
+ "and [NoInterfaceObject] together" % self.identifier.name,
+ [self.location])
+ if not self.isExposedInWindow():
+ raise WebIDLError("Interface %s has [LegacyWindowAlias] "
+ "but not exposed in Window" % self.identifier.name,
+ [self.location])
# Generate maplike/setlike interface members. Since generated members
# need to be treated like regular interface members, do this before
@@ -788,19 +970,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
# our required methods in Codegen. Generate members now.
self.maplikeOrSetlikeOrIterable.expand(self.members, self.isJSImplemented())
- # Now that we've merged in our partial interfaces, set the
- # _exposureGlobalNames on any members that don't have it set yet. Note
- # that any partial interfaces that had [Exposed] set have already set up
- # _exposureGlobalNames on all the members coming from them, so this is
- # just implementing the "members default to interface that defined them"
- # and "partial interfaces default to interface they're a partial for"
- # rules from the spec.
- for m in self.members:
- # If m, or the partial interface m came from, had [Exposed]
- # specified, it already has a nonempty exposure global names set.
- if len(m._exposureGlobalNames) == 0:
- m._exposureGlobalNames.update(self._exposureGlobalNames)
-
assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder)
parent = self.parent.finish(scope) if self.parent else None
if parent and isinstance(parent, IDLExternalInterface):
@@ -809,7 +978,11 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
(self.identifier.name,
self.parent.identifier.name),
[self.location])
- assert not parent or isinstance(parent, IDLInterface)
+ if parent and not isinstance(parent, IDLInterface):
+ raise WebIDLError("%s inherits from %s which is not an interface " %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, parent.location])
self.parent = parent
@@ -835,10 +1008,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.totalMembersInSlots = self.parent.totalMembersInSlots
- # Interfaces with [Global] or [PrimaryGlobal] must not
- # have anything inherit from them
- if (self.parent.getExtendedAttribute("Global") or
- self.parent.getExtendedAttribute("PrimaryGlobal")):
+ # Interfaces with [Global] must not have anything inherit from them
+ if self.parent.getExtendedAttribute("Global"):
# Note: This is not a self.parent.isOnGlobalProtoChain() check
# because ancestors of a [Global] interface can have other
# descendants.
@@ -854,10 +1025,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.parent.identifier.name),
[self.location, self.parent.location])
- # Callbacks must not inherit from non-callbacks or inherit from
- # anything that has consequential interfaces.
+ # Callbacks must not inherit from non-callbacks.
# XXXbz Can non-callbacks inherit from callbacks? Spec issue pending.
- # XXXbz Can callbacks have consequential interfaces? Spec issue pending
if self.isCallback():
if not self.parent.isCallback():
raise WebIDLError("Callback interface %s inheriting from "
@@ -894,48 +1063,40 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.parent.identifier.name),
[self.location, self.parent.location])
- for iface in self.implementedInterfaces:
- iface.finish(scope)
+ for mixin in self.includedMixins:
+ mixin.finish(scope)
cycleInGraph = self.findInterfaceLoopPoint(self)
if cycleInGraph:
- raise WebIDLError("Interface %s has itself as ancestor or "
- "implemented interface" % self.identifier.name,
- [self.location, cycleInGraph.location])
-
- if self.isCallback():
- # "implements" should have made sure we have no
- # consequential interfaces.
- assert len(self.getConsequentialInterfaces()) == 0
- # And that we're not consequential.
- assert not self.isConsequential()
-
- # Now resolve() and finish() our members before importing the
- # ones from our implemented interfaces.
-
- # resolve() will modify self.members, so we need to iterate
- # over a copy of the member list here.
- for member in list(self.members):
- member.resolve(self)
+ raise WebIDLError(
+ "Interface %s has itself as ancestor" % self.identifier.name,
+ [self.location, cycleInGraph.location])
- for member in self.members:
- member.finish(scope)
-
- # Now that we've finished our members, which has updated their exposure
- # sets, make sure they aren't exposed in places where we are not.
- for member in self.members:
- if not member.exposureSet.issubset(self.exposureSet):
- raise WebIDLError("Interface member has larger exposure set "
- "than the interface itself",
- [member.location, self.location])
+ self.finishMembers(scope)
ctor = self.ctor()
if ctor is not None:
- assert len(ctor._exposureGlobalNames) == 0
+ if not self.hasInterfaceObject():
+ raise WebIDLError(
+ "Can't have both a constructor and [NoInterfaceObject]",
+ [self.location, ctor.location])
+
+ if self.globalNames:
+ raise WebIDLError(
+ "Can't have both a constructor and [Global]",
+ [self.location, ctor.location])
+
+ assert(ctor._exposureGlobalNames == self._exposureGlobalNames)
ctor._exposureGlobalNames.update(self._exposureGlobalNames)
- ctor.finish(scope)
+ # Remove the constructor operation from our member list so
+ # it doesn't get in the way later.
+ self.members.remove(ctor)
for ctor in self.namedConstructors:
+ if self.globalNames:
+ raise WebIDLError(
+ "Can't have both a named constructor and [Global]",
+ [self.location, ctor.location])
assert len(ctor._exposureGlobalNames) == 0
ctor._exposureGlobalNames.update(self._exposureGlobalNames)
ctor.finish(scope)
@@ -945,41 +1106,16 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
# admixed.
self.originalMembers = list(self.members)
- # Import everything from our consequential interfaces into
- # self.members. Sort our consequential interfaces by name
- # just so we have a consistent order.
- for iface in sorted(self.getConsequentialInterfaces(),
- cmp=cmp,
+ for mixin in sorted(self.includedMixins,
key=lambda x: x.identifier.name):
- # Flag the interface as being someone's consequential interface
- iface.setIsConsequentialInterfaceOf(self)
- # Verify that we're not exposed somewhere where iface is not exposed
- if not self.exposureSet.issubset(iface.exposureSet):
- raise WebIDLError("Interface %s is exposed in globals where its "
- "consequential interface %s is not exposed." %
- (self.identifier.name, iface.identifier.name),
- [self.location, iface.location])
-
- # If we have a maplike or setlike, and the consequential interface
- # also does, throw an error.
- if iface.maplikeOrSetlikeOrIterable and self.maplikeOrSetlikeOrIterable:
- raise WebIDLError("Maplike/setlike/iterable interface %s cannot have "
- "maplike/setlike/iterable interface %s as a "
- "consequential interface" %
- (self.identifier.name,
- iface.identifier.name),
- [self.maplikeOrSetlikeOrIterable.location,
- iface.maplikeOrSetlikeOrIterable.location])
- additionalMembers = iface.originalMembers
- for additionalMember in additionalMembers:
+ for mixinMember in mixin.members:
for member in self.members:
- if additionalMember.identifier.name == member.identifier.name:
+ if mixinMember.identifier.name == member.identifier.name:
raise WebIDLError(
- "Multiple definitions of %s on %s coming from 'implements' statements" %
+ "Multiple definitions of %s on %s coming from 'includes' statements" %
(member.identifier.name, self),
- [additionalMember.location, member.location])
- self.members.extend(additionalMembers)
- iface.interfacesImplementingSelf.add(self)
+ [mixinMember.location, member.location])
+ self.members.extend(mixin.members)
for ancestor in self.getInheritedInterfaces():
ancestor.interfacesBasedOnSelf.add(self)
@@ -992,8 +1128,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
ancestor.identifier.name),
[self.maplikeOrSetlikeOrIterable.location,
ancestor.maplikeOrSetlikeOrIterable.location])
- for ancestorConsequential in ancestor.getConsequentialInterfaces():
- ancestorConsequential.interfacesBasedOnSelf.add(self)
# Deal with interfaces marked [Unforgeable], now that we have our full
# member list, except unforgeables pulled in from parents. We want to
@@ -1009,10 +1143,9 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
[self.location])
for m in self.members:
- if ((m.isMethod() and m.isJsonifier()) or
- m.identifier.name == "toJSON"):
+ if m.identifier.name == "toJSON":
raise WebIDLError("Unforgeable interface %s has a "
- "jsonifier so we won't be able to add "
+ "toJSON so we won't be able to add "
"one ourselves" % self.identifier.name,
[self.location, m.location])
@@ -1028,6 +1161,21 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
not hasattr(member, "originatingInterface")):
member.originatingInterface = self
+ for member in self.members:
+ if ((member.isMethod() and
+ member.getExtendedAttribute("CrossOriginCallable")) or
+ (member.isAttr() and
+ (member.getExtendedAttribute("CrossOriginReadable") or
+ member.getExtendedAttribute("CrossOriginWritable")))):
+ self.hasCrossOriginMembers = True
+ break
+
+ if self.hasCrossOriginMembers:
+ parent = self
+ while parent:
+ parent.hasDescendantWithCrossOriginMembers = True
+ parent = parent.parent
+
# Compute slot indices for our members before we pull in unforgeable
# members from our parent. Also, maplike/setlike declarations get a
# slot to hold their backing object.
@@ -1036,6 +1184,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
(member.getExtendedAttribute("StoreInSlot") or
member.getExtendedAttribute("Cached"))) or
member.isMaplikeOrSetlike()):
+ if self.isJSImplemented() and not member.isMaplikeOrSetlike():
+ raise WebIDLError("Interface %s is JS-implemented and we "
+ "don't support [Cached] or [StoreInSlot] "
+ "on JS-implemented interfaces" %
+ self.identifier.name,
+ [self.location, member.location])
if member.slotIndices is None:
member.slotIndices = dict()
member.slotIndices[self.identifier.name] = self.totalMembersInSlots
@@ -1044,13 +1198,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self._ownMembersInSlots += 1
if self.parent:
- # Make sure we don't shadow any of the [Unforgeable] attributes on
- # our ancestor interfaces. We don't have to worry about
- # consequential interfaces here, because those have already been
- # imported into the relevant .members lists. And we don't have to
- # worry about anything other than our parent, because it has already
- # imported its ancestors unforgeable attributes into its member
- # list.
+ # Make sure we don't shadow any of the [Unforgeable] attributes on our
+ # ancestor interfaces. We don't have to worry about mixins here, because
+ # those have already been imported into the relevant .members lists. And
+ # we don't have to worry about anything other than our parent, because it
+ # has already imported its ancestors' unforgeable attributes into its
+ # member list.
for unforgeableMember in (member for member in self.parent.members if
(member.isAttr() or member.isMethod()) and
member.isUnforgeable()):
@@ -1087,10 +1240,13 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
testInterface = testInterface.parent
# Ensure that there's at most one of each {named,indexed}
- # {getter,setter,creator,deleter}, at most one stringifier,
+ # {getter,setter,deleter}, at most one stringifier,
# and at most one legacycaller. Note that this last is not
# quite per spec, but in practice no one overloads
- # legacycallers.
+ # legacycallers. Also note that in practice we disallow
+ # indexed deleters, but it simplifies some other code to
+ # treat deleter analogously to getter/setter by
+ # prefixing it with "named".
specialMembersSeen = {}
for member in self.members:
if not member.isMethod():
@@ -1100,21 +1256,16 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
memberType = "getters"
elif member.isSetter():
memberType = "setters"
- elif member.isCreator():
- memberType = "creators"
elif member.isDeleter():
memberType = "deleters"
elif member.isStringifier():
memberType = "stringifiers"
- elif member.isJsonifier():
- memberType = "jsonifiers"
elif member.isLegacycaller():
memberType = "legacycallers"
else:
continue
- if (memberType != "stringifiers" and memberType != "legacycallers" and
- memberType != "jsonifiers"):
+ if (memberType != "stringifiers" and memberType != "legacycallers"):
if member.isNamed():
memberType = "named " + memberType
else:
@@ -1147,8 +1298,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
ancestor = ancestor.parent
if self._isOnGlobalProtoChain:
- # Make sure we have no named setters, creators, or deleters
- for memberType in ["setter", "creator", "deleter"]:
+ # Make sure we have no named setters or deleters
+ for memberType in ["setter", "deleter"]:
memberId = "named " + memberType + "s"
if memberId in specialMembersSeen:
raise WebIDLError("Interface with [Global] has a named %s" %
@@ -1172,16 +1323,21 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
parent = parent.parent
def validate(self):
- # We don't support consequential unforgeable interfaces. Need to check
- # this here, because in finish() an interface might not know yet that
- # it's consequential.
- if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
- raise WebIDLError(
- "%s is an unforgeable consequential interface" %
- self.identifier.name,
- [self.location] +
- list(i.location for i in
- (self.interfacesBasedOnSelf - {self})))
+
+ def checkDuplicateNames(member, name, attributeName):
+ for m in self.members:
+ if m.identifier.name == name:
+ raise WebIDLError("[%s=%s] has same name as interface member" %
+ (attributeName, name),
+ [member.location, m.location])
+ if m.isMethod() and m != member and name in m.aliases:
+ raise WebIDLError("conflicting [%s=%s] definitions" %
+ (attributeName, name),
+ [member.location, m.location])
+ if m.isAttr() and m != member and name in m.bindingAliases:
+ raise WebIDLError("conflicting [%s=%s] definitions" %
+ (attributeName, name),
+ [member.location, m.location])
# We also don't support inheriting from unforgeable interfaces.
if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
@@ -1192,6 +1348,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.identifier.name,
locations)
+ ctor = self.ctor()
+ if ctor is not None:
+ ctor.validate()
+ for namedCtor in self.namedConstructors:
+ namedCtor.validate()
+
indexedGetter = None
hasLengthAttribute = False
for member in self.members:
@@ -1278,23 +1440,22 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
raise WebIDLError("[Alias] must not be used on an "
"[Unforgeable] operation",
[member.location])
- for m in self.members:
- if m.identifier.name == alias:
- raise WebIDLError("[Alias=%s] has same name as "
- "interface member" % alias,
- [member.location, m.location])
- if m.isMethod() and m != member and alias in m.aliases:
- raise WebIDLError("duplicate [Alias=%s] definitions" %
- alias,
- [member.location, m.location])
+
+ checkDuplicateNames(member, alias, "Alias")
+
+ # Check that the name of a [BindingAlias] doesn't conflict with an
+ # interface member.
+ if member.isAttr():
+ for bindingAlias in member.bindingAliases:
+ checkDuplicateNames(member, bindingAlias, "BindingAlias")
+
# Conditional exposure makes no sense for interfaces with no
- # interface object, unless they're navigator properties.
+ # interface object.
# And SecureContext makes sense for interfaces with no interface object,
# since it is also propagated to interface members.
if (self.isExposedConditionally(exclusions=["SecureContext"]) and
- not self.hasInterfaceObject() and
- not self.isNavigatorProperty()):
+ not self.hasInterfaceObject()):
raise WebIDLError("Interface with no interface object is "
"exposed conditionally",
[self.location])
@@ -1308,7 +1469,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
if not indexedGetter:
raise WebIDLError("Interface with value iterator does not "
"support indexed properties",
- [self.location])
+ [self.location, iterableDecl.location])
if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
raise WebIDLError("Iterable type does not match indexed "
@@ -1319,7 +1480,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
if not hasLengthAttribute:
raise WebIDLError('Interface with value iterator does not '
'have an integer-typed "length" attribute',
- [self.location])
+ [self.location, iterableDecl.location])
else:
assert iterableDecl.isPairIterator()
if indexedGetter:
@@ -1328,15 +1489,10 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
[self.location, iterableDecl.location,
indexedGetter.location])
- def isExternal(self):
- return False
-
- def setIsConsequentialInterfaceOf(self, other):
- self._consequential = True
- self.interfacesBasedOnSelf.add(other)
-
- def isConsequential(self):
- return self._consequential
+ if indexedGetter and not hasLengthAttribute:
+ raise WebIDLError('Interface with an indexed getter does not have '
+ 'an integer-typed "length" attribute',
+ [self.location, indexedGetter.location])
def setCallback(self, value):
self._callback = value
@@ -1352,8 +1508,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
not self.isJSImplemented() and
# Not inheriting from another interface
not self.parent and
- # No consequential interfaces
- len(self.getConsequentialInterfaces()) == 0 and
# No attributes of any kinds
not any(m.isAttr() for m in self.members) and
# There is at least one regular operation, and all regular
@@ -1381,9 +1535,9 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
return (not self.isCallback() and not self.isNamespace()
and self.getUserData('hasConcreteDescendant', False))
- def addImplementedInterface(self, implementedInterface):
- assert(isinstance(implementedInterface, IDLInterface))
- self.implementedInterfaces.add(implementedInterface)
+ def addIncludedMixin(self, includedMixin):
+ assert(isinstance(includedMixin, IDLInterfaceMixin))
+ self.includedMixins.add(includedMixin)
def getInheritedInterfaces(self):
"""
@@ -1398,27 +1552,10 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
parentInterfaces.insert(0, self.parent)
return parentInterfaces
- def getConsequentialInterfaces(self):
- assert(self._finished)
- # The interfaces we implement directly
- consequentialInterfaces = set(self.implementedInterfaces)
-
- # And their inherited interfaces
- for iface in self.implementedInterfaces:
- consequentialInterfaces |= set(iface.getInheritedInterfaces())
-
- # And now collect up the consequential interfaces of all of those
- temp = set()
- for iface in consequentialInterfaces:
- temp |= iface.getConsequentialInterfaces()
-
- return consequentialInterfaces | temp
-
def findInterfaceLoopPoint(self, otherInterface):
"""
- Finds an interface, amongst our ancestors and consequential interfaces,
- that inherits from otherInterface or implements otherInterface
- directly. If there is no such interface, returns None.
+ Finds an interface amongst our ancestors that inherits from otherInterface.
+ If there is no such interface, returns None.
"""
if self.parent:
if self.parent == otherInterface:
@@ -1426,37 +1563,13 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
if loopPoint:
return loopPoint
- if otherInterface in self.implementedInterfaces:
- return self
- for iface in self.implementedInterfaces:
- loopPoint = iface.findInterfaceLoopPoint(otherInterface)
- if loopPoint:
- return loopPoint
return None
- def getExtendedAttribute(self, name):
- return self._extendedAttrDict.get(name, None)
-
def setNonPartial(self, location, parent, members):
assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
- if self._isKnownNonPartial:
- raise WebIDLError("Two non-partial definitions for the "
- "same %s" %
- ("interface" if self.isInterface()
- else "namespace"),
- [location, self.location])
- self._isKnownNonPartial = True
- # Now make it look like we were parsed at this new location, since
- # that's the place where the interface is "really" defined
- self.location = location
+ IDLInterfaceOrInterfaceMixinOrNamespace.setNonPartial(self, location, members)
assert not self.parent
self.parent = parent
- # Put the new members at the beginning
- self.members = members + self.members
-
- def addPartialInterface(self, partial):
- assert self.identifier.name == partial.identifier.name
- self._partialInterfaces.append(partial)
def getJSImplementation(self):
classId = self.getExtendedAttribute("JSImplementation")
@@ -1469,47 +1582,14 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
def isJSImplemented(self):
return bool(self.getJSImplementation())
- def isProbablyShortLivingObject(self):
+ def hasProbablyShortLivingWrapper(self):
current = self
while current:
- if current.getExtendedAttribute("ProbablyShortLivingObject"):
+ if current.getExtendedAttribute("ProbablyShortLivingWrapper"):
return True
current = current.parent
return False
- def isNavigatorProperty(self):
- naviProp = self.getExtendedAttribute("NavigatorProperty")
- if not naviProp:
- return False
- assert len(naviProp) == 1
- assert isinstance(naviProp, list)
- assert len(naviProp[0]) != 0
- return True
-
- def getNavigatorProperty(self):
- naviProp = self.getExtendedAttribute("NavigatorProperty")
- if not naviProp:
- return None
- assert len(naviProp) == 1
- assert isinstance(naviProp, list)
- assert len(naviProp[0]) != 0
- conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterfaceOrNamespace.conditionExtendedAttributes
- attr = IDLAttribute(self.location,
- IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
- IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
- True,
- extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
- navigatorObjectGetter=True)
- attr._exposureGlobalNames = self._exposureGlobalNames
- # We're abusing Constant a little bit here, because we need Cached. The
- # getter will create a new object every time, but we're never going to
- # clear the cached value.
- extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
- IDLExtendedAttribute(self.location, ("Cached", )),
- IDLExtendedAttribute(self.location, ("Constant", )) ]
- attr.addExtendedAttributes(extendedAttrs)
- return attr
-
def hasChildInterfaces(self):
return self._hasChildInterfaces
@@ -1518,7 +1598,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
def _getDependentObjects(self):
deps = set(self.members)
- deps.update(self.implementedInterfaces)
+ deps.update(self.includedMixins)
if self.parent:
deps.add(self.parent)
return deps
@@ -1526,18 +1606,19 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
def hasMembersInSlots(self):
return self._ownMembersInSlots != 0
- conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
- "SecureContext",
- "CheckAnyPermissions",
- "CheckAllPermissions" ]
+ conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func",
+ "SecureContext" ]
def isExposedConditionally(self, exclusions=[]):
return any(((not a in exclusions) and self.getExtendedAttribute(a)) for a in self.conditionExtendedAttributes)
class IDLInterface(IDLInterfaceOrNamespace):
def __init__(self, location, parentScope, name, parent, members,
- isKnownNonPartial):
+ isKnownNonPartial, classNameOverride=None,
+ toStringTag=None):
IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
- parent, members, isKnownNonPartial)
+ parent, members, isKnownNonPartial,
+ toStringTag)
+ self.classNameOverride = classNameOverride
def __str__(self):
return "Interface '%s'" % self.identifier.name
@@ -1545,6 +1626,11 @@ class IDLInterface(IDLInterfaceOrNamespace):
def isInterface(self):
return True
+ def getClassName(self):
+ if self.classNameOverride:
+ return self.classNameOverride
+ return self.identifier.name
+
def addExtendedAttributes(self, attrs):
for attr in attrs:
identifier = attr.identifier()
@@ -1561,86 +1647,46 @@ class IDLInterface(IDLInterfaceOrNamespace):
raise WebIDLError("[NoInterfaceObject] must take no arguments",
[attr.location])
- if self.ctor():
- raise WebIDLError("Constructor and NoInterfaceObject are incompatible",
- [self.location])
-
self._noInterfaceObject = True
- elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor":
- if identifier == "Constructor" and not self.hasInterfaceObject():
- raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
- [self.location])
-
- if identifier == "NamedConstructor" and not attr.hasValue():
+ elif identifier == "NamedConstructor":
+ if not attr.hasValue():
raise WebIDLError("NamedConstructor must either take an identifier or take a named argument list",
[attr.location])
- if identifier == "ChromeConstructor" and not self.hasInterfaceObject():
- raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
- [self.location])
args = attr.args() if attr.hasArgs() else []
- if self.identifier.name == "Promise":
- promiseType = BuiltinTypes[IDLBuiltinType.Types.any]
- else:
- promiseType = None
- retType = IDLWrapperType(self.location, self, promiseType)
+ retType = IDLWrapperType(self.location, self)
- if identifier == "Constructor" or identifier == "ChromeConstructor":
- name = "constructor"
- allowForbidden = True
- else:
- name = attr.value()
- allowForbidden = False
-
- methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
- allowForbidden=allowForbidden)
-
- method = IDLMethod(self.location, methodIdentifier, retType,
- args, static=True)
- # Constructors are always NewObject and are always
- # assumed to be able to throw (since there's no way to
- # indicate otherwise) and never have any other
- # extended attributes.
+ method = IDLConstructor(attr.location, args, attr.value())
+ method.reallyInit(self)
+
+ # Named constructors are always assumed to be able to
+ # throw (since there's no way to indicate otherwise).
method.addExtendedAttributes(
- [IDLExtendedAttribute(self.location, ("NewObject",)),
- IDLExtendedAttribute(self.location, ("Throws",))])
- if identifier == "ChromeConstructor":
- method.addExtendedAttributes(
- [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
-
- if identifier == "Constructor" or identifier == "ChromeConstructor":
- method.resolve(self)
- else:
- # We need to detect conflicts for NamedConstructors across
- # interfaces. We first call resolve on the parentScope,
- # which will merge all NamedConstructors with the same
- # identifier accross interfaces as overloads.
- method.resolve(self.parentScope)
-
- # Then we look up the identifier on the parentScope. If the
- # result is the same as the method we're adding then it
- # hasn't been added as an overload and it's the first time
- # we've encountered a NamedConstructor with that identifier.
- # If the result is not the same as the method we're adding
- # then it has been added as an overload and we need to check
- # whether the result is actually one of our existing
- # NamedConstructors.
- newMethod = self.parentScope.lookupIdentifier(method.identifier)
- if newMethod == method:
- self.namedConstructors.append(method)
- elif newMethod not in self.namedConstructors:
- raise WebIDLError("NamedConstructor conflicts with a NamedConstructor of a different interface",
- [method.location, newMethod.location])
- elif (identifier == "ArrayClass"):
- if not attr.noArguments():
- raise WebIDLError("[ArrayClass] must take no arguments",
- [attr.location])
- if self.parent:
- raise WebIDLError("[ArrayClass] must not be specified on "
- "an interface with inherited interfaces",
- [attr.location, self.location])
+ [IDLExtendedAttribute(self.location, ("Throws",))])
+
+ # We need to detect conflicts for NamedConstructors across
+ # interfaces. We first call resolve on the parentScope,
+ # which will merge all NamedConstructors with the same
+ # identifier accross interfaces as overloads.
+ method.resolve(self.parentScope)
+
+ # Then we look up the identifier on the parentScope. If the
+ # result is the same as the method we're adding then it
+ # hasn't been added as an overload and it's the first time
+ # we've encountered a NamedConstructor with that identifier.
+ # If the result is not the same as the method we're adding
+ # then it has been added as an overload and we need to check
+ # whether the result is actually one of our existing
+ # NamedConstructors.
+ newMethod = self.parentScope.lookupIdentifier(method.identifier)
+ if newMethod == method:
+ self.namedConstructors.append(method)
+ elif newMethod not in self.namedConstructors:
+ raise WebIDLError("NamedConstructor conflicts with a "
+ "NamedConstructor of a different interface",
+ [method.location, newMethod.location])
elif (identifier == "ExceptionClass"):
if not attr.noArguments():
raise WebIDLError("[ExceptionClass] must take no arguments",
@@ -1656,24 +1702,21 @@ class IDLInterface(IDLInterfaceOrNamespace):
self.globalNames = attr.args()
else:
self.globalNames = [self.identifier.name]
- self.parentScope.globalNames.update(self.globalNames)
- for globalName in self.globalNames:
- self.parentScope.globalNameMapping[globalName].add(self.identifier.name)
+ self.parentScope.addIfaceGlobalNames(self.identifier.name,
+ self.globalNames)
self._isOnGlobalProtoChain = True
- elif identifier == "PrimaryGlobal":
- if not attr.noArguments():
- raise WebIDLError("[PrimaryGlobal] must take no arguments",
+ elif identifier == "LegacyWindowAlias":
+ if attr.hasValue():
+ self.legacyWindowAliases = [attr.value()]
+ elif attr.hasArgs():
+ self.legacyWindowAliases = attr.args()
+ else:
+ raise WebIDLError("[%s] must either take an identifier "
+ "or take an identifier list" % identifier,
[attr.location])
- if self.parentScope.primaryGlobalAttr is not None:
- raise WebIDLError(
- "[PrimaryGlobal] specified twice",
- [attr.location,
- self.parentScope.primaryGlobalAttr.location])
- self.parentScope.primaryGlobalAttr = attr
- self.parentScope.primaryGlobalName = self.identifier.name
- self.parentScope.globalNames.add(self.identifier.name)
- self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name)
- self._isOnGlobalProtoChain = True
+ for alias in self.legacyWindowAliases:
+ unresolved = IDLUnresolvedIdentifier(attr.location, alias)
+ IDLObjectWithIdentifier(attr.location, self.parentScope, unresolved)
elif identifier == "SecureContext":
if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,
@@ -1690,11 +1733,12 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "OverrideBuiltins" or
identifier == "ChromeOnly" or
identifier == "Unforgeable" or
- identifier == "UnsafeInPrerendering" or
identifier == "LegacyEventInit" or
- identifier == "ProbablyShortLivingObject" or
+ identifier == "ProbablyShortLivingWrapper" or
identifier == "LegacyUnenumerableNamedProperties" or
- identifier == "NonOrdinaryGetPrototypeOf" or
+ identifier == "RunConstructorInCallerCompartment" or
+ identifier == "WantsEventListenerHooks" or
+ identifier == "Serializable" or
identifier == "Abstract" or
identifier == "Inline"):
# Known extended attributes that do not take values
@@ -1707,13 +1751,17 @@ class IDLInterface(IDLInterfaceOrNamespace):
elif (identifier == "Pref" or
identifier == "JSImplementation" or
identifier == "HeaderFile" or
- identifier == "NavigatorProperty" or
identifier == "Func" or
identifier == "Deprecated"):
# Known extended attributes that take a string value
if not attr.hasValue():
raise WebIDLError("[%s] must have a value" % identifier,
[attr.location])
+ elif identifier == "InstrumentedProps":
+ # Known extended attributes that take a list
+ if not attr.hasArgs():
+ raise WebIDLError("[%s] must have arguments" % identifier,
+ [attr.location])
else:
raise WebIDLError("Unknown extended attribute %s on interface" % identifier,
[attr.location])
@@ -1721,11 +1769,36 @@ class IDLInterface(IDLInterfaceOrNamespace):
attrlist = attr.listValue()
self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+ def validate(self):
+ IDLInterfaceOrNamespace.validate(self)
+ if self.parent and self.isSerializable() and not self.parent.isSerializable():
+ raise WebIDLError(
+ "Serializable interface inherits from non-serializable "
+ "interface. Per spec, that means the object should not be "
+ "serializable, so chances are someone made a mistake here "
+ "somewhere.",
+ [self.location, self.parent.location])
+
+ def isSerializable(self):
+ return self.getExtendedAttribute("Serializable")
+
+ def setNonPartial(self, location, parent, members):
+ # Before we do anything else, finish initializing any constructors that
+ # might be in "members", so we don't have partially-initialized objects
+ # hanging around. We couldn't do it before now because we needed to have
+ # to have the IDLInterface on hand to properly set the return type.
+ for member in members:
+ if isinstance(member, IDLConstructor):
+ member.reallyInit(self)
+
+ IDLInterfaceOrNamespace.setNonPartial(self, location, parent, members)
+
class IDLNamespace(IDLInterfaceOrNamespace):
def __init__(self, location, parentScope, name, members, isKnownNonPartial):
IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
- None, members, isKnownNonPartial)
+ None, members, isKnownNonPartial,
+ toStringTag=None)
def __str__(self):
return "Namespace '%s'" % self.identifier.name
@@ -1750,10 +1823,18 @@ class IDLNamespace(IDLInterfaceOrNamespace):
if not attr.hasValue():
raise WebIDLError("[%s] must have a value" % identifier,
[attr.location])
- elif identifier == "ProtoObjectHack":
+ elif (identifier == "ProtoObjectHack" or
+ identifier == "ChromeOnly"):
if not attr.noArguments():
raise WebIDLError("[%s] must not have arguments" % identifier,
[attr.location])
+ elif (identifier == "Pref" or
+ identifier == "HeaderFile" or
+ identifier == "Func"):
+ # Known extended attributes that take a string value
+ if not attr.hasValue():
+ raise WebIDLError("[%s] must have a value" % identifier,
+ [attr.location])
else:
raise WebIDLError("Unknown extended attribute %s on namespace" %
identifier,
@@ -1762,6 +1843,9 @@ class IDLNamespace(IDLInterfaceOrNamespace):
attrlist = attr.listValue()
self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+ def isSerializable(self):
+ return False
+
class IDLDictionary(IDLObjectWithScope):
def __init__(self, location, parentScope, name, parent, members):
@@ -1772,6 +1856,10 @@ class IDLDictionary(IDLObjectWithScope):
self.parent = parent
self._finished = False
self.members = list(members)
+ self._partialDictionaries = []
+ self._extendedAttrDict = {}
+ self.needsConversionToJS = False
+ self.needsConversionFromJS = False
IDLObjectWithScope.__init__(self, location, parentScope, name)
@@ -1808,6 +1896,11 @@ class IDLDictionary(IDLObjectWithScope):
# looking at them.
self.parent.finish(scope)
+ # Now go ahead and merge in our partial dictionaries.
+ for partial in self._partialDictionaries:
+ partial.finish(scope)
+ self.members.extend(partial.members)
+
for member in self.members:
member.resolve(self)
if not member.isComplete():
@@ -1815,7 +1908,7 @@ class IDLDictionary(IDLObjectWithScope):
assert member.type.isComplete()
# Members of a dictionary are sorted in lexicographic order
- self.members.sort(cmp=cmp, key=lambda x: x.identifier.name)
+ self.members.sort(key=lambda x: x.identifier.name)
inheritedMembers = []
ancestor = self.parent
@@ -1853,7 +1946,7 @@ class IDLDictionary(IDLObjectWithScope):
if (memberType.nullable() or
memberType.isSequence() or
- memberType.isMozMap()):
+ memberType.isRecord()):
return typeContainsDictionary(memberType.inner, dictionary)
if memberType.isDictionary():
@@ -1900,8 +1993,34 @@ class IDLDictionary(IDLObjectWithScope):
self.identifier.name,
[member.location] + locations)
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
+
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ if (identifier == "GenerateInitFromJSON" or
+ identifier == "GenerateInit"):
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must not have arguments" % identifier,
+ [attr.location])
+ self.needsConversionFromJS = True
+ elif (identifier == "GenerateConversionToJS" or
+ identifier == "GenerateToJSON"):
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must not have arguments" % identifier,
+ [attr.location])
+ # ToJSON methods require to-JS conversion, because we
+ # implement ToJSON by converting to a JS object and
+ # then using JSON.stringify.
+ self.needsConversionToJS = True
+ else:
+ raise WebIDLError("[%s] extended attribute not allowed on "
+ "dictionaries" % identifier,
+ [attr.location])
+
+ self._extendedAttrDict[identifier] = True
def _getDependentObjects(self):
deps = set(self.members)
@@ -1909,6 +2028,10 @@ class IDLDictionary(IDLObjectWithScope):
deps.add(self.parent)
return deps
+ def addPartialDictionary(self, partial):
+ assert self.identifier.name == partial.identifier.name
+ self._partialDictionaries.append(partial)
+
class IDLEnum(IDLObjectWithIdentifier):
def __init__(self, location, parentScope, name, values):
@@ -1935,7 +2058,10 @@ class IDLEnum(IDLObjectWithIdentifier):
return True
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ if len(attrs) != 0:
+ raise WebIDLError("There are no extended attributes that are "
+ "allowed on enums",
+ [attrs[0].location, self.location])
def _getDependentObjects(self):
return set()
@@ -1964,8 +2090,9 @@ class IDLType(IDLObject):
'domstring',
'bytestring',
'usvstring',
+ 'utf8string',
+ 'jsstring',
'object',
- 'date',
'void',
# Funny stuff
'interface',
@@ -1974,16 +2101,25 @@ class IDLType(IDLObject):
'callback',
'union',
'sequence',
- 'mozmap'
+ 'record',
+ 'promise',
)
def __init__(self, location, name):
IDLObject.__init__(self, location)
self.name = name
self.builtin = False
+ self.treatNullAsEmpty = False
+ self._clamp = False
+ self._enforceRange = False
+ self._allowShared = False
+ self._extendedAttrDict = {}
def __eq__(self, other):
- return other and self.builtin == other.builtin and self.name == other.name
+ return (other and self.builtin == other.builtin and self.name == other.name and
+ self._clamp == other.hasClamp() and self._enforceRange == other.hasEnforceRange() and
+ self.treatNullAsEmpty == other.treatNullAsEmpty and
+ self._allowShared == other.hasAllowShared())
def __ne__(self, other):
return not self == other
@@ -1991,6 +2127,14 @@ class IDLType(IDLObject):
def __str__(self):
return str(self.name)
+ def prettyName(self):
+ """
+ A name that looks like what this type is named in the IDL spec. By default
+ this is just our .name, but types that have more interesting spec
+ representations should override this.
+ """
+ return str(self.name)
+
def isType(self):
return True
@@ -2018,27 +2162,36 @@ class IDLType(IDLObject):
def isUSVString(self):
return False
+ def isUTF8String(self):
+ return False
+
+ def isJSString(self):
+ return False
+
def isVoid(self):
return self.name == "Void"
def isSequence(self):
return False
- def isMozMap(self):
+ def isRecord(self):
return False
- def isArrayBuffer(self):
+ def isReadableStream(self):
return False
- def isArrayBufferView(self):
+ def isArrayBuffer(self):
return False
- def isSharedArrayBuffer(self):
+ def isArrayBufferView(self):
return False
def isTypedArray(self):
return False
+ def isBufferSource(self):
+ return self.isArrayBuffer() or self.isArrayBufferView() or self.isTypedArray()
+
def isCallbackInterface(self):
return False
@@ -2054,12 +2207,9 @@ class IDLType(IDLObject):
def isSpiderMonkeyInterface(self):
""" Returns a boolean indicating whether this type is an 'interface'
- type that is implemented in Spidermonkey. At the moment, this
- only returns true for the types from the TypedArray spec. """
- return self.isInterface() and (self.isArrayBuffer() or
- self.isArrayBufferView() or
- self.isSharedArrayBuffer() or
- self.isTypedArray())
+ type that is implemented in SpiderMonkey. """
+ return self.isInterface() and (self.isBufferSource() or
+ self.isReadableStream())
def isDictionary(self):
return False
@@ -2070,9 +2220,6 @@ class IDLType(IDLObject):
def isAny(self):
return self.tag() == IDLType.Tags.any
- def isDate(self):
- return self.tag() == IDLType.Tags.date
-
def isObject(self):
return self.tag() == IDLType.Tags.object
@@ -2092,9 +2239,18 @@ class IDLType(IDLObject):
# Should only call this on float types
assert self.isFloat()
- def isSerializable(self):
+ def isJSONType(self):
return False
+ def hasClamp(self):
+ return self._clamp
+
+ def hasEnforceRange(self):
+ return self._enforceRange
+
+ def hasAllowShared(self):
+ return self._allowShared
+
def tag(self):
assert False # Override me!
@@ -2106,8 +2262,14 @@ class IDLType(IDLObject):
assert self.tag() == IDLType.Tags.callback
return self.nullable() and self.inner.callback._treatNonObjectAsNull
- def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ def withExtendedAttributes(self, attrs):
+ if len(attrs) > 0:
+ raise WebIDLError("Extended attributes on types only supported for builtins",
+ [attrs[0].location, self.location])
+ return self
+
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
def resolveType(self, parentScope):
pass
@@ -2128,9 +2290,9 @@ class IDLUnresolvedType(IDLType):
Unresolved types are interface types
"""
- def __init__(self, location, name, promiseInnerType=None):
+ def __init__(self, location, name, attrs=[]):
IDLType.__init__(self, location, name)
- self._promiseInnerType = promiseInnerType
+ self.extraTypeAttributes = attrs
def isComplete(self):
return False
@@ -2145,30 +2307,30 @@ class IDLUnresolvedType(IDLType):
assert obj
if obj.isType():
- print obj
+ print(obj)
assert not obj.isType()
if obj.isTypedef():
assert self.name.name == obj.identifier.name
typedefType = IDLTypedefType(self.location, obj.innerType,
obj.identifier)
assert not typedefType.isComplete()
- return typedefType.complete(scope)
+ return typedefType.complete(scope).withExtendedAttributes(self.extraTypeAttributes)
elif obj.isCallback() and not obj.isInterface():
assert self.name.name == obj.identifier.name
return IDLCallbackType(obj.location, obj)
- if self._promiseInnerType and not self._promiseInnerType.isComplete():
- self._promiseInnerType = self._promiseInnerType.complete(scope)
-
name = self.name.resolve(scope, None)
- return IDLWrapperType(self.location, obj, self._promiseInnerType)
+ return IDLWrapperType(self.location, obj)
+
+ def withExtendedAttributes(self, attrs):
+ return IDLUnresolvedType(self.location, self.name, attrs)
def isDistinguishableFrom(self, other):
raise TypeError("Can't tell whether an unresolved type is or is not "
"distinguishable from other things")
-class IDLParameterizedType(IDLType):
+class IDLParametrizedType(IDLType):
def __init__(self, location, name, innerType):
IDLType.__init__(self, location, name)
self.builtin = False
@@ -2191,22 +2353,25 @@ class IDLParameterizedType(IDLType):
return self.inner._getDependentObjects()
-class IDLNullableType(IDLParameterizedType):
+class IDLNullableType(IDLParametrizedType):
def __init__(self, location, innerType):
assert not innerType.isVoid()
assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
- name = innerType.name
- if innerType.isComplete():
- name += "OrNull"
- IDLParameterizedType.__init__(self, location, name, innerType)
+ IDLParametrizedType.__init__(self, location, None, innerType)
def __eq__(self, other):
return isinstance(other, IDLNullableType) and self.inner == other.inner
+ def __hash__(self):
+ return hash(self.inner)
+
def __str__(self):
return self.inner.__str__() + "OrNull"
+ def prettyName(self):
+ return self.inner.prettyName() + "?"
+
def nullable(self):
return True
@@ -2234,6 +2399,12 @@ class IDLNullableType(IDLParameterizedType):
def isUSVString(self):
return self.inner.isUSVString()
+ def isUTF8String(self):
+ return self.inner.isUTF8String()
+
+ def isJSString(self):
+ return self.inner.isJSString()
+
def isFloat(self):
return self.inner.isFloat()
@@ -2249,8 +2420,11 @@ class IDLNullableType(IDLParameterizedType):
def isSequence(self):
return self.inner.isSequence()
- def isMozMap(self):
- return self.inner.isMozMap()
+ def isRecord(self):
+ return self.inner.isRecord()
+
+ def isReadableStream(self):
+ return self.inner.isReadableStream()
def isArrayBuffer(self):
return self.inner.isArrayBuffer()
@@ -2258,9 +2432,6 @@ class IDLNullableType(IDLParameterizedType):
def isArrayBufferView(self):
return self.inner.isArrayBufferView()
- def isSharedArrayBuffer(self):
- return self.inner.isSharedArrayBuffer()
-
def isTypedArray(self):
return self.inner.isTypedArray()
@@ -2271,7 +2442,9 @@ class IDLNullableType(IDLParameterizedType):
return self.inner.isInterface()
def isPromise(self):
- return self.inner.isPromise()
+ # There is no such thing as a nullable Promise.
+ assert not self.inner.isPromise()
+ return False
def isCallbackInterface(self):
return self.inner.isCallbackInterface()
@@ -2285,14 +2458,29 @@ class IDLNullableType(IDLParameterizedType):
def isUnion(self):
return self.inner.isUnion()
- def isSerializable(self):
- return self.inner.isSerializable()
+ def isJSONType(self):
+ return self.inner.isJSONType()
+
+ def hasClamp(self):
+ return self.inner.hasClamp()
+
+ def hasEnforceRange(self):
+ return self.inner.hasEnforceRange()
+
+ def hasAllowShared(self):
+ return self.inner.hasAllowShared()
+
+ def isComplete(self):
+ return self.name is not None
def tag(self):
return self.inner.tag()
def complete(self, scope):
- self.inner = self.inner.complete(scope)
+ if not self.inner.isComplete():
+ self.inner = self.inner.complete(scope)
+ assert self.inner.isComplete()
+
if self.inner.nullable():
raise WebIDLError("The inner type of a nullable type must not be "
"a nullable type",
@@ -2302,23 +2490,36 @@ class IDLNullableType(IDLParameterizedType):
raise WebIDLError("The inner type of a nullable type must not "
"be a union type that itself has a nullable "
"type as a member type", [self.location])
+ if self.inner.isDOMString():
+ if self.inner.treatNullAsEmpty:
+ raise WebIDLError("[TreatNullAs] not allowed on a nullable DOMString",
+ [self.location, self.inner.location])
self.name = self.inner.name + "OrNull"
return self
def isDistinguishableFrom(self, other):
- if (other.nullable() or (other.isUnion() and other.hasNullableType) or
- other.isDictionary()):
+ if (other.nullable() or
+ other.isDictionary() or
+ (other.isUnion() and
+ (other.hasNullableType or other.hasDictionaryType()))):
# Can't tell which type null should become
return False
return self.inner.isDistinguishableFrom(other)
+ def withExtendedAttributes(self, attrs):
+ # See https://github.com/heycam/webidl/issues/827#issuecomment-565131350
+ # Allowing extended attributes to apply to a nullable type is an intermediate solution.
+ # A potential longer term solution is to introduce a null type and get rid of nullables.
+ # For example, we could do `([Clamp] long or null) foo` in the future.
+ return IDLNullableType(self.location, self.inner.withExtendedAttributes(attrs))
-class IDLSequenceType(IDLParameterizedType):
+
+class IDLSequenceType(IDLParametrizedType):
def __init__(self, location, parameterType):
assert not parameterType.isVoid()
- IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
+ IDLParametrizedType.__init__(self, location, parameterType.name, parameterType)
# Need to set self.name up front if our inner type is already complete,
# since in that case our .complete() won't be called.
if self.inner.isComplete():
@@ -2327,9 +2528,15 @@ class IDLSequenceType(IDLParameterizedType):
def __eq__(self, other):
return isinstance(other, IDLSequenceType) and self.inner == other.inner
+ def __hash__(self):
+ return hash(self.inner)
+
def __str__(self):
return self.inner.__str__() + "Sequence"
+ def prettyName(self):
+ return "sequence<%s>" % self.inner.prettyName()
+
def nullable(self):
return False
@@ -2348,6 +2555,12 @@ class IDLSequenceType(IDLParameterizedType):
def isUSVString(self):
return False
+ def isUTF8String(self):
+ return False
+
+ def isJSString(self):
+ return False
+
def isVoid(self):
return False
@@ -2363,8 +2576,8 @@ class IDLSequenceType(IDLParameterizedType):
def isEnum(self):
return False
- def isSerializable(self):
- return self.inner.isSerializable()
+ def isJSONType(self):
+ return self.inner.isJSONType()
def tag(self):
return IDLType.Tags.sequence
@@ -2381,36 +2594,45 @@ class IDLSequenceType(IDLParameterizedType):
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
return (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isDate() or other.isInterface() or
- other.isDictionary() or
- other.isCallback() or other.isMozMap())
+ other.isInterface() or other.isDictionary() or
+ other.isCallback() or other.isRecord())
-class IDLMozMapType(IDLParameterizedType):
- def __init__(self, location, parameterType):
- assert not parameterType.isVoid()
+class IDLRecordType(IDLParametrizedType):
+ def __init__(self, location, keyType, valueType):
+ assert keyType.isString()
+ assert keyType.isComplete()
+ assert not valueType.isVoid()
+
+ IDLParametrizedType.__init__(self, location, valueType.name, valueType)
+ self.keyType = keyType
- IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
# Need to set self.name up front if our inner type is already complete,
# since in that case our .complete() won't be called.
if self.inner.isComplete():
- self.name = self.inner.name + "MozMap"
+ self.name = self.keyType.name + self.inner.name + "Record"
def __eq__(self, other):
- return isinstance(other, IDLMozMapType) and self.inner == other.inner
+ return isinstance(other, IDLRecordType) and self.inner == other.inner
def __str__(self):
- return self.inner.__str__() + "MozMap"
+ return self.keyType.__str__() + self.inner.__str__() + "Record"
- def isMozMap(self):
+ def prettyName(self):
+ return "record<%s, %s>" % (self.keyType.prettyName(), self.inner.prettyName())
+
+ def isRecord(self):
return True
+ def isJSONType(self):
+ return self.inner.isJSONType()
+
def tag(self):
- return IDLType.Tags.mozmap
+ return IDLType.Tags.record
def complete(self, scope):
self.inner = self.inner.complete(scope)
- self.name = self.inner.name + "MozMap"
+ self.name = self.keyType.name + self.inner.name + "Record"
return self
def unroll(self):
@@ -2426,7 +2648,7 @@ class IDLMozMapType(IDLParameterizedType):
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
return (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isDate() or other.isNonCallbackInterface() or other.isSequence())
+ other.isNonCallbackInterface() or other.isSequence())
def isExposedInAllOf(self, exposureSet):
return self.inner.unroll().isExposedInAllOf(exposureSet)
@@ -2448,14 +2670,17 @@ class IDLUnionType(IDLType):
assert self.isComplete()
return self.name.__hash__()
+ def prettyName(self):
+ return "(" + " or ".join(m.prettyName() for m in self.memberTypes) + ")"
+
def isVoid(self):
return False
def isUnion(self):
return True
- def isSerializable(self):
- return all(m.isSerializable() for m in self.memberTypes)
+ def isJSONType(self):
+ return all(m.isJSONType() for m in self.memberTypes)
def includesRestrictedFloat(self):
return any(t.includesRestrictedFloat() for t in self.memberTypes)
@@ -2479,6 +2704,9 @@ class IDLUnionType(IDLType):
return typeName(type._identifier.object())
if isinstance(type, IDLObjectWithIdentifier):
return typeName(type.identifier)
+ if isinstance(type, IDLBuiltinType) and type.hasAllowShared():
+ assert type.isBufferSource()
+ return "MaybeShared" + type.name
return type.name
for (i, type) in enumerate(self.memberTypes):
@@ -2602,14 +2830,26 @@ class IDLTypedefType(IDLType):
def isUSVString(self):
return self.inner.isUSVString()
+ def isUTF8String(self):
+ return self.inner.isUTF8String()
+
+ def isJSString(self):
+ return self.inner.isJSString()
+
def isVoid(self):
return self.inner.isVoid()
+ def isJSONType(self):
+ return self.inner.isJSONType()
+
def isSequence(self):
return self.inner.isSequence()
- def isMozMap(self):
- return self.inner.isMozMap()
+ def isRecord(self):
+ return self.inner.isRecord()
+
+ def isReadableStream(self):
+ return self.inner.isReadableStream()
def isDictionary(self):
return self.inner.isDictionary()
@@ -2620,9 +2860,6 @@ class IDLTypedefType(IDLType):
def isArrayBufferView(self):
return self.inner.isArrayBufferView()
- def isSharedArrayBuffer(self):
- return self.inner.isSharedArrayBuffer()
-
def isTypedArray(self):
return self.inner.isTypedArray()
@@ -2658,12 +2895,17 @@ class IDLTypedefType(IDLType):
def _getDependentObjects(self):
return self.inner._getDependentObjects()
+ def withExtendedAttributes(self, attrs):
+ return IDLTypedefType(self.location, self.inner.withExtendedAttributes(attrs), self.name)
+
class IDLTypedef(IDLObjectWithIdentifier):
def __init__(self, location, parentScope, innerType, name):
+ # Set self.innerType first, because IDLObjectWithIdentifier.__init__
+ # will call our __str__, which wants to use it.
+ self.innerType = innerType
identifier = IDLUnresolvedIdentifier(location, name)
IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
- self.innerType = innerType
def __str__(self):
return "Typedef %s %s" % (self.identifier.name, self.innerType)
@@ -2679,26 +2921,30 @@ class IDLTypedef(IDLObjectWithIdentifier):
return True
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ if len(attrs) != 0:
+ raise WebIDLError("There are no extended attributes that are "
+ "allowed on typedefs",
+ [attrs[0].location, self.location])
def _getDependentObjects(self):
return self.innerType._getDependentObjects()
class IDLWrapperType(IDLType):
- def __init__(self, location, inner, promiseInnerType=None):
+ def __init__(self, location, inner):
IDLType.__init__(self, location, inner.identifier.name)
self.inner = inner
self._identifier = inner.identifier
self.builtin = False
- assert not promiseInnerType or inner.identifier.name == "Promise"
- self._promiseInnerType = promiseInnerType
def __eq__(self, other):
return (isinstance(other, IDLWrapperType) and
self._identifier == other._identifier and
self.builtin == other.builtin)
+ def __hash__(self):
+ return hash((self._identifier, self.builtin))
+
def __str__(self):
return str(self.name) + " (Wrapper)"
@@ -2720,6 +2966,12 @@ class IDLWrapperType(IDLType):
def isUSVString(self):
return False
+ def isUTF8String(self):
+ return False
+
+ def isJSString(self):
+ return False
+
def isVoid(self):
return False
@@ -2742,23 +2994,25 @@ class IDLWrapperType(IDLType):
def isEnum(self):
return isinstance(self.inner, IDLEnum)
- def isPromise(self):
- return (isinstance(self.inner, IDLInterface) and
- self.inner.identifier.name == "Promise")
-
- def promiseInnerType(self):
- assert self.isPromise()
- return self._promiseInnerType
-
- def isSerializable(self):
+ def isJSONType(self):
if self.isInterface():
if self.inner.isExternal():
return False
- return any(m.isMethod() and m.isJsonifier() for m in self.inner.members)
+ iface = self.inner
+ while iface:
+ if any(m.isMethod() and m.isToJSON() for m in iface.members):
+ return True
+ iface = iface.parent
+ return False
elif self.isEnum():
return True
elif self.isDictionary():
- return all(m.type.isSerializable() for m in self.inner.members)
+ dictionary = self.inner
+ while dictionary:
+ if not all(m.type.isJSONType() for m in dictionary.members):
+ return False
+ dictionary = dictionary.parent
+ return True
else:
raise WebIDLError("IDLWrapperType wraps type %s that we don't know if "
"is serializable" % type(self.inner), [self.location])
@@ -2781,8 +3035,6 @@ class IDLWrapperType(IDLType):
assert False
def isDistinguishableFrom(self, other):
- if self.isPromise():
- return False
if other.isPromise():
return False
if other.isUnion():
@@ -2792,11 +3044,11 @@ class IDLWrapperType(IDLType):
if self.isEnum():
return (other.isPrimitive() or other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord())
if self.isDictionary() and other.nullable():
return False
if (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isDate() or other.isSequence()):
+ other.isSequence()):
return True
if self.isDictionary():
return other.isNonCallbackInterface()
@@ -2814,7 +3066,7 @@ class IDLWrapperType(IDLType):
(self.isNonCallbackInterface() or
other.isNonCallbackInterface()))
if (other.isDictionary() or other.isCallback() or
- other.isMozMap()):
+ other.isRecord()):
return self.isNonCallbackInterface()
# Not much else |other| can be
@@ -2826,13 +3078,10 @@ class IDLWrapperType(IDLType):
return True
iface = self.inner
if iface.isExternal():
- # Let's say true, though ideally we'd only do this when
- # exposureSet contains the primary global's name.
+ # Let's say true, so we don't have to implement exposure mixins on
+ # external interfaces and sprinkle [Exposed=Window] on every single
+ # external interface declaration.
return True
- if (self.isPromise() and
- # Check the internal type
- not self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)):
- return False
return iface.exposureSet.issuperset(exposureSet)
def _getDependentObjects(self):
@@ -2860,6 +3109,48 @@ class IDLWrapperType(IDLType):
return set()
+class IDLPromiseType(IDLParametrizedType):
+ def __init__(self, location, innerType):
+ IDLParametrizedType.__init__(self, location, "Promise", innerType)
+
+ def __eq__(self, other):
+ return (isinstance(other, IDLPromiseType) and
+ self.promiseInnerType() == other.promiseInnerType())
+
+ def __str__(self):
+ return self.inner.__str__() + "Promise"
+
+ def prettyName(self):
+ return "Promise<%s>" % self.inner.prettyName()
+
+ def isPromise(self):
+ return True
+
+ def promiseInnerType(self):
+ return self.inner
+
+ def tag(self):
+ return IDLType.Tags.promise
+
+ def complete(self, scope):
+ self.inner = self.promiseInnerType().complete(scope)
+ return self
+
+ def unroll(self):
+ # We do not unroll our inner. Just stop at ourselves. That
+ # lets us add headers for both ourselves and our inner as
+ # needed.
+ return self
+
+ def isDistinguishableFrom(self, other):
+ # Promises are not distinguishable from anything.
+ return False
+
+ def isExposedInAllOf(self, exposureSet):
+ # Check the internal type
+ return self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)
+
+
class IDLBuiltinType(IDLType):
Types = enum(
@@ -2884,13 +3175,13 @@ class IDLBuiltinType(IDLType):
'domstring',
'bytestring',
'usvstring',
+ 'utf8string',
+ 'jsstring',
'object',
- 'date',
'void',
# Funny stuff
'ArrayBuffer',
'ArrayBufferView',
- 'SharedArrayBuffer',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
@@ -2899,7 +3190,8 @@ class IDLBuiltinType(IDLType):
'Int32Array',
'Uint32Array',
'Float32Array',
- 'Float64Array'
+ 'Float64Array',
+ 'ReadableStream',
)
TagLookup = {
@@ -2920,12 +3212,12 @@ class IDLBuiltinType(IDLType):
Types.domstring: IDLType.Tags.domstring,
Types.bytestring: IDLType.Tags.bytestring,
Types.usvstring: IDLType.Tags.usvstring,
+ Types.utf8string: IDLType.Tags.utf8string,
+ Types.jsstring: IDLType.Tags.jsstring,
Types.object: IDLType.Tags.object,
- Types.date: IDLType.Tags.date,
Types.void: IDLType.Tags.void,
Types.ArrayBuffer: IDLType.Tags.interface,
Types.ArrayBufferView: IDLType.Tags.interface,
- Types.SharedArrayBuffer: IDLType.Tags.interface,
Types.Int8Array: IDLType.Tags.interface,
Types.Uint8Array: IDLType.Tags.interface,
Types.Uint8ClampedArray: IDLType.Tags.interface,
@@ -2934,13 +3226,129 @@ class IDLBuiltinType(IDLType):
Types.Int32Array: IDLType.Tags.interface,
Types.Uint32Array: IDLType.Tags.interface,
Types.Float32Array: IDLType.Tags.interface,
- Types.Float64Array: IDLType.Tags.interface
+ Types.Float64Array: IDLType.Tags.interface,
+ Types.ReadableStream: IDLType.Tags.interface,
+ }
+
+ PrettyNames = {
+ Types.byte: "byte",
+ Types.octet: "octet",
+ Types.short: "short",
+ Types.unsigned_short: "unsigned short",
+ Types.long: "long",
+ Types.unsigned_long: "unsigned long",
+ Types.long_long: "long long",
+ Types.unsigned_long_long: "unsigned long long",
+ Types.boolean: "boolean",
+ Types.unrestricted_float: "unrestricted float",
+ Types.float: "float",
+ Types.unrestricted_double: "unrestricted double",
+ Types.double: "double",
+ Types.any: "any",
+ Types.domstring: "DOMString",
+ Types.bytestring: "ByteString",
+ Types.usvstring: "USVString",
+ Types.utf8string: "USVString", # That's what it is in spec terms
+ Types.jsstring: "USVString", # Again, that's what it is in spec terms
+ Types.object: "object",
+ Types.void: "void",
+ Types.ArrayBuffer: "ArrayBuffer",
+ Types.ArrayBufferView: "ArrayBufferView",
+ Types.Int8Array: "Int8Array",
+ Types.Uint8Array: "Uint8Array",
+ Types.Uint8ClampedArray: "Uint8ClampedArray",
+ Types.Int16Array: "Int16Array",
+ Types.Uint16Array: "Uint16Array",
+ Types.Int32Array: "Int32Array",
+ Types.Uint32Array: "Uint32Array",
+ Types.Float32Array: "Float32Array",
+ Types.Float64Array: "Float64Array",
+ Types.ReadableStream: "ReadableStream",
}
- def __init__(self, location, name, type):
+ def __init__(self, location, name, type, clamp=False, enforceRange=False, treatNullAsEmpty=False,
+ allowShared=False, attrLocation=[]):
+ """
+ The mutually exclusive clamp/enforceRange/treatNullAsEmpty/allowShared arguments are used
+ to create instances of this type with the appropriate attributes attached. Use .clamped(),
+ .rangeEnforced(), .withTreatNullAs() and .withAllowShared().
+
+ attrLocation is an array of source locations of these attributes for error reporting.
+ """
IDLType.__init__(self, location, name)
self.builtin = True
self._typeTag = type
+ self._clamped = None
+ self._rangeEnforced = None
+ self._withTreatNullAs = None
+ self._withAllowShared = None;
+ if self.isInteger():
+ if clamp:
+ self._clamp = True
+ self.name = "Clamped" + self.name
+ self._extendedAttrDict["Clamp"] = True
+ elif enforceRange:
+ self._enforceRange = True
+ self.name = "RangeEnforced" + self.name
+ self._extendedAttrDict["EnforceRange"] = True
+ elif clamp or enforceRange:
+ raise WebIDLError("Non-integer types cannot be [Clamp] or [EnforceRange]", attrLocation)
+ if self.isDOMString() or self.isUTF8String():
+ if treatNullAsEmpty:
+ self.treatNullAsEmpty = True
+ self.name = "NullIsEmpty" + self.name
+ self._extendedAttrDict["TreatNullAs"] = ["EmptyString"]
+ elif treatNullAsEmpty:
+ raise WebIDLError("Non-string types cannot be [TreatNullAs]", attrLocation)
+ if self.isBufferSource():
+ if allowShared:
+ self._allowShared = True
+ self._extendedAttrDict["AllowShared"] = True
+ elif allowShared:
+ raise WebIDLError("Types that are not buffer source types cannot be [AllowShared]", attrLocation)
+
+ def __str__(self):
+ if self._allowShared:
+ assert self.isBufferSource()
+ return "MaybeShared" + str(self.name)
+ return str(self.name)
+
+ def __eq__(self, other):
+ return other and self.location == other.location and self.name == other.name and self._typeTag == other._typeTag
+
+ def __hash__(self):
+ return hash((self.location, self.name, self._typeTag))
+
+ def prettyName(self):
+ return IDLBuiltinType.PrettyNames[self._typeTag]
+
+ def clamped(self, attrLocation):
+ if not self._clamped:
+ self._clamped = IDLBuiltinType(self.location, self.name,
+ self._typeTag, clamp=True,
+ attrLocation=attrLocation)
+ return self._clamped
+
+ def rangeEnforced(self, attrLocation):
+ if not self._rangeEnforced:
+ self._rangeEnforced = IDLBuiltinType(self.location, self.name,
+ self._typeTag, enforceRange=True,
+ attrLocation=attrLocation)
+ return self._rangeEnforced
+
+ def withTreatNullAs(self, attrLocation):
+ if not self._withTreatNullAs:
+ self._withTreatNullAs = IDLBuiltinType(self.location, self.name,
+ self._typeTag, treatNullAsEmpty=True,
+ attrLocation=attrLocation)
+ return self._withTreatNullAs
+
+ def withAllowShared(self, attrLocation):
+ if not self._withAllowShared:
+ self._withAllowShared = IDLBuiltinType(self.location, self.name,
+ self._typeTag, allowShared=True,
+ attrLocation=attrLocation)
+ return self._withAllowShared
def isPrimitive(self):
return self._typeTag <= IDLBuiltinType.Types.double
@@ -2954,7 +3362,9 @@ 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.utf8string or
+ self._typeTag == IDLBuiltinType.Types.jsstring)
def isByteString(self):
return self._typeTag == IDLBuiltinType.Types.bytestring
@@ -2965,6 +3375,12 @@ class IDLBuiltinType(IDLType):
def isUSVString(self):
return self._typeTag == IDLBuiltinType.Types.usvstring
+ def isUTF8String(self):
+ return self._typeTag == IDLBuiltinType.Types.utf8string
+
+ def isJSString(self):
+ return self._typeTag == IDLBuiltinType.Types.jsstring
+
def isInteger(self):
return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
@@ -2974,21 +3390,21 @@ class IDLBuiltinType(IDLType):
def isArrayBufferView(self):
return self._typeTag == IDLBuiltinType.Types.ArrayBufferView
- def isSharedArrayBuffer(self):
- return self._typeTag == IDLBuiltinType.Types.SharedArrayBuffer
-
def isTypedArray(self):
return (self._typeTag >= IDLBuiltinType.Types.Int8Array and
self._typeTag <= IDLBuiltinType.Types.Float64Array)
+ def isReadableStream(self):
+ return self._typeTag == IDLBuiltinType.Types.ReadableStream
+
def isInterface(self):
# TypedArray things are interface types per the TypedArray spec,
# but we handle them as builtins because SpiderMonkey implements
# all of it internally.
return (self.isArrayBuffer() or
self.isArrayBufferView() or
- self.isSharedArrayBuffer() or
- self.isTypedArray())
+ self.isTypedArray() or
+ self.isReadableStream())
def isNonCallbackInterface(self):
# All the interfaces we can be are non-callback
@@ -3005,8 +3421,8 @@ class IDLBuiltinType(IDLType):
return (self._typeTag == IDLBuiltinType.Types.unrestricted_float or
self._typeTag == IDLBuiltinType.Types.unrestricted_double)
- def isSerializable(self):
- return self.isPrimitive() or self.isString() or self.isDate()
+ def isJSONType(self):
+ return self.isPrimitive() or self.isString() or self.isObject()
def includesRestrictedFloat(self):
return self.isFloat() and not self.isUnrestricted()
@@ -3024,27 +3440,22 @@ class IDLBuiltinType(IDLType):
return (other.isNumeric() or other.isString() or other.isEnum() or
other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord())
if self.isNumeric():
return (other.isBoolean() or other.isString() or other.isEnum() or
other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord())
if self.isString():
return (other.isPrimitive() or other.isInterface() or
other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord())
if self.isAny():
# Can't tell "any" apart from anything
return False
if self.isObject():
return other.isPrimitive() or other.isString() or other.isEnum()
- if self.isDate():
- return (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isInterface() or other.isCallback() or
- other.isDictionary() or other.isSequence() or
- other.isMozMap())
if self.isVoid():
return not other.isVoid()
# Not much else we could be!
@@ -3052,12 +3463,12 @@ class IDLBuiltinType(IDLType):
# Like interfaces, but we know we're not a callback
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate() or
+ other.isSequence() or other.isRecord() or
(other.isInterface() and (
# ArrayBuffer is distinguishable from everything
# that's not an ArrayBuffer or a callback interface
(self.isArrayBuffer() and not other.isArrayBuffer()) or
- (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or
+ (self.isReadableStream() and not other.isReadableStream()) or
# ArrayBufferView is distinguishable from everything
# that's not an ArrayBufferView or typed array.
(self.isArrayBufferView() and not other.isArrayBufferView() and
@@ -3071,6 +3482,54 @@ class IDLBuiltinType(IDLType):
def _getDependentObjects(self):
return set()
+ def withExtendedAttributes(self, attrs):
+ ret = self
+ for attribute in attrs:
+ identifier = attribute.identifier()
+ if identifier == "Clamp":
+ if not attribute.noArguments():
+ raise WebIDLError("[Clamp] must take no arguments",
+ [attribute.location])
+ if ret.hasEnforceRange() or self._enforceRange:
+ raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
+ [self.location, attribute.location])
+ ret = self.clamped([self.location, attribute.location])
+ elif identifier == "EnforceRange":
+ if not attribute.noArguments():
+ raise WebIDLError("[EnforceRange] must take no arguments",
+ [attribute.location])
+ if ret.hasClamp() or self._clamp:
+ raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
+ [self.location, attribute.location])
+ ret = self.rangeEnforced([self.location, attribute.location])
+ elif identifier == "TreatNullAs":
+ if not (self.isDOMString() or self.isUTF8String()):
+ raise WebIDLError("[TreatNullAs] only allowed on DOMStrings and UTF8Strings",
+ [self.location, attribute.location])
+ assert not self.nullable()
+ if not attribute.hasValue():
+ raise WebIDLError("[TreatNullAs] must take an identifier argument",
+ [attribute.location])
+ value = attribute.value()
+ if value != 'EmptyString':
+ raise WebIDLError("[TreatNullAs] must take the identifier "
+ "'EmptyString', not '%s'" % value,
+ [attribute.location])
+ ret = self.withTreatNullAs([self.location, attribute.location])
+ elif identifier == "AllowShared":
+ if not attribute.noArguments():
+ raise WebIDLError("[AllowShared] must take no arguments",
+ [attribute.location])
+ if not self.isBufferSource():
+ raise WebIDLError("[AllowShared] only allowed on buffer source types",
+ [self.location, attribute.location])
+ ret = self.withAllowShared([self.location, attribute.location])
+
+ else:
+ raise WebIDLError("Unhandled extended attribute on type",
+ [self.location, attribute.location])
+ return ret
+
BuiltinTypes = {
IDLBuiltinType.Types.byte:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte",
@@ -3123,12 +3582,15 @@ BuiltinTypes = {
IDLBuiltinType.Types.usvstring:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "USVString",
IDLBuiltinType.Types.usvstring),
+ IDLBuiltinType.Types.utf8string:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UTF8String",
+ IDLBuiltinType.Types.utf8string),
+ IDLBuiltinType.Types.jsstring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "JSString",
+ IDLBuiltinType.Types.jsstring),
IDLBuiltinType.Types.object:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object",
IDLBuiltinType.Types.object),
- IDLBuiltinType.Types.date:
- IDLBuiltinType(BuiltinLocation("<builtin type>"), "Date",
- IDLBuiltinType.Types.date),
IDLBuiltinType.Types.void:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Void",
IDLBuiltinType.Types.void),
@@ -3138,9 +3600,6 @@ BuiltinTypes = {
IDLBuiltinType.Types.ArrayBufferView:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBufferView",
IDLBuiltinType.Types.ArrayBufferView),
- IDLBuiltinType.Types.SharedArrayBuffer:
- IDLBuiltinType(BuiltinLocation("<builtin type>"), "SharedArrayBuffer",
- IDLBuiltinType.Types.SharedArrayBuffer),
IDLBuiltinType.Types.Int8Array:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int8Array",
IDLBuiltinType.Types.Int8Array),
@@ -3167,7 +3626,10 @@ BuiltinTypes = {
IDLBuiltinType.Types.Float32Array),
IDLBuiltinType.Types.Float64Array:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
- IDLBuiltinType.Types.Float64Array)
+ IDLBuiltinType.Types.Float64Array),
+ IDLBuiltinType.Types.ReadableStream:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ReadableStream",
+ IDLBuiltinType.Types.ReadableStream),
}
@@ -3184,7 +3646,7 @@ integerTypeSizes = {
def matchIntegerValueToType(value):
- for type, extremes in integerTypeSizes.items():
+ for type, extremes in list(integerTypeSizes.items()):
(min, max) = extremes
if value <= max and value >= min:
return BuiltinTypes[type]
@@ -3263,7 +3725,7 @@ class IDLValue(IDLObject):
elif self.type.isString() and type.isEnum():
# Just keep our string, but make sure it's a valid value for this enum
enum = type.unroll().inner
- if self.value not in enum.values():
+ if self.value not in list(enum.values()):
raise WebIDLError("'%s' is not a valid default value for enum %s"
% (self.value, enum.identifier.name),
[location, enum.location])
@@ -3282,8 +3744,13 @@ class IDLValue(IDLObject):
# extra normalization step.
assert self.type.isDOMString()
return self
- elif self.type.isString() and type.isByteString():
- # Allow ByteStrings to use a default value like DOMString.
+ elif self.type.isDOMString() and type.treatNullAsEmpty:
+ # 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() or type.isJSString() or type.isUTF8String()):
+ # Allow ByteStrings, UTF8String, 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.
@@ -3312,8 +3779,6 @@ class IDLNullValue(IDLObject):
def coerceToType(self, type, location):
if (not isinstance(type, IDLNullableType) and
not (type.isUnion() and type.hasNullableType) and
- not (type.isUnion() and type.hasDictionaryType()) and
- not type.isDictionary() and
not type.isAny()):
raise WebIDLError("Cannot coerce null value to type %s." % type,
[location])
@@ -3362,6 +3827,35 @@ class IDLEmptySequenceValue(IDLObject):
return set()
+class IDLDefaultDictionaryValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if type.isUnion():
+ # We use the flat member types here, because if we have a nullable
+ # member type, or a nested union, we want the type the value
+ # actually coerces to, not the nullable or nested union type.
+ for subtype in type.unroll().flatMemberTypes:
+ try:
+ return self.coerceToType(subtype, location)
+ except:
+ pass
+
+ if not type.isDictionary():
+ raise WebIDLError("Cannot coerce default dictionary value to type %s." % type,
+ [location])
+
+ defaultDictionaryValue = IDLDefaultDictionaryValue(self.location)
+ defaultDictionaryValue.type = type
+ return defaultDictionaryValue
+
+ def _getDependentObjects(self):
+ return set()
+
+
class IDLUndefinedValue(IDLObject):
def __init__(self, location):
IDLObject.__init__(self, location)
@@ -3437,10 +3931,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
return self._extendedAttrDict.get(name, None)
def finish(self, scope):
- # We better be exposed _somewhere_.
- if (len(self._exposureGlobalNames) == 0):
- print self.identifier.name
- assert len(self._exposureGlobalNames) != 0
IDLExposureMixins.finish(self, scope)
def validate(self):
@@ -3456,6 +3946,14 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
raise WebIDLError("A [NewObject] method is not idempotent, "
"so it has to depend on something other than DOM state.",
[self.location])
+ if (self.getExtendedAttribute("Cached") or
+ self.getExtendedAttribute("StoreInSlot")):
+ raise WebIDLError("A [NewObject] attribute shouldnt be "
+ "[Cached] or [StoreInSlot], since the point "
+ "of those is to keep returning the same "
+ "thing across multiple calls, which is not "
+ "what [NewObject] does.",
+ [self.location])
def _setDependsOn(self, dependsOn):
if self.dependsOn != "Everything":
@@ -3483,6 +3981,11 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
[self.location])
self.aliases.append(alias)
+ def _addBindingAlias(self, bindingAlias):
+ if bindingAlias in self.bindingAliases:
+ raise WebIDLError("Duplicate [BindingAlias=%s] on attribute" % bindingAlias,
+ [self.location])
+ self.bindingAliases.append(bindingAlias)
class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
@@ -3527,8 +4030,10 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
(member.identifier.name,
self.maplikeOrSetlikeOrIterableType),
[self.location, member.location])
- # Check that there are no disallowed non-method members
- if (isAncestor or (member.isAttr() or member.isConst()) and
+ # Check that there are no disallowed non-method members.
+ # Ancestor members are always disallowed here; own members
+ # are disallowed only if they're non-methods.
+ if ((isAncestor or member.isAttr() or member.isConst()) and
member.identifier.name in self.disallowedNonMethodNames):
raise WebIDLError("Member '%s' conflicts "
"with reserved %s method." %
@@ -3601,6 +4106,11 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
if isIteratorAlias:
method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
+ # Methods generated for iterables should be enumerable, but the ones for
+ # maplike/setlike should not be.
+ if not self.isIterable():
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NonEnumerable",))])
members.append(method)
def resolve(self, parentScope):
@@ -3724,12 +4234,17 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
specification during parsing.
"""
# Both maplike and setlike have a size attribute
- members.append(IDLAttribute(self.location,
- IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
- BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
- True,
- maplikeOrSetlike=self))
+ sizeAttr = IDLAttribute(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
+ BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
+ True,
+ maplikeOrSetlike=self)
+ # This should be non-enumerable.
+ sizeAttr.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NonEnumerable",))])
+ members.append(sizeAttr)
self.reserved_ro_names = ["size"]
+ self.disallowedMemberNames.append("size")
# object entries()
self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
@@ -3820,6 +4335,9 @@ class IDLConst(IDLInterfaceMember):
if type.isDictionary():
raise WebIDLError("A constant cannot be of a dictionary type",
[self.location])
+ if type.isRecord():
+ raise WebIDLError("A constant cannot be of a record type",
+ [self.location])
self.type = type
self.value = value
@@ -3860,7 +4378,9 @@ class IDLConst(IDLInterfaceMember):
elif (identifier == "Pref" or
identifier == "ChromeOnly" or
identifier == "Func" or
- identifier == "SecureContext"):
+ identifier == "SecureContext" or
+ identifier == "NonEnumerable" or
+ identifier == "NeedsWindowsUndef"):
# Known attributes that we don't need to do anything with here
pass
else:
@@ -3875,7 +4395,7 @@ class IDLConst(IDLInterfaceMember):
class IDLAttribute(IDLInterfaceMember):
def __init__(self, location, identifier, type, readonly, inherit=False,
static=False, stringifier=False, maplikeOrSetlike=None,
- extendedAttrDict=None, navigatorObjectGetter=False):
+ extendedAttrDict=None):
IDLInterfaceMember.__init__(self, location, identifier,
IDLInterfaceMember.Tags.Attr,
extendedAttrDict=extendedAttrDict)
@@ -3888,14 +4408,12 @@ class IDLAttribute(IDLInterfaceMember):
self.lenientThis = False
self._unforgeable = False
self.stringifier = stringifier
- self.enforceRange = False
- self.clamp = False
self.slotIndices = None
assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
self.maplikeOrSetlike = maplikeOrSetlike
self.dependsOn = "Everything"
self.affects = "Everything"
- self.navigatorObjectGetter = navigatorObjectGetter
+ self.bindingAliases = []
if static and identifier.name == "prototype":
raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
@@ -3925,14 +4443,18 @@ class IDLAttribute(IDLInterfaceMember):
assert not isinstance(t.name, IDLUnresolvedIdentifier)
self.type = t
+ if self.readonly and (self.type.hasClamp() or self.type.hasEnforceRange() or
+ self.type.hasAllowShared() or self.type.treatNullAsEmpty):
+ raise WebIDLError("A readonly attribute cannot be [Clamp] or [EnforceRange] or [AllowShared]",
+ [self.location])
if self.type.isDictionary() and not self.getExtendedAttribute("Cached"):
raise WebIDLError("An attribute cannot be of a dictionary type",
[self.location])
if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
raise WebIDLError("A non-cached attribute cannot be of a sequence "
"type", [self.location])
- if self.type.isMozMap() and not self.getExtendedAttribute("Cached"):
- raise WebIDLError("A non-cached attribute cannot be of a MozMap "
+ if self.type.isRecord() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("A non-cached attribute cannot be of a record "
"type", [self.location])
if self.type.isUnion():
for f in self.type.unroll().flatMemberTypes:
@@ -3948,25 +4470,30 @@ class IDLAttribute(IDLInterfaceMember):
"one of its member types's member "
"types, and so on) is a sequence "
"type", [self.location, f.location])
- if f.isMozMap():
+ if f.isRecord():
raise WebIDLError("An attribute cannot be of a union "
"type if one of its member types (or "
"one of its member types's member "
- "types, and so on) is a MozMap "
+ "types, and so on) is a record "
"type", [self.location, f.location])
if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
raise WebIDLError("An attribute with [PutForwards] must have an "
"interface type as its type", [self.location])
- if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
+ if (not self.type.isInterface() and
+ self.getExtendedAttribute("SameObject")):
raise WebIDLError("An attribute with [SameObject] must have an "
"interface type as its type", [self.location])
+ if self.type.isPromise() and not self.readonly:
+ raise WebIDLError("Promise-returning attributes must be readonly",
+ [self.location])
+
def validate(self):
def typeContainsChromeOnlyDictionaryMember(type):
if (type.nullable() or
type.isSequence() or
- type.isMozMap()):
+ type.isRecord()):
return typeContainsChromeOnlyDictionaryMember(type.inner)
if type.isUnion():
@@ -4012,27 +4539,42 @@ class IDLAttribute(IDLInterfaceMember):
[self.location, location])
if self.getExtendedAttribute("Frozen"):
if (not self.type.isSequence() and not self.type.isDictionary() and
- not self.type.isMozMap()):
+ not self.type.isRecord()):
raise WebIDLError("[Frozen] is only allowed on "
"sequence-valued, dictionary-valued, and "
- "MozMap-valued attributes",
+ "record-valued attributes",
[self.location])
if not self.type.unroll().isExposedInAllOf(self.exposureSet):
raise WebIDLError("Attribute returns a type that is not exposed "
"everywhere where the attribute is exposed",
[self.location])
+ if self.getExtendedAttribute("CEReactions"):
+ if self.readonly:
+ raise WebIDLError("[CEReactions] is not allowed on "
+ "readonly attributes",
+ [self.location])
def handleExtendedAttribute(self, attr):
identifier = attr.identifier()
- if identifier == "SetterThrows" and self.readonly:
+ if ((identifier == "SetterThrows" or identifier == "SetterCanOOM" or
+ identifier == "SetterNeedsSubjectPrincipal")
+ and self.readonly):
raise WebIDLError("Readonly attributes must not be flagged as "
- "[SetterThrows]",
+ "[%s]" % identifier,
[self.location])
- elif (((identifier == "Throws" or identifier == "GetterThrows") and
+ elif identifier == "BindingAlias":
+ if not attr.hasValue():
+ raise WebIDLError("[BindingAlias] takes an identifier or string",
+ [attr.location])
+ self._addBindingAlias(attr.value())
+ elif (((identifier == "Throws" or identifier == "GetterThrows" or
+ identifier == "CanOOM" or identifier == "GetterCanOOM") and
self.getExtendedAttribute("StoreInSlot")) or
(identifier == "StoreInSlot" and
(self.getExtendedAttribute("Throws") or
- self.getExtendedAttribute("GetterThrows")))):
+ self.getExtendedAttribute("GetterThrows") or
+ self.getExtendedAttribute("CanOOM") or
+ self.getExtendedAttribute("GetterCanOOM")))):
raise WebIDLError("Throwing things can't be [StoreInSlot]",
[attr.location])
elif identifier == "LenientThis":
@@ -4066,6 +4608,10 @@ class IDLAttribute(IDLInterfaceMember):
if not self.readonly:
raise WebIDLError("[PutForwards] is only allowed on readonly "
"attributes", [attr.location, self.location])
+ if self.type.isPromise():
+ raise WebIDLError("[PutForwards] is not allowed on "
+ "Promise-typed attributes",
+ [attr.location, self.location])
if self.isStatic():
raise WebIDLError("[PutForwards] is only allowed on non-static "
"attributes", [attr.location, self.location])
@@ -4083,6 +4629,10 @@ class IDLAttribute(IDLInterfaceMember):
if not self.readonly:
raise WebIDLError("[Replaceable] is only allowed on readonly "
"attributes", [attr.location, self.location])
+ if self.type.isPromise():
+ raise WebIDLError("[Replaceable] is not allowed on "
+ "Promise-typed attributes",
+ [attr.location, self.location])
if self.isStatic():
raise WebIDLError("[Replaceable] is only allowed on non-static "
"attributes", [attr.location, self.location])
@@ -4097,6 +4647,10 @@ class IDLAttribute(IDLInterfaceMember):
if not self.readonly:
raise WebIDLError("[LenientSetter] is only allowed on readonly "
"attributes", [attr.location, self.location])
+ if self.type.isPromise():
+ raise WebIDLError("[LenientSetter] is not allowed on "
+ "Promise-typed attributes",
+ [attr.location, self.location])
if self.isStatic():
raise WebIDLError("[LenientSetter] is only allowed on non-static "
"attributes", [attr.location, self.location])
@@ -4116,16 +4670,6 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("[LenientFloat] used on an attribute with a "
"non-restricted-float type",
[attr.location, self.location])
- elif identifier == "EnforceRange":
- if self.readonly:
- raise WebIDLError("[EnforceRange] used on a readonly attribute",
- [attr.location, self.location])
- self.enforceRange = True
- elif identifier == "Clamp":
- if self.readonly:
- raise WebIDLError("[Clamp] used on a readonly attribute",
- [attr.location, self.location])
- self.clamp = True
elif identifier == "StoreInSlot":
if self.getExtendedAttribute("Cached"):
raise WebIDLError("[StoreInSlot] and [Cached] must not be "
@@ -4138,7 +4682,7 @@ class IDLAttribute(IDLInterfaceMember):
[attr.location, self.location])
elif (identifier == "CrossOriginReadable" or
identifier == "CrossOriginWritable"):
- if not attr.noArguments() and identifier == "CrossOriginReadable":
+ if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,
[attr.location])
if self.isStatic():
@@ -4191,18 +4735,30 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("[Unscopable] is only allowed on non-static "
"attributes and operations",
[attr.location, self.location])
+ elif identifier == "CEReactions":
+ if not attr.noArguments():
+ raise WebIDLError("[CEReactions] must take no arguments",
+ [attr.location])
elif (identifier == "Pref" or
identifier == "Deprecated" or
identifier == "SetterThrows" or
identifier == "Throws" or
identifier == "GetterThrows" or
+ identifier == "SetterCanOOM" or
+ identifier == "CanOOM" or
+ identifier == "GetterCanOOM" or
identifier == "ChromeOnly" or
identifier == "Func" or
identifier == "SecureContext" or
identifier == "Frozen" or
identifier == "NewObject" or
- identifier == "UnsafeInPrerendering" or
- identifier == "BinaryName"):
+ identifier == "NeedsSubjectPrincipal" or
+ identifier == "SetterNeedsSubjectPrincipal" or
+ identifier == "GetterNeedsSubjectPrincipal" or
+ identifier == "NeedsCallerType" or
+ identifier == "ReturnValueNeedsContainsHack" or
+ identifier == "BinaryName" or
+ identifier == "NonEnumerable"):
# Known attributes that we don't need to do anything with here
pass
else:
@@ -4215,10 +4771,6 @@ class IDLAttribute(IDLInterfaceMember):
self.type.resolveType(parentScope)
IDLObjectWithIdentifier.resolve(self, parentScope)
- def addExtendedAttributes(self, attrs):
- attrs = self.checkForStringHandlingExtendedAttributes(attrs)
- IDLInterfaceMember.addExtendedAttributes(self, attrs)
-
def hasLenientThis(self):
return self.lenientThis
@@ -4236,9 +4788,43 @@ class IDLAttribute(IDLInterfaceMember):
def _getDependentObjects(self):
return set([self.type])
+ def expand(self, members):
+ assert self.stringifier
+ if not self.type.isDOMString() and not self.type.isUSVString():
+ raise WebIDLError("The type of a stringifer attribute must be "
+ "either DOMString or USVString",
+ [self.location])
+ identifier = IDLUnresolvedIdentifier(self.location, "__stringifier",
+ allowDoubleUnderscore=True)
+ method = IDLMethod(self.location,
+ identifier,
+ returnType=self.type, arguments=[],
+ stringifier=True, underlyingAttr=self)
+ allowedExtAttrs = ["Throws", "NeedsSubjectPrincipal", "Pure"]
+ # Safe to ignore these as they are only meaningful for attributes
+ attributeOnlyExtAttrs = [
+ "CEReactions",
+ "CrossOriginWritable",
+ "SetterThrows",
+ ]
+ for (key, value) in list(self._extendedAttrDict.items()):
+ if key in allowedExtAttrs:
+ if value is not True:
+ raise WebIDLError("[%s] with a value is currently "
+ "unsupported in stringifier attributes, "
+ "please file a bug to add support" % key,
+ [self.location])
+ method.addExtendedAttributes([IDLExtendedAttribute(self.location, (key,))])
+ elif not key in attributeOnlyExtAttrs:
+ raise WebIDLError("[%s] is currently unsupported in "
+ "stringifier attributes, please file a bug "
+ "to add support" % key,
+ [self.location])
+ members.append(method)
+
class IDLArgument(IDLObjectWithIdentifier):
- def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
+ def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False, allowTypeAttributes=False):
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
assert isinstance(type, IDLType)
@@ -4249,41 +4835,25 @@ class IDLArgument(IDLObjectWithIdentifier):
self.variadic = variadic
self.dictionaryMember = dictionaryMember
self._isComplete = False
- self.enforceRange = False
- self.clamp = False
self._allowTreatNonCallableAsNull = False
self._extendedAttrDict = {}
+ self.allowTypeAttributes = allowTypeAttributes
assert not variadic or optional
assert not variadic or not defaultValue
def addExtendedAttributes(self, attrs):
- attrs = self.checkForStringHandlingExtendedAttributes(
- attrs,
- isDictionaryMember=self.dictionaryMember,
- isOptional=self.optional)
for attribute in attrs:
identifier = attribute.identifier()
- if identifier == "Clamp":
- if not attribute.noArguments():
- raise WebIDLError("[Clamp] must take no arguments",
- [attribute.location])
- if self.enforceRange:
- raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
- [self.location])
- self.clamp = True
- elif identifier == "EnforceRange":
- if not attribute.noArguments():
- raise WebIDLError("[EnforceRange] must take no arguments",
- [attribute.location])
- if self.clamp:
- raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
- [self.location])
- self.enforceRange = True
+ if self.allowTypeAttributes and (identifier == "EnforceRange" or identifier == "Clamp" or
+ identifier == "TreatNullAs" or identifier == "AllowShared"):
+ self.type = self.type.withExtendedAttributes([attribute])
elif identifier == "TreatNonCallableAsNull":
self._allowTreatNonCallableAsNull = True
elif (self.dictionaryMember and
- (identifier == "ChromeOnly" or identifier == "Func")):
+ (identifier == "ChromeOnly" or
+ identifier == "Func" or
+ identifier == "Pref")):
if not self.optional:
raise WebIDLError("[%s] must not be used on a required "
"dictionary member" % identifier,
@@ -4315,13 +4885,7 @@ class IDLArgument(IDLObjectWithIdentifier):
assert not isinstance(type.name, IDLUnresolvedIdentifier)
self.type = type
- if ((self.type.isDictionary() or
- self.type.isUnion() and self.type.unroll().hasDictionaryType()) and
- self.optional and not self.defaultValue and not self.variadic):
- # Default optional non-variadic dictionaries to null,
- # for simplicity, so the codegen doesn't have to special-case this.
- self.defaultValue = IDLNullValue(self.location)
- elif self.type.isAny():
+ if self.type.isAny():
assert (self.defaultValue is None or
isinstance(self.defaultValue, IDLNullValue))
# optional 'any' values always have a default value
@@ -4330,6 +4894,8 @@ class IDLArgument(IDLObjectWithIdentifier):
# codegen doesn't have to special-case this.
self.defaultValue = IDLUndefinedValue(self.location)
+ if self.dictionaryMember and self.type.treatNullAsEmpty:
+ raise WebIDLError("Dictionary members cannot be [TreatNullAs]", [self.location])
# Now do the coercing thing; this needs to happen after the
# above creation of a default value.
if self.defaultValue:
@@ -4351,7 +4917,7 @@ class IDLArgument(IDLObjectWithIdentifier):
class IDLCallback(IDLObjectWithScope):
- def __init__(self, location, parentScope, identifier, returnType, arguments):
+ def __init__(self, location, parentScope, identifier, returnType, arguments, isConstructor):
assert isinstance(returnType, IDLType)
self._returnType = returnType
@@ -4366,10 +4932,15 @@ class IDLCallback(IDLObjectWithScope):
self._treatNonCallableAsNull = False
self._treatNonObjectAsNull = False
+ self._isRunScriptBoundary = False
+ self._isConstructor = isConstructor
def isCallback(self):
return True
+ def isConstructor(self):
+ return self._isConstructor
+
def signatures(self):
return [(self._returnType, self._arguments)]
@@ -4402,7 +4973,16 @@ class IDLCallback(IDLObjectWithScope):
if attr.identifier() == "TreatNonCallableAsNull":
self._treatNonCallableAsNull = True
elif attr.identifier() == "TreatNonObjectAsNull":
+ if self._isConstructor:
+ raise WebIDLError("[TreatNonObjectAsNull] is not supported "
+ "on constructors", [self.location])
self._treatNonObjectAsNull = True
+ elif attr.identifier() == "MOZ_CAN_RUN_SCRIPT_BOUNDARY":
+ if self._isConstructor:
+ raise WebIDLError("[MOZ_CAN_RUN_SCRIPT_BOUNDARY] is not "
+ "permitted on constructors",
+ [self.location])
+ self._isRunScriptBoundary = True
else:
unhandledAttrs.append(attr)
if self._treatNonCallableAsNull and self._treatNonObjectAsNull:
@@ -4414,6 +4994,9 @@ class IDLCallback(IDLObjectWithScope):
def _getDependentObjects(self):
return set([self._returnType] + self._arguments)
+ def isRunScriptBoundary(self):
+ return self._isRunScriptBoundary;
+
class IDLCallbackType(IDLType):
def __init__(self, location, callback):
@@ -4433,8 +5016,7 @@ class IDLCallbackType(IDLType):
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
return (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isNonCallbackInterface() or other.isDate() or
- other.isSequence())
+ other.isNonCallbackInterface() or other.isSequence())
def _getDependentObjects(self):
return self.callback._getDependentObjects()
@@ -4460,13 +5042,15 @@ class IDLMethodOverload:
deps.add(self.returnType)
return deps
+ def includesRestrictedFloatArgument(self):
+ return any(arg.type.includesRestrictedFloat() for arg in self.arguments)
+
class IDLMethod(IDLInterfaceMember, IDLScope):
Special = enum(
'Getter',
'Setter',
- 'Creator',
'Deleter',
'LegacyCaller',
base=IDLInterfaceMember.Special
@@ -4479,10 +5063,11 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
)
def __init__(self, location, identifier, returnType, arguments,
- static=False, getter=False, setter=False, creator=False,
+ static=False, getter=False, setter=False,
deleter=False, specialType=NamedOrIndexed.Neither,
- legacycaller=False, stringifier=False, jsonifier=False,
- maplikeOrSetlikeOrIterable=None):
+ legacycaller=False, stringifier=False,
+ maplikeOrSetlikeOrIterable=None,
+ underlyingAttr=None):
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
IDLInterfaceMember.__init__(self, location, identifier,
IDLInterfaceMember.Tags.Method)
@@ -4500,18 +5085,16 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self._getter = getter
assert isinstance(setter, bool)
self._setter = setter
- assert isinstance(creator, bool)
- self._creator = creator
assert isinstance(deleter, bool)
self._deleter = deleter
assert isinstance(legacycaller, bool)
self._legacycaller = legacycaller
assert isinstance(stringifier, bool)
self._stringifier = stringifier
- assert isinstance(jsonifier, bool)
- self._jsonifier = jsonifier
assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
+ self._htmlConstructor = False
+ self.underlyingAttr = underlyingAttr
self._specialType = specialType
self._unforgeable = False
self.dependsOn = "Everything"
@@ -4538,7 +5121,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert not arguments[0].optional and not arguments[0].variadic
assert not self._getter or not overload.returnType.isVoid()
- if self._setter or self._creator:
+ if self._setter:
assert len(self._overloads) == 1
arguments = self._overloads[0].arguments
assert len(arguments) == 2
@@ -4551,13 +5134,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert len(self._overloads) == 1
overload = self._overloads[0]
assert len(overload.arguments) == 0
- assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
-
- if self._jsonifier:
- assert len(self._overloads) == 1
- overload = self._overloads[0]
- assert len(overload.arguments) == 0
- assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
+ if not self.underlyingAttr:
+ assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
def isStatic(self):
return self._static
@@ -4571,9 +5149,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isSetter(self):
return self._setter
- def isCreator(self):
- return self._creator
-
def isDeleter(self):
return self._deleter
@@ -4593,8 +5168,11 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isStringifier(self):
return self._stringifier
- def isJsonifier(self):
- return self._jsonifier
+ def isToJSON(self):
+ return self.identifier.name == "toJSON"
+
+ def isDefaultToJSON(self):
+ return self.isToJSON() and self.getExtendedAttribute("Default")
def isMaplikeOrSetlikeOrIterableMethod(self):
"""
@@ -4606,11 +5184,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isSpecial(self):
return (self.isGetter() or
self.isSetter() or
- self.isCreator() or
self.isDeleter() or
self.isLegacycaller() or
- self.isStringifier() or
- self.isJsonifier())
+ self.isStringifier())
+
+ def isHTMLConstructor(self):
+ return self._htmlConstructor
def hasOverloads(self):
return self._hasOverloads
@@ -4637,10 +5216,25 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def addOverload(self, method):
assert len(method._overloads) == 1
- if self._extendedAttrDict != method ._extendedAttrDict:
- raise WebIDLError("Extended attributes differ on different "
- "overloads of %s" % method.identifier,
- [self.location, method.location])
+ if self._extendedAttrDict != method._extendedAttrDict:
+ extendedAttrDiff = set(self._extendedAttrDict.keys()) ^ set(method._extendedAttrDict.keys())
+
+ if extendedAttrDiff == { "LenientFloat" }:
+ if "LenientFloat" not in self._extendedAttrDict:
+ for overload in self._overloads:
+ if overload.includesRestrictedFloatArgument():
+ raise WebIDLError("Restricted float behavior differs on different "
+ "overloads of %s" % method.identifier,
+ [overload.location, method.location])
+ self._extendedAttrDict["LenientFloat"] = method._extendedAttrDict["LenientFloat"]
+ elif method._overloads[0].includesRestrictedFloatArgument():
+ raise WebIDLError("Restricted float behavior differs on different "
+ "overloads of %s" % method.identifier,
+ [self.location, method.location])
+ else:
+ raise WebIDLError("Extended attributes differ on different "
+ "overloads of %s" % method.identifier,
+ [self.location, method.location])
self._overloads.extend(method._overloads)
@@ -4659,14 +5253,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert not method.isGetter()
assert not self.isSetter()
assert not method.isSetter()
- assert not self.isCreator()
- assert not method.isCreator()
assert not self.isDeleter()
assert not method.isDeleter()
assert not self.isStringifier()
assert not method.isStringifier()
- assert not self.isJsonifier()
- assert not method.isJsonifier()
+ assert not self.isHTMLConstructor()
+ assert not method.isHTMLConstructor()
return self
@@ -4735,7 +5327,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert argument.type.isComplete()
if ((argument.type.isDictionary() and
- argument.type.inner.canBeEmpty())or
+ argument.type.unroll().inner.canBeEmpty()) or
(argument.type.isUnion() and
argument.type.unroll().hasPossiblyEmptyDictionaryType())):
# Optional dictionaries and unions containing optional
@@ -4743,19 +5335,33 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
# optional arguments must be optional.
if (not argument.optional and
all(arg.optional for arg in arguments[idx+1:])):
- raise WebIDLError("Dictionary argument or union "
- "argument containing a dictionary "
- "not followed by a required argument "
+ raise WebIDLError("Dictionary argument without any "
+ "required fields or union argument "
+ "containing such dictionary not "
+ "followed by a required argument "
"must be optional",
[argument.location])
- # An argument cannot be a Nullable Dictionary
- if argument.type.nullable():
- raise WebIDLError("An argument cannot be a nullable "
- "dictionary or nullable union "
- "containing a dictionary",
+ if (not argument.defaultValue and
+ all(arg.optional for arg in arguments[idx+1:])):
+ raise WebIDLError("Dictionary argument without any "
+ "required fields or union argument "
+ "containing such dictionary not "
+ "followed by a required argument "
+ "must have a default value",
[argument.location])
+ # An argument cannot be a nullable dictionary or a
+ # nullable union containing a dictionary.
+ if (argument.type.nullable() and
+ (argument.type.isDictionary() or
+ (argument.type.isUnion() and
+ argument.type.unroll().hasDictionaryType()))):
+ raise WebIDLError("An argument cannot be a nullable "
+ "dictionary or nullable union "
+ "containing a dictionary",
+ [argument.location])
+
# Only the last argument can be variadic
if variadicArgument:
raise WebIDLError("Variadic argument is not last argument",
@@ -4786,6 +5392,19 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
" methods on JS-implemented classes only.",
[self.location])
+ # Ensure that toJSON methods satisfy the spec constraints on them.
+ if self.identifier.name == "toJSON":
+ if len(self.signatures()) != 1:
+ raise WebIDLError("toJSON method has multiple overloads",
+ [self._overloads[0].location,
+ self._overloads[1].location])
+ if len(self.signatures()[0][1]) != 0:
+ raise WebIDLError("toJSON method has arguments",
+ [self.location])
+ if not self.signatures()[0][0].isJSONType():
+ raise WebIDLError("toJSON method has non-JSON return type",
+ [self.location])
+
def overloadsForArgCount(self, argc):
return [overload for overload in self._overloads if
len(overload.arguments) == argc or
@@ -4831,13 +5450,14 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def handleExtendedAttribute(self, attr):
identifier = attr.identifier()
- if identifier == "GetterThrows":
+ if (identifier == "GetterThrows" or
+ identifier == "SetterThrows" or
+ identifier == "GetterCanOOM" or
+ identifier == "SetterCanOOM" or
+ identifier == "SetterNeedsSubjectPrincipal" or
+ identifier == "GetterNeedsSubjectPrincipal"):
raise WebIDLError("Methods must not be flagged as "
- "[GetterThrows]",
- [attr.location, self.location])
- elif identifier == "SetterThrows":
- raise WebIDLError("Methods must not be flagged as "
- "[SetterThrows]",
+ "[%s]" % identifier,
[attr.location, self.location])
elif identifier == "Unforgeable":
if self.isStatic():
@@ -4858,12 +5478,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
[attr.location, self.location])
elif identifier == "LenientFloat":
# This is called before we've done overload resolution
- assert len(self.signatures()) == 1
- sig = self.signatures()[0]
- if not sig[0].isVoid():
+ overloads = self._overloads
+ assert len(overloads) == 1
+ if not overloads[0].returnType.isVoid():
raise WebIDLError("[LenientFloat] used on a non-void method",
[attr.location, self.location])
- if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
+ if not overloads[0].includesRestrictedFloatArgument():
raise WebIDLError("[LenientFloat] used on an operation with no "
"restricted float type arguments",
[attr.location, self.location])
@@ -4875,6 +5495,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,
[attr.location])
+ if identifier == "CrossOriginCallable" and self.isStatic():
+ raise WebIDLError("[CrossOriginCallable] is only allowed on non-static "
+ "attributes",
+ [attr.location, self.location])
elif identifier == "Pure":
if not attr.noArguments():
raise WebIDLError("[Pure] must take no arguments",
@@ -4909,16 +5533,42 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
raise WebIDLError("[Unscopable] is only allowed on non-static "
"attributes and operations",
[attr.location, self.location])
+ elif identifier == "CEReactions":
+ if not attr.noArguments():
+ raise WebIDLError("[CEReactions] must take no arguments",
+ [attr.location])
+
+ if self.isSpecial() and not self.isSetter() and not self.isDeleter():
+ raise WebIDLError("[CEReactions] is only allowed on operation, "
+ "attribute, setter, and deleter",
+ [attr.location, self.location])
+ elif identifier == "Default":
+ if not attr.noArguments():
+ raise WebIDLError("[Default] must take no arguments",
+ [attr.location])
+
+ if not self.isToJSON():
+ raise WebIDLError("[Default] is only allowed on toJSON operations",
+ [attr.location, self.location])
+
+ if self.signatures()[0][0] != BuiltinTypes[IDLBuiltinType.Types.object]:
+ raise WebIDLError("The return type of the default toJSON "
+ "operation must be 'object'",
+ [attr.location, self.location])
elif (identifier == "Throws" or
+ identifier == "CanOOM" or
identifier == "NewObject" or
identifier == "ChromeOnly" or
- identifier == "UnsafeInPrerendering" or
identifier == "Pref" or
identifier == "Deprecated" or
identifier == "Func" or
identifier == "SecureContext" or
identifier == "BinaryName" or
- identifier == "StaticClassOverride"):
+ identifier == "NeedsSubjectPrincipal" or
+ identifier == "NeedsCallerType" or
+ identifier == "StaticClassOverride" or
+ identifier == "NonEnumerable" or
+ identifier == "Unexposed"):
# Known attributes that we don't need to do anything with here
pass
else:
@@ -4939,49 +5589,110 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
return deps
-class IDLImplementsStatement(IDLObject):
- def __init__(self, location, implementor, implementee):
+class IDLConstructor(IDLMethod):
+ def __init__(self, location, args, name):
+ # We can't actually init our IDLMethod yet, because we do not know the
+ # return type yet. Just save the info we have for now and we will init
+ # it later.
+ self._initLocation = location
+ self._initArgs = args
+ self._initName = name
+ self._inited = False
+ self._initExtendedAttrs = []
+
+ def addExtendedAttributes(self, attrs):
+ if self._inited:
+ return IDLMethod.addExtendedAttributes(self, attrs)
+ self._initExtendedAttrs.extend(attrs)
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if (identifier == "BinaryName" or
+ identifier == "ChromeOnly" or
+ identifier == "NewObject" or
+ identifier == "SecureContext" or
+ identifier == "Throws" or
+ identifier == "Func" or
+ identifier == "Pref"):
+ IDLMethod.handleExtendedAttribute(self, attr)
+ elif identifier == "HTMLConstructor":
+ if not attr.noArguments():
+ raise WebIDLError("[HTMLConstructor] must take no arguments",
+ [attr.location])
+ # We shouldn't end up here for named constructors.
+ assert(self.identifier.name == "constructor")
+
+ if any(len(sig[1]) != 0 for sig in self.signatures()):
+ raise WebIDLError("[HTMLConstructor] must not be applied to a "
+ "constructor operation that has arguments.",
+ [attr.location])
+ self._htmlConstructor = True
+ else:
+ raise WebIDLError("Unknown extended attribute %s on method" % identifier,
+ [attr.location])
+
+ def reallyInit(self, parentInterface):
+ name = self._initName
+ location = self._initLocation
+ identifier = IDLUnresolvedIdentifier(location, name, allowForbidden=True)
+ retType = IDLWrapperType(parentInterface.location, parentInterface)
+ IDLMethod.__init__(self, location, identifier, retType, self._initArgs,
+ static=True)
+ self._inited = True;
+ # Propagate through whatever extended attributes we already had
+ self.addExtendedAttributes(self._initExtendedAttrs)
+ self._initExtendedAttrs = []
+ # Constructors are always NewObject. Whether they throw or not is
+ # indicated by [Throws] annotations in the usual way.
+ self.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NewObject",))])
+
+
+class IDLIncludesStatement(IDLObject):
+ def __init__(self, location, interface, mixin):
IDLObject.__init__(self, location)
- self.implementor = implementor
- self.implementee = implementee
+ self.interface = interface
+ self.mixin = mixin
self._finished = False
def finish(self, scope):
if self._finished:
return
- assert(isinstance(self.implementor, IDLIdentifierPlaceholder))
- assert(isinstance(self.implementee, IDLIdentifierPlaceholder))
- implementor = self.implementor.finish(scope)
- implementee = self.implementee.finish(scope)
- # NOTE: we depend on not setting self.implementor and
- # self.implementee here to keep track of the original
+ self._finished = True
+ assert(isinstance(self.interface, IDLIdentifierPlaceholder))
+ assert(isinstance(self.mixin, IDLIdentifierPlaceholder))
+ interface = self.interface.finish(scope)
+ mixin = self.mixin.finish(scope)
+ # NOTE: we depend on not setting self.interface and
+ # self.mixin here to keep track of the original
# locations.
- if not isinstance(implementor, IDLInterface):
- raise WebIDLError("Left-hand side of 'implements' is not an "
- "interface",
- [self.implementor.location])
- if implementor.isCallback():
- raise WebIDLError("Left-hand side of 'implements' is a callback "
- "interface",
- [self.implementor.location])
- if not isinstance(implementee, IDLInterface):
- raise WebIDLError("Right-hand side of 'implements' is not an "
+ if not isinstance(interface, IDLInterface):
+ raise WebIDLError("Left-hand side of 'includes' is not an "
"interface",
- [self.implementee.location])
- if implementee.isCallback():
- raise WebIDLError("Right-hand side of 'implements' is a callback "
+ [self.interface.location, interface.location])
+ if interface.isCallback():
+ raise WebIDLError("Left-hand side of 'includes' is a callback "
"interface",
- [self.implementee.location])
- implementor.addImplementedInterface(implementee)
- self.implementor = implementor
- self.implementee = implementee
+ [self.interface.location, interface.location])
+ if not isinstance(mixin, IDLInterfaceMixin):
+ raise WebIDLError("Right-hand side of 'includes' is not an "
+ "interface mixin",
+ [self.mixin.location, mixin.location])
+
+ mixin.actualExposureGlobalNames.update(interface._exposureGlobalNames)
+
+ interface.addIncludedMixin(mixin)
+ self.interface = interface
+ self.mixin = mixin
def validate(self):
pass
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
-
+ if len(attrs) != 0:
+ raise WebIDLError("There are no extended attributes that are "
+ "allowed on includes statements",
+ [attrs[0].location, self.location])
class IDLExtendedAttribute(IDLObject):
"""
@@ -5028,6 +5739,7 @@ class Tokenizer(object):
"FLOATLITERAL",
"IDENTIFIER",
"STRING",
+ "COMMENTS",
"WHITESPACE",
"OTHER"
]
@@ -5051,7 +5763,7 @@ class Tokenizer(object):
return t
def t_IDENTIFIER(self, t):
- r'[A-Z_a-z][0-9A-Z_a-z-]*'
+ r'[_-]?[A-Za-z][0-9A-Z_a-z-]*'
t.type = self.keywords.get(t.value, 'IDENTIFIER')
return t
@@ -5060,8 +5772,12 @@ class Tokenizer(object):
t.value = t.value[1:-1]
return t
+ def t_COMMENTS(self, t):
+ r'(\/\*(.|\n)*?\*\/)|(\/\/.*)'
+ pass
+
def t_WHITESPACE(self, t):
- r'[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+'
+ r'[\t\n\r ]+'
pass
def t_ELLIPSIS(self, t):
@@ -5075,22 +5791,21 @@ class Tokenizer(object):
return t
keywords = {
- "module": "MODULE",
"interface": "INTERFACE",
"partial": "PARTIAL",
+ "mixin": "MIXIN",
"dictionary": "DICTIONARY",
"exception": "EXCEPTION",
"enum": "ENUM",
"callback": "CALLBACK",
"typedef": "TYPEDEF",
- "implements": "IMPLEMENTS",
+ "includes": "INCLUDES",
"const": "CONST",
"null": "NULL",
"true": "TRUE",
"false": "FALSE",
"serializer": "SERIALIZER",
"stringifier": "STRINGIFIER",
- "jsonifier": "JSONIFIER",
"unrestricted": "UNRESTRICTED",
"attribute": "ATTRIBUTE",
"readonly": "READONLY",
@@ -5098,16 +5813,16 @@ class Tokenizer(object):
"static": "STATIC",
"getter": "GETTER",
"setter": "SETTER",
- "creator": "CREATOR",
"deleter": "DELETER",
"legacycaller": "LEGACYCALLER",
"optional": "OPTIONAL",
"...": "ELLIPSIS",
"::": "SCOPE",
- "Date": "DATE",
"DOMString": "DOMSTRING",
"ByteString": "BYTESTRING",
"USVString": "USVSTRING",
+ "JSString": "JSSTRING",
+ "UTF8String": "UTF8STRING",
"any": "ANY",
"boolean": "BOOLEAN",
"byte": "BYTE",
@@ -5119,7 +5834,7 @@ class Tokenizer(object):
"Promise": "PROMISE",
"required": "REQUIRED",
"sequence": "SEQUENCE",
- "MozMap": "MOZMAP",
+ "record": "RECORD",
"short": "SHORT",
"unsigned": "UNSIGNED",
"void": "VOID",
@@ -5137,15 +5852,18 @@ class Tokenizer(object):
"<": "LT",
">": "GT",
"ArrayBuffer": "ARRAYBUFFER",
- "SharedArrayBuffer": "SHAREDARRAYBUFFER",
"or": "OR",
"maplike": "MAPLIKE",
"setlike": "SETLIKE",
"iterable": "ITERABLE",
- "namespace": "NAMESPACE"
+ "namespace": "NAMESPACE",
+ "ReadableStream": "READABLESTREAM",
+ "constructor": "CONSTRUCTOR",
+ "symbol": "SYMBOL",
+ "async": "ASYNC",
}
- tokens.extend(keywords.values())
+ tokens.extend(list(keywords.values()))
def t_error(self, t):
raise WebIDLError("Unrecognized Input",
@@ -5154,23 +5872,21 @@ class Tokenizer(object):
lexpos=self.lexer.lexpos,
filename=self.filename)])
- def __init__(self, outputdir, lexer=None):
+ def __init__(self, lexer=None):
if lexer:
self.lexer = lexer
else:
- self.lexer = lex.lex(object=self,
- outputdir=outputdir,
- lextab='webidllex',
- reflags=re.DOTALL)
+ self.lexer = lex.lex(object=self)
class SqueakyCleanLogger(object):
errorWhitelist = [
- # Web IDL defines the WHITESPACE token, but doesn't actually
+ # Web IDL defines the WHITESPACE and COMMENTS token, but doesn't actually
# use it ... so far.
"Token 'WHITESPACE' defined, but not used",
- # And that means we have an unused token
- "There is 1 unused token",
+ "Token 'COMMENTS' defined, but not used",
+ # And that means we have unused tokens
+ "There are 2 unused tokens",
# Web IDL defines a OtherOrComma rule that's only used in
# ExtendedAttributeInner, which we don't use yet.
"Rule 'OtherOrComma' defined, but not used",
@@ -5238,21 +5954,21 @@ class Parser(Tokenizer):
def p_Definition(self, p):
"""
- Definition : CallbackOrInterface
+ Definition : CallbackOrInterfaceOrMixin
| Namespace
| Partial
| Dictionary
| Exception
| Enum
| Typedef
- | ImplementsStatement
+ | IncludesStatement
"""
p[0] = p[1]
assert p[1] # We might not have implemented something ...
- def p_CallbackOrInterfaceCallback(self, p):
+ def p_CallbackOrInterfaceOrMixinCallback(self, p):
"""
- CallbackOrInterface : CALLBACK CallbackRestOrInterface
+ CallbackOrInterfaceOrMixin : CALLBACK CallbackRestOrInterface
"""
if p[2].isInterface():
assert isinstance(p[2], IDLInterface)
@@ -5260,16 +5976,17 @@ class Parser(Tokenizer):
p[0] = p[2]
- def p_CallbackOrInterfaceInterface(self, p):
+ def p_CallbackOrInterfaceOrMixinInterfaceOrMixin(self, p):
"""
- CallbackOrInterface : Interface
+ CallbackOrInterfaceOrMixin : INTERFACE InterfaceOrMixin
"""
- p[0] = p[1]
+ p[0] = p[2]
def p_CallbackRestOrInterface(self, p):
"""
CallbackRestOrInterface : CallbackRest
- | Interface
+ | CallbackConstructorRest
+ | CallbackInterface
"""
assert p[1]
p[0] = p[1]
@@ -5277,9 +5994,10 @@ class Parser(Tokenizer):
def handleNonPartialObject(self, location, identifier, constructor,
constructorArgs, nonPartialArgs):
"""
- This handles non-partial objects (interfaces and namespaces) by
- checking for an existing partial object, and promoting it to
- non-partial as needed. The return value is the non-partial object.
+ This handles non-partial objects (interfaces, namespaces and
+ dictionaries) by checking for an existing partial object, and promoting
+ it to non-partial as needed. The return value is the non-partial
+ object.
constructorArgs are all the args for the constructor except the last
one: isKnownNonPartial.
@@ -5301,7 +6019,7 @@ class Parser(Tokenizer):
[location, existingObj.location])
existingObj.setNonPartial(*nonPartialArgs)
return existingObj
- except Exception, ex:
+ except Exception as ex:
if isinstance(ex, WebIDLError):
raise ex
pass
@@ -5309,14 +6027,27 @@ class Parser(Tokenizer):
# True for isKnownNonPartial
return constructor(*(constructorArgs + [True]))
- def p_Interface(self, p):
+ def p_InterfaceOrMixin(self, p):
+ """
+ InterfaceOrMixin : InterfaceRest
+ | MixinRest
+ """
+ p[0] = p[1]
+
+ def p_CallbackInterface(self, p):
+ """
+ CallbackInterface : INTERFACE InterfaceRest
"""
- Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
+ p[0] = p[2]
+
+ def p_InterfaceRest(self, p):
+ """
+ InterfaceRest : IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
"""
location = self.getLocation(p, 1)
- identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
- members = p[5]
- parent = p[3]
+ identifier = IDLUnresolvedIdentifier(location, p[1])
+ members = p[4]
+ parent = p[2]
p[0] = self.handleNonPartialObject(
location, identifier, IDLInterface,
@@ -5325,10 +6056,10 @@ class Parser(Tokenizer):
def p_InterfaceForwardDecl(self, p):
"""
- Interface : INTERFACE IDENTIFIER SEMICOLON
+ InterfaceRest : IDENTIFIER SEMICOLON
"""
location = self.getLocation(p, 1)
- identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ identifier = IDLUnresolvedIdentifier(location, p[1])
try:
if self.globalScope()._lookupIdentifier(identifier):
@@ -5339,13 +6070,26 @@ class Parser(Tokenizer):
"%s and %s" % (identifier.name, p[0]),
[location, p[0].location])
return
- except Exception, ex:
+ except Exception as ex:
if isinstance(ex, WebIDLError):
raise ex
pass
p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
+ def p_MixinRest(self, p):
+ """
+ MixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[4]
+
+ p[0] = self.handleNonPartialObject(
+ location, identifier, IDLInterfaceMixin,
+ [location, self.globalScope(), identifier, members],
+ [location, members])
+
def p_Namespace(self, p):
"""
Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
@@ -5365,10 +6109,16 @@ class Parser(Tokenizer):
"""
p[0] = p[2]
+ def p_PartialDefinitionInterface(self, p):
+ """
+ PartialDefinition : INTERFACE PartialInterfaceOrPartialMixin
+ """
+ p[0] = p[2]
+
def p_PartialDefinition(self, p):
"""
- PartialDefinition : PartialInterface
- | PartialNamespace
+ PartialDefinition : PartialNamespace
+ | PartialDictionary
"""
p[0] = p[1]
@@ -5376,17 +6126,17 @@ class Parser(Tokenizer):
nonPartialConstructorArgs,
partialConstructorArgs):
"""
- This handles partial objects (interfaces and namespaces) by checking for
- an existing non-partial object, and adding ourselves to it as needed.
- The return value is our partial object. For now we just use
- IDLPartialInterfaceOrNamespace for partial objects.
+ This handles partial objects (interfaces, namespaces and dictionaries)
+ by checking for an existing non-partial object, and adding ourselves to
+ it as needed. The return value is our partial object. We use
+ IDLPartialInterfaceOrNamespace for partial interfaces or namespaces,
+ and IDLPartialDictionary for partial dictionaries.
nonPartialConstructorArgs are all the args for the non-partial
constructor except the last two: members and isKnownNonPartial.
- partialConstructorArgs are the arguments for the
- IDLPartialInterfaceOrNamespace constructor, except the last one (the
- non-partial object).
+ partialConstructorArgs are the arguments for the partial object
+ constructor, except the last one (the non-partial object).
"""
# The name of the class starts with "IDL", so strip that off.
# Also, starts with a capital letter after that, so nix that
@@ -5402,7 +6152,7 @@ class Parser(Tokenizer):
"non-%s object" %
(prettyname, prettyname),
[location, nonPartialObject.location])
- except Exception, ex:
+ except Exception as ex:
if isinstance(ex, WebIDLError):
raise ex
pass
@@ -5410,24 +6160,55 @@ class Parser(Tokenizer):
if not nonPartialObject:
nonPartialObject = nonPartialConstructor(
# No members, False for isKnownNonPartial
- *(nonPartialConstructorArgs + [[], False]))
- partialInterface = IDLPartialInterfaceOrNamespace(
- *(partialConstructorArgs + [nonPartialObject]))
- return partialInterface
+ *(nonPartialConstructorArgs), members=[], isKnownNonPartial=False)
+
+ partialObject = None
+ if isinstance(nonPartialObject, IDLDictionary):
+ partialObject = IDLPartialDictionary(
+ *(partialConstructorArgs + [nonPartialObject]))
+ elif isinstance(nonPartialObject, (IDLInterface, IDLInterfaceMixin, IDLNamespace)):
+ partialObject = IDLPartialInterfaceOrNamespace(
+ *(partialConstructorArgs + [nonPartialObject]))
+ else:
+ raise WebIDLError("Unknown partial object type %s" %
+ type(partialObject),
+ [location])
+
+ return partialObject
- def p_PartialInterface(self, p):
+ def p_PartialInterfaceOrPartialMixin(self, p):
"""
- PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+ PartialInterfaceOrPartialMixin : PartialInterfaceRest
+ | PartialMixinRest
+ """
+ p[0] = p[1]
+
+ def p_PartialInterfaceRest(self, p):
+ """
+ PartialInterfaceRest : IDENTIFIER LBRACE PartialInterfaceMembers RBRACE SEMICOLON
"""
location = self.getLocation(p, 1)
- identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
- members = p[4]
+ identifier = IDLUnresolvedIdentifier(location, p[1])
+ members = p[3]
p[0] = self.handlePartialObject(
location, identifier, IDLInterface,
[location, self.globalScope(), identifier, None],
[location, identifier, members])
+ def p_PartialMixinRest(self, p):
+ """
+ PartialMixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[4]
+
+ p[0] = self.handlePartialObject(
+ location, identifier, IDLInterfaceMixin,
+ [location, self.globalScope(), identifier],
+ [location, identifier, members])
+
def p_PartialNamespace(self, p):
"""
PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
@@ -5441,6 +6222,19 @@ class Parser(Tokenizer):
[location, self.globalScope(), identifier],
[location, identifier, members])
+ def p_PartialDictionary(self, p):
+ """
+ PartialDictionary : DICTIONARY IDENTIFIER LBRACE DictionaryMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[4]
+
+ p[0] = self.handlePartialObject(
+ location, identifier, IDLDictionary,
+ [location, self.globalScope(), identifier],
+ [location, identifier, members])
+
def p_Inheritance(self, p):
"""
Inheritance : COLON ScopedName
@@ -5457,7 +6251,7 @@ class Parser(Tokenizer):
"""
InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
"""
- p[0] = [p[2]] if p[2] else []
+ p[0] = [p[2]]
assert not p[1] or p[2]
p[2].addExtendedAttributes(p[1])
@@ -5472,8 +6266,64 @@ class Parser(Tokenizer):
def p_InterfaceMember(self, p):
"""
- InterfaceMember : Const
- | AttributeOrOperationOrMaplikeOrSetlikeOrIterable
+ InterfaceMember : PartialInterfaceMember
+ | Constructor
+ """
+ p[0] = p[1]
+
+ def p_Constructor(self, p):
+ """
+ Constructor : CONSTRUCTOR LPAREN ArgumentList RPAREN SEMICOLON
+ """
+ p[0] = IDLConstructor(self.getLocation(p, 1), p[3], "constructor")
+
+ def p_PartialInterfaceMembers(self, p):
+ """
+ PartialInterfaceMembers : ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers
+ """
+ p[0] = [p[2]]
+
+ assert not p[1] or p[2]
+ p[2].addExtendedAttributes(p[1])
+
+ p[0].extend(p[3])
+
+ def p_PartialInterfaceMembersEmpty(self, p):
+ """
+ PartialInterfaceMembers :
+ """
+ p[0] = []
+
+ def p_PartialInterfaceMember(self, p):
+ """
+ PartialInterfaceMember : Const
+ | AttributeOrOperationOrMaplikeOrSetlikeOrIterable
+ """
+ p[0] = p[1]
+
+
+ def p_MixinMembersEmpty(self, p):
+ """
+ MixinMembers :
+ """
+ p[0] = []
+
+ def p_MixinMembers(self, p):
+ """
+ MixinMembers : ExtendedAttributeList MixinMember MixinMembers
+ """
+ p[0] = [p[2]]
+
+ assert not p[1] or p[2]
+ p[2].addExtendedAttributes(p[1])
+
+ p[0].extend(p[3])
+
+ def p_MixinMember(self, p):
+ """
+ MixinMember : Const
+ | Attribute
+ | Operation
"""
p[0] = p[1]
@@ -5495,31 +6345,42 @@ class Parser(Tokenizer):
# We're at the end of the list
p[0] = []
return
- # Add our extended attributes
p[2].addExtendedAttributes(p[1])
p[0] = [p[2]]
p[0].extend(p[3])
- def p_DictionaryMember(self, p):
+ def p_DictionaryMemberRequired(self, p):
"""
- DictionaryMember : Required Type IDENTIFIER Default SEMICOLON
+ DictionaryMember : REQUIRED TypeWithExtendedAttributes IDENTIFIER SEMICOLON
"""
- # These quack a lot like optional arguments, so just treat them that way.
+ # These quack a lot like required arguments, so just treat them that way.
t = p[2]
assert isinstance(t, IDLType)
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
- defaultValue = p[4]
- optional = not p[1]
-
- if not optional and defaultValue:
- raise WebIDLError("Required dictionary members can't have a default value.",
- [self.getLocation(p, 4)])
p[0] = IDLArgument(self.getLocation(p, 3), identifier, t,
- optional=optional,
- defaultValue=defaultValue, variadic=False,
+ optional=False,
+ defaultValue=None, variadic=False,
dictionaryMember=True)
+ def p_DictionaryMember(self, p):
+ """
+ DictionaryMember : Type IDENTIFIER Default SEMICOLON
+ """
+ # These quack a lot like optional arguments, so just treat them that way.
+ t = p[1]
+ assert isinstance(t, IDLType)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ defaultValue = p[3]
+
+ # Any attributes that precede this may apply to the type, so
+ # we configure the argument to forward type attributes down instead of producing
+ # a parse error
+ p[0] = IDLArgument(self.getLocation(p, 2), identifier, t,
+ optional=True,
+ defaultValue=defaultValue, variadic=False,
+ dictionaryMember=True, allowTypeAttributes=True)
+
def p_Default(self, p):
"""
Default : EQUALS DefaultValue
@@ -5534,12 +6395,23 @@ class Parser(Tokenizer):
"""
DefaultValue : ConstValue
| LBRACKET RBRACKET
+ | LBRACE RBRACE
"""
if len(p) == 2:
p[0] = p[1]
else:
- assert len(p) == 3 # Must be []
- p[0] = IDLEmptySequenceValue(self.getLocation(p, 1))
+ assert len(p) == 3 # Must be [] or {}
+ if p[1] == "[":
+ p[0] = IDLEmptySequenceValue(self.getLocation(p, 1))
+ else:
+ assert p[1] == "{"
+ p[0] = IDLDefaultDictionaryValue(self.getLocation(p, 1))
+
+ def p_DefaultValueNull(self, p):
+ """
+ DefaultValue : NULL
+ """
+ p[0] = IDLNullValue(self.getLocation(p, 1))
def p_Exception(self, p):
"""
@@ -5596,7 +6468,15 @@ class Parser(Tokenizer):
"""
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
p[0] = IDLCallback(self.getLocation(p, 1), self.globalScope(),
- identifier, p[3], p[5])
+ identifier, p[3], p[5], isConstructor=False)
+
+ def p_CallbackConstructorRest(self, p):
+ """
+ CallbackConstructorRest : CONSTRUCTOR IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ p[0] = IDLCallback(self.getLocation(p, 2), self.globalScope(),
+ identifier, p[4], p[6], isConstructor=True)
def p_ExceptionMembers(self, p):
"""
@@ -5607,21 +6487,20 @@ class Parser(Tokenizer):
def p_Typedef(self, p):
"""
- Typedef : TYPEDEF Type IDENTIFIER SEMICOLON
+ Typedef : TYPEDEF TypeWithExtendedAttributes IDENTIFIER SEMICOLON
"""
typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(),
p[2], p[3])
p[0] = typedef
- def p_ImplementsStatement(self, p):
+ def p_IncludesStatement(self, p):
"""
- ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON
+ IncludesStatement : ScopedName INCLUDES ScopedName SEMICOLON
"""
- assert(p[2] == "implements")
- implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
- implementee = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
- p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor,
- implementee)
+ assert(p[2] == "includes")
+ interface = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
+ mixin = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
+ p[0] = IDLIncludesStatement(self.getLocation(p, 1), interface, mixin)
def p_Const(self, p):
"""
@@ -5670,12 +6549,6 @@ class Parser(Tokenizer):
stringType = BuiltinTypes[IDLBuiltinType.Types.domstring]
p[0] = IDLValue(location, stringType, p[1])
- def p_ConstValueNull(self, p):
- """
- ConstValue : NULL
- """
- p[0] = IDLNullValue(self.getLocation(p, 1))
-
def p_BooleanLiteralTrue(self, p):
"""
BooleanLiteral : TRUE
@@ -5700,8 +6573,8 @@ class Parser(Tokenizer):
def p_Iterable(self, p):
"""
- Iterable : ITERABLE LT Type GT SEMICOLON
- | ITERABLE LT Type COMMA Type GT SEMICOLON
+ Iterable : ITERABLE LT TypeWithExtendedAttributes GT SEMICOLON
+ | ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON
"""
location = self.getLocation(p, 2)
identifier = IDLUnresolvedIdentifier(location, "__iterable",
@@ -5717,7 +6590,7 @@ class Parser(Tokenizer):
def p_Setlike(self, p):
"""
- Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON
+ Setlike : ReadOnly SETLIKE LT TypeWithExtendedAttributes GT SEMICOLON
"""
readonly = p[1]
maplikeOrSetlikeType = p[2]
@@ -5731,7 +6604,7 @@ class Parser(Tokenizer):
def p_Maplike(self, p):
"""
- Maplike : ReadOnly MAPLIKE LT Type COMMA Type GT SEMICOLON
+ Maplike : ReadOnly MAPLIKE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON
"""
readonly = p[1]
maplikeOrSetlikeType = p[2]
@@ -5769,7 +6642,7 @@ class Parser(Tokenizer):
def p_AttributeRest(self, p):
"""
- AttributeRest : ReadOnly ATTRIBUTE Type AttributeName SEMICOLON
+ AttributeRest : ReadOnly ATTRIBUTE TypeWithExtendedAttributes AttributeName SEMICOLON
"""
location = self.getLocation(p, 2)
readonly = p[1]
@@ -5812,13 +6685,12 @@ class Parser(Tokenizer):
getter = True if IDLMethod.Special.Getter in p[1] else False
setter = True if IDLMethod.Special.Setter in p[1] else False
- creator = True if IDLMethod.Special.Creator in p[1] else False
deleter = True if IDLMethod.Special.Deleter in p[1] else False
legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False
if getter or deleter:
- if setter or creator:
- raise WebIDLError("getter and deleter are incompatible with setter and creator",
+ if setter:
+ raise WebIDLError("getter and deleter are incompatible with setter",
[self.getLocation(p, 1)])
(returnType, identifier, arguments) = p[2]
@@ -5837,6 +6709,9 @@ class Parser(Tokenizer):
specialType = IDLMethod.NamedOrIndexed.Named
elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
specialType = IDLMethod.NamedOrIndexed.Indexed
+ if deleter:
+ raise WebIDLError("There is no such thing as an indexed deleter.",
+ [self.getLocation(p, 1)])
else:
raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
("getter" if getter else "deleter"),
@@ -5850,10 +6725,9 @@ class Parser(Tokenizer):
if returnType.isVoid():
raise WebIDLError("getter cannot have void return type",
[self.getLocation(p, 2)])
- if setter or creator:
+ if setter:
if len(arguments) != 2:
- raise WebIDLError("%s has wrong number of arguments" %
- ("setter" if setter else "creator"),
+ raise WebIDLError("setter has wrong number of arguments",
[self.getLocation(p, 2)])
argType = arguments[0].type
if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
@@ -5861,18 +6735,15 @@ class Parser(Tokenizer):
elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
specialType = IDLMethod.NamedOrIndexed.Indexed
else:
- raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
- ("setter" if setter else "creator"),
+ raise WebIDLError("settter has wrong argument type (must be DOMString or UnsignedLong)",
[arguments[0].location])
if arguments[0].optional or arguments[0].variadic:
- raise WebIDLError("%s cannot have %s argument" %
- ("setter" if setter else "creator",
- "optional" if arguments[0].optional else "variadic"),
+ raise WebIDLError("setter cannot have %s argument" %
+ ("optional" if arguments[0].optional else "variadic"),
[arguments[0].location])
if arguments[1].optional or arguments[1].variadic:
- raise WebIDLError("%s cannot have %s argument" %
- ("setter" if setter else "creator",
- "optional" if arguments[1].optional else "variadic"),
+ raise WebIDLError("setter cannot have %s argument" %
+ ("optional" if arguments[1].optional else "variadic"),
[arguments[1].location])
if stringifier:
@@ -5885,7 +6756,7 @@ class Parser(Tokenizer):
# identifier might be None. This is only permitted for special methods.
if not identifier:
- if (not getter and not setter and not creator and
+ if (not getter and not setter and
not deleter and not legacycaller and not stringifier):
raise WebIDLError("Identifier required for non-special methods",
[self.getLocation(p, 2)])
@@ -5893,19 +6764,18 @@ class Parser(Tokenizer):
location = BuiltinLocation("<auto-generated-identifier>")
identifier = IDLUnresolvedIdentifier(
location,
- "__%s%s%s%s%s%s%s" %
+ "__%s%s%s%s%s%s" %
("named" if specialType == IDLMethod.NamedOrIndexed.Named else
"indexed" if specialType == IDLMethod.NamedOrIndexed.Indexed else "",
"getter" if getter else "",
"setter" if setter else "",
"deleter" if deleter else "",
- "creator" if creator else "",
"legacycaller" if legacycaller else "",
"stringifier" if stringifier else ""),
allowDoubleUnderscore=True)
method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
- static=static, getter=getter, setter=setter, creator=creator,
+ static=static, getter=getter, setter=setter,
deleter=deleter, specialType=specialType,
legacycaller=legacycaller, stringifier=stringifier)
p[0] = method
@@ -5924,19 +6794,6 @@ class Parser(Tokenizer):
stringifier=True)
p[0] = method
- def p_Jsonifier(self, p):
- """
- Operation : JSONIFIER SEMICOLON
- """
- identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
- "__jsonifier", allowDoubleUnderscore=True)
- method = IDLMethod(self.getLocation(p, 1),
- identifier,
- returnType=BuiltinTypes[IDLBuiltinType.Types.object],
- arguments=[],
- jsonifier=True)
- p[0] = method
-
def p_QualifierStatic(self, p):
"""
Qualifier : STATIC
@@ -5981,12 +6838,6 @@ class Parser(Tokenizer):
"""
p[0] = IDLMethod.Special.Setter
- def p_SpecialCreator(self, p):
- """
- Special : CREATOR
- """
- p[0] = IDLMethod.Special.Creator
-
def p_SpecialDeleter(self, p):
"""
Special : DELETER
@@ -6045,95 +6896,105 @@ class Parser(Tokenizer):
def p_Argument(self, p):
"""
- Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName Default
+ Argument : ExtendedAttributeList ArgumentRest
"""
- t = p[3]
+ p[0] = p[2]
+ p[0].addExtendedAttributes(p[1])
+
+ def p_ArgumentRestOptional(self, p):
+ """
+ ArgumentRest : OPTIONAL TypeWithExtendedAttributes ArgumentName Default
+ """
+ t = p[2]
assert isinstance(t, IDLType)
- identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5])
+ # Arg names can be reserved identifiers
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3],
+ allowForbidden=True)
- optional = p[2]
- variadic = p[4]
- defaultValue = p[6]
+ defaultValue = p[4]
- if not optional and defaultValue:
- raise WebIDLError("Mandatory arguments can't have a default value.",
- [self.getLocation(p, 6)])
# We can't test t.isAny() here and give it a default value as needed,
# since at this point t is not a fully resolved type yet (e.g. it might
# be a typedef). We'll handle the 'any' case in IDLArgument.complete.
- if variadic:
- if optional:
- raise WebIDLError("Variadic arguments should not be marked optional.",
- [self.getLocation(p, 2)])
- optional = variadic
+ p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, True, defaultValue, False)
- p[0] = IDLArgument(self.getLocation(p, 5), identifier, t, optional, defaultValue, variadic)
- p[0].addExtendedAttributes(p[1])
+ def p_ArgumentRest(self, p):
+ """
+ ArgumentRest : Type Ellipsis ArgumentName
+ """
+ t = p[1]
+ assert isinstance(t, IDLType)
+ # Arg names can be reserved identifiers
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3],
+ allowForbidden=True)
+
+ variadic = p[2]
+
+ # We can't test t.isAny() here and give it a default value as needed,
+ # since at this point t is not a fully resolved type yet (e.g. it might
+ # be a typedef). We'll handle the 'any' case in IDLArgument.complete.
+
+ # variadic implies optional
+ # Any attributes that precede this may apply to the type, so
+ # we configure the argument to forward type attributes down instead of producing
+ # a parse error
+ p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, variadic, None, variadic, allowTypeAttributes=True)
def p_ArgumentName(self, p):
"""
ArgumentName : IDENTIFIER
- | ATTRIBUTE
- | CALLBACK
- | CONST
- | CREATOR
- | DELETER
- | DICTIONARY
- | ENUM
- | EXCEPTION
- | GETTER
- | IMPLEMENTS
- | INHERIT
- | INTERFACE
- | ITERABLE
- | LEGACYCALLER
- | MAPLIKE
- | PARTIAL
- | REQUIRED
- | SERIALIZER
- | SETLIKE
- | SETTER
- | STATIC
- | STRINGIFIER
- | JSONIFIER
- | TYPEDEF
- | UNRESTRICTED
- | NAMESPACE
+ | ArgumentNameKeyword
"""
p[0] = p[1]
- def p_AttributeName(self, p):
- """
- AttributeName : IDENTIFIER
- | REQUIRED
+ def p_ArgumentNameKeyword(self, p):
+ """
+ ArgumentNameKeyword : ASYNC
+ | ATTRIBUTE
+ | CALLBACK
+ | CONST
+ | CONSTRUCTOR
+ | DELETER
+ | DICTIONARY
+ | ENUM
+ | EXCEPTION
+ | GETTER
+ | INCLUDES
+ | INHERIT
+ | INTERFACE
+ | ITERABLE
+ | LEGACYCALLER
+ | MAPLIKE
+ | MIXIN
+ | NAMESPACE
+ | PARTIAL
+ | READONLY
+ | REQUIRED
+ | SERIALIZER
+ | SETLIKE
+ | SETTER
+ | STATIC
+ | STRINGIFIER
+ | TYPEDEF
+ | UNRESTRICTED
"""
p[0] = p[1]
- def p_Optional(self, p):
- """
- Optional : OPTIONAL
- """
- p[0] = True
-
- def p_OptionalEmpty(self, p):
- """
- Optional :
- """
- p[0] = False
-
- def p_Required(self, p):
+ def p_AttributeName(self, p):
"""
- Required : REQUIRED
+ AttributeName : IDENTIFIER
+ | AttributeNameKeyword
"""
- p[0] = True
+ p[0] = p[1]
- def p_RequiredEmpty(self, p):
+ def p_AttributeNameKeyword(self, p):
"""
- Required :
+ AttributeNameKeyword : ASYNC
+ | REQUIRED
"""
- p[0] = False
+ p[0] = p[1]
def p_Ellipsis(self, p):
"""
@@ -6218,43 +7079,32 @@ class Parser(Tokenizer):
| EQUALS
| GT
| QUESTIONMARK
- | DATE
| DOMSTRING
| BYTESTRING
| USVSTRING
+ | UTF8STRING
+ | JSSTRING
+ | PROMISE
| ANY
- | ATTRIBUTE
| BOOLEAN
| BYTE
- | LEGACYCALLER
- | CONST
- | CREATOR
- | DELETER
| DOUBLE
- | EXCEPTION
| FALSE
| FLOAT
- | GETTER
- | IMPLEMENTS
- | INHERIT
- | INTERFACE
| LONG
- | MODULE
| NULL
| OBJECT
| OCTET
+ | OR
| OPTIONAL
+ | RECORD
| SEQUENCE
- | MOZMAP
- | SETTER
| SHORT
- | STATIC
- | STRINGIFIER
- | JSONIFIER
+ | SYMBOL
| TRUE
- | TYPEDEF
| UNSIGNED
| VOID
+ | ArgumentNameKeyword
"""
pass
@@ -6277,9 +7127,15 @@ class Parser(Tokenizer):
"""
p[0] = self.handleNullable(p[1], p[2])
- def p_SingleTypeNonAnyType(self, p):
+ def p_TypeWithExtendedAttributes(self, p):
"""
- SingleType : NonAnyType
+ TypeWithExtendedAttributes : ExtendedAttributeList Type
+ """
+ p[0] = p[2].withExtendedAttributes(p[1])
+
+ def p_SingleTypeDistinguishableType(self, p):
+ """
+ SingleType : DistinguishableType
"""
p[0] = p[1]
@@ -6289,6 +7145,14 @@ class Parser(Tokenizer):
"""
p[0] = BuiltinTypes[IDLBuiltinType.Types.any]
+ # Note: Promise<void> is allowed, so we want to parametrize on ReturnType,
+ # not Type. Promise types can't be null, hence no "Null" in there.
+ def p_SingleTypePromiseType(self, p):
+ """
+ SingleType : PROMISE LT ReturnType GT
+ """
+ p[0] = IDLPromiseType(self.getLocation(p, 1), p[3])
+
def p_UnionType(self, p):
"""
UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN
@@ -6297,11 +7161,11 @@ class Parser(Tokenizer):
types.extend(p[5])
p[0] = IDLUnionType(self.getLocation(p, 1), types)
- def p_UnionMemberTypeNonAnyType(self, p):
+ def p_UnionMemberTypeDistinguishableType(self, p):
"""
- UnionMemberType : NonAnyType
+ UnionMemberType : ExtendedAttributeList DistinguishableType
"""
- p[0] = p[1]
+ p[0] = p[2].withExtendedAttributes(p[1])
def p_UnionMemberType(self, p):
"""
@@ -6322,55 +7186,50 @@ class Parser(Tokenizer):
"""
p[0] = []
- def p_NonAnyType(self, p):
+ def p_DistinguishableType(self, p):
"""
- NonAnyType : PrimitiveOrStringType Null
- | ARRAYBUFFER Null
- | SHAREDARRAYBUFFER Null
- | OBJECT Null
+ DistinguishableType : PrimitiveType Null
+ | ARRAYBUFFER Null
+ | READABLESTREAM Null
+ | OBJECT Null
"""
if p[1] == "object":
type = BuiltinTypes[IDLBuiltinType.Types.object]
elif p[1] == "ArrayBuffer":
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
- elif p[1] == "SharedArrayBuffer":
- type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
+ elif p[1] == "ReadableStream":
+ type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream]
else:
type = BuiltinTypes[p[1]]
p[0] = self.handleNullable(type, p[2])
- def p_NonAnyTypeSequenceType(self, p):
+ def p_DistinguishableTypeStringType(self, p):
"""
- NonAnyType : SEQUENCE LT Type GT Null
+ DistinguishableType : StringType Null
"""
- innerType = p[3]
- type = IDLSequenceType(self.getLocation(p, 1), innerType)
- p[0] = self.handleNullable(type, p[5])
+ p[0] = self.handleNullable(p[1], p[2])
- # Note: Promise<void> is allowed, so we want to parametrize on
- # ReturnType, not Type. Also, we want this to end up picking up
- # the Promise interface for now, hence the games with IDLUnresolvedType.
- def p_NonAnyTypePromiseType(self, p):
+ def p_DistinguishableTypeSequenceType(self, p):
"""
- NonAnyType : PROMISE LT ReturnType GT Null
+ DistinguishableType : SEQUENCE LT TypeWithExtendedAttributes GT Null
"""
innerType = p[3]
- promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
- type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
+ type = IDLSequenceType(self.getLocation(p, 1), innerType)
p[0] = self.handleNullable(type, p[5])
- def p_NonAnyTypeMozMapType(self, p):
+ def p_DistinguishableTypeRecordType(self, p):
"""
- NonAnyType : MOZMAP LT Type GT Null
+ DistinguishableType : RECORD LT StringType COMMA TypeWithExtendedAttributes GT Null
"""
- innerType = p[3]
- type = IDLMozMapType(self.getLocation(p, 1), innerType)
- p[0] = self.handleNullable(type, p[5])
+ keyType = p[3]
+ valueType = p[5]
+ type = IDLRecordType(self.getLocation(p, 1), keyType, valueType)
+ p[0] = self.handleNullable(type, p[7])
- def p_NonAnyTypeScopedName(self, p):
+ def p_DistinguishableTypeScopedName(self, p):
"""
- NonAnyType : ScopedName Null
+ DistinguishableType : ScopedName Null
"""
assert isinstance(p[1], IDLUnresolvedIdentifier)
@@ -6400,95 +7259,104 @@ class Parser(Tokenizer):
type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
p[0] = self.handleNullable(type, p[2])
- def p_NonAnyTypeDate(self, p):
- """
- NonAnyType : DATE Null
- """
- p[0] = self.handleNullable(BuiltinTypes[IDLBuiltinType.Types.date],
- p[2])
-
def p_ConstType(self, p):
"""
- ConstType : PrimitiveOrStringType Null
+ ConstType : PrimitiveType
"""
- type = BuiltinTypes[p[1]]
- p[0] = self.handleNullable(type, p[2])
+ p[0] = BuiltinTypes[p[1]]
def p_ConstTypeIdentifier(self, p):
"""
- ConstType : IDENTIFIER Null
+ ConstType : IDENTIFIER
"""
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
- type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
- p[0] = self.handleNullable(type, p[2])
+ p[0] = IDLUnresolvedType(self.getLocation(p, 1), identifier)
- def p_PrimitiveOrStringTypeUint(self, p):
+ def p_PrimitiveTypeUint(self, p):
"""
- PrimitiveOrStringType : UnsignedIntegerType
+ PrimitiveType : UnsignedIntegerType
"""
p[0] = p[1]
- def p_PrimitiveOrStringTypeBoolean(self, p):
+ def p_PrimitiveTypeBoolean(self, p):
"""
- PrimitiveOrStringType : BOOLEAN
+ PrimitiveType : BOOLEAN
"""
p[0] = IDLBuiltinType.Types.boolean
- def p_PrimitiveOrStringTypeByte(self, p):
+ def p_PrimitiveTypeByte(self, p):
"""
- PrimitiveOrStringType : BYTE
+ PrimitiveType : BYTE
"""
p[0] = IDLBuiltinType.Types.byte
- def p_PrimitiveOrStringTypeOctet(self, p):
+ def p_PrimitiveTypeOctet(self, p):
"""
- PrimitiveOrStringType : OCTET
+ PrimitiveType : OCTET
"""
p[0] = IDLBuiltinType.Types.octet
- def p_PrimitiveOrStringTypeFloat(self, p):
+ def p_PrimitiveTypeFloat(self, p):
"""
- PrimitiveOrStringType : FLOAT
+ PrimitiveType : FLOAT
"""
p[0] = IDLBuiltinType.Types.float
- def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
+ def p_PrimitiveTypeUnrestictedFloat(self, p):
"""
- PrimitiveOrStringType : UNRESTRICTED FLOAT
+ PrimitiveType : UNRESTRICTED FLOAT
"""
p[0] = IDLBuiltinType.Types.unrestricted_float
- def p_PrimitiveOrStringTypeDouble(self, p):
+ def p_PrimitiveTypeDouble(self, p):
"""
- PrimitiveOrStringType : DOUBLE
+ PrimitiveType : DOUBLE
"""
p[0] = IDLBuiltinType.Types.double
- def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
+ def p_PrimitiveTypeUnrestictedDouble(self, p):
"""
- PrimitiveOrStringType : UNRESTRICTED DOUBLE
+ PrimitiveType : UNRESTRICTED DOUBLE
"""
p[0] = IDLBuiltinType.Types.unrestricted_double
- def p_PrimitiveOrStringTypeDOMString(self, p):
+ def p_StringType(self, p):
+ """
+ StringType : BuiltinStringType
+ """
+ p[0] = BuiltinTypes[p[1]]
+
+ def p_BuiltinStringTypeDOMString(self, p):
"""
- PrimitiveOrStringType : DOMSTRING
+ BuiltinStringType : DOMSTRING
"""
p[0] = IDLBuiltinType.Types.domstring
- def p_PrimitiveOrStringTypeBytestring(self, p):
+ def p_BuiltinStringTypeBytestring(self, p):
"""
- PrimitiveOrStringType : BYTESTRING
+ BuiltinStringType : BYTESTRING
"""
p[0] = IDLBuiltinType.Types.bytestring
- def p_PrimitiveOrStringTypeUSVString(self, p):
+ def p_BuiltinStringTypeUSVString(self, p):
"""
- PrimitiveOrStringType : USVSTRING
+ BuiltinStringType : USVSTRING
"""
p[0] = IDLBuiltinType.Types.usvstring
+ def p_BuiltinStringTypeUTF8String(self, p):
+ """
+ BuiltinStringType : UTF8STRING
+ """
+ p[0] = IDLBuiltinType.Types.utf8string
+
+ def p_BuiltinStringTypeJSString(self, p):
+ """
+ BuiltinStringType : JSSTRING
+ """
+ p[0] = IDLBuiltinType.Types.jsstring
+
def p_UnsignedIntegerTypeUnsigned(self, p):
"""
UnsignedIntegerType : UNSIGNED IntegerType
@@ -6622,7 +7490,13 @@ class Parser(Tokenizer):
IdentifierList : IDENTIFIER Identifiers
"""
idents = list(p[2])
- idents.insert(0, p[1])
+ # This is only used for identifier-list-valued extended attributes, and if
+ # we're going to restrict to IDENTIFIER here we should at least allow
+ # escaping with leading '_' as usual for identifiers.
+ ident = p[1]
+ if ident[0] == '_':
+ ident = ident[1:]
+ idents.insert(0, ident)
p[0] = idents
def p_IdentifiersList(self, p):
@@ -6630,7 +7504,13 @@ class Parser(Tokenizer):
Identifiers : COMMA IDENTIFIER Identifiers
"""
idents = list(p[3])
- idents.insert(0, p[2])
+ # This is only used for identifier-list-valued extended attributes, and if
+ # we're going to restrict to IDENTIFIER here we should at least allow
+ # escaping with leading '_' as usual for identifiers.
+ ident = p[2]
+ if ident[0] == '_':
+ ident = ident[1:]
+ idents.insert(0, ident)
p[0] = idents
def p_IdentifiersEmpty(self, p):
@@ -6647,36 +7527,16 @@ class Parser(Tokenizer):
raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)])
def __init__(self, outputdir='', lexer=None):
- Tokenizer.__init__(self, outputdir, lexer)
+ Tokenizer.__init__(self, lexer)
logger = SqueakyCleanLogger()
try:
- self.parser = yacc.yacc(module=self,
- outputdir=outputdir,
- tabmodule='webidlyacc',
- errorlog=logger,
- debug=False
- # Pickling the grammar is a speedup in
- # some cases (older Python?) but a
- # significant slowdown in others.
- # We're not pickling for now, until it
- # becomes a speedup again.
- # , picklefile='WebIDLGrammar.pkl'
- )
+ self.parser = yacc.yacc(module=self, errorlog=logger, debug=False)
finally:
logger.reportGrammarErrors()
self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
- # To make our test harness work, pretend like we have a primary global already.
- # Note that we _don't_ set _globalScope.primaryGlobalAttr,
- # so we'll still be able to detect multiple PrimaryGlobal extended attributes.
- self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal"
- self._globalScope.globalNames.add("FakeTestPrimaryGlobal")
- self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal")
- # And we add the special-cased "System" global name, which
- # doesn't have any corresponding interfaces.
- self._globalScope.globalNames.add("System")
- self._globalScope.globalNameMapping["System"].add("BackstagePass")
+
self._installBuiltins(self._globalScope)
self._productions = []
@@ -6689,8 +7549,8 @@ class Parser(Tokenizer):
def _installBuiltins(self, scope):
assert isinstance(scope, IDLScope)
- # xrange omits the last value.
- for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1):
+ # range omits the last value.
+ for x in range(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1):
builtin = BuiltinTypes[x]
name = builtin.name
typedef = IDLTypedef(BuiltinLocation("<builtin type>"), scope, builtin, name)
@@ -6703,12 +7563,12 @@ class Parser(Tokenizer):
return type
def parse(self, t, filename=None):
- self.lexer.input(t)
+ self._filename = filename
+ self.lexer.input(t.decode(encoding = 'utf-8'))
# for tok in iter(self.lexer.token, None):
# print tok
- self._filename = filename
self._productions.extend(self.parser.parse(lexer=self.lexer, tracking=True))
self._filename = None
@@ -6719,23 +7579,9 @@ class Parser(Tokenizer):
for p in self._productions:
if isinstance(p, IDLInterface):
interfaceStatements.append(p)
- if p.identifier.name == "Navigator":
- navigatorInterface = p
iterableIteratorIface = None
for iface in interfaceStatements:
- navigatorProperty = iface.getNavigatorProperty()
- if navigatorProperty:
- # We're generating a partial interface to add a readonly
- # property to the Navigator interface for every interface
- # annotated with NavigatorProperty.
- partialInterface = IDLPartialInterfaceOrNamespace(
- iface.location,
- IDLUnresolvedIdentifier(iface.location, "Navigator"),
- [ navigatorProperty ],
- navigatorInterface)
- self._productions.append(partialInterface)
-
iterable = None
# We haven't run finish() on the interface yet, so we don't know
# whether our interface is maplike/setlike/iterable or not. This
@@ -6755,9 +7601,12 @@ class Parser(Tokenizer):
nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
itr_ident = IDLUnresolvedIdentifier(iface.location,
iface.identifier.name + "Iterator")
+ toStringTag = iface.identifier.name + " Iterator"
itr_iface = IDLInterface(iface.location, self.globalScope(),
itr_ident, None, [nextMethod],
- isKnownNonPartial=True)
+ isKnownNonPartial=True,
+ classNameOverride=toStringTag,
+ toStringTag=toStringTag)
itr_iface.addExtendedAttributes([simpleExtendedAttr("NoInterfaceObject")])
# Make sure the exposure set for the iterator interface is the
# same as the exposure set for the iterable interface, because
@@ -6771,14 +7620,14 @@ class Parser(Tokenizer):
self._productions.append(itr_iface)
iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
- # Then, finish all the IDLImplementsStatements. In particular, we
- # have to make sure we do those before we do the IDLInterfaces.
+ # Make sure we finish IDLIncludesStatements before we finish the
+ # IDLInterfaces.
# XXX khuey hates this bit and wants to nuke it from orbit.
- implementsStatements = [p for p in self._productions if
- isinstance(p, IDLImplementsStatement)]
+ includesStatements = [p for p in self._productions if
+ isinstance(p, IDLIncludesStatement)]
otherStatements = [p for p in self._productions if
- not isinstance(p, IDLImplementsStatement)]
- for production in implementsStatements:
+ not isinstance(p, IDLIncludesStatement)]
+ for production in includesStatements:
production.finish(self.globalScope())
for production in otherStatements:
production.finish(self.globalScope())
@@ -6831,14 +7680,14 @@ def main():
f = open(fullPath, 'rb')
lines = f.readlines()
f.close()
- print fullPath
+ print(fullPath)
parser.parse(''.join(lines), fullPath)
parser.finish()
- except WebIDLError, e:
+ except WebIDLError as e:
if options.verbose_errors:
traceback.print_exc()
else:
- print e
+ print(e)
if __name__ == '__main__':
main()
diff --git a/components/script/dom/bindings/codegen/parser/abstract.patch b/components/script/dom/bindings/codegen/parser/abstract.patch
index a8e2ddcf759..180e345b61b 100644
--- a/components/script/dom/bindings/codegen/parser/abstract.patch
+++ b/components/script/dom/bindings/codegen/parser/abstract.patch
@@ -1,11 +1,11 @@
--- WebIDL.py
+++ WebIDL.py
-@@ -1416,7 +1416,8 @@
- identifier == "LegacyEventInit" or
- identifier == "ProbablyShortLivingObject" or
+@@ -1883,7 +1883,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "LegacyUnenumerableNamedProperties" or
-- identifier == "NonOrdinaryGetPrototypeOf"):
-+ identifier == "NonOrdinaryGetPrototypeOf" or
+ identifier == "RunConstructorInCallerCompartment" or
+ identifier == "WantsEventListenerHooks" or
+- identifier == "Serializable"):
++ identifier == "Serializable" or
+ identifier == "Abstract"):
# Known extended attributes that do not take values
if not attr.noArguments():
diff --git a/components/script/dom/bindings/codegen/parser/callback-location.patch b/components/script/dom/bindings/codegen/parser/callback-location.patch
index fac5d035801..b7a308df631 100644
--- a/components/script/dom/bindings/codegen/parser/callback-location.patch
+++ b/components/script/dom/bindings/codegen/parser/callback-location.patch
@@ -1,17 +1,15 @@
-diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
-index da32340..81c52b7 100644
--- WebIDL.py
+++ WebIDL.py
-@@ -2170,7 +2170,7 @@ class IDLUnresolvedType(IDLType):
- return typedefType.complete(scope)
+@@ -2283,7 +2283,7 @@ class IDLUnresolvedType(IDLType):
+ return typedefType.complete(scope).withExtendedAttributes(self.extraTypeAttributes)
elif obj.isCallback() and not obj.isInterface():
assert self.name.name == obj.identifier.name
- return IDLCallbackType(self.location, obj)
+ return IDLCallbackType(obj.location, obj)
-
- if self._promiseInnerType and not self._promiseInnerType.isComplete():
- self._promiseInnerType = self._promiseInnerType.complete(scope)
-@@ -6521,7 +6521,7 @@ class Parser(Tokenizer):
+
+ name = self.name.resolve(scope, None)
+ return IDLWrapperType(self.location, obj)
+@@ -6854,7 +6854,7 @@ class Parser(Tokenizer):
type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
obj.identifier.name)
elif obj.isCallback() and not obj.isInterface():
@@ -19,4 +17,4 @@ index da32340..81c52b7 100644
+ type = IDLCallbackType(obj.location, obj)
else:
type = IDLWrapperType(self.getLocation(p, 1), p[1])
- p[0] = self.handleModifiers(type, p[2])
+ p[0] = self.handleNullable(type, p[2])
diff --git a/components/script/dom/bindings/codegen/parser/debug.patch b/components/script/dom/bindings/codegen/parser/debug.patch
index ca391c38273..a4f8739000d 100644
--- a/components/script/dom/bindings/codegen/parser/debug.patch
+++ b/components/script/dom/bindings/codegen/parser/debug.patch
@@ -1,6 +1,6 @@
--- WebIDL.py
+++ WebIDL.py
-@@ -6823,7 +6823,8 @@ class Parser(Tokenizer):
+@@ -7382,7 +7382,8 @@ class Parser(Tokenizer):
self.parser = yacc.yacc(module=self,
outputdir=outputdir,
tabmodule='webidlyacc',
diff --git a/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch b/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch
new file mode 100644
index 00000000000..210134d8ca6
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch
@@ -0,0 +1,11 @@
+--- WebIDL.py
++++ WebIDL.py
+@@ -3490,7 +3490,7 @@ class IDLBuiltinType(IDLType):
+ [self.location, attribute.location])
+ assert not self.nullable()
+ if not attribute.hasValue():
+- raise WebIDLError("[TreatNullAs] must take an identifier argument"
++ raise WebIDLError("[TreatNullAs] must take an identifier argument",
+ [attribute.location])
+ value = attribute.value()
+ if value != 'EmptyString':
diff --git a/components/script/dom/bindings/codegen/parser/inline.patch b/components/script/dom/bindings/codegen/parser/inline.patch
index 5d1056d2b58..46971ce5067 100644
--- a/components/script/dom/bindings/codegen/parser/inline.patch
+++ b/components/script/dom/bindings/codegen/parser/inline.patch
@@ -1,12 +1,12 @@
--- WebIDL.py
+++ WebIDL.py
-@@ -1695,7 +1695,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
- identifier == "ProbablyShortLivingObject" or
- identifier == "LegacyUnenumerableNamedProperties" or
- identifier == "NonOrdinaryGetPrototypeOf" or
+@@ -1884,7 +1884,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
+ identifier == "RunConstructorInCallerCompartment" or
+ identifier == "WantsEventListenerHooks" or
+ identifier == "Serializable" or
- identifier == "Abstract"):
+ identifier == "Abstract" or
+ identifier == "Inline"):
# Known extended attributes that do not take values
if not attr.noArguments():
- raise WebIDLError("[%s] must take no arguments" % identifier, \ No newline at end of file
+ raise WebIDLError("[%s] must take no arguments" % identifier,
diff --git a/components/script/dom/bindings/codegen/parser/pref-main-thread.patch b/components/script/dom/bindings/codegen/parser/pref-main-thread.patch
deleted file mode 100644
index 7be2dcbfc5e..00000000000
--- a/components/script/dom/bindings/codegen/parser/pref-main-thread.patch
+++ /dev/null
@@ -1,28 +0,0 @@
---- WebIDL.py
-+++ WebIDL.py
-@@ -1239,12 +1239,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
- alias,
- [member.location, m.location])
-
-- if (self.getExtendedAttribute("Pref") and
-- self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
-- raise WebIDLError("[Pref] used on an interface that is not %s-only" %
-- self.parentScope.primaryGlobalName,
-- [self.location])
--
- # Conditional exposure makes no sense for interfaces with no
- # interface object, unless they're navigator properties.
- # And SecureContext makes sense for interfaces with no interface object,
-@@ -3459,12 +3453,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
- IDLExposureMixins.finish(self, scope)
-
- def validate(self):
-- if (self.getExtendedAttribute("Pref") and
-- self.exposureSet != set([self._globalScope.primaryGlobalName])):
-- raise WebIDLError("[Pref] used on an interface member that is not "
-- "%s-only" % self._globalScope.primaryGlobalName,
-- [self.location])
--
- if self.isAttr() or self.isMethod():
- if self.affects == "Everything" and self.dependsOn != "Everything":
- raise WebIDLError("Interface member is flagged as affecting "
diff --git a/components/script/dom/bindings/codegen/parser/runtests.py b/components/script/dom/bindings/codegen/parser/runtests.py
index b8d45ef31b5..0599bf55fec 100644
--- a/components/script/dom/bindings/codegen/parser/runtests.py
+++ b/components/script/dom/bindings/codegen/parser/runtests.py
@@ -1,6 +1,6 @@
# 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/.
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
import os, sys
import glob
@@ -62,7 +62,7 @@ def run_tests(tests, verbose):
harness.start()
try:
_test.WebIDLTest.__call__(WebIDL.Parser(), harness)
- except Exception, ex:
+ except Exception as ex:
print("TEST-UNEXPECTED-FAIL | Unhandled exception in test %s: %s" % (testpath, ex))
traceback.print_exc()
finally:
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_argument_keywords.py b/components/script/dom/bindings/codegen/parser/tests/test_argument_keywords.py
new file mode 100644
index 00000000000..e190f617e26
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_argument_keywords.py
@@ -0,0 +1,17 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Foo {
+ void foo(object constructor);
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 1, "Should have an interface");
+ iface = results[0];
+ harness.check(len(iface.members), 1, "Should have an operation");
+ operation = iface.members[0];
+ harness.check(len(operation.signatures()), 1, "Should have one signature");
+ (retval, args) = operation.signatures()[0];
+ harness.check(len(args), 1, "Should have an argument");
+ harness.check(args[0].identifier.name, "constructor",
+ "Should have an identifier named 'constructor'");
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attr.py b/components/script/dom/bindings/codegen/parser/tests/test_attr.py
index ad7aabc1918..35f680aaa82 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_attr.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_attr.py
@@ -133,7 +133,7 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should not allow [SetterThrows] on readonly attributes")
@@ -146,7 +146,7 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should spell [Throws] correctly")
@@ -159,7 +159,7 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should not allow [SameObject] on attributes not of interface type")
@@ -172,6 +172,6 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(not threw, "Should allow [SameObject] on attributes of interface type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py b/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py
new file mode 100644
index 00000000000..ff08791d16f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py
@@ -0,0 +1,415 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Basic functionality
+ threw = False
+ try:
+ parser.parse("""
+ typedef [EnforceRange] long Foo;
+ typedef [Clamp] long Bar;
+ typedef [TreatNullAs=EmptyString] DOMString Baz;
+ dictionary A {
+ required [EnforceRange] long a;
+ required [Clamp] long b;
+ [ChromeOnly, EnforceRange] long c;
+ Foo d;
+ };
+ interface B {
+ attribute Foo typedefFoo;
+ attribute [EnforceRange] long foo;
+ attribute [Clamp] long bar;
+ attribute [TreatNullAs=EmptyString] DOMString baz;
+ void method([EnforceRange] long foo, [Clamp] long bar,
+ [TreatNullAs=EmptyString] DOMString baz);
+ void method2(optional [EnforceRange] long foo, optional [Clamp] long bar,
+ optional [TreatNullAs=EmptyString] DOMString baz);
+ };
+ interface C {
+ attribute [EnforceRange] long? foo;
+ attribute [Clamp] long? bar;
+ void method([EnforceRange] long? foo, [Clamp] long? bar);
+ void method2(optional [EnforceRange] long? foo, optional [Clamp] long? bar);
+ };
+ interface Setlike {
+ setlike<[Clamp] long>;
+ };
+ interface Maplike {
+ maplike<[Clamp] long, [EnforceRange] long>;
+ };
+ interface Iterable {
+ iterable<[Clamp] long, [EnforceRange] long>;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw, "Should not have thrown on parsing normal")
+ if not threw:
+ harness.check(results[0].innerType.hasEnforceRange(), True, "Foo is [EnforceRange]")
+ harness.check(results[1].innerType.hasClamp(), True, "Bar is [Clamp]")
+ harness.check(results[2].innerType.treatNullAsEmpty, True, "Baz is [TreatNullAs=EmptyString]")
+ A = results[3]
+ harness.check(A.members[0].type.hasEnforceRange(), True, "A.a is [EnforceRange]")
+ harness.check(A.members[1].type.hasClamp(), True, "A.b is [Clamp]")
+ harness.check(A.members[2].type.hasEnforceRange(), True, "A.c is [EnforceRange]")
+ harness.check(A.members[3].type.hasEnforceRange(), True, "A.d is [EnforceRange]")
+ B = results[4]
+ harness.check(B.members[0].type.hasEnforceRange(), True, "B.typedefFoo is [EnforceRange]")
+ harness.check(B.members[1].type.hasEnforceRange(), True, "B.foo is [EnforceRange]")
+ harness.check(B.members[2].type.hasClamp(), True, "B.bar is [Clamp]")
+ harness.check(B.members[3].type.treatNullAsEmpty, True, "B.baz is [TreatNullAs=EmptyString]")
+ method = B.members[4].signatures()[0][1]
+ harness.check(method[0].type.hasEnforceRange(), True, "foo argument of method is [EnforceRange]")
+ harness.check(method[1].type.hasClamp(), True, "bar argument of method is [Clamp]")
+ harness.check(method[2].type.treatNullAsEmpty, True, "baz argument of method is [TreatNullAs=EmptyString]")
+ method2 = B.members[5].signatures()[0][1]
+ harness.check(method[0].type.hasEnforceRange(), True, "foo argument of method2 is [EnforceRange]")
+ harness.check(method[1].type.hasClamp(), True, "bar argument of method2 is [Clamp]")
+ harness.check(method[2].type.treatNullAsEmpty, True, "baz argument of method2 is [TreatNullAs=EmptyString]")
+ C = results[5]
+ harness.ok(C.members[0].type.nullable(), "C.foo is nullable")
+ harness.ok(C.members[0].type.hasEnforceRange(), "C.foo has [EnforceRange]")
+ harness.ok(C.members[1].type.nullable(), "C.bar is nullable")
+ harness.ok(C.members[1].type.hasClamp(), "C.bar has [Clamp]")
+ method = C.members[2].signatures()[0][1]
+ harness.ok(method[0].type.nullable(), "foo argument of method is nullable")
+ harness.ok(method[0].type.hasEnforceRange(), "foo argument of method has [EnforceRange]")
+ harness.ok(method[1].type.nullable(), "bar argument of method is nullable")
+ harness.ok(method[1].type.hasClamp(), "bar argument of method has [Clamp]")
+ method2 = C.members[3].signatures()[0][1]
+ harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable")
+ harness.ok(method2[0].type.hasEnforceRange(), "foo argument of method2 has [EnforceRange]")
+ harness.ok(method2[1].type.nullable(), "bar argument of method2 is nullable")
+ harness.ok(method2[1].type.hasClamp(), "bar argument of method2 has [Clamp]")
+
+ # Test [AllowShared]
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [AllowShared] ArrayBufferView Foo;
+ dictionary A {
+ required [AllowShared] ArrayBufferView a;
+ [ChromeOnly, AllowShared] ArrayBufferView b;
+ Foo c;
+ };
+ interface B {
+ attribute Foo typedefFoo;
+ attribute [AllowShared] ArrayBufferView foo;
+ void method([AllowShared] ArrayBufferView foo);
+ void method2(optional [AllowShared] ArrayBufferView foo);
+ };
+ interface C {
+ attribute [AllowShared] ArrayBufferView? foo;
+ void method([AllowShared] ArrayBufferView? foo);
+ void method2(optional [AllowShared] ArrayBufferView? foo);
+ };
+ interface Setlike {
+ setlike<[AllowShared] ArrayBufferView>;
+ };
+ interface Maplike {
+ maplike<[Clamp] long, [AllowShared] ArrayBufferView>;
+ };
+ interface Iterable {
+ iterable<[Clamp] long, [AllowShared] ArrayBufferView>;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw, "Should not have thrown on parsing normal")
+ if not threw:
+ harness.ok(results[0].innerType.hasAllowShared(), "Foo is [AllowShared]")
+ A = results[1]
+ harness.ok(A.members[0].type.hasAllowShared(), "A.a is [AllowShared]")
+ harness.ok(A.members[1].type.hasAllowShared(), "A.b is [AllowShared]")
+ harness.ok(A.members[2].type.hasAllowShared(), "A.c is [AllowShared]")
+ B = results[2]
+ harness.ok(B.members[0].type.hasAllowShared(), "B.typedefFoo is [AllowShared]")
+ harness.ok(B.members[1].type.hasAllowShared(), "B.foo is [AllowShared]")
+ method = B.members[2].signatures()[0][1]
+ harness.ok(method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]")
+ method2 = B.members[3].signatures()[0][1]
+ harness.ok(method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]")
+ C = results[3]
+ harness.ok(C.members[0].type.nullable(), "C.foo is nullable")
+ harness.ok(C.members[0].type.hasAllowShared(), "C.foo is [AllowShared]")
+ method = C.members[1].signatures()[0][1]
+ harness.ok(method[0].type.nullable(), "foo argument of method is nullable")
+ harness.ok(method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]")
+ method2 = C.members[2].signatures()[0][1]
+ harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable")
+ harness.ok(method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]")
+
+ ATTRIBUTES = [("[Clamp]", "long"), ("[EnforceRange]", "long"),
+ ("[TreatNullAs=EmptyString]", "DOMString"), ("[AllowShared]", "ArrayBufferView")]
+ TEMPLATES = [
+ ("required dictionary members", """
+ dictionary Foo {
+ %s required %s foo;
+ };
+ """),
+ ("optional arguments", """
+ interface Foo {
+ void foo(%s optional %s foo);
+ };
+ """),
+ ("typedefs", """
+ %s typedef %s foo;
+ """),
+ ("attributes", """
+ interface Foo {
+ %s attribute %s foo;
+ };
+ """),
+ ("readonly attributes", """
+ interface Foo {
+ readonly attribute %s %s foo;
+ };
+ """),
+ ("readonly unresolved attributes", """
+ interface Foo {
+ readonly attribute Bar baz;
+ };
+ typedef %s %s Bar;
+ """),
+ ("method", """
+ interface Foo {
+ %s %s foo();
+ };
+ """),
+ ("interface","""
+ %s
+ interface Foo {
+ attribute %s foo;
+ };
+ """),
+ ("partial interface","""
+ interface Foo {
+ void foo();
+ };
+ %s
+ partial interface Foo {
+ attribute %s bar;
+ };
+ """),
+ ("interface mixin","""
+ %s
+ interface mixin Foo {
+ attribute %s foo;
+ };
+ """),
+ ("namespace","""
+ %s
+ namespace Foo {
+ attribute %s foo;
+ };
+ """),
+ ("partial namespace","""
+ namespace Foo {
+ void foo();
+ };
+ %s
+ partial namespace Foo {
+ attribute %s bar;
+ };
+ """),
+ ("dictionary","""
+ %s
+ dictionary Foo {
+ %s foo;
+ };
+ """)
+ ];
+
+ for (name, template) in TEMPLATES:
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(template % ("", "long"))
+ parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw, "Template for %s parses without attributes" % name)
+ for (attribute, type) in ATTRIBUTES:
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(template % (attribute, type))
+ parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow %s on %s" % (attribute, name))
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [Clamp, EnforceRange] long Foo;
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [EnforceRange, Clamp] long Foo;
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]")
+
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [Clamp] long Foo;
+ typedef [EnforceRange] Foo bar;
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [EnforceRange] long Foo;
+ typedef [Clamp] Foo bar;
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs")
+
+ TYPES = ["DOMString", "unrestricted float", "float", "unrestricted double", "double"]
+
+ for type in TYPES:
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [Clamp] %s Foo;
+ """ % type)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow [Clamp] on %s" % type)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [EnforceRange] %s Foo;
+ """ % type)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow [EnforceRange] on %s" % type)
+
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [TreatNullAs=EmptyString] long Foo;
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ 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:
+ parser.parse("""
+ typedef [TreatNullAs=EmptyString] DOMString? Foo;
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow [TreatNullAs] on nullable DOMString")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [AllowShared] DOMString Foo;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[AllowShared] only allowed on buffer source types")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef [AllowShared=something] ArrayBufferView Foo;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[AllowShared] must take no arguments")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ void foo([Clamp] Bar arg);
+ };
+ typedef long Bar;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw, "Should allow type attributes on unresolved types")
+ harness.check(results[0].members[0].signatures()[0][1][0].type.hasClamp(), True,
+ "Unresolved types with type attributes should correctly resolve with attributes")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ void foo(Bar arg);
+ };
+ typedef [Clamp] long Bar;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw, "Should allow type attributes on typedefs")
+ harness.check(results[0].members[0].signatures()[0][1][0].type.hasClamp(), True,
+ "Unresolved types that resolve to typedefs with attributes should correctly resolve with attributes")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_callback.py b/components/script/dom/bindings/codegen/parser/tests/test_callback.py
index 4dfda1c3c76..c304d085ce5 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_callback.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_callback.py
@@ -32,3 +32,6 @@ def WebIDLTest(parser, harness):
harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type")
harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type")
harness.ok(t.isCallback(), "Attr has the right type")
+
+ callback = results[1]
+ harness.ok(not callback.isConstructor(), "callback is not constructor")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_callback_constructor.py b/components/script/dom/bindings/codegen/parser/tests/test_callback_constructor.py
new file mode 100644
index 00000000000..4999deef623
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_callback_constructor.py
@@ -0,0 +1,63 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestCallbackConstructor {
+ attribute CallbackConstructorType? constructorAttribute;
+ };
+
+ callback constructor CallbackConstructorType = TestCallbackConstructor (unsigned long arg);
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestCallbackConstructor interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestCallbackConstructor", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestCallbackConstructor", "Interface has the right name")
+ harness.check(len(iface.members), 1, "Expect %s members" % 1)
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Should be an attribute")
+ harness.ok(not attr.isMethod(), "Attr is not an method")
+ harness.ok(not attr.isConst(), "Attr is not a const")
+ harness.check(attr.identifier.QName(), "::TestCallbackConstructor::constructorAttribute", "Attr has the right QName")
+ harness.check(attr.identifier.name, "constructorAttribute", "Attr has the right name")
+ t = attr.type
+ harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type")
+ harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type")
+ harness.ok(t.isCallback(), "Attr has the right type")
+
+ callback = results[1]
+ harness.ok(callback.isConstructor(), "Callback is constructor")
+
+ parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [TreatNonObjectAsNull]
+ callback constructor CallbackConstructorType = object ();
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should throw on TreatNonObjectAsNull callback constructors")
+
+ parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [MOZ_CAN_RUN_SCRIPT_BOUNDARY]
+ callback constructor CallbackConstructorType = object ();
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not permit MOZ_CAN_RUN_SCRIPT_BOUNDARY callback constructors")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_cereactions.py b/components/script/dom/bindings/codegen/parser/tests/test_cereactions.py
new file mode 100644
index 00000000000..f726907c2fc
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_cereactions.py
@@ -0,0 +1,133 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ [CEReactions(DOMString a)] void foo(boolean arg2);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ [CEReactions(DOMString b)] readonly attribute boolean bar;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ [CEReactions] attribute boolean bar;
+ };
+ """)
+
+ results = parser.finish()
+ except Exception as e:
+ harness.ok(False, "Shouldn't have thrown for [CEReactions] used on writable attribute. %s" % e)
+ threw = True
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ [CEReactions] void foo(boolean arg2);
+ };
+ """)
+
+ results = parser.finish()
+ except Exception as e:
+ harness.ok(False, "Shouldn't have thrown for [CEReactions] used on regular operations. %s" % e)
+ threw = True
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ [CEReactions] readonly attribute boolean A;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] used on a readonly attribute")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [CEReactions]
+ interface Foo {
+ }
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for [CEReactions] used on a interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ [CEReactions] getter any(DOMString name);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [CEReactions] used on a named getter")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ [CEReactions] legacycaller double compute(double x);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [CEReactions] used on a legacycaller")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ [CEReactions] stringifier DOMString ();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [CEReactions] used on a stringifier")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py b/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py
index 433b7e501a4..8420f2ee4e0 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py
@@ -44,8 +44,8 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, exception:
- pass
+ except Exception as e:
+ exception = e
harness.ok(exception, "Should have thrown.")
harness.check(exception.message,
@@ -70,8 +70,8 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, exception:
- pass
+ except Exception as e:
+ exception = e
harness.ok(exception, "Should have thrown (2).")
harness.check(exception.message,
@@ -100,8 +100,8 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, exception:
- pass
+ except Exception as e:
+ exception = e
harness.ok(exception, "Should have thrown (3).")
harness.check(exception.message,
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_const.py b/components/script/dom/bindings/codegen/parser/tests/test_const.py
index 80b6fb0e9c8..918f284a226 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_const.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_const.py
@@ -12,9 +12,6 @@ expected = [
("::TestConsts::ll", "ll", "LongLong", -8),
("::TestConsts::t", "t", "Boolean", True),
("::TestConsts::f", "f", "Boolean", False),
- ("::TestConsts::n", "n", "BooleanOrNull", None),
- ("::TestConsts::nt", "nt", "BooleanOrNull", True),
- ("::TestConsts::nf", "nf", "BooleanOrNull", False),
("::TestConsts::fl", "fl", "Float", 0.2),
("::TestConsts::db", "db", "Double", 0.2),
("::TestConsts::ufl", "ufl", "UnrestrictedFloat", 0.2),
@@ -39,9 +36,6 @@ def WebIDLTest(parser, harness):
const long long ll = -010;
const boolean t = true;
const boolean f = false;
- const boolean? n = null;
- const boolean? nt = true;
- const boolean? nf = false;
const float fl = 0.2;
const double db = 0.2;
const unrestricted float ufl = 0.2;
@@ -78,3 +72,16 @@ def WebIDLTest(parser, harness):
"Const's value has the same type as the type")
harness.check(const.value.value, value, "Const value has the right value.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestConsts {
+ const boolean? zero = 0;
+ };
+ """)
+ parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "Nullable types are not allowed for consts.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
index 348204c7dc1..83e1f4fc34f 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
@@ -11,9 +11,9 @@ def WebIDLTest(parser, harness):
harness.check(argument.variadic, variadic, "Argument has the right variadic value")
def checkMethod(method, QName, name, signatures,
- static=True, getter=False, setter=False, creator=False,
- deleter=False, legacycaller=False, stringifier=False,
- chromeOnly=False):
+ static=True, getter=False, setter=False, deleter=False,
+ legacycaller=False, stringifier=False, chromeOnly=False,
+ htmlConstructor=False, secureContext=False, pref=None, func=None):
harness.ok(isinstance(method, WebIDL.IDLMethod),
"Should be an IDLMethod")
harness.ok(method.isMethod(), "Method is a method")
@@ -24,12 +24,15 @@ def WebIDLTest(parser, harness):
harness.check(method.isStatic(), static, "Method has the correct static value")
harness.check(method.isGetter(), getter, "Method has the correct getter value")
harness.check(method.isSetter(), setter, "Method has the correct setter value")
- harness.check(method.isCreator(), creator, "Method has the correct creator value")
harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly")
+ harness.check(method.isHTMLConstructor(), htmlConstructor, "Method has the correct htmlConstructor value")
harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
+ harness.check(method.getExtendedAttribute("Pref"), pref, "Method has the correct pref value")
+ harness.check(method.getExtendedAttribute("Func"), func, "Method has the correct func value")
+ harness.check(method.getExtendedAttribute("SecureContext") is not None, secureContext, "Method has the correct SecureContext value")
sigpairs = zip(method.signatures(), signatures)
for (gotSignature, expectedSignature) in sigpairs:
@@ -43,45 +46,116 @@ def WebIDLTest(parser, harness):
(QName, name, type, optional, variadic) = expectedArgs[i]
checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+ def checkResults(results):
+ harness.check(len(results), 3, "Should be three productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[2], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
+ "constructor", [("TestConstructorNoArgs (Wrapper)", [])])
+ harness.check(len(results[0].members), 0,
+ "TestConstructorNoArgs should not have members")
+ checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
+ "constructor",
+ [("TestConstructorWithArgs (Wrapper)",
+ [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
+ harness.check(len(results[1].members), 0,
+ "TestConstructorWithArgs should not have members")
+ checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
+ "constructor",
+ [("TestConstructorOverloads (Wrapper)",
+ [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
+ ("TestConstructorOverloads (Wrapper)",
+ [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
+ harness.check(len(results[2].members), 0,
+ "TestConstructorOverloads should not have members")
+
parser.parse("""
- [Constructor]
interface TestConstructorNoArgs {
+ constructor();
};
- [Constructor(DOMString name)]
interface TestConstructorWithArgs {
+ constructor(DOMString name);
};
- [Constructor(object foo), Constructor(boolean bar)]
interface TestConstructorOverloads {
+ constructor(object foo);
+ constructor(boolean bar);
+ };
+ """)
+ results = parser.finish()
+ checkResults(results)
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestPrefConstructor {
+ [Pref="dom.webidl.test1"] constructor();
};
""")
results = parser.finish()
- harness.check(len(results), 3, "Should be three productions")
+ harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
"Should be an IDLInterface")
- harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+
+ checkMethod(results[0].ctor(), "::TestPrefConstructor::constructor",
+ "constructor", [("TestPrefConstructor (Wrapper)", [])],
+ pref=["dom.webidl.test1"])
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestChromeOnlyConstructor {
+ [ChromeOnly] constructor();
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ checkMethod(results[0].ctor(), "::TestChromeOnlyConstructor::constructor",
+ "constructor", [("TestChromeOnlyConstructor (Wrapper)", [])],
+ chromeOnly=True)
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestSCConstructor {
+ [SecureContext] constructor();
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
"Should be an IDLInterface")
- harness.ok(isinstance(results[2], WebIDL.IDLInterface),
+
+ checkMethod(results[0].ctor(), "::TestSCConstructor::constructor",
+ "constructor", [("TestSCConstructor (Wrapper)", [])],
+ secureContext=True)
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestFuncConstructor {
+ [Func="Document::IsWebAnimationsEnabled"] constructor();
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
"Should be an IDLInterface")
- checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
- "constructor", [("TestConstructorNoArgs (Wrapper)", [])])
- checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
- "constructor",
- [("TestConstructorWithArgs (Wrapper)",
- [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
- checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
- "constructor",
- [("TestConstructorOverloads (Wrapper)",
- [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
- ("TestConstructorOverloads (Wrapper)",
- [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
+ checkMethod(results[0].ctor(), "::TestFuncConstructor::constructor",
+ "constructor", [("TestFuncConstructor (Wrapper)", [])],
+ func=["Document::IsWebAnimationsEnabled"])
parser = parser.reset()
parser.parse("""
- [ChromeConstructor()]
- interface TestChromeConstructor {
+ interface TestPrefChromeOnlySCFuncConstructor {
+ [ChromeOnly, Pref="dom.webidl.test1", SecureContext, Func="Document::IsWebAnimationsEnabled"]
+ constructor();
};
""")
results = parser.finish()
@@ -89,21 +163,262 @@ def WebIDLTest(parser, harness):
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
"Should be an IDLInterface")
- checkMethod(results[0].ctor(), "::TestChromeConstructor::constructor",
- "constructor", [("TestChromeConstructor (Wrapper)", [])],
- chromeOnly=True)
+ checkMethod(results[0].ctor(), "::TestPrefChromeOnlySCFuncConstructor::constructor",
+ "constructor", [("TestPrefChromeOnlySCFuncConstructor (Wrapper)", [])],
+ func=["Document::IsWebAnimationsEnabled"], pref=["dom.webidl.test1"],
+ chromeOnly=True, secureContext=True)
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestHTMLConstructor {
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ checkMethod(results[0].ctor(), "::TestHTMLConstructor::constructor",
+ "constructor", [("TestHTMLConstructor (Wrapper)", [])],
+ htmlConstructor=True)
parser = parser.reset()
threw = False
try:
parser.parse("""
- [Constructor(),
- ChromeConstructor(DOMString a)]
- interface TestChromeConstructor {
+ interface TestChromeOnlyConstructor {
+ constructor()
+ [ChromeOnly] constructor(DOMString a);
};
""")
results = parser.finish()
except:
threw = True
- harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor")
+ harness.ok(threw, "Can't have both a constructor and a ChromeOnly constructor")
+
+ # Test HTMLConstructor with argument
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorWithArgs {
+ [HTMLConstructor] constructor(DOMString a);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "HTMLConstructor should take no argument")
+
+ # Test HTMLConstructor on a callback interface
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ callback interface TestHTMLConstructorOnCallbackInterface {
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "HTMLConstructor can't be used on a callback interface")
+
+ # Test HTMLConstructor and constructor operation
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorAndConstructor {
+ constructor();
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Can't have both a constructor and a HTMLConstructor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorAndConstructor {
+ [Throws]
+ constructor();
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have both a throwing constructor and a HTMLConstructor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorAndConstructor {
+ constructor(DOMString a);
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have both a HTMLConstructor and a constructor operation")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorAndConstructor {
+ [Throws]
+ constructor(DOMString a);
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have both a HTMLConstructor and a throwing constructor "
+ "operation")
+
+ # Test HTMLConstructor and [ChromeOnly] constructor operation
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorAndConstructor {
+ [ChromeOnly]
+ constructor();
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have both a ChromeOnly constructor and a HTMLConstructor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorAndConstructor {
+ [Throws, ChromeOnly]
+ constructor();
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have both a throwing chromeonly constructor and a "
+ "HTMLConstructor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorAndConstructor {
+ [ChromeOnly]
+ constructor(DOMString a);
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have both a HTMLConstructor and a chromeonly constructor "
+ "operation")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestHTMLConstructorAndConstructor {
+ [Throws, ChromeOnly]
+ constructor(DOMString a);
+ [HTMLConstructor] constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have both a HTMLConstructor and a throwing chromeonly "
+ "constructor operation")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [NoInterfaceObject]
+ interface InterfaceWithoutInterfaceObject {
+ constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have a constructor operation on a [NoInterfaceObject] "
+ "interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface InterfaceWithPartial {
+ };
+
+ partial interface InterfaceWithPartial {
+ constructor();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have a constructor operation on a partial interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface InterfaceWithMixin {
+ };
+
+ interface mixin Mixin {
+ constructor();
+ };
+
+ InterfaceWithMixin includes Mixin
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Can't have a constructor operation on a mixin")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor_global.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor_global.py
new file mode 100644
index 00000000000..b7eabb1e35b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor_global.py
@@ -0,0 +1,63 @@
+import traceback
+
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ [Global, Exposed=TestConstructorGlobal]
+ interface TestConstructorGlobal {
+ constructor();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global, Exposed=TestNamedConstructorGlobal,
+ NamedConstructor=FooBar]
+ interface TestNamedConstructorGlobal {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [NamedConstructor=FooBar, Global,
+ Exposed=TestNamedConstructorGlobal]
+ interface TestNamedConstructorGlobal {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global, Exposed=TestHTMLConstructorGlobal]
+ interface TestHTMLConstructorGlobal {
+ [HTMLConstructor] constructor();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py
index 2b09ae71e69..24cc36066cd 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py
@@ -2,8 +2,9 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- [Constructor, NoInterfaceObject]
+ [NoInterfaceObject]
interface TestConstructorNoInterfaceObject {
+ constructor();
};
""")
@@ -13,11 +14,23 @@ def WebIDLTest(parser, harness):
harness.ok(threw, "Should have thrown.")
+ parser = parser.reset()
+
+ parser.parse("""
+ [NoInterfaceObject, NamedConstructor=FooBar]
+ interface TestNamedConstructorNoInterfaceObject {
+ };
+ """)
+
+ # Test HTMLConstructor and NoInterfaceObject
+ parser = parser.reset()
+
threw = False
try:
parser.parse("""
- [NoInterfaceObject, Constructor]
- interface TestConstructorNoInterfaceObject {
+ [NoInterfaceObject]
+ interface TestHTMLConstructorNoInterfaceObject {
+ [HTMLConstructor] constructor();
};
""")
@@ -26,11 +39,3 @@ def WebIDLTest(parser, harness):
threw = True
harness.ok(threw, "Should have thrown.")
-
- parser = parser.reset()
-
- parser.parse("""
- [NoInterfaceObject, NamedConstructor=FooBar]
- interface TestNamedConstructorNoInterfaceObject {
- };
- """)
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_date.py b/components/script/dom/bindings/codegen/parser/tests/test_date.py
deleted file mode 100644
index 2bdfc95e14f..00000000000
--- a/components/script/dom/bindings/codegen/parser/tests/test_date.py
+++ /dev/null
@@ -1,15 +0,0 @@
-def WebIDLTest(parser, harness):
- parser.parse("""
- interface WithDates {
- attribute Date foo;
- void bar(Date arg);
- void baz(sequence<Date> arg);
- };
- """)
-
- results = parser.finish()
- harness.ok(results[0].members[0].type.isDate(), "Should have Date")
- harness.ok(results[0].members[1].signatures()[0][1][0].type.isDate(),
- "Should have Date argument")
- harness.ok(not results[0].members[2].signatures()[0][1][0].type.isDate(),
- "Should have non-Date argument")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py b/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py
index 2c0fa61239d..3cad3022389 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py
@@ -26,6 +26,31 @@ def WebIDLTest(parser, harness):
harness.check(dict2.members[1].identifier.name, "child",
"'a' really comes before 'c'")
+ # Test partial dictionary.
+ parser = parser.reset();
+ parser.parse("""
+ dictionary A {
+ long c;
+ long g;
+ };
+ partial dictionary A {
+ long h;
+ long d;
+ };
+ """)
+ results = parser.finish()
+
+ dict1 = results[0];
+ harness.check(len(dict1.members), 4, "Dict1 has four members")
+ harness.check(dict1.members[0].identifier.name, "c",
+ "c should be first")
+ harness.check(dict1.members[1].identifier.name, "d",
+ "d should come after c")
+ harness.check(dict1.members[2].identifier.name, "g",
+ "g should come after d")
+ harness.check(dict1.members[3].identifier.name, "h",
+ "h should be last")
+
# Now reset our parser
parser = parser.reset()
threw = False
@@ -42,6 +67,24 @@ def WebIDLTest(parser, harness):
harness.ok(threw, "Should not allow name duplication in a dictionary")
+ # Test no name duplication across normal and partial dictionary.
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ long prop = 5;
+ };
+ partial dictionary A {
+ long prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow name duplication across normal and partial dictionary")
+
# Now reset our parser again
parser = parser.reset()
threw = False
@@ -131,6 +174,22 @@ def WebIDLTest(parser, harness):
dictionary A {
};
interface X {
+ void doFoo(optional A arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Trailing dictionary arg must have a default value")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
void doFoo((A or DOMString) arg);
};
""")
@@ -148,6 +207,23 @@ def WebIDLTest(parser, harness):
dictionary A {
};
interface X {
+ void doFoo(optional (A or DOMString) arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Trailing union arg containing a dictionary must have a default value")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
void doFoo(A arg1, optional long arg2);
};
""")
@@ -164,6 +240,22 @@ def WebIDLTest(parser, harness):
dictionary A {
};
interface X {
+ void doFoo(optional A arg1, optional long arg2);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg followed by optional arg must have default value")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
void doFoo(A arg1, optional long arg2, long arg3);
};
""")
@@ -193,6 +285,24 @@ def WebIDLTest(parser, harness):
"be optional")
parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (A or DOMString) arg1, optional long arg2);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Union arg containing dictionary followed by optional arg must "
+ "have a default value")
+
+ parser = parser.reset()
parser.parse("""
dictionary A {
};
@@ -210,14 +320,75 @@ def WebIDLTest(parser, harness):
dictionary A {
};
interface X {
- void doFoo(optional A? arg1);
+ void doFoo(optional A? arg1 = {});
};
""")
results = parser.finish()
- except:
- threw = True
+ except Exception as x:
+ threw = x
+
+ harness.ok(threw, "Optional dictionary arg must not be nullable")
+ harness.ok("nullable" in str(threw),
+ "Must have the expected exception for optional nullable dictionary arg")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ required long x;
+ };
+ interface X {
+ void doFoo(A? arg1);
+ };
+ """)
+ results = parser.finish()
+ except Exception as x:
+ threw = x
+
+ harness.ok(threw, "Required dictionary arg must not be nullable")
+ harness.ok("nullable" in str(threw),
+ "Must have the expected exception for required nullable "
+ "dictionary arg")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (A or long)? arg1 = {});
+ };
+ """)
+ results = parser.finish()
+ except Exception as x:
+ threw = x
+
+ harness.ok(threw, "Dictionary arg must not be in an optional nullable union")
+ harness.ok("nullable" in str(threw),
+ "Must have the expected exception for optional nullable union "
+ "arg containing dictionary")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ required long x;
+ };
+ interface X {
+ void doFoo((A or long)? arg1);
+ };
+ """)
+ results = parser.finish()
+ except Exception as x:
+ threw = x
- harness.ok(threw, "Dictionary arg must not be nullable")
+ harness.ok(threw, "Dictionary arg must not be in a required nullable union")
+ harness.ok("nullable" in str(threw),
+ "Must have the expected exception for required nullable union "
+ "arg containing dictionary")
parser = parser.reset()
threw = False
@@ -226,14 +397,15 @@ def WebIDLTest(parser, harness):
dictionary A {
};
interface X {
- void doFoo(optional (A or long)? arg1);
+ void doFoo(sequence<A?> arg1);
};
""")
results = parser.finish()
except:
threw = True
- harness.ok(threw, "Dictionary arg must not be in a nullable union")
+ harness.ok(not threw,
+ "Nullable union should be allowed in a sequence argument")
parser = parser.reset()
threw = False
@@ -283,7 +455,7 @@ def WebIDLTest(parser, harness):
dictionary A {
};
interface X {
- void doFoo(optional A arg);
+ void doFoo(optional A arg = {});
};
""")
results = parser.finish()
@@ -294,13 +466,24 @@ def WebIDLTest(parser, harness):
dictionary A {
};
interface X {
- void doFoo(optional (A or DOMString) arg);
+ void doFoo(optional (A or DOMString) arg = {});
};
""")
results = parser.finish()
harness.ok(True, "Union arg containing a dictionary should actually parse")
parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (A or DOMString) arg = "abc");
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Union arg containing a dictionary with string default should actually parse")
+
+ parser = parser.reset()
threw = False
try:
parser.parse("""
@@ -553,3 +736,17 @@ def WebIDLTest(parser, harness):
threw = True
harness.ok(threw, "Only unrestricted values can be initialized to NaN")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ long module;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw, "Should be able to use 'module' as a dictionary member name")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py
index d7780c1ffa1..505b36468d6 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py
@@ -3,13 +3,16 @@ def firstArgType(method):
def WebIDLTest(parser, harness):
parser.parse("""
+ // Give our dictionary a required member so we don't need to
+ // mess with optional and default values.
dictionary Dict {
+ required long member;
};
callback interface Foo {
};
interface Bar {
// Bit of a pain to get things that have dictionary types
- void passDict(optional Dict arg);
+ void passDict(Dict arg);
void passFoo(Foo arg);
void passNullableUnion((object? or DOMString) arg);
void passNullable(Foo? arg);
@@ -56,8 +59,6 @@ def WebIDLTest(parser, harness):
void passKid(Kid arg);
void passParent(Parent arg);
void passGrandparent(Grandparent arg);
- void passImplemented(Implemented arg);
- void passImplementedParent(ImplementedParent arg);
void passUnrelated1(Unrelated1 arg);
void passUnrelated2(Unrelated2 arg);
void passArrayBuffer(ArrayBuffer arg);
@@ -67,9 +68,6 @@ def WebIDLTest(parser, harness):
interface Kid : Parent {};
interface Parent : Grandparent {};
interface Grandparent {};
- interface Implemented : ImplementedParent {};
- Parent implements Implemented;
- interface ImplementedParent {};
interface Unrelated1 {};
interface Unrelated2 {};
""")
@@ -151,47 +149,50 @@ def WebIDLTest(parser, harness):
# Now let's test our whole distinguishability table
argTypes = [ "long", "short", "long?", "short?", "boolean",
- "boolean?", "DOMString", "ByteString", "Enum", "Enum2",
+ "boolean?", "DOMString", "ByteString", "UTF8String", "Enum", "Enum2",
"Interface", "Interface?",
- "AncestorInterface", "UnrelatedInterface",
- "ImplementedInterface", "CallbackInterface",
+ "AncestorInterface", "UnrelatedInterface", "CallbackInterface",
"CallbackInterface?", "CallbackInterface2",
- "object", "Callback", "Callback2", "optional Dict",
- "optional Dict2", "sequence<long>", "sequence<short>",
- "MozMap<object>", "MozMap<Dict>", "MozMap<long>",
- "Date", "Date?", "any",
- "Promise<any>", "Promise<any>?",
- "USVString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer",
- "Uint8Array", "Uint16Array" ]
- # When we can parse Date and RegExp, we need to add them here.
+ "object", "Callback", "Callback2", "Dict",
+ "Dict2", "sequence<long>", "sequence<short>",
+ "record<DOMString, object>",
+ "record<USVString, Dict>",
+ "record<ByteString, long>",
+ "record<UTF8String, long>",
+ "any", "Promise<any>", "Promise<any>?",
+ "USVString", "JSString", "ArrayBuffer", "ArrayBufferView",
+ "Uint8Array", "Uint16Array",
+ "(long or Callback)", "(long or Dict)",
+ ]
# Try to categorize things a bit to keep list lengths down
def allBut(list1, list2):
return [a for a in list1 if a not in list2 and
(a != "any" and a != "Promise<any>" and a != "Promise<any>?")]
+ unions = [ "(long or Callback)", "(long or Dict)" ]
numerics = [ "long", "short", "long?", "short?" ]
booleans = [ "boolean", "boolean?" ]
primitives = numerics + booleans
- nonNumerics = allBut(argTypes, numerics)
+ nonNumerics = allBut(argTypes, numerics + unions)
nonBooleans = allBut(argTypes, booleans)
- strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString" ]
+ strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString", "JSString", "UTF8String" ]
nonStrings = allBut(argTypes, strings)
nonObjects = primitives + strings
objects = allBut(argTypes, nonObjects )
bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"]
- sharedBufferSourceTypes = ["SharedArrayBuffer"]
interfaces = [ "Interface", "Interface?", "AncestorInterface",
- "UnrelatedInterface", "ImplementedInterface" ] + bufferSourceTypes + sharedBufferSourceTypes
- nullables = ["long?", "short?", "boolean?", "Interface?",
- "CallbackInterface?", "optional Dict", "optional Dict2",
- "Date?", "any", "Promise<any>?"]
- dates = [ "Date", "Date?" ]
+ "UnrelatedInterface" ] + bufferSourceTypes
+ nullables = (["long?", "short?", "boolean?", "Interface?",
+ "CallbackInterface?", "Dict", "Dict2",
+ "Date?", "any", "Promise<any>?"] +
+ allBut(unions, [ "(long or Callback)" ]))
sequences = [ "sequence<long>", "sequence<short>" ]
- nonUserObjects = nonObjects + interfaces + dates + sequences
+ nonUserObjects = nonObjects + interfaces + sequences
otherObjects = allBut(argTypes, nonUserObjects + ["object"])
notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] +
- otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes)
- mozMaps = [ "MozMap<object>", "MozMap<Dict>", "MozMap<long>" ]
+ otherObjects + sequences + bufferSourceTypes)
+ records = [ "record<DOMString, object>", "record<USVString, Dict>",
+ "record<ByteString, long>", "record<UTF8String, 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.
@@ -210,7 +211,9 @@ def WebIDLTest(parser, harness):
setDistinguishable("boolean?", allBut(nonBooleans, nullables))
setDistinguishable("DOMString", nonStrings)
setDistinguishable("ByteString", nonStrings)
+ setDistinguishable("UTF8String", nonStrings)
setDistinguishable("USVString", nonStrings)
+ setDistinguishable("JSString", nonStrings)
setDistinguishable("Enum", nonStrings)
setDistinguishable("Enum2", nonStrings)
setDistinguishable("Interface", notRelatedInterfaces)
@@ -218,24 +221,23 @@ def WebIDLTest(parser, harness):
setDistinguishable("AncestorInterface", notRelatedInterfaces)
setDistinguishable("UnrelatedInterface",
allBut(argTypes, ["object", "UnrelatedInterface"]))
- setDistinguishable("ImplementedInterface", notRelatedInterfaces)
setDistinguishable("CallbackInterface", nonUserObjects)
setDistinguishable("CallbackInterface?", allBut(nonUserObjects, nullables))
setDistinguishable("CallbackInterface2", nonUserObjects)
setDistinguishable("object", nonObjects)
setDistinguishable("Callback", nonUserObjects)
setDistinguishable("Callback2", nonUserObjects)
- setDistinguishable("optional Dict", allBut(nonUserObjects, nullables))
- setDistinguishable("optional Dict2", allBut(nonUserObjects, nullables))
+ setDistinguishable("Dict", allBut(nonUserObjects, nullables))
+ setDistinguishable("Dict2", allBut(nonUserObjects, nullables))
setDistinguishable("sequence<long>",
allBut(argTypes, sequences + ["object"]))
setDistinguishable("sequence<short>",
allBut(argTypes, sequences + ["object"]))
- setDistinguishable("MozMap<object>", nonUserObjects)
- setDistinguishable("MozMap<Dict>", nonUserObjects)
- setDistinguishable("MozMap<long>", nonUserObjects)
- setDistinguishable("Date", allBut(argTypes, dates + ["object"]))
- setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"]))
+ setDistinguishable("record<DOMString, object>", nonUserObjects)
+ setDistinguishable("record<USVString, Dict>", nonUserObjects)
+ # JSString not supported in records
+ setDistinguishable("record<ByteString, long>", nonUserObjects)
+ setDistinguishable("record<UTF8String, long>", nonUserObjects)
setDistinguishable("any", [])
setDistinguishable("Promise<any>", [])
setDistinguishable("Promise<any>?", [])
@@ -243,7 +245,10 @@ def WebIDLTest(parser, harness):
setDistinguishable("ArrayBufferView", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"]))
setDistinguishable("Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"]))
setDistinguishable("Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"]))
- setDistinguishable("SharedArrayBuffer", allBut(argTypes, ["SharedArrayBuffer", "object"]))
+ setDistinguishable("(long or Callback)",
+ allBut(nonUserObjects, numerics))
+ setDistinguishable("(long or Dict)",
+ allBut(nonUserObjects, numerics + nullables))
def areDistinguishable(type1, type2):
return data[type1].get(type2, False)
@@ -255,15 +260,14 @@ def WebIDLTest(parser, harness):
interface Interface : AncestorInterface {};
interface AncestorInterface {};
interface UnrelatedInterface {};
- interface ImplementedInterface {};
- Interface implements ImplementedInterface;
callback interface CallbackInterface {};
callback interface CallbackInterface2 {};
callback Callback = any();
callback Callback2 = long(short arg);
- dictionary Dict {};
- dictionary Dict2 {};
- interface _Promise {};
+ // Give our dictionaries required members so we don't need to
+ // mess with optional and default values.
+ dictionary Dict { required long member; };
+ dictionary Dict2 { required long member; };
interface TestInterface {%s
};
"""
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py b/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py
index 799f2e0e0ed..4874b3aafe6 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py
@@ -30,20 +30,6 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface DuplicateQualifiers3 {
- creator creator byte foo(unsigned long index, byte value);
- };
- """)
-
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown.")
-
- threw = False
- try:
- parser.parse("""
interface DuplicateQualifiers4 {
deleter deleter byte foo(unsigned long index);
};
@@ -68,17 +54,3 @@ def WebIDLTest(parser, harness):
threw = True
harness.ok(threw, "Should have thrown.")
-
- threw = False
- try:
- results = parser.parse("""
- interface DuplicateQualifiers6 {
- creator setter creator byte foo(unsigned long index, byte value);
- };
- """)
-
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py b/components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py
index 350ae72f022..a713266c88e 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py
@@ -10,7 +10,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
harness.ok(threw, "Constant cannot have [] as a default value")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py b/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py
index ca0674aec04..7afd15513c6 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py
@@ -8,7 +8,7 @@ def WebIDLTest(parser, harness):
try:
parser.parse(input)
results = parser.finish()
- except WebIDL.WebIDLError, e:
+ except WebIDL.WebIDLError as e:
threw = True
lines = str(e).split('\n')
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py b/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py
index f11222e7a4d..70bb1883682 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py
@@ -14,7 +14,7 @@ interface ?"""
try:
parser.parse(input)
results = parser.finish()
- except WebIDL.WebIDLError, e:
+ except WebIDL.WebIDLError as e:
threw = True
lines = str(e).split('\n')
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py b/components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py
index 48957098bfe..e0241a56426 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py
@@ -2,9 +2,9 @@ import WebIDL
def WebIDLTest(parser, harness):
parser.parse("""
- [PrimaryGlobal] interface Foo {};
- [Global=(Bar1,Bar2)] interface Bar {};
- [Global=Baz2] interface Baz {};
+ [Global, Exposed=Foo] interface Foo {};
+ [Global=(Bar, Bar1,Bar2), Exposed=Bar] interface Bar {};
+ [Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
[Exposed=(Foo,Bar1)]
interface Iface {
@@ -51,10 +51,11 @@ def WebIDLTest(parser, harness):
parser = parser.reset()
parser.parse("""
- [PrimaryGlobal] interface Foo {};
- [Global=(Bar1,Bar2)] interface Bar {};
- [Global=Baz2] interface Baz {};
+ [Global, Exposed=Foo] interface Foo {};
+ [Global=(Bar, Bar1, Bar2), Exposed=Bar] interface Bar {};
+ [Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
+ [Exposed=Foo]
interface Iface2 {
void method3();
};
@@ -80,9 +81,9 @@ def WebIDLTest(parser, harness):
parser = parser.reset()
parser.parse("""
- [PrimaryGlobal] interface Foo {};
- [Global=(Bar1,Bar2)] interface Bar {};
- [Global=Baz2] interface Baz {};
+ [Global, Exposed=Foo] interface Foo {};
+ [Global=(Bar, Bar1, Bar2), Exposed=Bar] interface Bar {};
+ [Global=(Baz, Baz2), Exposed=Baz] interface Baz {};
[Exposed=Foo]
interface Iface3 {
@@ -90,11 +91,11 @@ def WebIDLTest(parser, harness):
};
[Exposed=(Foo,Bar1)]
- interface Mixin {
+ interface mixin Mixin {
void method5();
};
- Iface3 implements Mixin;
+ Iface3 includes Mixin;
""")
results = parser.finish()
harness.check(len(results), 6, "Should know about six things");
@@ -124,7 +125,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown on invalid Exposed value on interface.")
@@ -140,7 +141,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown on invalid Exposed value on attribute.")
@@ -156,7 +157,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown on invalid Exposed value on operation.")
@@ -172,7 +173,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown on invalid Exposed value on constant.")
@@ -181,8 +182,8 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- [Global] interface Foo {};
- [Global] interface Bar {};
+ [Global, Exposed=Foo] interface Foo {};
+ [Global, Exposed=Bar] interface Bar {};
[Exposed=Foo]
interface Baz {
@@ -192,31 +193,46 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown on member exposed where its interface is not.")
parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- [Global] interface Foo {};
- [Global] interface Bar {};
+ parser.parse("""
+ [Global, Exposed=Foo] interface Foo {};
+ [Global, Exposed=Bar] interface Bar {};
- [Exposed=Foo]
- interface Baz {
- void method();
- };
+ [Exposed=Foo]
+ interface Baz {
+ void method();
+ };
- [Exposed=Bar]
- interface Mixin {};
+ [Exposed=Bar]
+ interface mixin Mixin {
+ void otherMethod();
+ };
- Baz implements Mixin;
- """)
+ Baz includes Mixin;
+ """)
+
+ results = parser.finish()
+
+ harness.check(len(results), 5, "Should know about five things");
+ iface = results[2]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = iface.members
+ harness.check(len(members), 2, "Should have two members")
+
+ harness.ok(members[0].exposureSet == set(["Foo"]),
+ "method should have the right exposure set")
+ harness.ok(members[0]._exposureGlobalNames == set(["Foo"]),
+ "method should have the right exposure global names")
+
+ harness.ok(members[1].exposureSet == set(["Bar"]),
+ "otherMethod should have the right exposure set")
+ harness.ok(members[1]._exposureGlobalNames == set(["Bar"]),
+ "otherMethod should have the right exposure global names")
- results = parser.finish()
- except Exception,x:
- threw = True
- harness.ok(threw, "Should have thrown on LHS of implements being exposed where RHS is not.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py
index 85a70d98f2c..144c945bc10 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py
@@ -56,9 +56,9 @@ def WebIDLTest(parser, harness):
results = parser.finish()
# Pull out the first argument out of the arglist of the first (and
# only) signature.
- harness.ok(results[0].members[0].signatures()[0][1][0].clamp,
+ harness.ok(results[0].members[0].signatures()[0][1][0].type.hasClamp(),
"Should be clamped")
- harness.ok(not results[0].members[1].signatures()[0][1][0].clamp,
+ harness.ok(not results[0].members[1].signatures()[0][1][0].type.hasClamp(),
"Should not be clamped")
parser = parser.reset()
@@ -86,9 +86,9 @@ def WebIDLTest(parser, harness):
results = parser.finish()
# Pull out the first argument out of the arglist of the first (and
# only) signature.
- harness.ok(results[0].members[0].signatures()[0][1][0].enforceRange,
+ harness.ok(results[0].members[0].signatures()[0][1][0].type.hasEnforceRange(),
"Should be enforceRange")
- harness.ok(not results[0].members[1].signatures()[0][1][0].enforceRange,
+ harness.ok(not results[0].members[1].signatures()[0][1][0].type.hasEnforceRange(),
"Should not be enforceRange")
parser = parser.reset()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_float_types.py b/components/script/dom/bindings/codegen/parser/tests/test_float_types.py
index 718f09c114b..b7325cf9d26 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_float_types.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_float_types.py
@@ -68,7 +68,7 @@ def WebIDLTest(parser, harness):
long m(float arg);
};
""")
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "[LenientFloat] only allowed on void methods")
@@ -81,7 +81,7 @@ def WebIDLTest(parser, harness):
void m(unrestricted float arg);
};
""")
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args")
@@ -94,7 +94,7 @@ def WebIDLTest(parser, harness):
void m(sequence<unrestricted float> arg);
};
""")
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args (2)")
@@ -107,7 +107,7 @@ def WebIDLTest(parser, harness):
void m((unrestricted float or FloatTypes) arg);
};
""")
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args (3)")
@@ -120,6 +120,6 @@ def WebIDLTest(parser, harness):
readonly attribute float foo;
};
""")
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "[LenientFloat] only allowed on writable attributes")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py b/components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py
index c752cecd298..28b79642d86 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py
@@ -1,9 +1,10 @@
def WebIDLTest(parser, harness):
parser.parse("""
- [Global]
+ [Global, Exposed=Foo]
interface Foo : Bar {
getter any(DOMString name);
};
+ [Exposed=Foo]
interface Bar {};
""")
@@ -18,7 +19,7 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- [Global]
+ [Global, Exposed=Foo]
interface Foo {
getter any(DOMString name);
setter void(DOMString name, any arg);
@@ -36,25 +37,7 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- [Global]
- interface Foo {
- getter any(DOMString name);
- creator void(DOMString name, any arg);
- };
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw,
- "Should have thrown for [Global] used on an interface with a "
- "named creator")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- [Global]
+ [Global, Exposed=Foo]
interface Foo {
getter any(DOMString name);
deleter void(DOMString name);
@@ -72,7 +55,7 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- [Global, OverrideBuiltins]
+ [Global, OverrideBuiltins, Exposed=Foo]
interface Foo {
};
""")
@@ -88,10 +71,10 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- [Global]
+ [Global, Exposed=Foo]
interface Foo : Bar {
};
- [OverrideBuiltins]
+ [OverrideBuiltins, Exposed=Foo]
interface Bar {
};
""")
@@ -107,9 +90,10 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- [Global]
+ [Global, Exposed=Foo]
interface Foo {
};
+ [Exposed=Foo]
interface Bar : Foo {
};
""")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_identifier_conflict.py b/components/script/dom/bindings/codegen/parser/tests/test_identifier_conflict.py
new file mode 100644
index 00000000000..0e9a6654aa7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_identifier_conflict.py
@@ -0,0 +1,39 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum Foo { "a" };
+ interface Foo;
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception as e:
+ harness.ok("Name collision" in e.message,
+ "Should have name collision for interface")
+
+ parser = parser.reset()
+ try:
+ parser.parse("""
+ dictionary Foo { long x; };
+ enum Foo { "a" };
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception as e:
+ harness.ok("Name collision" in e.message,
+ "Should have name collision for dictionary")
+
+ parser = parser.reset()
+ try:
+ parser.parse("""
+ enum Foo { "a" };
+ enum Foo { "b" };
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception as e:
+ harness.ok("Multiple unresolvable definitions" in e.message,
+ "Should have name collision for dictionary")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_implements.py b/components/script/dom/bindings/codegen/parser/tests/test_implements.py
deleted file mode 100644
index 04c47d92abe..00000000000
--- a/components/script/dom/bindings/codegen/parser/tests/test_implements.py
+++ /dev/null
@@ -1,216 +0,0 @@
-# Import the WebIDL module, so we can do isinstance checks and whatnot
-import WebIDL
-
-def WebIDLTest(parser, harness):
- # Basic functionality
- threw = False
- try:
- parser.parse("""
- A implements B;
- interface B {
- attribute long x;
- };
- interface A {
- attribute long y;
- };
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(not threw, "Should not have thrown on implements statement "
- "before interfaces")
- harness.check(len(results), 3, "We have three statements")
- harness.ok(isinstance(results[1], WebIDL.IDLInterface), "B is an interface")
- harness.check(len(results[1].members), 1, "B has one member")
- A = results[2]
- harness.ok(isinstance(A, WebIDL.IDLInterface), "A is an interface")
- harness.check(len(A.members), 2, "A has two members")
- harness.check(A.members[0].identifier.name, "y", "First member is 'y'")
- harness.check(A.members[1].identifier.name, "x", "Second member is 'x'")
-
- # Duplicated member names not allowed
- threw = False
- try:
- parser.parse("""
- C implements D;
- interface D {
- attribute long x;
- };
- interface C {
- attribute long x;
- };
- """)
- parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown on implemented interface duplicating "
- "a name on base interface")
-
- # Same, but duplicated across implemented interfaces
- threw = False
- try:
- parser.parse("""
- E implements F;
- E implements G;
- interface F {
- attribute long x;
- };
- interface G {
- attribute long x;
- };
- interface E {};
- """)
- parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown on implemented interfaces "
- "duplicating each other's member names")
-
- # Same, but duplicated across indirectly implemented interfaces
- threw = False
- try:
- parser.parse("""
- H implements I;
- H implements J;
- I implements K;
- interface K {
- attribute long x;
- };
- interface L {
- attribute long x;
- };
- interface I {};
- interface J : L {};
- interface H {};
- """)
- parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown on indirectly implemented interfaces "
- "duplicating each other's member names")
-
- # Same, but duplicated across an implemented interface and its parent
- threw = False
- try:
- parser.parse("""
- M implements N;
- interface O {
- attribute long x;
- };
- interface N : O {
- attribute long x;
- };
- interface M {};
- """)
- parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown on implemented interface and its "
- "ancestor duplicating member names")
-
- # Reset the parser so we can actually find things where we expect
- # them in the list
- parser = parser.reset()
-
- # Diamonds should be allowed
- threw = False
- try:
- parser.parse("""
- P implements Q;
- P implements R;
- Q implements S;
- R implements S;
- interface Q {};
- interface R {};
- interface S {
- attribute long x;
- };
- interface P {};
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(not threw, "Diamond inheritance is fine")
- harness.check(results[6].identifier.name, "S", "We should be looking at 'S'")
- harness.check(len(results[6].members), 1, "S should have one member")
- harness.check(results[6].members[0].identifier.name, "x",
- "S's member should be 'x'")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface TestInterface {
- };
- callback interface TestCallbackInterface {
- };
- TestInterface implements TestCallbackInterface;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw,
- "Should not allow callback interfaces on the right-hand side "
- "of 'implements'")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface TestInterface {
- };
- callback interface TestCallbackInterface {
- };
- TestCallbackInterface implements TestInterface;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw,
- "Should not allow callback interfaces on the left-hand side of "
- "'implements'")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface TestInterface {
- };
- dictionary Dict {
- };
- Dict implements TestInterface;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw,
- "Should not allow non-interfaces on the left-hand side "
- "of 'implements'")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface TestInterface {
- };
- dictionary Dict {
- };
- TestInterface implements Dict;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw,
- "Should not allow non-interfaces on the right-hand side "
- "of 'implements'")
-
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface.py b/components/script/dom/bindings/codegen/parser/tests/test_interface.py
index e8ed67b54b3..47db3ae4cc9 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_interface.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interface.py
@@ -84,100 +84,6 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface A {};
- interface B {};
- A implements B;
- B implements A;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should not allow cycles via implements")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface A {};
- interface C {};
- interface B {};
- A implements C;
- C implements B;
- B implements A;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should not allow indirect cycles via implements")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface A : B {};
- interface B {};
- B implements A;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should not allow inheriting from an interface that implements us")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface A : B {};
- interface B {};
- interface C {};
- B implements C;
- C implements A;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should not allow inheriting from an interface that indirectly implements us")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface A : B {};
- interface B : C {};
- interface C {};
- C implements A;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should not allow indirectly inheriting from an interface that implements us")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
- interface A : B {};
- interface B : C {};
- interface C {};
- interface D {};
- C implements D;
- D implements A;
- """)
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should not allow indirectly inheriting from an interface that indirectly implements us")
-
- parser = parser.reset()
- threw = False
- try:
- parser.parse("""
interface A;
interface B : A {};
""")
@@ -189,12 +95,12 @@ def WebIDLTest(parser, harness):
parser = parser.reset()
parser.parse("""
- [Constructor(long arg)]
interface A {
+ constructor();
+ constructor(long arg);
readonly attribute boolean x;
void foo();
};
- [Constructor]
partial interface A {
readonly attribute boolean y;
void foo(long arg);
@@ -219,13 +125,13 @@ def WebIDLTest(parser, harness):
parser = parser.reset()
parser.parse("""
- [Constructor]
partial interface A {
readonly attribute boolean y;
void foo(long arg);
};
- [Constructor(long arg)]
interface A {
+ constructor();
+ constructor(long arg);
readonly attribute boolean x;
void foo();
};
@@ -376,30 +282,89 @@ def WebIDLTest(parser, harness):
"Should not allow unknown extended attributes on interfaces")
parser = parser.reset()
+ parser.parse("""
+ [Global, Exposed=Window] interface Window {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface B {};
+ [Exposed=Window, LegacyWindowAlias=(C, D)]
+ interface E {};
+ """);
+ results = parser.finish();
+ harness.check(results[1].legacyWindowAliases, ["A"],
+ "Should support a single identifier")
+ harness.check(results[2].legacyWindowAliases, ["C", "D"],
+ "Should support an identifier list")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [LegacyWindowAlias]
+ interface A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [LegacyWindowAlias] with no value")
+
+ parser = parser.reset()
threw = False
try:
parser.parse("""
+ [Exposed=Worker, LegacyWindowAlias=B]
+ interface A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [LegacyWindowAlias] without Window exposure")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global, Exposed=Window] interface Window {};
+ [Exposed=Window]
+ interface A {};
+ [Exposed=Window, LegacyWindowAlias=A]
interface B {};
- [ArrayClass]
- interface A : B {
- };
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
- "Should not allow [ArrayClass] on interfaces with parents")
+ "Should not allow [LegacyWindowAlias] to conflict with other identifiers")
parser = parser.reset()
threw = False
try:
parser.parse("""
- [ArrayClass]
- interface A {
- };
+ [Global, Exposed=Window] interface Window {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface B {};
+ [Exposed=Window]
+ interface A {};
""")
results = parser.finish()
except:
threw = True
- harness.ok(not threw,
- "Should allow [ArrayClass] on interfaces without parents")
+ harness.ok(threw,
+ "Should not allow [LegacyWindowAlias] to conflict with other identifiers")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global, Exposed=Window] interface Window {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface B {};
+ [Exposed=Window, LegacyWindowAlias=A]
+ interface C {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [LegacyWindowAlias] to conflict with other identifiers")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py b/components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py
index 159b50f84ff..e070adee7e6 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py
@@ -37,10 +37,10 @@ def WebIDLTest(parser, harness):
p.finish()
harness.ok(False,
prefix + " - Interface passed when should've failed")
- except WebIDL.WebIDLError, e:
+ except WebIDL.WebIDLError as e:
harness.ok(True,
prefix + " - Interface failed as expected")
- except Exception, e:
+ except Exception as e:
harness.ok(False,
prefix + " - Interface failed but not as a WebIDLError exception: %s" % e)
@@ -88,6 +88,8 @@ def WebIDLTest(parser, harness):
disallowedNonMethodNames = ["clear", "delete"]
mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames
setDisallowedNonMethodNames = ["add"] + disallowedNonMethodNames
+ unrelatedMembers = [("unrelatedAttribute", WebIDL.IDLAttribute),
+ ("unrelatedMethod", WebIDL.IDLMethod)]
#
# Simple Usage Tests
@@ -99,86 +101,171 @@ def WebIDLTest(parser, harness):
iterable<long>;
readonly attribute unsigned long length;
getter long(unsigned long index);
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
};
- """, valueIterableMembers)
+ """, valueIterableMembers + unrelatedMembers)
+
+ shouldPass("Iterable (key only) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ iterable<long>;
+ readonly attribute unsigned long length;
+ getter long(unsigned long index);
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, valueIterableMembers, numProductions=2)
shouldPass("Iterable (key and value)",
"""
interface Foo1 {
iterable<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
};
- """, iterableMembers,
+ """, iterableMembers + unrelatedMembers,
# numProductions == 2 because of the generated iterator iface,
numProductions=2)
+ shouldPass("Iterable (key and value) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ iterable<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, iterableMembers,
+ # numProductions == 3 because of the generated iterator iface,
+ numProductions=3)
+
shouldPass("Maplike (readwrite)",
"""
interface Foo1 {
maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
};
- """, mapRWMembers)
+ """, mapRWMembers, numProductions=2)
shouldPass("Maplike (readwrite)",
"""
interface Foo1 {
maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ maplike<long, long>;
};
- """, mapRWMembers)
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers, numProductions=2)
shouldPass("Maplike (readonly)",
"""
interface Foo1 {
readonly maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapROMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readonly) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ readonly maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
};
- """, mapROMembers)
+ """, mapROMembers, numProductions=2)
shouldPass("Setlike (readwrite)",
"""
interface Foo1 {
setlike<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
};
- """, setRWMembers)
+ """, setRWMembers + unrelatedMembers)
+
+ shouldPass("Setlike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ setlike<long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setRWMembers, numProductions=2)
shouldPass("Setlike (readonly)",
"""
interface Foo1 {
readonly setlike<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
};
- """, setROMembers)
+ """, setROMembers + unrelatedMembers)
- shouldPass("Inheritance of maplike/setlike",
+ shouldPass("Setlike (readonly) inheriting from parent",
"""
- interface Foo1 {
- maplike<long, long>;
+ interface Foo1 : Foo2 {
+ readonly setlike<long>;
};
- interface Foo2 : Foo1 {
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
};
- """, mapRWMembers, numProductions=2)
+ """, setROMembers, numProductions=2)
- shouldPass("Implements with maplike/setlike",
+ shouldPass("Inheritance of maplike/setlike",
"""
interface Foo1 {
maplike<long, long>;
};
- interface Foo2 {
+ interface Foo2 : Foo1 {
};
- Foo2 implements Foo1;
- """, mapRWMembers, numProductions=3)
+ """, mapRWMembers, numProductions=2)
shouldPass("JS Implemented maplike interface",
"""
- [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
- Constructor()]
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"]
interface Foo1 {
+ constructor();
setlike<long>;
};
""", setRWChromeMembers)
shouldPass("JS Implemented maplike interface",
"""
- [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
- Constructor()]
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"]
interface Foo1 {
+ constructor();
maplike<long, long>;
};
""", mapRWChromeMembers)
@@ -253,31 +340,6 @@ def WebIDLTest(parser, harness):
};
""")
- shouldFail("Consequential interface with conflicting maplike/setlike",
- """
- interface Foo1 {
- maplike<long, long>;
- };
- interface Foo2 {
- setlike<long>;
- };
- Foo2 implements Foo1;
- """)
-
- shouldFail("Consequential interfaces with conflicting maplike/setlike",
- """
- interface Foo1 {
- maplike<long, long>;
- };
- interface Foo2 {
- setlike<long>;
- };
- interface Foo3 {
- };
- Foo3 implements Foo1;
- Foo3 implements Foo2;
- """)
-
#
# Member name collision tests
#
@@ -380,52 +442,28 @@ def WebIDLTest(parser, harness):
};
""", mapRWMembers, numProductions=3)
- shouldFail("Interface with consequential maplike/setlike interface member collision",
- """
- interface Foo1 {
- void entries();
- };
- interface Foo2 {
- maplike<long, long>;
- };
- Foo1 implements Foo2;
- """)
-
- shouldFail("Maplike interface with consequential interface member collision",
+ shouldFail("Maplike interface with mixin member collision",
"""
interface Foo1 {
maplike<long, long>;
};
- interface Foo2 {
+ interface mixin Foo2 {
void entries();
};
- Foo1 implements Foo2;
+ Foo1 includes Foo2;
""")
- shouldPass("Consequential Maplike interface with inherited interface member collision",
- """
- interface Foo1 {
- maplike<long, long>;
- };
- interface Foo2 {
- void entries();
- };
- interface Foo3 : Foo2 {
- };
- Foo3 implements Foo1;
- """, mapRWMembers, numProductions=4)
-
shouldPass("Inherited Maplike interface with consequential interface member collision",
"""
interface Foo1 {
maplike<long, long>;
};
- interface Foo2 {
+ interface mixin Foo2 {
void entries();
};
interface Foo3 : Foo1 {
};
- Foo3 implements Foo2;
+ Foo3 includes Foo2;
""", mapRWMembers, numProductions=4)
shouldFail("Inheritance of name collision with child maplike/setlike",
@@ -548,7 +586,7 @@ def WebIDLTest(parser, harness):
};
""")
- shouldPass("Implemented interface with readonly allowable overrides",
+ shouldPass("Interface with readonly allowable overrides",
"""
interface Foo1 {
readonly setlike<long>;
@@ -558,9 +596,9 @@ def WebIDLTest(parser, harness):
shouldPass("JS Implemented read-only interface with readonly allowable overrides",
"""
- [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
- Constructor()]
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"]
interface Foo1 {
+ constructor();
readonly setlike<long>;
readonly attribute boolean clear;
};
@@ -568,9 +606,9 @@ def WebIDLTest(parser, harness):
shouldFail("JS Implemented read-write interface with non-readwrite allowable overrides",
"""
- [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
- Constructor()]
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"]
interface Foo1 {
+ constructor();
setlike<long>;
readonly attribute boolean clear;
};
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py b/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py
new file mode 100644
index 00000000000..477a9f37799
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py
@@ -0,0 +1,437 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("interface mixin Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface mixin parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterfaceMixin),
+ "Should be an IDLInterfaceMixin")
+ mixin = results[0]
+ harness.check(mixin.identifier.QName(), "::Foo", "Interface mixin has the right QName")
+ harness.check(mixin.identifier.name, "Foo", "Interface mixin has the right name")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface mixin QNameBase {
+ const long foo = 3;
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterfaceMixin),
+ "Should be an IDLInterfaceMixin")
+ harness.check(len(results[0].members), 1, "Expect 1 productions")
+ mixin = results[0]
+ harness.check(mixin.members[0].identifier.QName(), "::QNameBase::foo",
+ "Member has the right QName")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface mixin A {
+ readonly attribute boolean x;
+ void foo();
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ void foo(long arg);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should have two results with partial interface mixin")
+ mixin = results[0]
+ harness.check(len(mixin.members), 3,
+ "Should have three members with partial interface mixin")
+ harness.check(mixin.members[0].identifier.name, "x",
+ "First member should be x with partial interface mixin")
+ harness.check(mixin.members[1].identifier.name, "foo",
+ "Second member should be foo with partial interface mixin")
+ harness.check(len(mixin.members[1].signatures()), 2,
+ "Should have two foo signatures with partial interface mixin")
+ harness.check(mixin.members[2].identifier.name, "y",
+ "Third member should be y with partial interface mixin")
+
+ parser = parser.reset()
+ parser.parse("""
+ partial interface mixin A {
+ readonly attribute boolean y;
+ void foo(long arg);
+ };
+ interface mixin A {
+ readonly attribute boolean x;
+ void foo();
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should have two results with reversed partial interface mixin")
+ mixin = results[1]
+ harness.check(len(mixin.members), 3,
+ "Should have three members with reversed partial interface mixin")
+ harness.check(mixin.members[0].identifier.name, "x",
+ "First member should be x with reversed partial interface mixin")
+ harness.check(mixin.members[1].identifier.name, "foo",
+ "Second member should be foo with reversed partial interface mixin")
+ harness.check(len(mixin.members[1].signatures()), 2,
+ "Should have two foo signatures with reversed partial interface mixin")
+ harness.check(mixin.members[2].identifier.name, "y",
+ "Third member should be y with reversed partial interface mixin")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface Interface {};
+ interface mixin Mixin {
+ attribute short x;
+ };
+ Interface includes Mixin;
+ """)
+ results = parser.finish()
+ iface = results[0]
+ harness.check(len(iface.members), 1, "Should merge members from mixins")
+ harness.check(iface.members[0].identifier.name, "x",
+ "Should merge members from mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ readonly attribute boolean x;
+ };
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow two non-partial interface mixins with the same name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ partial interface mixin A {
+ readonly attribute boolean x;
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Must have a non-partial interface mixin for a given name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between partial interface "
+ "mixin and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between interface mixin "
+ "and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ readonly attribute boolean x;
+ };
+ interface A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between external interface "
+ "and interface mixin")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [SomeRandomAnnotation]
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow unknown extended attributes on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ getter double (DOMString propertyName);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow getters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ setter void (DOMString propertyName, double propertyValue);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow setters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ deleter void (DOMString propertyName);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow deleters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ legacycaller double compute(double x);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow legacycallers on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ inherit attribute x;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow inherited attribute on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Interface {};
+ interface NotMixin {
+ attribute short x;
+ };
+ Interface includes NotMixin;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if the right side does not point an interface mixin")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin NotInterface {};
+ interface mixin Mixin {
+ attribute short x;
+ };
+ NotInterface includes Mixin;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if the left side does not point an interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin Mixin {
+ iterable<DOMString>;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if an interface mixin includes iterable")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin Mixin {
+ setlike<DOMString>;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if an interface mixin includes setlike")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin Mixin {
+ maplike<DOMString, DOMString>;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if an interface mixin includes maplike")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Interface {
+ attribute short attr;
+ };
+ interface mixin Mixin {
+ attribute short attr;
+ };
+ Interface includes Mixin;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if the included mixin interface has duplicated member")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Interface {};
+ interface mixin Mixin1 {
+ attribute short attr;
+ };
+ interface mixin Mixin2 {
+ attribute short attr;
+ };
+ Interface includes Mixin1;
+ Interface includes Mixin2;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if the included mixin interfaces have duplicated member")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Global, Exposed=Window] interface Window {};
+ [Global, Exposed=Worker] interface Worker {};
+ [Exposed=Window]
+ interface Base {};
+ interface mixin Mixin {
+ Base returnSelf();
+ };
+ Base includes Mixin;
+ """)
+ results = parser.finish()
+ base = results[2]
+ attr = base.members[0]
+ harness.check(attr.exposureSet, set(["Window"]),
+ "Should expose on globals where the base interfaces are exposed")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Global, Exposed=Window] interface Window {};
+ [Global, Exposed=Worker] interface Worker {};
+ [Exposed=Window]
+ interface Base {};
+ [Exposed=Window]
+ interface mixin Mixin {
+ attribute short a;
+ };
+ Base includes Mixin;
+ """)
+ results = parser.finish()
+ base = results[2]
+ attr = base.members[0]
+ harness.check(attr.exposureSet, set(["Window"]),
+ "Should follow [Exposed] on interface mixin")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Global, Exposed=Window] interface Window {};
+ [Global, Exposed=Worker] interface Worker {};
+ [Exposed=Window]
+ interface Base1 {};
+ [Exposed=Worker]
+ interface Base2 {};
+ interface mixin Mixin {
+ attribute short a;
+ };
+ Base1 includes Mixin;
+ Base2 includes Mixin;
+ """)
+ results = parser.finish()
+ base = results[2]
+ attr = base.members[0]
+ harness.check(attr.exposureSet, set(["Window", "Worker"]),
+ "Should expose on all globals where including interfaces are "
+ "exposed")
+ base = results[3]
+ attr = base.members[0]
+ harness.check(attr.exposureSet, set(["Window", "Worker"]),
+ "Should expose on all globals where including interfaces are "
+ "exposed")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_method.py b/components/script/dom/bindings/codegen/parser/tests/test_method.py
index cf7f1b40d76..88ee874386c 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_method.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_method.py
@@ -41,7 +41,7 @@ def WebIDLTest(parser, harness):
harness.check(argument.variadic, variadic, "Argument has the right variadic value")
def checkMethod(method, QName, name, signatures,
- static=False, getter=False, setter=False, creator=False,
+ static=False, getter=False, setter=False,
deleter=False, legacycaller=False, stringifier=False):
harness.ok(isinstance(method, WebIDL.IDLMethod),
"Should be an IDLMethod")
@@ -53,7 +53,6 @@ def WebIDLTest(parser, harness):
harness.check(method.isStatic(), static, "Method has the correct static value")
harness.check(method.isGetter(), getter, "Method has the correct getter value")
harness.check(method.isSetter(), setter, "Method has the correct setter value")
- harness.check(method.isCreator(), creator, "Method has the correct creator value")
harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
@@ -121,7 +120,7 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(not threw, "Should allow integer to float type corecion")
@@ -134,7 +133,7 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should not allow [GetterThrows] on methods")
@@ -147,7 +146,7 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should not allow [SetterThrows] on methods")
@@ -160,7 +159,7 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should spell [Throws] correctly on methods")
@@ -173,6 +172,85 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should not allow __noSuchMethod__ methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Throws, LenientFloat]
+ void foo(float myFloat);
+ [Throws]
+ void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception as x:
+ threw = True
+ harness.ok(not threw, "Should allow LenientFloat to be only in a specific overload")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface A {
+ [Throws]
+ void foo();
+ [Throws, LenientFloat]
+ void foo(float myFloat);
+ };
+ """)
+ results = parser.finish()
+ iface = results[0]
+ methods = iface.members
+ lenientFloat = methods[0].getExtendedAttribute("LenientFloat")
+ harness.ok(lenientFloat is not None, "LenientFloat in overloads must be added to the method")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Throws, LenientFloat]
+ void foo(float myFloat);
+ [Throws]
+ void foo(float myFloat, float yourFloat);
+ };
+ """)
+ results = parser.finish()
+ except Exception as x:
+ threw = True
+ harness.ok(threw, "Should prevent overloads from getting different restricted float behavior")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Throws]
+ void foo(float myFloat, float yourFloat);
+ [Throws, LenientFloat]
+ void foo(float myFloat);
+ };
+ """)
+ results = parser.finish()
+ except Exception as x:
+ threw = True
+ harness.ok(threw, "Should prevent overloads from getting different restricted float behavior (2)")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Throws, LenientFloat]
+ void foo(float myFloat);
+ [Throws, LenientFloat]
+ void foo(short myShort);
+ };
+ """)
+ results = parser.finish()
+ except Exception as x:
+ threw = True
+ harness.ok(threw, "Should prevent overloads from getting redundant [LenientFloat]")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_namespace.py b/components/script/dom/bindings/codegen/parser/tests/test_namespace.py
index 74533a1770e..62edb270c63 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_namespace.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_namespace.py
@@ -70,7 +70,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -85,7 +85,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -104,7 +104,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -123,7 +123,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -142,7 +142,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -161,7 +161,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -180,7 +180,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -199,7 +199,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -218,6 +218,6 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_newobject.py b/components/script/dom/bindings/codegen/parser/tests/test_newobject.py
new file mode 100644
index 00000000000..26785c6a270
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_newobject.py
@@ -0,0 +1,70 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Basic functionality
+ parser.parse(
+ """
+ interface Iface {
+ [NewObject] readonly attribute Iface attr;
+ [NewObject] Iface method();
+ };
+ """)
+ results = parser.finish()
+ harness.ok(results, "Should not have thrown on basic [NewObject] usage")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Pure, NewObject] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must depend on something")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Pure, NewObject] Iface method();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] methods must depend on something")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Cached, NewObject, Affects=Nothing] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must not be [Cached]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [StoreInSlot, NewObject, Affects=Nothing] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must not be [StoreInSlot]")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py
index 2b48b615dd4..8ba6771677a 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py
@@ -80,7 +80,7 @@ def checkEquivalent(iface, harness):
for attr in dir(type1):
if attr.startswith('_') or \
attr in ['nullable', 'builtin', 'filename', 'location',
- 'inner', 'QName', 'getDeps', 'name'] or \
+ 'inner', 'QName', 'getDeps', 'name', 'prettyName'] or \
(hasattr(type(type1), attr) and not callable(getattr(type1, attr))):
continue
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_promise.py b/components/script/dom/bindings/codegen/parser/tests/test_promise.py
index 55bc0768092..43c74029dc5 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_promise.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_promise.py
@@ -2,7 +2,6 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface _Promise {};
interface A {
legacycaller Promise<any> foo();
};
@@ -18,7 +17,6 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface _Promise {};
interface A {
Promise<any> foo();
long foo(long arg);
@@ -35,7 +33,6 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface _Promise {};
interface A {
long foo(long arg);
Promise<any> foo();
@@ -49,8 +46,35 @@ def WebIDLTest(parser, harness):
"non-Promise return types.")
parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ Promise<any>? foo();
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow nullable Promise return values.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ void foo(Promise<any>? arg);
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow nullable Promise arguments.")
+
+ parser = parser.reset()
parser.parse("""
- interface _Promise {};
interface A {
Promise<any> foo();
Promise<any> foo(long arg);
@@ -61,3 +85,73 @@ def WebIDLTest(parser, harness):
harness.ok(True,
"Should allow overloads which only have Promise and return "
"types.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ attribute Promise<any> attr;
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow writable Promise-typed attributes.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [LenientSetter] readonly attribute Promise<any> attr;
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [LenientSetter] Promise-typed attributes.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [PutForwards=bar] readonly attribute Promise<any> attr;
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [PutForwards] Promise-typed attributes.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Replaceable] readonly attribute Promise<any> attr;
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [Replaceable] Promise-typed attributes.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SameObject] readonly attribute Promise<any> attr;
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [SameObject] Promise-typed attributes.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_mozmap.py b/components/script/dom/bindings/codegen/parser/tests/test_record.py
index 1a36fdd62c4..d50572caf07 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_mozmap.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_record.py
@@ -3,8 +3,8 @@ import WebIDL
def WebIDLTest(parser, harness):
parser.parse("""
dictionary Dict {};
- interface MozMapArg {
- void foo(MozMap<Dict> arg);
+ interface RecordArg {
+ void foo(record<DOMString, Dict> arg);
};
""")
@@ -19,7 +19,7 @@ def WebIDLTest(parser, harness):
signature = members[0].signatures()[0]
args = signature[1]
harness.check(len(args), 1, "Should have one arg")
- harness.ok(args[0].type.isMozMap(), "Should have a MozMap type here")
+ harness.ok(args[0].type.isRecord(), "Should have a record type here")
harness.ok(args[0].type.inner.isDictionary(),
"Should have a dictionary inner type")
@@ -27,13 +27,27 @@ def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
- interface MozMapVoidArg {
- void foo(MozMap<void> arg);
+ interface RecordVoidArg {
+ void foo(record<DOMString, void> arg);
};
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
+ harness.ok(threw, "Should have thrown because record can't have void as value type.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Dict {
+ record<DOMString, Dict> val;
+ };
+ """)
- harness.ok(threw, "Should have thrown.")
+ results = parser.finish()
+ except Exception as x:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown on dictionary containing itself via record.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py b/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py
index 084f19fa7f5..442dba45d76 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py
@@ -288,33 +288,32 @@ def WebIDLTest(parser, harness):
threw = True
harness.ok(threw, "[SecureContext] must appear on interfaces that inherit from another [SecureContext] interface")
- # Test 'implements'. The behavior tested here may have to change depending
- # on the resolution of https://github.com/heycam/webidl/issues/118
+ # Test 'includes'.
parser = parser.reset()
parser.parse("""
[SecureContext]
- interface TestSecureContextInterfaceThatImplementsNonSecureContextInterface {
+ interface TestSecureContextInterfaceThatIncludesNonSecureContextMixin {
const octet TEST_CONSTANT = 0;
};
- interface TestNonSecureContextInterface {
+ interface mixin TestNonSecureContextMixin {
const octet TEST_CONSTANT_2 = 0;
readonly attribute byte testAttribute2;
void testMethod2(byte foo);
};
- TestSecureContextInterfaceThatImplementsNonSecureContextInterface implements TestNonSecureContextInterface;
+ TestSecureContextInterfaceThatIncludesNonSecureContextMixin includes TestNonSecureContextMixin;
""")
results = parser.finish()
- harness.check(len(results[0].members), 4, "TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have two members")
+ harness.check(len(results[0].members), 4, "TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have four members")
harness.ok(results[0].getExtendedAttribute("SecureContext"),
"Interface should have [SecureContext] extended attribute")
harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"),
"[SecureContext] should propagate from interface to constant members even when other members are copied from a non-[SecureContext] interface")
harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None,
- "Constants copied from non-[SecureContext] interface should not be [SecureContext]")
+ "Constants copied from non-[SecureContext] mixin should not be [SecureContext]")
harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
- "Attributes copied from non-[SecureContext] interface should not be [SecureContext]")
+ "Attributes copied from non-[SecureContext] mixin should not be [SecureContext]")
harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None,
- "Methods copied from non-[SecureContext] interface should not be [SecureContext]")
+ "Methods copied from non-[SecureContext] mixin should not be [SecureContext]")
# Test SecureContext and NoInterfaceObject
parser = parser.reset()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py b/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py
index 5ea1743d36a..52cfcb96817 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py
@@ -222,73 +222,3 @@ def WebIDLTest(parser, harness):
threw = True
harness.ok(threw, "Should have thrown.")
-
- threw = False
- try:
- parser.parse("""
- interface SpecialMethodSignatureMismatch20 {
- creator long long foo(long index, long long value);
- };
- """)
-
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown.")
-
- threw = False
- try:
- parser.parse("""
- interface SpecialMethodSignatureMismatch22 {
- creator boolean foo(unsigned long index, boolean value, long long extraArg);
- };
- """)
-
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown.")
-
- threw = False
- try:
- parser.parse("""
- interface SpecialMethodSignatureMismatch23 {
- creator boolean foo(unsigned long index, boolean... value);
- };
- """)
-
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown.")
-
- threw = False
- try:
- parser.parse("""
- interface SpecialMethodSignatureMismatch24 {
- creator boolean foo(unsigned long index, optional boolean value);
- };
- """)
-
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown.")
-
- threw = False
- try:
- parser.parse("""
- interface SpecialMethodSignatureMismatch25 {
- creator boolean foo();
- };
- """)
-
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py b/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py
index 695cfe4f250..7f911733b62 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py
@@ -5,26 +5,21 @@ def WebIDLTest(parser, harness):
interface SpecialMethods {
getter long long (unsigned long index);
setter long long (unsigned long index, long long value);
- creator long long (unsigned long index, long long value);
- deleter long long (unsigned long index);
getter boolean (DOMString name);
setter boolean (DOMString name, boolean value);
- creator boolean (DOMString name, boolean value);
deleter boolean (DOMString name);
+ readonly attribute unsigned long length;
};
interface SpecialMethodsCombination {
- getter deleter long long (unsigned long index);
- setter creator long long (unsigned long index, long long value);
getter deleter boolean (DOMString name);
- setter creator boolean (DOMString name, boolean value);
};
""")
results = parser.finish()
def checkMethod(method, QName, name,
- static=False, getter=False, setter=False, creator=False,
+ static=False, getter=False, setter=False,
deleter=False, legacycaller=False, stringifier=False):
harness.ok(isinstance(method, WebIDL.IDLMethod),
"Should be an IDLMethod")
@@ -33,7 +28,6 @@ def WebIDLTest(parser, harness):
harness.check(method.isStatic(), static, "Method has the correct static value")
harness.check(method.isGetter(), getter, "Method has the correct getter value")
harness.check(method.isSetter(), setter, "Method has the correct setter value")
- harness.check(method.isCreator(), creator, "Method has the correct creator value")
harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
@@ -41,33 +35,39 @@ def WebIDLTest(parser, harness):
harness.check(len(results), 2, "Expect 2 interfaces")
iface = results[0]
- harness.check(len(iface.members), 8, "Expect 8 members")
+ harness.check(len(iface.members), 6, "Expect 6 members")
checkMethod(iface.members[0], "::SpecialMethods::__indexedgetter", "__indexedgetter",
getter=True)
checkMethod(iface.members[1], "::SpecialMethods::__indexedsetter", "__indexedsetter",
setter=True)
- checkMethod(iface.members[2], "::SpecialMethods::__indexedcreator", "__indexedcreator",
- creator=True)
- checkMethod(iface.members[3], "::SpecialMethods::__indexeddeleter", "__indexeddeleter",
- deleter=True)
- checkMethod(iface.members[4], "::SpecialMethods::__namedgetter", "__namedgetter",
+ checkMethod(iface.members[2], "::SpecialMethods::__namedgetter", "__namedgetter",
getter=True)
- checkMethod(iface.members[5], "::SpecialMethods::__namedsetter", "__namedsetter",
+ checkMethod(iface.members[3], "::SpecialMethods::__namedsetter", "__namedsetter",
setter=True)
- checkMethod(iface.members[6], "::SpecialMethods::__namedcreator", "__namedcreator",
- creator=True)
- checkMethod(iface.members[7], "::SpecialMethods::__nameddeleter", "__nameddeleter",
+ checkMethod(iface.members[4], "::SpecialMethods::__nameddeleter", "__nameddeleter",
deleter=True)
iface = results[1]
- harness.check(len(iface.members), 4, "Expect 4 members")
+ harness.check(len(iface.members), 1, "Expect 1 member")
- checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedgetterdeleter",
- "__indexedgetterdeleter", getter=True, deleter=True)
- checkMethod(iface.members[1], "::SpecialMethodsCombination::__indexedsettercreator",
- "__indexedsettercreator", setter=True, creator=True)
- checkMethod(iface.members[2], "::SpecialMethodsCombination::__namedgetterdeleter",
+ checkMethod(iface.members[0], "::SpecialMethodsCombination::__namedgetterdeleter",
"__namedgetterdeleter", getter=True, deleter=True)
- checkMethod(iface.members[3], "::SpecialMethodsCombination::__namedsettercreator",
- "__namedsettercreator", setter=True, creator=True)
+
+ parser = parser.reset();
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IndexedDeleter {
+ deleter void(unsigned long index);
+ };
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "There are no indexed deleters")
+
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py b/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py
index 42e2c5bb71b..9bf3d903463 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py
@@ -35,23 +35,8 @@ def WebIDLTest(parser, harness):
try:
parser.parse("""
interface SpecialMethodUniqueness1 {
- setter creator boolean (DOMString name);
- creator boolean (DOMString name);
- };
- """)
-
- results = parser.finish()
- except:
- threw = True
-
- harness.ok(threw, "Should have thrown.")
-
- threw = False
- try:
- parser.parse("""
- interface SpecialMethodUniqueness1 {
setter boolean (DOMString name);
- creator setter boolean (DOMString name);
+ setter boolean (DOMString name);
};
""")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py b/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py
index 14c2c5226fc..deabdc5ec81 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py
@@ -44,3 +44,103 @@ def WebIDLTest(parser, harness):
harness.ok(threw, "Should not allow a 'stringifier;' and a 'stringifier()'")
+ parser = parser.reset()
+ parser.parse("""
+ interface TestStringifier {
+ stringifier attribute DOMString foo;
+ };
+ """)
+ results = parser.finish()
+ harness.ok(isinstance(results[0].members[0], WebIDL.IDLAttribute),
+ "Stringifier attribute should be an attribute")
+ stringifier = results[0].members[1]
+ harness.ok(isinstance(stringifier, WebIDL.IDLMethod),
+ "Stringifier attribute should insert a method")
+ harness.ok(stringifier.isStringifier(),
+ "Inserted method should be a stringifier")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestStringifier {};
+ interface mixin TestStringifierMixin {
+ stringifier attribute DOMString foo;
+ };
+ TestStringifier includes TestStringifierMixin;
+ """)
+ results = parser.finish()
+ harness.ok(isinstance(results[0].members[0], WebIDL.IDLAttribute),
+ "Stringifier attribute should be an attribute")
+ stringifier = results[0].members[1]
+ harness.ok(isinstance(stringifier, WebIDL.IDLMethod),
+ "Stringifier attribute should insert a method")
+ harness.ok(stringifier.isStringifier(),
+ "Inserted method should be a stringifier")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestStringifier {
+ stringifier attribute USVString foo;
+ };
+ """)
+ results = parser.finish()
+ stringifier = results[0].members[1]
+ harness.ok(stringifier.signatures()[0][0].isUSVString(),
+ "Stringifier attributes should allow USVString")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestStringifier {
+ [Throws, NeedsSubjectPrincipal]
+ stringifier attribute USVString foo;
+ };
+ """)
+ results = parser.finish()
+ stringifier = results[0].members[1]
+ harness.ok(stringifier.getExtendedAttribute("Throws"),
+ "Stringifier attributes should support [Throws]")
+ harness.ok(stringifier.getExtendedAttribute("NeedsSubjectPrincipal"),
+ "Stringifier attributes should support [NeedsSubjectPrincipal]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestStringifier {
+ stringifier attribute ByteString foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow ByteString")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestStringifier {
+ stringifier;
+ stringifier attribute DOMString foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow a 'stringifier;' and a stringifier attribute")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestStringifier {
+ stringifier attribute DOMString foo;
+ stringifier attribute DOMString bar;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow multiple stringifier attributes")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py b/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py
new file mode 100644
index 00000000000..ad01330e65a
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py
@@ -0,0 +1,195 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ object toJSON();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw, "Should allow a toJSON method.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ object toJSON(object arg);
+ object toJSON(long arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "Should not allow overloads of a toJSON method.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ object toJSON(object arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "Should not allow a toJSON method with arguments.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ long toJSON();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw, "Should allow a toJSON method with 'long' as return type.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ [Default] object toJSON();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw, "Should allow a default toJSON method with 'object' as return type.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Test {
+ [Default] long toJSON();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "Should not allow a default toJSON method with non-'object' as return type.")
+
+ JsonTypes = [ "byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long",
+ "unsigned long long", "float", "unrestricted float", "double", "unrestricted double", "boolean",
+ "DOMString", "ByteString", "UTF8String", "USVString", "Enum", "InterfaceWithToJSON", "object" ]
+
+ nonJsonTypes = [ "InterfaceWithoutToJSON", "any", "Int8Array", "Int16Array", "Int32Array","Uint8Array",
+ "Uint16Array", "Uint32Array", "Uint8ClampedArray", "Float32Array", "Float64Array", "ArrayBuffer" ]
+
+ def doTest(testIDL, shouldThrow, description):
+ p = parser.reset()
+ threw = False
+ try:
+ p.parse(testIDL +
+ """
+ enum Enum { "a", "b", "c" };
+ interface InterfaceWithToJSON { long toJSON(); };
+ interface InterfaceWithoutToJSON {};
+ """);
+ p.finish();
+ except Exception as x:
+ threw = True
+ harness.ok(x.message == "toJSON method has non-JSON return type", x)
+ harness.check(threw, shouldThrow, description)
+
+
+ for type in JsonTypes:
+ doTest("interface Test { %s toJSON(); };" % type, False,
+ "%s should be a JSON type" % type)
+
+ doTest("interface Test { sequence<%s> toJSON(); };" % type, False,
+ "sequence<%s> should be a JSON type" % type)
+
+ doTest("dictionary Foo { %s foo; }; "
+ "interface Test { Foo toJSON(); }; " % type, False,
+ "dictionary containing only JSON type (%s) should be a JSON type" % type)
+
+ doTest("dictionary Foo { %s foo; }; dictionary Bar : Foo { }; "
+ "interface Test { Bar toJSON(); }; " % type, False,
+ "dictionary whose ancestors only contain JSON types should be a JSON type")
+
+ doTest("dictionary Foo { any foo; }; dictionary Bar : Foo { %s bar; };"
+ "interface Test { Bar toJSON(); };" % type, True,
+ "dictionary whose ancestors contain non-JSON types should not be a JSON type")
+
+ doTest("interface Test { record<DOMString, %s> toJSON(); };" % type, False,
+ "record<DOMString, %s> should be a JSON type" % type)
+
+ doTest("interface Test { record<ByteString, %s> toJSON(); };" % type, False,
+ "record<ByteString, %s> should be a JSON type" % type)
+
+ doTest("interface Test { record<UTF8String, %s> toJSON(); };" % type, False,
+ "record<UTF8String, %s> should be a JSON type" % type)
+
+ doTest("interface Test { record<USVString, %s> toJSON(); };" % type, False,
+ "record<USVString, %s> should be a JSON type" % type)
+
+ otherUnionType = "Foo" if type != "object" else "long"
+ doTest("interface Foo { object toJSON(); };"
+ "interface Test { (%s or %s) toJSON(); };" % (otherUnionType, type), False,
+ "union containing only JSON types (%s or %s) should be a JSON type" %(otherUnionType, type))
+
+ doTest("interface test { %s? toJSON(); };" % type, False,
+ "Nullable type (%s) should be a JSON type" % type)
+
+ doTest("interface Foo : InterfaceWithoutToJSON { %s toJSON(); };"
+ "interface Test { Foo toJSON(); };" % type, False,
+ "interface with toJSON should be a JSON type")
+
+ doTest("interface Foo : InterfaceWithToJSON { };"
+ "interface Test { Foo toJSON(); };", False,
+ "inherited interface with toJSON should be a JSON type")
+
+ for type in nonJsonTypes:
+ doTest("interface Test { %s toJSON(); };" % type, True,
+ "%s should not be a JSON type" % type)
+
+ doTest("interface Test { sequence<%s> toJSON(); };" % type, True,
+ "sequence<%s> should not be a JSON type" % type)
+
+ doTest("dictionary Foo { %s foo; }; "
+ "interface Test { Foo toJSON(); }; " % type, True,
+ "Dictionary containing a non-JSON type (%s) should not be a JSON type" % type)
+
+ doTest("dictionary Foo { %s foo; }; dictionary Bar : Foo { }; "
+ "interface Test { Bar toJSON(); }; " % type, True,
+ "dictionary whose ancestors only contain non-JSON types should not be a JSON type")
+
+ doTest("interface Test { record<DOMString, %s> toJSON(); };" % type, True,
+ "record<DOMString, %s> should not be a JSON type" % type)
+
+ doTest("interface Test { record<ByteString, %s> toJSON(); };" % type, True,
+ "record<ByteString, %s> should not be a JSON type" % type)
+
+ doTest("interface Test { record<USVString, %s> toJSON(); };" % type, True,
+ "record<USVString, %s> should not be a JSON type" % type)
+
+ if type != "any":
+ doTest("interface Foo { object toJSON(); }; "
+ "interface Test { (Foo or %s) toJSON(); };" % type, True,
+ "union containing a non-JSON type (%s) should not be a JSON type" % type)
+
+ doTest("interface test { %s? toJSON(); };" % type, True,
+ "Nullable type (%s) should not be a JSON type" % type)
+
+ doTest("dictionary Foo { long foo; any bar; };"
+ "interface Test { Foo toJSON(); };", True,
+ "dictionary containing a non-JSON type should not be a JSON type")
+
+ doTest("interface Foo : InterfaceWithoutToJSON { }; "
+ "interface Test { Foo toJSON(); };", True,
+ "interface without toJSON should not be a JSON type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_typedef.py b/components/script/dom/bindings/codegen/parser/tests/test_typedef.py
index 8921985c5ca..b5fc1c68890 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_typedef.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_typedef.py
@@ -4,15 +4,15 @@ def WebIDLTest(parser, harness):
typedef long? mynullablelong;
interface Foo {
const mylong X = 5;
- const mynullablelong Y = 7;
- const mynullablelong Z = null;
- void foo(mylong arg);
+ void foo(optional mynullablelong arg = 7);
+ void bar(optional mynullablelong arg = null);
+ void baz(mylong arg);
};
""")
results = parser.finish()
- harness.check(results[2].members[1].type.name, "LongOrNull",
+ harness.check(results[2].members[1].signatures()[0][1][0].type.name, "LongOrNull",
"Should expand typedefs")
parser = parser.reset()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py b/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py
new file mode 100644
index 00000000000..0ea38ce437b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py
@@ -0,0 +1,16 @@
+def WebIDLTest(parser, harness):
+ exception = None
+ try:
+ parser.parse(
+ """
+ typedef long foo;
+ typedef long foo;
+ """)
+
+ results = parser.finish()
+ except Exception as e:
+ exception = e
+
+ harness.ok(exception, "Should have thrown.")
+ harness.ok("Multiple unresolvable definitions of identifier 'foo'" in str(exception),
+ "Should have a sane exception message")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py b/components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py
index d017d5ce092..d28cc1ec052 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py
@@ -24,7 +24,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -39,7 +39,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
@@ -59,6 +59,6 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception, x:
+ except Exception as x:
threw = True
harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py b/components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py
index 3787e8c6af1..770a9d3736f 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py
@@ -111,7 +111,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
harness.ok(threw,
"Should have thrown when shadowing unforgeable attribute on "
@@ -130,7 +130,7 @@ def WebIDLTest(parser, harness):
""")
results = parser.finish()
- except Exception,x:
+ except Exception as x:
threw = True
harness.ok(threw,
"Should have thrown when shadowing unforgeable operation on "
@@ -141,16 +141,16 @@ def WebIDLTest(parser, harness):
interface Child : Parent {
};
interface Parent {};
- interface Consequential {
+ interface mixin Mixin {
[Unforgeable] readonly attribute long foo;
};
- Parent implements Consequential;
+ Parent includes Mixin;
""")
results = parser.finish()
harness.check(len(results), 4,
"Should be able to inherit from an interface with a "
- "consequential interface with [Unforgeable] properties.")
+ "mixin with [Unforgeable] properties.")
parser = parser.reset();
threw = False
@@ -160,10 +160,10 @@ def WebIDLTest(parser, harness):
void foo();
};
interface Parent {};
- interface Consequential {
+ interface mixin Mixin {
[Unforgeable] readonly attribute long foo;
};
- Parent implements Consequential;
+ Parent includes Mixin;
""")
results = parser.finish()
@@ -182,14 +182,14 @@ def WebIDLTest(parser, harness):
};
interface Parent : GrandParent {};
interface GrandParent {};
- interface Consequential {
+ interface mixin Mixin {
[Unforgeable] readonly attribute long foo;
};
- GrandParent implements Consequential;
- interface ChildConsequential {
+ GrandParent includes Mixin;
+ interface mixin ChildMixin {
void foo();
};
- Child implements ChildConsequential;
+ Child includes ChildMixin;
""")
results = parser.finish()
@@ -208,14 +208,14 @@ def WebIDLTest(parser, harness):
};
interface Parent : GrandParent {};
interface GrandParent {};
- interface Consequential {
+ interface mixin Mixin {
[Unforgeable] void foo();
};
- GrandParent implements Consequential;
- interface ChildConsequential {
+ GrandParent includes Mixin;
+ interface mixin ChildMixin {
void foo();
};
- Child implements ChildConsequential;
+ Child includes ChildMixin;
""")
results = parser.finish()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_union.py b/components/script/dom/bindings/codegen/parser/tests/test_union.py
index 9c4f2a56ab6..801314fd0bd 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_union.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_union.py
@@ -17,7 +17,7 @@ def combinations(iterable, r):
n = len(pool)
if r > n:
return
- indices = range(r)
+ indices = list(range(r))
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
diff --git a/components/script/dom/bindings/codegen/parser/union-typedef.patch b/components/script/dom/bindings/codegen/parser/union-typedef.patch
index 3021e14193f..20efea8e129 100644
--- a/components/script/dom/bindings/codegen/parser/union-typedef.patch
+++ b/components/script/dom/bindings/codegen/parser/union-typedef.patch
@@ -1,22 +1,22 @@
--- WebIDL.py
+++ WebIDL.py
-@@ -2481,10 +2481,18 @@ class IDLUnionType(IDLType):
+@@ -2624,10 +2624,18 @@ class IDLUnionType(IDLType):
return type.name
-
+
for (i, type) in enumerate(self.memberTypes):
+- if not type.isComplete():
+ # Exclude typedefs because if given "typedef (B or C) test",
+ # we want AOrTest, not AOrBOrC
+ if not type.isComplete() and not isinstance(type, IDLTypedefType):
-+ self.memberTypes[i] = type.complete(scope)
-+
-+ self.name = "Or".join(typeName(type) for type in self.memberTypes)
+ self.memberTypes[i] = type.complete(scope)
+
+ self.name = "Or".join(typeName(type) for type in self.memberTypes)
+
+ # We do this again to complete the typedef types
+ for (i, type) in enumerate(self.memberTypes):
- if not type.isComplete():
- self.memberTypes[i] = type.complete(scope)
-
-- self.name = "Or".join(typeName(type) for type in self.memberTypes)
++ if not type.isComplete():
++ self.memberTypes[i] = type.complete(scope)
++
self.flatMemberTypes = list(self.memberTypes)
i = 0
while i < len(self.flatMemberTypes):
diff --git a/components/script/dom/bindings/codegen/parser/update.sh b/components/script/dom/bindings/codegen/parser/update.sh
index 213aba6df89..dd7803c940c 100755
--- a/components/script/dom/bindings/codegen/parser/update.sh
+++ b/components/script/dom/bindings/codegen/parser/update.sh
@@ -1,13 +1,12 @@
wget https://hg.mozilla.org/mozilla-central/raw-file/tip/dom/bindings/parser/WebIDL.py -O WebIDL.py
patch < abstract.patch
patch < debug.patch
-patch < pref-main-thread.patch
patch < callback-location.patch
patch < union-typedef.patch
patch < inline.patch
-wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz
+wget https://hg.mozilla.org/mozilla-central/archive/tip.zip/dom/bindings/parser/tests/ -O tests.zip
rm -r tests
mkdir tests
-tar xvpf tests.tar.gz -C tests --strip-components=5
-rm tests.tar.gz WebIDL.py.orig
+unzip -d tests -j tests.zip
+rm tests.zip WebIDL.py.orig
diff --git a/components/script/dom/bindings/codegen/ply/README b/components/script/dom/bindings/codegen/ply/README
index 2459c490197..d3de9993360 100644
--- a/components/script/dom/bindings/codegen/ply/README
+++ b/components/script/dom/bindings/codegen/ply/README
@@ -3,7 +3,7 @@ http://www.dabeaz.com/ply/
Licensed under BSD.
-This directory contains just the code and license from PLY version 3.3;
+This directory contains just the code and license from PLY version 4.0;
the full distribution (see the URL) also contains examples, tests,
documentation, and a longer README.
diff --git a/components/script/dom/bindings/codegen/ply/ply/__init__.py b/components/script/dom/bindings/codegen/ply/ply/__init__.py
index 853a985542b..87838622863 100644
--- a/components/script/dom/bindings/codegen/ply/ply/__init__.py
+++ b/components/script/dom/bindings/codegen/ply/ply/__init__.py
@@ -1,4 +1,6 @@
# PLY package
# Author: David Beazley (dave@dabeaz.com)
+# https://dabeaz.com/ply/index.html
+__version__ = '4.0'
__all__ = ['lex','yacc']
diff --git a/components/script/dom/bindings/codegen/ply/ply/lex.py b/components/script/dom/bindings/codegen/ply/ply/lex.py
index 267ec100fc2..57b61f1779e 100644
--- a/components/script/dom/bindings/codegen/ply/ply/lex.py
+++ b/components/script/dom/bindings/codegen/ply/ply/lex.py
@@ -1,22 +1,24 @@
# -----------------------------------------------------------------------------
# ply: lex.py
#
-# Copyright (C) 2001-2009,
+# Copyright (C) 2001-2020
# David M. Beazley (Dabeaz LLC)
# All rights reserved.
#
+# Latest version: https://github.com/dabeaz/ply
+#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
-#
+#
# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the David Beazley or Dabeaz LLC may be used to
+# and/or other materials provided with the distribution.
+# * Neither the name of David Beazley or Dabeaz LLC may be used to
# endorse or promote products derived from this software without
-# specific prior written permission.
+# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -31,72 +33,50 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
-__version__ = "3.3"
-__tabversion__ = "3.2" # Version of table file used
-
-import re, sys, types, copy, os
-
-# This tuple contains known string types
-try:
- # Python 2.6
- StringTypes = (types.StringType, types.UnicodeType)
-except AttributeError:
- # Python 3.0
- StringTypes = (str, bytes)
-
-# Extract the code attribute of a function. Different implementations
-# are for Python 2/3 compatibility.
+import re
+import sys
+import types
+import copy
+import os
+import inspect
-if sys.version_info[0] < 3:
- def func_code(f):
- return f.func_code
-else:
- def func_code(f):
- return f.__code__
+# This tuple contains acceptable string types
+StringTypes = (str, bytes)
# This regular expression is used to match valid token names
_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$')
# Exception thrown when invalid token encountered and no default error
# handler is defined.
-
class LexError(Exception):
- def __init__(self,message,s):
- self.args = (message,)
- self.text = s
+ def __init__(self, message, s):
+ self.args = (message,)
+ self.text = s
# Token class. This class is used to represent the tokens produced.
class LexToken(object):
- def __str__(self):
- return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos)
def __repr__(self):
- return str(self)
+ return f'LexToken({self.type},{self.value!r},{self.lineno},{self.lexpos})'
-# This object is a stand-in for a logging object created by the
-# logging module.
+# This object is a stand-in for a logging object created by the
+# logging module.
class PlyLogger(object):
- def __init__(self,f):
+ def __init__(self, f):
self.f = f
- def critical(self,msg,*args,**kwargs):
- self.f.write((msg % args) + "\n")
- def warning(self,msg,*args,**kwargs):
- self.f.write("WARNING: "+ (msg % args) + "\n")
+ def critical(self, msg, *args, **kwargs):
+ self.f.write((msg % args) + '\n')
+
+ def warning(self, msg, *args, **kwargs):
+ self.f.write('WARNING: ' + (msg % args) + '\n')
- def error(self,msg,*args,**kwargs):
- self.f.write("ERROR: " + (msg % args) + "\n")
+ def error(self, msg, *args, **kwargs):
+ self.f.write('ERROR: ' + (msg % args) + '\n')
info = critical
debug = critical
-# Null logger is used when no output is generated. Does nothing.
-class NullLogger(object):
- def __getattribute__(self,name):
- return self
- def __call__(self,*args,**kwargs):
- return self
-
# -----------------------------------------------------------------------------
# === Lexing Engine ===
#
@@ -114,31 +94,32 @@ class NullLogger(object):
class Lexer:
def __init__(self):
self.lexre = None # Master regular expression. This is a list of
- # tuples (re,findex) where re is a compiled
- # regular expression and findex is a list
- # mapping regex group numbers to rules
+ # tuples (re, findex) where re is a compiled
+ # regular expression and findex is a list
+ # mapping regex group numbers to rules
self.lexretext = None # Current regular expression strings
self.lexstatere = {} # Dictionary mapping lexer states to master regexs
self.lexstateretext = {} # Dictionary mapping lexer states to regex strings
self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names
- self.lexstate = "INITIAL" # Current lexer state
+ self.lexstate = 'INITIAL' # Current lexer state
self.lexstatestack = [] # Stack of lexer states
self.lexstateinfo = None # State information
self.lexstateignore = {} # Dictionary of ignored characters for each state
self.lexstateerrorf = {} # Dictionary of error functions for each state
+ self.lexstateeoff = {} # Dictionary of eof functions for each state
self.lexreflags = 0 # Optional re compile flags
self.lexdata = None # Actual input data (as a string)
self.lexpos = 0 # Current position in input text
self.lexlen = 0 # Length of the input text
self.lexerrorf = None # Error rule (if any)
+ self.lexeoff = None # EOF rule (if any)
self.lextokens = None # List of valid tokens
- self.lexignore = "" # Ignored characters
- self.lexliterals = "" # Literal characters that can be passed through
+ self.lexignore = '' # Ignored characters
+ self.lexliterals = '' # Literal characters that can be passed through
self.lexmodule = None # Module
self.lineno = 1 # Current line number
- self.lexoptimize = 0 # Optimized mode
- def clone(self,object=None):
+ def clone(self, object=None):
c = copy.copy(self)
# If the object parameter has been supplied, it means we are attaching the
@@ -146,113 +127,29 @@ class Lexer:
# the lexstatere and lexstateerrorf tables.
if object:
- newtab = { }
+ newtab = {}
for key, ritem in self.lexstatere.items():
newre = []
for cre, findex in ritem:
- newfindex = []
- for f in findex:
- if not f or not f[0]:
- newfindex.append(f)
- continue
- newfindex.append((getattr(object,f[0].__name__),f[1]))
- newre.append((cre,newfindex))
+ newfindex = []
+ for f in findex:
+ if not f or not f[0]:
+ newfindex.append(f)
+ continue
+ newfindex.append((getattr(object, f[0].__name__), f[1]))
+ newre.append((cre, newfindex))
newtab[key] = newre
c.lexstatere = newtab
- c.lexstateerrorf = { }
+ c.lexstateerrorf = {}
for key, ef in self.lexstateerrorf.items():
- c.lexstateerrorf[key] = getattr(object,ef.__name__)
+ c.lexstateerrorf[key] = getattr(object, ef.__name__)
c.lexmodule = object
return c
# ------------------------------------------------------------
- # writetab() - Write lexer information to a table file
- # ------------------------------------------------------------
- def writetab(self,tabfile,outputdir=""):
- if isinstance(tabfile,types.ModuleType):
- return
- basetabfilename = tabfile.split(".")[-1]
- filename = os.path.join(outputdir,basetabfilename)+".py"
- tf = open(filename,"w")
- tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__))
- tf.write("_tabversion = %s\n" % repr(__version__))
- tf.write("_lextokens = %s\n" % repr(self.lextokens))
- tf.write("_lexreflags = %s\n" % repr(self.lexreflags))
- tf.write("_lexliterals = %s\n" % repr(self.lexliterals))
- tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo))
-
- tabre = { }
- # Collect all functions in the initial state
- initial = self.lexstatere["INITIAL"]
- initialfuncs = []
- for part in initial:
- for f in part[1]:
- if f and f[0]:
- initialfuncs.append(f)
-
- for key, lre in self.lexstatere.items():
- titem = []
- for i in range(len(lre)):
- titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i])))
- tabre[key] = titem
-
- tf.write("_lexstatere = %s\n" % repr(tabre))
- tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore))
-
- taberr = { }
- for key, ef in self.lexstateerrorf.items():
- if ef:
- taberr[key] = ef.__name__
- else:
- taberr[key] = None
- tf.write("_lexstateerrorf = %s\n" % repr(taberr))
- tf.close()
-
- # ------------------------------------------------------------
- # readtab() - Read lexer information from a tab file
- # ------------------------------------------------------------
- def readtab(self,tabfile,fdict):
- if isinstance(tabfile,types.ModuleType):
- lextab = tabfile
- else:
- if sys.version_info[0] < 3:
- exec("import %s as lextab" % tabfile)
- else:
- env = { }
- exec("import %s as lextab" % tabfile, env,env)
- lextab = env['lextab']
-
- if getattr(lextab,"_tabversion","0.0") != __version__:
- raise ImportError("Inconsistent PLY version")
-
- self.lextokens = lextab._lextokens
- self.lexreflags = lextab._lexreflags
- self.lexliterals = lextab._lexliterals
- self.lexstateinfo = lextab._lexstateinfo
- self.lexstateignore = lextab._lexstateignore
- self.lexstatere = { }
- self.lexstateretext = { }
- for key,lre in lextab._lexstatere.items():
- titem = []
- txtitem = []
- for i in range(len(lre)):
- titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict)))
- txtitem.append(lre[i][0])
- self.lexstatere[key] = titem
- self.lexstateretext[key] = txtitem
- self.lexstateerrorf = { }
- for key,ef in lextab._lexstateerrorf.items():
- self.lexstateerrorf[key] = fdict[ef]
- self.begin('INITIAL')
-
- # ------------------------------------------------------------
# input() - Push a new string into the lexer
# ------------------------------------------------------------
- def input(self,s):
- # Pull off the first character to see if s looks like a string
- c = s[:1]
- if not isinstance(c,StringTypes):
- raise ValueError("Expected a string")
+ def input(self, s):
self.lexdata = s
self.lexpos = 0
self.lexlen = len(s)
@@ -260,19 +157,20 @@ class Lexer:
# ------------------------------------------------------------
# begin() - Changes the lexing state
# ------------------------------------------------------------
- def begin(self,state):
- if not state in self.lexstatere:
- raise ValueError("Undefined state")
+ def begin(self, state):
+ if state not in self.lexstatere:
+ raise ValueError(f'Undefined state {state!r}')
self.lexre = self.lexstatere[state]
self.lexretext = self.lexstateretext[state]
- self.lexignore = self.lexstateignore.get(state,"")
- self.lexerrorf = self.lexstateerrorf.get(state,None)
+ self.lexignore = self.lexstateignore.get(state, '')
+ self.lexerrorf = self.lexstateerrorf.get(state, None)
+ self.lexeoff = self.lexstateeoff.get(state, None)
self.lexstate = state
# ------------------------------------------------------------
# push_state() - Changes the lexing state and saves old on stack
# ------------------------------------------------------------
- def push_state(self,state):
+ def push_state(self, state):
self.lexstatestack.append(self.lexstate)
self.begin(state)
@@ -291,11 +189,11 @@ class Lexer:
# ------------------------------------------------------------
# skip() - Skip ahead n characters
# ------------------------------------------------------------
- def skip(self,n):
+ def skip(self, n):
self.lexpos += n
# ------------------------------------------------------------
- # opttoken() - Return the next token from the Lexer
+ # token() - Return the next token from the Lexer
#
# Note: This function has been carefully implemented to be as fast
# as possible. Don't make changes unless you really know what
@@ -315,9 +213,10 @@ class Lexer:
continue
# Look for a regular expression match
- for lexre,lexindexfunc in self.lexre:
- m = lexre.match(lexdata,lexpos)
- if not m: continue
+ for lexre, lexindexfunc in self.lexre:
+ m = lexre.match(lexdata, lexpos)
+ if not m:
+ continue
# Create a token for return
tok = LexToken()
@@ -326,16 +225,16 @@ class Lexer:
tok.lexpos = lexpos
i = m.lastindex
- func,tok.type = lexindexfunc[i]
+ func, tok.type = lexindexfunc[i]
if not func:
- # If no token type was set, it's an ignored token
- if tok.type:
- self.lexpos = m.end()
- return tok
- else:
- lexpos = m.end()
- break
+ # If no token type was set, it's an ignored token
+ if tok.type:
+ self.lexpos = m.end()
+ return tok
+ else:
+ lexpos = m.end()
+ break
lexpos = m.end()
@@ -344,22 +243,15 @@ class Lexer:
tok.lexer = self # Set additional attributes useful in token rules
self.lexmatch = m
self.lexpos = lexpos
-
newtok = func(tok)
+ del tok.lexer
+ del self.lexmatch
# Every function must return a token, if nothing, we just move to next token
if not newtok:
lexpos = self.lexpos # This is here in case user has updated lexpos.
lexignore = self.lexignore # This is here in case there was a state change
break
-
- # Verify type of the token. If not in the token map, raise an error
- if not self.lexoptimize:
- if not newtok.type in self.lextokens:
- raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % (
- func_code(func).co_filename, func_code(func).co_firstlineno,
- func.__name__, newtok.type),lexdata[lexpos:])
-
return newtok
else:
# No match, see if in literals
@@ -377,38 +269,50 @@ class Lexer:
tok = LexToken()
tok.value = self.lexdata[lexpos:]
tok.lineno = self.lineno
- tok.type = "error"
+ tok.type = 'error'
tok.lexer = self
tok.lexpos = lexpos
self.lexpos = lexpos
newtok = self.lexerrorf(tok)
if lexpos == self.lexpos:
# Error method didn't change text position at all. This is an error.
- raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:])
+ raise LexError(f"Scanning error. Illegal character {lexdata[lexpos]!r}",
+ lexdata[lexpos:])
lexpos = self.lexpos
- if not newtok: continue
+ if not newtok:
+ continue
return newtok
self.lexpos = lexpos
- raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:])
+ raise LexError(f"Illegal character {lexdata[lexpos]!r} at index {lexpos}",
+ lexdata[lexpos:])
+
+ if self.lexeoff:
+ tok = LexToken()
+ tok.type = 'eof'
+ tok.value = ''
+ tok.lineno = self.lineno
+ tok.lexpos = lexpos
+ tok.lexer = self
+ self.lexpos = lexpos
+ newtok = self.lexeoff(tok)
+ return newtok
self.lexpos = lexpos + 1
if self.lexdata is None:
- raise RuntimeError("No input string given with input()")
+ raise RuntimeError('No input string given with input()')
return None
# Iterator interface
def __iter__(self):
return self
- def next(self):
+ def __next__(self):
t = self.token()
if t is None:
raise StopIteration
return t
- __next__ = next
-
# -----------------------------------------------------------------------------
# ==== Lex Builder ===
#
@@ -417,59 +321,24 @@ class Lexer:
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
+# _get_regex(func)
+#
+# Returns the regular expression assigned to a function either as a doc string
+# or as a .regex attribute attached by the @TOKEN decorator.
+# -----------------------------------------------------------------------------
+def _get_regex(func):
+ return getattr(func, 'regex', func.__doc__)
+
+# -----------------------------------------------------------------------------
# get_caller_module_dict()
#
# This function returns a dictionary containing all of the symbols defined within
# a caller further down the call stack. This is used to get the environment
# associated with the yacc() call if none was provided.
# -----------------------------------------------------------------------------
-
def get_caller_module_dict(levels):
- try:
- raise RuntimeError
- except RuntimeError:
- e,b,t = sys.exc_info()
- f = t.tb_frame
- while levels > 0:
- f = f.f_back
- levels -= 1
- ldict = f.f_globals.copy()
- if f.f_globals != f.f_locals:
- ldict.update(f.f_locals)
-
- return ldict
-
-# -----------------------------------------------------------------------------
-# _funcs_to_names()
-#
-# Given a list of regular expression functions, this converts it to a list
-# suitable for output to a table file
-# -----------------------------------------------------------------------------
-
-def _funcs_to_names(funclist,namelist):
- result = []
- for f,name in zip(funclist,namelist):
- if f and f[0]:
- result.append((name, f[1]))
- else:
- result.append(f)
- return result
-
-# -----------------------------------------------------------------------------
-# _names_to_funcs()
-#
-# Given a list of regular expression function names, this converts it back to
-# functions.
-# -----------------------------------------------------------------------------
-
-def _names_to_funcs(namelist,fdict):
- result = []
- for n in namelist:
- if n and n[0]:
- result.append((fdict[n[0]],n[1]))
- else:
- result.append(n)
- return result
+ f = sys._getframe(levels)
+ return { **f.f_globals, **f.f_locals }
# -----------------------------------------------------------------------------
# _form_master_re()
@@ -478,36 +347,35 @@ def _names_to_funcs(namelist,fdict):
# form the master regular expression. Given limitations in the Python re
# module, it may be necessary to break the master regex into separate expressions.
# -----------------------------------------------------------------------------
-
-def _form_master_re(relist,reflags,ldict,toknames):
- if not relist: return []
- regex = "|".join(relist)
+def _form_master_re(relist, reflags, ldict, toknames):
+ if not relist:
+ return [], [], []
+ regex = '|'.join(relist)
try:
- lexre = re.compile(regex,re.VERBOSE | reflags)
+ lexre = re.compile(regex, reflags)
# Build the index to function map for the matching engine
- lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1)
+ lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1)
lexindexnames = lexindexfunc[:]
- for f,i in lexre.groupindex.items():
- handle = ldict.get(f,None)
+ for f, i in lexre.groupindex.items():
+ handle = ldict.get(f, None)
if type(handle) in (types.FunctionType, types.MethodType):
- lexindexfunc[i] = (handle,toknames[f])
+ lexindexfunc[i] = (handle, toknames[f])
lexindexnames[i] = f
elif handle is not None:
lexindexnames[i] = f
- if f.find("ignore_") > 0:
- lexindexfunc[i] = (None,None)
+ if f.find('ignore_') > 0:
+ lexindexfunc[i] = (None, None)
else:
lexindexfunc[i] = (None, toknames[f])
-
- return [(lexre,lexindexfunc)],[regex],[lexindexnames]
+
+ return [(lexre, lexindexfunc)], [regex], [lexindexnames]
except Exception:
- m = int(len(relist)/2)
- if m == 0: m = 1
- llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames)
- rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames)
- return llist+rlist, lre+rre, lnames+rnames
+ m = (len(relist) // 2) + 1
+ llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames)
+ rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames)
+ return (llist+rlist), (lre+rre), (lnames+rnames)
# -----------------------------------------------------------------------------
# def _statetoken(s,names)
@@ -517,22 +385,22 @@ def _form_master_re(relist,reflags,ldict,toknames):
# is a tuple of state names and tokenname is the name of the token. For example,
# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM')
# -----------------------------------------------------------------------------
+def _statetoken(s, names):
+ parts = s.split('_')
+ for i, part in enumerate(parts[1:], 1):
+ if part not in names and part != 'ANY':
+ break
-def _statetoken(s,names):
- nonstate = 1
- parts = s.split("_")
- for i in range(1,len(parts)):
- if not parts[i] in names and parts[i] != 'ANY': break
if i > 1:
- states = tuple(parts[1:i])
+ states = tuple(parts[1:i])
else:
- states = ('INITIAL',)
+ states = ('INITIAL',)
if 'ANY' in states:
- states = tuple(names)
+ states = tuple(names)
- tokenname = "_".join(parts[i:])
- return (states,tokenname)
+ tokenname = '_'.join(parts[i:])
+ return (states, tokenname)
# -----------------------------------------------------------------------------
@@ -542,19 +410,15 @@ def _statetoken(s,names):
# user's input file.
# -----------------------------------------------------------------------------
class LexerReflect(object):
- def __init__(self,ldict,log=None,reflags=0):
+ def __init__(self, ldict, log=None, reflags=0):
self.ldict = ldict
self.error_func = None
self.tokens = []
self.reflags = reflags
- self.stateinfo = { 'INITIAL' : 'inclusive'}
- self.files = {}
- self.error = 0
-
- if log is None:
- self.log = PlyLogger(sys.stderr)
- else:
- self.log = log
+ self.stateinfo = {'INITIAL': 'inclusive'}
+ self.modules = set()
+ self.error = False
+ self.log = PlyLogger(sys.stderr) if log is None else log
# Get all of the basic information
def get_all(self):
@@ -562,7 +426,7 @@ class LexerReflect(object):
self.get_literals()
self.get_states()
self.get_rules()
-
+
# Validate all of the information
def validate_all(self):
self.validate_tokens()
@@ -572,20 +436,20 @@ class LexerReflect(object):
# Get the tokens map
def get_tokens(self):
- tokens = self.ldict.get("tokens",None)
+ tokens = self.ldict.get('tokens', None)
if not tokens:
- self.log.error("No token list is defined")
- self.error = 1
+ self.log.error('No token list is defined')
+ self.error = True
return
- if not isinstance(tokens,(list, tuple)):
- self.log.error("tokens must be a list or tuple")
- self.error = 1
+ if not isinstance(tokens, (list, tuple)):
+ self.log.error('tokens must be a list or tuple')
+ self.error = True
return
-
+
if not tokens:
- self.log.error("tokens is empty")
- self.error = 1
+ self.log.error('tokens is empty')
+ self.error = True
return
self.tokens = tokens
@@ -595,280 +459,270 @@ class LexerReflect(object):
terminals = {}
for n in self.tokens:
if not _is_identifier.match(n):
- self.log.error("Bad token name '%s'",n)
- self.error = 1
+ self.log.error(f"Bad token name {n!r}")
+ self.error = True
if n in terminals:
- self.log.warning("Token '%s' multiply defined", n)
+ self.log.warning(f"Token {n!r} multiply defined")
terminals[n] = 1
# Get the literals specifier
def get_literals(self):
- self.literals = self.ldict.get("literals","")
+ self.literals = self.ldict.get('literals', '')
+ if not self.literals:
+ self.literals = ''
# Validate literals
def validate_literals(self):
try:
for c in self.literals:
- if not isinstance(c,StringTypes) or len(c) > 1:
- self.log.error("Invalid literal %s. Must be a single character", repr(c))
- self.error = 1
- continue
+ if not isinstance(c, StringTypes) or len(c) > 1:
+ self.log.error(f'Invalid literal {c!r}. Must be a single character')
+ self.error = True
except TypeError:
- self.log.error("Invalid literals specification. literals must be a sequence of characters")
- self.error = 1
+ self.log.error('Invalid literals specification. literals must be a sequence of characters')
+ self.error = True
def get_states(self):
- self.states = self.ldict.get("states",None)
+ self.states = self.ldict.get('states', None)
# Build statemap
if self.states:
- if not isinstance(self.states,(tuple,list)):
- self.log.error("states must be defined as a tuple or list")
- self.error = 1
- else:
- for s in self.states:
- if not isinstance(s,tuple) or len(s) != 2:
- self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s))
- self.error = 1
- continue
- name, statetype = s
- if not isinstance(name,StringTypes):
- self.log.error("State name %s must be a string", repr(name))
- self.error = 1
- continue
- if not (statetype == 'inclusive' or statetype == 'exclusive'):
- self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name)
- self.error = 1
- continue
- if name in self.stateinfo:
- self.log.error("State '%s' already defined",name)
- self.error = 1
- continue
- self.stateinfo[name] = statetype
+ if not isinstance(self.states, (tuple, list)):
+ self.log.error('states must be defined as a tuple or list')
+ self.error = True
+ else:
+ for s in self.states:
+ if not isinstance(s, tuple) or len(s) != 2:
+ self.log.error("Invalid state specifier %r. Must be a tuple (statename,'exclusive|inclusive')", s)
+ self.error = True
+ continue
+ name, statetype = s
+ if not isinstance(name, StringTypes):
+ self.log.error('State name %r must be a string', name)
+ self.error = True
+ continue
+ if not (statetype == 'inclusive' or statetype == 'exclusive'):
+ self.log.error("State type for state %r must be 'inclusive' or 'exclusive'", name)
+ self.error = True
+ continue
+ if name in self.stateinfo:
+ self.log.error("State %r already defined", name)
+ self.error = True
+ continue
+ self.stateinfo[name] = statetype
# Get all of the symbols with a t_ prefix and sort them into various
# categories (functions, strings, error functions, and ignore characters)
def get_rules(self):
- tsymbols = [f for f in self.ldict if f[:2] == 't_' ]
+ tsymbols = [f for f in self.ldict if f[:2] == 't_']
# Now build up a list of functions and a list of strings
-
- self.toknames = { } # Mapping of symbols to token names
- self.funcsym = { } # Symbols defined as functions
- self.strsym = { } # Symbols defined as strings
- self.ignore = { } # Ignore strings by state
- self.errorf = { } # Error functions by state
+ self.toknames = {} # Mapping of symbols to token names
+ self.funcsym = {} # Symbols defined as functions
+ self.strsym = {} # Symbols defined as strings
+ self.ignore = {} # Ignore strings by state
+ self.errorf = {} # Error functions by state
+ self.eoff = {} # EOF functions by state
for s in self.stateinfo:
- self.funcsym[s] = []
- self.strsym[s] = []
+ self.funcsym[s] = []
+ self.strsym[s] = []
if len(tsymbols) == 0:
- self.log.error("No rules of the form t_rulename are defined")
- self.error = 1
+ self.log.error('No rules of the form t_rulename are defined')
+ self.error = True
return
for f in tsymbols:
t = self.ldict[f]
- states, tokname = _statetoken(f,self.stateinfo)
+ states, tokname = _statetoken(f, self.stateinfo)
self.toknames[f] = tokname
- if hasattr(t,"__call__"):
+ if hasattr(t, '__call__'):
if tokname == 'error':
for s in states:
self.errorf[s] = t
+ elif tokname == 'eof':
+ for s in states:
+ self.eoff[s] = t
elif tokname == 'ignore':
- line = func_code(t).co_firstlineno
- file = func_code(t).co_filename
- self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__)
- self.error = 1
+ line = t.__code__.co_firstlineno
+ file = t.__code__.co_filename
+ self.log.error("%s:%d: Rule %r must be defined as a string", file, line, t.__name__)
+ self.error = True
else:
- for s in states:
- self.funcsym[s].append((f,t))
+ for s in states:
+ self.funcsym[s].append((f, t))
elif isinstance(t, StringTypes):
if tokname == 'ignore':
for s in states:
self.ignore[s] = t
- if "\\" in t:
- self.log.warning("%s contains a literal backslash '\\'",f)
+ if '\\' in t:
+ self.log.warning("%s contains a literal backslash '\\'", f)
elif tokname == 'error':
- self.log.error("Rule '%s' must be defined as a function", f)
- self.error = 1
+ self.log.error("Rule %r must be defined as a function", f)
+ self.error = True
else:
- for s in states:
- self.strsym[s].append((f,t))
+ for s in states:
+ self.strsym[s].append((f, t))
else:
- self.log.error("%s not defined as a function or string", f)
- self.error = 1
+ self.log.error('%s not defined as a function or string', f)
+ self.error = True
# Sort the functions by line number
for f in self.funcsym.values():
- if sys.version_info[0] < 3:
- f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno))
- else:
- # Python 3.0
- f.sort(key=lambda x: func_code(x[1]).co_firstlineno)
+ f.sort(key=lambda x: x[1].__code__.co_firstlineno)
# Sort the strings by regular expression length
for s in self.strsym.values():
- if sys.version_info[0] < 3:
- s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1])))
- else:
- # Python 3.0
- s.sort(key=lambda x: len(x[1]),reverse=True)
+ s.sort(key=lambda x: len(x[1]), reverse=True)
- # Validate all of the t_rules collected
+ # Validate all of the t_rules collected
def validate_rules(self):
for state in self.stateinfo:
# Validate all rules defined by functions
-
-
for fname, f in self.funcsym[state]:
- line = func_code(f).co_firstlineno
- file = func_code(f).co_filename
- self.files[file] = 1
+ line = f.__code__.co_firstlineno
+ file = f.__code__.co_filename
+ module = inspect.getmodule(f)
+ self.modules.add(module)
tokname = self.toknames[fname]
if isinstance(f, types.MethodType):
reqargs = 2
else:
reqargs = 1
- nargs = func_code(f).co_argcount
+ nargs = f.__code__.co_argcount
if nargs > reqargs:
- self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
- self.error = 1
+ self.log.error("%s:%d: Rule %r has too many arguments", file, line, f.__name__)
+ self.error = True
continue
if nargs < reqargs:
- self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
- self.error = 1
+ self.log.error("%s:%d: Rule %r requires an argument", file, line, f.__name__)
+ self.error = True
continue
- if not f.__doc__:
- self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__)
- self.error = 1
+ if not _get_regex(f):
+ self.log.error("%s:%d: No regular expression defined for rule %r", file, line, f.__name__)
+ self.error = True
continue
try:
- c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags)
- if c.match(""):
- self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__)
- self.error = 1
- except re.error:
- _etype, e, _etrace = sys.exc_info()
- self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e)
- if '#' in f.__doc__:
- self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__)
- self.error = 1
+ c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags)
+ if c.match(''):
+ self.log.error("%s:%d: Regular expression for rule %r matches empty string", file, line, f.__name__)
+ self.error = True
+ except re.error as e:
+ self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e)
+ if '#' in _get_regex(f):
+ self.log.error("%s:%d. Make sure '#' in rule %r is escaped with '\\#'", file, line, f.__name__)
+ self.error = True
# Validate all rules defined by strings
- for name,r in self.strsym[state]:
+ for name, r in self.strsym[state]:
tokname = self.toknames[name]
if tokname == 'error':
- self.log.error("Rule '%s' must be defined as a function", name)
- self.error = 1
+ self.log.error("Rule %r must be defined as a function", name)
+ self.error = True
continue
- if not tokname in self.tokens and tokname.find("ignore_") < 0:
- self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname)
- self.error = 1
+ if tokname not in self.tokens and tokname.find('ignore_') < 0:
+ self.log.error("Rule %r defined for an unspecified token %s", name, tokname)
+ self.error = True
continue
try:
- c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags)
- if (c.match("")):
- self.log.error("Regular expression for rule '%s' matches empty string",name)
- self.error = 1
- except re.error:
- _etype, e, _etrace = sys.exc_info()
- self.log.error("Invalid regular expression for rule '%s'. %s",name,e)
+ c = re.compile('(?P<%s>%s)' % (name, r), self.reflags)
+ if (c.match('')):
+ self.log.error("Regular expression for rule %r matches empty string", name)
+ self.error = True
+ except re.error as e:
+ self.log.error("Invalid regular expression for rule %r. %s", name, e)
if '#' in r:
- self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name)
- self.error = 1
+ self.log.error("Make sure '#' in rule %r is escaped with '\\#'", name)
+ self.error = True
if not self.funcsym[state] and not self.strsym[state]:
- self.log.error("No rules defined for state '%s'",state)
- self.error = 1
+ self.log.error("No rules defined for state %r", state)
+ self.error = True
# Validate the error function
- efunc = self.errorf.get(state,None)
+ efunc = self.errorf.get(state, None)
if efunc:
f = efunc
- line = func_code(f).co_firstlineno
- file = func_code(f).co_filename
- self.files[file] = 1
+ line = f.__code__.co_firstlineno
+ file = f.__code__.co_filename
+ module = inspect.getmodule(f)
+ self.modules.add(module)
if isinstance(f, types.MethodType):
reqargs = 2
else:
reqargs = 1
- nargs = func_code(f).co_argcount
+ nargs = f.__code__.co_argcount
if nargs > reqargs:
- self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
- self.error = 1
+ self.log.error("%s:%d: Rule %r has too many arguments", file, line, f.__name__)
+ self.error = True
if nargs < reqargs:
- self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
- self.error = 1
-
- for f in self.files:
- self.validate_file(f)
+ self.log.error("%s:%d: Rule %r requires an argument", file, line, f.__name__)
+ self.error = True
+ for module in self.modules:
+ self.validate_module(module)
# -----------------------------------------------------------------------------
- # validate_file()
+ # validate_module()
#
# This checks to see if there are duplicated t_rulename() functions or strings
# in the parser input file. This is done using a simple regular expression
- # match on each line in the given file.
+ # match on each line in the source code of the given module.
# -----------------------------------------------------------------------------
- def validate_file(self,filename):
- import os.path
- base,ext = os.path.splitext(filename)
- if ext != '.py': return # No idea what the file is. Return OK
-
+ def validate_module(self, module):
try:
- f = open(filename)
- lines = f.readlines()
- f.close()
+ lines, linen = inspect.getsourcelines(module)
except IOError:
- return # Couldn't find the file. Don't worry about it
+ return
fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(')
sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=')
- counthash = { }
- linen = 1
- for l in lines:
- m = fre.match(l)
+ counthash = {}
+ linen += 1
+ for line in lines:
+ m = fre.match(line)
if not m:
- m = sre.match(l)
+ m = sre.match(line)
if m:
name = m.group(1)
prev = counthash.get(name)
if not prev:
counthash[name] = linen
else:
- self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev)
- self.error = 1
+ filename = inspect.getsourcefile(module)
+ self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev)
+ self.error = True
linen += 1
-
+
# -----------------------------------------------------------------------------
# lex(module)
#
# Build all of the regular expression rules from definitions in the supplied module
# -----------------------------------------------------------------------------
-def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None):
+def lex(*, module=None, object=None, debug=False,
+ reflags=int(re.VERBOSE), debuglog=None, errorlog=None):
+
global lexer
+
ldict = None
- stateinfo = { 'INITIAL' : 'inclusive'}
+ stateinfo = {'INITIAL': 'inclusive'}
lexobj = Lexer()
- lexobj.lexoptimize = optimize
- global token,input
+ global token, input
if errorlog is None:
errorlog = PlyLogger(sys.stderr)
@@ -878,131 +732,124 @@ def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,now
debuglog = PlyLogger(sys.stderr)
# Get the module dictionary used for the lexer
- if object: module = object
+ if object:
+ module = object
+ # Get the module dictionary used for the parser
if module:
- _items = [(k,getattr(module,k)) for k in dir(module)]
+ _items = [(k, getattr(module, k)) for k in dir(module)]
ldict = dict(_items)
+ # If no __file__ attribute is available, try to obtain it from the __module__ instead
+ if '__file__' not in ldict:
+ ldict['__file__'] = sys.modules[ldict['__module__']].__file__
else:
ldict = get_caller_module_dict(2)
# Collect parser information from the dictionary
- linfo = LexerReflect(ldict,log=errorlog,reflags=reflags)
+ linfo = LexerReflect(ldict, log=errorlog, reflags=reflags)
linfo.get_all()
- if not optimize:
- if linfo.validate_all():
- raise SyntaxError("Can't build lexer")
-
- if optimize and lextab:
- try:
- lexobj.readtab(lextab,ldict)
- token = lexobj.token
- input = lexobj.input
- lexer = lexobj
- return lexobj
-
- except ImportError:
- pass
+ if linfo.validate_all():
+ raise SyntaxError("Can't build lexer")
# Dump some basic debugging information
if debug:
- debuglog.info("lex: tokens = %r", linfo.tokens)
- debuglog.info("lex: literals = %r", linfo.literals)
- debuglog.info("lex: states = %r", linfo.stateinfo)
+ debuglog.info('lex: tokens = %r', linfo.tokens)
+ debuglog.info('lex: literals = %r', linfo.literals)
+ debuglog.info('lex: states = %r', linfo.stateinfo)
# Build a dictionary of valid token names
- lexobj.lextokens = { }
+ lexobj.lextokens = set()
for n in linfo.tokens:
- lexobj.lextokens[n] = 1
+ lexobj.lextokens.add(n)
# Get literals specification
- if isinstance(linfo.literals,(list,tuple)):
+ if isinstance(linfo.literals, (list, tuple)):
lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
else:
lexobj.lexliterals = linfo.literals
+ lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals)
+
# Get the stateinfo dictionary
stateinfo = linfo.stateinfo
- regexs = { }
+ regexs = {}
# Build the master regular expressions
for state in stateinfo:
regex_list = []
# Add rules defined by functions first
for fname, f in linfo.funcsym[state]:
- line = func_code(f).co_firstlineno
- file = func_code(f).co_filename
- regex_list.append("(?P<%s>%s)" % (fname,f.__doc__))
+ regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f)))
if debug:
- debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state)
+ debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state)
# Now add all of the simple rules
- for name,r in linfo.strsym[state]:
- regex_list.append("(?P<%s>%s)" % (name,r))
+ for name, r in linfo.strsym[state]:
+ regex_list.append('(?P<%s>%s)' % (name, r))
if debug:
- debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state)
+ debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state)
regexs[state] = regex_list
# Build the master regular expressions
if debug:
- debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====")
+ debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====')
for state in regexs:
- lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames)
+ lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames)
lexobj.lexstatere[state] = lexre
lexobj.lexstateretext[state] = re_text
lexobj.lexstaterenames[state] = re_names
if debug:
- for i in range(len(re_text)):
- debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i])
+ for i, text in enumerate(re_text):
+ debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text)
# For inclusive states, we need to add the regular expressions from the INITIAL state
- for state,stype in stateinfo.items():
- if state != "INITIAL" and stype == 'inclusive':
- lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
- lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
- lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
+ for state, stype in stateinfo.items():
+ if state != 'INITIAL' and stype == 'inclusive':
+ lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
+ lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
+ lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
lexobj.lexstateinfo = stateinfo
- lexobj.lexre = lexobj.lexstatere["INITIAL"]
- lexobj.lexretext = lexobj.lexstateretext["INITIAL"]
+ lexobj.lexre = lexobj.lexstatere['INITIAL']
+ lexobj.lexretext = lexobj.lexstateretext['INITIAL']
lexobj.lexreflags = reflags
# Set up ignore variables
lexobj.lexstateignore = linfo.ignore
- lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","")
+ lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '')
# Set up error functions
lexobj.lexstateerrorf = linfo.errorf
- lexobj.lexerrorf = linfo.errorf.get("INITIAL",None)
+ lexobj.lexerrorf = linfo.errorf.get('INITIAL', None)
if not lexobj.lexerrorf:
- errorlog.warning("No t_error rule is defined")
+ errorlog.warning('No t_error rule is defined')
+
+ # Set up eof functions
+ lexobj.lexstateeoff = linfo.eoff
+ lexobj.lexeoff = linfo.eoff.get('INITIAL', None)
# Check state information for ignore and error rules
- for s,stype in stateinfo.items():
+ for s, stype in stateinfo.items():
if stype == 'exclusive':
- if not s in linfo.errorf:
- errorlog.warning("No error rule is defined for exclusive state '%s'", s)
- if not s in linfo.ignore and lexobj.lexignore:
- errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
+ if s not in linfo.errorf:
+ errorlog.warning("No error rule is defined for exclusive state %r", s)
+ if s not in linfo.ignore and lexobj.lexignore:
+ errorlog.warning("No ignore rule is defined for exclusive state %r", s)
elif stype == 'inclusive':
- if not s in linfo.errorf:
- linfo.errorf[s] = linfo.errorf.get("INITIAL",None)
- if not s in linfo.ignore:
- linfo.ignore[s] = linfo.ignore.get("INITIAL","")
+ if s not in linfo.errorf:
+ linfo.errorf[s] = linfo.errorf.get('INITIAL', None)
+ if s not in linfo.ignore:
+ linfo.ignore[s] = linfo.ignore.get('INITIAL', '')
# Create global versions of the token() and input() functions
token = lexobj.token
input = lexobj.input
lexer = lexobj
- # If in optimize mode, we write the lextab
- if lextab and optimize:
- lexobj.writetab(lextab,outputdir)
-
return lexobj
# -----------------------------------------------------------------------------
@@ -1011,15 +858,14 @@ def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,now
# This runs the lexer as a main program
# -----------------------------------------------------------------------------
-def runmain(lexer=None,data=None):
+def runmain(lexer=None, data=None):
if not data:
try:
filename = sys.argv[1]
- f = open(filename)
- data = f.read()
- f.close()
+ with open(filename) as f:
+ data = f.read()
except IndexError:
- sys.stdout.write("Reading from standard input (type EOF to end):\n")
+ sys.stdout.write('Reading from standard input (type EOF to end):\n')
data = sys.stdin.read()
if lexer:
@@ -1032,10 +878,11 @@ def runmain(lexer=None,data=None):
else:
_token = token
- while 1:
+ while True:
tok = _token()
- if not tok: break
- sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos))
+ if not tok:
+ break
+ sys.stdout.write(f'({tok.type},{tok.value!r},{tok.lineno},{tok.lexpos})\n')
# -----------------------------------------------------------------------------
# @TOKEN(regex)
@@ -1045,14 +892,10 @@ def runmain(lexer=None,data=None):
# -----------------------------------------------------------------------------
def TOKEN(r):
- def set_doc(f):
- if hasattr(r,"__call__"):
- f.__doc__ = r.__doc__
+ def set_regex(f):
+ if hasattr(r, '__call__'):
+ f.regex = _get_regex(r)
else:
- f.__doc__ = r
+ f.regex = r
return f
- return set_doc
-
-# Alternative spelling of the TOKEN decorator
-Token = TOKEN
-
+ return set_regex
diff --git a/components/script/dom/bindings/codegen/ply/ply/yacc.py b/components/script/dom/bindings/codegen/ply/ply/yacc.py
index e9f5c657551..bce63c18241 100644
--- a/components/script/dom/bindings/codegen/ply/ply/yacc.py
+++ b/components/script/dom/bindings/codegen/ply/ply/yacc.py
@@ -1,22 +1,24 @@
# -----------------------------------------------------------------------------
# ply: yacc.py
#
-# Copyright (C) 2001-2009,
+# Copyright (C) 2001-2020
# David M. Beazley (Dabeaz LLC)
# All rights reserved.
#
+# Latest version: https://github.com/dabeaz/ply
+#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
-#
+#
# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-# * Neither the name of the David Beazley or Dabeaz LLC may be used to
+# and/or other materials provided with the distribution.
+# * Neither the name of David Beazley or Dabeaz LLC may be used to
# endorse or promote products derived from this software without
-# specific prior written permission.
+# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -32,7 +34,7 @@
# -----------------------------------------------------------------------------
#
# This implements an LR parser that is constructed from grammar rules defined
-# as Python functions. The grammer is specified by supplying the BNF inside
+# as Python functions. The grammar is specified by supplying the BNF inside
# Python documentation strings. The inspiration for this technique was borrowed
# from John Aycock's Spark parsing system. PLY might be viewed as cross between
# Spark and the GNU bison utility.
@@ -59,8 +61,10 @@
# own risk!
# ----------------------------------------------------------------------------
-__version__ = "3.3"
-__tabversion__ = "3.2" # Table version
+import re
+import types
+import sys
+import inspect
#-----------------------------------------------------------------------------
# === User configurable parameters ===
@@ -68,95 +72,69 @@ __tabversion__ = "3.2" # Table version
# Change these to modify the default behavior of yacc (if you wish)
#-----------------------------------------------------------------------------
-yaccdebug = 1 # Debugging mode. If set, yacc generates a
- # a 'parser.out' file in the current directory
+yaccdebug = False # Debugging mode. If set, yacc generates a
+# a 'parser.out' file in the current directory
debug_file = 'parser.out' # Default name of the debugging file
-tab_module = 'parsetab' # Default name of the table module
-default_lr = 'LALR' # Default LR table generation method
-
error_count = 3 # Number of symbols that must be shifted to leave recovery mode
-
-yaccdevel = 0 # Set to True if developing yacc. This turns off optimized
- # implementations of certain functions.
-
resultlimit = 40 # Size limit of results when running in debug mode.
-pickle_protocol = 0 # Protocol to use when writing pickle files
-
-import re, types, sys, os.path
-
-# Compatibility function for python 2.6/3.0
-if sys.version_info[0] < 3:
- def func_code(f):
- return f.func_code
-else:
- def func_code(f):
- return f.__code__
-
-# Compatibility
-try:
- MAXINT = sys.maxint
-except AttributeError:
- MAXINT = sys.maxsize
+MAXINT = sys.maxsize
-# Python 2.x/3.0 compatibility.
-def load_ply_lex():
- if sys.version_info[0] < 3:
- import lex
- else:
- import ply.lex as lex
- return lex
-
-# This object is a stand-in for a logging object created by the
+# This object is a stand-in for a logging object created by the
# logging module. PLY will use this by default to create things
# such as the parser.out file. If a user wants more detailed
# information, they can create their own logging object and pass
# it into PLY.
class PlyLogger(object):
- def __init__(self,f):
+ def __init__(self, f):
self.f = f
- def debug(self,msg,*args,**kwargs):
- self.f.write((msg % args) + "\n")
- info = debug
- def warning(self,msg,*args,**kwargs):
- self.f.write("WARNING: "+ (msg % args) + "\n")
+ def debug(self, msg, *args, **kwargs):
+ self.f.write((msg % args) + '\n')
+
+ info = debug
- def error(self,msg,*args,**kwargs):
- self.f.write("ERROR: " + (msg % args) + "\n")
+ def warning(self, msg, *args, **kwargs):
+ self.f.write('WARNING: ' + (msg % args) + '\n')
+
+ def error(self, msg, *args, **kwargs):
+ self.f.write('ERROR: ' + (msg % args) + '\n')
critical = debug
# Null logger is used when no output is generated. Does nothing.
class NullLogger(object):
- def __getattribute__(self,name):
+ def __getattribute__(self, name):
return self
- def __call__(self,*args,**kwargs):
+
+ def __call__(self, *args, **kwargs):
return self
-
+
# Exception raised for yacc-related errors
-class YaccError(Exception): pass
+class YaccError(Exception):
+ pass
# Format the result message that the parser produces when running in debug mode.
def format_result(r):
repr_str = repr(r)
- if '\n' in repr_str: repr_str = repr(repr_str)
+ if '\n' in repr_str:
+ repr_str = repr(repr_str)
if len(repr_str) > resultlimit:
- repr_str = repr_str[:resultlimit]+" ..."
- result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str)
+ repr_str = repr_str[:resultlimit] + ' ...'
+ result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str)
return result
-
# Format stack entries when the parser is running in debug mode
def format_stack_entry(r):
repr_str = repr(r)
- if '\n' in repr_str: repr_str = repr(repr_str)
+ if '\n' in repr_str:
+ repr_str = repr(repr_str)
if len(repr_str) < 16:
return repr_str
else:
- return "<%s @ 0x%x>" % (type(r).__name__,id(r))
+ return '<%s @ 0x%x>' % (type(r).__name__, id(r))
#-----------------------------------------------------------------------------
# === LR Parsing Engine ===
@@ -176,8 +154,11 @@ def format_stack_entry(r):
# .endlexpos = Ending lex position (optional, set automatically)
class YaccSymbol:
- def __str__(self): return self.type
- def __repr__(self): return str(self)
+ def __str__(self):
+ return self.type
+
+ def __repr__(self):
+ return str(self)
# This class is a wrapper around the objects actually passed to each
# grammar rule. Index lookup and assignment actually assign the
@@ -189,46 +170,53 @@ class YaccSymbol:
# representing the range of positional information for a symbol.
class YaccProduction:
- def __init__(self,s,stack=None):
+ def __init__(self, s, stack=None):
self.slice = s
self.stack = stack
self.lexer = None
- self.parser= None
- def __getitem__(self,n):
- if n >= 0: return self.slice[n].value
- else: return self.stack[n].value
+ self.parser = None
+
+ def __getitem__(self, n):
+ if isinstance(n, slice):
+ return [s.value for s in self.slice[n]]
+ elif n >= 0:
+ return self.slice[n].value
+ else:
+ return self.stack[n].value
- def __setitem__(self,n,v):
+ def __setitem__(self, n, v):
self.slice[n].value = v
- def __getslice__(self,i,j):
+ def __getslice__(self, i, j):
return [s.value for s in self.slice[i:j]]
def __len__(self):
return len(self.slice)
- def lineno(self,n):
- return getattr(self.slice[n],"lineno",0)
+ def lineno(self, n):
+ return getattr(self.slice[n], 'lineno', 0)
- def set_lineno(self,n,lineno):
+ def set_lineno(self, n, lineno):
self.slice[n].lineno = lineno
- def linespan(self,n):
- startline = getattr(self.slice[n],"lineno",0)
- endline = getattr(self.slice[n],"endlineno",startline)
- return startline,endline
+ def linespan(self, n):
+ startline = getattr(self.slice[n], 'lineno', 0)
+ endline = getattr(self.slice[n], 'endlineno', startline)
+ return startline, endline
- def lexpos(self,n):
- return getattr(self.slice[n],"lexpos",0)
+ def lexpos(self, n):
+ return getattr(self.slice[n], 'lexpos', 0)
- def lexspan(self,n):
- startpos = getattr(self.slice[n],"lexpos",0)
- endpos = getattr(self.slice[n],"endlexpos",startpos)
- return startpos,endpos
+ def set_lexpos(self, n, lexpos):
+ self.slice[n].lexpos = lexpos
- def error(self):
- raise SyntaxError
+ def lexspan(self, n):
+ startpos = getattr(self.slice[n], 'lexpos', 0)
+ endpos = getattr(self.slice[n], 'endlexpos', startpos)
+ return startpos, endpos
+ def error(self):
+ raise SyntaxError
# -----------------------------------------------------------------------------
# == LRParser ==
@@ -237,14 +225,16 @@ class YaccProduction:
# -----------------------------------------------------------------------------
class LRParser:
- def __init__(self,lrtab,errorf):
+ def __init__(self, lrtab, errorf):
self.productions = lrtab.lr_productions
- self.action = lrtab.lr_action
- self.goto = lrtab.lr_goto
- self.errorfunc = errorf
+ self.action = lrtab.lr_action
+ self.goto = lrtab.lr_goto
+ self.errorfunc = errorf
+ self.set_defaulted_states()
+ self.errorok = True
def errok(self):
- self.errorok = 1
+ self.errorok = True
def restart(self):
del self.statestack[:]
@@ -254,47 +244,52 @@ class LRParser:
self.symstack.append(sym)
self.statestack.append(0)
- def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
- if debug or yaccdevel:
- if isinstance(debug,int):
- debug = PlyLogger(sys.stderr)
- return self.parsedebug(input,lexer,debug,tracking,tokenfunc)
- elif tracking:
- return self.parseopt(input,lexer,debug,tracking,tokenfunc)
- else:
- return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc)
-
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # parsedebug().
+ # Defaulted state support.
+ # This method identifies parser states where there is only one possible reduction action.
+ # For such states, the parser can make a choose to make a rule reduction without consuming
+ # the next look-ahead token. This delayed invocation of the tokenizer can be useful in
+ # certain kinds of advanced parsing situations where the lexer and parser interact with
+ # each other or change states (i.e., manipulation of scope, lexer states, etc.).
#
- # This is the debugging enabled version of parse(). All changes made to the
- # parsing engine should be made here. For the non-debugging version,
- # copy this code to a method parseopt() and delete all of the sections
- # enclosed in:
+ # See: http://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions
+ def set_defaulted_states(self):
+ self.defaulted_states = {}
+ for state, actions in self.action.items():
+ rules = list(actions.values())
+ if len(rules) == 1 and rules[0] < 0:
+ self.defaulted_states[state] = rules[0]
+
+ def disable_defaulted_states(self):
+ self.defaulted_states = {}
+
+ # parse().
#
- # #--! DEBUG
- # statements
- # #--! DEBUG
- #
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None):
- lookahead = None # Current lookahead symbol
- lookaheadstack = [ ] # Stack of lookahead symbols
- actions = self.action # Local reference to action table (to avoid lookup on self.)
- goto = self.goto # Local reference to goto table (to avoid lookup on self.)
- prod = self.productions # Local reference to production list (to avoid lookup on self.)
- pslice = YaccProduction(None) # Production object passed to grammar rules
- errorcount = 0 # Used during error recovery
+ # This is the core parsing engine. To operate, it requires a lexer object.
+ # Two options are provided. The debug flag turns on debugging so that you can
+ # see the various rule reductions and parsing steps. tracking turns on position
+ # tracking. In this mode, symbols will record the starting/ending line number and
+ # character index.
+
+ def parse(self, input=None, lexer=None, debug=False, tracking=False):
+ # If debugging has been specified as a flag, turn it into a logging object
+ if isinstance(debug, int) and debug:
+ debug = PlyLogger(sys.stderr)
+
+ lookahead = None # Current lookahead symbol
+ lookaheadstack = [] # Stack of lookahead symbols
+ actions = self.action # Local reference to action table (to avoid lookup on self.)
+ goto = self.goto # Local reference to goto table (to avoid lookup on self.)
+ prod = self.productions # Local reference to production list (to avoid lookup on self.)
+ defaulted_states = self.defaulted_states # Local reference to defaulted states
+ pslice = YaccProduction(None) # Production object passed to grammar rules
+ errorcount = 0 # Used during error recovery
- # --! DEBUG
- debug.info("PLY: PARSE DEBUG START")
- # --! DEBUG
+ if debug:
+ debug.info('PLY: PARSE DEBUG START')
# If no lexer was given, we will try to use the lex module
if not lexer:
- lex = load_ply_lex()
+ from . import lex
lexer = lex.lexer
# Set up the lexer and parser objects on pslice
@@ -305,72 +300,67 @@ class LRParser:
if input is not None:
lexer.input(input)
- if tokenfunc is None:
- # Tokenize function
- get_token = lexer.token
- else:
- get_token = tokenfunc
+ # Set the token function
+ get_token = self.token = lexer.token
# Set up the state and symbol stacks
-
- statestack = [ ] # Stack of parsing states
- self.statestack = statestack
- symstack = [ ] # Stack of grammar symbols
- self.symstack = symstack
-
- pslice.stack = symstack # Put in the production
- errtoken = None # Err token
+ statestack = self.statestack = [] # Stack of parsing states
+ symstack = self.symstack = [] # Stack of grammar symbols
+ pslice.stack = symstack # Put in the production
+ errtoken = None # Err token
# The start state is assumed to be (0,$end)
statestack.append(0)
sym = YaccSymbol()
- sym.type = "$end"
+ sym.type = '$end'
symstack.append(sym)
state = 0
- while 1:
+ while True:
# Get the next symbol on the input. If a lookahead symbol
# is already set, we just use that. Otherwise, we'll pull
# the next token off of the lookaheadstack or from the lexer
- # --! DEBUG
- debug.debug('')
- debug.debug('State : %s', state)
- # --! DEBUG
+ if debug:
+ debug.debug('State : %s', state)
- if not lookahead:
- if not lookaheadstack:
- lookahead = get_token() # Get the next token
- else:
- lookahead = lookaheadstack.pop()
+ if state not in defaulted_states:
if not lookahead:
- lookahead = YaccSymbol()
- lookahead.type = "$end"
-
- # --! DEBUG
- debug.debug('Stack : %s',
- ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
- # --! DEBUG
+ if not lookaheadstack:
+ lookahead = get_token() # Get the next token
+ else:
+ lookahead = lookaheadstack.pop()
+ if not lookahead:
+ lookahead = YaccSymbol()
+ lookahead.type = '$end'
+
+ # Check the action table
+ ltype = lookahead.type
+ t = actions[state].get(ltype)
+ else:
+ t = defaulted_states[state]
+ if debug:
+ debug.debug('Defaulted state %s: Reduce using %d', state, -t)
- # Check the action table
- ltype = lookahead.type
- t = actions[state].get(ltype)
+ if debug:
+ debug.debug('Stack : %s',
+ ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
if t is not None:
if t > 0:
# shift a symbol on the stack
statestack.append(t)
state = t
-
- # --! DEBUG
- debug.debug("Action : Shift and goto state %s", t)
- # --! DEBUG
+
+ if debug:
+ debug.debug('Action : Shift and goto state %s', t)
symstack.append(lookahead)
lookahead = None
# Decrease error count on successful shift
- if errorcount: errorcount -=1
+ if errorcount:
+ errorcount -= 1
continue
if t < 0:
@@ -384,358 +374,69 @@ class LRParser:
sym.type = pname # Production name
sym.value = None
- # --! DEBUG
- if plen:
- debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t)
- else:
- debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t)
-
- # --! DEBUG
+ if debug:
+ if plen:
+ debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str,
+ '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']',
+ goto[statestack[-1-plen]][pname])
+ else:
+ debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [],
+ goto[statestack[-1]][pname])
if plen:
targ = symstack[-plen-1:]
targ[0] = sym
- # --! TRACKING
if tracking:
- t1 = targ[1]
- sym.lineno = t1.lineno
- sym.lexpos = t1.lexpos
- t1 = targ[-1]
- sym.endlineno = getattr(t1,"endlineno",t1.lineno)
- sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos)
-
- # --! TRACKING
+ t1 = targ[1]
+ sym.lineno = t1.lineno
+ sym.lexpos = t1.lexpos
+ t1 = targ[-1]
+ sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
+ sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # The code enclosed in this section is duplicated
+ # The code enclosed in this section is duplicated
# below as a performance optimization. Make sure
# changes get made in both locations.
pslice.slice = targ
-
- try:
- # Call the grammar rule with our special slice object
- del symstack[-plen:]
- del statestack[-plen:]
- p.callable(pslice)
- # --! DEBUG
- debug.info("Result : %s", format_result(pslice[0]))
- # --! DEBUG
- symstack.append(sym)
- state = goto[statestack[-1]][pname]
- statestack.append(state)
- except SyntaxError:
- # If an error was set. Enter error recovery state
- lookaheadstack.append(lookahead)
- symstack.pop()
- statestack.pop()
- state = statestack[-1]
- sym.type = 'error'
- lookahead = sym
- errorcount = error_count
- self.errorok = 0
- continue
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- else:
-
- # --! TRACKING
- if tracking:
- sym.lineno = lexer.lineno
- sym.lexpos = lexer.lexpos
- # --! TRACKING
-
- targ = [ sym ]
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # The code enclosed in this section is duplicated
- # above as a performance optimization. Make sure
- # changes get made in both locations.
-
- pslice.slice = targ
try:
# Call the grammar rule with our special slice object
+ del symstack[-plen:]
+ self.state = state
p.callable(pslice)
- # --! DEBUG
- debug.info("Result : %s", format_result(pslice[0]))
- # --! DEBUG
+ del statestack[-plen:]
+ if debug:
+ debug.info('Result : %s', format_result(pslice[0]))
symstack.append(sym)
state = goto[statestack[-1]][pname]
statestack.append(state)
except SyntaxError:
# If an error was set. Enter error recovery state
- lookaheadstack.append(lookahead)
- symstack.pop()
- statestack.pop()
+ lookaheadstack.append(lookahead) # Save the current lookahead token
+ symstack.extend(targ[1:-1]) # Put the production slice back on the stack
+ statestack.pop() # Pop back one state (before the reduce)
state = statestack[-1]
sym.type = 'error'
+ sym.value = 'error'
lookahead = sym
errorcount = error_count
- self.errorok = 0
- continue
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ self.errorok = False
- if t == 0:
- n = symstack[-1]
- result = getattr(n,"value",None)
- # --! DEBUG
- debug.info("Done : Returning %s", format_result(result))
- debug.info("PLY: PARSE DEBUG END")
- # --! DEBUG
- return result
-
- if t == None:
-
- # --! DEBUG
- debug.error('Error : %s',
- ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
- # --! DEBUG
-
- # We have some kind of parsing error here. To handle
- # this, we are going to push the current token onto
- # the tokenstack and replace it with an 'error' token.
- # If there are any synchronization rules, they may
- # catch it.
- #
- # In addition to pushing the error token, we call call
- # the user defined p_error() function if this is the
- # first syntax error. This function is only called if
- # errorcount == 0.
- if errorcount == 0 or self.errorok:
- errorcount = error_count
- self.errorok = 0
- errtoken = lookahead
- if errtoken.type == "$end":
- errtoken = None # End of file!
- if self.errorfunc:
- global errok,token,restart
- errok = self.errok # Set some special functions available in error recovery
- token = get_token
- restart = self.restart
- if errtoken and not hasattr(errtoken,'lexer'):
- errtoken.lexer = lexer
- tok = self.errorfunc(errtoken)
- del errok, token, restart # Delete special functions
-
- if self.errorok:
- # User must have done some kind of panic
- # mode recovery on their own. The
- # returned token is the next lookahead
- lookahead = tok
- errtoken = None
- continue
- else:
- if errtoken:
- if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
- else: lineno = 0
- if lineno:
- sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
- else:
- sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
- else:
- sys.stderr.write("yacc: Parse error in input. EOF\n")
- return
-
- else:
- errorcount = error_count
-
- # case 1: the statestack only has 1 entry on it. If we're in this state, the
- # entire parse has been rolled back and we're completely hosed. The token is
- # discarded and we just keep going.
-
- if len(statestack) <= 1 and lookahead.type != "$end":
- lookahead = None
- errtoken = None
- state = 0
- # Nuke the pushback stack
- del lookaheadstack[:]
- continue
-
- # case 2: the statestack has a couple of entries on it, but we're
- # at the end of the file. nuke the top entry and generate an error token
-
- # Start nuking entries on the stack
- if lookahead.type == "$end":
- # Whoa. We're really hosed here. Bail out
- return
-
- if lookahead.type != 'error':
- sym = symstack[-1]
- if sym.type == 'error':
- # Hmmm. Error is on top of stack, we'll just nuke input
- # symbol and continue
- lookahead = None
continue
- t = YaccSymbol()
- t.type = 'error'
- if hasattr(lookahead,"lineno"):
- t.lineno = lookahead.lineno
- t.value = lookahead
- lookaheadstack.append(lookahead)
- lookahead = t
- else:
- symstack.pop()
- statestack.pop()
- state = statestack[-1] # Potential bug fix
-
- continue
-
- # Call an error function here
- raise RuntimeError("yacc: internal parser error!!!\n")
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # parseopt().
- #
- # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY.
- # Edit the debug version above, then copy any modifications to the method
- # below while removing #--! DEBUG sections.
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-
- def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
- lookahead = None # Current lookahead symbol
- lookaheadstack = [ ] # Stack of lookahead symbols
- actions = self.action # Local reference to action table (to avoid lookup on self.)
- goto = self.goto # Local reference to goto table (to avoid lookup on self.)
- prod = self.productions # Local reference to production list (to avoid lookup on self.)
- pslice = YaccProduction(None) # Production object passed to grammar rules
- errorcount = 0 # Used during error recovery
-
- # If no lexer was given, we will try to use the lex module
- if not lexer:
- lex = load_ply_lex()
- lexer = lex.lexer
-
- # Set up the lexer and parser objects on pslice
- pslice.lexer = lexer
- pslice.parser = self
-
- # If input was supplied, pass to lexer
- if input is not None:
- lexer.input(input)
-
- if tokenfunc is None:
- # Tokenize function
- get_token = lexer.token
- else:
- get_token = tokenfunc
-
- # Set up the state and symbol stacks
-
- statestack = [ ] # Stack of parsing states
- self.statestack = statestack
- symstack = [ ] # Stack of grammar symbols
- self.symstack = symstack
-
- pslice.stack = symstack # Put in the production
- errtoken = None # Err token
-
- # The start state is assumed to be (0,$end)
-
- statestack.append(0)
- sym = YaccSymbol()
- sym.type = '$end'
- symstack.append(sym)
- state = 0
- while 1:
- # Get the next symbol on the input. If a lookahead symbol
- # is already set, we just use that. Otherwise, we'll pull
- # the next token off of the lookaheadstack or from the lexer
-
- if not lookahead:
- if not lookaheadstack:
- lookahead = get_token() # Get the next token
- else:
- lookahead = lookaheadstack.pop()
- if not lookahead:
- lookahead = YaccSymbol()
- lookahead.type = '$end'
-
- # Check the action table
- ltype = lookahead.type
- t = actions[state].get(ltype)
-
- if t is not None:
- if t > 0:
- # shift a symbol on the stack
- statestack.append(t)
- state = t
-
- symstack.append(lookahead)
- lookahead = None
-
- # Decrease error count on successful shift
- if errorcount: errorcount -=1
- continue
-
- if t < 0:
- # reduce a symbol on the stack, emit a production
- p = prod[-t]
- pname = p.name
- plen = p.len
-
- # Get production function
- sym = YaccSymbol()
- sym.type = pname # Production name
- sym.value = None
-
- if plen:
- targ = symstack[-plen-1:]
- targ[0] = sym
-
- # --! TRACKING
- if tracking:
- t1 = targ[1]
- sym.lineno = t1.lineno
- sym.lexpos = t1.lexpos
- t1 = targ[-1]
- sym.endlineno = getattr(t1,"endlineno",t1.lineno)
- sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos)
-
- # --! TRACKING
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # The code enclosed in this section is duplicated
- # below as a performance optimization. Make sure
- # changes get made in both locations.
- pslice.slice = targ
-
- try:
- # Call the grammar rule with our special slice object
- del symstack[-plen:]
- del statestack[-plen:]
- p.callable(pslice)
- symstack.append(sym)
- state = goto[statestack[-1]][pname]
- statestack.append(state)
- except SyntaxError:
- # If an error was set. Enter error recovery state
- lookaheadstack.append(lookahead)
- symstack.pop()
- statestack.pop()
- state = statestack[-1]
- sym.type = 'error'
- lookahead = sym
- errorcount = error_count
- self.errorok = 0
- continue
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
else:
- # --! TRACKING
if tracking:
- sym.lineno = lexer.lineno
- sym.lexpos = lexer.lexpos
- # --! TRACKING
+ sym.lineno = lexer.lineno
+ sym.lexpos = lexer.lexpos
- targ = [ sym ]
+ targ = [sym]
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # The code enclosed in this section is duplicated
+ # The code enclosed in this section is duplicated
# above as a performance optimization. Make sure
# changes get made in both locations.
@@ -743,283 +444,41 @@ class LRParser:
try:
# Call the grammar rule with our special slice object
+ self.state = state
p.callable(pslice)
+ if debug:
+ debug.info('Result : %s', format_result(pslice[0]))
symstack.append(sym)
state = goto[statestack[-1]][pname]
statestack.append(state)
except SyntaxError:
# If an error was set. Enter error recovery state
- lookaheadstack.append(lookahead)
- symstack.pop()
- statestack.pop()
+ lookaheadstack.append(lookahead) # Save the current lookahead token
+ statestack.pop() # Pop back one state (before the reduce)
state = statestack[-1]
sym.type = 'error'
+ sym.value = 'error'
lookahead = sym
errorcount = error_count
- self.errorok = 0
+ self.errorok = False
+
continue
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if t == 0:
n = symstack[-1]
- return getattr(n,"value",None)
-
- if t == None:
-
- # We have some kind of parsing error here. To handle
- # this, we are going to push the current token onto
- # the tokenstack and replace it with an 'error' token.
- # If there are any synchronization rules, they may
- # catch it.
- #
- # In addition to pushing the error token, we call call
- # the user defined p_error() function if this is the
- # first syntax error. This function is only called if
- # errorcount == 0.
- if errorcount == 0 or self.errorok:
- errorcount = error_count
- self.errorok = 0
- errtoken = lookahead
- if errtoken.type == '$end':
- errtoken = None # End of file!
- if self.errorfunc:
- global errok,token,restart
- errok = self.errok # Set some special functions available in error recovery
- token = get_token
- restart = self.restart
- if errtoken and not hasattr(errtoken,'lexer'):
- errtoken.lexer = lexer
- tok = self.errorfunc(errtoken)
- del errok, token, restart # Delete special functions
-
- if self.errorok:
- # User must have done some kind of panic
- # mode recovery on their own. The
- # returned token is the next lookahead
- lookahead = tok
- errtoken = None
- continue
- else:
- if errtoken:
- if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
- else: lineno = 0
- if lineno:
- sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
- else:
- sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
- else:
- sys.stderr.write("yacc: Parse error in input. EOF\n")
- return
-
- else:
- errorcount = error_count
-
- # case 1: the statestack only has 1 entry on it. If we're in this state, the
- # entire parse has been rolled back and we're completely hosed. The token is
- # discarded and we just keep going.
-
- if len(statestack) <= 1 and lookahead.type != '$end':
- lookahead = None
- errtoken = None
- state = 0
- # Nuke the pushback stack
- del lookaheadstack[:]
- continue
+ result = getattr(n, 'value', None)
- # case 2: the statestack has a couple of entries on it, but we're
- # at the end of the file. nuke the top entry and generate an error token
-
- # Start nuking entries on the stack
- if lookahead.type == '$end':
- # Whoa. We're really hosed here. Bail out
- return
-
- if lookahead.type != 'error':
- sym = symstack[-1]
- if sym.type == 'error':
- # Hmmm. Error is on top of stack, we'll just nuke input
- # symbol and continue
- lookahead = None
- continue
- t = YaccSymbol()
- t.type = 'error'
- if hasattr(lookahead,"lineno"):
- t.lineno = lookahead.lineno
- t.value = lookahead
- lookaheadstack.append(lookahead)
- lookahead = t
- else:
- symstack.pop()
- statestack.pop()
- state = statestack[-1] # Potential bug fix
-
- continue
-
- # Call an error function here
- raise RuntimeError("yacc: internal parser error!!!\n")
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # parseopt_notrack().
- #
- # Optimized version of parseopt() with line number tracking removed.
- # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove
- # code in the #--! TRACKING sections
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
- lookahead = None # Current lookahead symbol
- lookaheadstack = [ ] # Stack of lookahead symbols
- actions = self.action # Local reference to action table (to avoid lookup on self.)
- goto = self.goto # Local reference to goto table (to avoid lookup on self.)
- prod = self.productions # Local reference to production list (to avoid lookup on self.)
- pslice = YaccProduction(None) # Production object passed to grammar rules
- errorcount = 0 # Used during error recovery
-
- # If no lexer was given, we will try to use the lex module
- if not lexer:
- lex = load_ply_lex()
- lexer = lex.lexer
-
- # Set up the lexer and parser objects on pslice
- pslice.lexer = lexer
- pslice.parser = self
-
- # If input was supplied, pass to lexer
- if input is not None:
- lexer.input(input)
-
- if tokenfunc is None:
- # Tokenize function
- get_token = lexer.token
- else:
- get_token = tokenfunc
-
- # Set up the state and symbol stacks
-
- statestack = [ ] # Stack of parsing states
- self.statestack = statestack
- symstack = [ ] # Stack of grammar symbols
- self.symstack = symstack
-
- pslice.stack = symstack # Put in the production
- errtoken = None # Err token
-
- # The start state is assumed to be (0,$end)
-
- statestack.append(0)
- sym = YaccSymbol()
- sym.type = '$end'
- symstack.append(sym)
- state = 0
- while 1:
- # Get the next symbol on the input. If a lookahead symbol
- # is already set, we just use that. Otherwise, we'll pull
- # the next token off of the lookaheadstack or from the lexer
-
- if not lookahead:
- if not lookaheadstack:
- lookahead = get_token() # Get the next token
- else:
- lookahead = lookaheadstack.pop()
- if not lookahead:
- lookahead = YaccSymbol()
- lookahead.type = '$end'
-
- # Check the action table
- ltype = lookahead.type
- t = actions[state].get(ltype)
-
- if t is not None:
- if t > 0:
- # shift a symbol on the stack
- statestack.append(t)
- state = t
-
- symstack.append(lookahead)
- lookahead = None
-
- # Decrease error count on successful shift
- if errorcount: errorcount -=1
- continue
+ if debug:
+ debug.info('Done : Returning %s', format_result(result))
+ debug.info('PLY: PARSE DEBUG END')
- if t < 0:
- # reduce a symbol on the stack, emit a production
- p = prod[-t]
- pname = p.name
- plen = p.len
-
- # Get production function
- sym = YaccSymbol()
- sym.type = pname # Production name
- sym.value = None
-
- if plen:
- targ = symstack[-plen-1:]
- targ[0] = sym
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # The code enclosed in this section is duplicated
- # below as a performance optimization. Make sure
- # changes get made in both locations.
-
- pslice.slice = targ
-
- try:
- # Call the grammar rule with our special slice object
- del symstack[-plen:]
- del statestack[-plen:]
- p.callable(pslice)
- symstack.append(sym)
- state = goto[statestack[-1]][pname]
- statestack.append(state)
- except SyntaxError:
- # If an error was set. Enter error recovery state
- lookaheadstack.append(lookahead)
- symstack.pop()
- statestack.pop()
- state = statestack[-1]
- sym.type = 'error'
- lookahead = sym
- errorcount = error_count
- self.errorok = 0
- continue
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- else:
-
- targ = [ sym ]
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # The code enclosed in this section is duplicated
- # above as a performance optimization. Make sure
- # changes get made in both locations.
-
- pslice.slice = targ
-
- try:
- # Call the grammar rule with our special slice object
- p.callable(pslice)
- symstack.append(sym)
- state = goto[statestack[-1]][pname]
- statestack.append(state)
- except SyntaxError:
- # If an error was set. Enter error recovery state
- lookaheadstack.append(lookahead)
- symstack.pop()
- statestack.pop()
- state = statestack[-1]
- sym.type = 'error'
- lookahead = sym
- errorcount = error_count
- self.errorok = 0
- continue
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ return result
- if t == 0:
- n = symstack[-1]
- return getattr(n,"value",None)
+ if t is None:
- if t == None:
+ if debug:
+ debug.error('Error : %s',
+ ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
# We have some kind of parsing error here. To handle
# this, we are going to push the current token onto
@@ -1033,20 +492,15 @@ class LRParser:
# errorcount == 0.
if errorcount == 0 or self.errorok:
errorcount = error_count
- self.errorok = 0
+ self.errorok = False
errtoken = lookahead
if errtoken.type == '$end':
errtoken = None # End of file!
if self.errorfunc:
- global errok,token,restart
- errok = self.errok # Set some special functions available in error recovery
- token = get_token
- restart = self.restart
- if errtoken and not hasattr(errtoken,'lexer'):
+ if errtoken and not hasattr(errtoken, 'lexer'):
errtoken.lexer = lexer
+ self.state = state
tok = self.errorfunc(errtoken)
- del errok, token, restart # Delete special functions
-
if self.errorok:
# User must have done some kind of panic
# mode recovery on their own. The
@@ -1056,14 +510,16 @@ class LRParser:
continue
else:
if errtoken:
- if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
- else: lineno = 0
+ if hasattr(errtoken, 'lineno'):
+ lineno = lookahead.lineno
+ else:
+ lineno = 0
if lineno:
- sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
+ sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
else:
- sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
+ sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
else:
- sys.stderr.write("yacc: Parse error in input. EOF\n")
+ sys.stderr.write('yacc: Parse error in input. EOF\n')
return
else:
@@ -1094,34 +550,43 @@ class LRParser:
if sym.type == 'error':
# Hmmm. Error is on top of stack, we'll just nuke input
# symbol and continue
+ if tracking:
+ sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
+ sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
lookahead = None
continue
+
+ # Create the error symbol for the first time and make it the new lookahead symbol
t = YaccSymbol()
t.type = 'error'
- if hasattr(lookahead,"lineno"):
- t.lineno = lookahead.lineno
+
+ if hasattr(lookahead, 'lineno'):
+ t.lineno = t.endlineno = lookahead.lineno
+ if hasattr(lookahead, 'lexpos'):
+ t.lexpos = t.endlexpos = lookahead.lexpos
t.value = lookahead
lookaheadstack.append(lookahead)
lookahead = t
else:
- symstack.pop()
+ sym = symstack.pop()
+ if tracking:
+ lookahead.lineno = sym.lineno
+ lookahead.lexpos = sym.lexpos
statestack.pop()
- state = statestack[-1] # Potential bug fix
+ state = statestack[-1]
continue
- # Call an error function here
- raise RuntimeError("yacc: internal parser error!!!\n")
+ # If we'r here, something really bad happened
+ raise RuntimeError('yacc: internal parser error!!!\n')
# -----------------------------------------------------------------------------
# === Grammar Representation ===
#
# The following functions, classes, and variables are used to represent and
-# manipulate the rules that make up a grammar.
+# manipulate the rules that make up a grammar.
# -----------------------------------------------------------------------------
-import re
-
# regex matching identifiers
_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
@@ -1131,7 +596,7 @@ _is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
# This class stores the raw information about a single production or grammar rule.
# A grammar rule refers to a specification such as this:
#
-# expr : expr PLUS term
+# expr : expr PLUS term
#
# Here are the basic attributes defined on all productions
#
@@ -1151,7 +616,7 @@ _is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
class Production(object):
reduced = 0
- def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0):
+ def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0):
self.name = name
self.prod = tuple(prod)
self.number = number
@@ -1162,11 +627,11 @@ class Production(object):
self.prec = precedence
# Internal settings used during table construction
-
+
self.len = len(self.prod) # Length of the production
# Create a list of unique production symbols used in the production
- self.usyms = [ ]
+ self.usyms = []
for s in self.prod:
if s not in self.usyms:
self.usyms.append(s)
@@ -1177,15 +642,15 @@ class Production(object):
# Create a string representation
if self.prod:
- self.str = "%s -> %s" % (self.name," ".join(self.prod))
+ self.str = '%s -> %s' % (self.name, ' '.join(self.prod))
else:
- self.str = "%s -> <empty>" % self.name
+ self.str = '%s -> <empty>' % self.name
def __str__(self):
return self.str
def __repr__(self):
- return "Production("+str(self)+")"
+ return 'Production(' + str(self) + ')'
def __len__(self):
return len(self.prod)
@@ -1193,62 +658,37 @@ class Production(object):
def __nonzero__(self):
return 1
- def __getitem__(self,index):
+ def __getitem__(self, index):
return self.prod[index]
-
- # Return the nth lr_item from the production (or None if at the end)
- def lr_item(self,n):
- if n > len(self.prod): return None
- p = LRItem(self,n)
- # Precompute the list of productions immediately following. Hack. Remove later
+ # Return the nth lr_item from the production (or None if at the end)
+ def lr_item(self, n):
+ if n > len(self.prod):
+ return None
+ p = LRItem(self, n)
+ # Precompute the list of productions immediately following.
try:
- p.lr_after = Prodnames[p.prod[n+1]]
- except (IndexError,KeyError):
+ p.lr_after = self.Prodnames[p.prod[n+1]]
+ except (IndexError, KeyError):
p.lr_after = []
try:
p.lr_before = p.prod[n-1]
except IndexError:
p.lr_before = None
-
return p
-
- # Bind the production function name to a callable
- def bind(self,pdict):
- if self.func:
- self.callable = pdict[self.func]
-
-# This class serves as a minimal standin for Production objects when
-# reading table data from files. It only contains information
-# actually used by the LR parsing engine, plus some additional
-# debugging information.
-class MiniProduction(object):
- def __init__(self,str,name,len,func,file,line):
- self.name = name
- self.len = len
- self.func = func
- self.callable = None
- self.file = file
- self.line = line
- self.str = str
- def __str__(self):
- return self.str
- def __repr__(self):
- return "MiniProduction(%s)" % self.str
# Bind the production function name to a callable
- def bind(self,pdict):
+ def bind(self, pdict):
if self.func:
self.callable = pdict[self.func]
-
# -----------------------------------------------------------------------------
# class LRItem
#
# This class represents a specific stage of parsing a production rule. For
-# example:
+# example:
#
-# expr : expr . PLUS term
+# expr : expr . PLUS term
#
# In the above, the "." represents the current location of the parse. Here
# basic attributes:
@@ -1267,26 +707,26 @@ class MiniProduction(object):
# -----------------------------------------------------------------------------
class LRItem(object):
- def __init__(self,p,n):
+ def __init__(self, p, n):
self.name = p.name
self.prod = list(p.prod)
self.number = p.number
self.lr_index = n
- self.lookaheads = { }
- self.prod.insert(n,".")
+ self.lookaheads = {}
+ self.prod.insert(n, '.')
self.prod = tuple(self.prod)
self.len = len(self.prod)
self.usyms = p.usyms
def __str__(self):
if self.prod:
- s = "%s -> %s" % (self.name," ".join(self.prod))
+ s = '%s -> %s' % (self.name, ' '.join(self.prod))
else:
- s = "%s -> <empty>" % self.name
+ s = '%s -> <empty>' % self.name
return s
def __repr__(self):
- return "LRItem("+str(self)+")"
+ return 'LRItem(' + str(self) + ')'
# -----------------------------------------------------------------------------
# rightmost_terminal()
@@ -1309,41 +749,42 @@ def rightmost_terminal(symbols, terminals):
# This data is used for critical parts of the table generation process later.
# -----------------------------------------------------------------------------
-class GrammarError(YaccError): pass
+class GrammarError(YaccError):
+ pass
class Grammar(object):
- def __init__(self,terminals):
+ def __init__(self, terminals):
self.Productions = [None] # A list of all of the productions. The first
- # entry is always reserved for the purpose of
- # building an augmented grammar
+ # entry is always reserved for the purpose of
+ # building an augmented grammar
- self.Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all
- # productions of that nonterminal.
+ self.Prodnames = {} # A dictionary mapping the names of nonterminals to a list of all
+ # productions of that nonterminal.
- self.Prodmap = { } # A dictionary that is only used to detect duplicate
- # productions.
+ self.Prodmap = {} # A dictionary that is only used to detect duplicate
+ # productions.
- self.Terminals = { } # A dictionary mapping the names of terminal symbols to a
- # list of the rules where they are used.
+ self.Terminals = {} # A dictionary mapping the names of terminal symbols to a
+ # list of the rules where they are used.
for term in terminals:
self.Terminals[term] = []
self.Terminals['error'] = []
- self.Nonterminals = { } # A dictionary mapping names of nonterminals to a list
- # of rule numbers where they are used.
+ self.Nonterminals = {} # A dictionary mapping names of nonterminals to a list
+ # of rule numbers where they are used.
- self.First = { } # A dictionary of precomputed FIRST(x) symbols
+ self.First = {} # A dictionary of precomputed FIRST(x) symbols
- self.Follow = { } # A dictionary of precomputed FOLLOW(x) symbols
+ self.Follow = {} # A dictionary of precomputed FOLLOW(x) symbols
- self.Precedence = { } # Precedence rules for each terminal. Contains tuples of the
- # form ('right',level) or ('nonassoc', level) or ('left',level)
+ self.Precedence = {} # Precedence rules for each terminal. Contains tuples of the
+ # form ('right',level) or ('nonassoc', level) or ('left',level)
- self.UsedPrecedence = { } # Precedence rules that were actually used by the grammer.
- # This is only used to provide error checking and to generate
- # a warning about unused precedence rules.
+ self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer.
+ # This is only used to provide error checking and to generate
+ # a warning about unused precedence rules.
self.Start = None # Starting symbol for the grammar
@@ -1351,7 +792,7 @@ class Grammar(object):
def __len__(self):
return len(self.Productions)
- def __getitem__(self,index):
+ def __getitem__(self, index):
return self.Productions[index]
# -----------------------------------------------------------------------------
@@ -1362,14 +803,14 @@ class Grammar(object):
#
# -----------------------------------------------------------------------------
- def set_precedence(self,term,assoc,level):
- assert self.Productions == [None],"Must call set_precedence() before add_production()"
+ def set_precedence(self, term, assoc, level):
+ assert self.Productions == [None], 'Must call set_precedence() before add_production()'
if term in self.Precedence:
- raise GrammarError("Precedence already specified for terminal '%s'" % term)
- if assoc not in ['left','right','nonassoc']:
+ raise GrammarError('Precedence already specified for terminal %r' % term)
+ if assoc not in ['left', 'right', 'nonassoc']:
raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'")
- self.Precedence[term] = (assoc,level)
-
+ self.Precedence[term] = (assoc, level)
+
# -----------------------------------------------------------------------------
# add_production()
#
@@ -1387,72 +828,74 @@ class Grammar(object):
# are valid and that %prec is used correctly.
# -----------------------------------------------------------------------------
- def add_production(self,prodname,syms,func=None,file='',line=0):
+ def add_production(self, prodname, syms, func=None, file='', line=0):
if prodname in self.Terminals:
- raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname))
+ raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname))
if prodname == 'error':
- raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname))
+ raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname))
if not _is_identifier.match(prodname):
- raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname))
+ raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname))
- # Look for literal tokens
- for n,s in enumerate(syms):
+ # Look for literal tokens
+ for n, s in enumerate(syms):
if s[0] in "'\"":
- try:
- c = eval(s)
- if (len(c) > 1):
- raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname))
- if not c in self.Terminals:
- self.Terminals[c] = []
- syms[n] = c
- continue
- except SyntaxError:
- pass
+ try:
+ c = eval(s)
+ if (len(c) > 1):
+ raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' %
+ (file, line, s, prodname))
+ if c not in self.Terminals:
+ self.Terminals[c] = []
+ syms[n] = c
+ continue
+ except SyntaxError:
+ pass
if not _is_identifier.match(s) and s != '%prec':
- raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname))
-
+ raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname))
+
# Determine the precedence level
if '%prec' in syms:
if syms[-1] == '%prec':
- raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line))
+ raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line))
if syms[-2] != '%prec':
- raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line))
+ raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' %
+ (file, line))
precname = syms[-1]
- prodprec = self.Precedence.get(precname,None)
+ prodprec = self.Precedence.get(precname)
if not prodprec:
- raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname))
+ raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname))
else:
- self.UsedPrecedence[precname] = 1
+ self.UsedPrecedence.add(precname)
del syms[-2:] # Drop %prec from the rule
else:
# If no %prec, precedence is determined by the rightmost terminal symbol
- precname = rightmost_terminal(syms,self.Terminals)
- prodprec = self.Precedence.get(precname,('right',0))
-
+ precname = rightmost_terminal(syms, self.Terminals)
+ prodprec = self.Precedence.get(precname, ('right', 0))
+
# See if the rule is already in the rulemap
- map = "%s -> %s" % (prodname,syms)
+ map = '%s -> %s' % (prodname, syms)
if map in self.Prodmap:
m = self.Prodmap[map]
- raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) +
- "Previous definition at %s:%d" % (m.file, m.line))
+ raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) +
+ 'Previous definition at %s:%d' % (m.file, m.line))
# From this point on, everything is valid. Create a new Production instance
pnumber = len(self.Productions)
- if not prodname in self.Nonterminals:
- self.Nonterminals[prodname] = [ ]
+ if prodname not in self.Nonterminals:
+ self.Nonterminals[prodname] = []
# Add the production number to Terminals and Nonterminals
for t in syms:
if t in self.Terminals:
self.Terminals[t].append(pnumber)
else:
- if not t in self.Nonterminals:
- self.Nonterminals[t] = [ ]
+ if t not in self.Nonterminals:
+ self.Nonterminals[t] = []
self.Nonterminals[t].append(pnumber)
# Create a production and add it to the list of productions
- p = Production(pnumber,prodname,syms,prodprec,func,file,line)
+ p = Production(pnumber, prodname, syms, prodprec, func, file, line)
self.Productions.append(p)
self.Prodmap[map] = p
@@ -1460,22 +903,21 @@ class Grammar(object):
try:
self.Prodnames[prodname].append(p)
except KeyError:
- self.Prodnames[prodname] = [ p ]
- return 0
+ self.Prodnames[prodname] = [p]
# -----------------------------------------------------------------------------
# set_start()
#
- # Sets the starting symbol and creates the augmented grammar. Production
+ # Sets the starting symbol and creates the augmented grammar. Production
# rule 0 is S' -> start where start is the start symbol.
# -----------------------------------------------------------------------------
- def set_start(self,start=None):
+ def set_start(self, start=None):
if not start:
start = self.Productions[1].name
if start not in self.Nonterminals:
- raise GrammarError("start symbol %s undefined" % start)
- self.Productions[0] = Production(0,"S'",[start])
+ raise GrammarError('start symbol %s undefined' % start)
+ self.Productions[0] = Production(0, "S'", [start])
self.Nonterminals[start].append(0)
self.Start = start
@@ -1487,26 +929,20 @@ class Grammar(object):
# -----------------------------------------------------------------------------
def find_unreachable(self):
-
+
# Mark all symbols that are reachable from a symbol s
def mark_reachable_from(s):
- if reachable[s]:
- # We've already reached symbol s.
+ if s in reachable:
return
- reachable[s] = 1
- for p in self.Prodnames.get(s,[]):
+ reachable.add(s)
+ for p in self.Prodnames.get(s, []):
for r in p.prod:
mark_reachable_from(r)
- reachable = { }
- for s in list(self.Terminals) + list(self.Nonterminals):
- reachable[s] = 0
+ reachable = set()
+ mark_reachable_from(self.Productions[0].prod[0])
+ return [s for s in self.Nonterminals if s not in reachable]
- mark_reachable_from( self.Productions[0].prod[0] )
-
- return [s for s in list(self.Nonterminals)
- if not reachable[s]]
-
# -----------------------------------------------------------------------------
# infinite_cycles()
#
@@ -1520,20 +956,20 @@ class Grammar(object):
# Terminals:
for t in self.Terminals:
- terminates[t] = 1
+ terminates[t] = True
- terminates['$end'] = 1
+ terminates['$end'] = True
# Nonterminals:
# Initialize to false:
for n in self.Nonterminals:
- terminates[n] = 0
+ terminates[n] = False
# Then propagate termination until no change:
- while 1:
- some_change = 0
- for (n,pl) in self.Prodnames.items():
+ while True:
+ some_change = False
+ for (n, pl) in self.Prodnames.items():
# Nonterminal n terminates iff any of its productions terminates.
for p in pl:
# Production p terminates iff all of its rhs symbols terminate.
@@ -1541,19 +977,19 @@ class Grammar(object):
if not terminates[s]:
# The symbol s does not terminate,
# so production p does not terminate.
- p_terminates = 0
+ p_terminates = False
break
else:
# didn't break from the loop,
# so every symbol s terminates
# so production p terminates.
- p_terminates = 1
+ p_terminates = True
if p_terminates:
# symbol n terminates!
if not terminates[n]:
- terminates[n] = 1
- some_change = 1
+ terminates[n] = True
+ some_change = True
# Don't need to consider any more productions for this n.
break
@@ -1561,9 +997,9 @@ class Grammar(object):
break
infinite = []
- for (s,term) in terminates.items():
+ for (s, term) in terminates.items():
if not term:
- if not s in self.Prodnames and not s in self.Terminals and s != 'error':
+ if s not in self.Prodnames and s not in self.Terminals and s != 'error':
# s is used-but-not-defined, and we've already warned of that,
# so it would be overkill to say that it's also non-terminating.
pass
@@ -1572,22 +1008,22 @@ class Grammar(object):
return infinite
-
# -----------------------------------------------------------------------------
# undefined_symbols()
#
# Find all symbols that were used the grammar, but not defined as tokens or
# grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol
- # and prod is the production where the symbol was used.
+ # and prod is the production where the symbol was used.
# -----------------------------------------------------------------------------
def undefined_symbols(self):
result = []
for p in self.Productions:
- if not p: continue
+ if not p:
+ continue
for s in p.prod:
- if not s in self.Prodnames and not s in self.Terminals and s != 'error':
- result.append((s,p))
+ if s not in self.Prodnames and s not in self.Terminals and s != 'error':
+ result.append((s, p))
return result
# -----------------------------------------------------------------------------
@@ -1598,7 +1034,7 @@ class Grammar(object):
# -----------------------------------------------------------------------------
def unused_terminals(self):
unused_tok = []
- for s,v in self.Terminals.items():
+ for s, v in self.Terminals.items():
if s != 'error' and not v:
unused_tok.append(s)
@@ -1613,7 +1049,7 @@ class Grammar(object):
def unused_rules(self):
unused_prod = []
- for s,v in self.Nonterminals.items():
+ for s, v in self.Nonterminals.items():
if not v:
p = self.Prodnames[s][0]
unused_prod.append(p)
@@ -1625,15 +1061,15 @@ class Grammar(object):
# Returns a list of tuples (term,precedence) corresponding to precedence
# rules that were never used by the grammar. term is the name of the terminal
# on which precedence was applied and precedence is a string such as 'left' or
- # 'right' corresponding to the type of precedence.
+ # 'right' corresponding to the type of precedence.
# -----------------------------------------------------------------------------
def unused_precedence(self):
unused = []
for termname in self.Precedence:
if not (termname in self.Terminals or termname in self.UsedPrecedence):
- unused.append((termname,self.Precedence[termname][0]))
-
+ unused.append((termname, self.Precedence[termname][0]))
+
return unused
# -------------------------------------------------------------------------
@@ -1644,19 +1080,20 @@ class Grammar(object):
# During execution of compute_first1, the result may be incomplete.
# Afterward (e.g., when called from compute_follow()), it will be complete.
# -------------------------------------------------------------------------
- def _first(self,beta):
+ def _first(self, beta):
# We are computing First(x1,x2,x3,...,xn)
- result = [ ]
+ result = []
for x in beta:
- x_produces_empty = 0
+ x_produces_empty = False
# Add all the non-<empty> symbols of First[x] to the result.
for f in self.First[x]:
if f == '<empty>':
- x_produces_empty = 1
+ x_produces_empty = True
else:
- if f not in result: result.append(f)
+ if f not in result:
+ result.append(f)
if x_produces_empty:
# We have to consider the next x in beta,
@@ -1695,17 +1132,17 @@ class Grammar(object):
self.First[n] = []
# Then propagate symbols until no change:
- while 1:
- some_change = 0
+ while True:
+ some_change = False
for n in self.Nonterminals:
for p in self.Prodnames[n]:
for f in self._first(p.prod):
if f not in self.First[n]:
- self.First[n].append( f )
- some_change = 1
+ self.First[n].append(f)
+ some_change = True
if not some_change:
break
-
+
return self.First
# ---------------------------------------------------------------------
@@ -1715,7 +1152,7 @@ class Grammar(object):
# follow set is the set of all symbols that might follow a given
# non-terminal. See the Dragon book, 2nd Ed. p. 189.
# ---------------------------------------------------------------------
- def compute_follow(self,start=None):
+ def compute_follow(self, start=None):
# If already computed, return the result
if self.Follow:
return self.Follow
@@ -1726,36 +1163,36 @@ class Grammar(object):
# Add '$end' to the follow list of the start symbol
for k in self.Nonterminals:
- self.Follow[k] = [ ]
+ self.Follow[k] = []
if not start:
start = self.Productions[1].name
- self.Follow[start] = [ '$end' ]
+ self.Follow[start] = ['$end']
- while 1:
- didadd = 0
+ while True:
+ didadd = False
for p in self.Productions[1:]:
# Here is the production set
- for i in range(len(p.prod)):
- B = p.prod[i]
+ for i, B in enumerate(p.prod):
if B in self.Nonterminals:
# Okay. We got a non-terminal in a production
fst = self._first(p.prod[i+1:])
- hasempty = 0
+ hasempty = False
for f in fst:
if f != '<empty>' and f not in self.Follow[B]:
self.Follow[B].append(f)
- didadd = 1
+ didadd = True
if f == '<empty>':
- hasempty = 1
+ hasempty = True
if hasempty or i == (len(p.prod)-1):
# Add elements of follow(a) to follow(b)
for f in self.Follow[p.name]:
if f not in self.Follow[B]:
self.Follow[B].append(f)
- didadd = 1
- if not didadd: break
+ didadd = True
+ if not didadd:
+ break
return self.Follow
@@ -1779,15 +1216,15 @@ class Grammar(object):
lastlri = p
i = 0
lr_items = []
- while 1:
+ while True:
if i > len(p):
lri = None
else:
- lri = LRItem(p,i)
+ lri = LRItem(p, i)
# Precompute the list of productions immediately following
try:
lri.lr_after = self.Prodnames[lri.prod[i+1]]
- except (IndexError,KeyError):
+ except (IndexError, KeyError):
lri.lr_after = []
try:
lri.lr_before = lri.prod[i-1]
@@ -1795,86 +1232,17 @@ class Grammar(object):
lri.lr_before = None
lastlri.lr_next = lri
- if not lri: break
+ if not lri:
+ break
lr_items.append(lri)
lastlri = lri
i += 1
p.lr_items = lr_items
# -----------------------------------------------------------------------------
-# == Class LRTable ==
-#
-# This basic class represents a basic table of LR parsing information.
-# Methods for generating the tables are not defined here. They are defined
-# in the derived class LRGeneratedTable.
-# -----------------------------------------------------------------------------
-
-class VersionError(YaccError): pass
-
-class LRTable(object):
- def __init__(self):
- self.lr_action = None
- self.lr_goto = None
- self.lr_productions = None
- self.lr_method = None
-
- def read_table(self,module):
- if isinstance(module,types.ModuleType):
- parsetab = module
- else:
- if sys.version_info[0] < 3:
- exec("import %s as parsetab" % module)
- else:
- env = { }
- exec("import %s as parsetab" % module, env, env)
- parsetab = env['parsetab']
-
- if parsetab._tabversion != __tabversion__:
- raise VersionError("yacc table file version is out of date")
-
- self.lr_action = parsetab._lr_action
- self.lr_goto = parsetab._lr_goto
-
- self.lr_productions = []
- for p in parsetab._lr_productions:
- self.lr_productions.append(MiniProduction(*p))
-
- self.lr_method = parsetab._lr_method
- return parsetab._lr_signature
-
- def read_pickle(self,filename):
- try:
- import cPickle as pickle
- except ImportError:
- import pickle
-
- in_f = open(filename,"rb")
-
- tabversion = pickle.load(in_f)
- if tabversion != __tabversion__:
- raise VersionError("yacc table file version is out of date")
- self.lr_method = pickle.load(in_f)
- signature = pickle.load(in_f)
- self.lr_action = pickle.load(in_f)
- self.lr_goto = pickle.load(in_f)
- productions = pickle.load(in_f)
-
- self.lr_productions = []
- for p in productions:
- self.lr_productions.append(MiniProduction(*p))
-
- in_f.close()
- return signature
-
- # Bind all production function names to callable objects in pdict
- def bind_callables(self,pdict):
- for p in self.lr_productions:
- p.bind(pdict)
-
-# -----------------------------------------------------------------------------
# === LR Generator ===
#
-# The following classes and functions are used to generate LR parsing tables on
+# The following classes and functions are used to generate LR parsing tables on
# a grammar.
# -----------------------------------------------------------------------------
@@ -1895,17 +1263,18 @@ class LRTable(object):
# FP - Set-valued function
# ------------------------------------------------------------------------------
-def digraph(X,R,FP):
- N = { }
+def digraph(X, R, FP):
+ N = {}
for x in X:
- N[x] = 0
+ N[x] = 0
stack = []
- F = { }
+ F = {}
for x in X:
- if N[x] == 0: traverse(x,N,stack,F,X,R,FP)
+ if N[x] == 0:
+ traverse(x, N, stack, F, X, R, FP)
return F
-def traverse(x,N,stack,F,X,R,FP):
+def traverse(x, N, stack, F, X, R, FP):
stack.append(x)
d = len(stack)
N[x] = d
@@ -1914,35 +1283,34 @@ def traverse(x,N,stack,F,X,R,FP):
rel = R(x) # Get y's related to x
for y in rel:
if N[y] == 0:
- traverse(y,N,stack,F,X,R,FP)
- N[x] = min(N[x],N[y])
- for a in F.get(y,[]):
- if a not in F[x]: F[x].append(a)
+ traverse(y, N, stack, F, X, R, FP)
+ N[x] = min(N[x], N[y])
+ for a in F.get(y, []):
+ if a not in F[x]:
+ F[x].append(a)
if N[x] == d:
- N[stack[-1]] = MAXINT
- F[stack[-1]] = F[x]
- element = stack.pop()
- while element != x:
- N[stack[-1]] = MAXINT
- F[stack[-1]] = F[x]
- element = stack.pop()
+ N[stack[-1]] = MAXINT
+ F[stack[-1]] = F[x]
+ element = stack.pop()
+ while element != x:
+ N[stack[-1]] = MAXINT
+ F[stack[-1]] = F[x]
+ element = stack.pop()
+
+class LALRError(YaccError):
+ pass
-class LALRError(YaccError): pass
# -----------------------------------------------------------------------------
-# == LRGeneratedTable ==
+# == LRTable ==
#
# This class implements the LR table generation algorithm. There are no
-# public methods except for write()
+# public methods.
# -----------------------------------------------------------------------------
-class LRGeneratedTable(LRTable):
- def __init__(self,grammar,method='LALR',log=None):
- if method not in ['SLR','LALR']:
- raise LALRError("Unsupported method %s" % method)
-
+class LRTable:
+ def __init__(self, grammar, log=None):
self.grammar = grammar
- self.lr_method = method
# Set up the logger
if not log:
@@ -1958,7 +1326,7 @@ class LRGeneratedTable(LRTable):
self._add_count = 0 # Internal counter used to detect cycles
- # Diagonistic information filled in by the table generator
+ # Diagnostic information filled in by the table generator
self.sr_conflict = 0
self.rr_conflict = 0
self.conflicts = [] # List of conflicts
@@ -1972,23 +1340,29 @@ class LRGeneratedTable(LRTable):
self.grammar.compute_follow()
self.lr_parse_table()
+ # Bind all production function names to callable objects in pdict
+ def bind_callables(self, pdict):
+ for p in self.lr_productions:
+ p.bind(pdict)
+
# Compute the LR(0) closure operation on I, where I is a set of LR(0) items.
- def lr0_closure(self,I):
+ def lr0_closure(self, I):
self._add_count += 1
# Add everything in I to J
J = I[:]
- didadd = 1
+ didadd = True
while didadd:
- didadd = 0
+ didadd = False
for j in J:
for x in j.lr_after:
- if getattr(x,"lr0_added",0) == self._add_count: continue
+ if getattr(x, 'lr0_added', 0) == self._add_count:
+ continue
# Add B --> .G to J
J.append(x.lr_next)
x.lr0_added = self._add_count
- didadd = 1
+ didadd = True
return J
@@ -1999,43 +1373,43 @@ class LRGeneratedTable(LRTable):
# objects). With uniqueness, we can later do fast set comparisons using
# id(obj) instead of element-wise comparison.
- def lr0_goto(self,I,x):
+ def lr0_goto(self, I, x):
# First we look for a previously cached entry
- g = self.lr_goto_cache.get((id(I),x),None)
- if g: return g
+ g = self.lr_goto_cache.get((id(I), x))
+ if g:
+ return g
# Now we generate the goto set in a way that guarantees uniqueness
# of the result
- s = self.lr_goto_cache.get(x,None)
+ s = self.lr_goto_cache.get(x)
if not s:
- s = { }
+ s = {}
self.lr_goto_cache[x] = s
- gs = [ ]
+ gs = []
for p in I:
n = p.lr_next
if n and n.lr_before == x:
- s1 = s.get(id(n),None)
+ s1 = s.get(id(n))
if not s1:
- s1 = { }
+ s1 = {}
s[id(n)] = s1
gs.append(n)
s = s1
- g = s.get('$end',None)
+ g = s.get('$end')
if not g:
if gs:
g = self.lr0_closure(gs)
s['$end'] = g
else:
s['$end'] = gs
- self.lr_goto_cache[(id(I),x)] = g
+ self.lr_goto_cache[(id(I), x)] = g
return g
# Compute the LR(0) sets of item function
def lr0_items(self):
-
- C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ]
+ C = [self.lr0_closure([self.grammar.Productions[0].lr_next])]
i = 0
for I in C:
self.lr0_cidhash[id(I)] = i
@@ -2048,15 +1422,15 @@ class LRGeneratedTable(LRTable):
i += 1
# Collect all of the symbols that could possibly be in the goto(I,X) sets
- asyms = { }
+ asyms = {}
for ii in I:
for s in ii.usyms:
asyms[s] = None
for x in asyms:
- g = self.lr0_goto(I,x)
- if not g: continue
- if id(g) in self.lr0_cidhash: continue
+ g = self.lr0_goto(I, x)
+ if not g or id(g) in self.lr0_cidhash:
+ continue
self.lr0_cidhash[id(g)] = len(C)
C.append(g)
@@ -2091,19 +1465,21 @@ class LRGeneratedTable(LRTable):
# -----------------------------------------------------------------------------
def compute_nullable_nonterminals(self):
- nullable = {}
+ nullable = set()
num_nullable = 0
- while 1:
- for p in self.grammar.Productions[1:]:
- if p.len == 0:
- nullable[p.name] = 1
+ while True:
+ for p in self.grammar.Productions[1:]:
+ if p.len == 0:
+ nullable.add(p.name)
continue
- for t in p.prod:
- if not t in nullable: break
- else:
- nullable[p.name] = 1
- if len(nullable) == num_nullable: break
- num_nullable = len(nullable)
+ for t in p.prod:
+ if t not in nullable:
+ break
+ else:
+ nullable.add(p.name)
+ if len(nullable) == num_nullable:
+ break
+ num_nullable = len(nullable)
return nullable
# -----------------------------------------------------------------------------
@@ -2117,16 +1493,16 @@ class LRGeneratedTable(LRTable):
# The input C is the set of LR(0) items.
# -----------------------------------------------------------------------------
- def find_nonterminal_transitions(self,C):
- trans = []
- for state in range(len(C)):
- for p in C[state]:
- if p.lr_index < p.len - 1:
- t = (state,p.prod[p.lr_index+1])
- if t[1] in self.grammar.Nonterminals:
- if t not in trans: trans.append(t)
- state = state + 1
- return trans
+ def find_nonterminal_transitions(self, C):
+ trans = []
+ for stateno, state in enumerate(C):
+ for p in state:
+ if p.lr_index < p.len - 1:
+ t = (stateno, p.prod[p.lr_index+1])
+ if t[1] in self.grammar.Nonterminals:
+ if t not in trans:
+ trans.append(t)
+ return trans
# -----------------------------------------------------------------------------
# dr_relation()
@@ -2137,21 +1513,21 @@ class LRGeneratedTable(LRTable):
# Returns a list of terminals.
# -----------------------------------------------------------------------------
- def dr_relation(self,C,trans,nullable):
- dr_set = { }
- state,N = trans
+ def dr_relation(self, C, trans, nullable):
+ state, N = trans
terms = []
- g = self.lr0_goto(C[state],N)
+ g = self.lr0_goto(C[state], N)
for p in g:
- if p.lr_index < p.len - 1:
- a = p.prod[p.lr_index+1]
- if a in self.grammar.Terminals:
- if a not in terms: terms.append(a)
+ if p.lr_index < p.len - 1:
+ a = p.prod[p.lr_index+1]
+ if a in self.grammar.Terminals:
+ if a not in terms:
+ terms.append(a)
# This extra bit is to handle the start state
if state == 0 and N == self.grammar.Productions[0].prod[0]:
- terms.append('$end')
+ terms.append('$end')
return terms
@@ -2161,18 +1537,18 @@ class LRGeneratedTable(LRTable):
# Computes the READS() relation (p,A) READS (t,C).
# -----------------------------------------------------------------------------
- def reads_relation(self,C, trans, empty):
+ def reads_relation(self, C, trans, empty):
# Look for empty transitions
rel = []
state, N = trans
- g = self.lr0_goto(C[state],N)
- j = self.lr0_cidhash.get(id(g),-1)
+ g = self.lr0_goto(C[state], N)
+ j = self.lr0_cidhash.get(id(g), -1)
for p in g:
if p.lr_index < p.len - 1:
- a = p.prod[p.lr_index + 1]
- if a in empty:
- rel.append((j,a))
+ a = p.prod[p.lr_index + 1]
+ if a in empty:
+ rel.append((j, a))
return rel
@@ -2204,8 +1580,7 @@ class LRGeneratedTable(LRTable):
#
# -----------------------------------------------------------------------------
- def compute_lookback_includes(self,C,trans,nullable):
-
+ def compute_lookback_includes(self, C, trans, nullable):
lookdict = {} # Dictionary of lookback relations
includedict = {} # Dictionary of include relations
@@ -2215,11 +1590,12 @@ class LRGeneratedTable(LRTable):
dtrans[t] = 1
# Loop over all transitions and compute lookbacks and includes
- for state,N in trans:
+ for state, N in trans:
lookb = []
includes = []
for p in C[state]:
- if p.name != N: continue
+ if p.name != N:
+ continue
# Okay, we have a name match. We now follow the production all the way
# through the state machine until we get the . on the right hand side
@@ -2227,44 +1603,50 @@ class LRGeneratedTable(LRTable):
lr_index = p.lr_index
j = state
while lr_index < p.len - 1:
- lr_index = lr_index + 1
- t = p.prod[lr_index]
-
- # Check to see if this symbol and state are a non-terminal transition
- if (j,t) in dtrans:
- # Yes. Okay, there is some chance that this is an includes relation
- # the only way to know for certain is whether the rest of the
- # production derives empty
-
- li = lr_index + 1
- while li < p.len:
- if p.prod[li] in self.grammar.Terminals: break # No forget it
- if not p.prod[li] in nullable: break
- li = li + 1
- else:
- # Appears to be a relation between (j,t) and (state,N)
- includes.append((j,t))
-
- g = self.lr0_goto(C[j],t) # Go to next set
- j = self.lr0_cidhash.get(id(g),-1) # Go to next state
+ lr_index = lr_index + 1
+ t = p.prod[lr_index]
+
+ # Check to see if this symbol and state are a non-terminal transition
+ if (j, t) in dtrans:
+ # Yes. Okay, there is some chance that this is an includes relation
+ # the only way to know for certain is whether the rest of the
+ # production derives empty
+
+ li = lr_index + 1
+ while li < p.len:
+ if p.prod[li] in self.grammar.Terminals:
+ break # No forget it
+ if p.prod[li] not in nullable:
+ break
+ li = li + 1
+ else:
+ # Appears to be a relation between (j,t) and (state,N)
+ includes.append((j, t))
+
+ g = self.lr0_goto(C[j], t) # Go to next set
+ j = self.lr0_cidhash.get(id(g), -1) # Go to next state
# When we get here, j is the final state, now we have to locate the production
for r in C[j]:
- if r.name != p.name: continue
- if r.len != p.len: continue
- i = 0
- # This look is comparing a production ". A B C" with "A B C ."
- while i < r.lr_index:
- if r.prod[i] != p.prod[i+1]: break
- i = i + 1
- else:
- lookb.append((j,r))
+ if r.name != p.name:
+ continue
+ if r.len != p.len:
+ continue
+ i = 0
+ # This look is comparing a production ". A B C" with "A B C ."
+ while i < r.lr_index:
+ if r.prod[i] != p.prod[i+1]:
+ break
+ i = i + 1
+ else:
+ lookb.append((j, r))
for i in includes:
- if not i in includedict: includedict[i] = []
- includedict[i].append((state,N))
- lookdict[(state,N)] = lookb
+ if i not in includedict:
+ includedict[i] = []
+ includedict[i].append((state, N))
+ lookdict[(state, N)] = lookb
- return lookdict,includedict
+ return lookdict, includedict
# -----------------------------------------------------------------------------
# compute_read_sets()
@@ -2278,10 +1660,10 @@ class LRGeneratedTable(LRTable):
# Returns a set containing the read sets
# -----------------------------------------------------------------------------
- def compute_read_sets(self,C, ntrans, nullable):
- FP = lambda x: self.dr_relation(C,x,nullable)
- R = lambda x: self.reads_relation(C,x,nullable)
- F = digraph(ntrans,R,FP)
+ def compute_read_sets(self, C, ntrans, nullable):
+ FP = lambda x: self.dr_relation(C, x, nullable)
+ R = lambda x: self.reads_relation(C, x, nullable)
+ F = digraph(ntrans, R, FP)
return F
# -----------------------------------------------------------------------------
@@ -2300,11 +1682,11 @@ class LRGeneratedTable(LRTable):
# Returns a set containing the follow sets
# -----------------------------------------------------------------------------
- def compute_follow_sets(self,ntrans,readsets,inclsets):
- FP = lambda x: readsets[x]
- R = lambda x: inclsets.get(x,[])
- F = digraph(ntrans,R,FP)
- return F
+ def compute_follow_sets(self, ntrans, readsets, inclsets):
+ FP = lambda x: readsets[x]
+ R = lambda x: inclsets.get(x, [])
+ F = digraph(ntrans, R, FP)
+ return F
# -----------------------------------------------------------------------------
# add_lookaheads()
@@ -2318,15 +1700,16 @@ class LRGeneratedTable(LRTable):
# in the lookbacks set
# -----------------------------------------------------------------------------
- def add_lookaheads(self,lookbacks,followset):
- for trans,lb in lookbacks.items():
+ def add_lookaheads(self, lookbacks, followset):
+ for trans, lb in lookbacks.items():
# Loop over productions in lookback
- for state,p in lb:
- if not state in p.lookaheads:
- p.lookaheads[state] = []
- f = followset.get(trans,[])
- for a in f:
- if a not in p.lookaheads[state]: p.lookaheads[state].append(a)
+ for state, p in lb:
+ if state not in p.lookaheads:
+ p.lookaheads[state] = []
+ f = followset.get(trans, [])
+ for a in f:
+ if a not in p.lookaheads[state]:
+ p.lookaheads[state].append(a)
# -----------------------------------------------------------------------------
# add_lalr_lookaheads()
@@ -2335,7 +1718,7 @@ class LRGeneratedTable(LRTable):
# with LALR parsing
# -----------------------------------------------------------------------------
- def add_lalr_lookaheads(self,C):
+ def add_lalr_lookaheads(self, C):
# Determine all of the nullable nonterminals
nullable = self.compute_nullable_nonterminals()
@@ -2343,16 +1726,16 @@ class LRGeneratedTable(LRTable):
trans = self.find_nonterminal_transitions(C)
# Compute read sets
- readsets = self.compute_read_sets(C,trans,nullable)
+ readsets = self.compute_read_sets(C, trans, nullable)
# Compute lookback/includes relations
- lookd, included = self.compute_lookback_includes(C,trans,nullable)
+ lookd, included = self.compute_lookback_includes(C, trans, nullable)
# Compute LALR FOLLOW sets
- followsets = self.compute_follow_sets(trans,readsets,included)
+ followsets = self.compute_follow_sets(trans, readsets, included)
# Add all of the lookaheads
- self.add_lookaheads(lookd,followsets)
+ self.add_lookaheads(lookd, followsets)
# -----------------------------------------------------------------------------
# lr_parse_table()
@@ -2366,324 +1749,179 @@ class LRGeneratedTable(LRTable):
action = self.lr_action # Action array
log = self.log # Logger for output
- actionp = { } # Action production array (temporary)
-
- log.info("Parsing method: %s", self.lr_method)
+ actionp = {} # Action production array (temporary)
# Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items
# This determines the number of states
C = self.lr0_items()
-
- if self.lr_method == 'LALR':
- self.add_lalr_lookaheads(C)
+ self.add_lalr_lookaheads(C)
# Build the parser table, state by state
st = 0
for I in C:
# Loop over each production in I
- actlist = [ ] # List of actions
- st_action = { }
- st_actionp = { }
- st_goto = { }
- log.info("")
- log.info("state %d", st)
- log.info("")
+ actlist = [] # List of actions
+ st_action = {}
+ st_actionp = {}
+ st_goto = {}
+ log.info('')
+ log.info('state %d', st)
+ log.info('')
for p in I:
- log.info(" (%d) %s", p.number, str(p))
- log.info("")
+ log.info(' (%d) %s', p.number, p)
+ log.info('')
for p in I:
- if p.len == p.lr_index + 1:
- if p.name == "S'":
- # Start symbol. Accept!
- st_action["$end"] = 0
- st_actionp["$end"] = p
- else:
- # We are at the end of a production. Reduce!
- if self.lr_method == 'LALR':
- laheads = p.lookaheads[st]
- else:
- laheads = self.grammar.Follow[p.name]
- for a in laheads:
- actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p)))
- r = st_action.get(a,None)
- if r is not None:
- # Whoa. Have a shift/reduce or reduce/reduce conflict
- if r > 0:
- # Need to decide on shift or reduce here
- # By default we favor shifting. Need to add
- # some precedence rules here.
- sprec,slevel = Productions[st_actionp[a].number].prec
- rprec,rlevel = Precedence.get(a,('right',0))
- if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')):
- # We really need to reduce here.
- st_action[a] = -p.number
- st_actionp[a] = p
- if not slevel and not rlevel:
- log.info(" ! shift/reduce conflict for %s resolved as reduce",a)
- self.sr_conflicts.append((st,a,'reduce'))
- Productions[p.number].reduced += 1
- elif (slevel == rlevel) and (rprec == 'nonassoc'):
- st_action[a] = None
- else:
- # Hmmm. Guess we'll keep the shift
- if not rlevel:
- log.info(" ! shift/reduce conflict for %s resolved as shift",a)
- self.sr_conflicts.append((st,a,'shift'))
- elif r < 0:
- # Reduce/reduce conflict. In this case, we favor the rule
- # that was defined first in the grammar file
- oldp = Productions[-r]
- pp = Productions[p.number]
- if oldp.line > pp.line:
- st_action[a] = -p.number
- st_actionp[a] = p
- chosenp,rejectp = pp,oldp
- Productions[p.number].reduced += 1
- Productions[oldp.number].reduced -= 1
- else:
- chosenp,rejectp = oldp,pp
- self.rr_conflicts.append((st,chosenp,rejectp))
- log.info(" ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a])
+ if p.len == p.lr_index + 1:
+ if p.name == "S'":
+ # Start symbol. Accept!
+ st_action['$end'] = 0
+ st_actionp['$end'] = p
+ else:
+ # We are at the end of a production. Reduce!
+ laheads = p.lookaheads[st]
+ for a in laheads:
+ actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p)))
+ r = st_action.get(a)
+ if r is not None:
+ # Whoa. Have a shift/reduce or reduce/reduce conflict
+ if r > 0:
+ # Need to decide on shift or reduce here
+ # By default we favor shifting. Need to add
+ # some precedence rules here.
+
+ # Shift precedence comes from the token
+ sprec, slevel = Precedence.get(a, ('right', 0))
+
+ # Reduce precedence comes from rule being reduced (p)
+ rprec, rlevel = Productions[p.number].prec
+
+ if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')):
+ # We really need to reduce here.
+ st_action[a] = -p.number
+ st_actionp[a] = p
+ if not slevel and not rlevel:
+ log.info(' ! shift/reduce conflict for %s resolved as reduce', a)
+ self.sr_conflicts.append((st, a, 'reduce'))
+ Productions[p.number].reduced += 1
+ elif (slevel == rlevel) and (rprec == 'nonassoc'):
+ st_action[a] = None
+ else:
+ # Hmmm. Guess we'll keep the shift
+ if not rlevel:
+ log.info(' ! shift/reduce conflict for %s resolved as shift', a)
+ self.sr_conflicts.append((st, a, 'shift'))
+ elif r < 0:
+ # Reduce/reduce conflict. In this case, we favor the rule
+ # that was defined first in the grammar file
+ oldp = Productions[-r]
+ pp = Productions[p.number]
+ if oldp.line > pp.line:
+ st_action[a] = -p.number
+ st_actionp[a] = p
+ chosenp, rejectp = pp, oldp
+ Productions[p.number].reduced += 1
+ Productions[oldp.number].reduced -= 1
else:
- raise LALRError("Unknown conflict in state %d" % st)
+ chosenp, rejectp = oldp, pp
+ self.rr_conflicts.append((st, chosenp, rejectp))
+ log.info(' ! reduce/reduce conflict for %s resolved using rule %d (%s)',
+ a, st_actionp[a].number, st_actionp[a])
else:
- st_action[a] = -p.number
- st_actionp[a] = p
- Productions[p.number].reduced += 1
- else:
- i = p.lr_index
- a = p.prod[i+1] # Get symbol right after the "."
- if a in self.grammar.Terminals:
- g = self.lr0_goto(I,a)
- j = self.lr0_cidhash.get(id(g),-1)
- if j >= 0:
- # We are in a shift state
- actlist.append((a,p,"shift and go to state %d" % j))
- r = st_action.get(a,None)
- if r is not None:
- # Whoa have a shift/reduce or shift/shift conflict
- if r > 0:
- if r != j:
- raise LALRError("Shift/shift conflict in state %d" % st)
- elif r < 0:
- # Do a precedence check.
- # - if precedence of reduce rule is higher, we reduce.
- # - if precedence of reduce is same and left assoc, we reduce.
- # - otherwise we shift
- rprec,rlevel = Productions[st_actionp[a].number].prec
- sprec,slevel = Precedence.get(a,('right',0))
- if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')):
- # We decide to shift here... highest precedence to shift
- Productions[st_actionp[a].number].reduced -= 1
- st_action[a] = j
- st_actionp[a] = p
- if not rlevel:
- log.info(" ! shift/reduce conflict for %s resolved as shift",a)
- self.sr_conflicts.append((st,a,'shift'))
- elif (slevel == rlevel) and (rprec == 'nonassoc'):
- st_action[a] = None
- else:
- # Hmmm. Guess we'll keep the reduce
- if not slevel and not rlevel:
- log.info(" ! shift/reduce conflict for %s resolved as reduce",a)
- self.sr_conflicts.append((st,a,'reduce'))
-
+ raise LALRError('Unknown conflict in state %d' % st)
+ else:
+ st_action[a] = -p.number
+ st_actionp[a] = p
+ Productions[p.number].reduced += 1
+ else:
+ i = p.lr_index
+ a = p.prod[i+1] # Get symbol right after the "."
+ if a in self.grammar.Terminals:
+ g = self.lr0_goto(I, a)
+ j = self.lr0_cidhash.get(id(g), -1)
+ if j >= 0:
+ # We are in a shift state
+ actlist.append((a, p, 'shift and go to state %d' % j))
+ r = st_action.get(a)
+ if r is not None:
+ # Whoa have a shift/reduce or shift/shift conflict
+ if r > 0:
+ if r != j:
+ raise LALRError('Shift/shift conflict in state %d' % st)
+ elif r < 0:
+ # Do a precedence check.
+ # - if precedence of reduce rule is higher, we reduce.
+ # - if precedence of reduce is same and left assoc, we reduce.
+ # - otherwise we shift
+
+ # Shift precedence comes from the token
+ sprec, slevel = Precedence.get(a, ('right', 0))
+
+ # Reduce precedence comes from the rule that could have been reduced
+ rprec, rlevel = Productions[st_actionp[a].number].prec
+
+ if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')):
+ # We decide to shift here... highest precedence to shift
+ Productions[st_actionp[a].number].reduced -= 1
+ st_action[a] = j
+ st_actionp[a] = p
+ if not rlevel:
+ log.info(' ! shift/reduce conflict for %s resolved as shift', a)
+ self.sr_conflicts.append((st, a, 'shift'))
+ elif (slevel == rlevel) and (rprec == 'nonassoc'):
+ st_action[a] = None
else:
- raise LALRError("Unknown conflict in state %d" % st)
+ # Hmmm. Guess we'll keep the reduce
+ if not slevel and not rlevel:
+ log.info(' ! shift/reduce conflict for %s resolved as reduce', a)
+ self.sr_conflicts.append((st, a, 'reduce'))
+
else:
- st_action[a] = j
- st_actionp[a] = p
+ raise LALRError('Unknown conflict in state %d' % st)
+ else:
+ st_action[a] = j
+ st_actionp[a] = p
# Print the actions associated with each terminal
- _actprint = { }
- for a,p,m in actlist:
+ _actprint = {}
+ for a, p, m in actlist:
if a in st_action:
if p is st_actionp[a]:
- log.info(" %-15s %s",a,m)
- _actprint[(a,m)] = 1
- log.info("")
+ log.info(' %-15s %s', a, m)
+ _actprint[(a, m)] = 1
+ log.info('')
# Print the actions that were not used. (debugging)
not_used = 0
- for a,p,m in actlist:
+ for a, p, m in actlist:
if a in st_action:
if p is not st_actionp[a]:
- if not (a,m) in _actprint:
- log.debug(" ! %-15s [ %s ]",a,m)
+ if not (a, m) in _actprint:
+ log.debug(' ! %-15s [ %s ]', a, m)
not_used = 1
- _actprint[(a,m)] = 1
+ _actprint[(a, m)] = 1
if not_used:
- log.debug("")
+ log.debug('')
# Construct the goto table for this state
- nkeys = { }
+ nkeys = {}
for ii in I:
for s in ii.usyms:
if s in self.grammar.Nonterminals:
nkeys[s] = None
for n in nkeys:
- g = self.lr0_goto(I,n)
- j = self.lr0_cidhash.get(id(g),-1)
+ g = self.lr0_goto(I, n)
+ j = self.lr0_cidhash.get(id(g), -1)
if j >= 0:
st_goto[n] = j
- log.info(" %-30s shift and go to state %d",n,j)
+ log.info(' %-30s shift and go to state %d', n, j)
action[st] = st_action
actionp[st] = st_actionp
goto[st] = st_goto
st += 1
-
- # -----------------------------------------------------------------------------
- # write()
- #
- # This function writes the LR parsing tables to a file
- # -----------------------------------------------------------------------------
-
- def write_table(self,modulename,outputdir='',signature=""):
- basemodulename = modulename.split(".")[-1]
- filename = os.path.join(outputdir,basemodulename) + ".py"
- try:
- f = open(filename,"w")
-
- f.write("""
-# %s
-# This file is automatically generated. Do not edit.
-_tabversion = %r
-
-_lr_method = %r
-
-_lr_signature = %r
- """ % (filename, __tabversion__, self.lr_method, signature))
-
- # Change smaller to 0 to go back to original tables
- smaller = 1
-
- # Factor out names to try and make smaller
- if smaller:
- items = { }
-
- for s,nd in self.lr_action.items():
- for name,v in nd.items():
- i = items.get(name)
- if not i:
- i = ([],[])
- items[name] = i
- i[0].append(s)
- i[1].append(v)
-
- f.write("\n_lr_action_items = {")
- for k,v in items.items():
- f.write("%r:([" % k)
- for i in v[0]:
- f.write("%r," % i)
- f.write("],[")
- for i in v[1]:
- f.write("%r," % i)
-
- f.write("]),")
- f.write("}\n")
-
- f.write("""
-_lr_action = { }
-for _k, _v in _lr_action_items.items():
- for _x,_y in zip(_v[0],_v[1]):
- if not _x in _lr_action: _lr_action[_x] = { }
- _lr_action[_x][_k] = _y
-del _lr_action_items
-""")
-
- else:
- f.write("\n_lr_action = { ");
- for k,v in self.lr_action.items():
- f.write("(%r,%r):%r," % (k[0],k[1],v))
- f.write("}\n");
-
- if smaller:
- # Factor out names to try and make smaller
- items = { }
-
- for s,nd in self.lr_goto.items():
- for name,v in nd.items():
- i = items.get(name)
- if not i:
- i = ([],[])
- items[name] = i
- i[0].append(s)
- i[1].append(v)
-
- f.write("\n_lr_goto_items = {")
- for k,v in items.items():
- f.write("%r:([" % k)
- for i in v[0]:
- f.write("%r," % i)
- f.write("],[")
- for i in v[1]:
- f.write("%r," % i)
-
- f.write("]),")
- f.write("}\n")
-
- f.write("""
-_lr_goto = { }
-for _k, _v in _lr_goto_items.items():
- for _x,_y in zip(_v[0],_v[1]):
- if not _x in _lr_goto: _lr_goto[_x] = { }
- _lr_goto[_x][_k] = _y
-del _lr_goto_items
-""")
- else:
- f.write("\n_lr_goto = { ");
- for k,v in self.lr_goto.items():
- f.write("(%r,%r):%r," % (k[0],k[1],v))
- f.write("}\n");
-
- # Write production table
- f.write("_lr_productions = [\n")
- for p in self.lr_productions:
- if p.func:
- f.write(" (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line))
- else:
- f.write(" (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len))
- f.write("]\n")
- f.close()
-
- except IOError:
- e = sys.exc_info()[1]
- sys.stderr.write("Unable to create '%s'\n" % filename)
- sys.stderr.write(str(e)+"\n")
- return
-
-
- # -----------------------------------------------------------------------------
- # pickle_table()
- #
- # This function pickles the LR parsing tables to a supplied file object
- # -----------------------------------------------------------------------------
-
- def pickle_table(self,filename,signature=""):
- try:
- import cPickle as pickle
- except ImportError:
- import pickle
- outf = open(filename,"wb")
- pickle.dump(__tabversion__,outf,pickle_protocol)
- pickle.dump(self.lr_method,outf,pickle_protocol)
- pickle.dump(signature,outf,pickle_protocol)
- pickle.dump(self.lr_action,outf,pickle_protocol)
- pickle.dump(self.lr_goto,outf,pickle_protocol)
-
- outp = []
- for p in self.lr_productions:
- if p.func:
- outp.append((p.str,p.name, p.len, p.func,p.file,p.line))
- else:
- outp.append((str(p),p.name,p.len,None,None,None))
- pickle.dump(outp,outf,pickle_protocol)
- outf.close()
-
# -----------------------------------------------------------------------------
# === INTROSPECTION ===
#
@@ -2700,26 +1938,18 @@ del _lr_goto_items
# -----------------------------------------------------------------------------
def get_caller_module_dict(levels):
- try:
- raise RuntimeError
- except RuntimeError:
- e,b,t = sys.exc_info()
- f = t.tb_frame
- while levels > 0:
- f = f.f_back
- levels -= 1
- ldict = f.f_globals.copy()
- if f.f_globals != f.f_locals:
- ldict.update(f.f_locals)
-
- return ldict
+ f = sys._getframe(levels)
+ ldict = f.f_globals.copy()
+ if f.f_globals != f.f_locals:
+ ldict.update(f.f_locals)
+ return ldict
# -----------------------------------------------------------------------------
# parse_grammar()
#
# This takes a raw grammar rule string and parses it into production data
# -----------------------------------------------------------------------------
-def parse_grammar(doc,file,line):
+def parse_grammar(doc, file, line):
grammar = []
# Split the doc string into lines
pstrings = doc.splitlines()
@@ -2728,12 +1958,13 @@ def parse_grammar(doc,file,line):
for ps in pstrings:
dline += 1
p = ps.split()
- if not p: continue
+ if not p:
+ continue
try:
if p[0] == '|':
# This is a continuation of a previous rule
if not lastp:
- raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline))
+ raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline))
prodname = lastp
syms = p[1:]
else:
@@ -2742,13 +1973,13 @@ def parse_grammar(doc,file,line):
syms = p[2:]
assign = p[1]
if assign != ':' and assign != '::=':
- raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline))
+ raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline))
- grammar.append((file,dline,prodname,syms))
+ grammar.append((file, dline, prodname, syms))
except SyntaxError:
raise
except Exception:
- raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip()))
+ raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip()))
return grammar
@@ -2760,14 +1991,14 @@ def parse_grammar(doc,file,line):
# etc.
# -----------------------------------------------------------------------------
class ParserReflect(object):
- def __init__(self,pdict,log=None):
+ def __init__(self, pdict, log=None):
self.pdict = pdict
self.start = None
self.error_func = None
self.tokens = None
- self.files = {}
+ self.modules = set()
self.grammar = []
- self.error = 0
+ self.error = False
if log is None:
self.log = PlyLogger(sys.stderr)
@@ -2781,7 +2012,7 @@ class ParserReflect(object):
self.get_tokens()
self.get_precedence()
self.get_pfunctions()
-
+
# Validate all of the information
def validate_all(self):
self.validate_start()
@@ -2789,32 +2020,28 @@ class ParserReflect(object):
self.validate_tokens()
self.validate_precedence()
self.validate_pfunctions()
- self.validate_files()
+ self.validate_modules()
return self.error
# Compute a signature over the grammar
def signature(self):
+ parts = []
try:
- from hashlib import md5
- except ImportError:
- from md5 import md5
- try:
- sig = md5()
if self.start:
- sig.update(self.start.encode('latin-1'))
+ parts.append(self.start)
if self.prec:
- sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1'))
+ parts.append(''.join([''.join(p) for p in self.prec]))
if self.tokens:
- sig.update(" ".join(self.tokens).encode('latin-1'))
+ parts.append(' '.join(self.tokens))
for f in self.pfuncs:
if f[3]:
- sig.update(f[3].encode('latin-1'))
- except (TypeError,ValueError):
+ parts.append(f[3])
+ except (TypeError, ValueError):
pass
- return sig.digest()
+ return ''.join(parts)
# -----------------------------------------------------------------------------
- # validate_file()
+ # validate_modules()
#
# This method checks to see if there are duplicated p_rulename() functions
# in the parser module file. Without this function, it is really easy for
@@ -2824,32 +2051,29 @@ class ParserReflect(object):
# to try and detect duplicates.
# -----------------------------------------------------------------------------
- def validate_files(self):
+ def validate_modules(self):
# Match def p_funcname(
fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(')
- for filename in self.files.keys():
- base,ext = os.path.splitext(filename)
- if ext != '.py': return 1 # No idea. Assume it's okay.
-
+ for module in self.modules:
try:
- f = open(filename)
- lines = f.readlines()
- f.close()
+ lines, linen = inspect.getsourcelines(module)
except IOError:
continue
- counthash = { }
- for linen,l in enumerate(lines):
+ counthash = {}
+ for linen, line in enumerate(lines):
linen += 1
- m = fre.match(l)
+ m = fre.match(line)
if m:
name = m.group(1)
prev = counthash.get(name)
if not prev:
counthash[name] = linen
else:
- self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev)
+ filename = inspect.getsourcefile(module)
+ self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d',
+ filename, linen, name, prev)
# Get the start symbol
def get_start(self):
@@ -2858,7 +2082,7 @@ class ParserReflect(object):
# Validate the start symbol
def validate_start(self):
if self.start is not None:
- if not isinstance(self.start,str):
+ if not isinstance(self.start, str):
self.log.error("'start' must be a string")
# Look for error handler
@@ -2868,162 +2092,173 @@ class ParserReflect(object):
# Validate the error function
def validate_error_func(self):
if self.error_func:
- if isinstance(self.error_func,types.FunctionType):
+ if isinstance(self.error_func, types.FunctionType):
ismethod = 0
elif isinstance(self.error_func, types.MethodType):
ismethod = 1
else:
self.log.error("'p_error' defined, but is not a function or method")
- self.error = 1
+ self.error = True
return
- eline = func_code(self.error_func).co_firstlineno
- efile = func_code(self.error_func).co_filename
- self.files[efile] = 1
+ eline = self.error_func.__code__.co_firstlineno
+ efile = self.error_func.__code__.co_filename
+ module = inspect.getmodule(self.error_func)
+ self.modules.add(module)
- if (func_code(self.error_func).co_argcount != 1+ismethod):
- self.log.error("%s:%d: p_error() requires 1 argument",efile,eline)
- self.error = 1
+ argcount = self.error_func.__code__.co_argcount - ismethod
+ if argcount != 1:
+ self.log.error('%s:%d: p_error() requires 1 argument', efile, eline)
+ self.error = True
# Get the tokens map
def get_tokens(self):
- tokens = self.pdict.get("tokens",None)
+ tokens = self.pdict.get('tokens')
if not tokens:
- self.log.error("No token list is defined")
- self.error = 1
+ self.log.error('No token list is defined')
+ self.error = True
return
- if not isinstance(tokens,(list, tuple)):
- self.log.error("tokens must be a list or tuple")
- self.error = 1
+ if not isinstance(tokens, (list, tuple)):
+ self.log.error('tokens must be a list or tuple')
+ self.error = True
return
-
+
if not tokens:
- self.log.error("tokens is empty")
- self.error = 1
+ self.log.error('tokens is empty')
+ self.error = True
return
- self.tokens = tokens
+ self.tokens = sorted(tokens)
# Validate the tokens
def validate_tokens(self):
# Validate the tokens.
if 'error' in self.tokens:
self.log.error("Illegal token name 'error'. Is a reserved word")
- self.error = 1
+ self.error = True
return
- terminals = {}
+ terminals = set()
for n in self.tokens:
if n in terminals:
- self.log.warning("Token '%s' multiply defined", n)
- terminals[n] = 1
+ self.log.warning('Token %r multiply defined', n)
+ terminals.add(n)
# Get the precedence map (if any)
def get_precedence(self):
- self.prec = self.pdict.get("precedence",None)
+ self.prec = self.pdict.get('precedence')
# Validate and parse the precedence map
def validate_precedence(self):
preclist = []
if self.prec:
- if not isinstance(self.prec,(list,tuple)):
- self.log.error("precedence must be a list or tuple")
- self.error = 1
+ if not isinstance(self.prec, (list, tuple)):
+ self.log.error('precedence must be a list or tuple')
+ self.error = True
return
- for level,p in enumerate(self.prec):
- if not isinstance(p,(list,tuple)):
- self.log.error("Bad precedence table")
- self.error = 1
+ for level, p in enumerate(self.prec):
+ if not isinstance(p, (list, tuple)):
+ self.log.error('Bad precedence table')
+ self.error = True
return
if len(p) < 2:
- self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p)
- self.error = 1
+ self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p)
+ self.error = True
return
assoc = p[0]
- if not isinstance(assoc,str):
- self.log.error("precedence associativity must be a string")
- self.error = 1
+ if not isinstance(assoc, str):
+ self.log.error('precedence associativity must be a string')
+ self.error = True
return
for term in p[1:]:
- if not isinstance(term,str):
- self.log.error("precedence items must be strings")
- self.error = 1
+ if not isinstance(term, str):
+ self.log.error('precedence items must be strings')
+ self.error = True
return
- preclist.append((term,assoc,level+1))
+ preclist.append((term, assoc, level+1))
self.preclist = preclist
# Get all p_functions from the grammar
def get_pfunctions(self):
p_functions = []
for name, item in self.pdict.items():
- if name[:2] != 'p_': continue
- if name == 'p_error': continue
- if isinstance(item,(types.FunctionType,types.MethodType)):
- line = func_code(item).co_firstlineno
- file = func_code(item).co_filename
- p_functions.append((line,file,name,item.__doc__))
-
- # Sort all of the actions by line number
- p_functions.sort()
+ if not name.startswith('p_') or name == 'p_error':
+ continue
+ if isinstance(item, (types.FunctionType, types.MethodType)):
+ line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno)
+ module = inspect.getmodule(item)
+ p_functions.append((line, module, name, item.__doc__))
+
+ # Sort all of the actions by line number; make sure to stringify
+ # modules to make them sortable, since `line` may not uniquely sort all
+ # p functions
+ p_functions.sort(key=lambda p_function: (
+ p_function[0],
+ str(p_function[1]),
+ p_function[2],
+ p_function[3]))
self.pfuncs = p_functions
-
# Validate all of the p_functions
def validate_pfunctions(self):
grammar = []
# Check for non-empty symbols
if len(self.pfuncs) == 0:
- self.log.error("no rules of the form p_rulename are defined")
- self.error = 1
- return
-
- for line, file, name, doc in self.pfuncs:
+ self.log.error('no rules of the form p_rulename are defined')
+ self.error = True
+ return
+
+ for line, module, name, doc in self.pfuncs:
+ file = inspect.getsourcefile(module)
func = self.pdict[name]
if isinstance(func, types.MethodType):
reqargs = 2
else:
reqargs = 1
- if func_code(func).co_argcount > reqargs:
- self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__)
- self.error = 1
- elif func_code(func).co_argcount < reqargs:
- self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__)
- self.error = 1
+ if func.__code__.co_argcount > reqargs:
+ self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__)
+ self.error = True
+ elif func.__code__.co_argcount < reqargs:
+ self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__)
+ self.error = True
elif not func.__doc__:
- self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__)
+ self.log.warning('%s:%d: No documentation string specified in function %r (ignored)',
+ file, line, func.__name__)
else:
try:
- parsed_g = parse_grammar(doc,file,line)
+ parsed_g = parse_grammar(doc, file, line)
for g in parsed_g:
grammar.append((name, g))
- except SyntaxError:
- e = sys.exc_info()[1]
+ except SyntaxError as e:
self.log.error(str(e))
- self.error = 1
+ self.error = True
# Looks like a valid grammar rule
# Mark the file in which defined.
- self.files[file] = 1
+ self.modules.add(module)
# Secondary validation step that looks for p_ definitions that are not functions
# or functions that look like they might be grammar rules.
- for n,v in self.pdict.items():
- if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue
- if n[0:2] == 't_': continue
- if n[0:2] == 'p_' and n != 'p_error':
- self.log.warning("'%s' not defined as a function", n)
- if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or
- (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)):
- try:
- doc = v.__doc__.split(" ")
- if doc[1] == ':':
- self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix",
- func_code(v).co_filename, func_code(v).co_firstlineno,n)
- except Exception:
- pass
+ for n, v in self.pdict.items():
+ if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)):
+ continue
+ if n.startswith('t_'):
+ continue
+ if n.startswith('p_') and n != 'p_error':
+ self.log.warning('%r not defined as a function', n)
+ if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or
+ (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)):
+ if v.__doc__:
+ try:
+ doc = v.__doc__.split(' ')
+ if doc[1] == ':':
+ self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix',
+ v.__code__.co_filename, v.__code__.co_firstlineno, n)
+ except IndexError:
+ pass
self.grammar = grammar
@@ -3033,76 +2268,61 @@ class ParserReflect(object):
# Build a parser
# -----------------------------------------------------------------------------
-def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None,
- check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='',
- debuglog=None, errorlog = None, picklefile=None):
-
- global parse # Reference to the parsing method of the last built parser
-
- # If pickling is enabled, table files are not created
+def yacc(*, debug=yaccdebug, module=None, start=None,
+ check_recursion=True, optimize=False, debugfile=debug_file,
+ debuglog=None, errorlog=None):
- if picklefile:
- write_tables = 0
+ # Reference to the parsing method of the last built parser
+ global parse
if errorlog is None:
errorlog = PlyLogger(sys.stderr)
# Get the module dictionary used for the parser
if module:
- _items = [(k,getattr(module,k)) for k in dir(module)]
+ _items = [(k, getattr(module, k)) for k in dir(module)]
pdict = dict(_items)
+ # If no __file__ or __package__ attributes are available, try to obtain them
+ # from the __module__ instead
+ if '__file__' not in pdict:
+ pdict['__file__'] = sys.modules[pdict['__module__']].__file__
+ if '__package__' not in pdict and '__module__' in pdict:
+ if hasattr(sys.modules[pdict['__module__']], '__package__'):
+ pdict['__package__'] = sys.modules[pdict['__module__']].__package__
else:
pdict = get_caller_module_dict(2)
+ # Set start symbol if it's specified directly using an argument
+ if start is not None:
+ pdict['start'] = start
+
# Collect parser information from the dictionary
- pinfo = ParserReflect(pdict,log=errorlog)
+ pinfo = ParserReflect(pdict, log=errorlog)
pinfo.get_all()
if pinfo.error:
- raise YaccError("Unable to build parser")
-
- # Check signature against table files (if any)
- signature = pinfo.signature()
-
- # Read the tables
- try:
- lr = LRTable()
- if picklefile:
- read_signature = lr.read_pickle(picklefile)
- else:
- read_signature = lr.read_table(tabmodule)
- if optimize or (read_signature == signature):
- try:
- lr.bind_callables(pinfo.pdict)
- parser = LRParser(lr,pinfo.error_func)
- parse = parser.parse
- return parser
- except Exception:
- e = sys.exc_info()[1]
- errorlog.warning("There was a problem loading the table file: %s", repr(e))
- except VersionError:
- e = sys.exc_info()
- errorlog.warning(str(e))
- except Exception:
- pass
+ raise YaccError('Unable to build parser')
if debuglog is None:
if debug:
- debuglog = PlyLogger(open(debugfile,"w"))
+ try:
+ debuglog = PlyLogger(open(debugfile, 'w'))
+ except IOError as e:
+ errorlog.warning("Couldn't open %r. %s" % (debugfile, e))
+ debuglog = NullLogger()
else:
debuglog = NullLogger()
- debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__)
+ debuglog.info('Created by PLY (http://www.dabeaz.com/ply)')
-
- errors = 0
+ errors = False
# Validate the parser information
if pinfo.validate_all():
- raise YaccError("Unable to build parser")
-
+ raise YaccError('Unable to build parser')
+
if not pinfo.error_func:
- errorlog.warning("no p_error() function is defined")
+ errorlog.warning('no p_error() function is defined')
# Create a grammar object
grammar = Grammar(pinfo.tokens)
@@ -3110,20 +2330,18 @@ def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, star
# Set precedence level for terminals
for term, assoc, level in pinfo.preclist:
try:
- grammar.set_precedence(term,assoc,level)
- except GrammarError:
- e = sys.exc_info()[1]
- errorlog.warning("%s",str(e))
+ grammar.set_precedence(term, assoc, level)
+ except GrammarError as e:
+ errorlog.warning('%s', e)
# Add productions to the grammar
for funcname, gram in pinfo.grammar:
file, line, prodname, syms = gram
try:
- grammar.add_production(prodname,syms,funcname,file,line)
- except GrammarError:
- e = sys.exc_info()[1]
- errorlog.error("%s",str(e))
- errors = 1
+ grammar.add_production(prodname, syms, funcname, file, line)
+ except GrammarError as e:
+ errorlog.error('%s', e)
+ errors = True
# Set the grammar start symbols
try:
@@ -3131,146 +2349,134 @@ def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, star
grammar.set_start(pinfo.start)
else:
grammar.set_start(start)
- except GrammarError:
- e = sys.exc_info()[1]
+ except GrammarError as e:
errorlog.error(str(e))
- errors = 1
+ errors = True
if errors:
- raise YaccError("Unable to build parser")
+ raise YaccError('Unable to build parser')
# Verify the grammar structure
undefined_symbols = grammar.undefined_symbols()
for sym, prod in undefined_symbols:
- errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym)
- errors = 1
+ errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym)
+ errors = True
unused_terminals = grammar.unused_terminals()
if unused_terminals:
- debuglog.info("")
- debuglog.info("Unused terminals:")
- debuglog.info("")
+ debuglog.info('')
+ debuglog.info('Unused terminals:')
+ debuglog.info('')
for term in unused_terminals:
- errorlog.warning("Token '%s' defined, but not used", term)
- debuglog.info(" %s", term)
+ errorlog.warning('Token %r defined, but not used', term)
+ debuglog.info(' %s', term)
# Print out all productions to the debug log
if debug:
- debuglog.info("")
- debuglog.info("Grammar")
- debuglog.info("")
- for n,p in enumerate(grammar.Productions):
- debuglog.info("Rule %-5d %s", n, p)
+ debuglog.info('')
+ debuglog.info('Grammar')
+ debuglog.info('')
+ for n, p in enumerate(grammar.Productions):
+ debuglog.info('Rule %-5d %s', n, p)
# Find unused non-terminals
unused_rules = grammar.unused_rules()
for prod in unused_rules:
- errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name)
+ errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name)
if len(unused_terminals) == 1:
- errorlog.warning("There is 1 unused token")
+ errorlog.warning('There is 1 unused token')
if len(unused_terminals) > 1:
- errorlog.warning("There are %d unused tokens", len(unused_terminals))
+ errorlog.warning('There are %d unused tokens', len(unused_terminals))
if len(unused_rules) == 1:
- errorlog.warning("There is 1 unused rule")
+ errorlog.warning('There is 1 unused rule')
if len(unused_rules) > 1:
- errorlog.warning("There are %d unused rules", len(unused_rules))
+ errorlog.warning('There are %d unused rules', len(unused_rules))
if debug:
- debuglog.info("")
- debuglog.info("Terminals, with rules where they appear")
- debuglog.info("")
+ debuglog.info('')
+ debuglog.info('Terminals, with rules where they appear')
+ debuglog.info('')
terms = list(grammar.Terminals)
terms.sort()
for term in terms:
- debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]]))
-
- debuglog.info("")
- debuglog.info("Nonterminals, with rules where they appear")
- debuglog.info("")
+ debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]]))
+
+ debuglog.info('')
+ debuglog.info('Nonterminals, with rules where they appear')
+ debuglog.info('')
nonterms = list(grammar.Nonterminals)
nonterms.sort()
for nonterm in nonterms:
- debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]]))
- debuglog.info("")
+ debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]]))
+ debuglog.info('')
if check_recursion:
unreachable = grammar.find_unreachable()
for u in unreachable:
- errorlog.warning("Symbol '%s' is unreachable",u)
+ errorlog.warning('Symbol %r is unreachable', u)
infinite = grammar.infinite_cycles()
for inf in infinite:
- errorlog.error("Infinite recursion detected for symbol '%s'", inf)
- errors = 1
-
+ errorlog.error('Infinite recursion detected for symbol %r', inf)
+ errors = True
+
unused_prec = grammar.unused_precedence()
for term, assoc in unused_prec:
- errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term)
- errors = 1
+ errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term)
+ errors = True
if errors:
- raise YaccError("Unable to build parser")
-
- # Run the LRGeneratedTable on the grammar
- if debug:
- errorlog.debug("Generating %s tables", method)
-
- lr = LRGeneratedTable(grammar,method,debuglog)
+ raise YaccError('Unable to build parser')
+
+ # Run the LRTable on the grammar
+ lr = LRTable(grammar, debuglog)
if debug:
num_sr = len(lr.sr_conflicts)
# Report shift/reduce and reduce/reduce conflicts
if num_sr == 1:
- errorlog.warning("1 shift/reduce conflict")
+ errorlog.warning('1 shift/reduce conflict')
elif num_sr > 1:
- errorlog.warning("%d shift/reduce conflicts", num_sr)
+ errorlog.warning('%d shift/reduce conflicts', num_sr)
num_rr = len(lr.rr_conflicts)
if num_rr == 1:
- errorlog.warning("1 reduce/reduce conflict")
+ errorlog.warning('1 reduce/reduce conflict')
elif num_rr > 1:
- errorlog.warning("%d reduce/reduce conflicts", num_rr)
+ errorlog.warning('%d reduce/reduce conflicts', num_rr)
# Write out conflicts to the output file
if debug and (lr.sr_conflicts or lr.rr_conflicts):
- debuglog.warning("")
- debuglog.warning("Conflicts:")
- debuglog.warning("")
+ debuglog.warning('')
+ debuglog.warning('Conflicts:')
+ debuglog.warning('')
for state, tok, resolution in lr.sr_conflicts:
- debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s", tok, state, resolution)
-
- already_reported = {}
+ debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s', tok, state, resolution)
+
+ already_reported = set()
for state, rule, rejected in lr.rr_conflicts:
- if (state,id(rule),id(rejected)) in already_reported:
+ if (state, id(rule), id(rejected)) in already_reported:
continue
- debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule)
- debuglog.warning("rejected rule (%s) in state %d", rejected,state)
- errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule)
- errorlog.warning("rejected rule (%s) in state %d", rejected, state)
- already_reported[state,id(rule),id(rejected)] = 1
-
+ debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
+ debuglog.warning('rejected rule (%s) in state %d', rejected, state)
+ errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
+ errorlog.warning('rejected rule (%s) in state %d', rejected, state)
+ already_reported.add((state, id(rule), id(rejected)))
+
warned_never = []
for state, rule, rejected in lr.rr_conflicts:
if not rejected.reduced and (rejected not in warned_never):
- debuglog.warning("Rule (%s) is never reduced", rejected)
- errorlog.warning("Rule (%s) is never reduced", rejected)
+ debuglog.warning('Rule (%s) is never reduced', rejected)
+ errorlog.warning('Rule (%s) is never reduced', rejected)
warned_never.append(rejected)
- # Write the table file if requested
- if write_tables:
- lr.write_table(tabmodule,outputdir,signature)
-
- # Write a pickled version of the tables
- if picklefile:
- lr.pickle_table(picklefile,signature)
-
# Build the parser
lr.bind_callables(pinfo.pdict)
- parser = LRParser(lr,pinfo.error_func)
+ parser = LRParser(lr, pinfo.error_func)
parse = parser.parse
return parser
diff --git a/components/script/dom/bindings/codegen/pythonpath.py b/components/script/dom/bindings/codegen/pythonpath.py
deleted file mode 100644
index 793089551b5..00000000000
--- a/components/script/dom/bindings/codegen/pythonpath.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# 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/.
-
-"""
-Run a python script, adding extra directories to the python path.
-"""
-
-
-def main(args):
- def usage():
- print >>sys.stderr, "pythonpath.py -I directory script.py [args...]"
- sys.exit(150)
-
- paths = []
-
- while True:
- try:
- arg = args[0]
- except IndexError:
- usage()
-
- if arg == '-I':
- args.pop(0)
- try:
- path = args.pop(0)
- except IndexError:
- usage()
-
- paths.append(os.path.abspath(path))
- continue
-
- if arg.startswith('-I'):
- paths.append(os.path.abspath(args.pop(0)[2:]))
- continue
-
- if arg.startswith('-D'):
- os.chdir(args.pop(0)[2:])
- continue
-
- break
-
- script = args[0]
-
- sys.path[0:0] = [os.path.abspath(os.path.dirname(script))] + paths
- sys.argv = args
- sys.argc = len(args)
-
- frozenglobals['__name__'] = '__main__'
- frozenglobals['__file__'] = script
-
- execfile(script, frozenglobals)
-
-# Freeze scope here ... why this makes things work I have no idea ...
-frozenglobals = globals()
-
-import sys
-import os
-
-if __name__ == '__main__':
- main(sys.argv[1:])
diff --git a/components/script/dom/bindings/codegen/run.py b/components/script/dom/bindings/codegen/run.py
new file mode 100644
index 00000000000..7f58de15d69
--- /dev/null
+++ b/components/script/dom/bindings/codegen/run.py
@@ -0,0 +1,119 @@
+# 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 https://mozilla.org/MPL/2.0/.
+
+import os
+import sys
+import json
+
+
+def main():
+ os.chdir(os.path.join(os.path.dirname(__file__)))
+ sys.path[0:0] = ["./parser", "./ply"]
+
+ css_properties_json, out_dir = sys.argv[1:]
+ doc_servo = "../../../../../target/doc/servo"
+ webidls_dir = "../../webidls"
+ config_file = "Bindings.conf"
+
+ import WebIDL
+ from Configuration import Configuration
+ from CodegenRust import CGBindingRoot
+
+ parser = WebIDL.Parser(make_dir(os.path.join(out_dir, "cache")))
+ webidls = [name for name in os.listdir(webidls_dir) if name.endswith(".webidl")]
+ for webidl in webidls:
+ filename = os.path.join(webidls_dir, webidl)
+ with open(filename, "rb") as f:
+ parser.parse(f.read(), filename)
+
+ add_css_properties_attributes(css_properties_json, parser)
+ parser_results = parser.finish()
+ config = Configuration(config_file, parser_results)
+ make_dir(os.path.join(out_dir, "Bindings"))
+
+ for name, filename in [
+ ("PrototypeList", "PrototypeList.rs"),
+ ("RegisterBindings", "RegisterBindings.rs"),
+ ("InterfaceObjectMap", "InterfaceObjectMap.rs"),
+ ("InterfaceObjectMapData", "InterfaceObjectMapData.json"),
+ ("InterfaceTypes", "InterfaceTypes.rs"),
+ ("InheritTypes", "InheritTypes.rs"),
+ ("Bindings", "Bindings/mod.rs"),
+ ("UnionTypes", "UnionTypes.rs"),
+ ]:
+ generate(config, name, os.path.join(out_dir, filename))
+ make_dir(doc_servo)
+ generate(config, "SupportedDomApis", os.path.join(doc_servo, "apis.html"))
+
+ for webidl in webidls:
+ filename = os.path.join(webidls_dir, webidl)
+ prefix = "Bindings/%sBinding" % webidl[:-len(".webidl")]
+ module = CGBindingRoot(config, prefix, filename).define()
+ if module:
+ with open(os.path.join(out_dir, prefix + ".rs"), "wb") as f:
+ f.write(module.encode("utf-8"))
+
+
+def make_dir(path):
+ if not os.path.exists(path):
+ os.makedirs(path)
+ return path
+
+
+def generate(config, name, filename):
+ from CodegenRust import GlobalGenRoots
+ root = getattr(GlobalGenRoots, name)(config)
+ code = root.define()
+ with open(filename, "wb") as f:
+ f.write(code.encode("utf-8"))
+
+
+def add_css_properties_attributes(css_properties_json, parser):
+ css_properties = json.load(open(css_properties_json, "rb"))
+ idl = "partial interface CSSStyleDeclaration {\n%s\n};\n" % "\n".join(
+ " [%sCEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString %s;" % (
+ ('Pref="%s", ' % data["pref"] if data["pref"] else ""),
+ attribute_name
+ )
+ for (kind, properties_list) in sorted(css_properties.items())
+ for (property_name, data) in sorted(properties_list.items())
+ for attribute_name in attribute_names(property_name)
+ )
+ parser.parse(idl.encode("utf-8"), "CSSStyleDeclaration_generated.webidl")
+
+
+def attribute_names(property_name):
+ # https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-dashed-attribute
+ if property_name != "float":
+ yield property_name
+ else:
+ yield "_float"
+
+ # https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-camel-cased-attribute
+ if "-" in property_name:
+ yield "".join(camel_case(property_name))
+
+ # https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-webkit-cased-attribute
+ if property_name.startswith("-webkit-"):
+ yield "".join(camel_case(property_name), True)
+
+
+# https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
+def camel_case(chars, webkit_prefixed=False):
+ if webkit_prefixed:
+ chars = chars[1:]
+ next_is_uppercase = False
+ for c in chars:
+ if c == '-':
+ next_is_uppercase = True
+ elif next_is_uppercase:
+ next_is_uppercase = False
+ # Should be ASCII-uppercase, but all non-custom CSS property names are within ASCII
+ yield c.upper()
+ else:
+ yield c
+
+
+if __name__ == "__main__":
+ main()