aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/bindings/codegen')
-rw-r--r--components/script/dom/bindings/codegen/BindingGen.py52
-rw-r--r--components/script/dom/bindings/codegen/BindingUtils.cpp633
-rw-r--r--components/script/dom/bindings/codegen/BindingUtils.h1151
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf28
-rw-r--r--components/script/dom/bindings/codegen/Codegen.py5788
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py5534
-rw-r--r--components/script/dom/bindings/codegen/Configuration.py341
-rw-r--r--components/script/dom/bindings/codegen/DOMJSClass.h114
-rw-r--r--components/script/dom/bindings/codegen/DOMJSProxyHandler.cpp247
-rw-r--r--components/script/dom/bindings/codegen/DOMJSProxyHandler.h109
-rw-r--r--components/script/dom/bindings/codegen/ErrorResult.h59
-rw-r--r--components/script/dom/bindings/codegen/Errors.msg30
-rw-r--r--components/script/dom/bindings/codegen/GenerateCSS2PropertiesWebIDL.py26
-rw-r--r--components/script/dom/bindings/codegen/GlobalGen.py83
-rw-r--r--components/script/dom/bindings/codegen/Makefile.in165
-rw-r--r--components/script/dom/bindings/codegen/Nullable.h68
-rw-r--r--components/script/dom/bindings/codegen/PrimitiveConversions.h350
-rw-r--r--components/script/dom/bindings/codegen/RegisterBindings.h14
-rw-r--r--components/script/dom/bindings/codegen/TypedArray.h121
-rw-r--r--components/script/dom/bindings/codegen/crashtests/769464.html11
-rw-r--r--components/script/dom/bindings/codegen/crashtests/crashtests.list1
-rw-r--r--components/script/dom/bindings/codegen/parser/README1
-rw-r--r--components/script/dom/bindings/codegen/parser/UPSTREAM1
-rw-r--r--components/script/dom/bindings/codegen/parser/WebIDL.py5583
-rw-r--r--components/script/dom/bindings/codegen/parser/external.patch49
-rw-r--r--components/script/dom/bindings/codegen/parser/module.patch12
-rw-r--r--components/script/dom/bindings/codegen/parser/runtests.py79
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_any_null.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_argument_identifier_conflicts.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_argument_novoid.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_array_of_interface.py13
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_arraybuffer.py84
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_attr.py302
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_attr_sequence_type.py67
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_builtin_filename.py11
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_builtins.py41
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_callback.py34
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_callback_interface.py47
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_const.py64
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_constructor.py75
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py28
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_deduplicate.py15
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_dictionary.py198
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py150
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_double_null.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py84
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_empty_enum.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_enum.py81
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_enum_duplicate_values.py13
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_error_colno.py20
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py28
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py107
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_forward_decl.py15
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_implements.py216
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_incomplete_parent.py18
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_incomplete_types.py44
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interface.py188
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interface_const_identifier_conflicts.py15
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interface_identifier_conflicts_across_members.py60
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_method.py145
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py126
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_nullable_void.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_optional_constraints.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_overload.py47
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_sanity.py7
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py294
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_methods.py73
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py62
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_treatNonCallableAsNull.py56
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_typedef.py76
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_union.py169
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_union_any.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_union_nullable.py53
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_variadic_callback.py10
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_variadic_constraints.py39
-rwxr-xr-xcomponents/script/dom/bindings/codegen/parser/update.sh3
-rw-r--r--components/script/dom/bindings/codegen/ply/COPYING28
-rw-r--r--components/script/dom/bindings/codegen/ply/README9
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/__init__.py4
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/lex.py1058
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/yacc.py3276
-rw-r--r--components/script/dom/bindings/codegen/pythonpath.py60
-rw-r--r--components/script/dom/bindings/codegen/stubgenerator/Skeleton.cpp40
-rw-r--r--components/script/dom/bindings/codegen/stubgenerator/Skeleton.h40
-rw-r--r--components/script/dom/bindings/codegen/stubgenerator/generate.sh16
-rw-r--r--components/script/dom/bindings/codegen/test/Makefile.in87
-rw-r--r--components/script/dom/bindings/codegen/test/TestBindingHeader.h653
-rw-r--r--components/script/dom/bindings/codegen/test/TestCodeGen.webidl442
-rw-r--r--components/script/dom/bindings/codegen/test/TestDictionary.webidl9
-rw-r--r--components/script/dom/bindings/codegen/test/TestTypedef.webidl7
-rw-r--r--components/script/dom/bindings/codegen/test/file_bug775543.html5
-rw-r--r--components/script/dom/bindings/codegen/test/forOf_iframe.html13
-rw-r--r--components/script/dom/bindings/codegen/test/test_InstanceOf.html28
-rw-r--r--components/script/dom/bindings/codegen/test/test_bug773326.html11
-rw-r--r--components/script/dom/bindings/codegen/test/test_bug775543.html37
-rw-r--r--components/script/dom/bindings/codegen/test/test_bug788369.html30
-rw-r--r--components/script/dom/bindings/codegen/test/test_enums.html15
-rw-r--r--components/script/dom/bindings/codegen/test/test_forOf.html94
-rw-r--r--components/script/dom/bindings/codegen/test/test_integers.html45
-rw-r--r--components/script/dom/bindings/codegen/test/test_interfaceToString.html38
-rw-r--r--components/script/dom/bindings/codegen/test/test_lookupGetter.html49
-rw-r--r--components/script/dom/bindings/codegen/test/test_sequence_wrapping.html60
-rw-r--r--components/script/dom/bindings/codegen/test/test_traceProtos.html37
103 files changed, 30131 insertions, 0 deletions
diff --git a/components/script/dom/bindings/codegen/BindingGen.py b/components/script/dom/bindings/codegen/BindingGen.py
new file mode 100644
index 00000000000..408280dacfb
--- /dev/null
+++ b/components/script/dom/bindings/codegen/BindingGen.py
@@ -0,0 +1,52 @@
+# 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
+sys.path.append("./parser/")
+sys.path.append("./ply/")
+import os
+import cPickle
+import WebIDL
+from Configuration import *
+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"
+ root = CGBindingRoot(config, outputprefix, webidlfile)
+ if replaceFileIfChanged(filename, root.define()):
+ print "Generating binding implementation: %s" % (filename)
+
+def main():
+ # Parse arguments.
+ from optparse import OptionParser
+ usagestring = "usage: %prog configFile outputPrefix webIDLFile"
+ o = OptionParser(usage=usagestring)
+ o.add_option("--verbose-errors", action='store_true', default=False,
+ help="When an error happens, display the Python traceback.")
+ (options, args) = o.parse_args()
+
+ if len(args) != 3:
+ o.error(usagestring)
+ configFile = os.path.normpath(args[0])
+ outputPrefix = args[1]
+ webIDLFile = os.path.normpath(args[2])
+
+ # Load the parsing results
+ f = open('ParserResults.pkl', 'rb')
+ parserData = cPickle.load(f)
+ f.close()
+
+ # 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/BindingUtils.cpp b/components/script/dom/bindings/codegen/BindingUtils.cpp
new file mode 100644
index 00000000000..27ac92e3596
--- /dev/null
+++ b/components/script/dom/bindings/codegen/BindingUtils.cpp
@@ -0,0 +1,633 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdarg.h>
+
+#include "BindingUtils.h"
+
+#include "WrapperFactory.h"
+#include "xpcprivate.h"
+#include "XPCQuickStubs.h"
+
+namespace mozilla {
+namespace dom {
+
+JSErrorFormatString ErrorFormatString[] = {
+#define MSG_DEF(_name, _argc, _str) \
+ { _str, _argc, JSEXN_TYPEERR },
+#include "mozilla/dom/Errors.msg"
+#undef MSG_DEF
+};
+
+const JSErrorFormatString*
+GetErrorMessage(void* aUserRef, const char* aLocale,
+ const unsigned aErrorNumber)
+{
+ MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString));
+ return &ErrorFormatString[aErrorNumber];
+}
+
+bool
+ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, aErrorNumber);
+ JS_ReportErrorNumberVA(aCx, GetErrorMessage, NULL,
+ static_cast<const unsigned>(aErrorNumber), ap);
+ va_end(ap);
+ return false;
+}
+
+bool
+DefineConstants(JSContext* cx, JSObject* obj, ConstantSpec* cs)
+{
+ for (; cs->name; ++cs) {
+ JSBool ok =
+ JS_DefineProperty(cx, obj, cs->name, cs->value, NULL, NULL,
+ JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
+ if (!ok) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool
+Define(JSContext* cx, JSObject* obj, JSFunctionSpec* spec) {
+ return JS_DefineFunctions(cx, obj, spec);
+}
+static inline bool
+Define(JSContext* cx, JSObject* obj, JSPropertySpec* spec) {
+ return JS_DefineProperties(cx, obj, spec);
+}
+static inline bool
+Define(JSContext* cx, JSObject* obj, ConstantSpec* spec) {
+ return DefineConstants(cx, obj, spec);
+}
+
+template<typename T>
+bool
+DefinePrefable(JSContext* cx, JSObject* obj, Prefable<T>* props)
+{
+ MOZ_ASSERT(props);
+ MOZ_ASSERT(props->specs);
+ do {
+ // Define if enabled
+ if (props->enabled) {
+ if (!Define(cx, obj, props->specs)) {
+ return false;
+ }
+ }
+ } while ((++props)->specs);
+ return true;
+}
+
+// We should use JSFunction objects for interface objects, but we need a custom
+// hasInstance hook because we have new interface objects on prototype chains of
+// old (XPConnect-based) bindings. Because Function.prototype.toString throws if
+// passed a non-Function object we also need to provide our own toString method
+// for interface objects.
+
+enum {
+ TOSTRING_CLASS_RESERVED_SLOT = 0,
+ TOSTRING_NAME_RESERVED_SLOT = 1
+};
+
+JSBool
+InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
+{
+ JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
+
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ if (!obj) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
+ "null", "object");
+ return false;
+ }
+
+ jsval v = js::GetFunctionNativeReserved(callee, TOSTRING_CLASS_RESERVED_SLOT);
+ JSClass* clasp = static_cast<JSClass*>(JSVAL_TO_PRIVATE(v));
+
+ v = js::GetFunctionNativeReserved(callee, TOSTRING_NAME_RESERVED_SLOT);
+ JSString* jsname = static_cast<JSString*>(JSVAL_TO_STRING(v));
+ size_t length;
+ const jschar* name = JS_GetInternedStringCharsAndLength(jsname, &length);
+
+ if (js::GetObjectJSClass(obj) != clasp) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
+ NS_ConvertUTF16toUTF8(name).get(), "toString",
+ "object");
+ return false;
+ }
+
+ nsString str;
+ str.AppendLiteral("function ");
+ str.Append(name, length);
+ str.AppendLiteral("() {");
+ str.Append('\n');
+ str.AppendLiteral(" [native code]");
+ str.Append('\n');
+ str.AppendLiteral("}");
+
+ return xpc::NonVoidStringToJsval(cx, str, vp);
+}
+
+static JSObject*
+CreateInterfaceObject(JSContext* cx, JSObject* global, JSObject* receiver,
+ JSClass* constructorClass, JSNative constructorNative,
+ unsigned ctorNargs, JSObject* proto,
+ Prefable<JSFunctionSpec>* staticMethods,
+ Prefable<ConstantSpec>* constants,
+ const char* name)
+{
+ JSObject* constructor;
+ if (constructorClass) {
+ JSObject* functionProto = JS_GetFunctionPrototype(cx, global);
+ if (!functionProto) {
+ return NULL;
+ }
+ constructor = JS_NewObject(cx, constructorClass, functionProto, global);
+ } else {
+ MOZ_ASSERT(constructorNative);
+ JSFunction* fun = JS_NewFunction(cx, constructorNative, ctorNargs,
+ JSFUN_CONSTRUCTOR, global, name);
+ if (!fun) {
+ return NULL;
+ }
+ constructor = JS_GetFunctionObject(fun);
+ }
+ if (!constructor) {
+ return NULL;
+ }
+
+ if (staticMethods && !DefinePrefable(cx, constructor, staticMethods)) {
+ return NULL;
+ }
+
+ if (constructorClass) {
+ JSFunction* toString = js::DefineFunctionWithReserved(cx, constructor,
+ "toString",
+ InterfaceObjectToString,
+ 0, 0);
+ if (!toString) {
+ return NULL;
+ }
+
+ JSObject* toStringObj = JS_GetFunctionObject(toString);
+ js::SetFunctionNativeReserved(toStringObj, TOSTRING_CLASS_RESERVED_SLOT,
+ PRIVATE_TO_JSVAL(constructorClass));
+
+ JSString *str = ::JS_InternString(cx, name);
+ if (!str) {
+ return NULL;
+ }
+ js::SetFunctionNativeReserved(toStringObj, TOSTRING_NAME_RESERVED_SLOT,
+ STRING_TO_JSVAL(str));
+ }
+
+ if (constants && !DefinePrefable(cx, constructor, constants)) {
+ return NULL;
+ }
+
+ if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
+ return NULL;
+ }
+
+ JSBool alreadyDefined;
+ if (!JS_AlreadyHasOwnProperty(cx, receiver, name, &alreadyDefined)) {
+ return NULL;
+ }
+
+ // This is Enumerable: False per spec.
+ if (!alreadyDefined &&
+ !JS_DefineProperty(cx, receiver, name, OBJECT_TO_JSVAL(constructor), NULL,
+ NULL, 0)) {
+ return NULL;
+ }
+
+ return constructor;
+}
+
+static JSObject*
+CreateInterfacePrototypeObject(JSContext* cx, JSObject* global,
+ JSObject* parentProto, JSClass* protoClass,
+ Prefable<JSFunctionSpec>* methods,
+ Prefable<JSPropertySpec>* properties,
+ Prefable<ConstantSpec>* constants)
+{
+ JSObject* ourProto = JS_NewObjectWithUniqueType(cx, protoClass, parentProto,
+ global);
+ if (!ourProto) {
+ return NULL;
+ }
+
+ if (methods && !DefinePrefable(cx, ourProto, methods)) {
+ return NULL;
+ }
+
+ if (properties && !DefinePrefable(cx, ourProto, properties)) {
+ return NULL;
+ }
+
+ if (constants && !DefinePrefable(cx, ourProto, constants)) {
+ return NULL;
+ }
+
+ return ourProto;
+}
+
+JSObject*
+CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver,
+ JSObject* protoProto, JSClass* protoClass,
+ JSClass* constructorClass, JSNative constructor,
+ unsigned ctorNargs, const DOMClass* domClass,
+ Prefable<JSFunctionSpec>* methods,
+ Prefable<JSPropertySpec>* properties,
+ Prefable<ConstantSpec>* constants,
+ Prefable<JSFunctionSpec>* staticMethods, const char* name)
+{
+ MOZ_ASSERT(protoClass || constructorClass || constructor,
+ "Need at least one class or a constructor!");
+ MOZ_ASSERT(!(methods || properties) || protoClass,
+ "Methods or properties but no protoClass!");
+ MOZ_ASSERT(!staticMethods || constructorClass || constructor,
+ "Static methods but no constructorClass or constructor!");
+ MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
+ "Must have name precisely when we have an interface object");
+ MOZ_ASSERT(!constructorClass || !constructor);
+
+ JSObject* proto;
+ if (protoClass) {
+ proto = CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
+ methods, properties, constants);
+ if (!proto) {
+ return NULL;
+ }
+
+ js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
+ JS::PrivateValue(const_cast<DOMClass*>(domClass)));
+ }
+ else {
+ proto = NULL;
+ }
+
+ JSObject* interface;
+ if (constructorClass || constructor) {
+ interface = CreateInterfaceObject(cx, global, receiver, constructorClass,
+ constructor, ctorNargs, proto,
+ staticMethods, constants, name);
+ if (!interface) {
+ return NULL;
+ }
+ }
+
+ return protoClass ? proto : interface;
+}
+
+static bool
+NativeInterface2JSObjectAndThrowIfFailed(XPCLazyCallContext& aLccx,
+ JSContext* aCx,
+ JS::Value* aRetval,
+ xpcObjectHelper& aHelper,
+ const nsIID* aIID,
+ bool aAllowNativeWrapper)
+{
+ nsresult rv;
+ if (!XPCConvert::NativeInterface2JSObject(aLccx, aRetval, NULL, aHelper, aIID,
+ NULL, aAllowNativeWrapper, &rv)) {
+ // I can't tell if NativeInterface2JSObject throws JS exceptions
+ // or not. This is a sloppy stab at the right semantics; the
+ // method really ought to be fixed to behave consistently.
+ if (!JS_IsExceptionPending(aCx)) {
+ Throw<true>(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
+ }
+ return false;
+ }
+ return true;
+}
+
+bool
+DoHandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope,
+ nsISupports* value, JS::Value* vp)
+{
+ if (JS_IsExceptionPending(cx)) {
+ return false;
+ }
+
+ XPCLazyCallContext lccx(JS_CALLER, cx, scope);
+
+ if (value) {
+ xpcObjectHelper helper(value);
+ return NativeInterface2JSObjectAndThrowIfFailed(lccx, cx, vp, helper, NULL,
+ true);
+ }
+
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+}
+
+// Can only be called with the immediate prototype of the instance object. Can
+// only be called on the prototype of an object known to be a DOM instance.
+JSBool
+InstanceClassHasProtoAtDepth(JSHandleObject protoObject, uint32_t protoID,
+ uint32_t depth)
+{
+ const DOMClass* domClass = static_cast<DOMClass*>(
+ js::GetReservedSlot(protoObject, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate());
+ return (uint32_t)domClass->mInterfaceChain[depth] == protoID;
+}
+
+// Only set allowNativeWrapper to false if you really know you need it, if in
+// doubt use true. Setting it to false disables security wrappers.
+bool
+XPCOMObjectToJsval(JSContext* cx, JSObject* scope, xpcObjectHelper &helper,
+ const nsIID* iid, bool allowNativeWrapper, JS::Value* rval)
+{
+ XPCLazyCallContext lccx(JS_CALLER, cx, scope);
+
+ if (!NativeInterface2JSObjectAndThrowIfFailed(lccx, cx, rval, helper, iid,
+ allowNativeWrapper)) {
+ return false;
+ }
+
+#ifdef DEBUG
+ JSObject* jsobj = JSVAL_TO_OBJECT(*rval);
+ if (jsobj && !js::GetObjectParent(jsobj))
+ NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
+ "Why did we recreate this wrapper?");
+#endif
+
+ return true;
+}
+
+JSBool
+QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::Value thisv = JS_THIS(cx, vp);
+ if (thisv == JSVAL_NULL)
+ return false;
+
+ // Get the object. It might be a security wrapper, in which case we do a checked
+ // unwrap.
+ JSObject* origObj = JSVAL_TO_OBJECT(thisv);
+ JSObject* obj = js::UnwrapObjectChecked(cx, origObj);
+ if (!obj)
+ return false;
+
+ nsISupports* native;
+ if (!UnwrapDOMObjectToISupports(obj, native)) {
+ return Throw<true>(cx, NS_ERROR_FAILURE);
+ }
+
+ if (argc < 1) {
+ return Throw<true>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+ }
+
+ JS::Value* argv = JS_ARGV(cx, vp);
+ if (!argv[0].isObject()) {
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ nsIJSIID* iid;
+ xpc_qsSelfRef iidRef;
+ if (NS_FAILED(xpc_qsUnwrapArg<nsIJSIID>(cx, argv[0], &iid, &iidRef.ptr,
+ &argv[0]))) {
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+ MOZ_ASSERT(iid);
+
+ if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) {
+ nsresult rv;
+ nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv);
+ if (NS_FAILED(rv)) {
+ return Throw<true>(cx, rv);
+ }
+
+ return WrapObject(cx, origObj, ci, &NS_GET_IID(nsIClassInfo), vp);
+ }
+
+ // Lie, otherwise we need to check classinfo or QI
+ *vp = thisv;
+ return true;
+}
+
+JSBool
+ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
+}
+
+bool
+XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
+ JSPropertyDescriptor* desc,
+ // And the things we need to determine the descriptor
+ Prefable<JSFunctionSpec>* methods,
+ jsid* methodIds,
+ JSFunctionSpec* methodSpecs,
+ size_t methodCount,
+ Prefable<JSPropertySpec>* attributes,
+ jsid* attributeIds,
+ JSPropertySpec* attributeSpecs,
+ size_t attributeCount,
+ Prefable<ConstantSpec>* constants,
+ jsid* constantIds,
+ ConstantSpec* constantSpecs,
+ size_t constantCount)
+{
+ for (size_t prefIdx = 0; prefIdx < methodCount; ++prefIdx) {
+ MOZ_ASSERT(methods[prefIdx].specs);
+ if (methods[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = methods[prefIdx].specs - methodSpecs;
+ for ( ; methodIds[i] != JSID_VOID; ++i) {
+ if (id == methodIds[i]) {
+ JSFunction *fun = JS_NewFunctionById(cx, methodSpecs[i].call.op,
+ methodSpecs[i].nargs, 0,
+ wrapper, id);
+ if (!fun) {
+ return false;
+ }
+ SET_JITINFO(fun, methodSpecs[i].call.info);
+ JSObject *funobj = JS_GetFunctionObject(fun);
+ desc->value.setObject(*funobj);
+ desc->attrs = methodSpecs[i].flags;
+ desc->obj = wrapper;
+ desc->setter = nullptr;
+ desc->getter = nullptr;
+ return true;
+ }
+ }
+ }
+ }
+
+ for (size_t prefIdx = 0; prefIdx < attributeCount; ++prefIdx) {
+ MOZ_ASSERT(attributes[prefIdx].specs);
+ if (attributes[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = attributes[prefIdx].specs - attributeSpecs;
+ for ( ; attributeIds[i] != JSID_VOID; ++i) {
+ if (id == attributeIds[i]) {
+ // Because of centralization, we need to make sure we fault in the
+ // JitInfos as well. At present, until the JSAPI changes, the easiest
+ // way to do this is wrap them up as functions ourselves.
+ desc->attrs = attributeSpecs[i].flags & ~JSPROP_NATIVE_ACCESSORS;
+ // They all have getters, so we can just make it.
+ JSObject *global = JS_GetGlobalForObject(cx, wrapper);
+ JSFunction *fun = JS_NewFunction(cx, (JSNative)attributeSpecs[i].getter.op,
+ 0, 0, global, NULL);
+ if (!fun)
+ return false;
+ SET_JITINFO(fun, attributeSpecs[i].getter.info);
+ JSObject *funobj = JS_GetFunctionObject(fun);
+ desc->getter = js::CastAsJSPropertyOp(funobj);
+ desc->attrs |= JSPROP_GETTER;
+ if (attributeSpecs[i].setter.op) {
+ // We have a setter! Make it.
+ fun = JS_NewFunction(cx, (JSNative)attributeSpecs[i].setter.op,
+ 1, 0, global, NULL);
+ if (!fun)
+ return false;
+ SET_JITINFO(fun, attributeSpecs[i].setter.info);
+ funobj = JS_GetFunctionObject(fun);
+ desc->setter = js::CastAsJSStrictPropertyOp(funobj);
+ desc->attrs |= JSPROP_SETTER;
+ } else {
+ desc->setter = NULL;
+ }
+ desc->obj = wrapper;
+ return true;
+ }
+ }
+ }
+ }
+
+ for (size_t prefIdx = 0; prefIdx < constantCount; ++prefIdx) {
+ MOZ_ASSERT(constants[prefIdx].specs);
+ if (constants[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = constants[prefIdx].specs - constantSpecs;
+ for ( ; constantIds[i] != JSID_VOID; ++i) {
+ if (id == constantIds[i]) {
+ desc->attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
+ desc->obj = wrapper;
+ desc->value = constantSpecs[i].value;
+ return true;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+XrayEnumerateProperties(JS::AutoIdVector& props,
+ Prefable<JSFunctionSpec>* methods,
+ jsid* methodIds,
+ JSFunctionSpec* methodSpecs,
+ size_t methodCount,
+ Prefable<JSPropertySpec>* attributes,
+ jsid* attributeIds,
+ JSPropertySpec* attributeSpecs,
+ size_t attributeCount,
+ Prefable<ConstantSpec>* constants,
+ jsid* constantIds,
+ ConstantSpec* constantSpecs,
+ size_t constantCount)
+{
+ for (size_t prefIdx = 0; prefIdx < methodCount; ++prefIdx) {
+ MOZ_ASSERT(methods[prefIdx].specs);
+ if (methods[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = methods[prefIdx].specs - methodSpecs;
+ for ( ; methodIds[i] != JSID_VOID; ++i) {
+ if ((methodSpecs[i].flags & JSPROP_ENUMERATE) &&
+ !props.append(methodIds[i])) {
+ return false;
+ }
+ }
+ }
+ }
+
+ for (size_t prefIdx = 0; prefIdx < attributeCount; ++prefIdx) {
+ MOZ_ASSERT(attributes[prefIdx].specs);
+ if (attributes[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = attributes[prefIdx].specs - attributeSpecs;
+ for ( ; attributeIds[i] != JSID_VOID; ++i) {
+ if ((attributeSpecs[i].flags & JSPROP_ENUMERATE) &&
+ !props.append(attributeIds[i])) {
+ return false;
+ }
+ }
+ }
+ }
+
+ for (size_t prefIdx = 0; prefIdx < constantCount; ++prefIdx) {
+ MOZ_ASSERT(constants[prefIdx].specs);
+ if (constants[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = constants[prefIdx].specs - constantSpecs;
+ for ( ; constantIds[i] != JSID_VOID; ++i) {
+ if (!props.append(constantIds[i])) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+GetPropertyOnPrototype(JSContext* cx, JSObject* proxy, jsid id, bool* found,
+ JS::Value* vp)
+{
+ JSObject* proto;
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ if (!proto) {
+ *found = false;
+ return true;
+ }
+
+ JSBool hasProp;
+ if (!JS_HasPropertyById(cx, proto, id, &hasProp)) {
+ return false;
+ }
+
+ *found = hasProp;
+ if (!hasProp || !vp) {
+ return true;
+ }
+
+ return JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp);
+}
+
+bool
+HasPropertyOnPrototype(JSContext* cx, JSObject* proxy, DOMProxyHandler* handler,
+ jsid id)
+{
+ Maybe<JSAutoCompartment> ac;
+ if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
+ proxy = js::UnwrapObject(proxy);
+ ac.construct(cx, proxy);
+ }
+ MOZ_ASSERT(js::IsProxy(proxy) && js::GetProxyHandler(proxy) == handler);
+
+ bool found;
+ // We ignore an error from GetPropertyOnPrototype.
+ return !GetPropertyOnPrototype(cx, proxy, id, &found, NULL) || found;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/components/script/dom/bindings/codegen/BindingUtils.h b/components/script/dom/bindings/codegen/BindingUtils.h
new file mode 100644
index 00000000000..ee9d6c3691c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/BindingUtils.h
@@ -0,0 +1,1151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_BindingUtils_h__
+#define mozilla_dom_BindingUtils_h__
+
+#include "mozilla/dom/DOMJSClass.h"
+#include "mozilla/dom/DOMJSProxyHandler.h"
+#include "mozilla/dom/workers/Workers.h"
+#include "mozilla/ErrorResult.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "jswrapper.h"
+
+#include "nsIXPConnect.h"
+#include "qsObjectHelper.h"
+#include "xpcpublic.h"
+#include "nsTraceRefcnt.h"
+#include "nsWrapperCacheInlines.h"
+#include "mozilla/Likely.h"
+
+// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't
+// try to use it without fixing that first.
+class nsGlobalWindow;
+
+namespace mozilla {
+namespace dom {
+
+enum ErrNum {
+#define MSG_DEF(_name, _argc, _str) \
+ _name,
+#include "mozilla/dom/Errors.msg"
+#undef MSG_DEF
+ Err_Limit
+};
+
+bool
+ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...);
+
+template<bool mainThread>
+inline bool
+Throw(JSContext* cx, nsresult rv)
+{
+ using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
+
+ // XXX Introduce exception machinery.
+ if (mainThread) {
+ xpc::Throw(cx, rv);
+ } else {
+ if (!JS_IsExceptionPending(cx)) {
+ ThrowDOMExceptionForNSResult(cx, rv);
+ }
+ }
+ return false;
+}
+
+template<bool mainThread>
+inline bool
+ThrowMethodFailedWithDetails(JSContext* cx, const ErrorResult& rv,
+ const char* /* ifaceName */,
+ const char* /* memberName */)
+{
+ return Throw<mainThread>(cx, rv.ErrorCode());
+}
+
+inline bool
+IsDOMClass(const JSClass* clasp)
+{
+ return clasp->flags & JSCLASS_IS_DOMJSCLASS;
+}
+
+inline bool
+IsDOMClass(const js::Class* clasp)
+{
+ return IsDOMClass(Jsvalify(clasp));
+}
+
+// It's ok for eRegularDOMObject and eProxyDOMObject to be the same, but
+// eNonDOMObject should always be different from the other two. This enum
+// shouldn't be used to differentiate between non-proxy and proxy bindings.
+enum DOMObjectSlot {
+ eNonDOMObject = -1,
+ eRegularDOMObject = DOM_OBJECT_SLOT,
+ eProxyDOMObject = DOM_PROXY_OBJECT_SLOT
+};
+
+template <class T>
+inline T*
+UnwrapDOMObject(JSObject* obj, DOMObjectSlot slot)
+{
+ MOZ_ASSERT(slot != eNonDOMObject,
+ "Don't pass non-DOM objects to this function");
+
+#ifdef DEBUG
+ if (IsDOMClass(js::GetObjectClass(obj))) {
+ MOZ_ASSERT(slot == eRegularDOMObject);
+ } else {
+ MOZ_ASSERT(js::IsObjectProxyClass(js::GetObjectClass(obj)) ||
+ js::IsFunctionProxyClass(js::GetObjectClass(obj)));
+ MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
+ MOZ_ASSERT(IsNewProxyBinding(js::GetProxyHandler(obj)));
+ MOZ_ASSERT(slot == eProxyDOMObject);
+ }
+#endif
+
+ JS::Value val = js::GetReservedSlot(obj, slot);
+ // XXXbz/khuey worker code tries to unwrap interface objects (which have
+ // nothing here). That needs to stop.
+ // XXX We don't null-check UnwrapObject's result; aren't we going to crash
+ // anyway?
+ if (val.isUndefined()) {
+ return NULL;
+ }
+
+ return static_cast<T*>(val.toPrivate());
+}
+
+// Only use this with a new DOM binding object (either proxy or regular).
+inline const DOMClass*
+GetDOMClass(JSObject* obj)
+{
+ js::Class* clasp = js::GetObjectClass(obj);
+ if (IsDOMClass(clasp)) {
+ return &DOMJSClass::FromJSClass(clasp)->mClass;
+ }
+
+ js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
+ MOZ_ASSERT(handler->family() == ProxyFamily());
+ MOZ_ASSERT(IsNewProxyBinding(handler));
+ return &static_cast<DOMProxyHandler*>(handler)->mClass;
+}
+
+inline DOMObjectSlot
+GetDOMClass(JSObject* obj, const DOMClass*& result)
+{
+ js::Class* clasp = js::GetObjectClass(obj);
+ if (IsDOMClass(clasp)) {
+ result = &DOMJSClass::FromJSClass(clasp)->mClass;
+ return eRegularDOMObject;
+ }
+
+ if (js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) {
+ js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
+ if (handler->family() == ProxyFamily() && IsNewProxyBinding(handler)) {
+ result = &static_cast<DOMProxyHandler*>(handler)->mClass;
+ return eProxyDOMObject;
+ }
+ }
+
+ return eNonDOMObject;
+}
+
+inline bool
+UnwrapDOMObjectToISupports(JSObject* obj, nsISupports*& result)
+{
+ const DOMClass* clasp;
+ DOMObjectSlot slot = GetDOMClass(obj, clasp);
+ if (slot == eNonDOMObject || !clasp->mDOMObjectIsISupports) {
+ return false;
+ }
+
+ result = UnwrapDOMObject<nsISupports>(obj, slot);
+ return true;
+}
+
+inline bool
+IsDOMObject(JSObject* obj)
+{
+ js::Class* clasp = js::GetObjectClass(obj);
+ return IsDOMClass(clasp) ||
+ ((js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) &&
+ (js::GetProxyHandler(obj)->family() == ProxyFamily() &&
+ IsNewProxyBinding(js::GetProxyHandler(obj))));
+}
+
+// Some callers don't want to set an exception when unwrapping fails
+// (for example, overload resolution uses unwrapping to tell what sort
+// of thing it's looking at).
+// U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>).
+template <prototypes::ID PrototypeID, class T, typename U>
+inline nsresult
+UnwrapObject(JSContext* cx, JSObject* obj, U& value)
+{
+ /* First check to see whether we have a DOM object */
+ const DOMClass* domClass;
+ DOMObjectSlot slot = GetDOMClass(obj, domClass);
+ if (slot == eNonDOMObject) {
+ /* Maybe we have a security wrapper or outer window? */
+ if (!js::IsWrapper(obj)) {
+ /* Not a DOM object, not a wrapper, just bail */
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+
+ obj = xpc::Unwrap(cx, obj, false);
+ if (!obj) {
+ return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
+ }
+ MOZ_ASSERT(!js::IsWrapper(obj));
+ slot = GetDOMClass(obj, domClass);
+ if (slot == eNonDOMObject) {
+ /* We don't have a DOM object */
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+ }
+
+ /* This object is a DOM object. Double-check that it is safely
+ castable to T by checking whether it claims to inherit from the
+ class identified by protoID. */
+ if (domClass->mInterfaceChain[PrototypeTraits<PrototypeID>::Depth] ==
+ PrototypeID) {
+ value = UnwrapDOMObject<T>(obj, slot);
+ return NS_OK;
+ }
+
+ /* It's the wrong sort of DOM object */
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+}
+
+inline bool
+IsArrayLike(JSContext* cx, JSObject* obj)
+{
+ MOZ_ASSERT(obj);
+ // For simplicity, check for security wrappers up front. In case we
+ // have a security wrapper, don't forget to enter the compartment of
+ // the underlying object after unwrapping.
+ Maybe<JSAutoCompartment> ac;
+ if (js::IsWrapper(obj)) {
+ obj = xpc::Unwrap(cx, obj, false);
+ if (!obj) {
+ // Let's say it's not
+ return false;
+ }
+
+ ac.construct(cx, obj);
+ }
+
+ // XXXbz need to detect platform objects (including listbinding
+ // ones) with indexGetters here!
+ return JS_IsArrayObject(cx, obj) || JS_IsTypedArrayObject(obj, cx);
+}
+
+inline bool
+IsPlatformObject(JSContext* cx, JSObject* obj)
+{
+ // XXXbz Should be treating list-binding objects as platform objects
+ // too? The one consumer so far wants non-array-like platform
+ // objects, so listbindings that have an indexGetter should test
+ // false from here. Maybe this function should have a different
+ // name?
+ MOZ_ASSERT(obj);
+ // Fast-path the common case
+ JSClass* clasp = js::GetObjectJSClass(obj);
+ if (IsDOMClass(clasp)) {
+ return true;
+ }
+ // Now for simplicity check for security wrappers before anything else
+ if (js::IsWrapper(obj)) {
+ obj = xpc::Unwrap(cx, obj, false);
+ if (!obj) {
+ // Let's say it's not
+ return false;
+ }
+ clasp = js::GetObjectJSClass(obj);
+ }
+ return IS_WRAPPER_CLASS(js::Valueify(clasp)) || IsDOMClass(clasp) ||
+ JS_IsArrayBufferObject(obj, cx);
+}
+
+// U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>).
+template <class T, typename U>
+inline nsresult
+UnwrapObject(JSContext* cx, JSObject* obj, U& value)
+{
+ return UnwrapObject<static_cast<prototypes::ID>(
+ PrototypeIDMap<T>::PrototypeID), T>(cx, obj, value);
+}
+
+const size_t kProtoOrIfaceCacheCount =
+ prototypes::id::_ID_Count + constructors::id::_ID_Count;
+
+inline void
+AllocateProtoOrIfaceCache(JSObject* obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+ MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
+
+ // Important: The () at the end ensure zero-initialization
+ JSObject** protoOrIfaceArray = new JSObject*[kProtoOrIfaceCacheCount]();
+
+ js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
+ JS::PrivateValue(protoOrIfaceArray));
+}
+
+inline void
+TraceProtoOrIfaceCache(JSTracer* trc, JSObject* obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+
+ if (!HasProtoOrIfaceArray(obj))
+ return;
+ JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(obj);
+ for (size_t i = 0; i < kProtoOrIfaceCacheCount; ++i) {
+ JSObject* proto = protoOrIfaceArray[i];
+ if (proto) {
+ JS_CALL_OBJECT_TRACER(trc, proto, "protoOrIfaceArray[i]");
+ }
+ }
+}
+
+inline void
+DestroyProtoOrIfaceCache(JSObject* obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+
+ JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(obj);
+
+ delete [] protoOrIfaceArray;
+}
+
+struct ConstantSpec
+{
+ const char* name;
+ JS::Value value;
+};
+
+/**
+ * Add constants to an object.
+ */
+bool
+DefineConstants(JSContext* cx, JSObject* obj, ConstantSpec* cs);
+
+template<typename T>
+struct Prefable {
+ // A boolean indicating whether this set of specs is enabled
+ bool enabled;
+ // Array of specs, terminated in whatever way is customary for T.
+ // Null to indicate a end-of-array for Prefable, when such an
+ // indicator is needed.
+ T* specs;
+};
+
+/*
+ * Create a DOM interface object (if constructorClass is non-null) and/or a
+ * DOM interface prototype object (if protoClass is non-null).
+ *
+ * global is used as the parent of the interface object and the interface
+ * prototype object
+ * receiver is the object on which we need to define the interface object as a
+ * property
+ * protoProto is the prototype to use for the interface prototype object.
+ * protoClass is the JSClass to use for the interface prototype object.
+ * This is null if we should not create an interface prototype
+ * object.
+ * constructorClass is the JSClass to use for the interface object.
+ * This is null if we should not create an interface object or
+ * if it should be a function object.
+ * constructor is the JSNative to use as a constructor. If this is non-null, it
+ * should be used as a JSNative to back the interface object, which
+ * should be a Function. If this is null, then we should create an
+ * object of constructorClass, unless that's also null, in which
+ * case we should not create an interface object at all.
+ * ctorNargs is the length of the constructor function; 0 if no constructor
+ * instanceClass is the JSClass of instance objects for this class. This can
+ * be null if this is not a concrete proto.
+ * methods and properties are to be defined on the interface prototype object;
+ * these arguments are allowed to be null if there are no
+ * methods or properties respectively.
+ * constants are to be defined on the interface object and on the interface
+ * prototype object; allowed to be null if there are no constants.
+ * staticMethods are to be defined on the interface object; allowed to be null
+ * if there are no static methods.
+ *
+ * At least one of protoClass and constructorClass should be non-null.
+ * If constructorClass is non-null, the resulting interface object will be
+ * defined on the given global with property name |name|, which must also be
+ * non-null.
+ *
+ * returns the interface prototype object if protoClass is non-null, else it
+ * returns the interface object.
+ */
+JSObject*
+CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* receiver,
+ JSObject* protoProto, JSClass* protoClass,
+ JSClass* constructorClass, JSNative constructor,
+ unsigned ctorNargs, const DOMClass* domClass,
+ Prefable<JSFunctionSpec>* methods,
+ Prefable<JSPropertySpec>* properties,
+ Prefable<ConstantSpec>* constants,
+ Prefable<JSFunctionSpec>* staticMethods, const char* name);
+
+template <class T>
+inline bool
+WrapNewBindingObject(JSContext* cx, JSObject* scope, T* value, JS::Value* vp)
+{
+ JSObject* obj = value->GetWrapper();
+ if (obj && js::GetObjectCompartment(obj) == js::GetObjectCompartment(scope)) {
+ *vp = JS::ObjectValue(*obj);
+ return true;
+ }
+
+ if (!obj) {
+ bool triedToWrap;
+ obj = value->WrapObject(cx, scope, &triedToWrap);
+ if (!obj) {
+ // At this point, obj is null, so just return false. We could
+ // try to communicate triedToWrap to the caller, but in practice
+ // callers seem to be testing JS_IsExceptionPending(cx) to
+ // figure out whether WrapObject() threw instead.
+ return false;
+ }
+ }
+
+ // When called via XrayWrapper, we end up here while running in the
+ // chrome compartment. But the obj we have would be created in
+ // whatever the content compartment is. So at this point we need to
+ // make sure it's correctly wrapped for the compartment of |scope|.
+ // cx should already be in the compartment of |scope| here.
+ MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
+ *vp = JS::ObjectValue(*obj);
+ return JS_WrapValue(cx, vp);
+}
+
+// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
+template <template <typename> class SmartPtr, class T>
+inline bool
+WrapNewBindingObject(JSContext* cx, JSObject* scope, const SmartPtr<T>& value,
+ JS::Value* vp)
+{
+ return WrapNewBindingObject(cx, scope, value.get(), vp);
+}
+
+template <class T>
+inline bool
+WrapNewBindingNonWrapperCachedObject(JSContext* cx, JSObject* scope, T* value,
+ JS::Value* vp)
+{
+ // We try to wrap in the compartment of the underlying object of "scope"
+ JSObject* obj;
+ {
+ // scope for the JSAutoCompartment so that we restore the compartment
+ // before we call JS_WrapValue.
+ Maybe<JSAutoCompartment> ac;
+ if (js::IsWrapper(scope)) {
+ scope = xpc::Unwrap(cx, scope, false);
+ if (!scope)
+ return false;
+ ac.construct(cx, scope);
+ }
+
+ obj = value->WrapObject(cx, scope);
+ }
+
+ // We can end up here in all sorts of compartments, per above. Make
+ // sure to JS_WrapValue!
+ *vp = JS::ObjectValue(*obj);
+ return JS_WrapValue(cx, vp);
+}
+
+// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
+template <template <typename> class SmartPtr, typename T>
+inline bool
+WrapNewBindingNonWrapperCachedObject(JSContext* cx, JSObject* scope,
+ const SmartPtr<T>& value, JS::Value* vp)
+{
+ return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), vp);
+}
+
+/**
+ * A method to handle new-binding wrap failure, by possibly falling back to
+ * wrapping as a non-new-binding object.
+ */
+bool
+DoHandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope,
+ nsISupports* value, JS::Value* vp);
+
+/**
+ * An easy way to call the above when you have a value which
+ * multiply-inherits from nsISupports.
+ */
+template <class T>
+bool
+HandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope, T* value,
+ JS::Value* vp)
+{
+ nsCOMPtr<nsISupports> val;
+ CallQueryInterface(value, getter_AddRefs(val));
+ return DoHandleNewBindingWrappingFailure(cx, scope, val, vp);
+}
+
+// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
+template <template <typename> class SmartPtr, class T>
+MOZ_ALWAYS_INLINE bool
+HandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope,
+ const SmartPtr<T>& value, JS::Value* vp)
+{
+ return HandleNewBindingWrappingFailure(cx, scope, value.get(), vp);
+}
+
+struct EnumEntry {
+ const char* value;
+ size_t length;
+};
+
+template<bool Fatal>
+inline bool
+EnumValueNotFound(JSContext* cx, const jschar* chars, size_t length,
+ const char* type)
+{
+ return false;
+}
+
+template<>
+inline bool
+EnumValueNotFound<false>(JSContext* cx, const jschar* chars, size_t length,
+ const char* type)
+{
+ // TODO: Log a warning to the console.
+ return true;
+}
+
+template<>
+inline bool
+EnumValueNotFound<true>(JSContext* cx, const jschar* chars, size_t length,
+ const char* type)
+{
+ NS_LossyConvertUTF16toASCII deflated(static_cast<const PRUnichar*>(chars),
+ length);
+ return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, deflated.get(), type);
+}
+
+
+template<bool InvalidValueFatal>
+inline int
+FindEnumStringIndex(JSContext* cx, JS::Value v, const EnumEntry* values,
+ const char* type, bool* ok)
+{
+ // JS_StringEqualsAscii is slow as molasses, so don't use it here.
+ JSString* str = JS_ValueToString(cx, v);
+ if (!str) {
+ *ok = false;
+ return 0;
+ }
+ JS::Anchor<JSString*> anchor(str);
+ size_t length;
+ const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
+ if (!chars) {
+ *ok = false;
+ return 0;
+ }
+ int i = 0;
+ for (const EnumEntry* value = values; value->value; ++value, ++i) {
+ if (length != value->length) {
+ continue;
+ }
+
+ bool equal = true;
+ const char* val = value->value;
+ for (size_t j = 0; j != length; ++j) {
+ if (unsigned(val[j]) != unsigned(chars[j])) {
+ equal = false;
+ break;
+ }
+ }
+
+ if (equal) {
+ *ok = true;
+ return i;
+ }
+ }
+
+ *ok = EnumValueNotFound<InvalidValueFatal>(cx, chars, length, type);
+ return -1;
+}
+
+inline nsWrapperCache*
+GetWrapperCache(nsWrapperCache* cache)
+{
+ return cache;
+}
+
+inline nsWrapperCache*
+GetWrapperCache(nsGlobalWindow* not_allowed);
+
+inline nsWrapperCache*
+GetWrapperCache(void* p)
+{
+ return NULL;
+}
+
+struct ParentObject {
+ template<class T>
+ ParentObject(T* aObject) :
+ mObject(aObject),
+ mWrapperCache(GetWrapperCache(aObject))
+ {}
+
+ template<class T, template<typename> class SmartPtr>
+ ParentObject(const SmartPtr<T>& aObject) :
+ mObject(aObject.get()),
+ mWrapperCache(GetWrapperCache(aObject.get()))
+ {}
+
+ ParentObject(nsISupports* aObject, nsWrapperCache* aCache) :
+ mObject(aObject),
+ mWrapperCache(aCache)
+ {}
+
+ nsISupports* const mObject;
+ nsWrapperCache* const mWrapperCache;
+};
+
+inline nsWrapperCache*
+GetWrapperCache(const ParentObject& aParentObject)
+{
+ return aParentObject.mWrapperCache;
+}
+
+template<class T>
+inline nsISupports*
+GetParentPointer(T* aObject)
+{
+ return ToSupports(aObject);
+}
+
+inline nsISupports*
+GetParentPointer(const ParentObject& aObject)
+{
+ return ToSupports(aObject.mObject);
+}
+
+template<class T>
+inline void
+ClearWrapper(T* p, nsWrapperCache* cache)
+{
+ cache->ClearWrapper();
+}
+
+template<class T>
+inline void
+ClearWrapper(T* p, void*)
+{
+ nsWrapperCache* cache;
+ CallQueryInterface(p, &cache);
+ ClearWrapper(p, cache);
+}
+
+// Can only be called with the immediate prototype of the instance object. Can
+// only be called on the prototype of an object known to be a DOM instance.
+JSBool
+InstanceClassHasProtoAtDepth(JSHandleObject protoObject, uint32_t protoID,
+ uint32_t depth);
+
+// Only set allowNativeWrapper to false if you really know you need it, if in
+// doubt use true. Setting it to false disables security wrappers.
+bool
+XPCOMObjectToJsval(JSContext* cx, JSObject* scope, xpcObjectHelper &helper,
+ const nsIID* iid, bool allowNativeWrapper, JS::Value* rval);
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, T* p, nsWrapperCache* cache,
+ const nsIID* iid, JS::Value* vp)
+{
+ if (xpc_FastGetCachedWrapper(cache, scope, vp))
+ return true;
+ qsObjectHelper helper(p, cache);
+ return XPCOMObjectToJsval(cx, scope, helper, iid, true, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, T* p, const nsIID* iid,
+ JS::Value* vp)
+{
+ return WrapObject(cx, scope, p, GetWrapperCache(p), iid, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, T* p, JS::Value* vp)
+{
+ return WrapObject(cx, scope, p, NULL, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, nsCOMPtr<T> &p, const nsIID* iid,
+ JS::Value* vp)
+{
+ return WrapObject(cx, scope, p.get(), iid, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, nsCOMPtr<T> &p, JS::Value* vp)
+{
+ return WrapObject(cx, scope, p, NULL, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, nsRefPtr<T> &p, const nsIID* iid,
+ JS::Value* vp)
+{
+ return WrapObject(cx, scope, p.get(), iid, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, nsRefPtr<T> &p, JS::Value* vp)
+{
+ return WrapObject(cx, scope, p, NULL, vp);
+}
+
+template<>
+inline bool
+WrapObject<JSObject>(JSContext* cx, JSObject* scope, JSObject* p, JS::Value* vp)
+{
+ vp->setObjectOrNull(p);
+ return true;
+}
+
+template<typename T>
+static inline JSObject*
+WrapNativeParent(JSContext* cx, JSObject* scope, const T& p)
+{
+ if (!GetParentPointer(p))
+ return scope;
+
+ nsWrapperCache* cache = GetWrapperCache(p);
+ JSObject* obj;
+ if (cache && (obj = cache->GetWrapper())) {
+#ifdef DEBUG
+ qsObjectHelper helper(GetParentPointer(p), cache);
+ JS::Value debugVal;
+
+ bool ok = XPCOMObjectToJsval(cx, scope, helper, NULL, false, &debugVal);
+ NS_ASSERTION(ok && JSVAL_TO_OBJECT(debugVal) == obj,
+ "Unexpected object in nsWrapperCache");
+#endif
+ return obj;
+ }
+
+ qsObjectHelper helper(GetParentPointer(p), cache);
+ JS::Value v;
+ return XPCOMObjectToJsval(cx, scope, helper, NULL, false, &v) ?
+ JSVAL_TO_OBJECT(v) :
+ NULL;
+}
+
+static inline bool
+InternJSString(JSContext* cx, jsid& id, const char* chars)
+{
+ if (JSString *str = ::JS_InternString(cx, chars)) {
+ id = INTERNED_STRING_TO_JSID(cx, str);
+ return true;
+ }
+ return false;
+}
+
+// Spec needs a name property
+template <typename Spec>
+static bool
+InitIds(JSContext* cx, Prefable<Spec>* prefableSpecs, jsid* ids)
+{
+ MOZ_ASSERT(prefableSpecs);
+ MOZ_ASSERT(prefableSpecs->specs);
+ do {
+ // We ignore whether the set of ids is enabled and just intern all the IDs,
+ // because this is only done once per application runtime.
+ Spec* spec = prefableSpecs->specs;
+ do {
+ if (!InternJSString(cx, *ids, spec->name)) {
+ return false;
+ }
+ } while (++ids, (++spec)->name);
+
+ // We ran out of ids for that pref. Put a JSID_VOID in on the id
+ // corresponding to the list terminator for the pref.
+ *ids = JSID_VOID;
+ ++ids;
+ } while ((++prefableSpecs)->specs);
+
+ return true;
+}
+
+JSBool
+QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp);
+JSBool
+ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+GetPropertyOnPrototype(JSContext* cx, JSObject* proxy, jsid id, bool* found,
+ JS::Value* vp);
+
+bool
+HasPropertyOnPrototype(JSContext* cx, JSObject* proxy, DOMProxyHandler* handler,
+ jsid id);
+
+template<class T>
+class NonNull
+{
+public:
+ NonNull()
+#ifdef DEBUG
+ : inited(false)
+#endif
+ {}
+
+ operator T&() {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr, "NonNull<T> was set to null");
+ return *ptr;
+ }
+
+ operator const T&() const {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr, "NonNull<T> was set to null");
+ return *ptr;
+ }
+
+ void operator=(T* t) {
+ ptr = t;
+ MOZ_ASSERT(ptr);
+#ifdef DEBUG
+ inited = true;
+#endif
+ }
+
+ template<typename U>
+ void operator=(U* t) {
+ ptr = t->ToAStringPtr();
+ MOZ_ASSERT(ptr);
+#ifdef DEBUG
+ inited = true;
+#endif
+ }
+
+ T** Slot() {
+#ifdef DEBUG
+ inited = true;
+#endif
+ return &ptr;
+ }
+
+protected:
+ T* ptr;
+#ifdef DEBUG
+ bool inited;
+#endif
+};
+
+template<class T>
+class OwningNonNull
+{
+public:
+ OwningNonNull()
+#ifdef DEBUG
+ : inited(false)
+#endif
+ {}
+
+ operator T&() {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr, "OwningNonNull<T> was set to null");
+ return *ptr;
+ }
+
+ void operator=(T* t) {
+ init(t);
+ }
+
+ void operator=(const already_AddRefed<T>& t) {
+ init(t);
+ }
+
+protected:
+ template<typename U>
+ void init(U t) {
+ ptr = t;
+ MOZ_ASSERT(ptr);
+#ifdef DEBUG
+ inited = true;
+#endif
+ }
+
+ nsRefPtr<T> ptr;
+#ifdef DEBUG
+ bool inited;
+#endif
+};
+
+// A struct that has the same layout as an nsDependentString but much
+// faster constructor and destructor behavior
+struct FakeDependentString {
+ FakeDependentString() :
+ mFlags(nsDependentString::F_TERMINATED)
+ {
+ }
+
+ void SetData(const nsDependentString::char_type* aData,
+ nsDependentString::size_type aLength) {
+ MOZ_ASSERT(mFlags == nsDependentString::F_TERMINATED);
+ mData = aData;
+ mLength = aLength;
+ }
+
+ void Truncate() {
+ mData = nsDependentString::char_traits::sEmptyBuffer;
+ mLength = 0;
+ }
+
+ void SetNull() {
+ Truncate();
+ mFlags |= nsDependentString::F_VOIDED;
+ }
+
+ const nsAString* ToAStringPtr() const {
+ return reinterpret_cast<const nsDependentString*>(this);
+ }
+
+ nsAString* ToAStringPtr() {
+ return reinterpret_cast<nsDependentString*>(this);
+ }
+
+ operator const nsAString& () const {
+ return *reinterpret_cast<const nsDependentString*>(this);
+ }
+
+private:
+ const nsDependentString::char_type* mData;
+ nsDependentString::size_type mLength;
+ uint32_t mFlags;
+
+ // A class to use for our static asserts to ensure our object layout
+ // matches that of nsDependentString.
+ class DependentStringAsserter;
+ friend class DependentStringAsserter;
+
+ class DepedentStringAsserter : public nsDependentString {
+ public:
+ static void StaticAsserts() {
+ MOZ_STATIC_ASSERT(sizeof(FakeDependentString) == sizeof(nsDependentString),
+ "Must have right object size");
+ MOZ_STATIC_ASSERT(offsetof(FakeDependentString, mData) ==
+ offsetof(DepedentStringAsserter, mData),
+ "Offset of mData should match");
+ MOZ_STATIC_ASSERT(offsetof(FakeDependentString, mLength) ==
+ offsetof(DepedentStringAsserter, mLength),
+ "Offset of mLength should match");
+ MOZ_STATIC_ASSERT(offsetof(FakeDependentString, mFlags) ==
+ offsetof(DepedentStringAsserter, mFlags),
+ "Offset of mFlags should match");
+ }
+ };
+};
+
+enum StringificationBehavior {
+ eStringify,
+ eEmpty,
+ eNull
+};
+
+// pval must not be null and must point to a rooted JS::Value
+static inline bool
+ConvertJSValueToString(JSContext* cx, const JS::Value& v, JS::Value* pval,
+ StringificationBehavior nullBehavior,
+ StringificationBehavior undefinedBehavior,
+ FakeDependentString& result)
+{
+ JSString *s;
+ if (v.isString()) {
+ s = v.toString();
+ } else {
+ StringificationBehavior behavior;
+ if (v.isNull()) {
+ behavior = nullBehavior;
+ } else if (v.isUndefined()) {
+ behavior = undefinedBehavior;
+ } else {
+ behavior = eStringify;
+ }
+
+ if (behavior != eStringify) {
+ if (behavior == eEmpty) {
+ result.Truncate();
+ } else {
+ result.SetNull();
+ }
+ return true;
+ }
+
+ s = JS_ValueToString(cx, v);
+ if (!s) {
+ return false;
+ }
+ pval->setString(s); // Root the new string.
+ }
+
+ size_t len;
+ const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
+ if (!chars) {
+ return false;
+ }
+
+ result.SetData(chars, len);
+ return true;
+}
+
+// Class for representing optional arguments.
+template<typename T>
+class Optional {
+public:
+ Optional() {}
+
+ bool WasPassed() const {
+ return !mImpl.empty();
+ }
+
+ void Construct() {
+ mImpl.construct();
+ }
+
+ template <class T1, class T2>
+ void Construct(const T1 &t1, const T2 &t2) {
+ mImpl.construct(t1, t2);
+ }
+
+ const T& Value() const {
+ return mImpl.ref();
+ }
+
+ T& Value() {
+ return mImpl.ref();
+ }
+
+private:
+ // Forbid copy-construction and assignment
+ Optional(const Optional& other) MOZ_DELETE;
+ const Optional &operator=(const Optional &other) MOZ_DELETE;
+
+ Maybe<T> mImpl;
+};
+
+// Specialization for strings.
+template<>
+class Optional<nsAString> {
+public:
+ Optional() : mPassed(false) {}
+
+ bool WasPassed() const {
+ return mPassed;
+ }
+
+ void operator=(const nsAString* str) {
+ MOZ_ASSERT(str);
+ mStr = str;
+ mPassed = true;
+ }
+
+ void operator=(const FakeDependentString* str) {
+ MOZ_ASSERT(str);
+ mStr = str->ToAStringPtr();
+ mPassed = true;
+ }
+
+ const nsAString& Value() const {
+ MOZ_ASSERT(WasPassed());
+ return *mStr;
+ }
+
+private:
+ // Forbid copy-construction and assignment
+ Optional(const Optional& other) MOZ_DELETE;
+ const Optional &operator=(const Optional &other) MOZ_DELETE;
+
+ bool mPassed;
+ const nsAString* mStr;
+};
+
+// Class for representing sequences in arguments. We use an auto array that can
+// hold 16 elements, to avoid having to allocate in common cases. This needs to
+// be fallible because web content controls the length of the array, and can
+// easily try to create very large lengths.
+template<typename T>
+class Sequence : public AutoFallibleTArray<T, 16>
+{
+public:
+ Sequence() : AutoFallibleTArray<T, 16>() {}
+};
+
+// Class for holding the type of members of a union. The union type has an enum
+// to keep track of which of its UnionMembers has been constructed.
+template<class T>
+class UnionMember {
+ AlignedStorage2<T> storage;
+
+public:
+ T& SetValue() {
+ new (storage.addr()) T();
+ return *storage.addr();
+ }
+ const T& Value() const {
+ return *storage.addr();
+ }
+ void Destroy() {
+ storage.addr()->~T();
+ }
+};
+
+// Implementation of the bits that XrayWrapper needs
+bool
+XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
+ JSPropertyDescriptor* desc,
+ // And the things we need to determine the descriptor
+ Prefable<JSFunctionSpec>* methods,
+ jsid* methodIds,
+ JSFunctionSpec* methodSpecs,
+ size_t methodCount,
+ Prefable<JSPropertySpec>* attributes,
+ jsid* attributeIds,
+ JSPropertySpec* attributeSpecs,
+ size_t attributeCount,
+ Prefable<ConstantSpec>* constants,
+ jsid* constantIds,
+ ConstantSpec* constantSpecs,
+ size_t constantCount);
+
+bool
+XrayEnumerateProperties(JS::AutoIdVector& props,
+ Prefable<JSFunctionSpec>* methods,
+ jsid* methodIds,
+ JSFunctionSpec* methodSpecs,
+ size_t methodCount,
+ Prefable<JSPropertySpec>* attributes,
+ jsid* attributeIds,
+ JSPropertySpec* attributeSpecs,
+ size_t attributeCount,
+ Prefable<ConstantSpec>* constants,
+ jsid* constantIds,
+ ConstantSpec* constantSpecs,
+ size_t constantCount);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_BindingUtils_h__ */
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
new file mode 100644
index 00000000000..f8119bc71f5
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -0,0 +1,28 @@
+# 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/.
+
+# DOM Bindings Configuration.
+#
+# The WebIDL interfaces are defined in dom/webidls. For each such interface,
+# there is a corresponding entry in the configuration table below.
+# The configuration table maps each interface name to a |descriptor|.
+#
+# Valid fields for all descriptors:
+# * createGlobal: True for global objects.
+# * outerObjectHook: string to use in place of default value for outerObject and thisObject
+# JS class hooks
+
+DOMInterfaces = {
+
+'EventListener': {
+ 'nativeType': 'EventListenerBinding::EventListener',
+},
+'Window': {
+ 'outerObjectHook': 'Some(bindings::utils::outerize_global)',
+},
+
+#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
+'TestBinding': {},
+
+}
diff --git a/components/script/dom/bindings/codegen/Codegen.py b/components/script/dom/bindings/codegen/Codegen.py
new file mode 100644
index 00000000000..6d2cc0bde36
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Codegen.py
@@ -0,0 +1,5788 @@
+# 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/.
+
+# Common codegen classes.
+
+import os
+import string
+import operator
+
+from WebIDL import *
+from Configuration import NoSuchDescriptorError
+
+AUTOGENERATED_WARNING_COMMENT = \
+ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
+ADDPROPERTY_HOOK_NAME = '_addProperty'
+FINALIZE_HOOK_NAME = '_finalize'
+TRACE_HOOK_NAME = '_trace'
+CONSTRUCT_HOOK_NAME = '_constructor'
+HASINSTANCE_HOOK_NAME = '_hasInstance'
+
+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.
+ """
+ oldFileContents = ""
+ try:
+ oldFile = open(filename, 'rb')
+ oldFileContents = ''.join(oldFile.readlines())
+ oldFile.close()
+ except:
+ pass
+
+ if newContents == oldFileContents:
+ return False
+
+ f = open(filename, 'wb')
+ f.write(newContents)
+ f.close()
+
+def toStringBool(arg):
+ return str(not not arg).lower()
+
+def toBindingNamespace(arg):
+ return re.sub("((_workers)?$)", "Binding\\1", arg);
+
+class CGThing():
+ """
+ Abstract base class for things that spit out code.
+ """
+ def __init__(self):
+ pass # Nothing for now
+ def declare(self):
+ """Produce code for a header file."""
+ assert(False) # Override me!
+ def define(self):
+ """Produce code for a cpp file."""
+ assert(False) # Override me!
+
+class CGNativePropertyHooks(CGThing):
+ """
+ Generate a NativePropertyHooks for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ if self.descriptor.workers:
+ return ""
+ return "extern const NativePropertyHooks NativeHooks;\n"
+ def define(self):
+ if self.descriptor.workers:
+ return ""
+ if self.descriptor.concrete and self.descriptor.proxy:
+ resolveOwnProperty = "ResolveOwnProperty"
+ enumerateOwnProperties = "EnumerateOwnProperties"
+ else:
+ enumerateOwnProperties = resolveOwnProperty = "NULL"
+ parent = self.descriptor.interface.parent
+ parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks"
+ if parent else 'NULL')
+ return """
+const NativePropertyHooks NativeHooks = { %s, ResolveProperty, %s, EnumerateProperties, %s };
+""" % (resolveOwnProperty, enumerateOwnProperties, parentHooks)
+
+def DOMClass(descriptor):
+ protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain]
+ # Pad out the list to the right length with _ID_Count so we
+ # guarantee that all the lists are the same length. _ID_Count
+ # is never the ID of any prototype, so it's safe to use as
+ # padding.
+ protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
+ prototypeChainString = ', '.join(protoList)
+ nativeHooks = "NULL" if descriptor.workers else "&NativeHooks"
+ return """{
+ { %s },
+ %s, %s
+}""" % (prototypeChainString, toStringBool(descriptor.nativeIsISupports),
+ nativeHooks)
+
+class CGDOMJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ return "extern DOMJSClass Class;\n"
+ def define(self):
+ traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else 'NULL'
+ return """
+DOMJSClass Class = {
+ { "%s",
+ JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1),
+ %s, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ %s, /* finalize */
+ NULL, /* checkAccess */
+ NULL, /* call */
+ NULL, /* hasInstance */
+ NULL, /* construct */
+ %s, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+ },
+ %s
+};
+""" % (self.descriptor.interface.identifier.name,
+ ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers and self.descriptor.wrapperCache else 'JS_PropertyStub',
+ FINALIZE_HOOK_NAME, traceHook,
+ CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
+
+class CGPrototypeJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ # We're purely for internal consumption
+ return ""
+ def define(self):
+ return """static JSClass PrototypeClass = {
+ "%sPrototype",
+ JSCLASS_HAS_RESERVED_SLOTS(1),
+ JS_PropertyStub, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ NULL, /* finalize */
+ NULL, /* checkAccess */
+ NULL, /* call */
+ NULL, /* hasInstance */
+ NULL, /* construct */
+ NULL, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (self.descriptor.interface.identifier.name)
+
+class CGInterfaceObjectJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ # We're purely for internal consumption
+ return ""
+ def define(self):
+ if not self.descriptor.hasInstanceInterface:
+ return ""
+ ctorname = "NULL" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
+ hasinstance = HASINSTANCE_HOOK_NAME
+ return """
+static JSClass InterfaceObjectClass = {
+ "Function", 0,
+ JS_PropertyStub, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ NULL, /* finalize */
+ NULL, /* checkAccess */
+ %s, /* call */
+ %s, /* hasInstance */
+ %s, /* construct */
+ NULL, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (ctorname, hasinstance, ctorname)
+
+class CGList(CGThing):
+ """
+ Generate code for a list of GCThings. Just concatenates them together, with
+ an optional joiner string. "\n" is a common joiner.
+ """
+ def __init__(self, children, joiner=""):
+ CGThing.__init__(self)
+ self.children = children
+ self.joiner = joiner
+ def append(self, child):
+ self.children.append(child)
+ def prepend(self, child):
+ self.children.insert(0, child)
+ def join(self, generator):
+ return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator)))
+ def declare(self):
+ return self.join(child.declare() for child in self.children if child is not None)
+ def define(self):
+ return self.join(child.define() for child in self.children if child is not None)
+
+class CGGeneric(CGThing):
+ """
+ A class that spits out a fixed string into the codegen. Can spit out a
+ separate string for the declaration too.
+ """
+ def __init__(self, define="", declare=""):
+ self.declareText = declare
+ self.defineText = define
+ def declare(self):
+ return self.declareText
+ def define(self):
+ return self.defineText
+
+# 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.
+lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
+class CGIndenter(CGThing):
+ """
+ A class that takes another CGThing and generates code that indents that
+ CGThing by some number of spaces. The default indent is two spaces.
+ """
+ def __init__(self, child, indentLevel=2, declareOnly=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.indent = " " * indentLevel
+ self.declareOnly = declareOnly
+ def declare(self):
+ decl = self.child.declare()
+ if decl is not "":
+ return re.sub(lineStartDetector, self.indent, decl)
+ else:
+ return ""
+ def define(self):
+ defn = self.child.define()
+ if defn is not "" and not self.declareOnly:
+ return re.sub(lineStartDetector, self.indent, defn)
+ else:
+ return defn
+
+class CGWrapper(CGThing):
+ """
+ Generic CGThing that wraps other CGThings with pre and post text.
+ """
+ def __init__(self, child, pre="", post="", declarePre=None,
+ declarePost=None, definePre=None, definePost=None,
+ declareOnly=False, defineOnly=False, reindent=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.declarePre = declarePre or pre
+ self.declarePost = declarePost or post
+ self.definePre = definePre or pre
+ self.definePost = definePost or post
+ self.declareOnly = declareOnly
+ self.defineOnly = defineOnly
+ self.reindent = reindent
+ def declare(self):
+ if self.defineOnly:
+ return ''
+ decl = self.child.declare()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ decl = stripTrailingWhitespace(
+ decl.replace("\n", "\n" + (" " * len(self.declarePre))))
+ return self.declarePre + decl + self.declarePost
+ def define(self):
+ if self.declareOnly:
+ return ''
+ defn = self.child.define()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ defn = stripTrailingWhitespace(
+ defn.replace("\n", "\n" + (" " * len(self.definePre))))
+ return self.definePre + defn + self.definePost
+
+class CGIfWrapper(CGWrapper):
+ def __init__(self, child, condition):
+ pre = CGWrapper(CGGeneric(condition), pre="if (", post=") {\n",
+ reindent=True)
+ CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
+ post="\n}")
+
+class CGNamespace(CGWrapper):
+ def __init__(self, namespace, child, declareOnly=False):
+ pre = "namespace %s {\n" % namespace
+ post = "} // namespace %s\n" % namespace
+ CGWrapper.__init__(self, child, pre=pre, post=post,
+ declareOnly=declareOnly)
+ @staticmethod
+ def build(namespaces, child, declareOnly=False):
+ """
+ Static helper method to build multiple wrapped namespaces.
+ """
+ if not namespaces:
+ return CGWrapper(child, declareOnly=declareOnly)
+ inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
+ return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
+
+class CGIncludeGuard(CGWrapper):
+ """
+ Generates include guards for a header.
+ """
+ def __init__(self, prefix, child):
+ """|prefix| is the filename without the extension."""
+ define = 'mozilla_dom_%s_h__' % prefix
+ CGWrapper.__init__(self, child,
+ declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
+ declarePost='\n#endif // %s\n' % define)
+
+def getTypes(descriptor):
+ """
+ Get all argument and return types for all members of the descriptor
+ """
+ members = [m for m in descriptor.interface.members]
+ if descriptor.interface.ctor():
+ members.append(descriptor.interface.ctor())
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ types = []
+ for s in signatures:
+ assert len(s) == 2
+ (returnType, arguments) = s
+ types.append(returnType)
+ types.extend([a.type for a in arguments])
+
+ types.extend(a.type for a in members if a.isAttr())
+ return types
+
+class CGHeaders(CGWrapper):
+ """
+ Generates the appropriate include statements.
+ """
+ def __init__(self, descriptors, dictionaries, declareIncludes,
+ defineIncludes, child):
+ """
+ Builds a set of includes to cover |descriptors|.
+
+ Also includes the files in |declareIncludes| in the header
+ file and the files in |defineIncludes| in the .cpp.
+ """
+
+ # Determine the filenames for which we need headers.
+ interfaceDeps = [d.interface for d in descriptors]
+ ancestors = []
+ for iface in interfaceDeps:
+ while iface.parent:
+ ancestors.append(iface.parent)
+ iface = iface.parent
+ interfaceDeps.extend(ancestors)
+ bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
+
+ # Grab all the implementation declaration files we need.
+ implementationIncludes = set(d.headerFile for d in descriptors)
+
+ # Now find all the things we'll need as arguments because we
+ # need to wrap or unwrap them.
+ bindingHeaders = set()
+ for d in descriptors:
+ types = getTypes(d)
+ for dictionary in dictionaries:
+ curDict = dictionary
+ while curDict:
+ types.extend([m.type for m in curDict.members])
+ curDict = curDict.parent
+
+ for t in types:
+ if t.unroll().isUnion():
+ # UnionConversions.h includes UnionTypes.h
+ bindingHeaders.add("mozilla/dom/UnionConversions.h")
+ elif t.unroll().isInterface():
+ if t.unroll().isSpiderMonkeyInterface():
+ bindingHeaders.add("jsfriendapi.h")
+ bindingHeaders.add("mozilla/dom/TypedArray.h")
+ else:
+ typeDesc = d.getDescriptor(t.unroll().inner.identifier.name)
+ if typeDesc is not None:
+ implementationIncludes.add(typeDesc.headerFile)
+ bindingHeaders.add(self.getDeclarationFilename(typeDesc.interface))
+ elif t.unroll().isDictionary():
+ bindingHeaders.add(self.getDeclarationFilename(t.unroll().inner))
+
+ declareIncludes = set(declareIncludes)
+ for d in dictionaries:
+ if d.parent:
+ declareIncludes.add(self.getDeclarationFilename(d.parent))
+ bindingHeaders.add(self.getDeclarationFilename(d))
+
+ # Let the machinery do its thing.
+ def _includeString(includes):
+ return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
+ CGWrapper.__init__(self, child,
+ declarePre=_includeString(sorted(declareIncludes)),
+ definePre=_includeString(sorted(set(defineIncludes) |
+ bindingIncludes |
+ bindingHeaders |
+ implementationIncludes)))
+ @staticmethod
+ def getDeclarationFilename(decl):
+ # Use our local version of the header, not the exported one, so that
+ # test bindings, which don't export, will work correctly.
+ basename = os.path.basename(decl.filename())
+ return basename.replace('.webidl', 'Binding.h')
+
+def SortedTuples(l):
+ """
+ Sort a list of tuples based on the first item in the tuple
+ """
+ return sorted(l, key=operator.itemgetter(0))
+
+def SortedDictValues(d):
+ """
+ Returns a list of values from the dict sorted by key.
+ """
+ # Create a list of tuples containing key and value, sorted on key.
+ d = SortedTuples(d.items())
+ # We're only interested in the values.
+ return (i[1] for i in d)
+
+def UnionTypes(descriptors):
+ """
+ Returns a tuple containing a set of header filenames to include, a set of
+ tuples containing a type declaration and a boolean if the type is a struct
+ for member types of the unions and a CGList containing CGUnionStructs for
+ every union.
+ """
+
+ # Now find all the things we'll need as arguments and return values because
+ # we need to wrap or unwrap them.
+ headers = set()
+ declarations = set()
+ unionStructs = dict()
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+
+ for t in getTypes(d):
+ t = t.unroll()
+ if t.isUnion():
+ name = str(t)
+ if not name in unionStructs:
+ unionStructs[name] = CGUnionStruct(t, d)
+ for f in t.flatMemberTypes:
+ f = f.unroll()
+ if f.isInterface():
+ if f.isSpiderMonkeyInterface():
+ headers.add("jsfriendapi.h")
+ headers.add("mozilla/dom/TypedArray.h")
+ else:
+ typeDesc = d.getDescriptor(f.inner.identifier.name)
+ if typeDesc is not None:
+ declarations.add((typeDesc.nativeType, False))
+ elif f.isDictionary():
+ declarations.add((f.inner.identifier.name, True))
+
+ return (headers, declarations, CGList(SortedDictValues(unionStructs), "\n"))
+
+def UnionConversions(descriptors):
+ """
+ Returns a CGThing to declare all union argument conversion helper structs.
+ """
+ # Now find all the things we'll need as arguments because we
+ # need to unwrap them.
+ unionConversions = dict()
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+
+ def addUnionTypes(type):
+ if type.isUnion():
+ type = type.unroll()
+ name = str(type)
+ if not name in unionConversions:
+ unionConversions[name] = CGUnionConversionStruct(type, d)
+
+ members = [m for m in d.interface.members]
+ if d.interface.ctor():
+ members.append(d.interface.ctor())
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ for s in signatures:
+ assert len(s) == 2
+ (_, arguments) = s
+ for a in arguments:
+ addUnionTypes(a.type)
+
+ for m in members:
+ if m.isAttr() and not m.readonly:
+ addUnionTypes(m.type)
+
+ return CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
+ post="\n\n")
+
+class Argument():
+ """
+ A class for outputting the type and name of an argument
+ """
+ def __init__(self, argType, name):
+ self.argType = argType
+ self.name = name
+ def __str__(self):
+ return self.argType + ' ' + self.name
+
+class CGAbstractMethod(CGThing):
+ """
+ An abstract class for generating code for a method. Subclasses
+ should override definition_body to create the actual code.
+
+ descriptor is the descriptor for the interface the method is associated with
+
+ name is the name of the method as a string
+
+ returnType is the IDLType of the return value
+
+ args is a list of Argument objects
+
+ inline should be True to generate an inline method, whose body is
+ part of the declaration.
+
+ alwaysInline should be True to generate an inline method annotated with
+ MOZ_ALWAYS_INLINE.
+
+ static should be True to generate a static method, which only has
+ a definition.
+
+ If templateArgs is not None it should be a list of strings containing
+ template arguments, and the function will be templatized using those
+ arguments.
+ """
+ def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.name = name
+ self.returnType = returnType
+ self.args = args
+ self.inline = inline
+ self.alwaysInline = alwaysInline
+ self.static = static
+ self.templateArgs = templateArgs
+ def _argstring(self):
+ return ', '.join([str(a) for a in self.args])
+ def _template(self):
+ if self.templateArgs is None:
+ return ''
+ return 'template <%s>\n' % ', '.join(self.templateArgs)
+ def _decorators(self):
+ decorators = []
+ if self.alwaysInline:
+ decorators.append('MOZ_ALWAYS_INLINE')
+ elif self.inline:
+ decorators.append('inline')
+ if self.static:
+ decorators.append('static')
+ decorators.append(self.returnType)
+ maybeNewline = " " if self.inline else "\n"
+ return ' '.join(decorators) + maybeNewline
+ def declare(self):
+ if self.inline:
+ return self._define()
+ return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring())
+ def _define(self):
+ return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue()
+ def define(self):
+ return "" if self.inline else self._define()
+ def definition_prologue(self):
+ return "%s%s%s(%s)\n{" % (self._template(), self._decorators(),
+ self.name, self._argstring())
+ def definition_epilogue(self):
+ return "\n}\n"
+ def definition_body(self):
+ assert(False) # Override me!
+
+class CGAbstractStaticMethod(CGAbstractMethod):
+ """
+ Abstract base class for codegen of implementation-only (no
+ declaration) static methods.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
+ inline=False, static=True)
+ def declare(self):
+ # We only have implementation
+ return ""
+
+class CGAbstractClassHook(CGAbstractStaticMethod):
+ """
+ Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
+ 'this' unwrapping as it assumes that the unwrapped type is always known.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
+ args)
+
+ def definition_body_prologue(self):
+ return """
+ %s* self = UnwrapDOMObject<%s>(obj, eRegularDOMObject);
+""" % (self.descriptor.nativeType, self.descriptor.nativeType)
+
+ def definition_body(self):
+ return self.definition_body_prologue() + self.generate_code()
+
+ def generate_code(self):
+ # Override me
+ assert(False)
+
+class CGAddPropertyHook(CGAbstractClassHook):
+ """
+ A hook for addProperty, used to preserve our wrapper from GC.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('JSHandleId', 'id'), Argument('JSMutableHandleValue', 'vp')]
+ CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
+ 'JSBool', args)
+
+ def generate_code(self):
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=774279
+ # Using a real trace hook might enable us to deal with non-nsISupports
+ # wrappercached things here.
+ assert self.descriptor.nativeIsISupports
+ return """ nsContentUtils::PreserveWrapper(reinterpret_cast<nsISupports*>(self), self);
+ return true;"""
+
+def finalizeHook(descriptor, hookName, context):
+ if descriptor.customFinalize:
+ return """if (self) {
+ self->%s(%s);
+}""" % (hookName, context)
+ clearWrapper = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else ""
+ if descriptor.workers:
+ release = "self->Release();"
+ else:
+ assert descriptor.nativeIsISupports
+ release = """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
+if (rt) {
+ rt->DeferredRelease(reinterpret_cast<nsISupports*>(self));
+} else {
+ NS_RELEASE(self);
+}"""
+ return clearWrapper + release
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+ """
+ A hook for finalize, used to release our native object.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))).define()
+
+class CGClassTraceHook(CGAbstractClassHook):
+ """
+ A hook to trace through our native object; used for GC and CC
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSTracer*', 'trc'), Argument('JSObject*', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
+ args)
+
+ def generate_code(self):
+ return """ if (self) {
+ self->%s(%s);
+ }""" % (self.name, self.args[0].name)
+
+class CGClassConstructHook(CGAbstractStaticMethod):
+ """
+ JS-visible constructor for our objects
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME,
+ 'JSBool', args)
+ self._ctor = self.descriptor.interface.ctor()
+
+ def define(self):
+ if not self._ctor:
+ return ""
+ return CGAbstractStaticMethod.define(self)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ preamble = """
+ JSObject* obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
+"""
+ if self.descriptor.workers:
+ preArgs = ["cx", "obj"]
+ else:
+ preamble += """
+ nsISupports* global;
+ xpc_qsSelfRef globalRef;
+ {
+ nsresult rv;
+ JS::Value val = OBJECT_TO_JSVAL(obj);
+ rv = xpc_qsUnwrapArg<nsISupports>(cx, val, &global, &globalRef.ptr, &val);
+ if (NS_FAILED(rv)) {
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+ }
+"""
+ preArgs = ["global"]
+
+ name = self._ctor.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ callGenerator = CGMethodCall(preArgs, nativeName, True,
+ self.descriptor, self._ctor)
+ return preamble + callGenerator.define();
+
+class CGClassHasInstanceHook(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('JSMutableHandleValue', 'vp'), Argument('JSBool*', 'bp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
+ 'JSBool', args)
+
+ def define(self):
+ if not self.descriptor.hasInstanceInterface:
+ return ""
+ return CGAbstractStaticMethod.define(self)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ return """ if (!vp.isObject()) {
+ *bp = false;
+ return true;
+ }
+
+ jsval protov;
+ if (!JS_GetProperty(cx, obj, "prototype", &protov))
+ return false;
+ if (!protov.isObject()) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE,
+ "%s");
+ return false;
+ }
+ JSObject *objProto = &protov.toObject();
+
+ JSObject* instance = &vp.toObject();
+ JSObject* proto;
+ if (!JS_GetPrototype(cx, instance, &proto))
+ return false;
+ while (proto) {
+ if (proto == objProto) {
+ *bp = true;
+ return true;
+ }
+ if (!JS_GetPrototype(cx, proto, &proto))
+ return false;
+ }
+
+ nsISupports* native =
+ nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, instance);
+ nsCOMPtr<%s> qiResult = do_QueryInterface(native);
+ *bp = !!qiResult;
+ return true;
+""" % (self.descriptor.name, self.descriptor.hasInstanceInterface)
+
+def isChromeOnly(m):
+ return m.getExtendedAttribute("ChromeOnly")
+
+class PropertyDefiner:
+ """
+ A common superclass for defining things on prototype objects.
+
+ Subclasses should implement generateArray to generate the actual arrays of
+ things we're defining. They should also set self.chrome to the list of
+ things exposed to chrome and self.regular to the list of things exposed to
+ web pages. self.chrome must be a superset of self.regular but also include
+ all the ChromeOnly stuff.
+ """
+ def __init__(self, descriptor, name):
+ self.descriptor = descriptor
+ self.name = name
+ # self.prefCacheData will store an array of (prefname, bool*)
+ # pairs for our bool var caches. generateArray will fill it
+ # in as needed.
+ self.prefCacheData = []
+ def hasChromeOnly(self):
+ return len(self.chrome) > len(self.regular)
+ def hasNonChromeOnly(self):
+ return len(self.regular) > 0
+ def variableName(self, chrome):
+ if chrome and self.hasChromeOnly():
+ return "sChrome" + self.name
+ if self.hasNonChromeOnly():
+ return "s" + self.name
+ return "NULL"
+ def usedForXrays(self, chrome):
+ # We only need Xrays for methods, attributes and constants. And we only
+ # need them for the non-chrome ones if we have no chromeonly things.
+ # Otherwise (we have chromeonly attributes) we need Xrays for the chrome
+ # methods/attributes/constants. Finally, in workers there are no Xrays.
+ return ((self.name is "Methods" or self.name is "Attributes" or
+ self.name is "Constants") and
+ chrome == self.hasChromeOnly() and
+ not self.descriptor.workers)
+
+ def __str__(self):
+ # We only need to generate id arrays for things that will end
+ # up used via ResolveProperty or EnumerateProperties.
+ str = self.generateArray(self.regular, self.variableName(False),
+ self.usedForXrays(False))
+ if self.hasChromeOnly():
+ str += self.generateArray(self.chrome, self.variableName(True),
+ self.usedForXrays(True))
+ return str
+
+ @staticmethod
+ def getControllingPref(interfaceMember):
+ prefName = interfaceMember.getExtendedAttribute("Pref")
+ if prefName is None:
+ return None
+ # It's a list of strings
+ assert(len(prefName) is 1)
+ assert(prefName[0] is not None)
+ return prefName[0]
+
+ def generatePrefableArray(self, array, name, specTemplate, specTerminator,
+ specType, getPref, getDataTuple, doIdArrays):
+ """
+ This method generates our various arrays.
+
+ array is an array of interface members as passed to generateArray
+
+ name is the name as passed to generateArray
+
+ specTemplate is a template for each entry of the spec array
+
+ specTerminator is a terminator for the spec array (inserted every time
+ our controlling pref changes and at the end of the array)
+
+ specType is the actual typename of our spec
+
+ getPref is a callback function that takes an array entry and returns
+ the corresponding pref value.
+
+ getDataTuple is a callback function that takes an array entry and
+ returns a tuple suitable for substitution into specTemplate.
+ """
+
+ # We want to generate a single list of specs, but with specTerminator
+ # inserted at every point where the pref name controlling the member
+ # changes. That will make sure the order of the properties as exposed
+ # on the interface and interface prototype objects does not change when
+ # pref control is added to members while still allowing us to define all
+ # the members in the smallest number of JSAPI calls.
+ assert(len(array) is not 0)
+ lastPref = getPref(array[0]) # So we won't put a specTerminator
+ # at the very front of the list.
+ specs = []
+ prefableSpecs = []
+ if doIdArrays:
+ prefableIds = []
+
+ prefableTemplate = ' { true, &%s[%d] }'
+ prefCacheTemplate = '&%s[%d].enabled'
+ def switchToPref(props, pref):
+ # Remember the info about where our pref-controlled
+ # booleans live.
+ if pref is not None:
+ props.prefCacheData.append(
+ (pref, prefCacheTemplate % (name, len(prefableSpecs)))
+ )
+ # Set up pointers to the new sets of specs and ids
+ # inside prefableSpecs and prefableIds
+ prefableSpecs.append(prefableTemplate %
+ (name + "_specs", len(specs)))
+
+ switchToPref(self, lastPref)
+
+ for member in array:
+ curPref = getPref(member)
+ if lastPref != curPref:
+ # Terminate previous list
+ specs.append(specTerminator)
+ # And switch to our new pref
+ switchToPref(self, curPref)
+ lastPref = curPref
+ # And the actual spec
+ specs.append(specTemplate % getDataTuple(member))
+ specs.append(specTerminator)
+ prefableSpecs.append(" { false, NULL }");
+
+ arrays = (("static %s %s_specs[] = {\n" +
+ ',\n'.join(specs) + "\n" +
+ "};\n\n" +
+ "static Prefable<%s> %s[] = {\n" +
+ ',\n'.join(prefableSpecs) + "\n" +
+ "};\n\n") % (specType, name, specType, name))
+ if doIdArrays:
+ arrays += ("static jsid %s_ids[%i] = { JSID_VOID };\n\n" %
+ (name, len(specs)))
+ return arrays
+
+
+# The length of a method is the maximum of the lengths of the
+# argument lists of all its overloads.
+def methodLength(method):
+ signatures = method.signatures()
+ return max([len(arguments) for (retType, arguments) in signatures])
+
+class MethodDefiner(PropertyDefiner):
+ """
+ A class for defining methods on a prototype object.
+ """
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
+ # We should be able to check for special operations without an
+ # identifier. For now we check if the name starts with __
+ methods = [m for m in descriptor.interface.members if
+ m.isMethod() and m.isStatic() == static and
+ not m.isIdentifierLess()]
+ self.chrome = [{"name": m.identifier.name,
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
+ "pref": PropertyDefiner.getControllingPref(m) }
+ for m in methods]
+ self.regular = [{"name": m.identifier.name,
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
+ "pref": PropertyDefiner.getControllingPref(m) }
+ for m in methods if not isChromeOnly(m)]
+
+ # FIXME Check for an existing iterator on the interface first.
+ if any(m.isGetter() and m.isIndexed() for m in methods):
+ self.chrome.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "pref": None })
+ self.regular.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "pref": None })
+
+ if not descriptor.interface.parent and not static and not descriptor.workers:
+ self.chrome.append({"name": 'QueryInterface',
+ "methodInfo": False,
+ "length": 1,
+ "flags": "0",
+ "pref": None })
+
+ if static:
+ if not descriptor.interface.hasInterfaceObject():
+ # static methods go on the interface object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+ else:
+ if not descriptor.interface.hasInterfacePrototypeObject():
+ # non-static methods go on the interface prototype object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def pref(m):
+ return m["pref"]
+
+ def specData(m):
+ if m.get("methodInfo", True):
+ jitinfo = ("&%s_methodinfo" % m["name"])
+ accessor = "genericMethod"
+ else:
+ jitinfo = "nullptr"
+ accessor = m.get("nativeName", m["name"])
+ return (m["name"], accessor, jitinfo, m["length"], m["flags"])
+
+ return self.generatePrefableArray(
+ array, name,
+ ' JS_FNINFO("%s", %s, %s, %s, %s)',
+ ' JS_FS_END',
+ 'JSFunctionSpec',
+ pref, specData, doIdArrays)
+
+class AttrDefiner(PropertyDefiner):
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.chrome = [m for m in descriptor.interface.members if m.isAttr()]
+ self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def flags(attr):
+ return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
+
+ def getter(attr):
+ native = ("genericLenientGetter" if attr.hasLenientThis()
+ else "genericGetter")
+ return ("{(JSPropertyOp)%(native)s, &%(name)s_getterinfo}"
+ % {"name" : attr.identifier.name,
+ "native" : native})
+
+ def setter(attr):
+ if attr.readonly:
+ return "JSOP_NULLWRAPPER"
+ native = ("genericLenientSetter" if attr.hasLenientThis()
+ else "genericSetter")
+ return ("{(JSStrictPropertyOp)%(native)s, &%(name)s_setterinfo}"
+ % {"name" : attr.identifier.name,
+ "native" : native})
+
+ def specData(attr):
+ return (attr.identifier.name, flags(attr), getter(attr),
+ setter(attr))
+
+ return self.generatePrefableArray(
+ array, name,
+ ' { "%s", 0, %s, %s, %s}',
+ ' { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }',
+ 'JSPropertySpec',
+ PropertyDefiner.getControllingPref, specData, doIdArrays)
+
+class ConstDefiner(PropertyDefiner):
+ """
+ A class for definining constants on the interface object
+ """
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.chrome = [m for m in descriptor.interface.members if m.isConst()]
+ self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def specData(const):
+ return (const.identifier.name,
+ convertConstIDLValueToJSVal(const.value))
+
+ return self.generatePrefableArray(
+ array, name,
+ ' { "%s", %s }',
+ ' { 0, JSVAL_VOID }',
+ 'ConstantSpec',
+ PropertyDefiner.getControllingPref, specData, doIdArrays)
+
+class PropertyArrays():
+ def __init__(self, descriptor):
+ self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
+ self.methods = MethodDefiner(descriptor, "Methods", False)
+ self.attrs = AttrDefiner(descriptor, "Attributes")
+ self.consts = ConstDefiner(descriptor, "Constants")
+
+ @staticmethod
+ def arrayNames():
+ return [ "staticMethods", "methods", "attrs", "consts" ]
+
+ @staticmethod
+ def xrayRelevantArrayNames():
+ return [ "methods", "attrs", "consts" ]
+
+ def hasChromeOnly(self):
+ return reduce(lambda b, a: b or getattr(self, a).hasChromeOnly(),
+ self.arrayNames(), False)
+ def variableNames(self, chrome):
+ names = {}
+ for array in self.arrayNames():
+ names[array] = getattr(self, array).variableName(chrome)
+ return names
+ def __str__(self):
+ define = ""
+ for array in self.arrayNames():
+ define += str(getattr(self, array))
+ return define
+
+class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
+ """
+ Generate the CreateInterfaceObjects method for an interface descriptor.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
+ Argument('JSObject*', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'JSObject*', args)
+ self.properties = properties
+ def definition_body(self):
+ protoChain = self.descriptor.prototypeChain
+ if len(protoChain) == 1:
+ getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
+ else:
+ parentProtoName = self.descriptor.prototypeChain[-2]
+ getParentProto = ("%s::GetProtoObject(aCx, aGlobal, aReceiver)" %
+ toBindingNamespace(parentProtoName))
+
+ needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
+ needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
+
+ # if we don't need to create anything, why are we generating this?
+ assert needInterfaceObject or needInterfacePrototypeObject
+
+ idsToInit = []
+ # There is no need to init any IDs in workers, because worker bindings
+ # don't have Xrays.
+ if not self.descriptor.workers:
+ for var in self.properties.xrayRelevantArrayNames():
+ props = getattr(self.properties, var)
+ # We only have non-chrome ids to init if we have no chrome ids.
+ if props.hasChromeOnly():
+ idsToInit.append(props.variableName(True))
+ elif props.hasNonChromeOnly():
+ idsToInit.append(props.variableName(False))
+ if len(idsToInit) > 0:
+ initIds = CGList(
+ [CGGeneric("!InitIds(aCx, %s, %s_ids)" % (varname, varname)) for
+ varname in idsToInit], ' ||\n')
+ if len(idsToInit) > 1:
+ initIds = CGWrapper(initIds, pre="(", post=")", reindent=True)
+ initIds = CGList(
+ [CGGeneric("%s_ids[0] == JSID_VOID &&" % idsToInit[0]), initIds],
+ "\n")
+ initIds = CGWrapper(initIds, pre="if (", post=") {", reindent=True)
+ initIds = CGList(
+ [initIds,
+ CGGeneric((" %s_ids[0] = JSID_VOID;\n"
+ " return NULL;") % idsToInit[0]),
+ CGGeneric("}")],
+ "\n")
+ else:
+ initIds = None
+
+ prefCacheData = []
+ for var in self.properties.arrayNames():
+ props = getattr(self.properties, var)
+ prefCacheData.extend(props.prefCacheData)
+ if len(prefCacheData) is not 0:
+ prefCacheData = [
+ CGGeneric('Preferences::AddBoolVarCache(%s, "%s");' % (ptr, pref)) for
+ (pref, ptr) in prefCacheData]
+ prefCache = CGWrapper(CGIndenter(CGList(prefCacheData, "\n")),
+ pre=("static bool sPrefCachesInited = false;\n"
+ "if (!sPrefCachesInited) {\n"
+ " sPrefCachesInited = true;\n"),
+ post="\n}")
+ else:
+ prefCache = None
+
+ getParentProto = ("JSObject* parentProto = %s;\n" +
+ "if (!parentProto) {\n" +
+ " return NULL;\n" +
+ "}\n") % getParentProto
+
+ needInterfaceObjectClass = (needInterfaceObject and
+ self.descriptor.hasInstanceInterface)
+ needConstructor = (needInterfaceObject and
+ not self.descriptor.hasInstanceInterface)
+ if self.descriptor.interface.ctor():
+ constructHook = CONSTRUCT_HOOK_NAME
+ constructArgs = methodLength(self.descriptor.interface.ctor())
+ else:
+ constructHook = "ThrowingConstructor"
+ constructArgs = 0
+
+ if self.descriptor.concrete:
+ if self.descriptor.proxy:
+ domClass = "&Class"
+ else:
+ domClass = "&Class.mClass"
+ else:
+ domClass = "nullptr"
+
+ call = """return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,
+ %s, %s, %s, %d,
+ %s,
+ %%(methods)s, %%(attrs)s,
+ %%(consts)s, %%(staticMethods)s,
+ %s);""" % (
+ "&PrototypeClass" if needInterfacePrototypeObject else "NULL",
+ "&InterfaceObjectClass" if needInterfaceObjectClass else "NULL",
+ constructHook if needConstructor else "NULL",
+ constructArgs,
+ domClass,
+ '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL")
+ if self.properties.hasChromeOnly():
+ if self.descriptor.workers:
+ accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
+ else:
+ accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))"
+ chrome = CGIfWrapper(CGGeneric(call % self.properties.variableNames(True)),
+ accessCheck)
+ chrome = CGWrapper(chrome, pre="\n\n")
+ else:
+ chrome = None
+
+ functionBody = CGList(
+ [CGGeneric(getParentProto), initIds, prefCache, chrome,
+ CGGeneric(call % self.properties.variableNames(False))],
+ "\n\n")
+ return CGIndenter(functionBody).define()
+
+class CGGetPerInterfaceObject(CGAbstractMethod):
+ """
+ A method for getting a per-interface object (a prototype object or interface
+ constructor object).
+ """
+ def __init__(self, descriptor, name, idPrefix=""):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
+ Argument('JSObject*', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, name,
+ 'JSObject*', args, inline=True)
+ self.id = idPrefix + "id::" + self.descriptor.name
+ def definition_body(self):
+ return """
+
+ /* aGlobal and aReceiver are usually the same, but they can be different
+ too. For example a sandbox often has an xray wrapper for a window as the
+ prototype of the sandbox's global. In that case aReceiver is the xray
+ wrapper and aGlobal is the sandbox's global.
+ */
+
+ /* Make sure our global is sane. Hopefully we can remove this sometime */
+ if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
+ return NULL;
+ }
+ /* Check to see whether the interface objects are already installed */
+ JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(aGlobal);
+ JSObject* cachedObject = protoOrIfaceArray[%s];
+ if (!cachedObject) {
+ protoOrIfaceArray[%s] = cachedObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
+ }
+
+ /* cachedObject might _still_ be null, but that's OK */
+ return cachedObject;""" % (self.id, self.id)
+
+class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface prototype object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
+ "prototypes::")
+ def definition_body(self):
+ return """
+ /* Get the interface prototype object for this class. This will create the
+ object as needed. */""" + CGGetPerInterfaceObject.definition_body(self)
+
+class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface constructor object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
+ "constructors::")
+ def definition_body(self):
+ return """
+ /* Get the interface object for this class. This will create the object as
+ needed. */""" + CGGetPerInterfaceObject.definition_body(self)
+
+def CheckPref(descriptor, globalName, varName, retval, wrapperCache = None):
+ """
+ Check whether bindings should be enabled for this descriptor. If not, set
+ varName to false and return retval.
+ """
+ if not descriptor.prefable:
+ return ""
+
+ if wrapperCache:
+ wrapperCache = " %s->ClearIsDOMBinding();\n" % (wrapperCache)
+ else:
+ wrapperCache = ""
+
+ failureCode = (" %s = false;\n" +
+ " return %s;") % (varName, retval)
+ return """
+ {
+ XPCWrappedNativeScope* scope =
+ XPCWrappedNativeScope::FindInJSObjectScope(aCx, %s);
+ if (!scope) {
+%s
+ }
+
+ if (!scope->ExperimentalBindingsEnabled()) {
+%s%s
+ }
+ }
+""" % (globalName, failureCode, wrapperCache, failureCode)
+
+class CGDefineDOMInterfaceMethod(CGAbstractMethod):
+ """
+ A method for resolve hooks to try to lazily define the interface object for
+ a given interface.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aReceiver'),
+ Argument('bool*', 'aEnabled')]
+ CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'bool', args)
+
+ def declare(self):
+ if self.descriptor.workers:
+ return ''
+ return CGAbstractMethod.declare(self)
+
+ def define(self):
+ if self.descriptor.workers:
+ return ''
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ if self.descriptor.interface.hasInterfacePrototypeObject():
+ # We depend on GetProtoObject defining an interface constructor
+ # object as needed.
+ getter = "GetProtoObject"
+ else:
+ getter = "GetConstructorObject"
+
+ return (" JSObject* global = JS_GetGlobalForObject(aCx, aReceiver);\n" +
+ CheckPref(self.descriptor, "global", "*aEnabled", "false") +
+ """
+ *aEnabled = true;
+ return !!%s(aCx, global, aReceiver);""" % (getter))
+
+class CGPrefEnabled(CGAbstractMethod):
+ """
+ A method for testing whether the preference controlling this
+ interface is enabled. When it's not, the interface should not be
+ visible on the global.
+ """
+ def __init__(self, descriptor):
+ CGAbstractMethod.__init__(self, descriptor, 'PrefEnabled', 'bool', [])
+
+ def declare(self):
+ return CGAbstractMethod.declare(self)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ return " return %s::PrefEnabled();" % self.descriptor.nativeType
+
+class CGIsMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, 'Is', 'bool', args)
+
+ def definition_body(self):
+ # Non-proxy implementation would check
+ # js::GetObjectJSClass(obj) == &Class.mBase
+ return """ return IsProxy(obj);"""
+
+def CreateBindingJSObject(descriptor, parent):
+ if descriptor.proxy:
+ create = """ JSObject *obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
+ JS::PrivateValue(aObject), proto, %s);
+ if (!obj) {
+ return NULL;
+ }
+
+"""
+ else:
+ create = """ JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, %s);
+ if (!obj) {
+ return NULL;
+ }
+
+ js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
+"""
+ return create % parent
+
+class CGWrapWithCacheMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument(descriptor.nativeType + '*', 'aObject'),
+ Argument('nsWrapperCache*', 'aCache'),
+ Argument('bool*', 'aTriedToWrap')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+
+ def definition_body(self):
+ if self.descriptor.workers:
+ return """ *aTriedToWrap = true;
+ return aObject->GetJSObject();"""
+
+ return """ *aTriedToWrap = true;
+
+ JSObject* parent = WrapNativeParent(aCx, aScope, aObject->GetParentObject());
+ if (!parent) {
+ return NULL;
+ }
+
+ JSAutoCompartment ac(aCx, parent);
+ JSObject* global = JS_GetGlobalForObject(aCx, parent);
+%s
+ JSObject* proto = GetProtoObject(aCx, global, global);
+ if (!proto) {
+ return NULL;
+ }
+
+%s
+ NS_ADDREF(aObject);
+
+ aCache->SetWrapper(obj);
+
+ return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aCache"),
+ CreateBindingJSObject(self.descriptor, "parent"))
+
+class CGWrapMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument('T*', 'aObject'), Argument('bool*', 'aTriedToWrap')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, inline=True, templateArgs=["class T"])
+
+ def definition_body(self):
+ return " return Wrap(aCx, aScope, aObject, aObject, aTriedToWrap);"
+
+class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument(descriptor.nativeType + '*', 'aObject')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+
+ def definition_body(self):
+ return """
+ JSObject* global = JS_GetGlobalForObject(aCx, aScope);
+ JSObject* proto = GetProtoObject(aCx, global, global);
+ if (!proto) {
+ return NULL;
+ }
+
+%s
+ NS_ADDREF(aObject);
+
+ return obj;""" % CreateBindingJSObject(self.descriptor, "global")
+
+builtinNames = {
+ IDLType.Tags.bool: 'bool',
+ IDLType.Tags.int8: 'int8_t',
+ IDLType.Tags.int16: 'int16_t',
+ IDLType.Tags.int32: 'int32_t',
+ IDLType.Tags.int64: 'int64_t',
+ IDLType.Tags.uint8: 'uint8_t',
+ IDLType.Tags.uint16: 'uint16_t',
+ IDLType.Tags.uint32: 'uint32_t',
+ IDLType.Tags.uint64: 'uint64_t',
+ IDLType.Tags.float: 'float',
+ IDLType.Tags.double: 'double'
+}
+
+numericTags = [
+ IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double
+ ]
+
+class CastableObjectUnwrapper():
+ """
+ A class for unwrapping an object named by the "source" argument
+ based on the passed-in descriptor and storing it in a variable
+ called by the name in the "target" argument.
+
+ codeOnFailure is the code to run if unwrapping fails.
+ """
+ def __init__(self, descriptor, source, target, codeOnFailure):
+ assert descriptor.castable
+
+ self.substitution = { "type" : descriptor.nativeType,
+ "protoID" : "prototypes::id::" + descriptor.name,
+ "source" : source,
+ "target" : target,
+ "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define() }
+ if descriptor.hasXPConnectImpls:
+ # We don't use xpc_qsUnwrapThis because it will always throw on
+ # unwrap failure, whereas we want to control whether we throw or
+ # not.
+ self.substitution["codeOnFailure"] = CGIndenter(CGGeneric(string.Template(
+ "${type} *objPtr;\n"
+ "xpc_qsSelfRef objRef;\n"
+ "JS::Value val = JS::ObjectValue(*${source});\n"
+ "nsresult rv = xpc_qsUnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n"
+ "if (NS_FAILED(rv)) {\n"
+ "${codeOnFailure}\n"
+ "}\n"
+ "// We should be castable!\n"
+ "MOZ_ASSERT(!objRef.ptr);\n"
+ "// We should have an object, too!\n"
+ "MOZ_ASSERT(objPtr);\n"
+ "${target} = objPtr;").substitute(self.substitution)), 4).define()
+
+ def __str__(self):
+ return string.Template(
+"""{
+ nsresult rv = UnwrapObject<${protoID}, ${type}>(cx, ${source}, ${target});
+ if (NS_FAILED(rv)) {
+${codeOnFailure}
+ }
+}""").substitute(self.substitution)
+
+class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
+ """
+ As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
+ """
+ def __init__(self, descriptor, source, target):
+ CastableObjectUnwrapper.__init__(self, descriptor, source, target,
+ "return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+
+class CallbackObjectUnwrapper:
+ """
+ A class for unwrapping objects implemented in JS.
+
+ |source| is the JSObject we want to use in native code.
+ |target| is an nsCOMPtr of the appropriate type in which we store the result.
+ """
+ def __init__(self, descriptor, source, target, codeOnFailure=None):
+ if codeOnFailure is None:
+ codeOnFailure = ("return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+ self.descriptor = descriptor
+ self.substitution = { "nativeType" : descriptor.nativeType,
+ "source" : source,
+ "target" : target,
+ "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure)).define() }
+
+ def __str__(self):
+ if self.descriptor.workers:
+ return string.Template(
+ "${target} = ${source};"
+ ).substitute(self.substitution)
+
+ return string.Template(
+ """nsresult rv;
+XPCCallContext ccx(JS_CALLER, cx);
+if (!ccx.IsValid()) {
+ rv = NS_ERROR_XPC_BAD_CONVERT_JS;
+${codeOnFailure}
+}
+
+const nsIID& iid = NS_GET_IID(${nativeType});
+nsRefPtr<nsXPCWrappedJS> wrappedJS;
+rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid,
+ NULL, getter_AddRefs(wrappedJS));
+if (NS_FAILED(rv) || !wrappedJS) {
+${codeOnFailure}
+}
+
+// Use a temp nsCOMPtr for the null-check, because ${target} might be
+// OwningNonNull, not an nsCOMPtr.
+nsCOMPtr<${nativeType}> tmp = do_QueryObject(wrappedJS.get());
+if (!tmp) {
+${codeOnFailure}
+}
+${target} = tmp.forget();""").substitute(self.substitution)
+
+def dictionaryHasSequenceMember(dictionary):
+ return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ dictionary.members) or
+ (dictionary.parent and
+ dictionaryHasSequenceMember(dictionary.parent)))
+
+def typeIsSequenceOrHasSequenceMember(type):
+ if type.nullable():
+ type = type.inner
+ if type.isSequence():
+ return True
+ if type.isArray():
+ elementType = type.inner
+ return typeIsSequenceOrHasSequenceMember(elementType)
+ if type.isDictionary():
+ return dictionaryHasSequenceMember(type.inner)
+ if type.isUnion():
+ return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ type.flatMemberTypes)
+ return False
+
+def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
+ isDefinitelyObject=False,
+ isMember=False,
+ isOptional=False,
+ invalidEnumValueFatal=True,
+ defaultValue=None,
+ treatNullAs="Default",
+ treatUndefinedAs="Default",
+ isEnforceRange=False,
+ isClamp=False):
+ """
+ Get a template for converting a JS value to a native object based on the
+ given type and descriptor. If failureCode is given, then we're actually
+ testing whether we can convert the argument to the desired type. That
+ means that failures to convert due to the JS value being the wrong type of
+ value need to use failureCode instead of throwing exceptions. Failures to
+ convert that are due to JS exceptions (from toString or valueOf methods) or
+ out of memory conditions need to throw exceptions no matter what
+ failureCode is.
+
+ If isDefinitelyObject is True, that means we know the value
+ isObject() and we have no need to recheck that.
+
+ if isMember is True, we're being converted from a property of some
+ JS object, not from an actual method argument, so we can't rely on
+ our jsval being rooted or outliving us in any way. Any caller
+ passing true needs to ensure that it is handled correctly in
+ typeIsSequenceOrHasSequenceMember.
+
+ If isOptional is true, then we are doing conversion of an optional
+ argument with no default value.
+
+ invalidEnumValueFatal controls whether an invalid enum value conversion
+ attempt will throw (if true) or simply return without doing anything (if
+ false).
+
+ 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.
+
+ The return value from this function is a tuple consisting of four things:
+
+ 1) A string representing the conversion code. This will have template
+ substitution performed on it as follows:
+
+ ${val} replaced by an expression for the JS::Value in question
+ ${valPtr} is a pointer to the JS::Value in question
+ ${holderName} replaced by the holder's name, if any
+ ${declName} replaced by the declaration's name
+ ${haveValue} replaced by an expression that evaluates to a boolean
+ for whether we have a JS::Value. Only used when
+ defaultValue is not None.
+
+ 2) A CGThing representing the native C++ type we're converting to
+ (declType). This is allowed to be None if the conversion code is
+ supposed to be used as-is.
+ 3) A CGThing representing the type of a "holder" (holderType) which will
+ hold a possible reference to the C++ thing whose type we returned in #1,
+ or None if no such holder is needed.
+ 4) A boolean indicating whether the caller has to do optional-argument handling.
+ This will only be true if isOptional is true and if the returned template
+ expects both declType and holderType to be wrapped in Optional<>, with
+ ${declName} and ${holderName} adjusted to point to the Value() of the
+ Optional, and Construct() calls to be made on the Optional<>s as needed.
+
+ ${declName} must be in scope before the generated code is entered.
+
+ If holderType is not None then ${holderName} must be in scope
+ before the generated code is entered.
+ """
+ # If we have a defaultValue then we're not actually optional for
+ # purposes of what we need to be declared as.
+ assert(defaultValue is None or not isOptional)
+
+ # Also, we should not have a defaultValue if we know we're an object
+ assert(not isDefinitelyObject or defaultValue is None)
+
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ def onFailureNotAnObject(failureCode):
+ return CGWrapper(CGGeneric(
+ failureCode or
+ 'return ThrowErrorMessage(cx, MSG_NOT_OBJECT);'), post="\n")
+ def onFailureBadType(failureCode, typeName):
+ return CGWrapper(CGGeneric(
+ failureCode or
+ 'return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % typeName), post="\n")
+
+ # A helper function for handling default values. Takes a template
+ # body and the C++ code to set the default value and wraps the
+ # given template body in handling for the default value.
+ def handleDefault(template, setDefault):
+ if defaultValue is None:
+ return template
+ return CGWrapper(
+ CGIndenter(CGGeneric(template)),
+ pre="if (${haveValue}) {\n",
+ post=("\n"
+ "} else {\n"
+ "%s;\n"
+ "}" %
+ CGIndenter(CGGeneric(setDefault)).define())).define()
+
+ # A helper function for handling null default values. Much like
+ # handleDefault, but checks that the default value, if it exists, is null.
+ def handleDefaultNull(template, codeToSetNull):
+ if (defaultValue is not None and
+ not isinstance(defaultValue, IDLNullValue)):
+ raise TypeError("Can't handle non-null default value here")
+ return handleDefault(template, codeToSetNull)
+
+ # A helper function for wrapping up the template body for
+ # possibly-nullable objecty stuff
+ def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+ codeToSetNull, failureCode=None):
+ if not isDefinitelyObject:
+ # Handle the non-object cases by wrapping up the whole
+ # thing in an if cascade.
+ templateBody = (
+ "if (${val}.isObject()) {\n" +
+ CGIndenter(CGGeneric(templateBody)).define() + "\n")
+ if type.nullable():
+ templateBody += (
+ "} else if (${val}.isNullOrUndefined()) {\n"
+ " %s;\n" % codeToSetNull)
+ templateBody += (
+ "} else {\n" +
+ CGIndenter(onFailureNotAnObject(failureCode)).define() +
+ "}")
+ if type.nullable():
+ templateBody = handleDefaultNull(templateBody, codeToSetNull)
+ else:
+ assert(defaultValue is None)
+
+ return templateBody
+
+ assert not (isEnforceRange and isClamp) # These are mutually exclusive
+
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is not None:
+ raise TypeError("Can't handle sequences when failureCode is not None")
+ nullable = type.nullable();
+ # Be very careful not to change "type": we need it later
+ if nullable:
+ elementType = type.inner.inner
+ else:
+ elementType = type.inner
+
+ # We have to be careful with reallocation behavior for arrays. In
+ # particular, if we have a sequence of elements which are themselves
+ # sequences (so nsAutoTArrays) or have sequences as members, we have a
+ # problem. In that case, resizing the outermost nsAutoTarray to the
+ # right size will memmove its elements, but nsAutoTArrays are not
+ # memmovable and hence will end up with pointers to bogus memory, which
+ # is bad. To deal with this, we disallow sequences, arrays,
+ # dictionaries, and unions which contain sequences as sequence item
+ # types. If WebIDL ever adds another container type, we'd have to
+ # disallow it as well.
+ if typeIsSequenceOrHasSequenceMember(elementType):
+ raise TypeError("Can't handle a sequence containing another "
+ "sequence as an element or member of an element. "
+ "See the big comment explaining why.\n%s" %
+ str(type.location))
+
+ (elementTemplate, elementDeclType,
+ elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
+ elementType, descriptorProvider, isMember=True)
+ if dealWithOptional:
+ raise TypeError("Shouldn't have optional things in sequences")
+ if elementHolderType is not None:
+ raise TypeError("Shouldn't need holders for sequences")
+
+ typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >")
+ if nullable:
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >")
+ arrayRef = "${declName}.Value()"
+ else:
+ arrayRef = "${declName}"
+ # If we're optional, the const will come from the Optional
+ mutableTypeName = typeName
+ if not isOptional:
+ typeName = CGWrapper(typeName, pre="const ")
+
+ templateBody = ("""JSObject* seq = &${val}.toObject();\n
+if (!IsArrayLike(cx, seq)) {
+ return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+}
+uint32_t length;
+// JS_GetArrayLength actually works on all objects
+if (!JS_GetArrayLength(cx, seq, &length)) {
+ return false;
+}
+Sequence< %s > &arr = const_cast< Sequence< %s >& >(%s);
+if (!arr.SetCapacity(length)) {
+ return Throw<%s>(cx, NS_ERROR_OUT_OF_MEMORY);
+}
+for (uint32_t i = 0; i < length; ++i) {
+ jsval temp;
+ if (!JS_GetElement(cx, seq, i, &temp)) {
+ return false;
+ }
+""" % (toStringBool(descriptorProvider.workers),
+ elementDeclType.define(),
+ elementDeclType.define(),
+ arrayRef,
+ toStringBool(descriptorProvider.workers)))
+
+ templateBody += CGIndenter(CGGeneric(
+ string.Template(elementTemplate).substitute(
+ {
+ "val" : "temp",
+ "valPtr": "&temp",
+ "declName" : "(*arr.AppendElement())"
+ }
+ ))).define()
+
+ templateBody += "\n}"
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type,
+ "const_cast< %s & >(${declName}).SetNull()" % mutableTypeName.define())
+ return (templateBody, typeName, None, isOptional)
+
+ if type.isUnion():
+ if isMember:
+ raise TypeError("Can't handle unions as members, we have a "
+ "holderType")
+ nullable = type.nullable();
+ if nullable:
+ type = type.inner
+
+ assert(defaultValue is None or
+ (isinstance(defaultValue, IDLNullValue) and nullable))
+
+ unionArgumentObj = "${holderName}"
+ if isOptional or nullable:
+ unionArgumentObj += ".ref()"
+
+ memberTypes = type.flatMemberTypes
+ names = []
+
+ interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ if len(interfaceMemberTypes) > 0:
+ interfaceObject = []
+ for memberType in interfaceMemberTypes:
+ if type.isGeckoInterface():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name)))
+ names.append(name)
+ interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
+ else:
+ interfaceObject = None
+
+ arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
+ if len(arrayObjectMemberTypes) > 0:
+ assert len(arrayObjectMemberTypes) == 1
+ memberType = arrayObjectMemberTypes[0]
+ name = memberType.name
+ arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ # XXX Now we're supposed to check for an array or a platform object
+ # that supports indexed properties... skip that last for now. It's a
+ # bit of a pain.
+ arrayObject = CGWrapper(CGIndenter(arrayObject),
+ pre="if (IsArrayLike(cx, &argObj)) {\n",
+ post="}")
+ names.append(name)
+ else:
+ arrayObject = None
+
+ dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
+ if len(dateObjectMemberTypes) > 0:
+ assert len(dateObjectMemberTypes) == 1
+ memberType = dateObjectMemberTypes[0]
+ name = memberType.name
+ dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${valPtr});\n"
+ "done = true;" % (unionArgumentObj, name))
+ dateObject = CGWrapper(CGIndenter(dateObject),
+ pre="if (JS_ObjectIsDate(cx, &argObj)) {\n",
+ post="\n}")
+ names.append(name)
+ else:
+ dateObject = None
+
+ callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
+ if len(callbackMemberTypes) > 0:
+ assert len(callbackMemberTypes) == 1
+ memberType = callbackMemberTypes[0]
+ name = memberType.name
+ callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ names.append(name)
+ else:
+ callbackObject = None
+
+ dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
+ if len(dictionaryMemberTypes) > 0:
+ raise TypeError("No support for unwrapping dictionaries as member "
+ "of a union")
+ else:
+ dictionaryObject = None
+
+ if callbackObject or dictionaryObject:
+ nonPlatformObject = CGList([callbackObject, dictionaryObject], "\n")
+ nonPlatformObject = CGWrapper(CGIndenter(nonPlatformObject),
+ pre="if (!IsPlatformObject(cx, &argObj)) {\n",
+ post="\n}")
+ else:
+ nonPlatformObject = None
+
+ objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ if len(objectMemberTypes) > 0:
+ object = CGGeneric("%s.SetToObject(&argObj);\n"
+ "done = true;" % unionArgumentObj)
+ else:
+ object = None
+
+ hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object
+ if hasObjectTypes:
+ # If we try more specific object types first then we need to check
+ # whether that succeeded before converting to object.
+ if object and (interfaceObject or arrayObject or dateObject or nonPlatformObject):
+ object = CGWrapper(CGIndenter(object), pre="if (!done) {\n",
+ post=("\n}"))
+
+ if arrayObject or dateObject or nonPlatformObject:
+ # An object can be both an array object and not a platform
+ # object, but we shouldn't have both in the union's members
+ # because they are not distinguishable.
+ assert not (arrayObject and nonPlatformObject)
+ templateBody = CGList([arrayObject, dateObject, nonPlatformObject], " else ")
+ else:
+ templateBody = None
+ if interfaceObject:
+ if templateBody:
+ templateBody = CGList([templateBody, object], "\n")
+ templateBody = CGWrapper(CGIndenter(templateBody),
+ pre="if (!done) {\n", post=("\n}"))
+ templateBody = CGList([interfaceObject, templateBody], "\n")
+ else:
+ templateBody = CGList([templateBody, object], "\n")
+
+ if any([arrayObject, dateObject, nonPlatformObject, object]):
+ templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();"))
+ templateBody = CGWrapper(CGIndenter(templateBody),
+ pre="if (${val}.isObject()) {\n",
+ post="\n}")
+ else:
+ templateBody = CGGeneric()
+
+ otherMemberTypes = filter(lambda t: t.isString() or t.isEnum(),
+ memberTypes)
+ otherMemberTypes.extend(t for t in memberTypes if t.isPrimitive())
+ if len(otherMemberTypes) > 0:
+ assert len(otherMemberTypes) == 1
+ memberType = otherMemberTypes[0]
+ if memberType.isEnum():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ names.append(name)
+ if hasObjectTypes:
+ other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
+ if object:
+ join = " else "
+ else:
+ other = CGWrapper(other, pre="if (!done) ")
+ join = "\n"
+ templateBody = CGList([templateBody, other], join)
+ else:
+ other = None
+
+ templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
+ throw = CGGeneric("if (failed) {\n"
+ " return false;\n"
+ "}\n"
+ "if (!done) {\n"
+ " return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
+ "}" % ", ".join(names))
+ templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
+
+ typeName = type.name
+ argumentTypeName = typeName + "Argument"
+ if nullable:
+ typeName = "Nullable<" + typeName + " >"
+ if isOptional:
+ nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})"
+ else:
+ nonConstDecl = "const_cast<" + typeName + "& >(${declName})"
+ typeName = "const " + typeName
+
+ def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
+ null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n"
+ " %s.SetNull();\n"
+ "}" % (extraConditionForNull, setToNullVar))
+ templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
+ return CGList([null, templateBody], " else ")
+
+ if type.hasNullableType:
+ templateBody = handleNull(templateBody, unionArgumentObj)
+
+ declType = CGGeneric(typeName)
+ holderType = CGGeneric(argumentTypeName)
+ if isOptional:
+ mutableDecl = nonConstDecl + ".Value()"
+ declType = CGWrapper(declType, pre="const Optional<", post=" >")
+ holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ constructDecl = CGGeneric(nonConstDecl + ".Construct();")
+ if nullable:
+ constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ else:
+ constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());")
+ else:
+ mutableDecl = nonConstDecl
+ constructDecl = None
+ if nullable:
+ holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ else:
+ constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});")
+ holderType = None
+
+ templateBody = CGList([constructHolder, templateBody], "\n")
+ if nullable:
+ if defaultValue:
+ assert(isinstance(defaultValue, IDLNullValue))
+ valueMissing = "!(${haveValue}) || "
+ else:
+ valueMissing = ""
+ templateBody = handleNull(templateBody, mutableDecl,
+ extraConditionForNull=valueMissing)
+ templateBody = CGList([constructDecl, templateBody], "\n")
+
+ return templateBody.define(), declType, holderType, False
+
+ if type.isGeckoInterface():
+ assert not isEnforceRange and not isClamp
+
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ # This is an interface that we implement as a concrete class
+ # or an XPCOM interface.
+
+ # Allow null pointers for nullable types and old-binding classes
+ argIsPointer = type.nullable() or type.unroll().inner.isExternal()
+
+ # Sequences and non-worker callbacks have to hold a strong ref to the
+ # thing being passed down.
+ forceOwningType = (descriptor.interface.isCallback() and
+ not descriptor.workers) or isMember
+
+ typeName = descriptor.nativeType
+ typePtr = typeName + "*"
+
+ # Compute a few things:
+ # - declType is the type we want to return as the first element of our
+ # tuple.
+ # - holderType is the type we want to return as the third element
+ # of our tuple.
+
+ # Set up some sensible defaults for these things insofar as we can.
+ holderType = None
+ if argIsPointer:
+ if forceOwningType:
+ declType = "nsRefPtr<" + typeName + ">"
+ else:
+ declType = typePtr
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<" + typeName + ">"
+ else:
+ declType = "NonNull<" + typeName + ">"
+
+ templateBody = ""
+ if descriptor.castable:
+ if descriptor.prefable:
+ raise TypeError("We don't support prefable castable object "
+ "arguments (like %s), because we don't know "
+ "how to handle them being preffed off" %
+ descriptor.interface.identifier.name)
+ if descriptor.interface.isConsequential():
+ raise TypeError("Consequential interface %s being used as an "
+ "argument but flagged as castable" %
+ descriptor.interface.identifier.name)
+ if failureCode is not None:
+ templateBody += str(CastableObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}",
+ failureCode))
+ else:
+ templateBody += str(FailureFatalCastableObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}"))
+ elif descriptor.interface.isCallback():
+ templateBody += str(CallbackObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}",
+ codeOnFailure=failureCode))
+ elif descriptor.workers:
+ templateBody += "${declName} = &${val}.toObject();"
+ else:
+ # Either external, or new-binding non-castable. We always have a
+ # holder for these, because we don't actually know whether we have
+ # to addref when unwrapping or not. So we just pass an
+ # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release
+ # it'll put a non-null pointer in there.
+ if forceOwningType:
+ # Don't return a holderType in this case; our declName
+ # will just own stuff.
+ templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n"
+ else:
+ holderType = "nsRefPtr<" + typeName + ">"
+ templateBody += (
+ "jsval tmpVal = ${val};\n" +
+ typePtr + " tmp;\n"
+ "if (NS_FAILED(xpc_qsUnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
+ templateBody += CGIndenter(onFailureBadType(failureCode,
+ descriptor.interface.identifier.name)).define()
+ templateBody += ("}\n"
+ "MOZ_ASSERT(tmp);\n")
+
+ if not isDefinitelyObject:
+ # Our tmpVal will go out of scope, so we can't rely on it
+ # for rooting
+ templateBody += (
+ "if (tmpVal != ${val} && !${holderName}) {\n"
+ " // We have to have a strong ref, because we got this off\n"
+ " // some random object that might get GCed\n"
+ " ${holderName} = tmp;\n"
+ "}\n")
+
+ # And store our tmp, before it goes out of scope.
+ templateBody += "${declName} = tmp;"
+
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type, "${declName} = NULL",
+ failureCode)
+
+ declType = CGGeneric(declType)
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ return (templateBody, declType, holderType, isOptional)
+
+ if type.isSpiderMonkeyInterface():
+ assert not isEnforceRange and not isClamp
+ if isMember:
+ raise TypeError("Can't handle member arraybuffers or "
+ "arraybuffer views because making sure all the "
+ "objects are properly rooted is hard")
+ name = type.name
+ # By default, we use a Maybe<> to hold our typed array. And in the optional
+ # non-nullable case we want to pass Optional<TypedArray> to consumers, not
+ # Optional<NonNull<TypedArray> >, so jump though some hoops to do that.
+ holderType = "Maybe<%s>" % name
+ constructLoc = "${holderName}"
+ constructMethod = "construct"
+ constructInternal = "ref"
+ if type.nullable():
+ if isOptional:
+ declType = "const Optional<" + name + "*>"
+ else:
+ declType = name + "*"
+ else:
+ if isOptional:
+ declType = "const Optional<" + name + ">"
+ # We don't need a holder in this case
+ holderType = None
+ constructLoc = "(const_cast<Optional<" + name + ">& >(${declName}))"
+ constructMethod = "Construct"
+ constructInternal = "Value"
+ else:
+ declType = "NonNull<" + name + ">"
+ template = (
+ "%s.%s(cx, &${val}.toObject());\n"
+ "if (!%s.%s().inited()) {\n"
+ "%s" # No newline here because onFailureBadType() handles that
+ "}\n" %
+ (constructLoc, constructMethod, constructLoc, constructInternal,
+ CGIndenter(onFailureBadType(failureCode, type.name)).define()))
+ nullableTarget = ""
+ if type.nullable():
+ if isOptional:
+ mutableDecl = "(const_cast<Optional<" + name + "*>& >(${declName}))"
+ template += "%s.Construct();\n" % mutableDecl
+ nullableTarget = "%s.Value()" % mutableDecl
+ else:
+ nullableTarget = "${declName}"
+ template += "%s = ${holderName}.addr();" % nullableTarget
+ elif not isOptional:
+ template += "${declName} = ${holderName}.addr();"
+ template = wrapObjectTemplate(template, isDefinitelyObject, type,
+ "%s = NULL" % nullableTarget,
+ failureCode)
+
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ # We handle all the optional stuff ourselves; no need for caller to do it.
+ return (template, CGGeneric(declType), holderType, False)
+
+ if type.isString():
+ assert not isEnforceRange and not isClamp
+
+ treatAs = {
+ "Default": "eStringify",
+ "EmptyString": "eEmpty",
+ "Null": "eNull"
+ }
+ if type.nullable():
+ # For nullable strings null becomes a null string.
+ treatNullAs = "Null"
+ # For nullable strings undefined becomes a null string unless
+ # specified otherwise.
+ if treatUndefinedAs == "Default":
+ treatUndefinedAs = "Null"
+ nullBehavior = treatAs[treatNullAs]
+ if treatUndefinedAs == "Missing":
+ raise TypeError("We don't support [TreatUndefinedAs=Missing]")
+ undefinedBehavior = treatAs[treatUndefinedAs]
+
+ def getConversionCode(varName):
+ conversionCode = (
+ "if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
+ " return false;\n"
+ "}" % (nullBehavior, undefinedBehavior, varName))
+ if defaultValue is None:
+ return conversionCode
+
+ if isinstance(defaultValue, IDLNullValue):
+ assert(type.nullable())
+ return handleDefault(conversionCode,
+ "%s.SetNull()" % varName)
+ return handleDefault(
+ conversionCode,
+ ("static const PRUnichar data[] = { %s };\n"
+ "%s.SetData(data, ArrayLength(data) - 1)" %
+ (", ".join(["'" + char + "'" for char in defaultValue.value] + ["0"]),
+ varName)))
+
+ if isMember:
+ # We have to make a copy, because our jsval may well not
+ # live as long as our string needs to.
+ declType = CGGeneric("nsString")
+ return (
+ "{\n"
+ " FakeDependentString str;\n"
+ "%s\n"
+ " ${declName} = str;\n"
+ "}\n" % CGIndenter(CGGeneric(getConversionCode("str"))).define(),
+ declType, None, isOptional)
+
+ if isOptional:
+ declType = "Optional<nsAString>"
+ else:
+ declType = "NonNull<nsAString>"
+
+ return (
+ "%s\n"
+ "const_cast<%s&>(${declName}) = &${holderName};" %
+ (getConversionCode("${holderName}"), declType),
+ CGGeneric("const " + declType), CGGeneric("FakeDependentString"),
+ # No need to deal with Optional here; we have handled it already
+ False)
+
+ if type.isEnum():
+ assert not isEnforceRange and not isClamp
+
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments "
+ "yet")
+ enum = type.inner.identifier.name
+ if invalidEnumValueFatal:
+ handleInvalidEnumValueCode = " MOZ_ASSERT(index >= 0);\n"
+ else:
+ handleInvalidEnumValueCode = (
+ " if (index < 0) {\n"
+ " return true;\n"
+ " }\n")
+
+ template = (
+ "{\n"
+ " bool ok;\n"
+ " int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n"
+ " if (!ok) {\n"
+ " return false;\n"
+ " }\n"
+ "%(handleInvalidEnumValueCode)s"
+ " ${declName} = static_cast<%(enumtype)s>(index);\n"
+ "}" % { "enumtype" : enum,
+ "values" : enum + "Values::strings",
+ "invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal),
+ "handleInvalidEnumValueCode" : handleInvalidEnumValueCode })
+
+ if defaultValue is not None:
+ assert(defaultValue.type.tag() == IDLType.Tags.domstring)
+ template = handleDefault(template,
+ ("${declName} = %sValues::%s" %
+ (enum,
+ getEnumValueName(defaultValue.value))))
+ return (template, CGGeneric(enum), None, isOptional)
+
+ if type.isCallback():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member callbacks; need to sort out "
+ "rooting issues")
+ # XXXbz we're going to assume that callback types are always
+ # nullable and always have [TreatNonCallableAsNull] for now.
+ haveCallable = "${val}.isObject() && JS_ObjectIsCallable(cx, &${val}.toObject())"
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ haveCallable = "${haveValue} && " + haveCallable
+ return (
+ "if (%s) {\n"
+ " ${declName} = &${val}.toObject();\n"
+ "} else {\n"
+ " ${declName} = NULL;\n"
+ "}" % haveCallable,
+ CGGeneric("JSObject*"), None, isOptional)
+
+ if type.isAny():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member 'any'; need to sort out "
+ "rooting issues")
+ templateBody = "${declName} = ${val};"
+ templateBody = handleDefaultNull(templateBody,
+ "${declName} = JS::NullValue()")
+ return (templateBody, CGGeneric("JS::Value"), None, isOptional)
+
+ if type.isObject():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member 'object'; need to sort out "
+ "rooting issues")
+ template = wrapObjectTemplate("${declName} = &${val}.toObject();",
+ isDefinitelyObject, type,
+ "${declName} = NULL",
+ failureCode)
+ if type.nullable():
+ declType = CGGeneric("JSObject*")
+ else:
+ declType = CGGeneric("NonNull<JSObject>")
+ return (template, declType, None, isOptional)
+
+ if type.isDictionary():
+ if failureCode is not None:
+ raise TypeError("Can't handle dictionaries when failureCode is not None")
+ # There are no nullable dictionaries
+ assert not type.nullable()
+ # All optional dictionaries always have default values, so we
+ # should be able to assume not isOptional here.
+ assert not isOptional
+
+ typeName = CGDictionary.makeDictionaryName(type.inner,
+ descriptorProvider.workers)
+ actualTypeName = typeName
+ selfRef = "${declName}"
+
+ declType = CGGeneric(actualTypeName)
+
+ # If we're a member of something else, the const
+ # will come from the Optional or our container.
+ if not isMember:
+ declType = CGWrapper(declType, pre="const ")
+ selfRef = "const_cast<%s&>(%s)" % (typeName, selfRef)
+
+ # We do manual default value handling here, because we
+ # actually do want a jsval, and we only handle null anyway
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ val = "(${haveValue}) ? ${val} : JSVAL_NULL"
+ else:
+ val = "${val}"
+
+ template = ("if (!%s.Init(cx, %s)) {\n"
+ " return false;\n"
+ "}" % (selfRef, val))
+
+ return (template, declType, None, False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need conversion for argument type '%s'" % str(type))
+
+ typeName = builtinNames[type.tag()]
+
+ conversionBehavior = "eDefault"
+ if isEnforceRange:
+ conversionBehavior = "eEnforceRange"
+ elif isClamp:
+ conversionBehavior = "eClamp"
+
+ if type.nullable():
+ dataLoc = "${declName}.SetValue()"
+ nullCondition = "${val}.isNullOrUndefined()"
+ if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
+ nullCondition = "!(${haveValue}) || " + nullCondition
+ template = (
+ "if (%s) {\n"
+ " ${declName}.SetNull();\n"
+ "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
+ " return false;\n"
+ "}" % (nullCondition, typeName, conversionBehavior, dataLoc))
+ declType = CGGeneric("Nullable<" + typeName + ">")
+ else:
+ assert(defaultValue is None or
+ not isinstance(defaultValue, IDLNullValue))
+ dataLoc = "${declName}"
+ template = (
+ "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
+ " return false;\n"
+ "}" % (typeName, conversionBehavior, dataLoc))
+ declType = CGGeneric(typeName)
+ if (defaultValue is not None and
+ # We already handled IDLNullValue, so just deal with the other ones
+ not isinstance(defaultValue, IDLNullValue)):
+ tag = defaultValue.type.tag()
+ if tag in numericTags:
+ defaultStr = defaultValue.value
+ else:
+ assert(tag == IDLType.Tags.bool)
+ defaultStr = toStringBool(defaultValue.value)
+ template = CGWrapper(CGIndenter(CGGeneric(template)),
+ pre="if (${haveValue}) {\n",
+ post=("\n"
+ "} else {\n"
+ " %s = %s;\n"
+ "}" % (dataLoc, defaultStr))).define()
+
+ return (template, declType, None, isOptional)
+
+def instantiateJSToNativeConversionTemplate(templateTuple, replacements,
+ argcAndIndex=None):
+ """
+ Take a tuple as returned by getJSToNativeConversionTemplate and a set of
+ replacements as required by the strings in such a tuple, and generate code
+ to convert into stack C++ types.
+
+ If argcAndIndex is not None it must be a dict that can be used to
+ replace ${argc} and ${index}, where ${index} is the index of this
+ argument (0-based) and ${argc} is the total number of arguments.
+ """
+ (templateBody, declType, holderType, dealWithOptional) = templateTuple
+
+ if dealWithOptional and argcAndIndex is None:
+ raise TypeError("Have to deal with optional things, but don't know how")
+ if argcAndIndex is not None and declType is None:
+ raise TypeError("Need to predeclare optional things, so they will be "
+ "outside the check for big enough arg count!");
+
+ result = CGList([], "\n")
+ # Make a copy of "replacements" since we may be about to start modifying it
+ replacements = dict(replacements)
+ originalHolderName = replacements["holderName"]
+ if holderType is not None:
+ if dealWithOptional:
+ replacements["holderName"] = (
+ "const_cast< %s & >(%s.Value())" %
+ (holderType.define(), originalHolderName))
+ mutableHolderType = CGWrapper(holderType, pre="Optional< ", post=" >")
+ holderType = CGWrapper(mutableHolderType, pre="const ")
+ result.append(
+ CGList([holderType, CGGeneric(" "),
+ CGGeneric(originalHolderName),
+ CGGeneric(";")]))
+
+ originalDeclName = replacements["declName"]
+ if declType is not None:
+ if dealWithOptional:
+ replacements["declName"] = (
+ "const_cast< %s & >(%s.Value())" %
+ (declType.define(), originalDeclName))
+ mutableDeclType = CGWrapper(declType, pre="Optional< ", post=" >")
+ declType = CGWrapper(mutableDeclType, pre="const ")
+ result.append(
+ CGList([declType, CGGeneric(" "),
+ CGGeneric(originalDeclName),
+ CGGeneric(";")]))
+
+ conversion = CGGeneric(
+ string.Template(templateBody).substitute(replacements)
+ )
+
+ if argcAndIndex is not None:
+ if dealWithOptional:
+ declConstruct = CGIndenter(
+ CGGeneric("const_cast< %s &>(%s).Construct();" %
+ (mutableDeclType.define(), originalDeclName)))
+ if holderType is not None:
+ holderConstruct = CGIndenter(
+ CGGeneric("const_cast< %s &>(%s).Construct();" %
+ (mutableHolderType.define(), originalHolderName)))
+ else:
+ holderConstruct = None
+ else:
+ declConstruct = None
+ holderConstruct = None
+
+ conversion = CGList(
+ [CGGeneric(
+ string.Template("if (${index} < ${argc}) {").substitute(
+ argcAndIndex
+ )),
+ declConstruct,
+ holderConstruct,
+ CGIndenter(conversion),
+ CGGeneric("}")],
+ "\n")
+
+ result.append(conversion)
+ # Add an empty CGGeneric to get an extra newline after the argument
+ # conversion.
+ result.append(CGGeneric(""))
+ return result;
+
+def convertConstIDLValueToJSVal(value):
+ if isinstance(value, IDLNullValue):
+ return "JSVAL_NULL"
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return "INT_TO_JSVAL(%s)" % (value.value)
+ if tag == IDLType.Tags.uint32:
+ return "UINT_TO_JSVAL(%s)" % (value.value)
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
+ return "DOUBLE_TO_JSVAL(%s)" % (value.value)
+ if tag == IDLType.Tags.bool:
+ return "JSVAL_TRUE" if value.value else "JSVAL_FALSE"
+ if tag in [IDLType.Tags.float, IDLType.Tags.double]:
+ return "DOUBLE_TO_JSVAL(%s)" % (value.value)
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGArgumentConverter(CGThing):
+ """
+ A class that takes an IDL argument object, its index in the
+ argument list, and the argv and argc strings and generates code to
+ unwrap the argument to the right native type.
+ """
+ def __init__(self, argument, index, argv, argc, descriptorProvider,
+ invalidEnumValueFatal=True):
+ CGThing.__init__(self)
+ self.argument = argument
+ if argument.variadic:
+ raise TypeError("We don't support variadic arguments yet " +
+ str(argument.location))
+ assert(not argument.defaultValue or argument.optional)
+
+ replacer = {
+ "index" : index,
+ "argc" : argc,
+ "argv" : argv
+ }
+ self.replacementVariables = {
+ "declName" : "arg%d" % index,
+ "holderName" : ("arg%d" % index) + "_holder"
+ }
+ self.replacementVariables["val"] = string.Template(
+ "${argv}[${index}]"
+ ).substitute(replacer)
+ self.replacementVariables["valPtr"] = (
+ "&" + self.replacementVariables["val"])
+ if argument.defaultValue:
+ self.replacementVariables["haveValue"] = string.Template(
+ "${index} < ${argc}").substitute(replacer)
+ self.descriptorProvider = descriptorProvider
+ if self.argument.optional and not self.argument.defaultValue:
+ self.argcAndIndex = replacer
+ else:
+ self.argcAndIndex = None
+ self.invalidEnumValueFatal = invalidEnumValueFatal
+
+ def define(self):
+ return instantiateJSToNativeConversionTemplate(
+ getJSToNativeConversionTemplate(self.argument.type,
+ self.descriptorProvider,
+ isOptional=(self.argcAndIndex is not None),
+ invalidEnumValueFatal=self.invalidEnumValueFatal,
+ defaultValue=self.argument.defaultValue,
+ treatNullAs=self.argument.treatNullAs,
+ treatUndefinedAs=self.argument.treatUndefinedAs,
+ isEnforceRange=self.argument.enforceRange,
+ isClamp=self.argument.clamp),
+ self.replacementVariables,
+ self.argcAndIndex).define()
+
+def getWrapTemplateForType(type, descriptorProvider, result, successCode,
+ isCreator):
+ """
+ Reflect a C++ value stored in "result", of IDL type "type" into JS. The
+ "successCode" is the code to run once we have successfully done the
+ conversion. The resulting string should be used with string.Template, it
+ needs the following keys when substituting: jsvalPtr/jsvalRef/obj.
+
+ Returns (templateString, infallibility of conversion template)
+ """
+ haveSuccessCode = successCode is not None
+ if not haveSuccessCode:
+ successCode = "return true;"
+
+ def setValue(value, callWrapValue=False):
+ """
+ Returns the code to set the jsval to value. If "callWrapValue" is true
+ JS_WrapValue will be called on the jsval.
+ """
+ if not callWrapValue:
+ tail = successCode
+ elif haveSuccessCode:
+ tail = ("if (!JS_WrapValue(cx, ${jsvalPtr})) {\n" +
+ " return false;\n" +
+ "}\n" +
+ successCode)
+ else:
+ tail = "return JS_WrapValue(cx, ${jsvalPtr});"
+ return ("${jsvalRef} = %s;\n" +
+ tail) % (value)
+
+ def wrapAndSetPtr(wrapCall, failureCode=None):
+ """
+ Returns the code to set the jsval by calling "wrapCall". "failureCode"
+ is the code to run if calling "wrapCall" fails
+ """
+ if failureCode is None:
+ if not haveSuccessCode:
+ return "return " + wrapCall + ";"
+ failureCode = "return false;"
+ str = ("if (!%s) {\n" +
+ CGIndenter(CGGeneric(failureCode)).define() + "\n" +
+ "}\n" +
+ successCode) % (wrapCall)
+ return str
+
+ if type is None or type.isVoid():
+ return (setValue("JSVAL_VOID"), True)
+
+ if type.isArray():
+ raise TypeError("Can't handle array return values yet")
+
+ if type.isSequence():
+ if type.nullable():
+ # Nullable sequences are Nullable< nsTArray<T> >
+ (recTemplate, recInfall) = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ isCreator)
+ return ("""
+if (%s.IsNull()) {
+%s
+}
+%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(), recTemplate), recInfall)
+
+ # Now do non-nullable sequences. We use setting the element
+ # in the array as our succcess code because when we succeed in
+ # wrapping that's what we should do.
+ innerTemplate = wrapForType(
+ type.inner, descriptorProvider,
+ {
+ 'result' : "%s[i]" % result,
+ 'successCode': ("if (!JS_DefineElement(cx, returnArray, i, tmp,\n"
+ " NULL, NULL, JSPROP_ENUMERATE)) {\n"
+ " return false;\n"
+ "}"),
+ 'jsvalRef': "tmp",
+ 'jsvalPtr': "&tmp",
+ 'isCreator': isCreator
+ }
+ )
+ innerTemplate = CGIndenter(CGGeneric(innerTemplate)).define()
+ return (("""
+uint32_t length = %s.Length();
+JSObject *returnArray = JS_NewArrayObject(cx, length, NULL);
+if (!returnArray) {
+ return false;
+}
+jsval tmp;
+for (uint32_t i = 0; i < length; ++i) {
+%s
+}\n""" % (result, innerTemplate)) + setValue("JS::ObjectValue(*returnArray)"), False)
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
+ if type.nullable():
+ wrappingCode = ("if (!%s) {\n" % (result) +
+ CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
+ "}\n")
+ else:
+ wrappingCode = ""
+ if (not descriptor.interface.isExternal() and
+ not descriptor.interface.isCallback()):
+ if descriptor.wrapperCache:
+ wrapMethod = "WrapNewBindingObject"
+ else:
+ if not isCreator:
+ raise MethodNotCreatorError(descriptor.interface.identifier.name)
+ wrapMethod = "WrapNewBindingNonWrapperCachedObject"
+ wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
+ # We don't support prefable stuff in workers.
+ assert(not descriptor.prefable or not descriptor.workers)
+ if not descriptor.prefable:
+ # Non-prefable bindings can only fail to wrap as a new-binding object
+ # if they already threw an exception. Same thing for
+ # non-prefable bindings.
+ failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
+ "return false;")
+ else:
+ if descriptor.notflattened:
+ raise TypeError("%s is prefable but not flattened; "
+ "fallback won't work correctly" %
+ descriptor.interface.identifier.name)
+ # Try old-style wrapping for bindings which might be preffed off.
+ failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalPtr})" % result)
+ wrappingCode += wrapAndSetPtr(wrap, failed)
+ else:
+ if descriptor.notflattened:
+ getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
+ else:
+ getIID = ""
+ wrap = "WrapObject(cx, ${obj}, %s, %s${jsvalPtr})" % (result, getIID)
+ wrappingCode += wrapAndSetPtr(wrap)
+ return (wrappingCode, False)
+
+ if type.isString():
+ if type.nullable():
+ return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalPtr})" % result), False)
+ else:
+ return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr})" % result), False)
+
+ if type.isEnum():
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated return types "
+ "yet")
+ return ("""MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s));
+JSString* %(resultStr)s = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length);
+if (!%(resultStr)s) {
+ return false;
+}
+""" % { "result" : result,
+ "resultStr" : result + "_str",
+ "strings" : type.inner.identifier.name + "Values::strings" } +
+ setValue("JS::StringValue(%s_str)" % result), False)
+
+ if type.isCallback():
+ assert not type.isInterface()
+ # XXXbz we're going to assume that callback types are always
+ # nullable and always have [TreatNonCallableAsNull] for now.
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue("JS::ObjectOrNullValue(%s)" % result, True), False)
+
+ if type.tag() == IDLType.Tags.any:
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue(result, True), False)
+
+ if type.isObject() or type.isSpiderMonkeyInterface():
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ if type.nullable():
+ toValue = "JS::ObjectOrNullValue(%s)"
+ else:
+ toValue = "JS::ObjectValue(*%s)"
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue(toValue % result, True), False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need to learn to wrap %s" % type)
+
+ if type.nullable():
+ (recTemplate, recInfal) = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ isCreator)
+ return ("if (%s.IsNull()) {\n" % result +
+ CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
+ "}\n" + recTemplate, recInfal)
+
+ tag = type.tag()
+
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return (setValue("INT_TO_JSVAL(int32_t(%s))" % result), True)
+
+ elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, IDLType.Tags.float,
+ IDLType.Tags.double]:
+ # XXXbz will cast to double do the "even significand" thing that webidl
+ # calls for for 64-bit ints? Do we care?
+ return (setValue("JS_NumberValue(double(%s))" % result), True)
+
+ elif tag == IDLType.Tags.uint32:
+ return (setValue("UINT_TO_JSVAL(%s)" % result), True)
+
+ elif tag == IDLType.Tags.bool:
+ return (setValue("BOOLEAN_TO_JSVAL(%s)" % result), True)
+
+ else:
+ raise TypeError("Need to learn to wrap primitive: %s" % type)
+
+def wrapForType(type, descriptorProvider, templateValues):
+ """
+ Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
+ that should contain:
+
+ * 'jsvalRef': a C++ reference to the jsval in which to store the result of
+ the conversion
+ * 'jsvalPtr': a C++ pointer to the jsval in which to store the result of
+ the conversion
+ * 'obj' (optional): the name of the variable that contains the JSObject to
+ use as a scope when wrapping, if not supplied 'obj'
+ will be used as the name
+ * 'result' (optional): the name of the variable in which the C++ value is
+ stored, if not supplied 'result' will be used as
+ the name
+ * 'successCode' (optional): the code to run once we have successfully done
+ the conversion, if not supplied 'return true;'
+ will be used as the code
+ * 'isCreator' (optional): If true, we're wrapping for the return value of
+ a [Creator] method. Assumed false if not set.
+ """
+ wrap = getWrapTemplateForType(type, descriptorProvider,
+ templateValues.get('result', 'result'),
+ templateValues.get('successCode', None),
+ templateValues.get('isCreator', False))[0]
+
+ defaultValues = {'obj': 'obj'}
+ return string.Template(wrap).substitute(defaultValues, **templateValues)
+
+def infallibleForMember(member, type, descriptorProvider):
+ """
+ Determine the fallibility of changing a C++ value of IDL type "type" into
+ JS for the given attribute. Apart from isCreator, all the defaults are used,
+ since the fallbility does not change based on the boolean values,
+ and the template will be discarded.
+
+ CURRENT ASSUMPTIONS:
+ We assume that successCode for wrapping up return values cannot contain
+ failure conditions.
+ """
+ return getWrapTemplateForType(type, descriptorProvider, 'result', None,\
+ memberIsCreator(member))[1]
+
+def typeNeedsCx(type, retVal=False):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ if type.isSequence() or type.isArray():
+ type = type.inner
+ if type.isUnion():
+ return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
+ if retVal and type.isSpiderMonkeyInterface():
+ return True
+ return type.isCallback() or type.isAny() or type.isObject()
+
+# Returns a tuple consisting of a CGThing containing the type of the return
+# value, or None if there is no need for a return value, and a boolean signaling
+# whether the return value is passed in an out parameter.
+def getRetvalDeclarationForType(returnType, descriptorProvider,
+ resultAlreadyAddRefed):
+ if returnType is None or returnType.isVoid():
+ # Nothing to declare
+ return None, False
+ if returnType.isPrimitive() and returnType.tag() in builtinNames:
+ result = CGGeneric(builtinNames[returnType.tag()])
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Nullable<", post=">")
+ return result, False
+ if returnType.isString():
+ return CGGeneric("nsString"), True
+ if returnType.isEnum():
+ if returnType.nullable():
+ raise TypeError("We don't support nullable enum return values")
+ return CGGeneric(returnType.inner.identifier.name), False
+ if returnType.isGeckoInterface():
+ result = CGGeneric(descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name).nativeType)
+ if resultAlreadyAddRefed:
+ result = CGWrapper(result, pre="nsRefPtr<", post=">")
+ else:
+ result = CGWrapper(result, post="*")
+ return result, False
+ if returnType.isCallback():
+ # XXXbz we're going to assume that callback types are always
+ # nullable for now.
+ return CGGeneric("JSObject*"), False
+ if returnType.isAny():
+ return CGGeneric("JS::Value"), False
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return CGGeneric("JSObject*"), False
+ if returnType.isSequence():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ # If our result is already addrefed, use the right type in the
+ # sequence argument here.
+ (result, _) = getRetvalDeclarationForType(returnType.inner,
+ descriptorProvider,
+ resultAlreadyAddRefed)
+ result = CGWrapper(result, pre="nsTArray< ", post=" >")
+ if nullable:
+ result = CGWrapper(result, pre="Nullable< ", post=" >")
+ return result, True
+ raise TypeError("Don't know how to declare return value for %s" %
+ returnType)
+
+def isResultAlreadyAddRefed(descriptor, extendedAttributes):
+ # Default to already_AddRefed on the main thread, raw pointer in workers
+ return not descriptor.workers and not 'resultNotAddRefed' in extendedAttributes
+
+class CGCallGenerator(CGThing):
+ """
+ A class to generate an actual call to a C++ object. Assumes that the C++
+ object is stored in a variable whose name is given by the |object| argument.
+
+ errorReport should be a CGThing for an error report or None if no
+ error reporting is needed.
+ """
+ def __init__(self, errorReport, arguments, argsPre, returnType,
+ extendedAttributes, descriptorProvider, nativeMethodName,
+ static, object="self", declareResult=True):
+ CGThing.__init__(self)
+
+ assert errorReport is None or isinstance(errorReport, CGThing)
+
+ isFallible = errorReport is not None
+
+ resultAlreadyAddRefed = isResultAlreadyAddRefed(descriptorProvider,
+ extendedAttributes)
+ (result, resultOutParam) = getRetvalDeclarationForType(returnType,
+ descriptorProvider,
+ resultAlreadyAddRefed)
+
+ args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
+ for (a, name) in arguments:
+ # This is a workaround for a bug in Apple's clang.
+ if a.type.isObject() and not a.type.nullable() and not a.optional:
+ name = "(JSObject&)" + name
+ args.append(CGGeneric(name))
+
+ # Return values that go in outparams go here
+ if resultOutParam:
+ args.append(CGGeneric("result"))
+ if isFallible:
+ args.append(CGGeneric("rv"))
+
+ needsCx = (typeNeedsCx(returnType, True) or
+ any(typeNeedsCx(a.type) for (a, _) in arguments) or
+ 'implicitJSContext' in extendedAttributes)
+
+ if not "cx" in argsPre and needsCx:
+ args.prepend(CGGeneric("cx"))
+
+ # Build up our actual call
+ self.cgRoot = CGList([], "\n")
+
+ call = CGGeneric(nativeMethodName)
+ if static:
+ call = CGWrapper(call, pre="%s::" % descriptorProvider.nativeType)
+ else:
+ call = CGWrapper(call, pre="%s->" % object)
+ call = CGList([call, CGWrapper(args, pre="(", post=");")])
+ if result is not None:
+ if declareResult:
+ result = CGWrapper(result, post=" result;")
+ self.cgRoot.prepend(result)
+ if not resultOutParam:
+ call = CGWrapper(call, pre="result = ")
+
+ call = CGWrapper(call)
+ self.cgRoot.append(call)
+
+ if isFallible:
+ self.cgRoot.prepend(CGGeneric("ErrorResult rv;"))
+ self.cgRoot.append(CGGeneric("if (rv.Failed()) {"))
+ self.cgRoot.append(CGIndenter(errorReport))
+ self.cgRoot.append(CGGeneric("}"))
+
+ def define(self):
+ return self.cgRoot.define()
+
+class MethodNotCreatorError(Exception):
+ def __init__(self, typename):
+ self.typename = typename
+
+class CGPerSignatureCall(CGThing):
+ """
+ This class handles the guts of generating code for a particular
+ call signature. A call signature consists of four things:
+
+ 1) A return type, which can be None to indicate that there is no
+ actual return value (e.g. this is an attribute setter) or an
+ IDLType if there's an IDL type involved (including |void|).
+ 2) An argument list, which is allowed to be empty.
+ 3) A name of a native method to call.
+ 4) Whether or not this method is static.
+
+ We also need to know whether this is a method or a getter/setter
+ to do error reporting correctly.
+
+ The idlNode parameter can be either a method or an attr. We can query
+ |idlNode.identifier| in both cases, so we can be agnostic between the two.
+ """
+ # XXXbz For now each entry in the argument list is either an
+ # IDLArgument or a FakeArgument, but longer-term we may want to
+ # have ways of flagging things like JSContext* or optional_argc in
+ # there.
+
+ def __init__(self, returnType, argsPre, arguments, nativeMethodName, static,
+ descriptor, idlNode, argConversionStartsAt=0,
+ getter=False, setter=False):
+ CGThing.__init__(self)
+ self.returnType = returnType
+ self.descriptor = descriptor
+ self.idlNode = idlNode
+ self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
+ getter=getter,
+ setter=setter)
+ self.argsPre = argsPre
+ self.arguments = arguments
+ self.argCount = len(arguments)
+ if self.argCount > argConversionStartsAt:
+ # Insert our argv in there
+ cgThings = [CGGeneric(self.getArgvDecl())]
+ else:
+ cgThings = []
+ cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(),
+ self.getArgc(), self.descriptor,
+ invalidEnumValueFatal=not setter) for
+ i in range(argConversionStartsAt, self.argCount)])
+
+ cgThings.append(CGCallGenerator(
+ self.getErrorReport() if self.isFallible() else None,
+ self.getArguments(), self.argsPre, returnType,
+ self.extendedAttributes, descriptor, nativeMethodName,
+ static))
+ self.cgRoot = CGList(cgThings, "\n")
+
+ def getArgv(self):
+ return "argv" if self.argCount > 0 else ""
+ def getArgvDecl(self):
+ return "\nJS::Value* argv = JS_ARGV(cx, vp);\n"
+ def getArgc(self):
+ return "argc"
+ def getArguments(self):
+ return [(a, "arg" + str(i)) for (i, a) in enumerate(self.arguments)]
+
+ def isFallible(self):
+ return not 'infallible' in self.extendedAttributes
+
+ def wrap_return_value(self):
+ isCreator = memberIsCreator(self.idlNode)
+ if isCreator:
+ # We better be returning addrefed things!
+ assert(isResultAlreadyAddRefed(self.descriptor,
+ self.extendedAttributes) or
+ # Workers use raw pointers for new-object return
+ # values or something
+ self.descriptor.workers)
+
+ resultTemplateValues = { 'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+ 'isCreator': isCreator}
+ try:
+ return wrapForType(self.returnType, self.descriptor,
+ resultTemplateValues)
+ except MethodNotCreatorError, err:
+ assert not isCreator
+ raise TypeError("%s being returned from non-creator method or property %s.%s" %
+ (err.typename,
+ self.descriptor.interface.identifier.name,
+ self.idlNode.identifier.name))
+
+ def getErrorReport(self):
+ return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'
+ % (toStringBool(not self.descriptor.workers),
+ self.descriptor.interface.identifier.name,
+ self.idlNode.identifier.name))
+
+ def define(self):
+ return (self.cgRoot.define() + "\n" + self.wrap_return_value())
+
+class CGSwitch(CGList):
+ """
+ A class to generate code for a switch statement.
+
+ Takes three constructor arguments: an expression, a list of cases,
+ and an optional default.
+
+ Each case is a CGCase. The default is a CGThing for the body of
+ the default case, if any.
+ """
+ def __init__(self, expression, cases, default=None):
+ CGList.__init__(self, [CGIndenter(c) for c in cases], "\n")
+ self.prepend(CGWrapper(CGGeneric(expression),
+ pre="switch (", post=") {"));
+ if default is not None:
+ self.append(
+ CGIndenter(
+ CGWrapper(
+ CGIndenter(default),
+ pre="default: {\n",
+ post="\n break;\n}"
+ )
+ )
+ )
+
+ self.append(CGGeneric("}"))
+
+class CGCase(CGList):
+ """
+ A class to generate code for a case statement.
+
+ Takes three constructor arguments: an expression, a CGThing for
+ the body (allowed to be None if there is no body), and an optional
+ argument (defaulting to False) for whether to fall through.
+ """
+ def __init__(self, expression, body, fallThrough=False):
+ CGList.__init__(self, [], "\n")
+ self.append(CGWrapper(CGGeneric(expression), pre="case ", post=": {"))
+ bodyList = CGList([body], "\n")
+ if fallThrough:
+ bodyList.append(CGGeneric("/* Fall through */"))
+ else:
+ bodyList.append(CGGeneric("break;"))
+ self.append(CGIndenter(bodyList));
+ self.append(CGGeneric("}"))
+
+class CGMethodCall(CGThing):
+ """
+ A class to generate selection of a method signature from a set of
+ signatures and generation of a call to that signature.
+ """
+ def __init__(self, argsPre, nativeMethodName, static, descriptor, method):
+ CGThing.__init__(self)
+
+ methodName = '"%s.%s"' % (descriptor.interface.identifier.name, method.identifier.name)
+
+ def requiredArgCount(signature):
+ arguments = signature[1]
+ if len(arguments) == 0:
+ return 0
+ requiredArgs = len(arguments)
+ while requiredArgs and arguments[requiredArgs-1].optional:
+ requiredArgs -= 1
+ return requiredArgs
+
+ def getPerSignatureCall(signature, argConversionStartsAt=0):
+ return CGPerSignatureCall(signature[0], argsPre, signature[1],
+ nativeMethodName, static, descriptor,
+ method, argConversionStartsAt)
+
+
+ signatures = method.signatures()
+ if len(signatures) == 1:
+ # Special case: we can just do a per-signature method call
+ # here for our one signature and not worry about switching
+ # on anything.
+ signature = signatures[0]
+ self.cgRoot = CGList([ CGIndenter(getPerSignatureCall(signature)) ])
+ requiredArgs = requiredArgCount(signature)
+
+
+ if requiredArgs > 0:
+ code = (
+ "if (argc < %d) {\n"
+ " return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n"
+ "}" % (requiredArgs, methodName))
+ self.cgRoot.prepend(
+ CGWrapper(CGIndenter(CGGeneric(code)), pre="\n", post="\n"))
+ return
+
+ # Need to find the right overload
+ maxArgCount = method.maxArgCount
+ allowedArgCounts = method.allowedArgCounts
+
+ argCountCases = []
+ for argCount in allowedArgCounts:
+ possibleSignatures = method.signaturesForArgCount(argCount)
+ if len(possibleSignatures) == 1:
+ # easy case!
+ signature = possibleSignatures[0]
+
+ # (possibly) important optimization: if signature[1] has >
+ # argCount arguments and signature[1][argCount] is optional and
+ # there is only one signature for argCount+1, then the
+ # signature for argCount+1 is just ourselves and we can fall
+ # through.
+ if (len(signature[1]) > argCount and
+ signature[1][argCount].optional and
+ (argCount+1) in allowedArgCounts and
+ len(method.signaturesForArgCount(argCount+1)) == 1):
+ argCountCases.append(
+ CGCase(str(argCount), None, True))
+ else:
+ argCountCases.append(
+ CGCase(str(argCount), getPerSignatureCall(signature)))
+ continue
+
+ distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
+
+ # We can't handle unions 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)
+
+ # Convert all our arguments up to the distinguishing index.
+ # Doesn't matter which of the possible signatures we use, since
+ # they all have the same types up to that point; just use
+ # possibleSignatures[0]
+ caseBody = [CGGeneric("JS::Value* argv_start = JS_ARGV(cx, vp);")]
+ caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i],
+ i, "argv_start", "argc",
+ descriptor) for i in
+ range(0, distinguishingIndex) ])
+
+ # Select the right overload from our set.
+ distinguishingArg = "argv_start[%d]" % distinguishingIndex
+
+ def pickFirstSignature(condition, filterLambda):
+ sigs = filter(filterLambda, possibleSignatures)
+ assert len(sigs) < 2
+ if len(sigs) > 0:
+ if condition is None:
+ caseBody.append(
+ getPerSignatureCall(sigs[0], distinguishingIndex))
+ else:
+ caseBody.append(CGGeneric("if (" + condition + ") {"))
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sigs[0], distinguishingIndex)))
+ caseBody.append(CGGeneric("}"))
+ return True
+ return False
+
+ # First check for null or undefined
+ pickFirstSignature("%s.isNullOrUndefined()" % distinguishingArg,
+ 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()) ]
+ # There might be more than one of these; we need to check
+ # which ones we unwrap to.
+
+ if len(interfacesSigs) > 0:
+ # The spec says that we should check for "platform objects
+ # implementing an interface", but it's enough to guard on these
+ # being an object. The code for unwrapping non-callback
+ # interfaces and typed arrays will just bail out and move on to
+ # the next overload if the object fails to unwrap correctly. We
+ # could even not do the isObject() check up front here, but in
+ # cases where we have multiple object overloads it makes sense
+ # to do it only once instead of for each overload. That will
+ # also allow the unwrapping test to skip having to do codegen
+ # for the null-or-undefined case, which we already handled
+ # above.
+ caseBody.append(CGGeneric("if (%s.isObject()) {" %
+ (distinguishingArg)))
+ for sig in interfacesSigs:
+ caseBody.append(CGIndenter(CGGeneric("do {")));
+ type = sig[1][distinguishingIndex].type
+
+ # The argument at index distinguishingIndex can't possibly
+ # be unset here, because we've already checked that argc is
+ # large enough that we can examine this argument.
+ testCode = instantiateJSToNativeConversionTemplate(
+ getJSToNativeConversionTemplate(type, descriptor,
+ failureCode="break;",
+ isDefinitelyObject=True),
+ {
+ "declName" : "arg%d" % distinguishingIndex,
+ "holderName" : ("arg%d" % distinguishingIndex) + "_holder",
+ "val" : distinguishingArg
+ })
+
+ # Indent by 4, since we need to indent further than our "do" statement
+ caseBody.append(CGIndenter(testCode, 4));
+ # If we got this far, we know we unwrapped to the right
+ # interface, so just do the call. Start conversion with
+ # distinguishingIndex + 1, since we already converted
+ # distinguishingIndex.
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sig, distinguishingIndex + 1), 4))
+ caseBody.append(CGIndenter(CGGeneric("} while (0);")))
+
+ caseBody.append(CGGeneric("}"))
+
+ # 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.isObject() && IsArrayLike(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s:
+ (s[1][distinguishingIndex].type.isArray() or
+ 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.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isDate() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for vanilla JS objects
+ # XXXbz Do we need to worry about security wrappers?
+ pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" %
+ (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()))
+
+ # 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())):
+ pass
+ # Check for primitives
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isPrimitive()):
+ pass
+ # Check for "any"
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isAny()):
+ pass
+ else:
+ # Just throw; we have no idea what we're supposed to
+ # do with this.
+ caseBody.append(CGGeneric("return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);" %
+ toStringBool(not descriptor.workers)))
+
+ argCountCases.append(CGCase(str(argCount),
+ CGList(caseBody, "\n")))
+
+ overloadCGThings = []
+ overloadCGThings.append(
+ CGGeneric("unsigned argcount = NS_MIN(argc, %du);" %
+ maxArgCount))
+ overloadCGThings.append(
+ CGSwitch("argcount",
+ argCountCases,
+ CGGeneric("return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n" % methodName)))
+ overloadCGThings.append(
+ CGGeneric('MOZ_NOT_REACHED("We have an always-returning default case");\n'
+ 'return false;'))
+ self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings, "\n")),
+ pre="\n")
+
+ def define(self):
+ return self.cgRoot.define()
+
+class CGGetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object getter call for a particular IDL
+ getter.
+ """
+ def __init__(self, returnType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, returnType, [], [],
+ nativeMethodName, False, descriptor,
+ attr, getter=True)
+
+class FakeArgument():
+ """
+ A class that quacks like an IDLArgument. This is used to make
+ setters look like method calls or for special operations.
+ """
+ def __init__(self, type, interfaceMember):
+ self.type = type
+ self.optional = False
+ self.variadic = False
+ self.defaultValue = None
+ self.treatNullAs = interfaceMember.treatNullAs
+ self.treatUndefinedAs = interfaceMember.treatUndefinedAs
+ self.enforceRange = False
+ self.clamp = False
+
+class CGSetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object setter call for a particular IDL
+ setter.
+ """
+ def __init__(self, argType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, None, [],
+ [FakeArgument(argType, attr)],
+ nativeMethodName, False, descriptor, attr,
+ setter=True)
+ def wrap_return_value(self):
+ # We have no return value
+ return "\nreturn true;"
+ def getArgc(self):
+ return "1"
+ def getArgvDecl(self):
+ # We just get our stuff from our last arg no matter what
+ return ""
+
+class FakeCastableDescriptor():
+ def __init__(self, descriptor):
+ self.castable = True
+ self.workers = descriptor.workers
+ self.nativeType = descriptor.nativeType
+ self.name = descriptor.name
+ self.hasXPConnectImpls = descriptor.hasXPConnectImpls
+
+class CGAbstractBindingMethod(CGAbstractStaticMethod):
+ """
+ Common class to generate the JSNatives for all our methods, getters, and
+ setters. This will generate the function declaration and unwrap the
+ |this| object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name, args, unwrapFailureCode=None):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ if unwrapFailureCode is None:
+ self.unwrapFailureCode = ("return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+ else:
+ self.unwrapFailureCode = unwrapFailureCode
+
+ def definition_body(self):
+ # Our descriptor might claim that we're not castable, simply because
+ # we're someone's consequential interface. But for this-unwrapping, we
+ # know that we're the real deal. So fake a descriptor here for
+ # consumption by FailureFatalCastableObjectUnwrapper.
+ unwrapThis = CGIndenter(CGGeneric(
+ str(CastableObjectUnwrapper(
+ FakeCastableDescriptor(self.descriptor),
+ "obj", "self", self.unwrapFailureCode))))
+ return CGList([ self.getThis(), unwrapThis,
+ self.generate_code() ], "\n").define()
+
+ def getThis(self):
+ return CGIndenter(
+ CGGeneric("js::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n"
+ "if (!obj) {\n"
+ " return false;\n"
+ "}\n"
+ "\n"
+ "%s* self;" % self.descriptor.nativeType))
+
+ def generate_code(self):
+ assert(False) # Override me
+
+def MakeNativeName(name):
+ return name[0].upper() + name[1:]
+
+class CGGenericMethod(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL method..
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitMethodOp method = (JSJitMethodOp)info->op;\n"
+ "return method(cx, obj, self, argc, vp);"))
+
+class CGSpecializedMethod(CGAbstractStaticMethod):
+ """
+ A class for generating the C++ code for a specialized method that the JIT
+ can call with lower overhead.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
+
+ def definition_body(self):
+ name = self.method.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ return CGMethodCall([], nativeName, self.method.isStatic(),
+ self.descriptor, self.method).define()
+
+class CGGenericGetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute getter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ if lenientThis:
+ name = "genericLenientGetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "JS_SET_RVAL(cx, vp, JS::UndefinedValue());\n"
+ "return true;")
+ else:
+ name = "genericGetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitPropertyOp getter = info->op;\n"
+ "return getter(cx, obj, self, vp);"))
+
+class CGSpecializedGetter(CGAbstractStaticMethod):
+ """
+ A class for generating the code for a specialized attribute getter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ args = [ Argument('JSContext*', 'cx'),
+ Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JS::Value*', 'vp') ]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ def definition_body(self):
+ name = self.attr.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ # resultOutParam does not depend on whether resultAlreadyAddRefed is set
+ (_, resultOutParam) = getRetvalDeclarationForType(self.attr.type,
+ self.descriptor,
+ False)
+ infallible = ('infallible' in
+ self.descriptor.getExtendedAttributes(self.attr,
+ getter=True))
+ if resultOutParam or self.attr.type.nullable() or not infallible:
+ nativeName = "Get" + nativeName
+ return CGIndenter(CGGetterCall(self.attr.type, nativeName,
+ self.descriptor, self.attr)).define()
+
+class CGGenericSetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute setter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ if lenientThis:
+ name = "genericLenientSetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "return true;")
+ else:
+ name = "genericSetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "JS::Value* argv = JS_ARGV(cx, vp);\n"
+ "JS::Value undef = JS::UndefinedValue();\n"
+ "if (argc == 0) {\n"
+ " argv = &undef;\n"
+ "}\n"
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitPropertyOp setter = info->op;\n"
+ "if (!setter(cx, obj, self, argv)) {\n"
+ " return false;\n"
+ "}\n"
+ "*vp = JSVAL_VOID;\n"
+ "return true;"))
+
+class CGSpecializedSetter(CGAbstractStaticMethod):
+ """
+ A class for generating the code for a specialized attribute setter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ args = [ Argument('JSContext*', 'cx'),
+ Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JS::Value*', 'argv')]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ def definition_body(self):
+ name = self.attr.identifier.name
+ nativeName = "Set" + MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ return CGIndenter(CGSetterCall(self.attr.type, nativeName,
+ self.descriptor, self.attr)).define()
+
+def memberIsCreator(member):
+ return member.getExtendedAttribute("Creator") is not None
+
+class CGMemberJITInfo(CGThing):
+ """
+ A class for generating the JITInfo for a property that points to
+ our specialized getter and setter.
+ """
+ def __init__(self, descriptor, member):
+ self.member = member
+ self.descriptor = descriptor
+
+ def declare(self):
+ return ""
+
+ def defineJitInfo(self, infoName, opName, infallible):
+ protoID = "prototypes::id::%s" % self.descriptor.name
+ depth = "PrototypeTraits<%s>::Depth" % protoID
+ failstr = "true" if infallible else "false"
+ return ("\n"
+ "const JSJitInfo %s = {\n"
+ " %s,\n"
+ " %s,\n"
+ " %s,\n"
+ " %s, /* isInfallible. False in setters. */\n"
+ " false /* isConstant. Only relevant for getters. */\n"
+ "};\n" % (infoName, opName, protoID, depth, failstr))
+
+ def define(self):
+ if self.member.isAttr():
+ getterinfo = ("%s_getterinfo" % self.member.identifier.name)
+ getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
+ getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
+ getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
+ result = self.defineJitInfo(getterinfo, getter, getterinfal)
+ if not self.member.readonly:
+ setterinfo = ("%s_setterinfo" % self.member.identifier.name)
+ setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
+ # Setters are always fallible, since they have to do a typed unwrap.
+ result += self.defineJitInfo(setterinfo, setter, False)
+ return result
+ if self.member.isMethod():
+ methodinfo = ("%s_methodinfo" % self.member.identifier.name)
+ # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
+ method = ("(JSJitPropertyOp)%s" % self.member.identifier.name)
+
+ # Methods are infallible if they are infallible, have no arguments
+ # to unwrap, and have a return type that's infallible to wrap up for
+ # return.
+ methodInfal = False
+ sigs = self.member.signatures()
+ if len(sigs) == 1:
+ # Don't handle overloading. If there's more than one signature,
+ # one of them must take arguments.
+ sig = sigs[0]
+ if len(sig[1]) == 0 and infallibleForMember(self.member, sig[0], self.descriptor):
+ # No arguments and infallible return boxing
+ methodInfal = True
+
+ result = self.defineJitInfo(methodinfo, method, methodInfal)
+ return result
+ raise TypeError("Illegal member type to CGPropertyJITInfo")
+
+def getEnumValueName(value):
+ # Some enum values can be empty strings. Others might have weird
+ # characters in them. Deal with the former by returning "_empty",
+ # deal with possible name collisions from that by throwing if the
+ # enum value is actually "_empty", and throw on any value
+ # containing chars other than [a-z] or '-' for now. Replace '-' with '_'.
+ value = value.replace('-', '_')
+ if value == "_empty":
+ raise SyntaxError('"_empty" is not an IDL enum value we support yet')
+ if value == "":
+ return "_empty"
+ if not re.match("^[a-z_]+$", value):
+ raise SyntaxError('Enum value "' + value + '" contains characters '
+ 'outside [a-z_]')
+ return MakeNativeName(value)
+
+class CGEnum(CGThing):
+ def __init__(self, enum):
+ CGThing.__init__(self)
+ self.enum = enum
+
+ def declare(self):
+ return """
+ enum valuelist {
+ %s
+ };
+
+ extern const EnumEntry strings[%d];
+""" % (",\n ".join(map(getEnumValueName, self.enum.values())),
+ len(self.enum.values()) + 1)
+
+ def define(self):
+ return """
+ const EnumEntry strings[%d] = {
+ %s,
+ { NULL, 0 }
+ };
+""" % (len(self.enum.values()) + 1,
+ ",\n ".join(['{"' + val + '", ' + str(len(val)) + '}' for val in self.enum.values()]))
+
+def getUnionAccessorSignatureType(type, descriptorProvider):
+ """
+ Returns the types that are used in the getter and setter signatures for
+ union types
+ """
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ nullable = type.nullable();
+ if nullable:
+ type = type.inner.inner
+ else:
+ type = type.inner
+ (elementTemplate, elementDeclType,
+ elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
+ type, descriptorProvider, isSequenceMember=True)
+ typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >&")
+ if nullable:
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+
+ return typeName
+
+ if type.isUnion():
+ typeName = CGGeneric(type.name)
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+
+ return typeName
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ typeName = CGGeneric(descriptor.nativeType)
+ # Allow null pointers for nullable types and old-binding classes
+ if type.nullable() or type.unroll().inner.isExternal():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if type.isSpiderMonkeyInterface():
+ typeName = CGGeneric(type.name)
+ if type.nullable():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if type.isString():
+ return CGGeneric("const nsAString&")
+
+ if type.isEnum():
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments or "
+ "union members yet")
+ return CGGeneric(type.inner.identifier.name)
+
+ if type.isCallback():
+ return CGGeneric("JSObject*")
+
+ if type.isAny():
+ return CGGeneric("JS::Value")
+
+ if type.isObject():
+ typeName = CGGeneric("JSObject")
+ if type.nullable():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if not type.isPrimitive():
+ raise TypeError("Need native type for argument type '%s'" % str(type))
+
+ typeName = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+ return typeName
+
+def getUnionTypeTemplateVars(type, descriptorProvider):
+ # For dictionaries and sequences we need to pass None as the failureCode
+ # for getJSToNativeConversionTemplate.
+ # Also, for dictionaries we would need to handle conversion of
+ # null/undefined to the dictionary correctly.
+ if type.isDictionary() or type.isSequence():
+ raise TypeError("Can't handle dictionaries or sequences in unions")
+
+ if type.isGeckoInterface():
+ name = type.inner.identifier.name
+ elif type.isEnum():
+ name = type.inner.identifier.name
+ elif type.isArray() or type.isSequence():
+ name = str(type)
+ else:
+ name = type.name
+
+ tryNextCode = """tryNext = true;
+return true;"""
+ if type.isGeckoInterface():
+ tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) {
+ mUnion.Destroy%s();
+}""" % name) + tryNextCode
+ (template, declType, holderType,
+ dealWithOptional) = getJSToNativeConversionTemplate(
+ type, descriptorProvider, failureCode=tryNextCode,
+ isDefinitelyObject=True)
+
+ # This is ugly, but UnionMember needs to call a constructor with no
+ # arguments so the type can't be const.
+ structType = declType.define()
+ if structType.startswith("const "):
+ structType = structType[6:]
+ externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
+
+ if type.isObject():
+ setter = CGGeneric("void SetToObject(JSObject* obj)\n"
+ "{\n"
+ " mUnion.mValue.mObject.SetValue() = obj;\n"
+ " mUnion.mType = mUnion.eObject;\n"
+ "}")
+ else:
+ jsConversion = string.Template(template).substitute(
+ {
+ "val": "value",
+ "valPtr": "pvalue",
+ "declName": "SetAs" + name + "()",
+ "holderName": "m" + name + "Holder"
+ }
+ )
+ jsConversion = CGWrapper(CGGeneric(jsConversion),
+ post="\n"
+ "return true;")
+ setter = CGWrapper(CGIndenter(jsConversion),
+ pre="bool TrySetTo" + name + "(JSContext* cx, const JS::Value& value, JS::Value* pvalue, bool& tryNext)\n"
+ "{\n"
+ " tryNext = false;\n",
+ post="\n"
+ "}")
+
+ return {
+ "name": name,
+ "structType": structType,
+ "externalType": externalType,
+ "setter": CGIndenter(setter).define(),
+ "holderType": holderType.define() if holderType else None
+ }
+
+def mapTemplate(template, templateVarArray):
+ return map(lambda v: string.Template(template).substitute(v),
+ templateVarArray)
+
+class CGUnionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+
+ callDestructors = []
+ enumValues = []
+ methods = []
+ if self.type.hasNullableType:
+ callDestructors.append(" case eNull:\n"
+ " break;")
+ enumValues.append("eNull")
+ methods.append(""" bool IsNull() const
+ {
+ return mType == eNull;
+ }""")
+
+ destructorTemplate = """ void Destroy${name}()
+ {
+ MOZ_ASSERT(Is${name}(), "Wrong type!");
+ mValue.m${name}.Destroy();
+ mType = eUninitialized;
+ }"""
+ destructors = mapTemplate(destructorTemplate, templateVars)
+ callDestructors.extend(mapTemplate(" case e${name}:\n"
+ " Destroy${name}();\n"
+ " break;", templateVars))
+ enumValues.extend(mapTemplate("e${name}", templateVars))
+ methodTemplate = """ bool Is${name}() const
+ {
+ return mType == e${name};
+ }
+ ${externalType} GetAs${name}() const
+ {
+ MOZ_ASSERT(Is${name}(), "Wrong type!");
+ // The cast to ${externalType} is needed to work around a bug in Apple's
+ // clang compiler, for some reason it doesn't call |S::operator T&| when
+ // casting S<T> to T& and T is forward declared.
+ return (${externalType})mValue.m${name}.Value();
+ }
+ ${structType}& SetAs${name}()
+ {
+ mType = e${name};
+ return mValue.m${name}.SetValue();
+ }"""
+ methods.extend(mapTemplate(methodTemplate, templateVars))
+ values = mapTemplate("UnionMember<${structType} > m${name};", templateVars)
+ return string.Template("""
+class ${structName} {
+public:
+ ${structName}() : mType(eUninitialized)
+ {
+ }
+ ~${structName}()
+ {
+ switch (mType) {
+${callDestructors}
+ case eUninitialized:
+ break;
+ }
+ }
+
+${methods}
+
+private:
+ friend class ${structName}Argument;
+
+${destructors}
+
+ enum Type {
+ eUninitialized,
+ ${enumValues}
+ };
+ union Value {
+ ${values}
+ };
+
+ Type mType;
+ Value mValue;
+};
+
+""").substitute(
+ {
+ "structName": self.type.__str__(),
+ "callDestructors": "\n".join(callDestructors),
+ "destructors": "\n".join(destructors),
+ "methods": "\n\n".join(methods),
+ "enumValues": ",\n ".join(enumValues),
+ "values": "\n ".join(values),
+ })
+
+ def define(self):
+ return """
+"""
+
+class CGUnionConversionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+ setters = []
+
+ if self.type.hasNullableType:
+ setters.append(""" bool SetNull()
+ {
+ mUnion.mType = mUnion.eNull;
+ return true;
+ }""")
+
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+ structName = self.type.__str__()
+
+ setters.extend(mapTemplate("${setter}", templateVars))
+ private = "\n".join(mapTemplate(""" ${structType}& SetAs${name}()
+ {
+ mUnion.mType = mUnion.e${name};
+ return mUnion.mValue.m${name}.SetValue();
+ }""", templateVars))
+ private += "\n\n"
+ holders = filter(lambda v: v["holderType"] is not None, templateVars)
+ if len(holders) > 0:
+ private += "\n".join(mapTemplate(" ${holderType} m${name}Holder;", holders))
+ private += "\n\n"
+ private += " " + structName + "& mUnion;"
+ return string.Template("""
+class ${structName}Argument {
+public:
+ ${structName}Argument(const ${structName}& aUnion) : mUnion(const_cast<${structName}&>(aUnion))
+ {
+ }
+
+${setters}
+
+private:
+${private}
+};
+""").substitute({"structName": structName,
+ "setters": "\n\n".join(setters),
+ "private": private
+ })
+
+ def define(self):
+ return """
+"""
+
+class ClassItem:
+ """ Use with CGClass """
+ def __init__(self, name, visibility):
+ self.name = name
+ self.visibility = visibility
+ def declare(self, cgClass):
+ assert False
+ def define(self, cgClass):
+ assert False
+
+class ClassBase(ClassItem):
+ def __init__(self, name, visibility='public'):
+ ClassItem.__init__(self, name, visibility)
+ def declare(self, cgClass):
+ return '%s %s' % (self.visibility, self.name)
+ def define(self, cgClass):
+ # Only in the header
+ return ''
+
+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):
+ self.returnType = returnType
+ self.args = args
+ self.inline = inline or bodyInHeader
+ self.static = static
+ self.virtual = virtual
+ self.const = const
+ self.bodyInHeader = bodyInHeader
+ self.templateArgs = templateArgs
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline:
+ decorators.append('inline')
+ if declaring:
+ if self.static:
+ decorators.append('static')
+ if self.virtual:
+ decorators.append('virtual')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ # Override me or pass a string to constructor
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ templateClause = 'template <%s>\n' % ', '.join(self.templateArgs) \
+ if self.bodyInHeader and self.templateArgs else ''
+ args = ', '.join([str(a) for a in self.args])
+ if self.bodyInHeader:
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+ body = '\n{\n' + body + '\n}'
+ else:
+ body = ';'
+
+ return string.Template("""${templateClause}${decorators}${returnType}
+${name}(${args})${const}${body}
+""").substitute({ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(True),
+ 'returnType': self.returnType,
+ 'name': self.name,
+ 'const': ' const' if self.const else '',
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ templateArgs = cgClass.templateArgs
+ if templateArgs:
+ if cgClass.templateSpecialization:
+ templateArgs = \
+ templateArgs[len(cgClass.templateSpecialization):]
+
+ if templateArgs:
+ templateClause = \
+ 'template <%s>\n' % ', '.join([str(a) for a in templateArgs])
+ else:
+ templateClause = ''
+
+ args = ', '.join([str(a) for a in self.args])
+
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+
+ return string.Template("""${templateClause}${decorators}${returnType}
+${className}::${name}(${args})${const}
+{
+${body}
+}\n
+""").substitute({ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(False),
+ 'returnType': self.returnType,
+ 'className': cgClass.getNameString(),
+ 'name': self.name,
+ 'args': args,
+ 'const': ' const' if self.const else '',
+ 'body': body })
+
+class ClassConstructor(ClassItem):
+ """
+ Used for adding a constructor to a CGClass.
+
+ args is a list of Argument objects that are the arguments taken by the
+ constructor.
+
+ inline should be True if the constructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the constructor (public,
+ protected, private), defaults to private.
+
+ baseConstructors is a list of strings containing calls to base constructors,
+ defaults to None.
+
+ body contains a string with the code for the constructor, defaults to None.
+ """
+ def __init__(self, args, inline=False, bodyInHeader=False,
+ visibility="private", baseConstructors=None, body=None):
+ self.args = args
+ self.inline = inline or bodyInHeader
+ self.bodyInHeader = bodyInHeader
+ self.baseConstructors = baseConstructors
+ self.body = body
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getInitializationList(self, cgClass):
+ items = [str(c) for c in self.baseConstructors]
+ for m in cgClass.members:
+ if not m.static:
+ initialize = m.getBody()
+ if initialize:
+ items.append(m.name + "(" + initialize + ")")
+
+ if len(items) > 0:
+ return '\n : ' + ',\n '.join(items)
+ return ''
+
+ def getBody(self):
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ args = ', '.join([str(a) for a in self.args])
+ if self.bodyInHeader:
+ body = ' ' + self.getBody();
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = self.getInitializationList(cgClass) + '\n{\n' + body + '}'
+ else:
+ body = ';'
+
+ return string.Template("""${decorators}${className}(${args})${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ args = ', '.join([str(a) for a in self.args])
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::${className}(${args})${initializationList}
+{${body}}\n
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'initializationList': self.getInitializationList(cgClass),
+ 'body': body })
+
+class ClassMember(ClassItem):
+ def __init__(self, name, type, visibility="private", static=False,
+ body=None):
+ self.type = type;
+ self.static = static
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return '%s%s %s;\n' % ('static ' if self.static else '', self.type,
+ self.name)
+
+ def define(self, cgClass):
+ if not self.static:
+ return ''
+ if self.body:
+ body = " = " + self.body
+ else:
+ body = ""
+ return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
+ self.name, body)
+
+class ClassTypedef(ClassItem):
+ def __init__(self, name, type, visibility="public"):
+ self.type = type
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'typedef %s %s;\n' % (self.type, self.name)
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassEnum(ClassItem):
+ def __init__(self, name, entries, values=None, visibility="public"):
+ self.entries = entries
+ self.values = values
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ entries = []
+ for i in range(0, len(self.entries)):
+ if i >= len(self.values):
+ entry = '%s' % self.entries[i]
+ else:
+ entry = '%s = %s' % (self.entries[i], self.values[i])
+ entries.append(entry)
+ name = '' if not self.name else ' ' + self.name
+ return 'enum%s\n{\n %s\n};\n' % (name, ',\n '.join(entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class CGClass(CGThing):
+ def __init__(self, name, bases=[], members=[], constructors=[], methods=[],
+ typedefs = [], enums=[], templateArgs=[],
+ templateSpecialization=[], isStruct=False, indent=''):
+ CGThing.__init__(self)
+ self.name = name
+ self.bases = bases
+ self.members = members
+ self.constructors = constructors
+ self.methods = methods
+ self.typedefs = typedefs
+ self.enums = enums
+ self.templateArgs = templateArgs
+ self.templateSpecialization = templateSpecialization
+ self.isStruct = isStruct
+ self.indent = indent
+ self.defaultVisibility ='public' if isStruct else 'private'
+
+ def getNameString(self):
+ className = self.name
+ if self.templateSpecialization:
+ className = className + \
+ '<%s>' % ', '.join([str(a) for a
+ in self.templateSpecialization])
+ return className
+
+ def declare(self):
+ result = ''
+ if self.templateArgs:
+ templateArgs = [str(a) for a in self.templateArgs]
+ templateArgs = templateArgs[len(self.templateSpecialization):]
+ result = result + self.indent + 'template <%s>\n' \
+ % ','.join([str(a) for a in templateArgs])
+
+ type = 'struct' if self.isStruct else 'class'
+
+ if self.templateSpecialization:
+ specialization = \
+ '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
+ else:
+ specialization = ''
+
+ result = result + '%s%s %s%s' \
+ % (self.indent, type, self.name, specialization)
+
+ if self.bases:
+ result = result + ' : %s' % ', '.join([d.declare(self) for d in self.bases])
+
+ result = result + '\n%s{\n' % self.indent
+
+ def declareMembers(cgClass, memberList, defaultVisibility, itemCount,
+ separator=''):
+ members = { 'private': [], 'protected': [], 'public': [] }
+
+ for member in memberList:
+ members[member.visibility].append(member)
+
+
+ if defaultVisibility == 'public':
+ order = [ 'public', 'protected', 'private' ]
+ else:
+ order = [ 'private', 'protected', 'public' ]
+
+ result = ''
+
+ lastVisibility = defaultVisibility
+ for visibility in order:
+ list = members[visibility]
+ if list:
+ if visibility != lastVisibility:
+ if itemCount:
+ result = result + '\n'
+ result = result + visibility + ':\n'
+ itemCount = 0
+ for member in list:
+ if itemCount != 0:
+ result = result + separator
+ declaration = member.declare(cgClass)
+ declaration = CGIndenter(CGGeneric(declaration)).define()
+ result = result + declaration
+ itemCount = itemCount + 1
+ lastVisibility = visibility
+ return (result, lastVisibility, itemCount)
+
+ order = [(self.enums, ''), (self.typedefs, ''), (self.members, ''),
+ (self.constructors, '\n'), (self.methods, '\n')]
+
+ lastVisibility = self.defaultVisibility
+ itemCount = 0
+ for (memberList, separator) in order:
+ (memberString, lastVisibility, itemCount) = \
+ declareMembers(self, memberList, lastVisibility, itemCount,
+ separator)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result = result + self.indent + '};\n'
+ return result
+
+ def define(self):
+ def defineMembers(cgClass, memberList, itemCount, separator=''):
+ result = ''
+ for member in memberList:
+ if itemCount != 0:
+ result = result + separator
+ result = result + member.define(cgClass)
+ itemCount = itemCount + 1
+ return (result, itemCount)
+
+ order = [(self.members, '\n'), (self.constructors, '\n'),
+ (self.methods, '\n')]
+
+ result = ''
+ itemCount = 0
+ for (memberList, separator) in order:
+ (memberString, itemCount) = defineMembers(self, memberList,
+ itemCount, separator)
+ result = result + memberString
+ return result
+
+class CGResolveOwnProperty(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ CGAbstractMethod.__init__(self, descriptor, "ResolveOwnProperty", "bool", args)
+ def definition_body(self):
+ return """ JSObject* obj = wrapper;
+ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ // We rely on getOwnPropertyDescriptor not shadowing prototype properties by named
+ // properties. If that changes we'll need to filter here.
+ return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, set, desc);
+"""
+
+class CGEnumerateOwnProperties(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGAbstractMethod.__init__(self, descriptor, "EnumerateOwnProperties", "bool", args)
+ def definition_body(self):
+ return """ JSObject* obj = wrapper;
+ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ // We rely on getOwnPropertyNames not shadowing prototype properties by named
+ // properties. If that changes we'll need to filter here.
+ return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);
+"""
+
+class CGXrayHelper(CGAbstractMethod):
+ def __init__(self, descriptor, name, args, properties):
+ CGAbstractMethod.__init__(self, descriptor, name, "bool", args)
+ self.properties = properties
+
+ def definition_body(self):
+ varNames = self.properties.variableNames(True)
+
+ methods = self.properties.methods
+ if methods.hasNonChromeOnly() or methods.hasChromeOnly():
+ methodArgs = """// %(methods)s has an end-of-list marker at the end that we ignore
+%(methods)s, %(methods)s_ids, %(methods)s_specs, ArrayLength(%(methods)s) - 1""" % varNames
+ else:
+ methodArgs = "NULL, NULL, NULL, 0"
+ methodArgs = CGGeneric(methodArgs)
+
+ attrs = self.properties.attrs
+ if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
+ attrArgs = """// %(attrs)s has an end-of-list marker at the end that we ignore
+%(attrs)s, %(attrs)s_ids, %(attrs)s_specs, ArrayLength(%(attrs)s) - 1""" % varNames
+ else:
+ attrArgs = "NULL, NULL, NULL, 0"
+ attrArgs = CGGeneric(attrArgs)
+
+ consts = self.properties.consts
+ if consts.hasNonChromeOnly() or consts.hasChromeOnly():
+ constArgs = """// %(consts)s has an end-of-list marker at the end that we ignore
+%(consts)s, %(consts)s_ids, %(consts)s_specs, ArrayLength(%(consts)s) - 1""" % varNames
+ else:
+ constArgs = "NULL, NULL, NULL, 0"
+ constArgs = CGGeneric(constArgs)
+
+ prefixArgs = CGGeneric(self.getPrefixArgs())
+
+ return CGIndenter(
+ CGWrapper(CGList([prefixArgs, methodArgs, attrArgs, constArgs], ",\n"),
+ pre=("return Xray%s(" % self.name),
+ post=");",
+ reindent=True)).define()
+
+class CGResolveProperty(CGXrayHelper):
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ CGXrayHelper.__init__(self, descriptor, "ResolveProperty", args,
+ properties)
+
+ def getPrefixArgs(self):
+ return "cx, wrapper, id, desc"
+
+
+class CGEnumerateProperties(CGXrayHelper):
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGXrayHelper.__init__(self, descriptor, "EnumerateProperties", args,
+ properties)
+
+ def getPrefixArgs(self):
+ return "props"
+
+class CGPrototypeTraitsClass(CGClass):
+ def __init__(self, descriptor, indent=''):
+ templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
+ templateSpecialization = ['prototypes::id::' + descriptor.name]
+ enums = [ClassEnum('', ['Depth'],
+ [descriptor.interface.inheritanceDepth()])]
+ typedefs = [ClassTypedef('NativeType', descriptor.nativeType)]
+ CGClass.__init__(self, 'PrototypeTraits', indent=indent,
+ templateArgs=templateArgs,
+ templateSpecialization=templateSpecialization,
+ enums=enums, typedefs=typedefs, isStruct=True)
+
+class CGPrototypeIDMapClass(CGClass):
+ def __init__(self, descriptor, indent=''):
+ templateArgs = [Argument('class', 'ConcreteClass')]
+ templateSpecialization = [descriptor.nativeType]
+ enums = [ClassEnum('', ['PrototypeID'],
+ ['prototypes::id::' + descriptor.name])]
+ CGClass.__init__(self, 'PrototypeIDMap', indent=indent,
+ templateArgs=templateArgs,
+ templateSpecialization=templateSpecialization,
+ enums=enums, isStruct=True)
+
+class CGClassForwardDeclare(CGThing):
+ def __init__(self, name, isStruct=False):
+ CGThing.__init__(self)
+ self.name = name
+ self.isStruct = isStruct
+ def declare(self):
+ type = 'struct' if self.isStruct else 'class'
+ return '%s %s;\n' % (type, self.name)
+ def define(self):
+ # Header only
+ return ''
+
+class CGProxySpecialOperation(CGPerSignatureCall):
+ """
+ Base class for classes for calling an indexed or named special operation
+ (don't use this directly, use the derived classes below).
+ """
+ def __init__(self, descriptor, operation):
+ nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation))
+ operation = descriptor.operations[operation]
+ assert len(operation.signatures()) == 1
+ signature = operation.signatures()[0]
+ extendedAttributes = descriptor.getExtendedAttributes(operation)
+
+ (returnType, arguments) = signature
+
+ # We pass len(arguments) as the final argument so that the
+ # CGPerSignatureCall won't do any argument conversion of its own.
+ CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
+ False, descriptor, operation,
+ len(arguments))
+
+ if operation.isSetter() or operation.isCreator():
+ # arguments[0] is the index or name of the item that we're setting.
+ argument = arguments[1]
+ template = getJSToNativeConversionTemplate(argument.type, descriptor,
+ treatNullAs=argument.treatNullAs,
+ treatUndefinedAs=argument.treatUndefinedAs)
+ templateValues = {
+ "declName": argument.identifier.name,
+ "holderName": argument.identifier.name + "_holder",
+ "val": "desc->value",
+ "valPtr": "&desc->value"
+ }
+ self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues))
+ elif operation.isGetter():
+ self.cgRoot.prepend(CGGeneric("bool found;"))
+
+ def getArguments(self):
+ args = [(a, a.identifier.name) for a in self.arguments]
+ if self.idlNode.isGetter():
+ args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
+ self.idlNode),
+ "found"))
+ return args
+
+ def wrap_return_value(self):
+ if not self.idlNode.isGetter() or self.templateValues is None:
+ return ""
+
+ wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues))
+ wrap = CGIfWrapper(wrap, "found")
+ return "\n" + wrap.define()
+
+class CGProxyIndexedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter')
+
+class CGProxyIndexedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter')
+
+class CGProxyNamedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an named getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter')
+
+class CGProxyNamedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to a named setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
+
+class CGProxyIsProxy(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
+ def declare(self):
+ return ""
+ def definition_body(self):
+ return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();"
+
+class CGProxyUnwrap(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True)
+ def declare(self):
+ return ""
+ def definition_body(self):
+ return """ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ MOZ_ASSERT(IsProxy(obj));
+ return static_cast<%s*>(js::GetProxyPrivate(obj).toPrivate());""" % (self.descriptor.nativeType)
+
+class CGDOMJSProxyHandlerDOMClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ return "extern const DOMClass Class;\n"
+ def define(self):
+ return """
+const DOMClass Class = """ + DOMClass(self.descriptor) + """;
+
+"""
+
+class CGDOMJSProxyHandler_CGDOMJSProxyHandler(ClassConstructor):
+ def __init__(self):
+ ClassConstructor.__init__(self, [], inline=True, visibility="private",
+ baseConstructors=["mozilla::dom::DOMProxyHandler(Class)"],
+ body="")
+
+class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ ClassMethod.__init__(self, "getOwnPropertyDescriptor", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+
+ setOrIndexedGet = ""
+ if indexedGetter or indexedSetter:
+ setOrIndexedGet += "int32_t index = GetArrayIndexFromId(cx, id);\n"
+
+ if indexedGetter:
+ readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
+ 'obj': 'proxy', 'successCode': fillDescriptor}
+ get = ("if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+
+ if indexedSetter or self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += "if (set) {\n"
+ if indexedSetter:
+ setOrIndexedGet += (" if (index >= 0) {\n")
+ if not 'IndexedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property index'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
+ " return true;\n" +
+ " }\n")
+ if self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += " if (JSID_IS_STRING(id)) {\n"
+ if not 'NamedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property name'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
+ " return true;\n" +
+ " }\n")
+ setOrIndexedGet += "}"
+ if indexedGetter:
+ setOrIndexedGet += (" else {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}")
+ setOrIndexedGet += "\n\n"
+ elif indexedGetter:
+ setOrIndexedGet += ("if (!set) {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}\n\n")
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
+ 'obj': 'proxy', 'successCode': fillDescriptor}
+ # Once we start supporting OverrideBuiltins we need to make
+ # ResolveOwnProperty or EnumerateOwnProperties filter out named
+ # properties that shadow prototype properties.
+ namedGet = ("\n" +
+ "if (!set && JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+ else:
+ namedGet = ""
+
+ return setOrIndexedGet + """JSObject* expando;
+if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+ unsigned flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED;
+ if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc)) {
+ return false;
+ }
+ if (desc->obj) {
+ // Pretend the property lives on the wrapper.
+ desc->obj = proxy;
+ return true;
+ }
+}
+""" + namedGet + """
+desc->obj = NULL;
+return true;"""
+
+class CGDOMJSProxyHandler_defineProperty(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ ClassMethod.__init__(self, "defineProperty", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ set = ""
+
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+ if indexedSetter:
+ if not (self.descriptor.operations['IndexedCreator'] is indexedSetter):
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
+ " return true;\n" +
+ "}\n") % (self.descriptor.nativeType)
+ elif self.descriptor.operations['IndexedGetter']:
+ set += ("if (GetArrayIndexFromId(cx, id) >= 0) {\n" +
+ " return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ "}\n") % self.descriptor.name
+
+ namedSetter = self.descriptor.operations['NamedSetter']
+ if namedSetter:
+ if not self.descriptor.operations['NamedCreator'] is namedSetter:
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+ elif self.descriptor.operations['NamedGetter']:
+ set += ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
+ " if (found) {\n"
+ " return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ " }\n" +
+ " return true;\n"
+ "}\n") % (self.descriptor.nativeType, self.descriptor.name)
+ return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args)
+
+class CGDOMJSProxyHandler_getOwnPropertyNames(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JS::AutoIdVector&', 'props')]
+ ClassMethod.__init__(self, "getOwnPropertyNames", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ addIndices = """uint32_t length = UnwrapProxy(proxy)->Length();
+MOZ_ASSERT(int32_t(length) >= 0);
+for (int32_t i = 0; i < int32_t(length); ++i) {
+ if (!props.append(INT_TO_JSID(i))) {
+ return false;
+ }
+}
+
+"""
+ else:
+ addIndices = ""
+
+ return addIndices + """JSObject* expando;
+if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
+ !js::GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props)) {
+ return false;
+}
+
+// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=772869 Add named items
+return true;"""
+
+class CGDOMJSProxyHandler_hasOwn(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool*', 'bp')]
+ ClassMethod.__init__(self, "hasOwn", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ indexed = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n" +
+ " return true;\n" +
+ "}\n\n") % (self.descriptor.nativeType)
+ else:
+ indexed = ""
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ named = ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+ " jsval nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n"
+ " return true;\n"
+ "}\n" +
+ "\n") % (self.descriptor.nativeType)
+ else:
+ named = ""
+
+ return indexed + """JSObject* expando = GetExpandoObject(proxy);
+if (expando) {
+ JSBool b = true;
+ JSBool ok = JS_HasPropertyById(cx, expando, id, &b);
+ *bp = !!b;
+ if (!ok || *bp) {
+ return ok;
+ }
+}
+
+""" + named + """*bp = false;
+return true;"""
+
+class CGDOMJSProxyHandler_get(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JSObject*', 'receiver'), Argument('jsid', 'id'),
+ Argument('JS::Value*', 'vp')]
+ ClassMethod.__init__(self, "get", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ getFromExpando = """JSObject* expando = DOMProxyHandler::GetExpandoObject(proxy);
+if (expando) {
+ JSBool hasProp;
+ if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
+ return false;
+ }
+
+ if (hasProp) {
+ return JS_GetPropertyById(cx, expando, id, vp);
+ }
+}"""
+
+ templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', 'obj': 'proxy'}
+
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ getIndexedOrExpando = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) % (self.descriptor.nativeType)
+ getIndexedOrExpando += """
+ // Even if we don't have this index, we don't forward the
+ // get on to our expando object.
+} else {
+ %s
+}
+""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n ')))
+ else:
+ getIndexedOrExpando = getFromExpando + "\n"
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ getNamed = ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
+ "}\n") % (self.descriptor.nativeType)
+ else:
+ getNamed = ""
+
+ return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+%s
+bool found;
+if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp)) {
+ return false;
+}
+
+if (found) {
+ return true;
+}
+%s
+vp->setUndefined();
+return true;""" % (getIndexedOrExpando, getNamed)
+
+class CGDOMJSProxyHandler_obj_toString(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy')]
+ ClassMethod.__init__(self, "obj_toString", "JSString*", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ stringifier = self.descriptor.operations['Stringifier']
+ if stringifier:
+ name = stringifier.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ signature = stringifier.signatures()[0]
+ returnType = signature[0]
+ extendedAttributes = self.descriptor.getExtendedAttributes(stringifier)
+ infallible = 'infallible' in extendedAttributes
+ if not infallible:
+ error = CGGeneric(
+ ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString");\n' +
+ "return NULL;") % self.descriptor.interface.identifier.name)
+ else:
+ error = None
+ call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)")
+ return call.define() + """
+
+JSString* jsresult;
+return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;"""
+
+ return "return mozilla::dom::DOMProxyHandler::obj_toString(cx, \"%s\");" % self.descriptor.name
+
+class CGDOMJSProxyHandler_finalize(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
+ ClassMethod.__init__(self, "finalize", "void", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ return ("%s self = UnwrapProxy(proxy);\n\n" % (self.descriptor.nativeType + "*") +
+ finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name))
+
+class CGDOMJSProxyHandler_getElementIfPresent(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JSObject*', 'receiver'),
+ Argument('uint32_t', 'index'),
+ Argument('JS::Value*', 'vp'), Argument('bool*', 'present')]
+ ClassMethod.__init__(self, "getElementIfPresent", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ successCode = """*present = found;
+return true;"""
+ templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+ 'obj': 'proxy', 'successCode': successCode}
+ get = ("%s* self = UnwrapProxy(proxy);\n" +
+ CGProxyIndexedGetter(self.descriptor, templateValues).define() + "\n"
+ "// We skip the expando object if there is an indexed getter.\n" +
+ "\n") % (self.descriptor.nativeType)
+ else:
+ get = """
+
+JSObject* expando = GetExpandoObject(proxy);
+if (expando) {
+ JSBool isPresent;
+ if (!JS_GetElementIfPresent(cx, expando, index, expando, vp, &isPresent)) {
+ return false;
+ }
+ if (isPresent) {
+ *present = true;
+ return true;
+ }
+}
+"""
+
+ return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+""" + get + """
+// No need to worry about name getters here, so just check the proto.
+
+JSObject *proto;
+if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+}
+if (proto) {
+ JSBool isPresent;
+ if (!JS_GetElementIfPresent(cx, proto, index, proxy, vp, &isPresent)) {
+ return false;
+ }
+ *present = isPresent;
+ return true;
+}
+
+*present = false;
+// Can't Debug_SetValueRangeToCrashOnTouch because it's not public
+return true;"""
+
+class CGDOMJSProxyHandler_getInstance(ClassMethod):
+ def __init__(self):
+ ClassMethod.__init__(self, "getInstance", "DOMProxyHandler*", [], static=True)
+ def getBody(self):
+ return """static DOMProxyHandler instance;
+return &instance;"""
+
+class CGDOMJSProxyHandler(CGClass):
+ def __init__(self, descriptor):
+ constructors = [CGDOMJSProxyHandler_CGDOMJSProxyHandler()]
+ methods = [CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)]
+ if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
+ methods.append(CGDOMJSProxyHandler_defineProperty(descriptor))
+ methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
+ CGDOMJSProxyHandler_hasOwn(descriptor),
+ CGDOMJSProxyHandler_get(descriptor),
+ CGDOMJSProxyHandler_obj_toString(descriptor),
+ CGDOMJSProxyHandler_finalize(descriptor),
+ CGDOMJSProxyHandler_getElementIfPresent(descriptor),
+ CGDOMJSProxyHandler_getInstance()])
+ CGClass.__init__(self, 'DOMProxyHandler',
+ bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
+ constructors=constructors,
+ methods=methods)
+
+def stripTrailingWhitespace(text):
+ tail = '\n' if text.endswith('\n') else ''
+ lines = text.splitlines()
+ for i in range(len(lines)):
+ lines[i] = lines[i].rstrip()
+ return '\n'.join(lines) + tail
+
+class CGDescriptor(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
+
+ cgThings = []
+ if descriptor.interface.hasInterfacePrototypeObject():
+ (hasMethod, hasGetter, hasLenientGetter,
+ hasSetter, hasLenientSetter) = False, False, False, False, False
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isStatic() and not m.isIdentifierLess():
+ cgThings.append(CGSpecializedMethod(descriptor, m))
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ hasMethod = True
+ elif m.isAttr():
+ cgThings.append(CGSpecializedGetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientGetter = True
+ else:
+ hasGetter = True
+ if not m.readonly:
+ cgThings.append(CGSpecializedSetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientSetter = True
+ else:
+ hasSetter = True
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ if hasMethod: cgThings.append(CGGenericMethod(descriptor))
+ if hasGetter: cgThings.append(CGGenericGetter(descriptor))
+ if hasLenientGetter: cgThings.append(CGGenericGetter(descriptor,
+ lenientThis=True))
+ if hasSetter: cgThings.append(CGGenericSetter(descriptor))
+ if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
+ lenientThis=True))
+
+ if descriptor.concrete and not descriptor.proxy:
+ if not descriptor.workers and descriptor.wrapperCache:
+ cgThings.append(CGAddPropertyHook(descriptor))
+
+ # Always have a finalize hook, regardless of whether the class wants a
+ # custom hook.
+ cgThings.append(CGClassFinalizeHook(descriptor))
+
+ # Only generate a trace hook if the class wants a custom hook.
+ if (descriptor.customTrace):
+ cgThings.append(CGClassTraceHook(descriptor))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGClassConstructHook(descriptor))
+ cgThings.append(CGClassHasInstanceHook(descriptor))
+ cgThings.append(CGInterfaceObjectJSClass(descriptor))
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGPrototypeJSClass(descriptor))
+
+ properties = PropertyArrays(descriptor)
+ cgThings.append(CGGeneric(define=str(properties)))
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
+ else:
+ cgThings.append(CGGetConstructorObjectMethod(descriptor))
+
+ # Set up our Xray callbacks as needed. Note that we don't need to do
+ # it in workers.
+ if (descriptor.interface.hasInterfacePrototypeObject() and
+ not descriptor.workers):
+ if descriptor.concrete and descriptor.proxy:
+ cgThings.append(CGResolveOwnProperty(descriptor))
+ cgThings.append(CGEnumerateOwnProperties(descriptor))
+ cgThings.append(CGResolveProperty(descriptor, properties))
+ cgThings.append(CGEnumerateProperties(descriptor, properties))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+ if (not descriptor.interface.isExternal() and
+ # Workers stuff is never pref-controlled
+ not descriptor.workers and
+ descriptor.interface.getExtendedAttribute("PrefControlled") is not None):
+ cgThings.append(CGPrefEnabled(descriptor))
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGNativePropertyHooks(descriptor))
+
+ if descriptor.concrete:
+ if descriptor.proxy:
+ cgThings.append(CGProxyIsProxy(descriptor))
+ cgThings.append(CGProxyUnwrap(descriptor))
+ cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
+ cgThings.append(CGDOMJSProxyHandler(descriptor))
+ cgThings.append(CGIsMethod(descriptor))
+ else:
+ cgThings.append(CGDOMJSClass(descriptor))
+
+ if descriptor.wrapperCache:
+ cgThings.append(CGWrapWithCacheMethod(descriptor))
+ cgThings.append(CGWrapMethod(descriptor))
+ else:
+ cgThings.append(CGWrapNonWrapperCacheMethod(descriptor))
+
+ cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
+ cgThings = CGWrapper(cgThings, pre='\n', post='\n')
+ self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
+ cgThings),
+ post='\n')
+
+ def declare(self):
+ return self.cgRoot.declare()
+ def define(self):
+ return self.cgRoot.define()
+
+class CGNamespacedEnum(CGThing):
+ def __init__(self, namespace, enumName, names, values, comment=""):
+
+ if not values:
+ values = []
+
+ # Account for explicit enum values.
+ entries = []
+ for i in range(0, len(names)):
+ if len(values) > i and values[i] is not None:
+ entry = "%s = %s" % (names[i], values[i])
+ else:
+ entry = names[i]
+ entries.append(entry)
+
+ # Append a Count.
+ entries.append('_' + enumName + '_Count')
+
+ # Indent.
+ entries = [' ' + e for e in entries]
+
+ # Build the enum body.
+ enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries))
+ curr = CGGeneric(declare=enumstr)
+
+ # Add some whitespace padding.
+ curr = CGWrapper(curr, pre='\n',post='\n')
+
+ # Add the namespace.
+ curr = CGNamespace(namespace, curr)
+
+ # Add the typedef
+ typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
+ curr = CGList([curr, CGGeneric(declare=typedef)])
+
+ # Save the result.
+ self.node = curr
+
+ def declare(self):
+ return self.node.declare()
+ def define(self):
+ assert False # Only for headers.
+
+class CGDictionary(CGThing):
+ def __init__(self, dictionary, descriptorProvider):
+ self.dictionary = dictionary;
+ self.workers = descriptorProvider.workers
+ if all(CGDictionary(d, descriptorProvider).generatable for
+ d in CGDictionary.getDictionaryDependencies(dictionary)):
+ self.generatable = True
+ else:
+ self.generatable = False
+ # Nothing else to do here
+ return
+ # Getting a conversion template for interface types can fail
+ # if we don't have a relevant descriptor when self.workers is True.
+ # If that happens, just mark ourselves as not being
+ # generatable and move on.
+ try:
+ self.memberInfo = [
+ (member,
+ getJSToNativeConversionTemplate(member.type,
+ descriptorProvider,
+ isMember=True,
+ isOptional=(not member.defaultValue),
+ defaultValue=member.defaultValue))
+ for member in dictionary.members ]
+ except NoSuchDescriptorError, err:
+ if not self.workers:
+ raise err
+ self.generatable = False
+
+ def declare(self):
+ if not self.generatable:
+ return ""
+ d = self.dictionary
+ if d.parent:
+ inheritance = ": public %s " % self.makeClassName(d.parent)
+ else:
+ inheritance = ""
+ memberDecls = [" %s %s;" %
+ (self.getMemberType(m), m[0].identifier.name)
+ for m in self.memberInfo]
+
+ return (string.Template(
+ "struct ${selfName} ${inheritance}{\n"
+ " ${selfName}() {}\n"
+ " bool Init(JSContext* cx, const JS::Value& val);\n"
+ "\n" +
+ "\n".join(memberDecls) + "\n"
+ "private:\n"
+ " // Disallow copy-construction\n"
+ " ${selfName}(const ${selfName}&) MOZ_DELETE;\n" +
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ (" static bool InitIds(JSContext* cx);\n"
+ " static bool initedIds;\n" if not self.workers else "") +
+ "\n".join(" static jsid " +
+ self.makeIdName(m.identifier.name) + ";" for
+ m in d.members) + "\n"
+ "};").substitute( { "selfName": self.makeClassName(d),
+ "inheritance": inheritance }))
+
+ def define(self):
+ if not self.generatable:
+ return ""
+ d = self.dictionary
+ if d.parent:
+ initParent = ("// Per spec, we init the parent's members first\n"
+ "if (!%s::Init(cx, val)) {\n"
+ " return false;\n"
+ "}\n" % self.makeClassName(d.parent))
+ else:
+ initParent = ""
+
+ memberInits = [CGIndenter(self.getMemberConversion(m)).define()
+ for m in self.memberInfo]
+ idinit = [CGGeneric('!InternJSString(cx, %s, "%s")' %
+ (m.identifier.name + "_id", m.identifier.name))
+ for m in d.members]
+ idinit = CGList(idinit, " ||\n")
+ idinit = CGWrapper(idinit, pre="if (",
+ post=(") {\n"
+ " return false;\n"
+ "}"),
+ reindent=True)
+
+ return string.Template(
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ ("bool ${selfName}::initedIds = false;\n" +
+ "\n".join("jsid ${selfName}::%s = JSID_VOID;" %
+ self.makeIdName(m.identifier.name)
+ for m in d.members) + "\n"
+ "\n"
+ "bool\n"
+ "${selfName}::InitIds(JSContext* cx)\n"
+ "{\n"
+ " MOZ_ASSERT(!initedIds);\n"
+ "${idInit}\n"
+ " initedIds = true;\n"
+ " return true;\n"
+ "}\n"
+ "\n" if not self.workers else "") +
+ "bool\n"
+ "${selfName}::Init(JSContext* cx, const JS::Value& val)\n"
+ "{\n" +
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ (" if (!initedIds && !InitIds(cx)) {\n"
+ " return false;\n"
+ " }\n" if not self.workers else "") +
+ "${initParent}"
+ " JSBool found;\n"
+ " JS::Value temp;\n"
+ " bool isNull = val.isNullOrUndefined();\n"
+ " if (!isNull && !val.isObject()) {\n"
+ " return Throw<${isMainThread}>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
+ " }\n"
+ "\n"
+ "${initMembers}\n"
+ " return true;\n"
+ "}").substitute({
+ "selfName": self.makeClassName(d),
+ "initParent": CGIndenter(CGGeneric(initParent)).define(),
+ "initMembers": "\n\n".join(memberInits),
+ "idInit": CGIndenter(idinit).define(),
+ "isMainThread": toStringBool(not self.workers)
+ })
+
+ @staticmethod
+ def makeDictionaryName(dictionary, workers):
+ suffix = "Workers" if workers else ""
+ return dictionary.identifier.name + suffix
+
+ def makeClassName(self, dictionary):
+ return self.makeDictionaryName(dictionary, self.workers)
+
+ def getMemberType(self, memberInfo):
+ (member, (templateBody, declType,
+ holderType, dealWithOptional)) = memberInfo
+ # We can't handle having a holderType here
+ assert holderType is None
+ if dealWithOptional:
+ declType = CGWrapper(declType, pre="Optional< ", post=" >")
+ return declType.define()
+
+ def getMemberConversion(self, memberInfo):
+ (member, (templateBody, declType,
+ holderType, dealWithOptional)) = memberInfo
+ replacements = { "val": "temp",
+ "valPtr": "&temp",
+ # Use this->%s to refer to members, because we don't
+ # control the member names and want to make sure we're
+ # talking about the member, not some local that
+ # shadows the member. Another option would be to move
+ # the guts of init to a static method which is passed
+ # an explicit reference to our dictionary object, so
+ # we couldn't screw this up even if we wanted to....
+ "declName": ("(this->%s)" % member.identifier.name),
+ # We need a holder name for external interfaces, but
+ # it's scoped down to the conversion so we can just use
+ # anything we want.
+ "holderName": "holder"}
+ # We can't handle having a holderType here
+ assert holderType is None
+ if dealWithOptional:
+ replacements["declName"] = "(" + replacements["declName"] + ".Value())"
+ if member.defaultValue:
+ replacements["haveValue"] = "found"
+
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ if self.workers:
+ propName = member.identifier.name
+ propCheck = ('JS_HasProperty(cx, &val.toObject(), "%s", &found)' %
+ propName)
+ propGet = ('JS_GetProperty(cx, &val.toObject(), "%s", &temp)' %
+ propName)
+ else:
+ propId = self.makeIdName(member.identifier.name);
+ propCheck = ("JS_HasPropertyById(cx, &val.toObject(), %s, &found)" %
+ propId)
+ propGet = ("JS_GetPropertyById(cx, &val.toObject(), %s, &temp)" %
+ propId)
+
+ conversionReplacements = {
+ "prop": "(this->%s)" % member.identifier.name,
+ "convert": string.Template(templateBody).substitute(replacements),
+ "propCheck": propCheck,
+ "propGet": propGet
+ }
+ conversion = ("if (isNull) {\n"
+ " found = false;\n"
+ "} else if (!${propCheck}) {\n"
+ " return false;\n"
+ "}\n")
+ if member.defaultValue:
+ conversion += (
+ "if (found) {\n"
+ " if (!${propGet}) {\n"
+ " return false;\n"
+ " }\n"
+ "}\n"
+ "${convert}")
+ else:
+ conversion += (
+ "if (found) {\n"
+ " ${prop}.Construct();\n"
+ " if (!${propGet}) {\n"
+ " return false;\n"
+ " }\n"
+ "${convert}\n"
+ "}")
+ conversionReplacements["convert"] = CGIndenter(
+ CGGeneric(conversionReplacements["convert"])).define()
+
+ return CGGeneric(
+ string.Template(conversion).substitute(conversionReplacements)
+ )
+
+ @staticmethod
+ def makeIdName(name):
+ return name + "_id"
+
+ @staticmethod
+ def getDictionaryDependencies(dictionary):
+ deps = set();
+ if dictionary.parent:
+ deps.add(dictionary.parent)
+ for member in dictionary.members:
+ if member.type.isDictionary():
+ deps.add(member.type.unroll().inner)
+ return deps
+
+
+class CGRegisterProtos(CGAbstractMethod):
+ def __init__(self, config):
+ CGAbstractMethod.__init__(self, None, 'Register', 'void',
+ [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')])
+ self.config = config
+
+ def _defineMacro(self):
+ return """
+#define REGISTER_PROTO(_dom_class, _pref_check) \\
+ aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_class), _dom_class##Binding::DefineDOMInterface, _pref_check);\n\n"""
+ def _undefineMacro(self):
+ return "\n#undef REGISTER_PROTO"
+ def _registerProtos(self):
+ def getPrefCheck(desc):
+ if desc.interface.getExtendedAttribute("PrefControlled") is None:
+ return "nullptr"
+ return "%sBinding::PrefEnabled" % desc.name
+ lines = ["REGISTER_PROTO(%s, %s);" % (desc.name, getPrefCheck(desc))
+ for desc in self.config.getDescriptors(hasInterfaceObject=True,
+ isExternal=False,
+ workers=False,
+ register=True)]
+ return '\n'.join(lines) + '\n'
+ def definition_body(self):
+ return self._defineMacro() + self._registerProtos() + self._undefineMacro()
+
+class CGBindingRoot(CGThing):
+ """
+ Root 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):
+ descriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ hasInterfaceOrInterfacePrototypeObject=True)
+ dictionaries = config.getDictionaries(webIDLFile)
+
+ forwardDeclares = [CGClassForwardDeclare('XPCWrappedNativeScope')]
+
+ descriptorsForForwardDeclaration = list(descriptors)
+ for dictionary in dictionaries:
+ curDict = dictionary
+ ifacemembers = []
+ while curDict:
+ ifacemembers.extend([m.type.unroll().inner for m
+ in curDict.members
+ if m.type.unroll().isInterface()])
+ curDict = curDict.parent
+ # Put in all the non-worker descriptors
+ descriptorsForForwardDeclaration.extend(
+ [config.getDescriptor(iface.identifier.name, False) for
+ iface in ifacemembers])
+ # And now the worker ones. But these may not exist, so we
+ # have to be more careful.
+ for iface in ifacemembers:
+ try:
+ descriptorsForForwardDeclaration.append(
+ config.getDescriptor(iface.identifier.name, True))
+ except NoSuchDescriptorError:
+ # just move along
+ pass
+
+ for x in descriptorsForForwardDeclaration:
+ nativeType = x.nativeType
+ components = x.nativeType.split('::')
+ className = components[-1]
+ # JSObject is a struct, not a class
+ declare = CGClassForwardDeclare(className, className is "JSObject")
+ if len(components) > 1:
+ declare = CGNamespace.build(components[:-1],
+ CGWrapper(declare, declarePre='\n',
+ declarePost='\n'),
+ declareOnly=True)
+ forwardDeclares.append(CGWrapper(declare, declarePost='\n'))
+
+ forwardDeclares = CGList(forwardDeclares)
+
+ descriptorsWithPrototype = filter(lambda d: d.interface.hasInterfacePrototypeObject(),
+ descriptors)
+ traitsClasses = [CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype]
+
+ # We must have a 1:1 mapping here, skip for prototypes that have more
+ # than one concrete class implementation.
+ traitsClasses.extend([CGPrototypeIDMapClass(d) for d in descriptorsWithPrototype
+ if d.uniqueImplementation])
+
+ # Wrap all of that in our namespaces.
+ if len(traitsClasses) > 0:
+ traitsClasses = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(CGList(traitsClasses),
+ declarePre='\n'),
+ declareOnly=True)
+ traitsClasses = CGWrapper(traitsClasses, declarePost='\n')
+ else:
+ traitsClasses = None
+
+ # Do codegen for all the enums
+ def makeEnum(e):
+ return CGNamespace.build([e.identifier.name + "Values"],
+ CGEnum(e))
+ def makeEnumTypedef(e):
+ return CGGeneric(declare=("typedef %sValues::valuelist %s;\n" %
+ (e.identifier.name, e.identifier.name)))
+ cgthings = [ fun(e) for e in config.getEnums(webIDLFile)
+ for fun in [makeEnum, makeEnumTypedef] ]
+
+ # Do codegen for all the dictionaries. We have to be a bit careful
+ # here, because we have to generate these in order from least derived
+ # to most derived so that class inheritance works out. We also have to
+ # generate members before the dictionary that contains them.
+ #
+ # XXXbz this will fail if we have two webidl files A and B such that A
+ # declares a dictionary which inherits from a dictionary in B and B
+ # declares a dictionary (possibly a different one!) that inherits from a
+ # dictionary in A. The good news is that I expect this to never happen.
+ reSortedDictionaries = []
+ dictionaries = set(dictionaries)
+ while len(dictionaries) != 0:
+ # Find the dictionaries that don't depend on anything else anymore
+ # and move them over.
+ toMove = [d for d in dictionaries if
+ len(CGDictionary.getDictionaryDependencies(d) &
+ dictionaries) == 0]
+ if len(toMove) == 0:
+ raise TypeError("Loop in dictionary dependency graph")
+ dictionaries = dictionaries - set(toMove)
+ reSortedDictionaries.extend(toMove)
+
+ dictionaries = reSortedDictionaries
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider(True))
+ for d in dictionaries])
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
+ for d in dictionaries])
+
+ # Do codegen for all the descriptors
+ cgthings.extend([CGDescriptor(x) for x in descriptors])
+
+ # And make sure we have the right number of newlines at the end
+ curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(curr, pre="\n"))
+
+ curr = CGList([forwardDeclares,
+ CGWrapper(CGGeneric("using namespace mozilla::dom;"),
+ defineOnly=True),
+ traitsClasses, curr],
+ "\n")
+
+ # Add header includes.
+ curr = CGHeaders(descriptors,
+ dictionaries,
+ ['mozilla/dom/BindingUtils.h',
+ 'mozilla/dom/DOMJSClass.h',
+ 'mozilla/dom/DOMJSProxyHandler.h'],
+ ['mozilla/dom/Nullable.h',
+ 'PrimitiveConversions.h',
+ 'XPCQuickStubs.h',
+ 'nsDOMQS.h',
+ 'AccessCheck.h',
+ 'WorkerPrivate.h',
+ 'nsContentUtils.h',
+ 'mozilla/Preferences.h',
+ # Have to include nsDOMQS.h to get fast arg unwrapping
+ # for old-binding things with castability.
+ 'nsDOMQS.h'
+ ],
+ curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard(prefix, curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Store the final result.
+ self.root = curr
+
+ def declare(self):
+ return stripTrailingWhitespace(self.root.declare())
+ def define(self):
+ return stripTrailingWhitespace(self.root.define())
+
+
+class GlobalGenRoots():
+ """
+ Roots for global codegen.
+
+ To generate code, call the method associated with the target, and then
+ call the appropriate define/declare method.
+ """
+
+ @staticmethod
+ def PrototypeList(config):
+
+ # Prototype ID enum.
+ protos = [d.name for d in config.getDescriptors(hasInterfacePrototypeObject=True)]
+ idEnum = CGNamespacedEnum('id', 'ID', protos, [0])
+ idEnum = CGList([idEnum])
+ idEnum.append(CGGeneric(declare="const unsigned MaxProtoChainLength = " +
+ str(config.maxProtoChainLength) + ";\n\n"))
+
+ # Wrap all of that in our namespaces.
+ idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
+ CGWrapper(idEnum, pre='\n'))
+ idEnum = CGWrapper(idEnum, post='\n')
+
+ curr = CGList([idEnum])
+
+ # Constructor ID enum.
+ constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True,
+ hasInterfacePrototypeObject=False)]
+ idEnum = CGNamespacedEnum('id', 'ID', constructors, [0])
+
+ # Wrap all of that in our namespaces.
+ idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
+ CGWrapper(idEnum, pre='\n'))
+ idEnum = CGWrapper(idEnum, post='\n')
+
+ curr.append(idEnum)
+
+ traitsDecl = CGGeneric(declare="""
+template <prototypes::ID PrototypeID>
+struct PrototypeTraits;
+
+template <class ConcreteClass>
+struct PrototypeIDMap;
+""")
+
+ traitsDecl = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(traitsDecl, post='\n'))
+
+ curr.append(traitsDecl)
+
+ # Add include guards.
+ curr = CGIncludeGuard('PrototypeList', curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def RegisterBindings(config):
+
+ # TODO - Generate the methods we want
+ curr = CGRegisterProtos(config)
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(curr, post='\n'))
+ curr = CGWrapper(curr, post='\n')
+
+ # Add the includes
+ defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
+ for desc in config.getDescriptors(hasInterfaceObject=True,
+ workers=False,
+ register=True)]
+ defineIncludes.append('nsScriptNameSpaceManager.h')
+ curr = CGHeaders([], [], [], defineIncludes, curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('RegisterBindings', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionTypes(config):
+
+ (includes, declarations, unions) = UnionTypes(config.getDescriptors())
+ includes.add("mozilla/dom/BindingUtils.h")
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ namespaces = []
+ stack = [CGList([])]
+ for (clazz, isStruct) in SortedTuples(declarations):
+ elements = clazz.split("::")
+ clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct)
+ i = 0
+ if len(elements) > 0:
+ common = min(len(namespaces), len(elements))
+ while i < common and namespaces[i] == elements[i]:
+ i += 1
+
+ # pop all the namespaces that should be closed
+ namespaces = namespaces[:i]
+
+ # add all the namespaces that should be opened
+ for j, namespace in enumerate(elements[i:]):
+ namespaces.append(namespace)
+ # every CGNamespace that we add holds a CGList
+ list = CGList([])
+ # add the new namespace to the list on top of the stack
+ stack[i + j].append(CGNamespace(namespace, list))
+ # set the top of the namespace stack to the list of the new
+ # namespace
+ stack[i + j + 1:] = [list]
+
+ stack[len(elements)].append(clazz)
+
+ curr = CGList([stack[0], curr], "\n")
+
+ curr = CGHeaders([], [], includes, [], curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('UnionTypes', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionConversions(config):
+
+ unions = UnionConversions(config.getDescriptors())
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ curr = CGHeaders([], [], ["nsDebug.h", "mozilla/dom/UnionTypes.h", "nsDOMQS.h"], [], curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('UnionConversions', curr)
+
+ # Done.
+ return curr
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
new file mode 100644
index 00000000000..1666589940e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -0,0 +1,5534 @@
+# 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/.
+
+# Common codegen classes.
+
+import operator
+import os
+import re
+import string
+
+from WebIDL import (
+ BuiltinTypes,
+ IDLBuiltinType,
+ IDLNullValue,
+ IDLType,
+ IDLUndefinedValue,
+)
+
+from Configuration import getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback
+
+AUTOGENERATED_WARNING_COMMENT = \
+ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
+ADDPROPERTY_HOOK_NAME = '_addProperty'
+FINALIZE_HOOK_NAME = '_finalize'
+TRACE_HOOK_NAME = '_trace'
+CONSTRUCT_HOOK_NAME = '_constructor'
+HASINSTANCE_HOOK_NAME = '_hasInstance'
+
+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:
+ # oldFile = open(filename, 'rb')
+ # oldFileContents = ''.join(oldFile.readlines())
+ # oldFile.close()
+ #except:
+ # pass
+
+ #if newContents == oldFileContents:
+ # return False
+
+ f = open(filename, 'wb')
+ f.write(newContents)
+ f.close()
+
+ return True
+
+def toStringBool(arg):
+ return str(not not arg).lower()
+
+def toBindingNamespace(arg):
+ return re.sub("((_workers)?$)", "Binding\\1", arg);
+
+def stripTrailingWhitespace(text):
+ tail = '\n' if text.endswith('\n') else ''
+ lines = text.splitlines()
+ for i in range(len(lines)):
+ lines[i] = lines[i].rstrip()
+ return '\n'.join(lines) + tail
+
+def MakeNativeName(name):
+ return name[0].upper() + name[1:]
+
+builtinNames = {
+ IDLType.Tags.bool: 'bool',
+ IDLType.Tags.int8: 'i8',
+ IDLType.Tags.int16: 'i16',
+ IDLType.Tags.int32: 'i32',
+ IDLType.Tags.int64: 'i64',
+ IDLType.Tags.uint8: 'u8',
+ IDLType.Tags.uint16: 'u16',
+ IDLType.Tags.uint32: 'u32',
+ IDLType.Tags.uint64: 'u64',
+ IDLType.Tags.float: 'f32',
+ IDLType.Tags.double: 'f64'
+}
+
+numericTags = [
+ IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double
+ ]
+
+class CastableObjectUnwrapper():
+ """
+ A class for unwrapping an object named by the "source" argument
+ based on the passed-in descriptor. Stringifies to a Rust expression of
+ the appropriate type.
+
+ codeOnFailure is the code to run if unwrapping fails.
+ """
+ def __init__(self, descriptor, source, codeOnFailure):
+ self.substitution = {
+ "type": descriptor.nativeType,
+ "depth": descriptor.interface.inheritanceDepth(),
+ "prototype": "PrototypeList::id::" + descriptor.name,
+ "protoID": "PrototypeList::id::" + descriptor.name + " as uint",
+ "source": source,
+ "codeOnFailure": CGIndenter(CGGeneric(codeOnFailure), 4).define(),
+ }
+
+ def __str__(self):
+ return string.Template(
+"""match unwrap_jsmanaged(${source}, ${prototype}, ${depth}) {
+ Ok(val) => val,
+ Err(()) => {
+${codeOnFailure}
+ }
+}""").substitute(self.substitution)
+
+
+class CGThing():
+ """
+ Abstract base class for things that spit out code.
+ """
+ def __init__(self):
+ pass # Nothing for now
+
+ def define(self):
+ """Produce code for a Rust file."""
+ assert(False) # Override me!
+
+
+class CGNativePropertyHooks(CGThing):
+ """
+ Generate a NativePropertyHooks for a given descriptor
+ """
+ def __init__(self, descriptor, properties):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.properties = properties
+
+ def define(self):
+ parent = self.descriptor.interface.parent
+ if parent:
+ parentHooks = "Some(&::dom::bindings::codegen::Bindings::%sBinding::sNativePropertyHooks)" % parent.identifier.name
+ else:
+ parentHooks = "None"
+
+ substitutions = {
+ "parentHooks": parentHooks
+ }
+
+ return string.Template(
+ "pub static sNativePropertyHooks: NativePropertyHooks = NativePropertyHooks {\n"
+ " native_properties: &sNativeProperties,\n"
+ " proto_hooks: ${parentHooks},\n"
+ "};\n").substitute(substitutions)
+
+
+class CGMethodCall(CGThing):
+ """
+ A class to generate selection of a method signature from a set of
+ signatures and generation of a call to that signature.
+ """
+ def __init__(self, argsPre, nativeMethodName, static, descriptor, method):
+ CGThing.__init__(self)
+
+ methodName = '\\"%s.%s\\"' % (descriptor.interface.identifier.name, method.identifier.name)
+
+ def requiredArgCount(signature):
+ arguments = signature[1]
+ if len(arguments) == 0:
+ return 0
+ requiredArgs = len(arguments)
+ while requiredArgs and arguments[requiredArgs-1].optional:
+ requiredArgs -= 1
+ return requiredArgs
+
+ def getPerSignatureCall(signature, argConversionStartsAt=0, signatureIndex=0):
+ return CGPerSignatureCall(signature[0], argsPre, signature[1],
+ nativeMethodName + '_'*signatureIndex,
+ static, descriptor,
+ method, argConversionStartsAt)
+
+
+ signatures = method.signatures()
+ if len(signatures) == 1:
+ # Special case: we can just do a per-signature method call
+ # here for our one signature and not worry about switching
+ # on anything.
+ signature = signatures[0]
+ self.cgRoot = CGList([getPerSignatureCall(signature)])
+ requiredArgs = requiredArgCount(signature)
+
+
+ if requiredArgs > 0:
+ code = (
+ "if argc < %d {\n"
+ " throw_type_error(cx, \"Not enough arguments to %s.\");\n"
+ " return 0;\n"
+ "}" % (requiredArgs, methodName))
+ self.cgRoot.prepend(
+ CGWrapper(CGGeneric(code), pre="\n", post="\n"))
+
+ return
+
+ # Need to find the right overload
+ maxArgCount = method.maxArgCount
+ allowedArgCounts = method.allowedArgCounts
+
+ argCountCases = []
+ for argCount in allowedArgCounts:
+ possibleSignatures = method.signaturesForArgCount(argCount)
+ if len(possibleSignatures) == 1:
+ # easy case!
+ signature = possibleSignatures[0]
+
+
+ sigIndex = signatures.index(signature)
+ argCountCases.append(
+ CGCase(str(argCount), getPerSignatureCall(signature,
+ signatureIndex=sigIndex)))
+ continue
+
+ distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
+
+ # We can't handle unions 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)
+
+ # Convert all our arguments up to the distinguishing index.
+ # Doesn't matter which of the possible signatures we use, since
+ # they all have the same types up to that point; just use
+ # possibleSignatures[0]
+ caseBody = [CGGeneric("let argv_start = JS_ARGV(cx, vp);")]
+ caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i],
+ i, "argv_start", "argc",
+ descriptor) for i in
+ range(0, distinguishingIndex) ])
+
+ # Select the right overload from our set.
+ distinguishingArg = "(*argv_start.offset(%d))" % distinguishingIndex
+
+ def pickFirstSignature(condition, filterLambda):
+ sigs = filter(filterLambda, possibleSignatures)
+ assert len(sigs) < 2
+ if len(sigs) > 0:
+ if condition is None:
+ caseBody.append(
+ getPerSignatureCall(sigs[0], distinguishingIndex,
+ possibleSignatures.index(sigs[0])))
+ else:
+ caseBody.append(CGGeneric("if " + condition + " {"))
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sigs[0], distinguishingIndex,
+ possibleSignatures.index(sigs[0]))))
+ caseBody.append(CGGeneric("}"))
+ return True
+ return False
+
+ # First check for null or undefined
+ pickFirstSignature("%s.isNullOrUndefined()" % distinguishingArg,
+ 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()) ]
+ # There might be more than one of these; we need to check
+ # which ones we unwrap to.
+
+ if len(interfacesSigs) > 0:
+ # The spec says that we should check for "platform objects
+ # implementing an interface", but it's enough to guard on these
+ # being an object. The code for unwrapping non-callback
+ # interfaces and typed arrays will just bail out and move on to
+ # the next overload if the object fails to unwrap correctly. We
+ # could even not do the isObject() check up front here, but in
+ # cases where we have multiple object overloads it makes sense
+ # to do it only once instead of for each overload. That will
+ # also allow the unwrapping test to skip having to do codegen
+ # for the null-or-undefined case, which we already handled
+ # above.
+ caseBody.append(CGGeneric("if (%s).is_object() {" %
+ (distinguishingArg)))
+ for idx, sig in enumerate(interfacesSigs):
+ caseBody.append(CGIndenter(CGGeneric("loop {")));
+ type = sig[1][distinguishingIndex].type
+
+ # The argument at index distinguishingIndex can't possibly
+ # be unset here, because we've already checked that argc is
+ # large enough that we can examine this argument.
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ type, descriptor, failureCode="break;", isDefinitelyObject=True)
+
+ testCode = instantiateJSToNativeConversionTemplate(
+ template,
+ {"val": distinguishingArg},
+ declType,
+ "arg%d" % distinguishingIndex,
+ needsRooting)
+
+ # Indent by 4, since we need to indent further than our "do" statement
+ caseBody.append(CGIndenter(testCode, 4));
+ # If we got this far, we know we unwrapped to the right
+ # interface, so just do the call. Start conversion with
+ # distinguishingIndex + 1, since we already converted
+ # distinguishingIndex.
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sig, distinguishingIndex + 1, idx), 4))
+ caseBody.append(CGIndenter(CGGeneric("}")))
+
+ caseBody.append(CGGeneric("}"))
+
+ # 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.isObject() && IsArrayLike(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s:
+ (s[1][distinguishingIndex].type.isArray() or
+ 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.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isDate() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for vanilla JS objects
+ # XXXbz Do we need to worry about security wrappers?
+ pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" %
+ (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()))
+
+ # 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())):
+ pass
+ # Check for primitives
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isPrimitive()):
+ pass
+ # Check for "any"
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isAny()):
+ pass
+ 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);"))
+
+ argCountCases.append(CGCase(str(argCount),
+ CGList(caseBody, "\n")))
+
+ overloadCGThings = []
+ overloadCGThings.append(
+ CGGeneric("let argcount = cmp::min(argc, %d);" %
+ maxArgCount))
+ overloadCGThings.append(
+ CGSwitch("argcount",
+ argCountCases,
+ CGGeneric("throw_type_error(cx, \"Not enough arguments to %s.\");\n"
+ "return 0;\n" % methodName)))
+ #XXXjdm Avoid unreachable statement warnings
+ #overloadCGThings.append(
+ # CGGeneric('fail!("We have an always-returning default case");\n'
+ # 'return 0;'))
+ self.cgRoot = CGWrapper(CGList(overloadCGThings, "\n"),
+ pre="\n")
+
+ def define(self):
+ return self.cgRoot.define()
+
+class FakeCastableDescriptor():
+ def __init__(self, descriptor):
+ self.nativeType = "*const %s" % descriptor.concreteType
+ self.name = descriptor.name
+ class FakeInterface:
+ def inheritanceDepth(self):
+ return descriptor.interface.inheritanceDepth()
+ self.interface = FakeInterface()
+
+def dictionaryHasSequenceMember(dictionary):
+ return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ dictionary.members) or
+ (dictionary.parent and
+ dictionaryHasSequenceMember(dictionary.parent)))
+
+def typeIsSequenceOrHasSequenceMember(type):
+ if type.nullable():
+ type = type.inner
+ if type.isSequence():
+ return True
+ if type.isArray():
+ elementType = type.inner
+ return typeIsSequenceOrHasSequenceMember(elementType)
+ if type.isDictionary():
+ return dictionaryHasSequenceMember(type.inner)
+ if type.isUnion():
+ return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ type.flatMemberTypes)
+ return False
+
+def typeNeedsRooting(type, descriptorProvider):
+ return type.isGeckoInterface() and descriptorProvider.getDescriptor(type.name).needsRooting
+
+def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
+ isDefinitelyObject=False,
+ isMember=False,
+ isArgument=False,
+ invalidEnumValueFatal=True,
+ defaultValue=None,
+ treatNullAs="Default",
+ isEnforceRange=False,
+ isClamp=False,
+ exceptionCode=None,
+ allowTreatNonObjectAsNull=False,
+ isCallbackReturnValue=False,
+ sourceDescription="value"):
+ """
+ Get a template for converting a JS value to a native object based on the
+ given type and descriptor. If failureCode is given, then we're actually
+ testing whether we can convert the argument to the desired type. That
+ means that failures to convert due to the JS value being the wrong type of
+ value need to use failureCode instead of throwing exceptions. Failures to
+ convert that are due to JS exceptions (from toString or valueOf methods) or
+ out of memory conditions need to throw exceptions no matter what
+ failureCode is.
+
+ If isDefinitelyObject is True, that means we know the value
+ isObject() and we have no need to recheck that.
+
+ if isMember is True, we're being converted from a property of some
+ JS object, not from an actual method argument, so we can't rely on
+ our jsval being rooted or outliving us in any way. Any caller
+ passing true needs to ensure that it is handled correctly in
+ typeIsSequenceOrHasSequenceMember.
+
+ invalidEnumValueFatal controls whether an invalid enum value conversion
+ attempt will throw (if true) or simply return without doing anything (if
+ false).
+
+ 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.
+
+ The return value from this function is a tuple consisting of four things:
+
+ 1) A string representing the conversion code. This will have template
+ substitution performed on it as follows:
+
+ ${val} replaced by an expression for the JS::Value in question
+
+ 2) A string or None representing Rust code for the default value (if any).
+
+ 3) A CGThing representing the native C++ type we're converting to
+ (declType). This is allowed to be None if the conversion code is
+ supposed to be used as-is.
+
+ 4) A boolean indicating whether the caller has to root the result.
+
+ """
+ # We should not have a defaultValue if we know we're an object
+ assert(not isDefinitelyObject or defaultValue is None)
+
+ # If exceptionCode is not set, we'll just rethrow the exception we got.
+ # Note that we can't just set failureCode to exceptionCode, because setting
+ # failureCode will prevent pending exceptions from being set in cases when
+ # they really should be!
+ if exceptionCode is None:
+ exceptionCode = "return 0;"
+
+ needsRooting = typeNeedsRooting(type, descriptorProvider)
+
+ def handleOptional(template, declType, default):
+ assert (defaultValue is None) == (default is None)
+ return (template, default, declType, needsRooting)
+
+ # Unfortunately, .capitalize() on a string will lowercase things inside the
+ # string, which we do not want.
+ def firstCap(string):
+ return string[0].upper() + string[1:]
+
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ def onFailureNotAnObject(failureCode):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, "%s is not an object.");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode))),
+ post="\n")
+ def onFailureBadType(failureCode, typeName):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, \"%s does not implement interface %s.\");\n'
+ '%s' % (firstCap(sourceDescription), typeName,
+ exceptionCode))),
+ post="\n")
+ def onFailureNotCallable(failureCode):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, \"%s is not callable.\");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode))),
+ post="\n")
+
+
+ # A helper function for handling null default values. Checks that the
+ # default value, if it exists, is null.
+ def handleDefaultNull(nullValue):
+ if defaultValue is None:
+ return None
+
+ if not isinstance(defaultValue, IDLNullValue):
+ raise TypeError("Can't handle non-null default value here")
+
+ assert type.nullable() or type.isDictionary()
+ return nullValue
+
+ # A helper function for wrapping up the template body for
+ # possibly-nullable objecty stuff
+ def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+ failureCode=None):
+ if not isDefinitelyObject:
+ # Handle the non-object cases by wrapping up the whole
+ # thing in an if cascade.
+ templateBody = (
+ "if (${val}).is_object() {\n" +
+ CGIndenter(CGGeneric(templateBody)).define() + "\n")
+ if type.nullable():
+ templateBody += (
+ "} else if (${val}).is_null_or_undefined() {\n"
+ " None\n")
+ templateBody += (
+ "} else {\n" +
+ CGIndenter(onFailureNotAnObject(failureCode)).define() +
+ "}\n")
+
+ return templateBody
+
+ assert not (isEnforceRange and isClamp) # These are mutually exclusive
+
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ raise TypeError("Can't handle sequence arguments yet")
+
+ if type.isUnion():
+ declType = CGGeneric(type.name + "::" + type.name)
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=" >")
+
+ templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(value) => value,\n"
+ " Err(()) => { %s },\n"
+ "}" % exceptionCode)
+
+ return handleOptional(templateBody, declType, handleDefaultNull("None"))
+
+ if type.isGeckoInterface():
+ assert not isEnforceRange and not isClamp
+
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+
+ if descriptor.interface.isCallback():
+ name = descriptor.nativeType
+ declType = CGGeneric("Option<%s>" % name);
+ conversion = ("Some(%s::new((${val}).to_object()))" % name)
+
+ template = wrapObjectTemplate(conversion, isDefinitelyObject, type,
+ failureCode)
+ return handleOptional(template, declType, handleDefaultNull("None"))
+
+ if isMember:
+ descriptorType = descriptor.memberType
+ elif isArgument:
+ descriptorType = descriptor.argumentType
+ else:
+ descriptorType = descriptor.nativeType
+
+ templateBody = ""
+ 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 = string.Template(
+ 'throw_type_error(cx, "${sourceDescription} does not '
+ 'implement interface ${interface}.");\n'
+ '${exceptionCode}').substitute(substitutions)
+ else:
+ unwrapFailureCode = failureCode
+
+ templateBody = str(CastableObjectUnwrapper(
+ descriptor,
+ "(${val}).to_object()",
+ unwrapFailureCode))
+
+ declType = CGGeneric(descriptorType)
+ if type.nullable():
+ templateBody = "Some(%s)" % templateBody
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ if isMember:
+ templateBody += ".root()"
+
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type, failureCode)
+
+ return handleOptional(templateBody, declType, handleDefaultNull("None"))
+
+ if type.isSpiderMonkeyInterface():
+ raise TypeError("Can't handle SpiderMonkey interface arguments yet")
+
+ if type.isDOMString():
+ assert not isEnforceRange and not isClamp
+
+ treatAs = {
+ "Default": "Default",
+ "EmptyString": "Empty",
+ }
+ if treatNullAs not in treatAs:
+ raise TypeError("We don't support [TreatNullAs=%s]" % treatNullAs)
+ if type.nullable():
+ nullBehavior = "()"
+ else:
+ nullBehavior = treatAs[treatNullAs]
+
+ conversionCode = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
+ " Ok(strval) => strval,\n"
+ " Err(_) => { %s },\n"
+ "}" % (nullBehavior, exceptionCode))
+
+ if defaultValue is None:
+ default = None
+ elif isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ default = "None"
+ else:
+ assert defaultValue.type.tag() == IDLType.Tags.domstring
+ value = "str::from_utf8(data).unwrap().to_string()"
+ if type.nullable():
+ value = "Some(%s)" % value
+
+ default = (
+ "static data: [u8, ..%s] = [ %s ];\n"
+ "%s" %
+ (len(defaultValue.value) + 1,
+ ", ".join(["'" + char + "' as u8" for char in defaultValue.value] + ["0"]),
+ value))
+
+ declType = "DOMString"
+ if type.nullable():
+ declType = "Option<%s>" % declType
+
+ return handleOptional(conversionCode, CGGeneric(declType), default)
+
+ if type.isByteString():
+ assert not isEnforceRange and not isClamp
+
+ conversionCode = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(strval) => strval,\n"
+ " Err(_) => { %s },\n"
+ "}" % exceptionCode)
+
+ declType = CGGeneric("ByteString")
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ return handleOptional(conversionCode, declType, handleDefaultNull("None"))
+
+ if type.isEnum():
+ assert not isEnforceRange and not isClamp
+
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments "
+ "yet")
+ enum = type.inner.identifier.name
+ if invalidEnumValueFatal:
+ handleInvalidEnumValueCode = exceptionCode
+ else:
+ handleInvalidEnumValueCode = "return 1;"
+
+ template = (
+ "match FindEnumStringIndex(cx, ${val}, %(values)s) {\n"
+ " Err(_) => { %(exceptionCode)s },\n"
+ " Ok(None) => { %(handleInvalidEnumValueCode)s },\n"
+ " Ok(Some(index)) => {\n"
+ " //XXXjdm need some range checks up in here.\n"
+ " unsafe { mem::transmute(index) }\n"
+ " },\n"
+ "}" % { "values" : enum + "Values::strings",
+ "exceptionCode" : exceptionCode,
+"handleInvalidEnumValueCode" : handleInvalidEnumValueCode })
+
+ if defaultValue is not None:
+ assert(defaultValue.type.tag() == IDLType.Tags.domstring)
+ default = "%sValues::%s" % (enum, getEnumValueName(defaultValue.value))
+ else:
+ default = None
+
+ return handleOptional(template, CGGeneric(enum), default)
+
+ if type.isCallback():
+ assert not isEnforceRange and not isClamp
+ assert not type.treatNonCallableAsNull()
+ assert not type.treatNonObjectAsNull() or type.nullable()
+ assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
+
+ declType = CGGeneric('%s::%s' % (type.unroll().module(), type.unroll().identifier.name))
+
+ conversion = CGCallbackTempRoot(declType.define())
+
+ if type.nullable():
+ declType = CGTemplatedType("Option", declType)
+ conversion = CGWrapper(conversion, pre="Some(", post=")")
+
+ if allowTreatNonObjectAsNull and type.treatNonObjectAsNull():
+ if not isDefinitelyObject:
+ haveObject = "${val}.is_object()"
+ template = CGIfElseWrapper(haveObject,
+ conversion,
+ CGGeneric("None")).define()
+ else:
+ template = conversion
+ else:
+ template = CGIfElseWrapper("JS_ObjectIsCallable(cx, ${val}.to_object()) != 0",
+ conversion,
+ onFailureNotCallable(failureCode)).define()
+ template = wrapObjectTemplate(
+ template,
+ isDefinitelyObject,
+ type,
+ failureCode)
+
+ if defaultValue is not None:
+ assert allowTreatNonObjectAsNull
+ assert type.treatNonObjectAsNull()
+ assert type.nullable()
+ assert isinstance(defaultValue, IDLNullValue)
+ default = "None"
+ else:
+ default = None
+
+ return (template, default, declType, needsRooting)
+
+ if type.isAny():
+ assert not isEnforceRange and not isClamp
+
+ declType = CGGeneric("JSVal")
+
+ if defaultValue is None:
+ default = None
+ elif isinstance(defaultValue, IDLNullValue):
+ default = "NullValue()"
+ elif isinstance(defaultValue, IDLUndefinedValue):
+ default = "UndefinedValue()"
+ else:
+ raise TypeError("Can't handle non-null, non-undefined default value here")
+
+ return handleOptional("${val}", declType, default)
+
+ if type.isObject():
+ raise TypeError("Can't handle object arguments yet")
+
+ if type.isDictionary():
+ if failureCode is not None:
+ raise TypeError("Can't handle dictionaries when failureCode is not None")
+ # There are no nullable dictionaries
+ assert not type.nullable()
+
+ typeName = CGDictionary.makeDictionaryName(type.inner)
+ declType = CGGeneric(typeName)
+ template = ("match %s::new(cx, ${val}) {\n"
+ " Ok(dictionary) => dictionary,\n"
+ " Err(_) => return 0,\n"
+ "}" % typeName)
+
+ return handleOptional(template, declType, handleDefaultNull("%s::empty()" % typeName))
+
+ if type.isVoid():
+ # This one only happens for return values, and its easy: Just
+ # ignore the jsval.
+ return ("", None, None, False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need conversion for argument type '%s'" % str(type))
+
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is None:
+ failureCode = 'return 0'
+
+ declType = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ #XXXjdm support conversionBehavior here
+ template = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(v) => v,\n"
+ " Err(_) => { %s }\n"
+ "}" % exceptionCode)
+
+ if defaultValue is not None:
+ if isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ defaultStr = "None"
+ else:
+ tag = defaultValue.type.tag()
+ if tag in numericTags:
+ defaultStr = str(defaultValue.value)
+ else:
+ assert(tag == IDLType.Tags.bool)
+ defaultStr = toStringBool(defaultValue.value)
+
+ if type.nullable():
+ defaultStr = "Some(%s)" % defaultStr
+ else:
+ defaultStr = None
+
+ return handleOptional(template, declType, defaultStr)
+
+def instantiateJSToNativeConversionTemplate(templateBody, replacements,
+ declType, declName, needsRooting):
+ """
+ Take the templateBody and declType as returned by
+ getJSToNativeConversionTemplate, a set of replacements as required by the
+ strings in such a templateBody, and a declName, and generate code to
+ convert into a stack Rust binding with that name.
+ """
+ result = CGList([], "\n")
+
+ conversion = CGGeneric(
+ string.Template(templateBody).substitute(replacements)
+ )
+
+ if declType is not None:
+ newDecl = [
+ CGGeneric("let "),
+ CGGeneric(declName),
+ CGGeneric(": "),
+ declType,
+ CGGeneric(" = "),
+ conversion,
+ CGGeneric(";"),
+ ]
+ result.append(CGList(newDecl))
+ else:
+ result.append(conversion)
+
+ # Add an empty CGGeneric to get an extra newline after the argument
+ # conversion.
+ result.append(CGGeneric(""))
+
+ if needsRooting:
+ rootBody = "let %s = %s.root();" % (declName, declName)
+ result.append(CGGeneric(rootBody))
+ result.append(CGGeneric(""))
+
+ return result;
+
+def convertConstIDLValueToJSVal(value):
+ if isinstance(value, IDLNullValue):
+ return "NullVal"
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return "IntVal(%s)" % (value.value)
+ if tag == IDLType.Tags.uint32:
+ return "UintVal(%s)" % (value.value)
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
+ return "DoubleVal(%s)" % (value.value)
+ if tag == IDLType.Tags.bool:
+ return "BoolVal(true)" if value.value else "BoolVal(false)"
+ if tag in [IDLType.Tags.float, IDLType.Tags.double]:
+ return "DoubleVal(%s)" % (value.value)
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGArgumentConverter(CGThing):
+ """
+ A class that takes an IDL argument object, its index in the
+ argument list, and the argv and argc strings and generates code to
+ unwrap the argument to the right native type.
+ """
+ def __init__(self, argument, index, argv, argc, descriptorProvider,
+ invalidEnumValueFatal=True):
+ CGThing.__init__(self)
+ assert(not argument.defaultValue or argument.optional)
+
+ replacer = {
+ "index": index,
+ "argc": argc,
+ "argv": argv
+ }
+ condition = string.Template("${index} < ${argc}").substitute(replacer)
+
+ replacementVariables = {
+ "val": string.Template("(*${argv}.offset(${index}))").substitute(replacer),
+ }
+
+ template, default, declType, needsRooting = getJSToNativeConversionTemplate(
+ argument.type,
+ descriptorProvider,
+ invalidEnumValueFatal=invalidEnumValueFatal,
+ defaultValue=argument.defaultValue,
+ treatNullAs=argument.treatNullAs,
+ isEnforceRange=argument.enforceRange,
+ isClamp=argument.clamp,
+ isMember="Variadic" if argument.variadic else False,
+ allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull())
+
+ if not argument.variadic:
+ if argument.optional:
+ if argument.defaultValue:
+ assert default
+ template = CGIfElseWrapper(condition,
+ CGGeneric(template),
+ CGGeneric(default)).define()
+ else:
+ assert not default
+ declType = CGWrapper(declType, pre="Option<", post=">")
+ template = CGIfElseWrapper(condition,
+ CGGeneric("Some(%s)" % template),
+ CGGeneric("None")).define()
+ else:
+ assert not default
+
+ self.converter = instantiateJSToNativeConversionTemplate(
+ template, replacementVariables, declType, "arg%d" % index,
+ needsRooting)
+ else:
+ assert argument.optional
+ variadicConversion = {
+ "val": string.Template("(*${argv}.offset(variadicArg as int))").substitute(replacer),
+ }
+ innerConverter = instantiateJSToNativeConversionTemplate(
+ template, variadicConversion, declType, "slot",
+ needsRooting)
+
+ seqType = CGTemplatedType("Vec", declType)
+ variadicConversion = string.Template(
+ "{\n"
+ " let mut vector: ${seqType} = Vec::with_capacity((${argc} - ${index}) as uint);\n"
+ " for variadicArg in range(${index}, ${argc}) {\n"
+ "${inner}\n"
+ " vector.push(slot);\n"
+ " }\n"
+ " vector\n"
+ "}"
+ ).substitute({
+ "index": index,
+ "argc": argc,
+ "seqType": seqType.define(),
+ "inner": CGIndenter(innerConverter, 4).define(),
+ })
+
+ self.converter = instantiateJSToNativeConversionTemplate(
+ variadicConversion, replacementVariables, seqType, "arg%d" % index,
+ False)
+
+ def define(self):
+ return self.converter.define()
+
+
+def wrapForType(jsvalRef, result='result', successCode='return 1;'):
+ """
+ Reflect a Rust value into JS.
+
+ * 'jsvalRef': a Rust reference to the JSVal in which to store the result
+ of the conversion;
+ * 'result': the name of the variable in which the Rust value is stored;
+ * 'successCode': the code to run once we have done the conversion.
+ """
+ return "%s = (%s).to_jsval(cx);\n%s" % (jsvalRef, result, successCode)
+
+
+def typeNeedsCx(type, retVal=False):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ if type.isSequence() or type.isArray():
+ type = type.inner
+ if type.isUnion():
+ return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
+ if retVal and type.isSpiderMonkeyInterface():
+ return True
+ return type.isAny() or type.isObject()
+
+def typeRetValNeedsRooting(type):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ return type.isGeckoInterface() and not type.isCallback() and not type.isCallbackInterface()
+
+def memberIsCreator(member):
+ return member.getExtendedAttribute("Creator") is not None
+
+# Returns a CGThing containing the type of the return value.
+def getRetvalDeclarationForType(returnType, descriptorProvider):
+ if returnType is None or returnType.isVoid():
+ # Nothing to declare
+ return CGGeneric("()")
+ if returnType.isPrimitive() and returnType.tag() in builtinNames:
+ result = CGGeneric(builtinNames[returnType.tag()])
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isDOMString():
+ result = CGGeneric("DOMString")
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isByteString():
+ result = CGGeneric("ByteString")
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isEnum():
+ result = CGGeneric(returnType.unroll().inner.identifier.name)
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name)
+ result = CGGeneric(descriptor.returnType)
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isCallback():
+ result = CGGeneric('%s::%s' % (returnType.unroll().module(),
+ returnType.unroll().identifier.name))
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isUnion():
+ result = CGGeneric('%s::%s' % (returnType.unroll().name, returnType.unroll().name))
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isAny():
+ return CGGeneric("JSVal")
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return CGGeneric("*mut JSObject")
+ if returnType.isSequence():
+ raise TypeError("We don't support sequence return values")
+
+ raise TypeError("Don't know how to declare return value for %s" %
+ returnType)
+
+class PropertyDefiner:
+ """
+ A common superclass for defining things on prototype objects.
+
+ Subclasses should implement generateArray to generate the actual arrays of
+ things we're defining. They should also set self.regular to the list of
+ things exposed to web pages.
+ """
+ def __init__(self, descriptor, name):
+ self.descriptor = descriptor
+ self.name = name
+
+ def variableName(self):
+ return "s" + self.name
+
+ def length(self):
+ return len(self.regular)
+
+ def __str__(self):
+ # We only need to generate id arrays for things that will end
+ # up used via ResolveProperty or EnumerateProperties.
+ return self.generateArray(self.regular, self.variableName())
+
+ def generatePrefableArray(self, array, name, specTemplate, specTerminator,
+ specType, getDataTuple):
+ """
+ This method generates our various arrays.
+
+ array is an array of interface members as passed to generateArray
+
+ name is the name as passed to generateArray
+
+ specTemplate is a template for each entry of the spec array
+
+ specTerminator is a terminator for the spec array (inserted at the end
+ of the array), or None
+
+ specType is the actual typename of our spec
+
+ getDataTuple is a callback function that takes an array entry and
+ returns a tuple suitable for substitution into specTemplate.
+ """
+
+ assert(len(array) is not 0)
+ specs = []
+
+ for member in array:
+ specs.append(specTemplate % getDataTuple(member))
+ if specTerminator:
+ specs.append(specTerminator)
+
+ return (("static %s: &'static [%s] = &[\n" +
+ ",\n".join(specs) + "\n" +
+ "];\n\n") % (name, specType))
+
+# The length of a method is the maximum of the lengths of the
+# argument lists of all its overloads.
+def methodLength(method):
+ signatures = method.signatures()
+ return max([len(arguments) for (retType, arguments) in signatures])
+
+class MethodDefiner(PropertyDefiner):
+ """
+ A class for defining methods on a prototype object.
+ """
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
+ # We should be able to check for special operations without an
+ # identifier. For now we check if the name starts with __
+ methods = [m for m in descriptor.interface.members if
+ m.isMethod() and m.isStatic() == static and
+ not m.isIdentifierLess()]
+ self.regular = [{"name": m.identifier.name,
+ "methodInfo": not m.isStatic(),
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE" }
+ 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):
+ self.regular.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE" })
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def specData(m):
+ if m.get("methodInfo", True):
+ jitinfo = ("&%s_methodinfo" % m["name"])
+ accessor = "genericMethod"
+ else:
+ jitinfo = "0 as *const JSJitInfo"
+ accessor = m.get("nativeName", m["name"])
+ return (m["name"], accessor, jitinfo, m["length"], m["flags"])
+
+ def stringDecl(m):
+ return "static %s_name: [u8, ..%i] = %s;\n" % (m["name"], len(m["name"]) + 1,
+ str_to_const_array(m["name"]))
+
+ decls = ''.join([stringDecl(m) for m in array])
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' JSFunctionSpec {name: &%s_name as *const u8 as *const libc::c_char, call: JSNativeWrapper {op: Some(%s), info: %s}, nargs: %s, flags: %s as u16, selfHostedName: 0 as *const libc::c_char }',
+ ' JSFunctionSpec {name: 0 as *const libc::c_char, call: JSNativeWrapper {op: None, info: 0 as *const JSJitInfo}, nargs: 0, flags: 0, selfHostedName: 0 as *const libc::c_char }',
+ 'JSFunctionSpec',
+ specData)
+
+class AttrDefiner(PropertyDefiner):
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.regular = [
+ m
+ for m in descriptor.interface.members
+ if m.isAttr() and m.isStatic() == static
+ ]
+ self.static = static
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def flags(attr):
+ return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
+
+ def getter(attr):
+ if self.static:
+ accessor = 'get_' + attr.identifier.name
+ jitinfo = "0"
+ else:
+ if attr.hasLenientThis():
+ accessor = "genericLenientGetter"
+ else:
+ accessor = "genericGetter"
+ jitinfo = "&%s_getterinfo" % attr.identifier.name
+
+ return ("JSPropertyOpWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}"
+ % {"info" : jitinfo,
+ "native" : accessor})
+
+ def setter(attr):
+ if attr.readonly:
+ return "JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}"
+
+ if self.static:
+ accessor = 'set_' + attr.identifier.name
+ jitinfo = "0"
+ else:
+ if attr.hasLenientThis():
+ accessor = "genericLenientSetter"
+ else:
+ accessor = "genericSetter"
+ jitinfo = "&%s_setterinfo" % attr.identifier.name
+
+ return ("JSStrictPropertyOpWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}"
+ % {"info" : jitinfo,
+ "native" : accessor})
+
+ def specData(attr):
+ return (attr.identifier.name, flags(attr), getter(attr),
+ setter(attr))
+
+ def stringDecl(attr):
+ name = attr.identifier.name
+ return "static %s_name: [u8, ..%i] = %s;\n" % (name, len(name) + 1,
+ str_to_const_array(name))
+
+ decls = ''.join([stringDecl(m) for m in array])
+
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' JSPropertySpec { name: &%s_name as *const u8 as *const libc::c_char, tinyid: 0, flags: ((%s) & 0xFF) as u8, getter: %s, setter: %s }',
+ ' JSPropertySpec { name: 0 as *const libc::c_char, tinyid: 0, flags: 0, getter: JSPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}, setter: JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo} }',
+ 'JSPropertySpec',
+ specData)
+
+class ConstDefiner(PropertyDefiner):
+ """
+ A class for definining constants on the interface object
+ """
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.regular = [m for m in descriptor.interface.members if m.isConst()]
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def specData(const):
+ return (const.identifier.name,
+ convertConstIDLValueToJSVal(const.value))
+
+ def stringDecl(const):
+ name = const.identifier.name
+ return "static %s_name: &'static [u8] = &%s;\n" % (name, str_to_const_array(name))
+
+ decls = ''.join([stringDecl(m) for m in array])
+
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' ConstantSpec { name: %s_name, value: %s }',
+ None,
+ 'ConstantSpec',
+ 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.
+lineStartDetector = re.compile("^(?=[^\n])", re.MULTILINE)
+class CGIndenter(CGThing):
+ """
+ A class that takes another CGThing and generates code that indents that
+ CGThing by some number of spaces. The default indent is two spaces.
+ """
+ def __init__(self, child, indentLevel=2):
+ CGThing.__init__(self)
+ self.child = child
+ self.indent = " " * indentLevel
+
+ def define(self):
+ defn = self.child.define()
+ if defn is not "":
+ return re.sub(lineStartDetector, self.indent, defn)
+ else:
+ return defn
+
+class CGWrapper(CGThing):
+ """
+ Generic CGThing that wraps other CGThings with pre and post text.
+ """
+ def __init__(self, child, pre="", post="", reindent=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.pre = pre
+ self.post = post
+ self.reindent = reindent
+
+ def define(self):
+ defn = self.child.define()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ defn = stripTrailingWhitespace(
+ defn.replace("\n", "\n" + (" " * len(self.pre))))
+ return self.pre + defn + self.post
+
+class CGImports(CGWrapper):
+ """
+ Generates the appropriate import/use statements.
+ """
+ def __init__(self, child, descriptors, imports):
+ """
+ Adds a set of imports.
+ """
+ ignored_warnings = [
+ # Allow unreachable_code because we use 'break' in a way that
+ # sometimes produces two 'break's in a row. See for example
+ # CallbackMember.getArgConversions.
+ 'unreachable_code',
+ 'non_camel_case_types',
+ 'non_uppercase_statics',
+ 'unnecessary_parens',
+ 'unused_imports',
+ 'unused_variable',
+ 'unused_unsafe',
+ 'unused_mut',
+ 'dead_assignment',
+ 'dead_code',
+ ]
+
+ statements = ['#![allow(%s)]' % ','.join(ignored_warnings)]
+ statements.extend('use %s;' % i for i in sorted(imports))
+
+ CGWrapper.__init__(self, child,
+ pre='\n'.join(statements) + '\n\n')
+
+ @staticmethod
+ def getDeclarationFilename(decl):
+ # Use our local version of the header, not the exported one, so that
+ # test bindings, which don't export, will work correctly.
+ basename = os.path.basename(decl.filename())
+ return basename.replace('.webidl', 'Binding.rs')
+
+class CGIfWrapper(CGWrapper):
+ def __init__(self, child, condition):
+ pre = CGWrapper(CGGeneric(condition), pre="if ", post=" {\n",
+ reindent=True)
+ CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
+ post="\n}")
+
+class CGTemplatedType(CGWrapper):
+ def __init__(self, templateName, child):
+ CGWrapper.__init__(self, child, pre=templateName + "<", post=">")
+
+class CGNamespace(CGWrapper):
+ def __init__(self, namespace, child, public=False):
+ pre = "%smod %s {\n" % ("pub " if public else "", namespace)
+ post = "} // mod %s\n" % namespace
+ CGWrapper.__init__(self, child, pre=pre, post=post)
+
+ @staticmethod
+ def build(namespaces, child, public=False):
+ """
+ Static helper method to build multiple wrapped namespaces.
+ """
+ if not namespaces:
+ return child
+ inner = CGNamespace.build(namespaces[1:], child, public=public)
+ return CGNamespace(namespaces[0], inner, public=public)
+
+def DOMClass(descriptor):
+ protoList = ['PrototypeList::id::' + proto for proto in descriptor.prototypeChain]
+ # Pad out the list to the right length with IDCount so we
+ # guarantee that all the lists are the same length. IDCount
+ # is never the ID of any prototype, so it's safe to use as
+ # padding.
+ protoList.extend(['PrototypeList::id::IDCount'] * (descriptor.config.maxProtoChainLength - len(protoList)))
+ prototypeChainString = ', '.join(protoList)
+ return """DOMClass {
+ interface_chain: [ %s ],
+ native_hooks: &sNativePropertyHooks,
+}""" % prototypeChainString
+
+class CGDOMJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ traceHook = "Some(%s)" % TRACE_HOOK_NAME
+ if self.descriptor.isGlobal():
+ flags = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
+ slots = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
+ else:
+ flags = "0"
+ slots = "1"
+ return """
+static Class_name: [u8, ..%i] = %s;
+static Class: DOMJSClass = DOMJSClass {
+ base: js::Class {
+ name: &Class_name as *const u8 as *const libc::c_char,
+ flags: JSCLASS_IS_DOMJSCLASS | %s | (((%s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT as uint), //JSCLASS_HAS_RESERVED_SLOTS(%s),
+ addProperty: Some(JS_PropertyStub),
+ delProperty: Some(JS_PropertyStub),
+ getProperty: Some(JS_PropertyStub),
+ setProperty: Some(JS_StrictPropertyStub),
+ enumerate: Some(JS_EnumerateStub),
+ resolve: Some(JS_ResolveStub),
+ convert: Some(JS_ConvertStub),
+ finalize: Some(%s),
+ checkAccess: None,
+ call: None,
+ hasInstance: None,
+ construct: None,
+ trace: %s,
+
+ ext: js::ClassExtension {
+ equality: 0 as *const u8,
+ outerObject: %s,
+ innerObject: None,
+ iteratorObject: 0 as *const u8,
+ unused: 0 as *const u8,
+ isWrappedNative: 0 as *const u8,
+ },
+
+ ops: js::ObjectOps {
+ lookupGeneric: 0 as *const u8,
+ lookupProperty: 0 as *const u8,
+ lookupElement: 0 as *const u8,
+ lookupSpecial: 0 as *const u8,
+ defineGeneric: 0 as *const u8,
+ defineProperty: 0 as *const u8,
+ defineElement: 0 as *const u8,
+ defineSpecial: 0 as *const u8,
+ getGeneric: 0 as *const u8,
+ getProperty: 0 as *const u8,
+ getElement: 0 as *const u8,
+ getElementIfPresent: 0 as *const u8,
+ getSpecial: 0 as *const u8,
+ setGeneric: 0 as *const u8,
+ setProperty: 0 as *const u8,
+ setElement: 0 as *const u8,
+ setSpecial: 0 as *const u8,
+ getGenericAttributes: 0 as *const u8,
+ getPropertyAttributes: 0 as *const u8,
+ getElementAttributes: 0 as *const u8,
+ getSpecialAttributes: 0 as *const u8,
+ setGenericAttributes: 0 as *const u8,
+ setPropertyAttributes: 0 as *const u8,
+ setElementAttributes: 0 as *const u8,
+ setSpecialAttributes: 0 as *const u8,
+ deleteProperty: 0 as *const u8,
+ deleteElement: 0 as *const u8,
+ deleteSpecial: 0 as *const u8,
+
+ enumerate: 0 as *const u8,
+ typeOf: 0 as *const u8,
+ thisObject: %s,
+ clear: 0 as *const u8,
+ },
+ },
+ dom_class: %s
+};
+""" % (len(self.descriptor.interface.identifier.name) + 1,
+ str_to_const_array(self.descriptor.interface.identifier.name),
+ flags, slots, slots,
+ FINALIZE_HOOK_NAME, traceHook,
+ self.descriptor.outerObjectHook,
+ self.descriptor.outerObjectHook,
+ CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
+
+def str_to_const_array(s):
+ return "[" + (", ".join(map(lambda x: "'" + x + "' as u8", list(s)) + ['0 as u8'])) + "]"
+
+class CGPrototypeJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ return """
+static PrototypeClassName__: [u8, ..%s] = %s;
+static PrototypeClass: JSClass = JSClass {
+ name: &PrototypeClassName__ as *const u8 as *const libc::c_char,
+ flags: (1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT as uint, //JSCLASS_HAS_RESERVED_SLOTS(1)
+ addProperty: Some(JS_PropertyStub),
+ delProperty: Some(JS_PropertyStub),
+ getProperty: Some(JS_PropertyStub),
+ setProperty: Some(JS_StrictPropertyStub),
+ enumerate: Some(JS_EnumerateStub),
+ resolve: Some(JS_ResolveStub),
+ convert: Some(JS_ConvertStub),
+ finalize: None,
+ checkAccess: None,
+ call: None,
+ hasInstance: None,
+ construct: None,
+ trace: None,
+ reserved: [0 as *mut libc::c_void, ..40]
+};
+""" % (len(self.descriptor.interface.identifier.name + "Prototype") + 1,
+ str_to_const_array(self.descriptor.interface.identifier.name + "Prototype"))
+
+class CGInterfaceObjectJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ if True:
+ return ""
+ ctorname = "0 as *const u8" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
+ hasinstance = HASINSTANCE_HOOK_NAME
+ return """
+static InterfaceObjectClass: JSClass = {
+ %s, 0,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_StrictPropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ 0 as *const u8,
+ 0 as *const u8,
+ %s,
+ %s,
+ %s,
+ 0 as *const u8,
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (str_to_const_array("Function"), ctorname, hasinstance, ctorname)
+
+class CGList(CGThing):
+ """
+ Generate code for a list of GCThings. Just concatenates them together, with
+ an optional joiner string. "\n" is a common joiner.
+ """
+ def __init__(self, children, joiner=""):
+ CGThing.__init__(self)
+ self.children = children
+ self.joiner = joiner
+ def append(self, child):
+ self.children.append(child)
+ def prepend(self, child):
+ self.children.insert(0, child)
+ def join(self, generator):
+ return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator)))
+
+ def define(self):
+ return self.join(child.define() for child in self.children if child is not None)
+
+
+class CGIfElseWrapper(CGList):
+ def __init__(self, condition, ifTrue, ifFalse):
+ kids = [ CGIfWrapper(ifTrue, condition),
+ CGWrapper(CGIndenter(ifFalse), pre=" else {\n", post="\n}") ]
+ CGList.__init__(self, kids)
+
+
+class CGGeneric(CGThing):
+ """
+ A class that spits out a fixed string into the codegen. Can spit out a
+ separate string for the declaration too.
+ """
+ def __init__(self, text):
+ self.text = text
+
+ def define(self):
+ return self.text
+
+class CGCallbackTempRoot(CGGeneric):
+ def __init__(self, name):
+ val = "%s::new(tempRoot)" % name
+ define = """{
+ let tempRoot = ${val}.to_object();
+ %s
+}""" % val
+ CGGeneric.__init__(self, define)
+
+
+def getAllTypes(descriptors, dictionaries, callbacks):
+ """
+ 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.
+ """
+ for d in descriptors:
+ for t in getTypesFromDescriptor(d):
+ yield (t, d, None)
+ for dictionary in dictionaries:
+ for t in getTypesFromDictionary(dictionary):
+ yield (t, None, dictionary)
+ for callback in callbacks:
+ for t in getTypesFromCallback(callback):
+ yield (t, None, None)
+
+def SortedTuples(l):
+ """
+ Sort a list of tuples based on the first item in the tuple
+ """
+ return sorted(l, key=operator.itemgetter(0))
+
+def SortedDictValues(d):
+ """
+ Returns a list of values from the dict sorted by key.
+ """
+ # Create a list of tuples containing key and value, sorted on key.
+ d = SortedTuples(d.items())
+ # We're only interested in the values.
+ return (i[1] for i in d)
+
+def UnionTypes(descriptors, dictionaries, callbacks, config):
+ """
+ Returns a CGList containing CGUnionStructs for every union.
+ """
+
+ imports = [
+ 'dom::bindings::utils::unwrap_jsmanaged',
+ 'dom::bindings::codegen::PrototypeList',
+ 'dom::bindings::conversions::FromJSValConvertible',
+ 'dom::bindings::conversions::ToJSValConvertible',
+ 'dom::bindings::conversions::Default',
+ 'dom::bindings::error::throw_not_in_union',
+ 'dom::bindings::js::JS',
+ 'dom::types::*',
+ 'js::jsapi::JSContext',
+ 'js::jsval::JSVal',
+ 'servo_util::str::DOMString',
+ ]
+
+ # 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):
+ assert not descriptor or not dictionary
+ t = t.unroll()
+ if not t.isUnion():
+ continue
+ name = str(t)
+ if not name in unionStructs:
+ provider = descriptor or config.getDescriptorProvider()
+ unionStructs[name] = CGNamespace(name,
+ CGImports(CGList([
+ CGUnionStruct(t, provider),
+ CGUnionConversionStruct(t, provider)
+ ]), [], imports),
+ public=True)
+
+ return CGList(SortedDictValues(unionStructs), "\n\n")
+
+
+class Argument():
+ """
+ A class for outputting the type and name of an argument
+ """
+ def __init__(self, argType, name, default=None, mutable=False):
+ self.argType = argType
+ self.name = name
+ self.default = default
+ self.mutable = mutable
+ def declare(self):
+ string = ('mut ' if self.mutable else '') + self.name + ((': ' + self.argType) if self.argType else '')
+ #XXXjdm Support default arguments somehow :/
+ #if self.default is not None:
+ # string += " = " + self.default
+ return string
+ def define(self):
+ return self.argType + ' ' + self.name
+
+class CGAbstractMethod(CGThing):
+ """
+ An abstract class for generating code for a method. Subclasses
+ should override definition_body to create the actual code.
+
+ descriptor is the descriptor for the interface the method is associated with
+
+ name is the name of the method as a string
+
+ returnType is the IDLType of the return value
+
+ args is a list of Argument objects
+
+ inline should be True to generate an inline method, whose body is
+ part of the declaration.
+
+ alwaysInline should be True to generate an inline method annotated with
+ MOZ_ALWAYS_INLINE.
+
+ If templateArgs is not None it should be a list of strings containing
+ template arguments, and the function will be templatized using those
+ arguments.
+ """
+ def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, extern=False, pub=False, templateArgs=None, unsafe=True):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.name = name
+ self.returnType = returnType
+ self.args = args
+ self.alwaysInline = alwaysInline
+ self.extern = extern
+ self.templateArgs = templateArgs
+ self.pub = pub;
+ self.unsafe = unsafe
+ def _argstring(self):
+ return ', '.join([a.declare() for a in self.args])
+ def _template(self):
+ if self.templateArgs is None:
+ return ''
+ return '<%s>\n' % ', '.join(self.templateArgs)
+
+ def _decorators(self):
+ decorators = []
+ if self.alwaysInline:
+ decorators.append('#[inline(always)]')
+
+ if self.extern:
+ decorators.append('extern')
+
+ if self.pub:
+ decorators.append('pub')
+
+ if not decorators:
+ return ''
+ return ' '.join(decorators) + ' '
+
+ def _returnType(self):
+ return (" -> %s" % self.returnType) if self.returnType != "void" else ""
+
+ def define(self):
+ body = self.definition_body()
+ if self.unsafe:
+ body = CGWrapper(body, pre="unsafe {\n", post="\n}")
+
+ return CGWrapper(CGIndenter(body),
+ pre=self.definition_prologue(),
+ post=self.definition_epilogue()).define()
+
+ def definition_prologue(self):
+ return "%sfn %s%s(%s)%s {\n" % (self._decorators(), self.name, self._template(),
+ self._argstring(), self._returnType())
+ def definition_epilogue(self):
+ return "\n}\n"
+ def definition_body(self):
+ assert(False) # Override me!
+
+def CreateBindingJSObject(descriptor, parent=None):
+ create = "let mut raw: JS<%s> = JS::from_raw(&*aObject);\n" % descriptor.concreteType
+ if descriptor.proxy:
+ assert not descriptor.isGlobal()
+ create += """
+let handler = RegisterBindings::proxy_handlers[PrototypeList::proxies::%s as uint];
+let mut private = PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void);
+let obj = with_compartment(aCx, proto, || {
+ NewProxyObject(aCx, handler,
+ &private,
+ proto, %s,
+ ptr::mut_null(), ptr::mut_null())
+});
+assert!(obj.is_not_null());
+
+""" % (descriptor.name, parent)
+ else:
+ if descriptor.isGlobal():
+ create += "let obj = CreateDOMGlobal(aCx, &Class.base as *const js::Class as *const JSClass);\n"
+ else:
+ create += ("let obj = with_compartment(aCx, proto, || {\n"
+ " JS_NewObject(aCx, &Class.base as *const js::Class as *const JSClass, &*proto, &*%s)\n"
+ "});\n" % parent)
+ create += """assert!(obj.is_not_null());
+
+JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32,
+ PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void));
+"""
+ return create
+
+class CGWrapMethod(CGAbstractMethod):
+ """
+ Class that generates the FooBinding::Wrap function for non-callback
+ interfaces.
+ """
+ def __init__(self, descriptor):
+ assert not descriptor.interface.isCallback()
+ if not descriptor.isGlobal():
+ args = [Argument('*mut JSContext', 'aCx'), Argument('&GlobalRef', 'aScope'),
+ Argument("Box<%s>" % descriptor.concreteType, 'aObject', mutable=True)]
+ else:
+ args = [Argument('*mut JSContext', 'aCx'),
+ Argument("Box<%s>" % descriptor.concreteType, 'aObject', mutable=True)]
+ retval = 'Temporary<%s>' % descriptor.concreteType
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, pub=True)
+
+ def definition_body(self):
+ if not self.descriptor.isGlobal():
+ return CGGeneric("""\
+let scope = aScope.reflector().get_jsobject();
+assert!(scope.is_not_null());
+assert!(((*JS_GetClass(scope)).flags & JSCLASS_IS_GLOBAL) != 0);
+
+let proto = with_compartment(aCx, scope, || GetProtoObject(aCx, scope, scope));
+assert!(proto.is_not_null());
+
+%s
+
+raw.reflector().set_jsobject(obj);
+
+Temporary::new(raw)""" % CreateBindingJSObject(self.descriptor, "scope"))
+ else:
+ return CGGeneric("""\
+%s
+with_compartment(aCx, obj, || {
+ let proto = GetProtoObject(aCx, obj, obj);
+ JS_SetPrototype(aCx, obj, proto);
+
+ raw.reflector().set_jsobject(obj);
+
+ RegisterBindings::Register(aCx, obj);
+});
+
+Temporary::new(raw)""" % CreateBindingJSObject(self.descriptor))
+
+
+class CGIDLInterface(CGThing):
+ """
+ Class for codegen of an implementation of the IDLInterface trait.
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ replacer = {
+ 'type': self.descriptor.name,
+ 'depth': self.descriptor.interface.inheritanceDepth(),
+ }
+ return string.Template("""
+impl IDLInterface for ${type} {
+ fn get_prototype_id(_: Option<${type}>) -> PrototypeList::id::ID {
+ PrototypeList::id::${type}
+ }
+ fn get_prototype_depth(_: Option<${type}>) -> uint {
+ ${depth}
+ }
+}
+""").substitute(replacer)
+
+
+class CGAbstractExternMethod(CGAbstractMethod):
+ """
+ Abstract base class for codegen of implementation-only (no
+ declaration) static methods.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
+ inline=False, extern=True)
+
+class PropertyArrays():
+ def __init__(self, descriptor):
+ self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
+ static=True)
+ self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
+ static=True)
+ self.methods = MethodDefiner(descriptor, "Methods", static=False)
+ self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
+ self.consts = ConstDefiner(descriptor, "Constants")
+ pass
+
+ @staticmethod
+ def arrayNames():
+ return [ "staticMethods", "staticAttrs", "methods", "attrs", "consts" ]
+
+ def variableNames(self):
+ names = {}
+ for array in self.arrayNames():
+ names[array] = getattr(self, array).variableName()
+ return names
+ def __str__(self):
+ define = ""
+ for array in self.arrayNames():
+ define += str(getattr(self, array))
+ return define
+
+
+class CGNativeProperties(CGThing):
+ def __init__(self, descriptor, properties):
+ CGThing.__init__(self)
+ self.properties = properties
+
+ def define(self):
+ def getField(array):
+ propertyArray = getattr(self.properties, array)
+ if propertyArray.length() > 0:
+ value = "Some(%s)" % propertyArray.variableName()
+ else:
+ value = "None"
+
+ return CGGeneric(string.Template('${name}: ${value},').substitute({
+ 'name': array,
+ 'value': value,
+ }))
+
+ nativeProps = CGList([getField(array) for array in self.properties.arrayNames()], '\n')
+ return CGWrapper(CGIndenter(nativeProps),
+ pre="static sNativeProperties: NativeProperties = NativeProperties {\n",
+ post="\n};\n").define()
+
+
+class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
+ """
+ Generate the CreateInterfaceObjects method for an interface descriptor.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ assert not descriptor.interface.isCallback()
+ args = [Argument('*mut JSContext', 'aCx'), Argument('*mut JSObject', 'aGlobal'),
+ Argument('*mut JSObject', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', '*mut JSObject', args)
+ self.properties = properties
+ def definition_body(self):
+ protoChain = self.descriptor.prototypeChain
+ if len(protoChain) == 1:
+ getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
+ else:
+ parentProtoName = self.descriptor.prototypeChain[-2]
+ getParentProto = ("%s::GetProtoObject(aCx, aGlobal, aReceiver)" %
+ toBindingNamespace(parentProtoName))
+
+ getParentProto = ("let parentProto: *mut JSObject = %s;\n"
+ "assert!(parentProto.is_not_null());\n") % getParentProto
+
+ if self.descriptor.concrete:
+ if self.descriptor.proxy:
+ domClass = "&Class"
+ else:
+ domClass = "&Class.dom_class"
+ else:
+ domClass = "ptr::null()"
+
+ if self.descriptor.interface.hasInterfaceObject():
+ if self.descriptor.interface.ctor():
+ constructHook = CONSTRUCT_HOOK_NAME
+ constructArgs = methodLength(self.descriptor.interface.ctor())
+ else:
+ constructHook = "ThrowingConstructor"
+ constructArgs = 0
+
+ constructor = 'Some((%s, "%s", %d))' % (
+ constructHook, self.descriptor.interface.identifier.name,
+ constructArgs)
+ else:
+ constructor = 'None'
+
+ call = """return CreateInterfaceObjects2(aCx, aGlobal, aReceiver, parentProto,
+ &PrototypeClass, %s,
+ %s,
+ &sNativeProperties);""" % (constructor, domClass)
+
+ return CGList([
+ CGGeneric(getParentProto),
+ CGGeneric(call % self.properties.variableNames())
+ ], "\n")
+
+class CGGetPerInterfaceObject(CGAbstractMethod):
+ """
+ A method for getting a per-interface object (a prototype object or interface
+ constructor object).
+ """
+ def __init__(self, descriptor, name, idPrefix="", pub=False):
+ args = [Argument('*mut JSContext', 'aCx'), Argument('*mut JSObject', 'aGlobal'),
+ Argument('*mut JSObject', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, name,
+ '*mut JSObject', args, pub=pub)
+ self.id = idPrefix + "id::" + self.descriptor.name
+ def definition_body(self):
+ return CGGeneric("""
+
+/* aGlobal and aReceiver are usually the same, but they can be different
+ too. For example a sandbox often has an xray wrapper for a window as the
+ prototype of the sandbox's global. In that case aReceiver is the xray
+ wrapper and aGlobal is the sandbox's global.
+ */
+
+assert!(((*JS_GetClass(aGlobal)).flags & JSCLASS_DOM_GLOBAL) != 0);
+
+/* Check to see whether the interface objects are already installed */
+let protoOrIfaceArray = GetProtoOrIfaceArray(aGlobal);
+let cachedObject: *mut JSObject = *protoOrIfaceArray.offset(%s as int);
+if cachedObject.is_null() {
+ let tmp: *mut JSObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
+ assert!(tmp.is_not_null());
+ *protoOrIfaceArray.offset(%s as int) = tmp;
+ tmp
+} else {
+ cachedObject
+}""" % (self.id, self.id))
+
+class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface prototype object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
+ "PrototypeList::", pub=True)
+ def definition_body(self):
+ return CGList([
+ CGGeneric("""\
+/* Get the interface prototype object for this class. This will create the
+ object as needed. */"""),
+ CGGetPerInterfaceObject.definition_body(self),
+ ])
+
+class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface constructor object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
+ "constructors::")
+ def definition_body(self):
+ return CGList([
+ CGGeneric("""\
+/* Get the interface object for this class. This will create the object as
+ needed. */"""),
+ CGGetPerInterfaceObject.definition_body(self),
+ ])
+
+
+class CGDefineProxyHandler(CGAbstractMethod):
+ """
+ A method to create and cache the proxy trap for a given interface.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.proxy
+ CGAbstractMethod.__init__(self, descriptor, 'DefineProxyHandler', '*const libc::c_void', [], pub=True)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ body = """\
+let traps = ProxyTraps {
+ getPropertyDescriptor: Some(getPropertyDescriptor),
+ getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
+ defineProperty: Some(defineProperty),
+ getOwnPropertyNames: ptr::null(),
+ delete_: Some(delete_),
+ enumerate: ptr::null(),
+
+ has: None,
+ hasOwn: Some(hasOwn),
+ get: Some(get),
+ set: None,
+ keys: ptr::null(),
+ iterate: None,
+
+ call: None,
+ construct: None,
+ nativeCall: ptr::null(),
+ hasInstance: None,
+ typeOf: None,
+ objectClassIs: None,
+ obj_toString: Some(obj_toString),
+ fun_toString: None,
+ //regexp_toShared: ptr::null(),
+ defaultValue: None,
+ iteratorNext: None,
+ finalize: Some(%s),
+ getElementIfPresent: None,
+ getPrototypeOf: None,
+ trace: Some(%s)
+};
+
+CreateProxyHandler(&traps, &Class as *const _ as *const _)
+""" % (FINALIZE_HOOK_NAME,
+ TRACE_HOOK_NAME)
+ return CGGeneric(body)
+
+
+
+class CGDefineDOMInterfaceMethod(CGAbstractMethod):
+ """
+ A method for resolve hooks to try to lazily define the interface object for
+ a given interface.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.interface.hasInterfaceObject()
+ args = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('*mut JSObject', 'global'),
+ ]
+ CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'void', args, pub=True)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ return CGGeneric("""\
+assert!(global.is_not_null());
+assert!(GetProtoObject(cx, global, global).is_not_null());""")
+
+def needCx(returnType, arguments, considerTypes):
+ return (considerTypes and
+ (typeNeedsCx(returnType, True) or
+ any(typeNeedsCx(a.type) for a in arguments)))
+
+class CGCallGenerator(CGThing):
+ """
+ A class to generate an actual call to a C++ object. Assumes that the C++
+ object is stored in a variable whose name is given by the |object| argument.
+
+ errorResult should be a string for the value to return in case of an
+ exception from the native code, or None if no error reporting is needed.
+ """
+ def __init__(self, errorResult, arguments, argsPre, returnType,
+ extendedAttributes, descriptorProvider, nativeMethodName,
+ static, object="this"):
+ CGThing.__init__(self)
+
+ assert errorResult is None or isinstance(errorResult, str)
+
+ isFallible = errorResult is not None
+
+ result = getRetvalDeclarationForType(returnType, descriptorProvider)
+ if isFallible:
+ result = CGWrapper(result, pre="Result<", post=", Error>")
+
+ args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
+ for (a, name) in arguments:
+ #XXXjdm Perhaps we should pass all nontrivial types by borrowed pointer
+ if a.type.isGeckoInterface():
+ if not (a.type.nullable() or a.optional):
+ name = "&" + name
+ elif a.type.isDictionary():
+ name = "&" + name
+ args.append(CGGeneric(name))
+
+ needsCx = needCx(returnType, (a for (a, _) in arguments), True)
+
+ if not "cx" in argsPre and needsCx:
+ args.prepend(CGGeneric("cx"))
+
+ # Build up our actual call
+ self.cgRoot = CGList([], "\n")
+
+ call = CGGeneric(nativeMethodName)
+ if static:
+ call = CGWrapper(call, pre="%s::" % descriptorProvider.interface.identifier.name)
+ else:
+ call = CGWrapper(call, pre="(*%s)." % object)
+ call = CGList([call, CGWrapper(args, pre="(", post=")")])
+
+ self.cgRoot.append(CGList([
+ CGGeneric("let result: "),
+ result,
+ CGGeneric(" = "),
+ call,
+ CGGeneric(";"),
+ ]))
+
+ if isFallible:
+ if static:
+ glob = ""
+ else:
+ glob = " let global = global_object_for_js_object(this.reflector().get_jsobject());\n"\
+ " let global = global.root();\n"
+
+ self.cgRoot.append(CGGeneric(
+ "let result = match result {\n"
+ " Ok(result) => result,\n"
+ " Err(e) => {\n"
+ "%s"
+ " throw_dom_exception(cx, &global.root_ref(), e);\n"
+ " return%s;\n"
+ " },\n"
+ "};\n" % (glob, errorResult)))
+
+ if typeRetValNeedsRooting(returnType):
+ self.cgRoot.append(CGGeneric("let result = result.root();"))
+
+ def define(self):
+ return self.cgRoot.define()
+
+class MethodNotCreatorError(Exception):
+ def __init__(self, typename):
+ self.typename = typename
+
+class CGPerSignatureCall(CGThing):
+ """
+ This class handles the guts of generating code for a particular
+ call signature. A call signature consists of four things:
+
+ 1) A return type, which can be None to indicate that there is no
+ actual return value (e.g. this is an attribute setter) or an
+ IDLType if there's an IDL type involved (including |void|).
+ 2) An argument list, which is allowed to be empty.
+ 3) A name of a native method to call.
+ 4) Whether or not this method is static.
+
+ We also need to know whether this is a method or a getter/setter
+ to do error reporting correctly.
+
+ The idlNode parameter can be either a method or an attr. We can query
+ |idlNode.identifier| in both cases, so we can be agnostic between the two.
+ """
+ # XXXbz For now each entry in the argument list is either an
+ # IDLArgument or a FakeArgument, but longer-term we may want to
+ # have ways of flagging things like JSContext* or optional_argc in
+ # there.
+
+ def __init__(self, returnType, argsPre, arguments, nativeMethodName, static,
+ descriptor, idlNode, argConversionStartsAt=0,
+ getter=False, setter=False):
+ CGThing.__init__(self)
+ self.returnType = returnType
+ self.descriptor = descriptor
+ self.idlNode = idlNode
+ self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
+ getter=getter,
+ setter=setter)
+ self.argsPre = argsPre
+ self.arguments = arguments
+ self.argCount = len(arguments)
+ if self.argCount > argConversionStartsAt:
+ # Insert our argv in there
+ cgThings = [CGGeneric(self.getArgvDecl())]
+ else:
+ cgThings = []
+ cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(),
+ self.getArgc(), self.descriptor,
+ invalidEnumValueFatal=not setter) for
+ i in range(argConversionStartsAt, self.argCount)])
+
+ cgThings.append(CGCallGenerator(
+ ' false as JSBool' if self.isFallible() else None,
+ self.getArguments(), self.argsPre, returnType,
+ self.extendedAttributes, descriptor, nativeMethodName,
+ static))
+ self.cgRoot = CGList(cgThings, "\n")
+
+ def getArgv(self):
+ return "argv" if self.argCount > 0 else ""
+ def getArgvDecl(self):
+ return "\nlet argv = JS_ARGV(cx, vp);\n"
+ def getArgc(self):
+ return "argc"
+ def getArguments(self):
+ def process(arg, i):
+ argVal = "arg" + str(i)
+ if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
+ argVal += ".root_ref()"
+ return argVal
+ return [(a, process(a, i)) for (i, a) in enumerate(self.arguments)]
+
+ def isFallible(self):
+ return not 'infallible' in self.extendedAttributes
+
+ def wrap_return_value(self):
+ return wrapForType('*vp')
+
+ def define(self):
+ return (self.cgRoot.define() + "\n" + self.wrap_return_value())
+
+class CGSwitch(CGList):
+ """
+ A class to generate code for a switch statement.
+
+ Takes three constructor arguments: an expression, a list of cases,
+ and an optional default.
+
+ Each case is a CGCase. The default is a CGThing for the body of
+ the default case, if any.
+ """
+ def __init__(self, expression, cases, default=None):
+ CGList.__init__(self, [CGIndenter(c) for c in cases], "\n")
+ self.prepend(CGWrapper(CGGeneric(expression),
+ pre="match ", post=" {"));
+ if default is not None:
+ self.append(
+ CGIndenter(
+ CGWrapper(
+ CGIndenter(default),
+ pre="_ => {\n",
+ post="\n}"
+ )
+ )
+ )
+
+ self.append(CGGeneric("}"))
+
+class CGCase(CGList):
+ """
+ A class to generate code for a case statement.
+
+ Takes three constructor arguments: an expression, a CGThing for
+ the body (allowed to be None if there is no body), and an optional
+ argument (defaulting to False) for whether to fall through.
+ """
+ def __init__(self, expression, body, fallThrough=False):
+ CGList.__init__(self, [], "\n")
+ self.append(CGWrapper(CGGeneric(expression), post=" => {"))
+ bodyList = CGList([body], "\n")
+ if fallThrough:
+ raise TypeError("fall through required but unsupported")
+ #bodyList.append(CGGeneric('fail!("fall through unsupported"); /* Fall through */'))
+ self.append(CGIndenter(bodyList));
+ self.append(CGGeneric("}"))
+
+class CGGetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object getter call for a particular IDL
+ getter.
+ """
+ def __init__(self, argsPre, returnType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, returnType, argsPre, [],
+ nativeMethodName, attr.isStatic(), descriptor,
+ attr, getter=True)
+
+class FakeArgument():
+ """
+ A class that quacks like an IDLArgument. This is used to make
+ setters look like method calls or for special operations.
+ """
+ def __init__(self, type, interfaceMember, allowTreatNonObjectAsNull=False):
+ self.type = type
+ self.optional = False
+ 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
+
+class CGSetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object setter call for a particular IDL
+ setter.
+ """
+ def __init__(self, argsPre, argType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, None, argsPre,
+ [FakeArgument(argType, attr, allowTreatNonObjectAsNull=True)],
+ nativeMethodName, attr.isStatic(), descriptor, attr,
+ setter=True)
+ def wrap_return_value(self):
+ # We have no return value
+ return "\nreturn 1;"
+ def getArgc(self):
+ return "1"
+ def getArgvDecl(self):
+ # We just get our stuff from our last arg no matter what
+ return ""
+
+class CGAbstractBindingMethod(CGAbstractExternMethod):
+ """
+ Common class to generate the JSNatives for all our methods, getters, and
+ setters. This will generate the function declaration and unwrap the
+ |this| object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name, args, unwrapFailureCode=None):
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ if unwrapFailureCode is None:
+ self.unwrapFailureCode = (
+ 'throw_type_error(cx, "\\"this\\" object does not '
+ 'implement interface %s.");\n'
+ 'return 0;' % descriptor.interface.identifier.name)
+ else:
+ self.unwrapFailureCode = unwrapFailureCode
+
+ def definition_body(self):
+ # Our descriptor might claim that we're not castable, simply because
+ # we're someone's consequential interface. But for this-unwrapping, we
+ # know that we're the real deal. So fake a descriptor here for
+ # consumption by FailureFatalCastableObjectUnwrapper.
+ unwrapThis = str(CastableObjectUnwrapper(
+ FakeCastableDescriptor(self.descriptor),
+ "obj", self.unwrapFailureCode))
+ unwrapThis = CGGeneric(
+ "let obj: *mut JSObject = JS_THIS_OBJECT(cx, vp as *mut JSVal);\n"
+ "if obj.is_null() {\n"
+ " return false as JSBool;\n"
+ "}\n"
+ "\n"
+ "let this: JS<%s> = %s;\n" % (self.descriptor.concreteType, unwrapThis))
+ return CGList([ unwrapThis, self.generate_code() ], "\n")
+
+ def generate_code(self):
+ assert(False) # Override me
+
+
+class CGAbstractStaticBindingMethod(CGAbstractMethod):
+ """
+ Common class to generate the JSNatives for all our static methods, getters
+ and setters. This will generate the function declaration and unwrap the
+ global object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name):
+ args = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp'),
+ ]
+ CGAbstractMethod.__init__(self, descriptor, name, "JSBool", args, extern=True)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ assert False # Override me
+
+
+class CGGenericMethod(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL method..
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let _info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "return CallJitMethodOp(_info, cx, obj, this.unsafe_get() as *mut libc::c_void, argc, vp);")
+
+class CGSpecializedMethod(CGAbstractExternMethod):
+ """
+ A class for generating the C++ code for a specialized method that the JIT
+ can call with lower overhead.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, name, 'JSBool', args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+ self.method)
+ return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(),
+ self.descriptor, self.method),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, method):
+ return MakeNativeName(method.identifier.name)
+
+class CGStaticMethod(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the Rust code for an IDL static method.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+ self.method)
+ return CGMethodCall([], nativeName, True, self.descriptor, self.method)
+
+
+class CGGenericGetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute getter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ if lenientThis:
+ name = "genericLenientGetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "JS_SET_RVAL(cx, vp, JS::UndefinedValue());\n"
+ "return true;")
+ else:
+ name = "genericGetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "return CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *mut libc::c_void, vp);\n")
+
+class CGSpecializedGetter(CGAbstractExternMethod):
+ """
+ A class for generating the code for a specialized attribute getter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ args = [ Argument('*mut JSContext', 'cx'),
+ Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('*mut JSVal', 'vp') ]
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+ self.attr)
+
+ return CGWrapper(CGGetterCall([], self.attr.type, nativeName,
+ self.descriptor, self.attr),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, attr):
+ nativeName = MakeNativeName(attr.identifier.name)
+ infallible = ('infallible' in
+ descriptor.getExtendedAttributes(attr, getter=True))
+ if attr.type.nullable() or not infallible:
+ return "Get" + nativeName
+
+ return nativeName
+
+
+class CGStaticGetter(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the C++ code for an IDL static attribute getter.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+ self.attr)
+ return CGGetterCall([], self.attr.type, nativeName, self.descriptor,
+ self.attr)
+
+
+class CGGenericSetter(CGAbstractBindingMethod):
+ """
+ A class for generating the Rust code for an IDL attribute setter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ if lenientThis:
+ name = "genericLenientSetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "return true;")
+ else:
+ name = "genericSetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let mut undef = UndefinedValue();\n"
+ "let argv: *mut JSVal = if argc != 0 { JS_ARGV(cx, vp) } else { &mut undef as *mut JSVal };\n"
+ "let info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "if CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *mut libc::c_void, argv) == 0 {\n"
+ " return 0;\n"
+ "}\n"
+ "*vp = UndefinedValue();\n"
+ "return 1;")
+
+class CGSpecializedSetter(CGAbstractExternMethod):
+ """
+ A class for generating the code for a specialized attribute setter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ args = [ Argument('*mut JSContext', 'cx'),
+ Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('*mut JSVal', 'argv')]
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+ self.attr)
+ return CGWrapper(CGSetterCall([], self.attr.type, nativeName,
+ self.descriptor, self.attr),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, attr):
+ return "Set" + MakeNativeName(attr.identifier.name)
+
+
+class CGStaticSetter(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the C++ code for an IDL static attribute setter.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+ self.attr)
+ checkForArg = CGGeneric(
+ "let argv = JS_ARGV(cx, vp);\n"
+ "if (argc == 0) {\n"
+ " throw_type_error(cx, \"Not enough arguments to %s setter.\");\n"
+ " return 0;\n"
+ "}\n" % self.attr.identifier.name)
+ call = CGSetterCall([], self.attr.type, nativeName, self.descriptor,
+ self.attr)
+ return CGList([checkForArg, call])
+
+
+class CGMemberJITInfo(CGThing):
+ """
+ A class for generating the JITInfo for a property that points to
+ our specialized getter and setter.
+ """
+ def __init__(self, descriptor, member):
+ self.member = member
+ self.descriptor = descriptor
+
+ def defineJitInfo(self, infoName, opName, infallible):
+ protoID = "PrototypeList::id::%s as u32" % self.descriptor.name
+ depth = self.descriptor.interface.inheritanceDepth()
+ failstr = "true" if infallible else "false"
+ return ("\n"
+ "static %s: JSJitInfo = JSJitInfo {\n"
+ " op: %s as *const u8,\n"
+ " protoID: %s,\n"
+ " depth: %s,\n"
+ " isInfallible: %s, /* False in setters. */\n"
+ " isConstant: false /* Only relevant for getters. */\n"
+ "};\n" % (infoName, opName, protoID, depth, failstr))
+
+ def define(self):
+ if self.member.isAttr():
+ getterinfo = ("%s_getterinfo" % self.member.identifier.name)
+ getter = ("get_%s" % self.member.identifier.name)
+ getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
+ result = self.defineJitInfo(getterinfo, getter, getterinfal)
+ if not self.member.readonly:
+ setterinfo = ("%s_setterinfo" % self.member.identifier.name)
+ setter = ("set_%s" % self.member.identifier.name)
+ # Setters are always fallible, since they have to do a typed unwrap.
+ result += self.defineJitInfo(setterinfo, setter, False)
+ return result
+ if self.member.isMethod():
+ methodinfo = ("%s_methodinfo" % self.member.identifier.name)
+ # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
+ method = ("%s" % self.member.identifier.name)
+
+ # Methods are infallible if they are infallible, have no arguments
+ # to unwrap, and have a return type that's infallible to wrap up for
+ # return.
+ methodInfal = False
+ sigs = self.member.signatures()
+ if len(sigs) == 1:
+ # Don't handle overloading. If there's more than one signature,
+ # one of them must take arguments.
+ sig = sigs[0]
+ if len(sig[1]) == 0:
+ # No arguments and infallible return boxing
+ methodInfal = True
+
+ result = self.defineJitInfo(methodinfo, method, methodInfal)
+ return result
+ raise TypeError("Illegal member type to CGPropertyJITInfo")
+
+def getEnumValueName(value):
+ # Some enum values can be empty strings. Others might have weird
+ # characters in them. Deal with the former by returning "_empty",
+ # deal with possible name collisions from that by throwing if the
+ # enum value is actually "_empty", and throw on any value
+ # containing non-ASCII chars for now. Replace all chars other than
+ # [0-9A-Za-z_] with '_'.
+ 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 = 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')
+ if value == "_empty":
+ raise SyntaxError('"_empty" is not an IDL enum value we support yet')
+ if value == "":
+ return "_empty"
+ return MakeNativeName(value)
+
+class CGEnum(CGThing):
+ def __init__(self, enum):
+ CGThing.__init__(self)
+ inner = """
+use dom::bindings::conversions::ToJSValConvertible;
+use js::jsapi::JSContext;
+use js::jsval::JSVal;
+
+#[repr(uint)]
+#[deriving(Encodable, PartialEq)]
+pub enum valuelist {
+ %s
+}
+
+pub static strings: &'static [&'static str] = &[
+ %s,
+];
+
+impl ToJSValConvertible for valuelist {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ strings[*self as uint].to_string().to_jsval(cx)
+ }
+}
+""" % (",\n ".join(map(getEnumValueName, enum.values())),
+ ",\n ".join(['"%s"' % val for val in enum.values()]))
+
+ self.cgRoot = CGList([
+ CGNamespace.build([enum.identifier.name + "Values"],
+ CGIndenter(CGGeneric(inner)), public=True),
+ CGGeneric("pub type %s = self::%sValues::valuelist;\n" %
+ (enum.identifier.name, enum.identifier.name)),
+ ])
+
+ def define(self):
+ return self.cgRoot.define()
+
+
+def convertConstIDLValueToRust(value):
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double]:
+ return str(value.value)
+
+ if tag == IDLType.Tags.bool:
+ return toStringBool(value.value)
+
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGConstant(CGThing):
+ def __init__(self, constants):
+ CGThing.__init__(self)
+ self.constants = constants
+
+ def define(self):
+ def stringDecl(const):
+ name = const.identifier.name
+ value = convertConstIDLValueToRust(const.value)
+ return CGGeneric("pub static %s: %s = %s;\n" % (name, builtinNames[const.value.type.tag()], value))
+
+ return CGIndenter(CGList(stringDecl(m) for m in self.constants)).define()
+
+def getUnionTypeTemplateVars(type, descriptorProvider):
+ # For dictionaries and sequences we need to pass None as the failureCode
+ # for getJSToNativeConversionTemplate.
+ # Also, for dictionaries we would need to handle conversion of
+ # null/undefined to the dictionary correctly.
+ if type.isDictionary() or type.isSequence():
+ raise TypeError("Can't handle dictionaries or sequences in unions")
+
+ if type.isGeckoInterface():
+ name = type.inner.identifier.name
+ typeName = descriptorProvider.getDescriptor(name).nativeType
+ elif type.isEnum():
+ name = type.inner.identifier.name
+ typeName = name
+ elif type.isArray() or type.isSequence():
+ name = str(type)
+ #XXXjdm dunno about typeName here
+ typeName = "/*" + type.name + "*/"
+ elif type.isDOMString():
+ name = type.name
+ typeName = "DOMString"
+ elif type.isPrimitive():
+ name = type.name
+ typeName = builtinNames[type.tag()]
+ else:
+ name = type.name
+ typeName = "/*" + type.name + "*/"
+
+ template, _, _, _ = getJSToNativeConversionTemplate(
+ type, descriptorProvider, failureCode="return Ok(None);",
+ exceptionCode='return Err(());',
+ isDefinitelyObject=True)
+
+ assert not type.isObject()
+ jsConversion = string.Template(template).substitute({
+ "val": "value",
+ })
+ jsConversion = CGWrapper(CGGeneric(jsConversion), pre="Ok(Some(", post="))")
+
+ return {
+ "name": name,
+ "typeName": typeName,
+ "jsConversion": jsConversion,
+ }
+
+class CGUnionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ assert not type.nullable()
+ assert not type.hasNullableType
+
+ CGThing.__init__(self)
+ self.type = type
+ self.descriptorProvider = descriptorProvider
+
+ def define(self):
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+ enumValues = [
+ " e%s(%s)," % (v["name"], v["typeName"]) for v in templateVars
+ ]
+ enumConversions = [
+ " e%s(ref inner) => inner.to_jsval(cx)," % v["name"] for v in templateVars
+ ]
+ return ("""pub enum %s {
+%s
+}
+
+impl ToJSValConvertible for %s {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ match *self {
+%s
+ }
+ }
+}
+""") % (self.type, "\n".join(enumValues),
+ self.type, "\n".join(enumConversions))
+
+
+class CGUnionConversionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ assert not type.nullable()
+ assert not type.hasNullableType
+
+ CGThing.__init__(self)
+ self.type = type
+ self.descriptorProvider = descriptorProvider
+
+ def from_jsval(self):
+ memberTypes = self.type.flatMemberTypes
+ names = []
+ conversions = []
+
+ interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ if len(interfaceMemberTypes) > 0:
+ def get_name(memberType):
+ if self.type.isGeckoInterface():
+ return memberType.inner.identifier.name
+
+ return memberType.name
+
+ def get_match(name):
+ return (
+ "match %s::TryConvertTo%s(cx, value) {\n"
+ " Err(_) => return Err(()),\n"
+ " Ok(Some(value)) => return Ok(e%s(value)),\n"
+ " Ok(None) => (),\n"
+ "}\n") % (self.type, name, name)
+
+ typeNames = [get_name(memberType) for memberType in interfaceMemberTypes]
+ interfaceObject = CGList(CGGeneric(get_match(typeName)) for typeName in typeNames)
+ names.extend(typeNames)
+ else:
+ interfaceObject = None
+
+ arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
+ if len(arrayObjectMemberTypes) > 0:
+ assert len(arrayObjectMemberTypes) == 1
+ raise TypeError("Can't handle arrays or sequences in unions.")
+ 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)
+ if len(callbackMemberTypes) > 0:
+ assert len(callbackMemberTypes) == 1
+ raise TypeError("Can't handle callbacks in unions.")
+ else:
+ callbackObject = None
+
+ dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
+ if len(dictionaryMemberTypes) > 0:
+ raise TypeError("No support for unwrapping dictionaries as member "
+ "of a union")
+ else:
+ dictionaryObject = None
+
+ if callbackObject or dictionaryObject:
+ assert False, "Not currently supported"
+ else:
+ nonPlatformObject = None
+
+ objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ if len(objectMemberTypes) > 0:
+ raise TypeError("Can't handle objects in unions.")
+ else:
+ object = None
+
+ hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object
+ if hasObjectTypes:
+ assert interfaceObject
+ templateBody = CGList([interfaceObject], "\n")
+ conversions.append(CGIfWrapper(templateBody, "value.is_object()"))
+
+ otherMemberTypes = [
+ t for t in memberTypes if t.isPrimitive() or t.isString() or t.isEnum()
+ ]
+ if len(otherMemberTypes) > 0:
+ assert len(otherMemberTypes) == 1
+ memberType = otherMemberTypes[0]
+ if memberType.isEnum():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ match = (
+ "match %s::TryConvertTo%s(cx, value) {\n"
+ " Err(_) => return Err(()),\n"
+ " Ok(Some(value)) => return Ok(e%s(value)),\n"
+ " Ok(None) => (),\n"
+ "}\n") % (self.type, name, name)
+ conversions.append(CGGeneric(match))
+ names.append(name)
+
+ conversions.append(CGGeneric(
+ "throw_not_in_union(cx, \"%s\");\n"
+ "Err(())" % ", ".join(names)))
+ method = CGWrapper(
+ CGIndenter(CGList(conversions, "\n\n")),
+ pre="fn from_jsval(cx: *mut JSContext, value: JSVal, _option: ()) -> Result<%s, ()> {\n" % self.type,
+ post="\n}")
+ return CGWrapper(
+ CGIndenter(method),
+ pre="impl FromJSValConvertible<()> for %s {\n" % self.type,
+ post="\n}")
+
+ def try_method(self, t):
+ templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider)
+ returnType = "Result<Option<%s>, ()>" % templateVars["typeName"]
+ jsConversion = templateVars["jsConversion"]
+
+ return CGWrapper(
+ CGIndenter(jsConversion, 4),
+ pre="fn TryConvertTo%s(cx: *mut JSContext, value: JSVal) -> %s {\n" % (t.name, returnType),
+ post="\n}")
+
+ def define(self):
+ from_jsval = self.from_jsval()
+ methods = CGIndenter(CGList([
+ self.try_method(t) for t in self.type.flatMemberTypes
+ ], "\n\n"))
+ return """
+%s
+
+impl %s {
+%s
+}
+""" % (from_jsval.define(), self.type, methods.define())
+
+
+class ClassItem:
+ """ Use with CGClass """
+ def __init__(self, name, visibility):
+ self.name = name
+ self.visibility = visibility
+ def declare(self, cgClass):
+ assert False
+ def define(self, cgClass):
+ assert False
+
+class ClassBase(ClassItem):
+ def __init__(self, name, visibility='pub'):
+ ClassItem.__init__(self, name, visibility)
+ def declare(self, cgClass):
+ return '%s %s' % (self.visibility, self.name)
+ def define(self, cgClass):
+ # Only in the header
+ return ''
+
+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",
+ breakAfterSelf="\n", override=False):
+ """
+ override indicates whether to flag the method as MOZ_OVERRIDE
+ """
+ assert not override or virtual
+ self.returnType = returnType
+ self.args = args
+ self.inline = False
+ self.static = static
+ self.virtual = virtual
+ self.const = const
+ self.bodyInHeader = True
+ self.templateArgs = templateArgs
+ self.body = body
+ self.breakAfterReturnDecl = breakAfterReturnDecl
+ self.breakAfterSelf = breakAfterSelf
+ self.override = override
+ ClassItem.__init__(self, name, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline:
+ decorators.append('inline')
+ if declaring:
+ if self.static:
+ decorators.append('static')
+ if self.virtual:
+ decorators.append('virtual')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ # Override me or pass a string to constructor
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ templateClause = '<%s>' % ', '.join(self.templateArgs) \
+ if self.bodyInHeader and self.templateArgs else ''
+ args = ', '.join([a.declare() for a in self.args])
+ if self.bodyInHeader:
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+ body = ' {\n' + body + '\n}'
+ else:
+ body = ';'
+
+ return string.Template("${decorators}%s"
+ "${visibility}fn ${name}${templateClause}(${args})${returnType}${const}${override}${body}%s" %
+ (self.breakAfterReturnDecl, self.breakAfterSelf)
+ ).substitute({
+ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(True),
+ 'returnType': (" -> %s" % self.returnType) if self.returnType else "",
+ 'name': self.name,
+ 'const': ' const' if self.const else '',
+ 'override': ' MOZ_OVERRIDE' if self.override else '',
+ 'args': args,
+ 'body': body,
+ 'visibility': self.visibility + ' ' if self.visibility is not 'priv' else ''
+ })
+
+ def define(self, cgClass):
+ pass
+
+class ClassUsingDeclaration(ClassItem):
+ """"
+ Used for importing a name from a base class into a CGClass
+
+ baseClass is the name of the base class to import the name from
+
+ name is the name to import
+
+ visibility determines the visibility of the name (public,
+ protected, private), defaults to public.
+ """
+ def __init__(self, baseClass, name, visibility='public'):
+ self.baseClass = baseClass
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return string.Template("""using ${baseClass}::${name};
+""").substitute({ 'baseClass': self.baseClass,
+ 'name': self.name })
+
+ def define(self, cgClass):
+ return ''
+
+class ClassConstructor(ClassItem):
+ """
+ Used for adding a constructor to a CGClass.
+
+ args is a list of Argument objects that are the arguments taken by the
+ constructor.
+
+ inline should be True if the constructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the constructor (public,
+ protected, private), defaults to private.
+
+ explicit should be True if the constructor should be marked explicit.
+
+ baseConstructors is a list of strings containing calls to base constructors,
+ defaults to None.
+
+ body contains a string with the code for the constructor, defaults to empty.
+ """
+ def __init__(self, args, inline=False, bodyInHeader=False,
+ visibility="priv", explicit=False, baseConstructors=None,
+ body=""):
+ self.args = args
+ self.inline = False
+ self.bodyInHeader = bodyInHeader
+ self.explicit = explicit
+ self.baseConstructors = baseConstructors or []
+ self.body = body
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.explicit:
+ decorators.append('explicit')
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getInitializationList(self, cgClass):
+ items = [str(c) for c in self.baseConstructors]
+ for m in cgClass.members:
+ if not m.static:
+ initialize = m.body
+ if initialize:
+ items.append(m.name + "(" + initialize + ")")
+
+ if len(items) > 0:
+ return '\n : ' + ',\n '.join(items)
+ return ''
+
+ def getBody(self, cgClass):
+ initializers = [" parent: %s" % str(self.baseConstructors[0])]
+ return (self.body + (
+ "%s {\n"
+ "%s\n"
+ "}") % (cgClass.name, '\n'.join(initializers)))
+
+ def declare(self, cgClass):
+ args = ', '.join([a.declare() for a in self.args])
+ body = ' ' + self.getBody(cgClass);
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = ' {\n' + body + '}'
+
+ return string.Template("""pub fn ${decorators}new(${args}) -> ${className}${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ args = ', '.join([a.define() for a in self.args])
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::${className}(${args})${initializationList}
+{${body}}
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'initializationList': self.getInitializationList(cgClass),
+ 'body': body })
+
+class ClassDestructor(ClassItem):
+ """
+ Used for adding a destructor to a CGClass.
+
+ inline should be True if the destructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the destructor (public,
+ protected, private), defaults to private.
+
+ body contains a string with the code for the destructor, defaults to empty.
+
+ virtual determines whether the destructor is virtual, defaults to False.
+ """
+ def __init__(self, inline=False, bodyInHeader=False,
+ visibility="private", body='', virtual=False):
+ self.inline = inline or bodyInHeader
+ self.bodyInHeader = bodyInHeader
+ self.body = body
+ self.virtual = virtual
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.virtual and declaring:
+ decorators.append('virtual')
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ return self.body
+
+ def declare(self, cgClass):
+ if self.bodyInHeader:
+ body = ' ' + self.getBody();
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = '\n{\n' + body + '}'
+ else:
+ body = ';'
+
+ return string.Template("""${decorators}~${className}()${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::~${className}()
+{${body}}
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'body': body })
+
+class ClassMember(ClassItem):
+ def __init__(self, name, type, visibility="priv", static=False,
+ body=None):
+ self.type = type;
+ self.static = static
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return '%s %s: %s,\n' % (self.visibility, self.name, self.type)
+
+ def define(self, cgClass):
+ if not self.static:
+ return ''
+ if self.body:
+ body = " = " + self.body
+ else:
+ body = ""
+ return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
+ self.name, body)
+
+class ClassTypedef(ClassItem):
+ def __init__(self, name, type, visibility="public"):
+ self.type = type
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'typedef %s %s;\n' % (self.type, self.name)
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassEnum(ClassItem):
+ def __init__(self, name, entries, values=None, visibility="public"):
+ self.entries = entries
+ self.values = values
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ entries = []
+ for i in range(0, len(self.entries)):
+ if not self.values or i >= len(self.values):
+ entry = '%s' % self.entries[i]
+ else:
+ entry = '%s = %s' % (self.entries[i], self.values[i])
+ entries.append(entry)
+ name = '' if not self.name else ' ' + self.name
+ return 'enum%s\n{\n %s\n};\n' % (name, ',\n '.join(entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassUnion(ClassItem):
+ def __init__(self, name, entries, visibility="public"):
+ self.entries = [entry + ";" for entry in entries]
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'union %s\n{\n %s\n};\n' % (self.name, '\n '.join(self.entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class CGClass(CGThing):
+ def __init__(self, name, bases=[], members=[], constructors=[],
+ destructor=None, methods=[],
+ typedefs = [], enums=[], unions=[], templateArgs=[],
+ templateSpecialization=[], isStruct=False,
+ disallowCopyConstruction=False, indent='',
+ decorators='',
+ extradeclarations='',
+ extradefinitions=''):
+ CGThing.__init__(self)
+ self.name = name
+ self.bases = bases
+ self.members = members
+ self.constructors = constructors
+ # We store our single destructor in a list, since all of our
+ # code wants lists of members.
+ self.destructors = [destructor] if destructor else []
+ self.methods = methods
+ self.typedefs = typedefs
+ self.enums = enums
+ self.unions = unions
+ self.templateArgs = templateArgs
+ self.templateSpecialization = templateSpecialization
+ self.isStruct = isStruct
+ self.disallowCopyConstruction = disallowCopyConstruction
+ self.indent = indent
+ self.decorators = decorators
+ self.extradeclarations = extradeclarations
+ self.extradefinitions = extradefinitions
+
+ def getNameString(self):
+ className = self.name
+ if self.templateSpecialization:
+ className = className + \
+ '<%s>' % ', '.join([str(a) for a
+ in self.templateSpecialization])
+ return className
+
+ def define(self):
+ result = ''
+ if self.templateArgs:
+ templateArgs = [a.declare() for a in self.templateArgs]
+ templateArgs = templateArgs[len(self.templateSpecialization):]
+ result = result + self.indent + 'template <%s>\n' \
+ % ','.join([str(a) for a in templateArgs])
+
+ if self.templateSpecialization:
+ specialization = \
+ '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
+ else:
+ specialization = ''
+
+ myself = ''
+ if self.decorators != '':
+ myself += self.decorators + '\n'
+ myself += '%spub struct %s%s' % (self.indent, self.name, specialization)
+ result += myself
+
+ assert len(self.bases) == 1 #XXjdm Can we support multiple inheritance?
+
+ result += '{\n%s\n' % self.indent
+
+ if self.bases:
+ self.members = [ClassMember("parent", self.bases[0].name, "pub")] + self.members
+
+ result += CGIndenter(CGGeneric(self.extradeclarations),
+ len(self.indent)).define()
+
+ def declareMembers(cgClass, memberList):
+ result = ''
+
+ for member in memberList:
+ declaration = member.declare(cgClass)
+ declaration = CGIndenter(CGGeneric(declaration)).define()
+ result = result + declaration
+ return result
+
+ if self.disallowCopyConstruction:
+ class DisallowedCopyConstructor(object):
+ def __init__(self):
+ self.visibility = "private"
+ def declare(self, cgClass):
+ name = cgClass.getNameString()
+ return ("%s(const %s&) MOZ_DELETE;\n"
+ "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name))
+ disallowedCopyConstructors = [DisallowedCopyConstructor()]
+ else:
+ disallowedCopyConstructors = []
+
+ order = [(self.enums, ''), (self.unions, ''),
+ (self.typedefs, ''), (self.members, '')]
+
+ for (memberList, separator) in order:
+ memberString = declareMembers(self, memberList)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result += self.indent + '}\n\n'
+ result += 'impl %s {\n' % self.name
+
+ order = [(self.constructors + disallowedCopyConstructors, '\n'),
+ (self.destructors, '\n'), (self.methods, '\n)')]
+ for (memberList, separator) in order:
+ memberString = declareMembers(self, memberList)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result += "}"
+ return result
+
+class CGProxySpecialOperation(CGPerSignatureCall):
+ """
+ Base class for classes for calling an indexed or named special operation
+ (don't use this directly, use the derived classes below).
+ """
+ def __init__(self, descriptor, operation):
+ nativeName = MakeNativeName(operation)
+ operation = descriptor.operations[operation]
+ assert len(operation.signatures()) == 1
+ signature = operation.signatures()[0]
+
+ (returnType, arguments) = signature
+
+ # We pass len(arguments) as the final argument so that the
+ # CGPerSignatureCall won't do any argument conversion of its own.
+ CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
+ False, descriptor, operation,
+ len(arguments))
+
+ if operation.isSetter() or operation.isCreator():
+ # arguments[0] is the index or name of the item that we're setting.
+ argument = arguments[1]
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ argument.type, descriptor, treatNullAs=argument.treatNullAs)
+ templateValues = {
+ "val": "(*desc).value",
+ }
+ self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(
+ template, templateValues, declType, argument.identifier.name,
+ needsRooting))
+ elif operation.isGetter():
+ self.cgRoot.prepend(CGGeneric("let mut found = false;"))
+
+ def getArguments(self):
+ def process(arg):
+ argVal = arg.identifier.name
+ if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
+ argVal += ".root_ref()"
+ return argVal
+ args = [(a, process(a)) for a in self.arguments]
+ if self.idlNode.isGetter():
+ args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
+ self.idlNode),
+ "&mut found"))
+ return args
+
+ def wrap_return_value(self):
+ if not self.idlNode.isGetter() or self.templateValues is None:
+ return ""
+
+ wrap = CGGeneric(wrapForType(**self.templateValues))
+ wrap = CGIfWrapper(wrap, "found")
+ return "\n" + wrap.define()
+
+class CGProxyIndexedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter')
+
+class CGProxyIndexedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter')
+
+class CGProxyNamedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an named getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter')
+
+class CGProxyNamedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to a named setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
+
+class CGProxyUnwrap(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSObject', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*const ' + descriptor.concreteType, args, alwaysInline=True)
+
+ def definition_body(self):
+ return CGGeneric("""/*if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+}*/
+//MOZ_ASSERT(IsProxy(obj));
+let box_ = GetProxyPrivate(obj).to_private() as *const %s;
+return box_;""" % self.descriptor.concreteType)
+
+class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('*mut JSPropertyDescriptor', 'desc')]
+ CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor",
+ "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+
+ setOrIndexedGet = ""
+ if indexedGetter or indexedSetter:
+ setOrIndexedGet += "let index = GetArrayIndexFromId(cx, id);\n"
+
+ if indexedGetter:
+ readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor}
+ get = ("if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n")
+
+ if indexedSetter or self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += "if set != 0 {\n"
+ if indexedSetter:
+ setOrIndexedGet += (" if index.is_some() {\n" +
+ " let index = index.unwrap();\n")
+ if not 'IndexedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property index'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, proxy, false);\n" +
+ " return true;\n" +
+ " }\n")
+ if self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += " if RUST_JSID_IS_STRING(id) {\n"
+ if not 'NamedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property name'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, proxy, false);\n" +
+ " return true;\n" +
+ " }\n")
+ setOrIndexedGet += "}"
+ if indexedGetter:
+ setOrIndexedGet += (" else {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}")
+ setOrIndexedGet += "\n\n"
+ elif indexedGetter:
+ setOrIndexedGet += ("if !set {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}\n\n")
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor}
+ # Once we start supporting OverrideBuiltins we need to make
+ # ResolveOwnProperty or EnumerateOwnProperties filter out named
+ # properties that shadow prototype properties.
+ namedGet = ("\n" +
+ "if !set && RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n")
+ else:
+ namedGet = ""
+
+ return setOrIndexedGet + """let expando: *mut JSObject = GetExpandoObject(proxy);
+//if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+if expando.is_not_null() {
+ let flags = if set { JSRESOLVE_ASSIGNING } else { 0 } | JSRESOLVE_QUALIFIED;
+ if JS_GetPropertyDescriptorById(cx, expando, id, flags, desc) == 0 {
+ return false;
+ }
+ if (*desc).obj.is_not_null() {
+ // Pretend the property lives on the wrapper.
+ (*desc).obj = proxy;
+ return true;
+ }
+}
+""" + namedGet + """
+(*desc).obj = ptr::mut_null();
+return true;"""
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'),
+ Argument('*const JSPropertyDescriptor', 'desc')]
+ CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ set = ""
+
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+ if indexedSetter:
+ if not (self.descriptor.operations['IndexedCreator'] is indexedSetter):
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
+ " return true;\n" +
+ "}\n")
+ elif self.descriptor.operations['IndexedGetter']:
+ set += ("if GetArrayIndexFromId(cx, id).is_some() {\n" +
+ " return false;\n" +
+ " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ "}\n") % self.descriptor.name
+
+ namedSetter = self.descriptor.operations['NamedSetter']
+ if namedSetter:
+ if not self.descriptor.operations['NamedCreator'] is namedSetter:
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("if RUST_JSID_IS_STRING(id) != 0 {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
+ "}\n")
+ elif self.descriptor.operations['NamedGetter']:
+ set += ("if RUST_JSID_IS_STRING(id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
+ " if (found) {\n"
+ " return false;\n" +
+ " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ " }\n" +
+ " return true;\n"
+ "}\n") % (self.descriptor.name)
+ return set + """return proxyhandler::defineProperty_(%s);""" % ", ".join(a.name for a in self.args)
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'), Argument('*mut bool', 'bp')]
+ CGAbstractExternMethod.__init__(self, descriptor, "hasOwn", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ indexed = ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n" +
+ " return true;\n" +
+ "}\n\n")
+ else:
+ indexed = ""
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ named = ("if RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n"
+ " return true;\n"
+ "}\n" +
+ "\n")
+ else:
+ named = ""
+
+ return indexed + """let expando: *mut JSObject = GetExpandoObject(proxy);
+if expando.is_not_null() {
+ let mut b: JSBool = 1;
+ let ok = JS_HasPropertyById(cx, expando, id, &mut b) != 0;
+ *bp = b != 0;
+ if !ok || *bp {
+ return ok;
+ }
+}
+
+""" + named + """*bp = false;
+return true;"""
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_get(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('*mut JSObject', 'receiver'), Argument('jsid', 'id'),
+ Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ getFromExpando = """let expando = GetExpandoObject(proxy);
+if expando.is_not_null() {
+ let mut hasProp = 0;
+ if JS_HasPropertyById(cx, expando, id, &mut hasProp) == 0 {
+ return false;
+ }
+
+ if hasProp != 0 {
+ return JS_GetPropertyById(cx, expando, id, vp) != 0;
+ }
+}"""
+
+ templateValues = {
+ 'jsvalRef': '*vp',
+ 'successCode': 'return true;',
+ }
+
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ getIndexedOrExpando = ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\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.
+} else {
+ %s
+}
+""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n ')))
+ else:
+ getIndexedOrExpando = getFromExpando + "\n"
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter and False: #XXXjdm unfinished
+ getNamed = ("if (JSID_IS_STRING(id)) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
+ "}\n") % (self.descriptor.concreteType)
+ else:
+ getNamed = ""
+
+ return """//MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ //"Should not have a XrayWrapper here");
+
+%s
+let mut found = false;
+if !GetPropertyOnPrototype(cx, proxy, id, &mut found, vp) {
+ return false;
+}
+
+if found {
+ return true;
+}
+%s
+*vp = UndefinedValue();
+return true;""" % (getIndexedOrExpando, getNamed)
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_obj_toString(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy')]
+ CGAbstractExternMethod.__init__(self, descriptor, "obj_toString", "*mut JSString", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ stringifier = self.descriptor.operations['Stringifier']
+ if stringifier:
+ nativeName = MakeNativeName(stringifier.identifier.name)
+ signature = stringifier.signatures()[0]
+ returnType = signature[0]
+ extendedAttributes = self.descriptor.getExtendedAttributes(stringifier)
+ infallible = 'infallible' in extendedAttributes
+ if not infallible:
+ error = CGGeneric(
+ ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString");\n' +
+ "return NULL;") % self.descriptor.interface.identifier.name)
+ else:
+ error = None
+ call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)")
+ return call.define() + """
+
+JSString* jsresult;
+return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;"""
+
+ return """let s = "%s".to_c_str();
+ _obj_toString(cx, s.as_ptr())""" % self.descriptor.name
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGAbstractClassHook(CGAbstractExternMethod):
+ """
+ Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
+ 'this' unwrapping as it assumes that the unwrapped type is always known.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractExternMethod.__init__(self, descriptor, name, returnType,
+ args)
+
+ def definition_body_prologue(self):
+ return CGGeneric("""\
+let this: *const %s = unwrap::<%s>(obj);
+""" % (self.descriptor.concreteType, self.descriptor.concreteType))
+
+ def definition_body(self):
+ return CGList([
+ self.definition_body_prologue(),
+ self.generate_code(),
+ ])
+
+ def generate_code(self):
+ # Override me
+ assert(False)
+
+def finalizeHook(descriptor, hookName, context):
+ release = """let val = JS_GetReservedSlot(obj, dom_object_slot(obj));
+let _: Box<%s> = mem::transmute(val.to_private());
+debug!("%s finalize: {:p}", this);
+""" % (descriptor.concreteType, descriptor.concreteType)
+ return release
+
+class CGClassTraceHook(CGAbstractClassHook):
+ """
+ A hook to trace through our native object; used for GC and CC
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSTracer', 'trc'), Argument('*mut JSObject', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
+ args)
+
+ def generate_code(self):
+ return CGGeneric("(*this).trace(%s);" % self.args[0].name)
+
+class CGClassConstructHook(CGAbstractExternMethod):
+ """
+ JS-visible constructor for our objects
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME,
+ 'JSBool', args)
+ self._ctor = self.descriptor.interface.ctor()
+
+ def define(self):
+ if not self._ctor:
+ return ""
+ return CGAbstractExternMethod.define(self)
+
+ def definition_body(self):
+ preamble = CGGeneric("""\
+let global = global_object_for_js_object(JS_CALLEE(cx, vp).to_object());
+let global = global.root();
+""")
+ nativeName = MakeNativeName(self._ctor.identifier.name)
+ callGenerator = CGMethodCall(["&global.root_ref()"], nativeName, True,
+ self.descriptor, self._ctor)
+ return CGList([preamble, callGenerator])
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+ """
+ A hook for finalize, used to release our native object.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSFreeOp', 'fop'), Argument('*mut JSObject', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ return CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))
+
+class CGDOMJSProxyHandlerDOMClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ return """
+static Class: DOMClass = """ + DOMClass(self.descriptor) + """;
+
+"""
+
+
+class CGInterfaceTrait(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ def argument_type(ty, optional=False, defaultValue=None, variadic=False):
+ _, _, declType, _ = getJSToNativeConversionTemplate(
+ ty, descriptor, isArgument=True)
+
+ if variadic:
+ declType = CGWrapper(declType, pre="Vec<", post=">")
+ elif optional and not defaultValue:
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ if ty.isGeckoInterface() and not (ty.nullable() or optional):
+ declType = CGWrapper(declType, pre="&")
+ elif ty.isDictionary():
+ declType = CGWrapper(declType, pre="&")
+
+ return declType.define()
+
+ def attribute_arguments(needCx, argument=None):
+ if needCx:
+ yield "cx", "*mut JSContext"
+
+ if argument:
+ yield "value", argument_type(argument)
+
+ def method_arguments(returnType, arguments, trailing=None):
+ if needCx(returnType, arguments, True):
+ yield "cx", "*mut JSContext"
+
+ for argument in arguments:
+ ty = argument_type(argument.type, argument.optional,
+ argument.defaultValue, argument.variadic)
+ yield CGDictionary.makeMemberName(argument.identifier.name), ty
+
+ if trailing:
+ yield trailing
+
+ def return_type(rettype, infallible):
+ result = getRetvalDeclarationForType(rettype, descriptor)
+ if not infallible:
+ result = CGWrapper(result, pre="Fallible<", post=">")
+ return result.define()
+
+ def members():
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isStatic():
+ name = CGSpecializedMethod.makeNativeName(descriptor, m)
+ infallible = 'infallible' in descriptor.getExtendedAttributes(m)
+ for idx, (rettype, arguments) in enumerate(m.signatures()):
+ arguments = method_arguments(rettype, arguments)
+ rettype = return_type(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)
+ needCx = typeNeedsCx(m.type)
+ yield name, attribute_arguments(needCx), return_type(m.type, infallible)
+
+ if not m.readonly:
+ name = CGSpecializedSetter.makeNativeName(descriptor, m)
+ infallible = 'infallible' in descriptor.getExtendedAttributes(m, setter=True)
+ if infallible:
+ rettype = "()"
+ else:
+ rettype = "ErrorResult"
+ yield name, attribute_arguments(needCx, m.type), rettype
+
+ if descriptor.proxy:
+ for name, operation in descriptor.operations.iteritems():
+ if not operation:
+ continue
+
+ assert len(operation.signatures()) == 1
+ rettype, arguments = operation.signatures()[0]
+
+ infallible = 'infallible' in descriptor.getExtendedAttributes(operation)
+ arguments = method_arguments(rettype, arguments, ("found", "&mut bool"))
+ rettype = return_type(rettype, infallible)
+ yield name, arguments, rettype
+
+ def fmt(arguments):
+ return "".join(", %s: %s" % argument for argument in arguments)
+
+ methods = CGList([
+ CGGeneric("fn %s(&self%s) -> %s;\n" % (name, fmt(arguments), rettype))
+ for name, arguments, rettype in members()
+ ], "")
+ self.cgRoot = CGWrapper(CGIndenter(methods),
+ pre="pub trait %sMethods {\n" % descriptor.interface.identifier.name,
+ post="}")
+
+ def define(self):
+ return self.cgRoot.define()
+
+
+class CGDescriptor(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ assert not descriptor.interface.isCallback()
+
+ cgThings = []
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
+ if descriptor.interface.hasInterfaceObject():
+ # https://github.com/mozilla/servo/issues/2665
+ # cgThings.append(CGGetConstructorObjectMethod(descriptor))
+ pass
+
+ (hasMethod, hasGetter, hasLenientGetter,
+ hasSetter, hasLenientSetter) = False, False, False, False, False
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isIdentifierLess():
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticMethod(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedMethod(descriptor, m))
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ hasMethod = True
+ elif m.isAttr():
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticGetter(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedGetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientGetter = True
+ else:
+ hasGetter = True
+
+ if not m.readonly:
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticSetter(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedSetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientSetter = True
+ else:
+ hasSetter = True
+
+ if not m.isStatic():
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ if hasMethod:
+ cgThings.append(CGGenericMethod(descriptor))
+ if hasGetter:
+ cgThings.append(CGGenericGetter(descriptor))
+ if hasLenientGetter:
+ pass
+ if hasSetter:
+ cgThings.append(CGGenericSetter(descriptor))
+ if hasLenientSetter:
+ pass
+
+ if descriptor.concrete:
+ cgThings.append(CGClassFinalizeHook(descriptor))
+ cgThings.append(CGClassTraceHook(descriptor))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGClassConstructHook(descriptor))
+ cgThings.append(CGInterfaceObjectJSClass(descriptor))
+
+ cgThings.append(CGPrototypeJSClass(descriptor))
+
+ properties = PropertyArrays(descriptor)
+ cgThings.append(CGGeneric(str(properties)))
+ cgThings.append(CGNativeProperties(descriptor, properties))
+ cgThings.append(CGNativePropertyHooks(descriptor, properties))
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
+
+ cgThings.append(CGNamespace.build([descriptor.name + "Constants"],
+ CGConstant(m for m in descriptor.interface.members if m.isConst()),
+ public=True))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+
+ if descriptor.proxy:
+ cgThings.append(CGDefineProxyHandler(descriptor))
+
+ if descriptor.concrete:
+ if descriptor.proxy:
+ #cgThings.append(CGProxyIsProxy(descriptor))
+ cgThings.append(CGProxyUnwrap(descriptor))
+ cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_obj_toString(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_get(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_hasOwn(descriptor))
+ if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
+ cgThings.append(CGDOMJSProxyHandler_defineProperty(descriptor))
+
+ #cgThings.append(CGDOMJSProxyHandler(descriptor))
+ #cgThings.append(CGIsMethod(descriptor))
+ pass
+ else:
+ cgThings.append(CGDOMJSClass(descriptor))
+ pass
+
+ cgThings.append(CGWrapMethod(descriptor))
+
+ cgThings.append(CGIDLInterface(descriptor))
+ cgThings.append(CGInterfaceTrait(descriptor))
+
+ cgThings = CGList(cgThings, "\n")
+ cgThings = CGWrapper(cgThings, pre='\n', post='\n')
+ #self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
+ # cgThings),
+ # post='\n')
+ self.cgRoot = cgThings
+
+ def define(self):
+ return self.cgRoot.define()
+
+class CGNamespacedEnum(CGThing):
+ def __init__(self, namespace, enumName, names, values, comment="", deriving=""):
+
+ if not values:
+ values = []
+
+ # Account for explicit enum values.
+ entries = []
+ for i in range(0, len(names)):
+ if len(values) > i and values[i] is not None:
+ entry = "%s = %s" % (names[i], values[i])
+ else:
+ entry = names[i]
+ entries.append(entry)
+
+ # Append a Count.
+ entries.append(enumName + 'Count = ' + str(len(entries)))
+
+ # Indent.
+ entries = [' ' + e for e in entries]
+
+ # Build the enum body.
+ enumstr = comment + 'pub enum %s {\n%s\n}\n' % (enumName, ',\n'.join(entries))
+ if deriving:
+ enumstr = ('#[deriving(%s)]\n' % deriving) + enumstr
+ curr = CGGeneric(enumstr)
+
+ # Add some whitespace padding.
+ curr = CGWrapper(curr, pre='\n',post='\n')
+
+ # Add the namespace.
+ curr = CGNamespace(namespace, curr, public=True)
+
+ # Add the typedef
+ #typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
+ #curr = CGList([curr, CGGeneric(typedef)])
+
+ # Save the result.
+ self.node = curr
+
+ def define(self):
+ return self.node.define()
+
+class CGDictionary(CGThing):
+ def __init__(self, dictionary, descriptorProvider):
+ self.dictionary = dictionary;
+ if all(CGDictionary(d, descriptorProvider).generatable for
+ d in CGDictionary.getDictionaryDependencies(dictionary)):
+ self.generatable = True
+ else:
+ self.generatable = False
+ # Nothing else to do here
+ return
+ self.memberInfo = [
+ (member,
+ getJSToNativeConversionTemplate(member.type,
+ descriptorProvider,
+ isMember="Dictionary",
+ defaultValue=member.defaultValue,
+ failureCode="return Err(());",
+ exceptionCode="return Err(());"))
+ for member in dictionary.members ]
+
+ def define(self):
+ if not self.generatable:
+ return ""
+ return self.struct() + "\n" + self.impl()
+
+ def struct(self):
+ d = self.dictionary
+ if d.parent:
+ inheritance = " pub parent: %s::%s<'a, 'b>,\n" % (self.makeModuleName(d.parent),
+ self.makeClassName(d.parent))
+ else:
+ inheritance = ""
+ memberDecls = [" pub %s: %s," %
+ (self.makeMemberName(m[0].identifier.name), self.getMemberType(m))
+ for m in self.memberInfo]
+
+ return (string.Template(
+ "pub struct ${selfName}<'a, 'b> {\n" +
+ "${inheritance}" +
+ "\n".join(memberDecls) + "\n" +
+ "}").substitute( { "selfName": self.makeClassName(d),
+ "inheritance": inheritance }))
+
+ def impl(self):
+ d = self.dictionary
+ if d.parent:
+ initParent = ("parent: match %s::%s::new(cx, val) {\n"
+ " Ok(parent) => parent,\n"
+ " Err(_) => return Err(()),\n"
+ "},\n") % (self.makeModuleName(d.parent),
+ self.makeClassName(d.parent))
+ else:
+ initParent = ""
+
+ def memberInit(memberInfo):
+ member, _ = memberInfo
+ name = self.makeMemberName(member.identifier.name)
+ conversion = self.getMemberConversion(memberInfo)
+ return CGGeneric("%s: %s,\n" % (name, conversion.define()))
+
+ memberInits = CGList([memberInit(m) for m in self.memberInfo])
+
+ return string.Template(
+ "impl<'a, 'b> ${selfName}<'a, 'b> {\n"
+ " pub fn empty() -> ${selfName}<'a, 'b> {\n"
+ " ${selfName}::new(ptr::mut_null(), NullValue()).unwrap()\n"
+ " }\n"
+ " pub fn new(cx: *mut JSContext, val: JSVal) -> Result<${selfName}<'a, 'b>, ()> {\n"
+ " let object = if val.is_null_or_undefined() {\n"
+ " ptr::mut_null()\n"
+ " } else if val.is_object() {\n"
+ " val.to_object()\n"
+ " } else {\n"
+ " throw_type_error(cx, \"Value not an object.\");\n"
+ " return Err(());\n"
+ " };\n"
+ " Ok(${selfName} {\n"
+ "${initParent}"
+ "${initMembers}"
+ " })\n"
+ " }\n"
+ "}").substitute({
+ "selfName": self.makeClassName(d),
+ "initParent": CGIndenter(CGGeneric(initParent), indentLevel=6).define(),
+ "initMembers": CGIndenter(memberInits, indentLevel=6).define(),
+ })
+
+ @staticmethod
+ def makeDictionaryName(dictionary):
+ return dictionary.identifier.name
+
+ def makeClassName(self, dictionary):
+ return self.makeDictionaryName(dictionary)
+
+ @staticmethod
+ def makeModuleName(dictionary):
+ name = dictionary.identifier.name
+ if name.endswith('Init'):
+ return toBindingNamespace(name.replace('Init', ''))
+ #XXXjdm This breaks on the test webidl files, sigh.
+ #raise TypeError("No idea how to find this dictionary's definition: " + name)
+ return "/* uh oh */ %s" % name
+
+ def getMemberType(self, memberInfo):
+ member, (_, _, declType, _) = memberInfo
+ if not member.defaultValue:
+ declType = CGWrapper(declType, pre="Option<", post=">")
+ return declType.define()
+
+ def getMemberConversion(self, memberInfo):
+ def indent(s):
+ return CGIndenter(CGGeneric(s), 8).define()
+
+ member, (templateBody, default, declType, _) = memberInfo
+ replacements = { "val": "value" }
+ conversion = string.Template(templateBody).substitute(replacements)
+
+ assert (member.defaultValue is None) == (default is None)
+ if not default:
+ default = "None"
+ conversion = "Some(%s)" % conversion
+
+ conversion = (
+ "match get_dictionary_property(cx, object, \"%s\") {\n"
+ " Err(()) => return Err(()),\n"
+ " Ok(Some(value)) => {\n"
+ "%s\n"
+ " },\n"
+ " Ok(None) => {\n"
+ "%s\n"
+ " },\n"
+ "}") % (member.identifier.name, indent(conversion), indent(default))
+
+ return CGGeneric(conversion)
+
+ @staticmethod
+ def makeIdName(name):
+ return name + "_id"
+
+ @staticmethod
+ def makeMemberName(name):
+ # Can't use Rust keywords as member names.
+ if name == "type":
+ return name + "_"
+ return name
+
+ @staticmethod
+ def getDictionaryDependencies(dictionary):
+ deps = set();
+ if dictionary.parent:
+ deps.add(dictionary.parent)
+ for member in dictionary.members:
+ if member.type.isDictionary():
+ deps.add(member.type.unroll().inner)
+ return deps
+
+class CGRegisterProtos(CGAbstractMethod):
+ def __init__(self, config):
+ arguments = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('*mut JSObject', 'global'),
+ ]
+ CGAbstractMethod.__init__(self, None, 'Register', 'void', arguments,
+ unsafe=False, pub=True)
+ self.config = config
+
+ def definition_body(self):
+ return CGList([
+ CGGeneric("codegen::Bindings::%sBinding::DefineDOMInterface(cx, global);" % desc.name)
+ for desc in self.config.getDescriptors(hasInterfaceObject=True, register=True)
+ ], "\n")
+
+
+class CGRegisterProxyHandlersMethod(CGAbstractMethod):
+ def __init__(self, descriptors):
+ CGAbstractMethod.__init__(self, None, 'RegisterProxyHandlers', 'void', [],
+ unsafe=True, pub=True)
+ self.descriptors = descriptors
+
+ def definition_body(self):
+ return CGList([
+ CGGeneric("proxy_handlers[proxies::%s as uint] = codegen::Bindings::%sBinding::DefineProxyHandler();" % (desc.name, desc.name))
+ for desc in self.descriptors
+ ], "\n")
+
+
+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)),
+ CGRegisterProxyHandlersMethod(descriptors),
+ ], "\n")
+
+ def define(self):
+ return self.root.define()
+
+
+class CGBindingRoot(CGThing):
+ """
+ Root 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):
+ descriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ isCallback=False)
+ dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
+
+ cgthings = []
+
+ mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile)
+ callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ isCallback=True)
+
+ # Do codegen for all the enums
+ cgthings = [CGEnum(e) for e in config.getEnums(webIDLFile)]
+
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider())
+ for d in dictionaries])
+
+ # Do codegen for all the callbacks.
+ cgthings.extend(CGList([CGCallbackFunction(c, config.getDescriptorProvider()),
+ CGCallbackFunctionImpl(c)], "\n")
+ for c in mainCallbacks)
+
+ # Do codegen for all the descriptors
+ cgthings.extend([CGDescriptor(x) for x in descriptors])
+
+ # Do codegen for all the callback interfaces.
+ cgthings.extend(CGList([CGCallbackInterface(x),
+ CGCallbackFunctionImpl(x)], "\n")
+ for x in callbackDescriptors)
+
+ # And make sure we have the right number of newlines at the end
+ curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
+
+ # Wrap all of that in our namespaces.
+ #curr = CGNamespace.build(['dom'],
+ # CGWrapper(curr, pre="\n"))
+
+ # Add imports
+ #XXXjdm This should only import the namespace for the current binding,
+ # not every binding ever.
+ curr = CGImports(curr, descriptors, [
+ 'js',
+ 'js::{JS_ARGV, JS_CALLEE, JS_THIS_OBJECT}',
+ 'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS}',
+ 'js::{JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_SHIFT}',
+ 'js::{JSCLASS_RESERVED_SLOTS_MASK, JSID_VOID, JSJitInfo}',
+ 'js::{JSPROP_ENUMERATE, JSPROP_NATIVE_ACCESSORS, JSPROP_SHARED}',
+ 'js::{JSRESOLVE_ASSIGNING, JSRESOLVE_QUALIFIED}',
+ 'js::jsapi::{JS_CallFunctionValue, JS_GetClass, JS_GetGlobalForObject}',
+ 'js::jsapi::{JS_GetObjectPrototype, JS_GetProperty, JS_GetPropertyById}',
+ 'js::jsapi::{JS_GetPropertyDescriptorById, JS_GetReservedSlot}',
+ 'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_IsExceptionPending}',
+ 'js::jsapi::{JS_NewObject, JS_ObjectIsCallable, JS_SetPrototype}',
+ 'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSBool, JSContext}',
+ 'js::jsapi::{JSClass, JSFreeOp, JSFunctionSpec, JSHandleObject, jsid}',
+ 'js::jsapi::{JSNativeWrapper, JSObject, JSPropertyDescriptor, JS_ArrayIterator}',
+ 'js::jsapi::{JSPropertyOpWrapper, JSPropertySpec, JS_PropertyStub}',
+ 'js::jsapi::{JSStrictPropertyOpWrapper, JSString, JSTracer, JS_ConvertStub}',
+ 'js::jsapi::{JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub}',
+ 'js::jsval::JSVal',
+ 'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
+ 'js::jsval::{NullValue, UndefinedValue}',
+ 'js::glue::{CallJitMethodOp, CallJitPropertyOp, CreateProxyHandler}',
+ 'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
+ 'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}',
+ 'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}',
+ 'js::rust::with_compartment',
+ 'dom::types::*',
+ 'dom::bindings',
+ 'dom::bindings::global::GlobalRef',
+ 'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary}',
+ 'dom::bindings::js::{OptionalRootable, OptionalRootedRootable, ResultRootable}',
+ 'dom::bindings::js::{OptionalRootedReference, OptionalOptionalRootedRootable}',
+ 'dom::bindings::utils::{CreateDOMGlobal, CreateInterfaceObjects2}',
+ 'dom::bindings::utils::{ConstantSpec, cx_for_dom_object}',
+ 'dom::bindings::utils::{dom_object_slot, DOM_OBJECT_SLOT, DOMClass}',
+ 'dom::bindings::utils::{DOMJSClass, JSCLASS_DOM_GLOBAL}',
+ 'dom::bindings::utils::{FindEnumStringIndex, GetArrayIndexFromId}',
+ 'dom::bindings::utils::{GetPropertyOnPrototype, GetProtoOrIfaceArray}',
+ 'dom::bindings::utils::{HasPropertyOnPrototype, IntVal}',
+ 'dom::bindings::utils::{jsid_to_str}',
+ 'dom::bindings::utils::global_object_for_js_object',
+ 'dom::bindings::utils::{Reflectable}',
+ 'dom::bindings::utils::{squirrel_away_unique}',
+ 'dom::bindings::utils::{ThrowingConstructor, unwrap, unwrap_jsmanaged}',
+ 'dom::bindings::utils::VoidVal',
+ 'dom::bindings::utils::get_dictionary_property',
+ 'dom::bindings::utils::{NativeProperties, NativePropertyHooks}',
+ 'dom::bindings::trace::JSTraceable',
+ 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
+ 'dom::bindings::callback::{CallSetup,ExceptionHandling}',
+ 'dom::bindings::callback::{WrapCallThisObject}',
+ 'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}',
+ 'dom::bindings::conversions::IDLInterface',
+ 'dom::bindings::conversions::{Default, Empty}',
+ 'dom::bindings::codegen::*',
+ 'dom::bindings::codegen::Bindings::*',
+ 'dom::bindings::codegen::RegisterBindings',
+ 'dom::bindings::codegen::UnionTypes::*',
+ 'dom::bindings::error::{FailureUnknown, Fallible, Error, ErrorResult}',
+ 'dom::bindings::error::throw_dom_exception',
+ 'dom::bindings::error::throw_type_error',
+ 'dom::bindings::proxyhandler',
+ 'dom::bindings::proxyhandler::{_obj_toString, defineProperty}',
+ 'dom::bindings::proxyhandler::{FillPropertyDescriptor, GetExpandoObject}',
+ 'dom::bindings::proxyhandler::{delete_, getPropertyDescriptor}',
+ 'dom::bindings::str::ByteString',
+ 'page::JSPageInfo',
+ 'libc',
+ 'servo_util::str::DOMString',
+ 'std::mem',
+ 'std::cmp',
+ 'std::ptr',
+ 'std::str',
+ 'std::num',
+ ])
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Store the final result.
+ self.root = curr
+
+ def define(self):
+ return stripTrailingWhitespace(self.root.define())
+
+class CGNativeMember(ClassMethod):
+ def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
+ breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
+ jsObjectsArePtr=False, variadicIsSequence=False):
+ """
+ If jsObjectsArePtr is true, typed arrays and "object" will be
+ passed as JSObject*.
+
+ If passJSBitsAsNeeded is false, we don't automatically pass in a
+ JSContext* or a JSObject* based on the return and argument types.
+ """
+ self.descriptorProvider = descriptorProvider
+ self.member = member
+ self.extendedAttrs = extendedAttrs
+ self.passJSBitsAsNeeded = passJSBitsAsNeeded
+ self.jsObjectsArePtr = jsObjectsArePtr
+ self.variadicIsSequence = variadicIsSequence
+ breakAfterSelf = "\n" if breakAfter else ""
+ ClassMethod.__init__(self, name,
+ self.getReturnType(signature[0], False),
+ self.getArgs(signature[0], signature[1]),
+ 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()),
+ breakAfterReturnDecl=" ",
+ breakAfterSelf=breakAfterSelf,
+ visibility=visibility)
+
+ def getReturnType(self, type, isMember):
+ return self.getRetvalInfo(type, isMember)[0]
+
+ def getRetvalInfo(self, type, isMember):
+ """
+ Returns a tuple:
+
+ The first element is the type declaration for the retval
+
+ The second element is a template for actually returning a value stored in
+ "${declName}". This means actually returning it if
+ we're not outparam, else assigning to the "retval" outparam. If
+ isMember is true, this can be None, since in that case the caller will
+ never examine this value.
+ """
+ if type.isVoid():
+ typeDecl, template = "", ""
+ elif type.isPrimitive() and type.tag() in builtinNames:
+ result = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ raise TypeError("Nullable primitives are not supported here.")
+
+ typeDecl, template = result.define(), "return Ok(${declName});"
+ elif type.isDOMString():
+ if isMember:
+ # No need for a third element in the isMember case
+ typeDecl, template = "nsString", None
+ # Outparam
+ else:
+ typeDecl, template = "void", "retval = ${declName};"
+ elif type.isByteString():
+ if isMember:
+ # No need for a third element in the isMember case
+ typeDecl, template = "nsCString", None
+ # Outparam
+ typeDecl, template = "void", "retval = ${declName};"
+ elif type.isEnum():
+ enumName = type.unroll().inner.identifier.name
+ if type.nullable():
+ enumName = CGTemplatedType("Nullable",
+ CGGeneric(enumName)).define()
+ typeDecl, template = enumName, "return ${declName};"
+ elif type.isGeckoInterface():
+ iface = type.unroll().inner;
+ nativeType = self.descriptorProvider.getDescriptor(
+ iface.identifier.name).nativeType
+ # Now trim off unnecessary namespaces
+ nativeType = nativeType.split("::")
+ if nativeType[0] == "mozilla":
+ nativeType.pop(0)
+ if nativeType[0] == "dom":
+ nativeType.pop(0)
+ result = CGWrapper(CGGeneric("::".join(nativeType)), post="*")
+ # Since we always force an owning type for callback return values,
+ # our ${declName} is an OwningNonNull or nsRefPtr. So we can just
+ # .forget() to get our already_AddRefed.
+ typeDecl, template = result.define(), "return ${declName}.forget();"
+ elif type.isCallback():
+ typeDecl, template = \
+ ("already_AddRefed<%s>" % type.unroll().identifier.name,
+ "return ${declName}.forget();")
+ elif type.isAny():
+ typeDecl, template = "JSVal", "return Ok(${declName});"
+ elif type.isObject():
+ typeDecl, template = "JSObject*", "return ${declName};"
+ elif type.isSpiderMonkeyInterface():
+ if type.nullable():
+ returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();"
+ else:
+ returnCode = "return ${declName}.Obj();"
+ typeDecl, template = "JSObject*", returnCode
+ elif type.isSequence():
+ # If we want to handle sequence-of-sequences return values, we're
+ # going to need to fix example codegen to not produce nsTArray<void>
+ # for the relevant argument...
+ assert not isMember
+ # Outparam.
+ if type.nullable():
+ returnCode = ("if (${declName}.IsNull()) {\n"
+ " retval.SetNull();\n"
+ "} else {\n"
+ " retval.SetValue().SwapElements(${declName}.Value());\n"
+ "}")
+ else:
+ returnCode = "retval.SwapElements(${declName});"
+ typeDecl, template = "void", returnCode
+ elif type.isDate():
+ result = CGGeneric("Date")
+ if type.nullable():
+ result = CGTemplatedType("Nullable", result)
+ typeDecl, template = result.define(), "return ${declName};"
+ else:
+ raise TypeError("Don't know how to declare return value for %s" % type)
+
+ if not 'infallible' in self.extendedAttrs:
+ if typeDecl:
+ typeDecl = "Fallible<%s>" % typeDecl
+ else:
+ typeDecl = "ErrorResult"
+ if not template:
+ template = "return Ok(());"
+ return typeDecl, template
+
+ def getArgs(self, returnType, argList):
+ args = [self.getArg(arg) for arg in argList]
+ # Now the outparams
+ if returnType.isDOMString():
+ args.append(Argument("nsString&", "retval"))
+ if returnType.isByteString():
+ args.append(Argument("nsCString&", "retval"))
+ elif returnType.isSequence():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ # And now the actual underlying type
+ elementDecl = self.getReturnType(returnType.inner, True)
+ type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
+ if nullable:
+ type = CGTemplatedType("Nullable", type)
+ args.append(Argument("%s&" % type.define(), "retval"))
+ # The legacycaller thisval
+ if self.member.isMethod() and self.member.isLegacycaller():
+ # If it has an identifier, we can't deal with it yet
+ assert self.member.isIdentifierLess()
+ args.insert(0, Argument("JS::Value", "aThisVal"))
+ # And jscontext bits.
+ if needCx(returnType, argList, self.passJSBitsAsNeeded):
+ args.insert(0, Argument("JSContext*", "cx"))
+ # And if we're static, a global
+ if self.member.isStatic():
+ args.insert(0, Argument("const GlobalObject&", "global"))
+ return args
+
+ def doGetArgType(self, type, optional, isMember):
+ """
+ The main work of getArgType. Returns a string type decl, whether this
+ is a const ref, as well as whether the type should be wrapped in
+ Nullable as needed.
+
+ isMember can be false or one of the strings "Sequence" or "Variadic"
+ """
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ nullable = type.nullable()
+ if nullable:
+ type = type.inner
+ elementType = type.inner
+ argType = self.getArgType(elementType, False, "Sequence")[0]
+ decl = CGTemplatedType("Sequence", argType)
+ return decl.define(), True, True
+
+ if type.isUnion():
+ if type.nullable():
+ type = type.inner
+ return str(type) + "::" + str(type), False, True
+
+ if type.isGeckoInterface() and not type.isCallbackInterface():
+ iface = type.unroll().inner
+ argIsPointer = type.nullable()
+ forceOwningType = iface.isCallback() or isMember
+ if argIsPointer:
+ if (optional or isMember) and forceOwningType:
+ typeDecl = "nsRefPtr<%s>"
+ else:
+ typeDecl = "*%s"
+ else:
+ if optional or isMember:
+ if forceOwningType:
+ typeDecl = "OwningNonNull<%s>"
+ else:
+ typeDecl = "NonNull<%s>"
+ else:
+ typeDecl = "%s"
+ descriptor = self.descriptorProvider.getDescriptor(iface.identifier.name)
+ return (typeDecl % descriptor.argumentType,
+ False, False)
+
+ if type.isSpiderMonkeyInterface():
+ if self.jsObjectsArePtr:
+ return "JSObject*", False, False
+
+ return type.name, True, True
+
+ if type.isDOMString():
+ declType = "DOMString"
+ return declType, True, False
+
+ if type.isByteString():
+ declType = "nsCString"
+ return declType, True, False
+
+ if type.isEnum():
+ return type.unroll().inner.identifier.name, False, True
+
+ if type.isCallback() or type.isCallbackInterface():
+ forceOwningType = optional or isMember
+ if type.nullable():
+ if forceOwningType:
+ declType = "nsRefPtr<%s>"
+ else:
+ declType = "%s*"
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<%s>"
+ else:
+ declType = "%s&"
+ if type.isCallback():
+ name = type.unroll().identifier.name
+ else:
+ name = type.unroll().inner.identifier.name
+ return declType % name, False, False
+
+ if type.isAny():
+ # Don't do the rooting stuff for variadics for now
+ if isMember:
+ declType = "JS::Value"
+ else:
+ declType = "JSVal"
+ return declType, False, False
+
+ if type.isObject():
+ if isMember:
+ declType = "JSObject*"
+ else:
+ declType = "JS::Handle<JSObject*>"
+ return declType, False, False
+
+ if type.isDictionary():
+ typeName = CGDictionary.makeDictionaryName(type.inner)
+ return typeName, True, True
+
+ if type.isDate():
+ return "Date", False, True
+
+ assert type.isPrimitive()
+
+ return builtinNames[type.tag()], False, True
+
+ def getArgType(self, type, optional, isMember):
+ """
+ Get the type of an argument declaration. Returns the type CGThing, and
+ whether this should be a const ref.
+
+ isMember can be False, "Sequence", or "Variadic"
+ """
+ (decl, ref, handleNullable) = self.doGetArgType(type, optional,
+ isMember)
+ decl = CGGeneric(decl)
+ if handleNullable and type.nullable():
+ decl = CGTemplatedType("Nullable", decl)
+ ref = True
+ if isMember == "Variadic":
+ arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
+ decl = CGTemplatedType(arrayType, decl)
+ ref = True
+ elif optional:
+ # Note: All variadic args claim to be optional, but we can just use
+ # empty arrays to represent them not being present.
+ decl = CGTemplatedType("Option", decl)
+ ref = False
+ return (decl, ref)
+
+ def getArg(self, arg):
+ """
+ Get the full argument declaration for an argument
+ """
+ (decl, ref) = self.getArgType(arg.type,
+ arg.optional and not arg.defaultValue,
+ "Variadic" if arg.variadic else False)
+ if ref:
+ decl = CGWrapper(decl, pre="&")
+
+ return Argument(decl.define(), arg.identifier.name)
+
+class CGCallback(CGClass):
+ def __init__(self, idlObject, descriptorProvider, baseName, methods,
+ getters=[], setters=[]):
+ self.baseName = baseName
+ self._deps = idlObject.getDeps()
+ name = idlObject.identifier.name
+ # For our public methods that needThisHandling we want most of the
+ # same args and the same return type as what CallbackMember
+ # generates. So we want to take advantage of all its
+ # CGNativeMember infrastructure, but that infrastructure can't deal
+ # with templates and most especially template arguments. So just
+ # cheat and have CallbackMember compute all those things for us.
+ realMethods = []
+ for method in methods:
+ if not method.needThisHandling:
+ realMethods.append(method)
+ else:
+ realMethods.extend(self.getMethodImpls(method))
+ CGClass.__init__(self, name,
+ bases=[ClassBase(baseName)],
+ constructors=self.getConstructors(),
+ methods=realMethods+getters+setters,
+ decorators="#[deriving(PartialEq,Clone,Encodable)]")
+
+ def getConstructors(self):
+ return [ClassConstructor(
+ [Argument("*mut JSObject", "aCallback")],
+ bodyInHeader=True,
+ visibility="pub",
+ explicit=False,
+ baseConstructors=[
+ "%s::new(aCallback)" % self.baseName
+ ])]
+
+ def getMethodImpls(self, method):
+ assert method.needThisHandling
+ 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[1].name == "aThisObj" and args[1].argType == "*mut JSObject"
+ args = args[2:]
+ # Record the names of all the arguments, so we can use them when we call
+ # the private method.
+ argnames = [arg.name for arg in args]
+ argnamesWithThis = ["s.GetContext()", "thisObjJS"] + argnames
+ argnamesWithoutThis = ["s.GetContext()", "ptr::mut_null()"] + argnames
+ # Now that we've recorded the argnames for our call to our private
+ # method, insert our optional argument for deciding whether the
+ # CallSetup should re-throw exceptions on aRv.
+ args.append(Argument("ExceptionHandling", "aExceptionHandling",
+ "ReportExceptions"))
+
+ args[0] = Argument('&' + args[0].argType, args[0].name, args[0].default)
+ method.args[2] = args[0]
+
+ # And now insert our template argument.
+ argsWithoutThis = list(args)
+ args.insert(0, Argument("&JSRef<T>", "thisObj"))
+
+ # And the self argument
+ method.args.insert(0, Argument(None, "&self"))
+ args.insert(0, Argument(None, "&self"))
+ argsWithoutThis.insert(0, Argument(None, "&self"))
+
+ setupCall = ("let s = CallSetup::new(self, aExceptionHandling);\n"
+ "if s.GetContext().is_null() {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n")
+
+ bodyWithThis = string.Template(
+ setupCall+
+ "let thisObjJS = WrapCallThisObject(s.GetContext(), thisObj);\n"
+ "if thisObjJS.is_null() {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n"
+ "return ${methodName}(${callArgs});").substitute({
+ "callArgs" : ", ".join(argnamesWithThis),
+ "methodName": 'self.' + method.name,
+ })
+ bodyWithoutThis = string.Template(
+ setupCall +
+ "return ${methodName}(${callArgs});").substitute({
+ "callArgs" : ", ".join(argnamesWithoutThis),
+ "methodName": 'self.' + method.name,
+ })
+ return [ClassMethod(method.name+'_', method.returnType, args,
+ bodyInHeader=True,
+ templateArgs=["T: Reflectable"],
+ body=bodyWithThis,
+ visibility='pub'),
+ ClassMethod(method.name+'__', method.returnType, argsWithoutThis,
+ bodyInHeader=True,
+ body=bodyWithoutThis,
+ visibility='pub'),
+ method]
+
+ def deps(self):
+ return self._deps
+
+# We're always fallible
+def callbackGetterName(attr):
+ return "Get" + MakeNativeName(attr.identifier.name)
+
+def callbackSetterName(attr):
+ return "Set" + MakeNativeName(attr.identifier.name)
+
+class CGCallbackFunction(CGCallback):
+ def __init__(self, callback, descriptorProvider):
+ CGCallback.__init__(self, callback, descriptorProvider,
+ "CallbackFunction",
+ methods=[CallCallback(callback, descriptorProvider)])
+
+ def getConstructors(self):
+ return CGCallback.getConstructors(self)
+
+class CGCallbackFunctionImpl(CGGeneric):
+ def __init__(self, callback):
+ impl = string.Template("""impl CallbackContainer for ${type} {
+ fn new(callback: *mut JSObject) -> ${type} {
+ ${type}::new(callback)
+ }
+
+ fn callback(&self) -> *mut JSObject {
+ self.parent.callback()
+ }
+}
+
+impl ToJSValConvertible for ${type} {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ self.callback().to_jsval(cx)
+ }
+}
+""").substitute({"type": callback.name})
+ CGGeneric.__init__(self, impl)
+
+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]
+ 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)
+
+class FakeMember():
+ def __init__(self):
+ self.treatNullAs = "Default"
+ def isStatic(self):
+ return False
+ def isAttr(self):
+ return False
+ def isMethod(self):
+ return False
+ def getExtendedAttribute(self, name):
+ return None
+
+class CallbackMember(CGNativeMember):
+ def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
+ """
+ needThisHandling is True if we need to be able to accept a specified
+ thisObj, False otherwise.
+ """
+ assert not rethrowContentException or not needThisHandling
+
+ self.retvalType = sig[0]
+ self.originalSig = sig
+ args = sig[1]
+ self.argCount = len(args)
+ if self.argCount > 0:
+ # Check for variadic arguments
+ lastArg = args[self.argCount-1]
+ if lastArg.variadic:
+ self.argCountStr = (
+ "(%d - 1) + %s.Length()" % (self.argCount,
+ lastArg.identifier.name))
+ else:
+ self.argCountStr = "%d" % self.argCount
+ self.needThisHandling = needThisHandling
+ # If needThisHandling, we generate ourselves as private and the caller
+ # will handle generating public versions that handle the "this" stuff.
+ visibility = "priv" if needThisHandling else "pub"
+ self.rethrowContentException = rethrowContentException
+ # We don't care, for callback codegen, whether our original member was
+ # a method or attribute or whatnot. Just always pass FakeMember()
+ # here.
+ CGNativeMember.__init__(self, descriptorProvider, FakeMember(),
+ name, (self.retvalType, args),
+ extendedAttrs={},
+ passJSBitsAsNeeded=False,
+ visibility=visibility,
+ jsObjectsArePtr=True)
+ # We have to do all the generation of our body now, because
+ # the caller relies on us throwing if we can't manage it.
+ self.exceptionCode= "return Err(FailureUnknown);\n"
+ self.body = self.getImpl()
+
+ def getImpl(self):
+ replacements = {
+ "declRval": self.getRvalDecl(),
+ "returnResult": self.getResultConversion(),
+ "convertArgs": self.getArgConversions(),
+ "doCall": self.getCall(),
+ "setupCall": self.getCallSetup(),
+ }
+ if self.argCount > 0:
+ replacements["argCount"] = self.argCountStr
+ replacements["argvDecl"] = string.Template(
+ "let mut argv = Vec::from_elem(${argCount}, UndefinedValue());\n"
+ ).substitute(replacements)
+ else:
+ # Avoid weird 0-sized arrays
+ replacements["argvDecl"] = ""
+
+ # Newlines and semicolons are in the values
+ pre = string.Template(
+ "${setupCall}"
+ "${declRval}"
+ "${argvDecl}").substitute(replacements)
+ body = string.Template(
+ "${convertArgs}"
+ "${doCall}"
+ "${returnResult}").substitute(replacements)
+ return CGList([
+ CGGeneric(pre),
+ CGWrapper(CGIndenter(CGGeneric(body)),
+ pre="with_compartment(cx, self.parent.callback(), || {\n",
+ post="})")
+ ], "\n").define()
+
+ def getResultConversion(self):
+ replacements = {
+ "val": "rval",
+ "declName": "rvalDecl",
+ }
+
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ self.retvalType,
+ self.descriptorProvider,
+ exceptionCode=self.exceptionCode,
+ isCallbackReturnValue="Callback",
+ # XXXbz we should try to do better here
+ sourceDescription="return value")
+
+ convertType = instantiateJSToNativeConversionTemplate(
+ template, replacements, declType, "rvalDecl", needsRooting)
+
+ assignRetval = string.Template(
+ self.getRetvalInfo(self.retvalType,
+ False)[1]).substitute(replacements)
+ return convertType.define() + "\n" + assignRetval + "\n"
+
+ def getArgConversions(self):
+ # Just reget the arglist from self.originalSig, because our superclasses
+ # just have way to many members they like to clobber, so I can't find a
+ # safe member name to store it in.
+ argConversions = [self.getArgConversion(i, arg) for (i, arg)
+ in enumerate(self.originalSig[1])]
+ # Do them back to front, so our argc modifications will work
+ # correctly, because we examine trailing arguments first.
+ argConversions.reverse();
+ # Wrap each one in a scope so that any locals it has don't leak out, and
+ # also so that we can just "break;" for our successCode.
+ argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
+ pre="loop {\n",
+ post="\nbreak;}\n")
+ for c in argConversions]
+ if self.argCount > 0:
+ argConversions.insert(0, self.getArgcDecl())
+ # And slap them together.
+ return CGList(argConversions, "\n\n").define() + "\n\n"
+
+ def getArgConversion(self, i, arg):
+ argval = arg.identifier.name
+
+ if arg.variadic:
+ argval = argval + "[idx]"
+ jsvalIndex = "%d + idx" % i
+ else:
+ jsvalIndex = "%d" % i
+ if arg.optional and not arg.defaultValue:
+ argval += ".clone().unwrap()"
+
+ conversion = wrapForType("*argv.get_mut(%s)" % jsvalIndex,
+ result=argval,
+ successCode="continue;" if arg.variadic else "break;")
+ if arg.variadic:
+ conversion = string.Template(
+ "for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {\n" +
+ CGIndenter(CGGeneric(conversion)).define() + "\n"
+ "}\n"
+ "break;").substitute({ "arg": arg.identifier.name })
+ elif arg.optional and not arg.defaultValue:
+ conversion = (
+ CGIfWrapper(CGGeneric(conversion),
+ "%s.is_some()" % arg.identifier.name).define() +
+ " else if (argc == %d) {\n"
+ " // This is our current trailing argument; reduce argc\n"
+ " argc -= 1;\n"
+ "} else {\n"
+ " *argv.get_mut(%d) = UndefinedValue();\n"
+ "}" % (i+1, i))
+ return conversion
+
+ def getArgs(self, returnType, argList):
+ args = CGNativeMember.getArgs(self, returnType, argList)
+ if not self.needThisHandling:
+ # Since we don't need this handling, we're the actual method that
+ # will be called, so we need an aRethrowExceptions argument.
+ if self.rethrowContentException:
+ args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+ else:
+ args.append(Argument("ExceptionHandling", "aExceptionHandling",
+ "ReportExceptions"))
+ return args
+ # We want to allow the caller to pass in a "this" object, as
+ # well as a JSContext.
+ return [Argument("*mut JSContext", "cx"),
+ Argument("*mut JSObject", "aThisObj")] + args
+
+ def getCallSetup(self):
+ if self.needThisHandling:
+ # It's been done for us already
+ return ""
+ callSetup = "CallSetup s(CallbackPreserveColor(), aRv"
+ if self.rethrowContentException:
+ # getArgs doesn't add the aExceptionHandling argument but does add
+ # aCompartment for us.
+ callSetup += ", RethrowContentExceptions, aCompartment"
+ else:
+ callSetup += ", aExceptionHandling"
+ callSetup += ");"
+ return string.Template(
+ "${callSetup}\n"
+ "JSContext* cx = s.GetContext();\n"
+ "if (!cx) {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n").substitute({
+ "callSetup": callSetup,
+ })
+
+ def getArgcDecl(self):
+ return CGGeneric("let mut argc = %su32;" % self.argCountStr);
+
+ @staticmethod
+ def ensureASCIIName(idlObject):
+ type = "attribute" if idlObject.isAttr() else "operation"
+ if re.match("[^\x20-\x7E]", idlObject.identifier.name):
+ raise SyntaxError('Callback %s name "%s" contains non-ASCII '
+ "characters. We can't handle that. %s" %
+ (type, idlObject.identifier.name,
+ idlObject.location))
+ if re.match('"', idlObject.identifier.name):
+ raise SyntaxError("Callback %s name '%s' contains "
+ "double-quote character. We can't handle "
+ "that. %s" %
+ (type, idlObject.identifier.name,
+ idlObject.location))
+
+class CallbackMethod(CallbackMember):
+ def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
+ CallbackMember.__init__(self, sig, name, descriptorProvider,
+ needThisHandling, rethrowContentException)
+ def getRvalDecl(self):
+ return "let mut rval = UndefinedValue();\n"
+
+ def getCall(self):
+ replacements = {
+ "thisObj": self.getThisObj(),
+ "getCallable": self.getCallableDecl()
+ }
+ if self.argCount > 0:
+ replacements["argv"] = "argv.as_mut_ptr()"
+ replacements["argc"] = "argc"
+ else:
+ replacements["argv"] = "nullptr"
+ replacements["argc"] = "0"
+ return string.Template("${getCallable}"
+ "let ok = unsafe {\n"
+ " JS_CallFunctionValue(cx, ${thisObj}, callable,\n"
+ " ${argc}, ${argv}, &mut rval)\n"
+ "};\n"
+ "if ok == 0 {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n").substitute(replacements)
+
+class CallCallback(CallbackMethod):
+ def __init__(self, callback, descriptorProvider):
+ CallbackMethod.__init__(self, callback.signatures()[0], "Call",
+ descriptorProvider, needThisHandling=True)
+
+ def getThisObj(self):
+ return "aThisObj"
+
+ def getCallableDecl(self):
+ return "let callable = ObjectValue(unsafe {&*self.parent.callback()});\n";
+
+class CallbackOperationBase(CallbackMethod):
+ """
+ Common class for implementing various callback operations.
+ """
+ def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False):
+ self.singleOperation = singleOperation
+ self.methodName = jsName
+ CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException)
+
+ def getThisObj(self):
+ if not self.singleOperation:
+ return "self.parent.callback()"
+ # This relies on getCallableDecl declaring a boolean
+ # isCallable in the case when we're a single-operation
+ # interface.
+ return "if isCallable { aThisObj } else { self.parent.callback() }"
+
+ def getCallableDecl(self):
+ replacements = {
+ "methodName": self.methodName
+ }
+ getCallableFromProp = string.Template(
+ 'match self.parent.GetCallableProperty(cx, "${methodName}") {\n'
+ ' Err(_) => return Err(FailureUnknown),\n'
+ ' Ok(callable) => callable,\n'
+ '}').substitute(replacements)
+ if not self.singleOperation:
+ return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
+ return (
+ 'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback()) != 0 };\n'
+ 'let callable =\n' +
+ CGIndenter(
+ CGIfElseWrapper('isCallable',
+ CGGeneric('unsafe { ObjectValue(&*self.parent.callback()) }'),
+ CGGeneric(getCallableFromProp))).define() + ';\n')
+
+class CallbackOperation(CallbackOperationBase):
+ """
+ Codegen actual WebIDL operations on callback interfaces.
+ """
+ def __init__(self, method, signature, descriptor):
+ self.ensureASCIIName(method)
+ jsName = method.identifier.name
+ CallbackOperationBase.__init__(self, signature,
+ jsName, MakeNativeName(jsName),
+ descriptor, descriptor.interface.isSingleOperationInterface(),
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+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,
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+ 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(FailureUnknown);\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,
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+ 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(FailureUnknown);\n'
+ '}\n').substitute(replacements)
+
+ def getArgcDecl(self):
+ return None
+
+class GlobalGenRoots():
+ """
+ Roots for global codegen.
+
+ To generate code, call the method associated with the target, and then
+ call the appropriate define/declare method.
+ """
+
+ @staticmethod
+ def PrototypeList(config):
+ # Prototype ID enum.
+ protos = [d.name for d in config.getDescriptors(isCallback=False)]
+ proxies = [d.name for d in config.getDescriptors(proxy=True)]
+
+ return CGList([
+ CGGeneric(AUTOGENERATED_WARNING_COMMENT),
+ CGGeneric("pub static MAX_PROTO_CHAIN_LENGTH: uint = %d;\n\n" % config.maxProtoChainLength),
+ CGNamespacedEnum('id', 'ID', protos, [0], deriving="PartialEq"),
+ CGNamespacedEnum('proxies', 'Proxy', proxies, [0], deriving="PartialEq"),
+ ])
+
+
+ @staticmethod
+ def RegisterBindings(config):
+ # TODO - Generate the methods we want
+ code = CGList([
+ CGRegisterProtos(config),
+ CGRegisterProxyHandlers(config),
+ ], "\n")
+
+ return CGImports(code, [], [
+ 'dom::bindings::codegen',
+ 'dom::bindings::codegen::PrototypeList::proxies',
+ 'js::jsapi::JSContext',
+ 'js::jsapi::JSObject',
+ 'libc',
+ ])
+
+ @staticmethod
+ def InterfaceTypes(config):
+ descriptors = [d.name for d in config.getDescriptors(register=True, isCallback=False)]
+ curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors])
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def Bindings(config):
+
+ descriptors = (set(d.name + "Binding" for d in config.getDescriptors(register=True)) |
+ set(d.unroll().module() for d in config.callbacks))
+ curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)])
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def InheritTypes(config):
+
+ descriptors = config.getDescriptors(register=True, isCallback=False)
+ allprotos = [CGGeneric("#![allow(unused_imports)]\n"),
+ CGGeneric("use dom::types::*;\n"),
+ CGGeneric("use dom::bindings::js::{JS, JSRef, Temporary};\n"),
+ CGGeneric("use dom::bindings::trace::JSTraceable;\n"),
+ CGGeneric("use dom::bindings::utils::Reflectable;\n"),
+ CGGeneric("use serialize::{Encodable, Encoder};\n"),
+ CGGeneric("use js::jsapi::JSTracer;\n\n")]
+ for descriptor in descriptors:
+ name = descriptor.name
+ protos = [CGGeneric('pub trait %s {}\n' % (name + 'Base'))]
+ for proto in descriptor.prototypeChain:
+ protos += [CGGeneric('impl %s for %s {}\n' % (proto + 'Base',
+ descriptor.concreteType))]
+ derived = [CGGeneric('pub trait %s { fn %s(&self) -> bool; }\n' %
+ (name + 'Derived', 'is_' + name.lower()))]
+ for protoName in descriptor.prototypeChain[1:-1]:
+ protoDescriptor = config.getDescriptor(protoName)
+ delegate = string.Template('''impl ${selfName} for ${baseName} {
+ fn ${fname}(&self) -> bool {
+ self.${parentName}.${fname}()
+ }
+}
+''').substitute({'fname': 'is_' + name.lower(),
+ 'selfName': name + 'Derived',
+ 'baseName': protoDescriptor.concreteType,
+ 'parentName': protoDescriptor.prototypeChain[-2].lower()})
+ derived += [CGGeneric(delegate)]
+ derived += [CGGeneric('\n')]
+
+ cast = [CGGeneric(string.Template('''pub trait ${castTraitName} {
+ #[inline(always)]
+ fn to_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a JSRef<'b, T>) -> Option<&'a JSRef<'b, Self>> {
+ match base.deref().${checkFn}() {
+ true => unsafe { Some(base.transmute()) },
+ false => None
+ }
+ }
+
+ #[inline(always)]
+ fn to_mut_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a mut JSRef<'b, T>) -> Option<&'a mut JSRef<'b, Self>> {
+ match base.deref().${checkFn}() {
+ true => unsafe { Some(base.transmute_mut()) },
+ false => None
+ }
+ }
+
+ #[inline(always)]
+ fn from_ref<'a, 'b, T: ${fromBound}>(derived: &'a JSRef<'b, T>) -> &'a JSRef<'b, Self> {
+ unsafe { derived.transmute() }
+ }
+
+ #[inline(always)]
+ fn from_mut_ref<'a, 'b, T: ${fromBound}>(derived: &'a mut JSRef<'b, T>) -> &'a mut JSRef<'b, Self> {
+ unsafe { derived.transmute_mut() }
+ }
+
+ #[inline(always)]
+ fn from_temporary<T: ${fromBound}+Reflectable>(derived: Temporary<T>) -> Temporary<Self> {
+ unsafe { derived.transmute() }
+ }
+}
+''').substitute({'checkFn': 'is_' + name.lower(),
+ 'castTraitName': name + 'Cast',
+ 'fromBound': name + 'Base',
+ 'toBound': name + 'Derived'})),
+ CGGeneric("impl %s for %s {}\n\n" % (name + 'Cast', name))]
+
+ trace = [CGGeneric(string.Template('''impl JSTraceable for ${name} {
+ fn trace(&self, tracer: *mut JSTracer) {
+ unsafe {
+ self.encode(&mut *tracer).ok().expect("failed to encode");
+ }
+ }
+}
+''').substitute({'name': name}))]
+
+ allprotos += protos + derived + cast + trace
+
+ curr = CGList(allprotos)
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def UnionTypes(config):
+
+ curr = UnionTypes(config.getDescriptors(),
+ config.getDictionaries(),
+ config.getCallbacks(),
+ config)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr
diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py
new file mode 100644
index 00000000000..d9be43fd2e6
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Configuration.py
@@ -0,0 +1,341 @@
+# 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/.
+
+from WebIDL import IDLInterface
+
+autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
+
+class Configuration:
+ """
+ Represents global configuration state based on IDL parse data and
+ the configuration file.
+ """
+ def __init__(self, filename, parseData):
+ # Read the configuration file.
+ glbl = {}
+ execfile(filename, glbl)
+ config = glbl['DOMInterfaces']
+
+ # Build descriptors for all the interfaces we have in the parse data.
+ # This allows callers to specify a subset of interfaces by filtering
+ # |parseData|.
+ self.descriptors = []
+ self.interfaces = {}
+ self.maxProtoChainLength = 0;
+ for thing in parseData:
+ # Some toplevel things are sadly types, and those have an
+ # isInterface that doesn't mean the same thing as IDLObject's
+ # isInterface()...
+ if not isinstance(thing, IDLInterface):
+ continue
+
+ 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]
+ if not isinstance(entry, list):
+ assert isinstance(entry, dict)
+ entry = [entry]
+ self.descriptors.extend([Descriptor(self, iface, x) for x in entry])
+
+ # Mark the descriptors for which only a single nativeType implements
+ # an interface.
+ for descriptor in self.descriptors:
+ intefaceName = descriptor.interface.identifier.name
+ otherDescriptors = [d for d in self.descriptors
+ if d.interface.identifier.name == intefaceName]
+ descriptor.uniqueImplementation = len(otherDescriptors) == 1
+
+ self.enums = [e for e in parseData if e.isEnum()]
+ self.dictionaries = [d for d in parseData if d.isDictionary()]
+ self.callbacks = [c for c in parseData if
+ 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 getInterface(self, ifname):
+ return self.interfaces[ifname]
+ def getDescriptors(self, **filters):
+ """Gets the descriptors that match the given filters."""
+ curr = self.descriptors
+ for key, val in filters.iteritems():
+ if key == 'webIDLFile':
+ getter = lambda x: x.interface.filename()
+ elif key == 'hasInterfaceObject':
+ getter = lambda x: x.interface.hasInterfaceObject()
+ elif key == 'isCallback':
+ getter = lambda x: x.interface.isCallback()
+ elif key == 'isJSImplemented':
+ getter = lambda x: x.interface.isJSImplemented()
+ else:
+ getter = lambda x: getattr(x, key)
+ curr = filter(lambda x: getter(x) == val, curr)
+ return curr
+ def getEnums(self, webIDLFile):
+ return filter(lambda e: e.filename() == webIDLFile, self.enums)
+
+ @staticmethod
+ def _filterForFile(items, webIDLFile=""):
+ """Gets the items that match the given filters."""
+ if not webIDLFile:
+ return items
+
+ return filter(lambda x: x.filename() == webIDLFile, items)
+
+ def getDictionaries(self, webIDLFile=""):
+ return self._filterForFile(self.dictionaries, webIDLFile=webIDLFile)
+ def getCallbacks(self, webIDLFile=""):
+ return self._filterForFile(self.callbacks, webIDLFile=webIDLFile)
+
+ def getDescriptor(self, interfaceName):
+ """
+ Gets the appropriate descriptor for the given interface name.
+ """
+ iface = self.getInterface(interfaceName)
+ descriptors = self.getDescriptors(interface=iface)
+
+ # We should have exactly one result.
+ if len(descriptors) is not 1:
+ raise NoSuchDescriptorError("For " + interfaceName + " found " +
+ str(len(matches)) + " matches");
+ return descriptors[0]
+ def getDescriptorProvider(self):
+ """
+ Gets a descriptor provider that can provide descriptors as needed.
+ """
+ return DescriptorProvider(self)
+
+class NoSuchDescriptorError(TypeError):
+ def __init__(self, str):
+ TypeError.__init__(self, str)
+
+class DescriptorProvider:
+ """
+ A way of getting descriptors for interface names
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def getDescriptor(self, interfaceName):
+ """
+ Gets the appropriate descriptor for the given interface name given the
+ context of the current descriptor.
+ """
+ return self.config.getDescriptor(interfaceName)
+
+class Descriptor(DescriptorProvider):
+ """
+ Represents a single descriptor for an interface. See Bindings.conf.
+ """
+ def __init__(self, config, interface, desc):
+ DescriptorProvider.__init__(self, config)
+ self.interface = interface
+
+ # Read the desc, and fill in the relevant defaults.
+ ifaceName = self.interface.identifier.name
+
+ # Callback types do not use JS smart pointers, so we should not use the
+ # built-in rooting mechanisms for them.
+ if self.interface.isCallback():
+ self.needsRooting = False
+ else:
+ self.needsRooting = True
+
+ self.returnType = desc.get('returnType', "Temporary<%s>" % ifaceName)
+ self.argumentType = "JSRef<%s>" % ifaceName
+ self.memberType = "Root<'a, 'b, %s>" % ifaceName
+ self.nativeType = desc.get('nativeType', 'JS<%s>' % ifaceName)
+ self.concreteType = desc.get('concreteType', ifaceName)
+ self.register = desc.get('register', True)
+ self.outerObjectHook = desc.get('outerObjectHook', 'None')
+
+ # If we're concrete, we need to crawl our ancestor interfaces and mark
+ # them as having a concrete descendant.
+ self.concrete = desc.get('concrete', True)
+ if self.concrete:
+ self.proxy = False
+ operations = {
+ 'IndexedGetter': None,
+ 'IndexedSetter': None,
+ 'IndexedCreator': None,
+ 'IndexedDeleter': None,
+ 'NamedGetter': None,
+ 'NamedSetter': None,
+ 'NamedCreator': None,
+ 'NamedDeleter': None,
+ 'Stringifier': None
+ }
+ iface = self.interface
+ while iface:
+ for m in iface.members:
+ if not m.isMethod():
+ continue
+
+ def addOperation(operation, m):
+ if not operations[operation]:
+ operations[operation] = m
+ def addIndexedOrNamedOperation(operation, m):
+ self.proxy = True
+ if m.isIndexed():
+ operation = 'Indexed' + operation
+ else:
+ assert m.isNamed()
+ operation = 'Named' + operation
+ addOperation(operation, m)
+
+ if m.isStringifier():
+ addOperation('Stringifier', m)
+ else:
+ if m.isGetter():
+ addIndexedOrNamedOperation('Getter', m)
+ if m.isSetter():
+ addIndexedOrNamedOperation('Setter', m)
+ if m.isCreator():
+ addIndexedOrNamedOperation('Creator', m)
+ if m.isDeleter():
+ addIndexedOrNamedOperation('Deleter', m)
+ raise TypeError("deleter specified on %s but we "
+ "don't support deleters yet" %
+ self.interface.identifier.name)
+
+ iface.setUserData('hasConcreteDescendant', True)
+ iface = iface.parent
+
+ if self.proxy:
+ self.operations = operations
+ iface = self.interface
+ while iface:
+ iface.setUserData('hasProxyDescendant', True)
+ iface = iface.parent
+
+ self.name = interface.identifier.name
+
+ # self.extendedAttributes is a dict of dicts, keyed on
+ # all/getterOnly/setterOnly and then on member name. Values are an
+ # array of extended attributes.
+ self.extendedAttributes = { 'all': {}, 'getterOnly': {}, 'setterOnly': {} }
+
+ def addExtendedAttribute(attribute, config):
+ def add(key, members, attribute):
+ for member in members:
+ self.extendedAttributes[key].setdefault(member, []).append(attribute)
+
+ if isinstance(config, dict):
+ for key in ['all', 'getterOnly', 'setterOnly']:
+ add(key, config.get(key, []), attribute)
+ elif isinstance(config, list):
+ add('all', config, attribute)
+ else:
+ assert isinstance(config, str)
+ if config == '*':
+ iface = self.interface
+ while iface:
+ add('all', map(lambda m: m.name, iface.members), attribute)
+ iface = iface.parent
+ else:
+ add('all', [config], attribute)
+
+ # Build the prototype chain.
+ self.prototypeChain = []
+ parent = interface
+ while parent:
+ self.prototypeChain.insert(0, parent.identifier.name)
+ parent = parent.parent
+ config.maxProtoChainLength = max(config.maxProtoChainLength,
+ len(self.prototypeChain))
+
+ def getExtendedAttributes(self, member, getter=False, setter=False):
+ def maybeAppendInfallibleToAttrs(attrs, throws):
+ if throws is None:
+ attrs.append("infallible")
+ elif throws is True:
+ pass
+ else:
+ raise TypeError("Unknown value for 'Throws'")
+
+ name = member.identifier.name
+ if member.isMethod():
+ attrs = self.extendedAttributes['all'].get(name, [])
+ throws = member.getExtendedAttribute("Throws")
+ maybeAppendInfallibleToAttrs(attrs, throws)
+ return attrs
+
+ assert member.isAttr()
+ assert bool(getter) != bool(setter)
+ key = 'getterOnly' if getter else 'setterOnly'
+ attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, [])
+ throws = member.getExtendedAttribute("Throws")
+ if throws is None:
+ throwsAttr = "GetterThrows" if getter else "SetterThrows"
+ throws = member.getExtendedAttribute(throwsAttr)
+ maybeAppendInfallibleToAttrs(attrs, throws)
+ return attrs
+
+ def isGlobal(self):
+ """
+ Returns true if this is the primary interface for a global object
+ of some sort.
+ """
+ return (self.interface.getExtendedAttribute("Global") or
+ self.interface.getExtendedAttribute("PrimaryGlobal"))
+
+
+# Some utility methods
+def getTypesFromDescriptor(descriptor):
+ """
+ Get all argument and return types for all members of the descriptor
+ """
+ members = [m for m in descriptor.interface.members]
+ if descriptor.interface.ctor():
+ members.append(descriptor.interface.ctor())
+ members.extend(descriptor.interface.namedConstructors)
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ types = []
+ for s in signatures:
+ assert len(s) == 2
+ (returnType, arguments) = s
+ types.append(returnType)
+ types.extend(a.type for a in arguments)
+
+ types.extend(a.type for a in members if a.isAttr())
+ return types
+
+def getFlatTypes(types):
+ retval = set()
+ for type in types:
+ type = type.unroll()
+ if type.isUnion():
+ retval |= set(type.flatMemberTypes)
+ else:
+ retval.add(type)
+ return retval
+
+def getTypesFromDictionary(dictionary):
+ """
+ Get all member types for this dictionary
+ """
+ types = []
+ curDict = dictionary
+ while curDict:
+ types.extend([m.type for m in curDict.members])
+ curDict = curDict.parent
+ return types
+
+def getTypesFromCallback(callback):
+ """
+ Get the types this callback depends on: its return type and the
+ types of its arguments.
+ """
+ sig = callback.signatures()[0]
+ types = [sig[0]] # Return type
+ types.extend(arg.type for arg in sig[1]) # Arguments
+ return types
diff --git a/components/script/dom/bindings/codegen/DOMJSClass.h b/components/script/dom/bindings/codegen/DOMJSClass.h
new file mode 100644
index 00000000000..151960b5901
--- /dev/null
+++ b/components/script/dom/bindings/codegen/DOMJSClass.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* 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/. */
+
+#ifndef mozilla_dom_DOMJSClass_h
+#define mozilla_dom_DOMJSClass_h
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+#include "mozilla/dom/PrototypeList.h" // auto-generated
+
+// We use slot 0 for holding the raw object. This is safe for both
+// globals and non-globals.
+#define DOM_OBJECT_SLOT 0
+
+// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT. We have to
+// start at 1 past JSCLASS_GLOBAL_SLOT_COUNT because XPConnect uses
+// that one.
+#define DOM_PROTOTYPE_SLOT (JSCLASS_GLOBAL_SLOT_COUNT + 1)
+
+// We use these flag bits for the new bindings.
+#define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT1
+
+// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
+// LSetDOMProperty. Those constants need to be changed accordingly if this value
+// changes.
+#define DOM_PROTO_INSTANCE_CLASS_SLOT 0
+
+namespace mozilla {
+namespace dom {
+
+typedef bool
+(* ResolveProperty)(JSContext* cx, JSObject* wrapper, jsid id, bool set,
+ JSPropertyDescriptor* desc);
+typedef bool
+(* EnumerateProperties)(JSContext* cx, JSObject* wrapper,
+ JS::AutoIdVector& props);
+
+struct NativePropertyHooks
+{
+ ResolveProperty mResolveOwnProperty;
+ ResolveProperty mResolveProperty;
+ EnumerateProperties mEnumerateOwnProperties;
+ EnumerateProperties mEnumerateProperties;
+
+ const NativePropertyHooks *mProtoHooks;
+};
+
+struct DOMClass
+{
+ // A list of interfaces that this object implements, in order of decreasing
+ // derivedness.
+ const prototypes::ID mInterfaceChain[prototypes::id::_ID_Count];
+
+ // We store the DOM object in reserved slot with index DOM_OBJECT_SLOT or in
+ // the proxy private if we use a proxy object.
+ // Sometimes it's an nsISupports and sometimes it's not; this class tells
+ // us which it is.
+ const bool mDOMObjectIsISupports;
+
+ const NativePropertyHooks* mNativeHooks;
+};
+
+// Special JSClass for reflected DOM objects.
+struct DOMJSClass
+{
+ // It would be nice to just inherit from JSClass, but that precludes pure
+ // compile-time initialization of the form |DOMJSClass = {...};|, since C++
+ // only allows brace initialization for aggregate/POD types.
+ JSClass mBase;
+
+ DOMClass mClass;
+
+ static DOMJSClass* FromJSClass(JSClass* base) {
+ MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
+ return reinterpret_cast<DOMJSClass*>(base);
+ }
+ static const DOMJSClass* FromJSClass(const JSClass* base) {
+ MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
+ return reinterpret_cast<const DOMJSClass*>(base);
+ }
+
+ static DOMJSClass* FromJSClass(js::Class* base) {
+ return FromJSClass(Jsvalify(base));
+ }
+ static const DOMJSClass* FromJSClass(const js::Class* base) {
+ return FromJSClass(Jsvalify(base));
+ }
+
+ JSClass* ToJSClass() { return &mBase; }
+};
+
+inline bool
+HasProtoOrIfaceArray(JSObject* global)
+{
+ MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
+ // This can be undefined if we GC while creating the global
+ return !js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).isUndefined();
+}
+
+inline JSObject**
+GetProtoOrIfaceArray(JSObject* global)
+{
+ MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
+ return static_cast<JSObject**>(
+ js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).toPrivate());
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_DOMJSClass_h */
diff --git a/components/script/dom/bindings/codegen/DOMJSProxyHandler.cpp b/components/script/dom/bindings/codegen/DOMJSProxyHandler.cpp
new file mode 100644
index 00000000000..af45cc6ed1a
--- /dev/null
+++ b/components/script/dom/bindings/codegen/DOMJSProxyHandler.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=99 ft=cpp: */
+/* 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/. */
+
+#include "mozilla/Util.h"
+
+#include "DOMJSProxyHandler.h"
+#include "xpcpublic.h"
+#include "xpcprivate.h"
+#include "XPCQuickStubs.h"
+#include "XPCWrapper.h"
+#include "WrapperFactory.h"
+#include "nsDOMClassInfo.h"
+#include "nsGlobalWindow.h"
+#include "nsWrapperCacheInlines.h"
+#include "mozilla/dom/BindingUtils.h"
+
+#include "jsapi.h"
+
+using namespace JS;
+
+namespace mozilla {
+namespace dom {
+
+jsid s_length_id = JSID_VOID;
+
+bool
+DefineStaticJSVals(JSContext* cx)
+{
+ JSAutoRequest ar(cx);
+
+ return InternJSString(cx, s_length_id, "length");
+}
+
+
+int HandlerFamily;
+
+// Store the information for the specialized ICs.
+struct SetListBaseInformation
+{
+ SetListBaseInformation() {
+ js::SetListBaseInformation((void*) &HandlerFamily, js::JSSLOT_PROXY_EXTRA + JSPROXYSLOT_EXPANDO);
+ }
+};
+
+SetListBaseInformation gSetListBaseInformation;
+
+
+bool
+DefineConstructor(JSContext* cx, JSObject* obj, DefineInterface aDefine, nsresult* aResult)
+{
+ bool enabled;
+ bool defined = aDefine(cx, obj, &enabled);
+ MOZ_ASSERT(!defined || enabled,
+ "We defined a constructor but the new bindings are disabled?");
+ *aResult = defined ? NS_OK : NS_ERROR_FAILURE;
+ return enabled;
+}
+
+// static
+JSObject*
+DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JSObject* obj)
+{
+ NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
+ JSObject* expando = GetExpandoObject(obj);
+ if (!expando) {
+ expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr,
+ js::GetObjectParent(obj));
+ if (!expando) {
+ return NULL;
+ }
+
+ xpc::CompartmentPrivate* priv = xpc::GetCompartmentPrivate(obj);
+ if (!priv->RegisterDOMExpandoObject(obj)) {
+ return NULL;
+ }
+
+ nsWrapperCache* cache;
+ CallQueryInterface(UnwrapDOMObject<nsISupports>(obj, eProxyDOMObject), &cache);
+ cache->SetPreservingWrapper(true);
+
+ js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
+ }
+ return expando;
+}
+
+bool
+DOMProxyHandler::getPropertyDescriptor(JSContext* cx, JSObject* proxy, jsid id, bool set,
+ JSPropertyDescriptor* desc)
+{
+ if (!getOwnPropertyDescriptor(cx, proxy, id, set, desc)) {
+ return false;
+ }
+ if (desc->obj) {
+ return true;
+ }
+
+ JSObject* proto;
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ if (!proto) {
+ desc->obj = NULL;
+ return true;
+ }
+
+ return JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, desc);
+}
+
+bool
+DOMProxyHandler::defineProperty(JSContext* cx, JSObject* proxy, jsid id,
+ JSPropertyDescriptor* desc)
+{
+ if ((desc->attrs & JSPROP_GETTER) && desc->setter == JS_StrictPropertyStub) {
+ return JS_ReportErrorFlagsAndNumber(cx,
+ JSREPORT_WARNING | JSREPORT_STRICT |
+ JSREPORT_STRICT_MODE_ERROR,
+ js_GetErrorMessage, NULL,
+ JSMSG_GETTER_ONLY);
+ }
+
+ if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
+ return true;
+ }
+
+ JSObject* expando = EnsureExpandoObject(cx, proxy);
+ if (!expando) {
+ return false;
+ }
+
+ return JS_DefinePropertyById(cx, expando, id, desc->value, desc->getter, desc->setter,
+ desc->attrs);
+}
+
+bool
+DOMProxyHandler::delete_(JSContext* cx, JSObject* proxy, jsid id, bool* bp)
+{
+ JSBool b = true;
+
+ JSObject* expando;
+ if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+ Value v;
+ if (!JS_DeletePropertyById2(cx, expando, id, &v) || !JS_ValueToBoolean(cx, v, &b)) {
+ return false;
+ }
+ }
+
+ *bp = !!b;
+ return true;
+}
+
+bool
+DOMProxyHandler::enumerate(JSContext* cx, JSObject* proxy, AutoIdVector& props)
+{
+ JSObject* proto;
+ if (!JS_GetPrototype(cx, proxy, &proto)) {
+ return false;
+ }
+ return getOwnPropertyNames(cx, proxy, props) &&
+ (!proto || js::GetPropertyNames(cx, proto, 0, &props));
+}
+
+bool
+DOMProxyHandler::fix(JSContext* cx, JSObject* proxy, Value* vp)
+{
+ vp->setUndefined();
+ return true;
+}
+
+bool
+DOMProxyHandler::has(JSContext* cx, JSObject* proxy, jsid id, bool* bp)
+{
+ if (!hasOwn(cx, proxy, id, bp)) {
+ return false;
+ }
+
+ if (*bp) {
+ // We have the property ourselves; no need to worry about our prototype
+ // chain.
+ return true;
+ }
+
+ // OK, now we have to look at the proto
+ JSObject *proto;
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ if (!proto) {
+ return true;
+ }
+ JSBool protoHasProp;
+ bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
+ if (ok) {
+ *bp = protoHasProp;
+ }
+ return ok;
+}
+
+// static
+JSString*
+DOMProxyHandler::obj_toString(JSContext* cx, const char* className)
+{
+ size_t nchars = sizeof("[object ]") - 1 + strlen(className);
+ jschar* chars = static_cast<jschar*>(JS_malloc(cx, (nchars + 1) * sizeof(jschar)));
+ if (!chars) {
+ return NULL;
+ }
+
+ const char* prefix = "[object ";
+ nchars = 0;
+ while ((chars[nchars] = (jschar)*prefix) != 0) {
+ nchars++, prefix++;
+ }
+ while ((chars[nchars] = (jschar)*className) != 0) {
+ nchars++, className++;
+ }
+ chars[nchars++] = ']';
+ chars[nchars] = 0;
+
+ JSString* str = JS_NewUCString(cx, chars, nchars);
+ if (!str) {
+ JS_free(cx, chars);
+ }
+ return str;
+}
+
+int32_t
+IdToInt32(JSContext* cx, jsid id)
+{
+ JSAutoRequest ar(cx);
+
+ jsval idval;
+ double array_index;
+ int32_t i;
+ if (!::JS_IdToValue(cx, id, &idval) ||
+ !::JS_ValueToNumber(cx, idval, &array_index) ||
+ !::JS_DoubleIsInt32(array_index, &i)) {
+ return -1;
+ }
+
+ return i;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/components/script/dom/bindings/codegen/DOMJSProxyHandler.h b/components/script/dom/bindings/codegen/DOMJSProxyHandler.h
new file mode 100644
index 00000000000..394e2dc4d2f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/DOMJSProxyHandler.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* 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/. */
+
+#ifndef mozilla_dom_DOMJSProxyHandler_h
+#define mozilla_dom_DOMJSProxyHandler_h
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "jsproxy.h"
+#include "xpcpublic.h"
+#include "nsString.h"
+#include "mozilla/Likely.h"
+
+#define DOM_PROXY_OBJECT_SLOT js::JSSLOT_PROXY_PRIVATE
+
+namespace mozilla {
+namespace dom {
+
+enum {
+ JSPROXYSLOT_EXPANDO = 0
+};
+
+template<typename T> struct Prefable;
+
+class DOMProxyHandler : public DOMBaseProxyHandler
+{
+public:
+ DOMProxyHandler(const DOMClass& aClass)
+ : DOMBaseProxyHandler(true),
+ mClass(aClass)
+ {
+ }
+
+ bool getPropertyDescriptor(JSContext* cx, JSObject* proxy, jsid id, bool set,
+ JSPropertyDescriptor* desc);
+ bool defineProperty(JSContext* cx, JSObject* proxy, jsid id,
+ JSPropertyDescriptor* desc);
+ bool delete_(JSContext* cx, JSObject* proxy, jsid id, bool* bp);
+ bool enumerate(JSContext* cx, JSObject* proxy, JS::AutoIdVector& props);
+ bool fix(JSContext* cx, JSObject* proxy, JS::Value* vp);
+ bool has(JSContext* cx, JSObject* proxy, jsid id, bool* bp);
+ using js::BaseProxyHandler::obj_toString;
+
+ static JSObject* GetExpandoObject(JSObject* obj)
+ {
+ MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ return v.isUndefined() ? NULL : v.toObjectOrNull();
+ }
+ static JSObject* EnsureExpandoObject(JSContext* cx, JSObject* obj);
+
+ const DOMClass& mClass;
+
+protected:
+ static JSString* obj_toString(JSContext* cx, const char* className);
+};
+
+extern jsid s_length_id;
+
+int32_t IdToInt32(JSContext* cx, jsid id);
+
+inline int32_t
+GetArrayIndexFromId(JSContext* cx, jsid id)
+{
+ if (MOZ_LIKELY(JSID_IS_INT(id))) {
+ return JSID_TO_INT(id);
+ }
+ if (MOZ_LIKELY(id == s_length_id)) {
+ return -1;
+ }
+ if (MOZ_LIKELY(JSID_IS_ATOM(id))) {
+ JSAtom* atom = JSID_TO_ATOM(id);
+ jschar s = *js::GetAtomChars(atom);
+ if (MOZ_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z'))
+ return -1;
+
+ uint32_t i;
+ JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id));
+ return js::StringIsArrayIndex(str, &i) ? i : -1;
+ }
+ return IdToInt32(cx, id);
+}
+
+inline void
+FillPropertyDescriptor(JSPropertyDescriptor* desc, JSObject* obj, bool readonly)
+{
+ desc->obj = obj;
+ desc->attrs = (readonly ? JSPROP_READONLY : 0) | JSPROP_ENUMERATE;
+ desc->getter = NULL;
+ desc->setter = NULL;
+ desc->shortid = 0;
+}
+
+inline void
+FillPropertyDescriptor(JSPropertyDescriptor* desc, JSObject* obj, jsval v, bool readonly)
+{
+ desc->value = v;
+ FillPropertyDescriptor(desc, obj, readonly);
+}
+
+JSObject*
+EnsureExpandoObject(JSContext* cx, JSObject* obj);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_DOMProxyHandler_h */
diff --git a/components/script/dom/bindings/codegen/ErrorResult.h b/components/script/dom/bindings/codegen/ErrorResult.h
new file mode 100644
index 00000000000..bbd9404a865
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ErrorResult.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * A struct for tracking exceptions that need to be thrown to JS.
+ */
+
+#ifndef mozilla_ErrorResult_h
+#define mozilla_ErrorResult_h
+
+#include "nscore.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+
+class ErrorResult {
+public:
+ ErrorResult() {
+ mResult = NS_OK;
+ }
+
+ void Throw(nsresult rv) {
+ MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
+ mResult = rv;
+ }
+
+ // In the future, we can add overloads of Throw that take more
+ // interesting things, like strings or DOM exception types or
+ // something if desired.
+
+ // Backwards-compat to make conversion simpler. We don't call
+ // Throw() here because people can easily pass success codes to
+ // this.
+ void operator=(nsresult rv) {
+ mResult = rv;
+ }
+
+ bool Failed() const {
+ return NS_FAILED(mResult);
+ }
+
+ nsresult ErrorCode() const {
+ return mResult;
+ }
+
+private:
+ nsresult mResult;
+
+ // Not to be implemented, to make sure people always pass this by
+ // reference, not by value.
+ ErrorResult(const ErrorResult&) MOZ_DELETE;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_ErrorResult_h */
diff --git a/components/script/dom/bindings/codegen/Errors.msg b/components/script/dom/bindings/codegen/Errors.msg
new file mode 100644
index 00000000000..81d6624cec8
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Errors.msg
@@ -0,0 +1,30 @@
+/* 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/. */
+
+/*
+ * The format for each error message is:
+ *
+ * MSG_DEF(<SYMBOLIC_NAME>, <ARGUMENT_COUNT>, <FORMAT_STRING>)
+ *
+ * where
+ *
+ * <SYMBOLIC_NAME> is a legal C++ identifer that will be used in the source.
+ *
+ * <ARGUMENT_COUNT> is an integer literal specifying the total number of
+ * replaceable arguments in the following format string.
+ *
+ * <FORMAT_STRING> is a string literal, containing <ARGUMENT_COUNT> sequences
+ * {X} where X is an integer representing the argument number that will
+ * be replaced with a string value when the error is reported.
+ */
+
+MSG_DEF(MSG_INVALID_ENUM_VALUE, 2, "Value '{0}' is not a valid value for enumeration {1}.")
+MSG_DEF(MSG_MISSING_ARGUMENTS, 1, "Not enough arguments to {0}.")
+MSG_DEF(MSG_NOT_OBJECT, 0, "Value not an object.")
+MSG_DEF(MSG_DOES_NOT_IMPLEMENT_INTERFACE, 1, "Value does not implement interface {0}.")
+MSG_DEF(MSG_NOT_IN_UNION, 1, "Value could not be converted to any of: {0}.")
+MSG_DEF(MSG_ILLEGAL_CONSTRUCTOR, 0, "Illegal constructor.")
+MSG_DEF(MSG_NO_PROPERTY_SETTER, 1, "{0} doesn't have an indexed property setter.")
+MSG_DEF(MSG_ENFORCE_RANGE_NON_FINITE, 1, "Non-finite value is out of range for {0}.")
+MSG_DEF(MSG_ENFORCE_RANGE_OUT_OF_RANGE, 1, "Value is out of range for {0}.")
diff --git a/components/script/dom/bindings/codegen/GenerateCSS2PropertiesWebIDL.py b/components/script/dom/bindings/codegen/GenerateCSS2PropertiesWebIDL.py
new file mode 100644
index 00000000000..1bb50afaee7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/GenerateCSS2PropertiesWebIDL.py
@@ -0,0 +1,26 @@
+# 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 string
+
+propList = eval(sys.stdin.read())
+props = ""
+for [prop, pref] in propList:
+ extendedAttrs = ["Throws", "TreatNullAs=EmptyString"]
+ if pref is not "":
+ extendedAttrs.append("Pref=%s" % pref)
+ if not prop.startswith("Moz"):
+ prop = prop[0].lower() + prop[1:]
+ # Unfortunately, even some of the getters here are fallible
+ # (e.g. on nsComputedDOMStyle).
+ props += " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
+ prop)
+
+idlFile = open(sys.argv[1], "r");
+idlTemplate = idlFile.read();
+idlFile.close();
+
+print ("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n" +
+ string.Template(idlTemplate).substitute({ "props": props }))
diff --git a/components/script/dom/bindings/codegen/GlobalGen.py b/components/script/dom/bindings/codegen/GlobalGen.py
new file mode 100644
index 00000000000..cdca464e029
--- /dev/null
+++ b/components/script/dom/bindings/codegen/GlobalGen.py
@@ -0,0 +1,83 @@
+# 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
+sys.path.append("./parser/")
+sys.path.append("./ply/")
+import os
+import cStringIO
+import WebIDL
+import cPickle
+from Configuration import *
+from CodegenRust import GlobalGenRoots, replaceFileIfChanged
+# import Codegen in general, so we can set a variable on it
+import Codegen
+
+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] 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("--verbose-errors", action='store_true', default=False,
+ help="When an error happens, display the Python traceback.")
+ (options, args) = o.parse_args()
+
+ if len(args) < 2:
+ o.error(usageString)
+
+ configFile = args[0]
+ baseDir = args[1]
+ fileList = args[2:]
+
+ # Parse the WebIDL.
+ parser = WebIDL.Parser(options.cachedir)
+ for filename in fileList:
+ fullPath = os.path.normpath(os.path.join(baseDir, filename))
+ f = open(fullPath, 'rb')
+ lines = f.readlines()
+ f.close()
+ parser.parse(''.join(lines), fullPath)
+ parserResults = parser.finish()
+
+ # Write the parser results out to a pickle.
+ resultsFile = open('ParserResults.pkl', 'wb')
+ cPickle.dump(parserResults, resultsFile, -1)
+ resultsFile.close()
+
+ # Load the configuration.
+ config = Configuration(configFile, parserResults)
+
+ # Generate the prototype list.
+ generate_file(config, 'PrototypeList', 'PrototypeList.rs')
+
+ # Generate the common code.
+ generate_file(config, 'RegisterBindings', 'RegisterBindings.rs')
+
+ # Generate the type list.
+ generate_file(config, 'InterfaceTypes', 'InterfaceTypes.rs')
+
+ # Generate the type list.
+ generate_file(config, 'InheritTypes', 'InheritTypes.rs')
+
+ # Generate the module declarations.
+ generate_file(config, 'Bindings', 'Bindings/mod.rs')
+
+ generate_file(config, 'UnionTypes', 'UnionTypes.rs')
+
+if __name__ == '__main__':
+ main()
diff --git a/components/script/dom/bindings/codegen/Makefile.in b/components/script/dom/bindings/codegen/Makefile.in
new file mode 100644
index 00000000000..5fef1e77218
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Makefile.in
@@ -0,0 +1,165 @@
+# 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/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+FAIL_ON_WARNINGS := 1
+
+MODULE = dom
+LIBRARY_NAME = dombindings_s
+LIBXUL_LIBRARY = 1
+FORCE_STATIC_LIB = 1
+EXPORT_LIBRARY = 1
+
+include $(topsrcdir)/config/config.mk
+
+# Need this to find all our DOM source files.
+include $(topsrcdir)/dom/dom-config.mk
+
+include $(topsrcdir)/dom/webidl/WebIDL.mk
+
+binding_include_path := mozilla/dom
+all_webidl_files = $(webidl_files) $(generated_webidl_files)
+# Set exported_binding_headers before adding the test IDL to the mix
+exported_binding_headers := $(subst .webidl,Binding.h,$(all_webidl_files))
+# Set linked_binding_cpp_files before adding the test IDL to the mix
+linked_binding_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files))
+
+all_webidl_files += $(test_webidl_files)
+
+binding_header_files := $(subst .webidl,Binding.h,$(all_webidl_files))
+binding_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files))
+
+globalgen_targets := \
+ PrototypeList.h \
+ RegisterBindings.h \
+ RegisterBindings.cpp \
+ UnionTypes.h \
+ UnionConversions.h \
+ $(NULL)
+
+CPPSRCS = \
+ $(linked_binding_cpp_files) \
+ $(filter %.cpp, $(globalgen_targets)) \
+ BindingUtils.cpp \
+ DOMJSProxyHandler.cpp \
+ $(NULL)
+
+EXPORTS_NAMESPACES = $(binding_include_path) mozilla
+
+EXPORTS_mozilla = \
+ ErrorResult.h \
+ $(NULL)
+
+EXPORTS_$(binding_include_path) = \
+ BindingUtils.h \
+ DOMJSClass.h \
+ DOMJSProxyHandler.h \
+ Errors.msg \
+ Nullable.h \
+ PrimitiveConversions.h \
+ PrototypeList.h \
+ RegisterBindings.h \
+ TypedArray.h \
+ UnionConversions.h \
+ UnionTypes.h \
+ $(exported_binding_headers) \
+ $(NULL)
+
+LOCAL_INCLUDES += -I$(topsrcdir)/js/xpconnect/src \
+ -I$(topsrcdir)/js/xpconnect/wrappers \
+ -I$(topsrcdir)/content/canvas/src \
+ -I$(topsrcdir)/content/html/content/src
+
+include $(topsrcdir)/config/rules.mk
+
+# If you change bindinggen_dependencies here, change it in
+# dom/bindings/test/Makefile.in too.
+bindinggen_dependencies := \
+ BindingGen.py \
+ Bindings.conf \
+ Configuration.py \
+ Codegen.py \
+ parser/WebIDL.py \
+ ParserResults.pkl \
+ $(GLOBAL_DEPS) \
+ $(NULL)
+
+CSS2Properties.webidl: $(topsrcdir)/layout/style/nsCSSPropList.h \
+ $(topsrcdir)/layout/style/nsCSSPropAliasList.h \
+ $(webidl_base)/CSS2Properties.webidl.in \
+ $(webidl_base)/CSS2PropertiesProps.h \
+ $(srcdir)/GenerateCSS2PropertiesWebIDL.py \
+ $(GLOBAL_DEPS)
+ $(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style $(webidl_base)/CSS2PropertiesProps.h | \
+ $(PYTHON) \
+ $(srcdir)/GenerateCSS2PropertiesWebIDL.py $(webidl_base)/CSS2Properties.webidl.in > CSS2Properties.webidl
+
+$(webidl_files): %: $(webidl_base)/%
+ $(INSTALL) $(IFLAGS1) $(webidl_base)/$* .
+
+$(test_webidl_files): %: $(srcdir)/test/%
+ $(INSTALL) $(IFLAGS1) $(srcdir)/test/$* .
+
+$(binding_header_files): %Binding.h: $(bindinggen_dependencies) \
+ %.webidl \
+ $(NULL)
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) -I$(srcdir)/parser \
+ $(srcdir)/BindingGen.py header \
+ $(srcdir)/Bindings.conf $*Binding \
+ $*.webidl
+
+$(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \
+ %.webidl \
+ $(NULL)
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) -I$(srcdir)/parser \
+ $(srcdir)/BindingGen.py cpp \
+ $(srcdir)/Bindings.conf $*Binding \
+ $*.webidl
+
+$(globalgen_targets): ParserResults.pkl
+
+CACHE_DIR = _cache
+
+globalgen_dependencies := \
+ GlobalGen.py \
+ Bindings.conf \
+ Configuration.py \
+ Codegen.py \
+ parser/WebIDL.py \
+ $(CACHE_DIR)/.done \
+ $(GLOBAL_DEPS) \
+ $(NULL)
+
+$(CACHE_DIR)/.done:
+ $(MKDIR) -p $(CACHE_DIR)
+ @$(TOUCH) $@
+
+ParserResults.pkl: $(globalgen_dependencies) \
+ $(all_webidl_files)
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) -I$(srcdir)/parser \
+ $(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \
+ --cachedir=$(CACHE_DIR) \
+ $(all_webidl_files)
+
+GARBAGE += \
+ $(binding_header_files) \
+ $(binding_cpp_files) \
+ $(all_webidl_files) \
+ $(globalgen_targets) \
+ ParserResults.pkl \
+ webidlyacc.py \
+ parser.out \
+ $(NULL)
+
+# Make sure all binding header files are created during the export stage, so we
+# don't have issues with .cpp files being compiled before we've generated the
+# headers they depend on. This is really only needed for the test files, since
+# the non-test headers are all exported above anyway.
+export:: $(binding_header_files)
diff --git a/components/script/dom/bindings/codegen/Nullable.h b/components/script/dom/bindings/codegen/Nullable.h
new file mode 100644
index 00000000000..8b2cc08642b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Nullable.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_Nullable_h
+#define mozilla_dom_Nullable_h
+
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace dom {
+
+// Support for nullable types
+template <typename T>
+struct Nullable
+{
+private:
+ T mValue;
+ bool mIsNull;
+
+public:
+ Nullable()
+ : mIsNull(true)
+ {}
+
+ Nullable(T aValue)
+ : mValue(aValue)
+ , mIsNull(false)
+ {}
+
+ void SetValue(T aValue) {
+ mValue = aValue;
+ mIsNull = false;
+ }
+
+ // For cases when |T| is some type with nontrivial copy behavior, we may want
+ // to get a reference to our internal copy of T and work with it directly
+ // instead of relying on the copying version of SetValue().
+ T& SetValue() {
+ mIsNull = false;
+ return mValue;
+ }
+
+ void SetNull() {
+ mIsNull = true;
+ }
+
+ const T& Value() const {
+ MOZ_ASSERT(!mIsNull);
+ return mValue;
+ }
+
+ T& Value() {
+ MOZ_ASSERT(!mIsNull);
+ return mValue;
+ }
+
+ bool IsNull() const {
+ return mIsNull;
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_Nullable_h */
diff --git a/components/script/dom/bindings/codegen/PrimitiveConversions.h b/components/script/dom/bindings/codegen/PrimitiveConversions.h
new file mode 100644
index 00000000000..40c27425772
--- /dev/null
+++ b/components/script/dom/bindings/codegen/PrimitiveConversions.h
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Conversions from jsval to primitive values
+ */
+
+#ifndef mozilla_dom_PrimitiveConversions_h
+#define mozilla_dom_PrimitiveConversions_h
+
+#include <limits>
+#include <math.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/FloatingPoint.h"
+#include "xpcpublic.h"
+
+namespace mozilla {
+namespace dom {
+
+template<typename T>
+struct TypeName {
+};
+
+template<>
+struct TypeName<int8_t> {
+ static const char* value() {
+ return "byte";
+ }
+};
+template<>
+struct TypeName<uint8_t> {
+ static const char* value() {
+ return "octet";
+ }
+};
+template<>
+struct TypeName<int16_t> {
+ static const char* value() {
+ return "short";
+ }
+};
+template<>
+struct TypeName<uint16_t> {
+ static const char* value() {
+ return "unsigned short";
+ }
+};
+template<>
+struct TypeName<int32_t> {
+ static const char* value() {
+ return "long";
+ }
+};
+template<>
+struct TypeName<uint32_t> {
+ static const char* value() {
+ return "unsigned long";
+ }
+};
+template<>
+struct TypeName<int64_t> {
+ static const char* value() {
+ return "long long";
+ }
+};
+template<>
+struct TypeName<uint64_t> {
+ static const char* value() {
+ return "unsigned long long";
+ }
+};
+
+
+enum ConversionBehavior {
+ eDefault,
+ eEnforceRange,
+ eClamp
+};
+
+template<typename T, ConversionBehavior B>
+struct PrimitiveConversionTraits {
+};
+
+template<typename T>
+struct DisallowedConversion {
+ typedef int jstype;
+ typedef int intermediateType;
+
+private:
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ MOZ_NOT_REACHED("This should never be instantiated!");
+ return false;
+ }
+};
+
+struct PrimitiveConversionTraits_smallInt {
+ // The output of JS::ToInt32 is determined as follows:
+ // 1) The value is converted to a double
+ // 2) Anything that's not a finite double returns 0
+ // 3) The double is rounded towards zero to the nearest integer
+ // 4) The resulting integer is reduced mod 2^32. The output of this
+ // operation is an integer in the range [0, 2^32).
+ // 5) If the resulting number is >= 2^31, 2^32 is subtracted from it.
+ //
+ // The result of all this is a number in the range [-2^31, 2^31)
+ //
+ // WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types
+ // are defined in the same way, except that step 4 uses reduction mod
+ // 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5
+ // is only done for the signed types.
+ //
+ // C/C++ define integer conversion semantics to unsigned types as taking
+ // your input integer mod (1 + largest value representable in the
+ // unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32,
+ // converting to the unsigned int of the relevant width will correctly
+ // perform step 4; in particular, the 2^32 possibly subtracted in step 5
+ // will become 0.
+ //
+ // Once we have step 4 done, we're just going to assume 2s-complement
+ // representation and cast directly to the type we really want.
+ //
+ // So we can cast directly for all unsigned types and for int32_t; for
+ // the smaller-width signed types we need to cast through the
+ // corresponding unsigned type.
+ typedef int32_t jstype;
+ typedef int32_t intermediateType;
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ return JS::ToInt32(cx, v, retval);
+ }
+};
+template<>
+struct PrimitiveConversionTraits<int8_t, eDefault> : PrimitiveConversionTraits_smallInt {
+ typedef uint8_t intermediateType;
+};
+template<>
+struct PrimitiveConversionTraits<uint8_t, eDefault> : PrimitiveConversionTraits_smallInt {
+};
+template<>
+struct PrimitiveConversionTraits<int16_t, eDefault> : PrimitiveConversionTraits_smallInt {
+ typedef uint16_t intermediateType;
+};
+template<>
+struct PrimitiveConversionTraits<uint16_t, eDefault> : PrimitiveConversionTraits_smallInt {
+};
+template<>
+struct PrimitiveConversionTraits<int32_t, eDefault> : PrimitiveConversionTraits_smallInt {
+};
+template<>
+struct PrimitiveConversionTraits<uint32_t, eDefault> : PrimitiveConversionTraits_smallInt {
+};
+
+template<>
+struct PrimitiveConversionTraits<int64_t, eDefault> {
+ typedef int64_t jstype;
+ typedef int64_t intermediateType;
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ return JS::ToInt64(cx, v, retval);
+ }
+};
+
+template<>
+struct PrimitiveConversionTraits<uint64_t, eDefault> {
+ typedef uint64_t jstype;
+ typedef uint64_t intermediateType;
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ return JS::ToUint64(cx, v, retval);
+ }
+};
+
+template<typename T>
+struct PrimitiveConversionTraits_Limits {
+ static inline T min() {
+ return std::numeric_limits<T>::min();
+ }
+ static inline T max() {
+ return std::numeric_limits<T>::max();
+ }
+};
+
+template<>
+struct PrimitiveConversionTraits_Limits<int64_t> {
+ static inline int64_t min() {
+ return -(1LL << 53) + 1;
+ }
+ static inline int64_t max() {
+ return (1LL << 53) - 1;
+ }
+};
+
+template<>
+struct PrimitiveConversionTraits_Limits<uint64_t> {
+ static inline uint64_t min() {
+ return 0;
+ }
+ static inline uint64_t max() {
+ return (1LL << 53) - 1;
+ }
+};
+
+template<typename T, bool (*Enforce)(JSContext* cx, const double& d, T* retval)>
+struct PrimitiveConversionTraits_ToCheckedIntHelper {
+ typedef T jstype;
+ typedef T intermediateType;
+
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ double intermediate;
+ if (!JS::ToNumber(cx, v, &intermediate)) {
+ return false;
+ }
+
+ return Enforce(cx, intermediate, retval);
+ }
+};
+
+template<typename T>
+inline bool
+PrimitiveConversionTraits_EnforceRange(JSContext* cx, const double& d, T* retval)
+{
+ MOZ_STATIC_ASSERT(std::numeric_limits<T>::is_integer,
+ "This can only be applied to integers!");
+
+ if (!MOZ_DOUBLE_IS_FINITE(d)) {
+ return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_NON_FINITE, TypeName<T>::value());
+ }
+
+ bool neg = (d < 0);
+ double rounded = floor(neg ? -d : d);
+ rounded = neg ? -rounded : rounded;
+ if (rounded < PrimitiveConversionTraits_Limits<T>::min() ||
+ rounded > PrimitiveConversionTraits_Limits<T>::max()) {
+ return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_OUT_OF_RANGE, TypeName<T>::value());
+ }
+
+ *retval = static_cast<T>(rounded);
+ return true;
+}
+
+template<typename T>
+struct PrimitiveConversionTraits<T, eEnforceRange> :
+ public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_EnforceRange<T> > {
+};
+
+template<typename T>
+inline bool
+PrimitiveConversionTraits_Clamp(JSContext* cx, const double& d, T* retval)
+{
+ MOZ_STATIC_ASSERT(std::numeric_limits<T>::is_integer,
+ "This can only be applied to integers!");
+
+ if (MOZ_DOUBLE_IS_NaN(d)) {
+ *retval = 0;
+ return true;
+ }
+ if (d >= PrimitiveConversionTraits_Limits<T>::max()) {
+ *retval = PrimitiveConversionTraits_Limits<T>::max();
+ return true;
+ }
+ if (d <= PrimitiveConversionTraits_Limits<T>::min()) {
+ *retval = PrimitiveConversionTraits_Limits<T>::min();
+ return true;
+ }
+
+ MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(d));
+
+ // Banker's rounding (round ties towards even).
+ // We move away from 0 by 0.5f and then truncate. That gets us the right
+ // answer for any starting value except plus or minus N.5. With a starting
+ // value of that form, we now have plus or minus N+1. If N is odd, this is
+ // the correct result. If N is even, plus or minus N is the correct result.
+ double toTruncate = (d < 0) ? d - 0.5 : d + 0.5;
+
+ T truncated(toTruncate);
+
+ if (truncated == toTruncate) {
+ /*
+ * It was a tie (since moving away from 0 by 0.5 gave us the exact integer
+ * we want). Since we rounded away from 0, we either already have an even
+ * number or we have an odd number but the number we want is one closer to
+ * 0. So just unconditionally masking out the ones bit should do the trick
+ * to get us the value we want.
+ */
+ truncated &= ~1;
+ }
+
+ *retval = truncated;
+ return true;
+}
+
+template<typename T>
+struct PrimitiveConversionTraits<T, eClamp> :
+ public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_Clamp<T> > {
+};
+
+
+template<ConversionBehavior B>
+struct PrimitiveConversionTraits<bool, B> : public DisallowedConversion<bool> {};
+
+template<>
+struct PrimitiveConversionTraits<bool, eDefault> {
+ typedef JSBool jstype;
+ typedef bool intermediateType;
+ static inline bool converter(JSContext* /* unused */, JS::Value v, jstype* retval) {
+ *retval = JS::ToBoolean(v);
+ return true;
+ }
+};
+
+
+template<ConversionBehavior B>
+struct PrimitiveConversionTraits<float, B> : public DisallowedConversion<float> {};
+
+template<ConversionBehavior B>
+struct PrimitiveConversionTraits<double, B> : public DisallowedConversion<double> {};
+
+struct PrimitiveConversionTraits_float {
+ typedef double jstype;
+ typedef double intermediateType;
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ return JS::ToNumber(cx, v, retval);
+ }
+};
+
+template<>
+struct PrimitiveConversionTraits<float, eDefault> : PrimitiveConversionTraits_float {
+};
+template<>
+struct PrimitiveConversionTraits<double, eDefault> : PrimitiveConversionTraits_float {
+};
+
+
+template<typename T, ConversionBehavior B>
+bool ValueToPrimitive(JSContext* cx, JS::Value v, T* retval)
+{
+ typename PrimitiveConversionTraits<T, B>::jstype t;
+ if (!PrimitiveConversionTraits<T, B>::converter(cx, v, &t))
+ return false;
+
+ *retval =
+ static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>(t);
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_PrimitiveConversions_h */
diff --git a/components/script/dom/bindings/codegen/RegisterBindings.h b/components/script/dom/bindings/codegen/RegisterBindings.h
new file mode 100644
index 00000000000..7d83a747cc3
--- /dev/null
+++ b/components/script/dom/bindings/codegen/RegisterBindings.h
@@ -0,0 +1,14 @@
+#ifndef mozilla_dom_RegisterBindings_h__
+#define mozilla_dom_RegisterBindings_h__
+
+
+namespace mozilla {
+namespace dom {
+void
+Register(nsScriptNameSpaceManager* aNameSpaceManager);
+
+} // namespace dom
+} // namespace mozilla
+
+
+#endif // mozilla_dom_RegisterBindings_h__
diff --git a/components/script/dom/bindings/codegen/TypedArray.h b/components/script/dom/bindings/codegen/TypedArray.h
new file mode 100644
index 00000000000..2a6f17bcb96
--- /dev/null
+++ b/components/script/dom/bindings/codegen/TypedArray.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TypedArray_h
+#define mozilla_dom_TypedArray_h
+
+#include "jsfriendapi.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * Various typed array classes for argument conversion. We have a base class
+ * that has a way of initializing a TypedArray from an existing typed array, and
+ * a subclass of the base class that supports creation of a relevant typed array
+ * or array buffer object.
+ */
+template<typename T,
+ JSObject* UnboxArray(JSContext*, JSObject*, uint32_t*, T**)>
+struct TypedArray_base {
+ TypedArray_base(JSContext* cx, JSObject* obj)
+ {
+ mObj = UnboxArray(cx, obj, &mLength, &mData);
+ }
+
+private:
+ T* mData;
+ uint32_t mLength;
+ JSObject* mObj;
+
+public:
+ inline bool inited() const {
+ return !!mObj;
+ }
+
+ inline T *Data() const {
+ MOZ_ASSERT(inited());
+ return mData;
+ }
+
+ inline uint32_t Length() const {
+ MOZ_ASSERT(inited());
+ return mLength;
+ }
+
+ inline JSObject *Obj() const {
+ MOZ_ASSERT(inited());
+ return mObj;
+ }
+};
+
+
+template<typename T,
+ T* GetData(JSObject*, JSContext*),
+ JSObject* UnboxArray(JSContext*, JSObject*, uint32_t*, T**),
+ JSObject* CreateNew(JSContext*, uint32_t)>
+struct TypedArray : public TypedArray_base<T,UnboxArray> {
+ TypedArray(JSContext* cx, JSObject* obj) :
+ TypedArray_base<T,UnboxArray>(cx, obj)
+ {}
+
+ static inline JSObject*
+ Create(JSContext* cx, nsWrapperCache* creator, uint32_t length,
+ const T* data = NULL) {
+ JSObject* creatorWrapper;
+ Maybe<JSAutoCompartment> ac;
+ if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) {
+ ac.construct(cx, creatorWrapper);
+ }
+ JSObject* obj = CreateNew(cx, length);
+ if (!obj) {
+ return NULL;
+ }
+ if (data) {
+ T* buf = static_cast<T*>(GetData(obj, cx));
+ memcpy(buf, data, length*sizeof(T));
+ }
+ return obj;
+ }
+};
+
+typedef TypedArray<int8_t, JS_GetInt8ArrayData, JS_GetObjectAsInt8Array,
+ JS_NewInt8Array>
+ Int8Array;
+typedef TypedArray<uint8_t, JS_GetUint8ArrayData,
+ JS_GetObjectAsUint8Array, JS_NewUint8Array>
+ Uint8Array;
+typedef TypedArray<uint8_t, JS_GetUint8ClampedArrayData,
+ JS_GetObjectAsUint8ClampedArray, JS_NewUint8ClampedArray>
+ Uint8ClampedArray;
+typedef TypedArray<int16_t, JS_GetInt16ArrayData,
+ JS_GetObjectAsInt16Array, JS_NewInt16Array>
+ Int16Array;
+typedef TypedArray<uint16_t, JS_GetUint16ArrayData,
+ JS_GetObjectAsUint16Array, JS_NewUint16Array>
+ Uint16Array;
+typedef TypedArray<int32_t, JS_GetInt32ArrayData,
+ JS_GetObjectAsInt32Array, JS_NewInt32Array>
+ Int32Array;
+typedef TypedArray<uint32_t, JS_GetUint32ArrayData,
+ JS_GetObjectAsUint32Array, JS_NewUint32Array>
+ Uint32Array;
+typedef TypedArray<float, JS_GetFloat32ArrayData,
+ JS_GetObjectAsFloat32Array, JS_NewFloat32Array>
+ Float32Array;
+typedef TypedArray<double, JS_GetFloat64ArrayData,
+ JS_GetObjectAsFloat64Array, JS_NewFloat64Array>
+ Float64Array;
+typedef TypedArray_base<uint8_t, JS_GetObjectAsArrayBufferView>
+ ArrayBufferView;
+typedef TypedArray<uint8_t, JS_GetArrayBufferData,
+ JS_GetObjectAsArrayBuffer, JS_NewArrayBuffer>
+ ArrayBuffer;
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_TypedArray_h */
diff --git a/components/script/dom/bindings/codegen/crashtests/769464.html b/components/script/dom/bindings/codegen/crashtests/769464.html
new file mode 100644
index 00000000000..84d6dbc08b4
--- /dev/null
+++ b/components/script/dom/bindings/codegen/crashtests/769464.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+
+function boom()
+{
+ window.getComputedStyle(new Worker("404.js"));
+}
+
+window.addEventListener("load", boom, false);
+
+</script>
diff --git a/components/script/dom/bindings/codegen/crashtests/crashtests.list b/components/script/dom/bindings/codegen/crashtests/crashtests.list
new file mode 100644
index 00000000000..cb954bd91fc
--- /dev/null
+++ b/components/script/dom/bindings/codegen/crashtests/crashtests.list
@@ -0,0 +1 @@
+asserts-if(cocoaWidget,0-1) load 769464.html
diff --git a/components/script/dom/bindings/codegen/parser/README b/components/script/dom/bindings/codegen/parser/README
new file mode 100644
index 00000000000..94b64b88459
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/README
@@ -0,0 +1 @@
+A WebIDL parser written in Python to be used in Mozilla. \ No newline at end of file
diff --git a/components/script/dom/bindings/codegen/parser/UPSTREAM b/components/script/dom/bindings/codegen/parser/UPSTREAM
new file mode 100644
index 00000000000..7ac5899379e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/UPSTREAM
@@ -0,0 +1 @@
+http://dev.w3.org/cvsweb/~checkout~/2006/webapi/WebIDL/Overview.html?rev=1.409;content-type=text%2Fhtml%3b+charset=utf-8 \ No newline at end of file
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
new file mode 100644
index 00000000000..32f80e82c56
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -0,0 +1,5583 @@
+# 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/.
+
+""" A WebIDL parser. """
+
+from ply import lex, yacc
+import re
+import os
+import traceback
+import math
+from collections import defaultdict
+
+# Machinery
+
+def parseInt(literal):
+ string = literal
+ sign = 0
+ base = 0
+
+ if string[0] == '-':
+ sign = -1
+ string = string[1:]
+ else:
+ sign = 1
+
+ if string[0] == '0' and len(string) > 1:
+ if string[1] == 'x' or string[1] == 'X':
+ base = 16
+ string = string[2:]
+ else:
+ base = 8
+ string = string[1:]
+ else:
+ base = 10
+
+ value = int(string, base)
+ 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)
+ def __setattr__(self, name, value): # this makes it read-only
+ raise NotImplementedError
+ return Foo()
+
+class WebIDLError(Exception):
+ def __init__(self, message, locations, warning=False):
+ self.message = message
+ self.locations = [str(loc) for loc in locations]
+ self.warning = warning
+
+ def __str__(self):
+ return "%s: %s%s%s" % (self.warning and 'warning' or 'error',
+ self.message,
+ ", " if len(self.locations) != 0 else "",
+ "\n".join(self.locations))
+
+class Location(object):
+ def __init__(self, lexer, lineno, lexpos, filename):
+ self._line = None
+ self._lineno = lineno
+ self._lexpos = lexpos
+ self._lexdata = lexer.lexdata
+ self._file = filename if filename else "<unknown>"
+
+ def __eq__(self, other):
+ return self._lexpos == other._lexpos and \
+ self._file == other._file
+
+ def filename(self):
+ return self._file
+
+ def resolve(self):
+ if self._line:
+ return
+
+ startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
+ endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
+ if endofline != -1:
+ self._line = self._lexdata[startofline:endofline]
+ else:
+ self._line = self._lexdata[startofline:]
+ self._colno = self._lexpos - startofline
+
+ # Our line number seems to point to the start of self._lexdata
+ self._lineno += self._lexdata.count('\n', 0, startofline)
+
+ def get(self):
+ self.resolve()
+ return "%s line %s:%s" % (self._file, self._lineno, self._colno)
+
+ def _pointerline(self):
+ return " " * self._colno + "^"
+
+ def __str__(self):
+ self.resolve()
+ return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
+ self._line, self._pointerline())
+
+class BuiltinLocation(object):
+ def __init__(self, text):
+ self.msg = text + "\n"
+
+ def __eq__(self, other):
+ return isinstance(other, BuiltinLocation) and \
+ self.msg == other.msg
+
+ def filename(self):
+ return '<builtin>'
+
+ def resolve(self):
+ pass
+
+ def get(self):
+ return self.msg
+
+ def __str__(self):
+ return self.get()
+
+
+# Data Model
+
+class IDLObject(object):
+ def __init__(self, location):
+ self.location = location
+ self.userData = dict()
+
+ def filename(self):
+ return self.location.filename()
+
+ def isInterface(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def isCallback(self):
+ return False
+
+ def isType(self):
+ return False
+
+ def isDictionary(self):
+ return False;
+
+ def isUnion(self):
+ return False
+
+ def getUserData(self, key, default):
+ return self.userData.get(key, default)
+
+ def setUserData(self, key, value):
+ self.userData[key] = value
+
+ def addExtendedAttributes(self, attrs):
+ assert False # Override me!
+
+ def handleExtendedAttribute(self, attr):
+ assert False # Override me!
+
+ def _getDependentObjects(self):
+ assert False # Override me!
+
+ def getDeps(self, visited=None):
+ """ Return a set of files that this object depends on. If any of
+ these files are changed the parser needs to be rerun to regenerate
+ a new IDLObject.
+
+ The visited argument is a set of all the objects already visited.
+ We must test to see if we are in it, and if so, do nothing. This
+ prevents infinite recursion."""
+
+ # NB: We can't use visited=set() above because the default value is
+ # evaluated when the def statement is evaluated, not when the function
+ # is executed, so there would be one set for all invocations.
+ if visited == None:
+ visited = set()
+
+ if self in visited:
+ return set()
+
+ visited.add(self)
+
+ deps = set()
+ if self.filename() != "<builtin>":
+ deps.add(self.filename())
+
+ for d in self._getDependentObjects():
+ deps = deps.union(d.getDeps(visited))
+
+ return deps
+
+class IDLScope(IDLObject):
+ def __init__(self, location, parentScope, identifier):
+ IDLObject.__init__(self, location)
+
+ self.parentScope = parentScope
+ if identifier:
+ assert isinstance(identifier, IDLIdentifier)
+ self._name = identifier
+ else:
+ self._name = None
+
+ self._dict = {}
+ self.globalNames = set()
+ # 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() + "::"
+ return "::"
+
+ def ensureUnique(self, identifier, object):
+ """
+ Ensure that there is at most one 'identifier' in scope ('self').
+ Note that object can be None. This occurs if we end up here for an
+ interface type we haven't seen yet.
+ """
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ assert not object or isinstance(object, IDLObjectWithIdentifier)
+ assert not object or object.identifier == identifier
+
+ if identifier.name in self._dict:
+ if not object:
+ return
+
+ # ensureUnique twice with the same object is not allowed
+ assert id(object) != id(self._dict[identifier.name])
+
+ replacement = self.resolveIdentifierConflict(self, identifier,
+ self._dict[identifier.name],
+ object)
+ self._dict[identifier.name] = replacement
+ return
+
+ assert object
+
+ self._dict[identifier.name] = object
+
+ def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
+ if isinstance(originalObject, IDLExternalInterface) and \
+ isinstance(newObject, IDLExternalInterface) and \
+ originalObject.identifier.name == newObject.identifier.name:
+ return originalObject
+
+ if (isinstance(originalObject, IDLExternalInterface) or
+ isinstance(newObject, IDLExternalInterface)):
+ raise WebIDLError(
+ "Name collision between "
+ "interface declarations for identifier '%s' at '%s' and '%s'"
+ % (identifier.name,
+ originalObject.location, newObject.location), [])
+
+ if (isinstance(originalObject, IDLDictionary) or
+ isinstance(newObject, IDLDictionary)):
+ raise WebIDLError(
+ "Name collision between dictionary declarations for "
+ "identifier '%s'.\n%s\n%s"
+ % (identifier.name,
+ originalObject.location, newObject.location), [])
+
+ # We do the merging of overloads here as opposed to in IDLInterface
+ # 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:
+ return originalObject.addOverload(newObject)
+
+ # Default to throwing, derived classes can override.
+ conflictdesc = "\n\t%s at %s\n\t%s at %s" % \
+ (originalObject, originalObject.location, newObject, newObject.location)
+
+ raise WebIDLError(
+ "Multiple unresolvable definitions of identifier '%s' in scope '%s%s"
+ % (identifier.name, str(self), conflictdesc), [])
+
+ def _lookupIdentifier(self, identifier):
+ return self._dict[identifier.name]
+
+ def lookupIdentifier(self, identifier):
+ assert isinstance(identifier, IDLIdentifier)
+ assert identifier.scope == self
+ return self._lookupIdentifier(identifier)
+
+class IDLIdentifier(IDLObject):
+ def __init__(self, location, scope, name):
+ IDLObject.__init__(self, location)
+
+ self.name = name
+ assert isinstance(scope, IDLScope)
+ self.scope = scope
+
+ def __str__(self):
+ return self.QName()
+
+ def QName(self):
+ return self.scope.QName() + self.name
+
+ def __hash__(self):
+ return self.QName().__hash__()
+
+ def __eq__(self, other):
+ return self.QName() == other.QName()
+
+ def object(self):
+ return self.scope.lookupIdentifier(self)
+
+class IDLUnresolvedIdentifier(IDLObject):
+ def __init__(self, location, name, allowDoubleUnderscore = False,
+ allowForbidden = False):
+ IDLObject.__init__(self, location)
+
+ assert len(name) > 0
+
+ if name[:2] == "__" and name != "__content" and name != "___noSuchMethod__" and not allowDoubleUnderscore:
+ raise WebIDLError("Identifiers beginning with __ are reserved",
+ [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),
+ [location])
+
+ self.name = name
+
+ def __str__(self):
+ return self.QName()
+
+ def QName(self):
+ return "<unresolved scope>::" + self.name
+
+ def resolve(self, scope, object):
+ assert isinstance(scope, IDLScope)
+ assert not object or isinstance(object, IDLObjectWithIdentifier)
+ assert not object or object.identifier == self
+
+ scope.ensureUnique(self, object)
+
+ identifier = IDLIdentifier(self.location, scope, self.name)
+ if object:
+ object.identifier = identifier
+ return identifier
+
+ def finish(self):
+ assert False # Should replace with a resolved identifier first.
+
+class IDLObjectWithIdentifier(IDLObject):
+ def __init__(self, location, parentScope, identifier):
+ IDLObject.__init__(self, location)
+
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+
+ self.identifier = identifier
+
+ 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):
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+ IDLScope.__init__(self, location, parentScope, self.identifier)
+
+class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
+ def __init__(self, location, identifier):
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+
+ def finish(self, scope):
+ try:
+ scope._lookupIdentifier(self.identifier)
+ except:
+ raise WebIDLError("Unresolved type '%s'." % self.identifier,
+ [self.location])
+
+ obj = self.identifier.resolve(scope, None)
+ return scope.lookupIdentifier(obj)
+
+class IDLExternalInterface(IDLObjectWithIdentifier):
+ def __init__(self, location, parentScope, identifier):
+ raise WebIDLError("Servo does not support external interfaces.",
+ [self.location])
+
+class IDLPartialInterface(IDLObject):
+ def __init__(self, location, name, members, nonPartialInterface):
+ assert isinstance(name, IDLUnresolvedIdentifier)
+
+ IDLObject.__init__(self, location)
+ self.identifier = name
+ self.members = members
+ # propagatedExtendedAttrs are the ones that should get
+ # propagated to our non-partial interface.
+ self.propagatedExtendedAttrs = []
+ self._nonPartialInterface = nonPartialInterface
+ self._finished = False
+ nonPartialInterface.addPartialInterface(self)
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ if identifier in ["Constructor", "NamedConstructor"]:
+ self.propagatedExtendedAttrs.append(attr)
+ elif identifier == "Exposed":
+ # This just gets propagated to all our members.
+ for member in self.members:
+ if len(member._exposureGlobalNames) != 0:
+ raise WebIDLError("[Exposed] specified on both a "
+ "partial interface member and on the "
+ "partial interface itself",
+ [member.location, attr.location])
+ member.addExtendedAttributes([attr])
+ else:
+ raise WebIDLError("Unknown extended attribute %s on partial "
+ "interface" % identifier,
+ [attr.location])
+
+ def finish(self, scope):
+ if self._finished:
+ return
+ self._finished = True
+ # Need to make sure our non-partial interface gets finished so it can
+ # report cases when we only have partial interfaces.
+ self._nonPartialInterface.finish(scope)
+
+ def validate(self):
+ pass
+
+
+def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet):
+ assert len(targetSet) == 0
+ if exposedAttr.hasValue():
+ targetSet.add(exposedAttr.value())
+ else:
+ assert exposedAttr.hasArgs()
+ targetSet.update(exposedAttr.args())
+
+def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
+ for name in nameSet:
+ exposureSet.update(globalScope.globalNameMapping[name])
+
+class IDLInterface(IDLObjectWithScope):
+ def __init__(self, location, parentScope, name, parent, members,
+ isKnownNonPartial):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+ assert isKnownNonPartial or not parent
+ assert isKnownNonPartial or len(members) == 0
+
+ self.parent = None
+ self._callback = False
+ self._finished = False
+ self.members = []
+ 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.interfacesBasedOnSelf is the set of interfaces that inherit from
+ # self or have self as a consequential interface, 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
+ # members and those of ancestor interfaces.
+ self.totalMembersInSlots = 0
+ # Tracking of the number of own own members we have in slots
+ self._ownMembersInSlots = 0
+ # _exposureGlobalNames are the global names listed in our [Exposed]
+ # extended attribute. exposureSet is the exposure set as defined in the
+ # Web IDL spec: it contains interface names.
+ self._exposureGlobalNames = set()
+ self.exposureSet = set()
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+
+ if isKnownNonPartial:
+ self.setNonPartial(location, parent, members)
+
+ def __str__(self):
+ return "Interface '%s'" % self.identifier.name
+
+ def ctor(self):
+ identifier = IDLUnresolvedIdentifier(self.location, "constructor",
+ allowForbidden=True)
+ try:
+ return self._lookupIdentifier(identifier)
+ except:
+ return 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])
+
+ # Verify that our [Exposed] value, if any, makes sense.
+ for globalName in self._exposureGlobalNames:
+ if globalName not in scope.globalNames:
+ raise WebIDLError("Unknown [Exposed] value %s" % globalName,
+ [self.location])
+
+ if len(self._exposureGlobalNames) == 0:
+ self._exposureGlobalNames.add(scope.primaryGlobalName)
+
+ globalNameSetToExposureSet(scope, self._exposureGlobalNames,
+ self.exposureSet)
+
+ # 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)
+
+ # 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):
+ raise WebIDLError("%s inherits from %s which does not have "
+ "a definition" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location])
+ assert not parent or isinstance(parent, IDLInterface)
+
+ self.parent = parent
+
+ assert iter(self.members)
+
+ if self.parent:
+ self.parent.finish(scope)
+
+ self.parent._hasChildInterfaces = True
+
+ 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")):
+ # Note: This is not a self.parent.isOnGlobalProtoChain() check
+ # because ancestors of a [Global] interface can have other
+ # descendants.
+ raise WebIDLError("[Global] interface has another interface "
+ "inheriting from it",
+ [self.location, self.parent.location])
+
+ # Make sure that we're not exposed in places where our parent is not
+ if not self.exposureSet.issubset(self.parent.exposureSet):
+ raise WebIDLError("Interface %s is exposed in globals where its "
+ "parent interface %s is not exposed." %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+
+ # Callbacks must not inherit from non-callbacks or inherit from
+ # anything that has consequential interfaces.
+ # 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 "
+ "non-callback interface %s" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+ elif self.parent.isCallback():
+ raise WebIDLError("Non-callback interface %s inheriting from "
+ "callback interface %s" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+
+ for iface in self.implementedInterfaces:
+ iface.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)
+
+ 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])
+
+ ctor = self.ctor()
+ if ctor is not None:
+ ctor.finish(scope)
+
+ for ctor in self.namedConstructors:
+ ctor.finish(scope)
+
+ # Make a copy of our member list, so things that implement us
+ # can get those without all the stuff we implement ourselves
+ # 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,
+ 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])
+ additionalMembers = iface.originalMembers;
+ for additionalMember in additionalMembers:
+ for member in self.members:
+ if additionalMember.identifier.name == member.identifier.name:
+ raise WebIDLError(
+ "Multiple definitions of %s on %s coming from 'implements' statements" %
+ (member.identifier.name, self),
+ [additionalMember.location, member.location])
+ self.members.extend(additionalMembers)
+ iface.interfacesImplementingSelf.add(self)
+
+ for ancestor in self.getInheritedInterfaces():
+ ancestor.interfacesBasedOnSelf.add(self)
+ 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
+ # do this before we set "originatingInterface" on our unforgeable
+ # members.
+ if self.getExtendedAttribute("Unforgeable"):
+ # Check that the interface already has all the things the
+ # spec would otherwise require us to synthesize and is
+ # missing the ones we plan to synthesize.
+ if not any(m.isMethod() and m.isStringifier() for m in self.members):
+ raise WebIDLError("Unforgeable interface %s does not have a "
+ "stringifier" % self.identifier.name,
+ [self.location])
+
+ for m in self.members:
+ if ((m.isMethod() and m.isJsonifier()) or
+ m.identifier.name == "toJSON"):
+ raise WebIDLError("Unforgeable interface %s has a "
+ "jsonifier so we won't be able to add "
+ "one ourselves" % self.identifier.name,
+ [self.location, m.location])
+
+ if m.identifier.name == "valueOf" and not m.isStatic():
+ raise WebIDLError("Unforgeable interface %s has a valueOf "
+ "member so we won't be able to add one "
+ "ourselves" % self.identifier.name,
+ [self.location, m.location])
+
+ for member in self.members:
+ if ((member.isAttr() or member.isMethod()) and
+ member.isUnforgeable() and
+ not hasattr(member, "originatingInterface")):
+ member.originatingInterface = self
+
+ # Compute slot indices for our members before we pull in
+ # unforgeable members from our parent.
+ for member in self.members:
+ if (member.isAttr() and
+ (member.getExtendedAttribute("StoreInSlot") or
+ member.getExtendedAttribute("Cached"))):
+ member.slotIndex = self.totalMembersInSlots
+ self.totalMembersInSlots += 1
+ if member.getExtendedAttribute("StoreInSlot"):
+ 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.
+ for unforgeableMember in (member for member in self.parent.members if
+ (member.isAttr() or member.isMethod()) and
+ member.isUnforgeable()):
+ shadows = [ m for m in self.members if
+ (m.isAttr() or m.isMethod()) and
+ not m.isStatic() and
+ m.identifier.name == unforgeableMember.identifier.name ]
+ if len(shadows) != 0:
+ locs = [unforgeableMember.location] + [ s.location for s
+ in shadows ]
+ raise WebIDLError("Interface %s shadows [Unforgeable] "
+ "members of %s" %
+ (self.identifier.name,
+ ancestor.identifier.name),
+ locs)
+ # And now just stick it in our members, since we won't be
+ # inheriting this down the proto chain. If we really cared we
+ # could try to do something where we set up the unforgeable
+ # attributes/methods of ancestor interfaces, with their
+ # corresponding getters, on our interface, but that gets pretty
+ # complicated and seems unnecessary.
+ self.members.append(unforgeableMember)
+
+ # Ensure that there's at most one of each {named,indexed}
+ # {getter,setter,creator,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.
+ specialMembersSeen = {}
+ for member in self.members:
+ if not member.isMethod():
+ continue
+
+ if member.isGetter():
+ 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 member.isNamed():
+ memberType = "named " + memberType
+ else:
+ assert member.isIndexed()
+ memberType = "indexed " + memberType
+
+ if memberType in specialMembersSeen:
+ raise WebIDLError("Multiple " + memberType + " on %s" % (self),
+ [self.location,
+ specialMembersSeen[memberType].location,
+ member.location])
+
+ specialMembersSeen[memberType] = member
+
+ if self._isOnGlobalProtoChain:
+ # Make sure we have no named setters, creators, or deleters
+ for memberType in ["setter", "creator", "deleter"]:
+ memberId = "named " + memberType + "s"
+ if memberId in specialMembersSeen:
+ raise WebIDLError("Interface with [Global] has a named %s" %
+ memberType,
+ [self.location,
+ specialMembersSeen[memberId].location])
+ # Make sure we're not [OverrideBuiltins]
+ if self.getExtendedAttribute("OverrideBuiltins"):
+ raise WebIDLError("Interface with [Global] also has "
+ "[OverrideBuiltins]",
+ [self.location])
+ # Mark all of our ancestors as being on the global's proto chain too
+ parent = self.parent
+ while parent:
+ # Must not inherit from an interface with [OverrideBuiltins]
+ if parent.getExtendedAttribute("OverrideBuiltins"):
+ raise WebIDLError("Interface with [Global] inherits from "
+ "interface with [OverrideBuiltins]",
+ [self.location, parent.location])
+ parent._isOnGlobalProtoChain = True
+ parent = parent.parent
+
+ def validate(self):
+ # We don't support consequential unforgeable interfaces. Need to check
+ # this here, becaue 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 }) ))
+
+ # We also don't support inheriting from unforgeable interfaces.
+ if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
+ raise WebIDLError("%s is an unforgeable ancestor interface" %
+ self.identifier.name,
+ [self.location] +
+ list(i.location for i in
+ self.interfacesBasedOnSelf if i.parent == self))
+
+
+ for member in self.members:
+ member.validate()
+
+ # Check that PutForwards refers to another attribute and that no
+ # cycles exist in forwarded assignments.
+ if member.isAttr():
+ iface = self
+ attr = member
+ putForwards = attr.getExtendedAttribute("PutForwards")
+ if putForwards and self.isCallback():
+ raise WebIDLError("[PutForwards] used on an attribute "
+ "on interface %s which is a callback "
+ "interface" % self.identifier.name,
+ [self.location, member.location])
+
+ while putForwards is not None:
+ forwardIface = attr.type.unroll().inner
+ fowardAttr = None
+
+ for forwardedMember in forwardIface.members:
+ if (not forwardedMember.isAttr() or
+ forwardedMember.identifier.name != putForwards[0]):
+ continue
+ if forwardedMember == member:
+ raise WebIDLError("Cycle detected in forwarded "
+ "assignments for attribute %s on "
+ "%s" %
+ (member.identifier.name, self),
+ [member.location])
+ fowardAttr = forwardedMember
+ break
+
+ if fowardAttr is None:
+ raise WebIDLError("Attribute %s on %s forwards to "
+ "missing attribute %s" %
+ (attr.identifier.name, iface, putForwards),
+ [attr.location])
+
+ iface = forwardIface
+ attr = fowardAttr
+ putForwards = attr.getExtendedAttribute("PutForwards")
+
+ if (self.getExtendedAttribute("Pref") and
+ self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
+ raise WebIDLError("[Pref] used on an member that is not %s-only" %
+ self.parentScope.primaryGlobalName,
+ [self.location])
+
+
+ def isInterface(self):
+ return True
+
+ def isExternal(self):
+ return False
+
+ def setIsConsequentialInterfaceOf(self, other):
+ self._consequential = True
+ self.interfacesBasedOnSelf.add(other)
+
+ def isConsequential(self):
+ return self._consequential
+
+ def setCallback(self, value):
+ self._callback = value
+
+ def isCallback(self):
+ return self._callback
+
+ def isSingleOperationInterface(self):
+ assert self.isCallback() or self.isJSImplemented()
+ return (
+ # JS-implemented things should never need the
+ # this-handling weirdness of single-operation interfaces.
+ 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
+ # operations have the same identifier
+ len(set(m.identifier.name for m in self.members if
+ m.isMethod() and not m.isStatic())) == 1)
+
+ def inheritanceDepth(self):
+ depth = 0
+ parent = self.parent
+ while parent:
+ depth = depth + 1
+ parent = parent.parent
+ return depth
+
+ def hasConstants(self):
+ return any(m.isConst() for m in self.members)
+
+ def hasInterfaceObject(self):
+ if self.isCallback():
+ return self.hasConstants()
+ return not hasattr(self, "_noInterfaceObject")
+
+ def hasInterfacePrototypeObject(self):
+ return not self.isCallback() and self.getUserData('hasConcreteDescendant', False)
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ # Special cased attrs
+ if identifier == "TreatNonCallableAsNull":
+ raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces",
+ [attr.location, self.location])
+ if identifier == "TreatNonObjectAsNull":
+ raise WebIDLError("TreatNonObjectAsNull cannot be specified on interfaces",
+ [attr.location, self.location])
+ elif identifier == "NoInterfaceObject":
+ if not attr.noArguments():
+ 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():
+ 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 []
+
+ 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.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 not newMethod 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])
+ elif (identifier == "ExceptionClass"):
+ if not attr.noArguments():
+ raise WebIDLError("[ExceptionClass] must take no arguments",
+ [attr.location])
+ if self.parent:
+ raise WebIDLError("[ExceptionClass] must not be specified on "
+ "an interface with inherited interfaces",
+ [attr.location, self.location])
+ elif identifier == "Global":
+ if attr.hasValue():
+ self.globalNames = [ attr.value() ]
+ elif attr.hasArgs():
+ 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._isOnGlobalProtoChain = True
+ elif identifier == "PrimaryGlobal":
+ if not attr.noArguments():
+ raise WebIDLError("[PrimaryGlobal] must take no arguments",
+ [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
+ elif (identifier == "NeedNewResolve" or
+ identifier == "OverrideBuiltins" or
+ identifier == "ChromeOnly" or
+ identifier == "Unforgeable" or
+ identifier == "LegacyEventInit"):
+ # Known extended attributes that do not take values
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr,
+ self._exposureGlobalNames)
+ elif (identifier == "Pref" or
+ identifier == "JSImplementation" or
+ identifier == "HeaderFile" or
+ identifier == "NavigatorProperty" or
+ identifier == "AvailableIn" or
+ identifier == "Func" or
+ identifier == "CheckPermissions"):
+ # 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 interface" % identifier,
+ [attr.location])
+
+ attrlist = attr.listValue()
+ self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+
+ def addImplementedInterface(self, implementedInterface):
+ assert(isinstance(implementedInterface, IDLInterface))
+ self.implementedInterfaces.add(implementedInterface)
+
+ def getInheritedInterfaces(self):
+ """
+ Returns a list of the interfaces this interface inherits from
+ (not including this interface itself). The list is in order
+ from most derived to least derived.
+ """
+ assert(self._finished)
+ if not self.parent:
+ return []
+ parentInterfaces = self.parent.getInheritedInterfaces()
+ 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.
+ """
+ if self.parent:
+ if self.parent == otherInterface:
+ return self
+ 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 interface",
+ [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
+ 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")
+ if not classId:
+ return classId
+ assert isinstance(classId, list)
+ assert len(classId) == 1
+ return classId[0]
+
+ def isJSImplemented(self):
+ return bool(self.getJSImplementation())
+
+ 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
+ return naviProp[0]
+
+ def hasChildInterfaces(self):
+ return self._hasChildInterfaces
+
+ def isOnGlobalProtoChain(self):
+ return self._isOnGlobalProtoChain
+
+ def _getDependentObjects(self):
+ deps = set(self.members)
+ deps.union(self.implementedInterfaces)
+ if self.parent:
+ deps.add(self.parent)
+ return deps
+
+ def hasMembersInSlots(self):
+ return self._ownMembersInSlots != 0
+
+class IDLDictionary(IDLObjectWithScope):
+ def __init__(self, location, parentScope, name, parent, members):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+ assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
+
+ self.parent = parent
+ self._finished = False
+ self.members = list(members)
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+
+ def __str__(self):
+ return "Dictionary '%s'" % self.identifier.name
+
+ def isDictionary(self):
+ return True;
+
+ def finish(self, scope):
+ if self._finished:
+ return
+
+ self._finished = True
+
+ if self.parent:
+ assert isinstance(self.parent, IDLIdentifierPlaceholder)
+ oldParent = self.parent
+ self.parent = self.parent.finish(scope)
+ if not isinstance(self.parent, IDLDictionary):
+ raise WebIDLError("Dictionary %s has parent that is not a dictionary" %
+ self.identifier.name,
+ [oldParent.location, self.parent.location])
+
+ # Make sure the parent resolves all its members before we start
+ # looking at them.
+ self.parent.finish(scope)
+
+ for member in self.members:
+ member.resolve(self)
+ if not member.isComplete():
+ member.complete(scope)
+ assert member.type.isComplete()
+
+ # Members of a dictionary are sorted in lexicographic order
+ self.members.sort(cmp=cmp, key=lambda x: x.identifier.name)
+
+ inheritedMembers = []
+ ancestor = self.parent
+ while ancestor:
+ if ancestor == self:
+ raise WebIDLError("Dictionary %s has itself as an ancestor" %
+ self.identifier.name,
+ [self.identifier.location])
+ inheritedMembers.extend(ancestor.members)
+ ancestor = ancestor.parent
+
+ # Catch name duplication
+ for inheritedMember in inheritedMembers:
+ for member in self.members:
+ if member.identifier.name == inheritedMember.identifier.name:
+ raise WebIDLError("Dictionary %s has two members with name %s" %
+ (self.identifier.name, member.identifier.name),
+ [member.location, inheritedMember.location])
+
+ def validate(self):
+ def typeContainsDictionary(memberType, dictionary):
+ """
+ Returns a tuple whose:
+
+ - First element is a Boolean value indicating whether
+ memberType contains dictionary.
+
+ - Second element is:
+ A list of locations that leads from the type that was passed in
+ the memberType argument, to the dictionary being validated,
+ if the boolean value in the first element is True.
+
+ None, if the boolean value in the first element is False.
+ """
+
+ if (memberType.nullable() or
+ memberType.isArray() or
+ memberType.isSequence() or
+ memberType.isMozMap()):
+ return typeContainsDictionary(memberType.inner, dictionary)
+
+ if memberType.isDictionary():
+ if memberType.inner == dictionary:
+ return (True, [memberType.location])
+
+ (contains, locations) = dictionaryContainsDictionary(memberType.inner, \
+ dictionary)
+ if contains:
+ return (True, [memberType.location] + locations)
+
+ if memberType.isUnion():
+ for member in memberType.flatMemberTypes:
+ (contains, locations) = typeContainsDictionary(member, dictionary)
+ if contains:
+ return (True, locations)
+
+ return (False, None)
+
+ def dictionaryContainsDictionary(dictMember, dictionary):
+ for member in dictMember.members:
+ (contains, locations) = typeContainsDictionary(member.type, dictionary)
+ if contains:
+ return (True, [member.location] + locations)
+
+ if dictMember.parent:
+ if dictMember.parent == dictionary:
+ return (True, [dictMember.location])
+ else:
+ (contains, locations) = dictionaryContainsDictionary(dictMember.parent, dictionary)
+ if contains:
+ return (True, [dictMember.location] + locations)
+
+ return (False, None)
+
+ for member in self.members:
+ if member.type.isDictionary() and member.type.nullable():
+ raise WebIDLError("Dictionary %s has member with nullable "
+ "dictionary type" % self.identifier.name,
+ [member.location])
+ (contains, locations) = typeContainsDictionary(member.type, self)
+ if contains:
+ raise WebIDLError("Dictionary %s has member with itself as type." %
+ self.identifier.name,
+ [member.location] + locations)
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def _getDependentObjects(self):
+ deps = set(self.members)
+ if (self.parent):
+ deps.add(self.parent)
+ return deps
+
+class IDLEnum(IDLObjectWithIdentifier):
+ def __init__(self, location, parentScope, name, values):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+
+ if len(values) != len(set(values)):
+ raise WebIDLError("Enum %s has multiple identical strings" % name.name,
+ [location])
+
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, name)
+ self._values = values
+
+ def values(self):
+ return self._values
+
+ def finish(self, scope):
+ pass
+
+ def validate(self):
+ pass
+
+ def isEnum(self):
+ return True
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLType(IDLObject):
+ Tags = enum(
+ # The integer types
+ 'int8',
+ 'uint8',
+ 'int16',
+ 'uint16',
+ 'int32',
+ 'uint32',
+ 'int64',
+ 'uint64',
+ # Additional primitive types
+ 'bool',
+ 'unrestricted_float',
+ 'float',
+ 'unrestricted_double',
+ # "double" last primitive type to match IDLBuiltinType
+ 'double',
+ # Other types
+ 'any',
+ 'domstring',
+ 'bytestring',
+ 'scalarvaluestring',
+ 'object',
+ 'date',
+ 'void',
+ # Funny stuff
+ 'interface',
+ 'dictionary',
+ 'enum',
+ 'callback',
+ 'union',
+ 'sequence',
+ 'mozmap',
+ 'array'
+ )
+
+ def __init__(self, location, name):
+ IDLObject.__init__(self, location)
+ self.name = name
+ self.builtin = False
+
+ def __eq__(self, other):
+ return other and self.builtin == other.builtin and self.name == other.name
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __str__(self):
+ return str(self.name)
+
+ def isType(self):
+ return True
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isBoolean(self):
+ return False
+
+ def isNumeric(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isScalarValueString(self):
+ return False
+
+ def isVoid(self):
+ return self.name == "Void"
+
+ def isSequence(self):
+ return False
+
+ def isMozMap(self):
+ return False
+
+ def isArray(self):
+ return False
+
+ def isArrayBuffer(self):
+ return False
+
+ def isArrayBufferView(self):
+ return False
+
+ def isTypedArray(self):
+ return False
+
+ def isCallbackInterface(self):
+ return False
+
+ def isNonCallbackInterface(self):
+ return False
+
+ def isGeckoInterface(self):
+ """ Returns a boolean indicating whether this type is an 'interface'
+ type that is implemented in Gecko. At the moment, this returns
+ true for all interface types that are not types from the TypedArray
+ spec."""
+ return self.isInterface() and not self.isSpiderMonkeyInterface()
+
+ 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.isTypedArray())
+
+ def isDictionary(self):
+ return False
+
+ def isInterface(self):
+ return False
+
+ 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
+
+ def isPromise(self):
+ return False
+
+ def isComplete(self):
+ return True
+
+ def includesRestrictedFloat(self):
+ return False
+
+ def isFloat(self):
+ return False
+
+ def isUnrestricted(self):
+ # Should only call this on float types
+ assert self.isFloat()
+
+ def isSerializable(self):
+ return False
+
+ def tag(self):
+ assert False # Override me!
+
+ def treatNonCallableAsNull(self):
+ assert self.tag() == IDLType.Tags.callback
+ return self.nullable() and self.inner._treatNonCallableAsNull
+
+ def treatNonObjectAsNull(self):
+ assert self.tag() == IDLType.Tags.callback
+ return self.nullable() and self.inner._treatNonObjectAsNull
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def resolveType(self, parentScope):
+ pass
+
+ def unroll(self):
+ return self
+
+ def isDistinguishableFrom(self, other):
+ raise TypeError("Can't tell whether a generic type is or is not "
+ "distinguishable from other things")
+
+ def isExposedInAllOf(self, exposureSet):
+ return True
+
+class IDLUnresolvedType(IDLType):
+ """
+ Unresolved types are interface types
+ """
+
+ def __init__(self, location, name, promiseInnerType=None):
+ IDLType.__init__(self, location, name)
+ self._promiseInnerType = promiseInnerType
+
+ def isComplete(self):
+ return False
+
+ def complete(self, scope):
+ obj = None
+ try:
+ obj = scope._lookupIdentifier(self.name)
+ except:
+ raise WebIDLError("Unresolved type '%s'." % self.name,
+ [self.location])
+
+ assert obj
+ if obj.isType():
+ # obj itself might not be complete; deal with that.
+ assert obj != self
+ if not obj.isComplete():
+ obj = obj.complete(scope)
+ return 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)
+
+ def isDistinguishableFrom(self, other):
+ raise TypeError("Can't tell whether an unresolved type is or is not "
+ "distinguishable from other things")
+
+class IDLNullableType(IDLType):
+ def __init__(self, location, innerType):
+ assert not innerType.isVoid()
+ assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
+
+ IDLType.__init__(self, location, innerType.name)
+ self.inner = innerType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLNullableType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "OrNull"
+
+ def nullable(self):
+ return True
+
+ def isCallback(self):
+ return self.inner.isCallback()
+
+ def isPrimitive(self):
+ return self.inner.isPrimitive()
+
+ def isBoolean(self):
+ return self.inner.isBoolean()
+
+ def isNumeric(self):
+ return self.inner.isNumeric()
+
+ def isString(self):
+ return self.inner.isString()
+
+ def isByteString(self):
+ return self.inner.isByteString()
+
+ def isDOMString(self):
+ return self.inner.isDOMString()
+
+ def isScalarValueString(self):
+ return self.inner.isScalarValueString()
+
+ def isFloat(self):
+ return self.inner.isFloat()
+
+ def isUnrestricted(self):
+ return self.inner.isUnrestricted()
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
+ def isInteger(self):
+ return self.inner.isInteger()
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return self.inner.isSequence()
+
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
+ def isArray(self):
+ return self.inner.isArray()
+
+ def isArrayBuffer(self):
+ return self.inner.isArrayBuffer()
+
+ def isArrayBufferView(self):
+ return self.inner.isArrayBufferView()
+
+ def isTypedArray(self):
+ return self.inner.isTypedArray()
+
+ def isDictionary(self):
+ return self.inner.isDictionary()
+
+ def isInterface(self):
+ return self.inner.isInterface()
+
+ def isCallbackInterface(self):
+ return self.inner.isCallbackInterface()
+
+ def isNonCallbackInterface(self):
+ return self.inner.isNonCallbackInterface()
+
+ def isEnum(self):
+ return self.inner.isEnum()
+
+ def isUnion(self):
+ return self.inner.isUnion()
+
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
+ def tag(self):
+ return self.inner.tag()
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ if self.inner.nullable():
+ raise WebIDLError("The inner type of a nullable type must not be "
+ "a nullable type",
+ [self.location, self.inner.location])
+ if self.inner.isUnion():
+ if self.inner.hasNullableType:
+ 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])
+
+ self.name = self.inner.name
+ return self
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ if (other.nullable() or (other.isUnion() and other.hasNullableType) or
+ other.isDictionary()):
+ # Can't tell which type null should become
+ return False
+ return self.inner.isDistinguishableFrom(other)
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLSequenceType(IDLType):
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ IDLType.__init__(self, location, parameterType.name)
+ self.inner = parameterType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLSequenceType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "Sequence"
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False;
+
+ def isString(self):
+ return False;
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isScalarValueString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return True
+
+ def isArray(self):
+ return False
+
+ def isDictionary(self):
+ return False
+
+ def isInterface(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
+ def tag(self):
+ return IDLType.Tags.sequence
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name
+ return self
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # 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.isMozMap())
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLMozMapType(IDLType):
+ # XXXbz This is pretty similar to IDLSequenceType in various ways.
+ # And maybe to IDLNullableType. Should we have a superclass for
+ # "type containing this other type"? Bug 1015318.
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ IDLType.__init__(self, location, parameterType.name)
+ self.inner = parameterType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLMozMapType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "MozMap"
+
+ def isMozMap(self):
+ return True
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
+ def tag(self):
+ return IDLType.Tags.mozmap
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name
+ 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):
+ if other.isUnion():
+ # 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())
+
+ def isExposedInAllOf(self, exposureSet):
+ return self.inner.unroll().isExposedInAllOf(exposureSet)
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLUnionType(IDLType):
+ def __init__(self, location, memberTypes):
+ IDLType.__init__(self, location, "")
+ self.memberTypes = memberTypes
+ self.hasNullableType = False
+ self.hasDictionaryType = False
+ self.flatMemberTypes = None
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes
+
+ def isVoid(self):
+ return False
+
+ def isUnion(self):
+ return True
+
+ def isSerializable(self):
+ return all(m.isSerializable() for m in self.memberTypes)
+
+ def includesRestrictedFloat(self):
+ return any(t.includesRestrictedFloat() for t in self.memberTypes)
+
+ def tag(self):
+ return IDLType.Tags.union
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ for t in self.memberTypes:
+ t.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.flatMemberTypes is not None
+
+ def complete(self, scope):
+ def typeName(type):
+ if isinstance(type, IDLNullableType):
+ return typeName(type.inner) + "OrNull"
+ if isinstance(type, IDLWrapperType):
+ return typeName(type._identifier.object())
+ if isinstance(type, IDLObjectWithIdentifier):
+ return typeName(type.identifier)
+ if (isinstance(type, IDLType) and
+ (type.isArray() or type.isSequence() or type.isMozMap)):
+ return str(type)
+ return type.name
+
+ 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)
+ self.flatMemberTypes = list(self.memberTypes)
+ i = 0
+ while i < len(self.flatMemberTypes):
+ if self.flatMemberTypes[i].nullable():
+ if self.hasNullableType:
+ raise WebIDLError("Can't have more than one nullable types in a union",
+ [nullableType.location, self.flatMemberTypes[i].location])
+ if self.hasDictionaryType:
+ raise WebIDLError("Can't have a nullable type and a "
+ "dictionary type in a union",
+ [dictionaryType.location,
+ self.flatMemberTypes[i].location])
+ self.hasNullableType = True
+ nullableType = self.flatMemberTypes[i]
+ self.flatMemberTypes[i] = self.flatMemberTypes[i].inner
+ continue
+ if self.flatMemberTypes[i].isDictionary():
+ if self.hasNullableType:
+ raise WebIDLError("Can't have a nullable type and a "
+ "dictionary type in a union",
+ [nullableType.location,
+ self.flatMemberTypes[i].location])
+ self.hasDictionaryType = True
+ dictionaryType = self.flatMemberTypes[i]
+ elif self.flatMemberTypes[i].isUnion():
+ self.flatMemberTypes[i:i + 1] = self.flatMemberTypes[i].memberTypes
+ continue
+ i += 1
+
+ for (i, t) in enumerate(self.flatMemberTypes[:-1]):
+ for u in self.flatMemberTypes[i + 1:]:
+ if not t.isDistinguishableFrom(u):
+ raise WebIDLError("Flat member types of a union should be "
+ "distinguishable, " + str(t) + " is not "
+ "distinguishable from " + str(u),
+ [self.location, t.location, u.location])
+
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if self.hasNullableType and other.nullable():
+ # Can't tell which type null should become
+ return False
+ if other.isUnion():
+ otherTypes = other.unroll().memberTypes
+ else:
+ otherTypes = [other]
+ # For every type in otherTypes, check that it's distinguishable from
+ # every type in our types
+ for u in otherTypes:
+ if any(not t.isDistinguishableFrom(u) for t in self.memberTypes):
+ return False
+ return True
+
+ def isExposedInAllOf(self, exposureSet):
+ # We could have different member types in different globals. Just make sure that each thing in exposureSet has one of our member types exposed in it.
+ for globalName in exposureSet:
+ if not any(t.unroll().isExposedInAllOf(set([globalName])) for t
+ in self.flatMemberTypes):
+ return False
+ return True
+
+ def _getDependentObjects(self):
+ return set(self.memberTypes)
+
+class IDLArrayType(IDLType):
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+ if parameterType.isSequence():
+ raise WebIDLError("Array type cannot parameterize over a sequence type",
+ [location])
+ if parameterType.isMozMap():
+ raise WebIDLError("Array type cannot parameterize over a MozMap type",
+ [location])
+ if parameterType.isDictionary():
+ raise WebIDLError("Array type cannot parameterize over a dictionary type",
+ [location])
+
+ IDLType.__init__(self, location, parameterType.name)
+ self.inner = parameterType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLArrayType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "Array"
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isScalarValueString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ assert not self.inner.isSequence()
+ return False
+
+ def isArray(self):
+ return True
+
+ def isDictionary(self):
+ assert not self.inner.isDictionary()
+ return False
+
+ def isInterface(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def tag(self):
+ return IDLType.Tags.array
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name
+
+ if self.inner.isDictionary():
+ raise WebIDLError("Array type must not contain "
+ "dictionary as element type.",
+ [self.inner.location])
+
+ assert not self.inner.isSequence()
+
+ return self
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # 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())
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
+ def __init__(self, location, innerType, name):
+ IDLType.__init__(self, location, innerType.name)
+
+ identifier = IDLUnresolvedIdentifier(location, name)
+
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+
+ self.inner = innerType
+ self.name = name
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLTypedefType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.identifier.name
+
+ def nullable(self):
+ return self.inner.nullable()
+
+ def isPrimitive(self):
+ return self.inner.isPrimitive()
+
+ def isBoolean(self):
+ return self.inner.isBoolean()
+
+ def isNumeric(self):
+ return self.inner.isNumeric()
+
+ def isString(self):
+ return self.inner.isString()
+
+ def isByteString(self):
+ return self.inner.isByteString()
+
+ def isDOMString(self):
+ return self.inner.isDOMString()
+
+ def isScalarValueString(self):
+ return self.inner.isScalarValueString()
+
+ def isVoid(self):
+ return self.inner.isVoid()
+
+ def isSequence(self):
+ return self.inner.isSequence()
+
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
+ def isArray(self):
+ return self.inner.isArray()
+
+ def isDictionary(self):
+ return self.inner.isDictionary()
+
+ def isArrayBuffer(self):
+ return self.inner.isArrayBuffer()
+
+ def isArrayBufferView(self):
+ return self.inner.isArrayBufferView()
+
+ def isTypedArray(self):
+ return self.inner.isTypedArray()
+
+ def isInterface(self):
+ return self.inner.isInterface()
+
+ def isCallbackInterface(self):
+ return self.inner.isCallbackInterface()
+
+ def isNonCallbackInterface(self):
+ return self.inner.isNonCallbackInterface()
+
+ def isComplete(self):
+ return False
+
+ def complete(self, parentScope):
+ if not self.inner.isComplete():
+ self.inner = self.inner.complete(parentScope)
+ assert self.inner.isComplete()
+ return self.inner
+
+ def finish(self, parentScope):
+ # Maybe the IDLObjectWithIdentifier for the typedef should be
+ # a separate thing from the type? If that happens, we can
+ # remove some hackery around avoiding isInterface() in
+ # Configuration.py.
+ self.complete(parentScope)
+
+ def validate(self):
+ pass
+
+ # Do we need a resolveType impl? I don't think it's particularly useful....
+
+ def tag(self):
+ return self.inner.tag()
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ return self.inner.isDistinguishableFrom(other)
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLWrapperType(IDLType):
+ def __init__(self, location, inner, promiseInnerType=None):
+ 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 __str__(self):
+ return str(self.name) + " (Wrapper)"
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isScalarValueString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return False
+
+ def isArray(self):
+ return False
+
+ def isDictionary(self):
+ return isinstance(self.inner, IDLDictionary)
+
+ def isInterface(self):
+ return isinstance(self.inner, IDLInterface) or \
+ isinstance(self.inner, IDLExternalInterface)
+
+ def isCallbackInterface(self):
+ return self.isInterface() and self.inner.isCallback()
+
+ def isNonCallbackInterface(self):
+ return self.isInterface() and not self.inner.isCallback()
+
+ def isEnum(self):
+ return isinstance(self.inner, IDLEnum)
+
+ def isPromise(self):
+ return isinstance(self.inner, IDLInterface) and \
+ self.inner.identifier.name == "Promise"
+
+ def isSerializable(self):
+ if self.isInterface():
+ if self.inner.isExternal():
+ return False
+ return any(m.isMethod() and m.isJsonifier() for m in self.inner.members)
+ elif self.isEnum():
+ return True
+ elif self.isDictionary():
+ return all(m.type.isSerializable() for m in self.inner.members)
+ else:
+ raise WebIDLError("IDLWrapperType wraps type %s that we don't know if "
+ "is serializable" % type(self.inner), [self.location])
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolve(parentScope)
+
+ def isComplete(self):
+ return True
+
+ def tag(self):
+ if self.isInterface():
+ return IDLType.Tags.interface
+ elif self.isEnum():
+ return IDLType.Tags.enum
+ elif self.isDictionary():
+ return IDLType.Tags.dictionary
+ else:
+ assert False
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ assert self.isInterface() or self.isEnum() or self.isDictionary()
+ 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.isArray() or
+ other.isDate())
+ if self.isDictionary() and other.nullable():
+ return False
+ if other.isPrimitive() or other.isString() or other.isEnum() or other.isDate():
+ return True
+ if self.isDictionary():
+ return other.isNonCallbackInterface()
+
+ assert self.isInterface()
+ if other.isInterface():
+ if other.isSpiderMonkeyInterface():
+ # Just let |other| handle things
+ return other.isDistinguishableFrom(self)
+ assert self.isGeckoInterface() and other.isGeckoInterface()
+ if self.inner.isExternal() or other.unroll().inner.isExternal():
+ return self != other
+ return (len(self.inner.interfacesBasedOnSelf &
+ other.unroll().inner.interfacesBasedOnSelf) == 0 and
+ (self.isNonCallbackInterface() or
+ other.isNonCallbackInterface()))
+ if (other.isDictionary() or other.isCallback() or
+ other.isSequence() or other.isMozMap() or other.isArray()):
+ return self.isNonCallbackInterface()
+
+ # Not much else |other| can be
+ assert other.isObject()
+ return False
+
+ def isExposedInAllOf(self, exposureSet):
+ if not self.isInterface():
+ 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.
+ return True
+ if (iface.identifier.name == "Promise" and
+ # Check the internal type
+ not self._promiseInnerType.unroll().isExposedInAllOf(exposureSet)):
+ return False
+ return iface.exposureSet.issuperset(exposureSet)
+
+ def _getDependentObjects(self):
+ # NB: The codegen for an interface type depends on
+ # a) That the identifier is in fact an interface (as opposed to
+ # a dictionary or something else).
+ # b) The native type of the interface.
+ # If we depend on the interface object we will also depend on
+ # anything the interface depends on which is undesirable. We
+ # considered implementing a dependency just on the interface type
+ # file, but then every modification to an interface would cause this
+ # to be regenerated which is still undesirable. We decided not to
+ # depend on anything, reasoning that:
+ # 1) Changing the concrete type of the interface requires modifying
+ # Bindings.conf, which is still a global dependency.
+ # 2) Changing an interface to a dictionary (or vice versa) with the
+ # same identifier should be incredibly rare.
+ return set()
+
+class IDLBuiltinType(IDLType):
+
+ Types = enum(
+ # The integer types
+ 'byte',
+ 'octet',
+ 'short',
+ 'unsigned_short',
+ 'long',
+ 'unsigned_long',
+ 'long_long',
+ 'unsigned_long_long',
+ # Additional primitive types
+ 'boolean',
+ 'unrestricted_float',
+ 'float',
+ 'unrestricted_double',
+ # IMPORTANT: "double" must be the last primitive type listed
+ 'double',
+ # Other types
+ 'any',
+ 'domstring',
+ 'bytestring',
+ 'scalarvaluestring',
+ 'object',
+ 'date',
+ 'void',
+ # Funny stuff
+ 'ArrayBuffer',
+ 'ArrayBufferView',
+ 'Int8Array',
+ 'Uint8Array',
+ 'Uint8ClampedArray',
+ 'Int16Array',
+ 'Uint16Array',
+ 'Int32Array',
+ 'Uint32Array',
+ 'Float32Array',
+ 'Float64Array'
+ )
+
+ TagLookup = {
+ Types.byte: IDLType.Tags.int8,
+ Types.octet: IDLType.Tags.uint8,
+ Types.short: IDLType.Tags.int16,
+ Types.unsigned_short: IDLType.Tags.uint16,
+ Types.long: IDLType.Tags.int32,
+ Types.unsigned_long: IDLType.Tags.uint32,
+ Types.long_long: IDLType.Tags.int64,
+ Types.unsigned_long_long: IDLType.Tags.uint64,
+ Types.boolean: IDLType.Tags.bool,
+ Types.unrestricted_float: IDLType.Tags.unrestricted_float,
+ Types.float: IDLType.Tags.float,
+ Types.unrestricted_double: IDLType.Tags.unrestricted_double,
+ Types.double: IDLType.Tags.double,
+ Types.any: IDLType.Tags.any,
+ Types.domstring: IDLType.Tags.domstring,
+ Types.bytestring: IDLType.Tags.bytestring,
+ Types.scalarvaluestring: IDLType.Tags.scalarvaluestring,
+ 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.Int8Array: IDLType.Tags.interface,
+ Types.Uint8Array: IDLType.Tags.interface,
+ Types.Uint8ClampedArray: IDLType.Tags.interface,
+ Types.Int16Array: IDLType.Tags.interface,
+ Types.Uint16Array: IDLType.Tags.interface,
+ Types.Int32Array: IDLType.Tags.interface,
+ Types.Uint32Array: IDLType.Tags.interface,
+ Types.Float32Array: IDLType.Tags.interface,
+ Types.Float64Array: IDLType.Tags.interface
+ }
+
+ def __init__(self, location, name, type):
+ IDLType.__init__(self, location, name)
+ self.builtin = True
+ self._typeTag = type
+
+ def isPrimitive(self):
+ return self._typeTag <= IDLBuiltinType.Types.double
+
+ def isBoolean(self):
+ return self._typeTag == IDLBuiltinType.Types.boolean
+
+ def isNumeric(self):
+ return self.isPrimitive() and not self.isBoolean()
+
+ def isString(self):
+ return self._typeTag == IDLBuiltinType.Types.domstring or \
+ self._typeTag == IDLBuiltinType.Types.bytestring or \
+ self._typeTag == IDLBuiltinType.Types.scalarvaluestring
+
+ def isByteString(self):
+ return self._typeTag == IDLBuiltinType.Types.bytestring
+
+ def isDOMString(self):
+ return self._typeTag == IDLBuiltinType.Types.domstring
+
+ def isScalarValueString(self):
+ return self._typeTag == IDLBuiltinType.Types.scalarvaluestring
+
+ def isInteger(self):
+ return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
+
+ def isArrayBuffer(self):
+ return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
+
+ def isArrayBufferView(self):
+ return self._typeTag == IDLBuiltinType.Types.ArrayBufferView
+
+ def isTypedArray(self):
+ return self._typeTag >= IDLBuiltinType.Types.Int8Array and \
+ self._typeTag <= IDLBuiltinType.Types.Float64Array
+
+ 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.isTypedArray()
+
+ def isNonCallbackInterface(self):
+ # All the interfaces we can be are non-callback
+ return self.isInterface()
+
+ def isFloat(self):
+ return self._typeTag == IDLBuiltinType.Types.float or \
+ self._typeTag == IDLBuiltinType.Types.double or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_float or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_double
+
+ def isUnrestricted(self):
+ assert self.isFloat()
+ return self._typeTag == IDLBuiltinType.Types.unrestricted_float or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_double
+
+ def isSerializable(self):
+ return self.isPrimitive() or self.isDOMString() or self.isDate()
+
+ def includesRestrictedFloat(self):
+ return self.isFloat() and not self.isUnrestricted()
+
+ def tag(self):
+ return IDLBuiltinType.TagLookup[self._typeTag]
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ if self.isBoolean():
+ 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.isArray() or
+ other.isDate())
+ 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.isArray() or
+ other.isDate())
+ 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.isArray() or
+ other.isDate())
+ 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() or other.isArray())
+ if self.isVoid():
+ return not other.isVoid()
+ # Not much else we could be!
+ assert self.isSpiderMonkeyInterface()
+ # 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.isArray() or
+ other.isDate() 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
+ # ArrayBufferView is distinguishable from everything
+ # that's not an ArrayBufferView or typed array.
+ (self.isArrayBufferView() and not other.isArrayBufferView() and
+ not other.isTypedArray()) or
+ # Typed arrays are distinguishable from everything
+ # except ArrayBufferView and the same type of typed
+ # array
+ (self.isTypedArray() and not other.isArrayBufferView() and not
+ (other.isTypedArray() and other.name == self.name)))))
+
+ def _getDependentObjects(self):
+ return set()
+
+BuiltinTypes = {
+ IDLBuiltinType.Types.byte:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte",
+ IDLBuiltinType.Types.byte),
+ IDLBuiltinType.Types.octet:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Octet",
+ IDLBuiltinType.Types.octet),
+ IDLBuiltinType.Types.short:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Short",
+ IDLBuiltinType.Types.short),
+ IDLBuiltinType.Types.unsigned_short:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedShort",
+ IDLBuiltinType.Types.unsigned_short),
+ IDLBuiltinType.Types.long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Long",
+ IDLBuiltinType.Types.long),
+ IDLBuiltinType.Types.unsigned_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLong",
+ IDLBuiltinType.Types.unsigned_long),
+ IDLBuiltinType.Types.long_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "LongLong",
+ IDLBuiltinType.Types.long_long),
+ IDLBuiltinType.Types.unsigned_long_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLongLong",
+ IDLBuiltinType.Types.unsigned_long_long),
+ IDLBuiltinType.Types.boolean:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Boolean",
+ IDLBuiltinType.Types.boolean),
+ IDLBuiltinType.Types.float:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float",
+ IDLBuiltinType.Types.float),
+ IDLBuiltinType.Types.unrestricted_float:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnrestrictedFloat",
+ IDLBuiltinType.Types.unrestricted_float),
+ IDLBuiltinType.Types.double:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Double",
+ IDLBuiltinType.Types.double),
+ IDLBuiltinType.Types.unrestricted_double:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnrestrictedDouble",
+ IDLBuiltinType.Types.unrestricted_double),
+ IDLBuiltinType.Types.any:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Any",
+ IDLBuiltinType.Types.any),
+ IDLBuiltinType.Types.domstring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "String",
+ IDLBuiltinType.Types.domstring),
+ IDLBuiltinType.Types.bytestring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ByteString",
+ IDLBuiltinType.Types.bytestring),
+ IDLBuiltinType.Types.scalarvaluestring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ScalarValueString",
+ IDLBuiltinType.Types.scalarvaluestring),
+ 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),
+ IDLBuiltinType.Types.ArrayBuffer:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBuffer",
+ IDLBuiltinType.Types.ArrayBuffer),
+ IDLBuiltinType.Types.ArrayBufferView:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBufferView",
+ IDLBuiltinType.Types.ArrayBufferView),
+ IDLBuiltinType.Types.Int8Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int8Array",
+ IDLBuiltinType.Types.Int8Array),
+ IDLBuiltinType.Types.Uint8Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint8Array",
+ IDLBuiltinType.Types.Uint8Array),
+ IDLBuiltinType.Types.Uint8ClampedArray:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint8ClampedArray",
+ IDLBuiltinType.Types.Uint8ClampedArray),
+ IDLBuiltinType.Types.Int16Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int16Array",
+ IDLBuiltinType.Types.Int16Array),
+ IDLBuiltinType.Types.Uint16Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint16Array",
+ IDLBuiltinType.Types.Uint16Array),
+ IDLBuiltinType.Types.Int32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int32Array",
+ IDLBuiltinType.Types.Int32Array),
+ IDLBuiltinType.Types.Uint32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint32Array",
+ IDLBuiltinType.Types.Uint32Array),
+ IDLBuiltinType.Types.Float32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float32Array",
+ IDLBuiltinType.Types.Float32Array),
+ IDLBuiltinType.Types.Float64Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
+ IDLBuiltinType.Types.Float64Array)
+ }
+
+
+integerTypeSizes = {
+ IDLBuiltinType.Types.byte: (-128, 127),
+ IDLBuiltinType.Types.octet: (0, 255),
+ IDLBuiltinType.Types.short: (-32768, 32767),
+ IDLBuiltinType.Types.unsigned_short: (0, 65535),
+ IDLBuiltinType.Types.long: (-2147483648, 2147483647),
+ IDLBuiltinType.Types.unsigned_long: (0, 4294967295),
+ IDLBuiltinType.Types.long_long: (-9223372036854775808,
+ 9223372036854775807),
+ IDLBuiltinType.Types.unsigned_long_long: (0, 18446744073709551615)
+ }
+
+def matchIntegerValueToType(value):
+ for type, extremes in integerTypeSizes.items():
+ (min, max) = extremes
+ if value <= max and value >= min:
+ return BuiltinTypes[type]
+
+ return None
+
+class IDLValue(IDLObject):
+ def __init__(self, location, type, value):
+ IDLObject.__init__(self, location)
+ self.type = type
+ assert isinstance(type, IDLType)
+
+ self.value = value
+
+ def coerceToType(self, type, location):
+ if type == self.type:
+ return self # Nothing to do
+
+ # We first check for unions to ensure that even if the union is nullable
+ # we end up with the right flat member type, not the union's type.
+ 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:
+ coercedValue = self.coerceToType(subtype, location)
+ # Create a new IDLValue to make sure that we have the
+ # correct float/double type. This is necessary because we
+ # use the value's type when it is a default value of a
+ # union, and the union cares about the exact float type.
+ return IDLValue(self.location, subtype, coercedValue.value)
+ except:
+ pass
+ # If the type allows null, rerun this matching on the inner type, except
+ # nullable enums. We handle those specially, because we want our
+ # default string values to stay strings even when assigned to a nullable
+ # enum.
+ elif type.nullable() and not type.isEnum():
+ innerValue = self.coerceToType(type.inner, location)
+ return IDLValue(self.location, type, innerValue.value)
+
+ elif self.type.isInteger() and type.isInteger():
+ # We're both integer types. See if we fit.
+
+ (min, max) = integerTypeSizes[type._typeTag]
+ if self.value <= max and self.value >= min:
+ # Promote
+ return IDLValue(self.location, type, self.value)
+ else:
+ raise WebIDLError("Value %s is out of range for type %s." %
+ (self.value, type), [location])
+ elif self.type.isInteger() and type.isFloat():
+ # Convert an integer literal into float
+ if -2**24 <= self.value <= 2**24:
+ floatType = BuiltinTypes[IDLBuiltinType.Types.float]
+ return IDLValue(self.location, floatType, float(self.value))
+ else:
+ raise WebIDLError("Converting value %s to %s will lose precision." %
+ (self.value, type), [location])
+ 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():
+ raise WebIDLError("'%s' is not a valid default value for enum %s"
+ % (self.value, enum.identifier.name),
+ [location, enum.location])
+ return self
+ elif self.type.isFloat() and type.isFloat():
+ if (not type.isUnrestricted() and
+ (self.value == float("inf") or self.value == float("-inf") or
+ math.isnan(self.value))):
+ raise WebIDLError("Trying to convert unrestricted value %s to non-unrestricted"
+ % self.value, [location]);
+ return self
+ elif self.type.isString() and type.isScalarValueString():
+ # Allow ScalarValueStrings to use default value just like
+ # DOMString. No coercion is required in this case as Codegen.py
+ # treats ScalarValueString just like DOMString, but with an
+ # extra normalization step.
+ assert self.type.isDOMString()
+ return self
+ raise WebIDLError("Cannot coerce type %s to type %s." %
+ (self.type, type), [location])
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLNullValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ 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])
+
+ nullValue = IDLNullValue(self.location)
+ if type.isUnion() and not type.nullable() and type.hasDictionaryType:
+ # We're actually a default value for the union's dictionary member.
+ # Use its type.
+ for t in type.flatMemberTypes:
+ if t.isDictionary():
+ nullValue.type = t
+ return nullValue
+ nullValue.type = type
+ return nullValue
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLEmptySequenceValue(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.isSequence():
+ raise WebIDLError("Cannot coerce empty sequence value to type %s." % type,
+ [location])
+
+ emptySequenceValue = IDLEmptySequenceValue(self.location)
+ emptySequenceValue.type = type
+ return emptySequenceValue
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLUndefinedValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if not type.isAny():
+ raise WebIDLError("Cannot coerce undefined value to type %s." % type,
+ [location])
+
+ undefinedValue = IDLUndefinedValue(self.location)
+ undefinedValue.type = type
+ return undefinedValue
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLInterfaceMember(IDLObjectWithIdentifier):
+
+ Tags = enum(
+ 'Const',
+ 'Attr',
+ 'Method'
+ )
+
+ Special = enum(
+ 'Static',
+ 'Stringifier'
+ )
+
+ def __init__(self, location, identifier, tag):
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+ self.tag = tag
+ self._extendedAttrDict = {}
+ # _exposureGlobalNames are the global names listed in our [Exposed]
+ # extended attribute. exposureSet is the exposure set as defined in the
+ # Web IDL spec: it contains interface names.
+ self._exposureGlobalNames = set()
+ self.exposureSet = set()
+
+ def isMethod(self):
+ return self.tag == IDLInterfaceMember.Tags.Method
+
+ def isAttr(self):
+ return self.tag == IDLInterfaceMember.Tags.Attr
+
+ def isConst(self):
+ return self.tag == IDLInterfaceMember.Tags.Const
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ self.handleExtendedAttribute(attr)
+ attrlist = attr.listValue()
+ self._extendedAttrDict[attr.identifier()] = attrlist if len(attrlist) else True
+
+ def handleExtendedAttribute(self, attr):
+ pass
+
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
+
+ def finish(self, scope):
+ for globalName in self._exposureGlobalNames:
+ if globalName not in scope.globalNames:
+ raise WebIDLError("Unknown [Exposed] value %s" % globalName,
+ [self.location])
+ globalNameSetToExposureSet(scope, self._exposureGlobalNames,
+ self.exposureSet)
+ self._scope = scope
+
+ def validate(self):
+ if (self.getExtendedAttribute("Pref") and
+ self.exposureSet != set([self._scope.primaryGlobalName])):
+ raise WebIDLError("[Pref] used on an interface member that is not "
+ "%s-only" % self._scope.primaryGlobalName,
+ [self.location])
+
+class IDLConst(IDLInterfaceMember):
+ def __init__(self, location, identifier, type, value):
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Const)
+
+ assert isinstance(type, IDLType)
+ if type.isDictionary():
+ raise WebIDLError("A constant cannot be of a dictionary type",
+ [self.location])
+ self.type = type
+ self.value = value
+
+ if identifier.name == "prototype":
+ raise WebIDLError("The identifier of a constant must not be 'prototype'",
+ [location])
+
+ def __str__(self):
+ return "'%s' const '%s'" % (self.type, self.identifier)
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ if not self.type.isComplete():
+ type = self.type.complete(scope)
+ if not type.isPrimitive() and not type.isString():
+ locations = [self.type.location, type.location]
+ try:
+ locations.append(type.inner.location)
+ except:
+ pass
+ raise WebIDLError("Incorrect type for constant", locations)
+ self.type = type
+
+ # The value might not match the type
+ coercedValue = self.value.coerceToType(self.type, self.location)
+ assert coercedValue
+
+ self.value = coercedValue
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif (identifier == "Pref" or
+ identifier == "ChromeOnly" or
+ identifier == "Func" or
+ identifier == "AvailableIn" or
+ identifier == "CheckPermissions"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on constant" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def _getDependentObjects(self):
+ return set([self.type, self.value])
+
+class IDLAttribute(IDLInterfaceMember):
+ def __init__(self, location, identifier, type, readonly, inherit=False,
+ static=False, stringifier=False):
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Attr)
+
+ assert isinstance(type, IDLType)
+ self.type = type
+ self.readonly = readonly
+ self.inherit = inherit
+ self.static = static
+ self.lenientThis = False
+ self._unforgeable = False
+ self.stringifier = stringifier
+ self.enforceRange = False
+ self.clamp = False
+ self.slotIndex = None
+
+ if static and identifier.name == "prototype":
+ raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
+ [location])
+
+ if readonly and inherit:
+ raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
+ [self.location])
+
+ def isStatic(self):
+ return self.static
+
+ def __str__(self):
+ return "'%s' attribute '%s'" % (self.type, self.identifier)
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ if not self.type.isComplete():
+ t = self.type.complete(scope)
+
+ assert not isinstance(t, IDLUnresolvedType)
+ assert not isinstance(t, IDLTypedefType)
+ assert not isinstance(t.name, IDLUnresolvedIdentifier)
+ self.type = t
+
+ 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 "
+ "type", [self.location])
+ if self.type.isUnion():
+ for f in self.type.unroll().flatMemberTypes:
+ if f.isDictionary():
+ 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 dictionary "
+ "type", [self.location, f.location])
+ if f.isSequence():
+ 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 sequence "
+ "type", [self.location, f.location])
+ if f.isMozMap():
+ 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 "
+ "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"):
+ raise WebIDLError("An attribute with [SameObject] must have an "
+ "interface type as its type", [self.location])
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ if ((self.getExtendedAttribute("Cached") or
+ self.getExtendedAttribute("StoreInSlot")) and
+ not self.getExtendedAttribute("Constant") and
+ not self.getExtendedAttribute("Pure")):
+ raise WebIDLError("Cached attributes and attributes stored in "
+ "slots must be constant or pure, since the "
+ "getter won't always be called.",
+ [self.location])
+ if self.getExtendedAttribute("Frozen"):
+ if (not self.type.isSequence() and not self.type.isDictionary() and
+ not self.type.isMozMap()):
+ raise WebIDLError("[Frozen] is only allowed on "
+ "sequence-valued, dictionary-valued, and "
+ "MozMap-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])
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "SetterThrows" and self.readonly:
+ raise WebIDLError("Readonly attributes must not be flagged as "
+ "[SetterThrows]",
+ [self.location])
+ elif (((identifier == "Throws" or identifier == "GetterThrows") and
+ self.getExtendedAttribute("StoreInSlot")) or
+ (identifier == "StoreInSlot" and
+ (self.getExtendedAttribute("Throws") or
+ self.getExtendedAttribute("GetterThrows")))):
+ raise WebIDLError("Throwing things can't be [Pure] or [Constant] "
+ "or [SameObject] or [StoreInSlot]",
+ [attr.location])
+ elif identifier == "LenientThis":
+ if not attr.noArguments():
+ raise WebIDLError("[LenientThis] must take no arguments",
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[LenientThis] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("CrossOriginReadable"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [CrossOriginReadable]",
+ [attr.location, self.location])
+ if self.getExtendedAttribute("CrossOriginWritable"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [CrossOriginWritable]",
+ [attr.location, self.location])
+ self.lenientThis = True
+ elif identifier == "Unforgeable":
+ if self.isStatic():
+ raise WebIDLError("[Unforgeable] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ self._unforgeable = True
+ elif identifier == "SameObject" and not self.readonly:
+ raise WebIDLError("[SameObject] only allowed on readonly attributes",
+ [attr.location, self.location])
+ elif identifier == "Constant" and not self.readonly:
+ raise WebIDLError("[Constant] only allowed on readonly attributes",
+ [attr.location, self.location])
+ elif identifier == "PutForwards":
+ if not self.readonly:
+ raise WebIDLError("[PutForwards] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[PutForwards] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("Replaceable") is not None:
+ raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ if not attr.hasValue():
+ raise WebIDLError("[PutForwards] takes an identifier",
+ [attr.location, self.location])
+ elif identifier == "Replaceable":
+ if self.getExtendedAttribute("PutForwards") is not None:
+ raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "LenientFloat":
+ if self.readonly:
+ raise WebIDLError("[LenientFloat] used on a readonly attribute",
+ [attr.location, self.location])
+ if not self.type.includesRestrictedFloat():
+ 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 "
+ "specified on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "Cached":
+ if self.getExtendedAttribute("StoreInSlot"):
+ raise WebIDLError("[Cached] and [StoreInSlot] must not be "
+ "specified on the same attribute",
+ [attr.location, self.location])
+ elif (identifier == "CrossOriginReadable" or
+ identifier == "CrossOriginWritable"):
+ if not attr.noArguments() and identifier == "CrossOriginReadable":
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[%s] is only allowed on non-static "
+ "attributes" % identifier,
+ [attr.location, self.location])
+ if self.getExtendedAttribute("LenientThis"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [%s]" % identifier,
+ [attr.location, self.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif (identifier == "Pref" or
+ identifier == "SetterThrows" or
+ identifier == "Pure" or
+ identifier == "Throws" or
+ identifier == "GetterThrows" or
+ identifier == "ChromeOnly" or
+ identifier == "SameObject" or
+ identifier == "Constant" or
+ identifier == "Func" or
+ identifier == "Frozen" or
+ identifier == "AvailableIn" or
+ identifier == "NewObject" or
+ identifier == "CheckPermissions"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on attribute" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def resolve(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ 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
+
+ def isUnforgeable(self):
+ return self._unforgeable
+
+ def _getDependentObjects(self):
+ return set([self.type])
+
+class IDLArgument(IDLObjectWithIdentifier):
+ def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+
+ assert isinstance(type, IDLType)
+ self.type = type
+
+ self.optional = optional
+ self.defaultValue = defaultValue
+ self.variadic = variadic
+ self.dictionaryMember = dictionaryMember
+ self._isComplete = False
+ self.enforceRange = False
+ self.clamp = False
+ self._allowTreatNonCallableAsNull = False
+
+ assert not variadic or optional
+
+ 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
+ elif identifier == "TreatNonCallableAsNull":
+ self._allowTreatNonCallableAsNull = True
+ else:
+ raise WebIDLError("Unhandled extended attribute on an argument",
+ [attribute.location])
+
+ def isComplete(self):
+ return self._isComplete
+
+ def complete(self, scope):
+ if self._isComplete:
+ return
+
+ self._isComplete = True
+
+ if not self.type.isComplete():
+ type = self.type.complete(scope)
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ 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):
+ # Default optional dictionaries to null, for simplicity,
+ # so the codegen doesn't have to special-case this.
+ self.defaultValue = IDLNullValue(self.location)
+ elif self.type.isAny():
+ assert (self.defaultValue is None or
+ isinstance(self.defaultValue, IDLNullValue))
+ # optional 'any' values always have a default value
+ if self.optional and not self.defaultValue and not self.variadic:
+ # Set the default value to undefined, for simplicity, so the
+ # codegen doesn't have to special-case this.
+ self.defaultValue = IDLUndefinedValue(self.location)
+
+ # Now do the coercing thing; this needs to happen after the
+ # above creation of a default value.
+ if self.defaultValue:
+ self.defaultValue = self.defaultValue.coerceToType(self.type,
+ self.location)
+ assert self.defaultValue
+
+ def allowTreatNonCallableAsNull(self):
+ return self._allowTreatNonCallableAsNull
+
+ def _getDependentObjects(self):
+ deps = set([self.type])
+ if self.defaultValue:
+ deps.add(self.defaultValue)
+ return deps
+
+class IDLCallbackType(IDLType, IDLObjectWithScope):
+ def __init__(self, location, parentScope, identifier, returnType, arguments):
+ assert isinstance(returnType, IDLType)
+
+ IDLType.__init__(self, location, identifier.name)
+
+ self._returnType = returnType
+ # Clone the list
+ self._arguments = list(arguments)
+
+ IDLObjectWithScope.__init__(self, location, parentScope, identifier)
+
+ for (returnType, arguments) in self.signatures():
+ for argument in arguments:
+ argument.resolve(self)
+
+ self._treatNonCallableAsNull = False
+ self._treatNonObjectAsNull = False
+
+ def module(self):
+ return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding'
+
+ def isCallback(self):
+ return True
+
+ def signatures(self):
+ return [(self._returnType, self._arguments)]
+
+ def tag(self):
+ return IDLType.Tags.callback
+
+ def finish(self, scope):
+ if not self._returnType.isComplete():
+ type = self._returnType.complete(scope)
+
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ assert not isinstance(type.name, IDLUnresolvedIdentifier)
+ self._returnType = type
+
+ for argument in self._arguments:
+ if argument.type.isComplete():
+ continue
+
+ type = argument.type.complete(scope)
+
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ assert not isinstance(type.name, IDLUnresolvedIdentifier)
+ argument.type = type
+
+ def validate(self):
+ pass
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # 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())
+
+ def addExtendedAttributes(self, attrs):
+ unhandledAttrs = []
+ for attr in attrs:
+ if attr.identifier() == "TreatNonCallableAsNull":
+ self._treatNonCallableAsNull = True
+ elif attr.identifier() == "TreatNonObjectAsNull":
+ self._treatNonObjectAsNull = True
+ else:
+ unhandledAttrs.append(attr)
+ if self._treatNonCallableAsNull and self._treatNonObjectAsNull:
+ raise WebIDLError("Cannot specify both [TreatNonCallableAsNull] "
+ "and [TreatNonObjectAsNull]", [self.location])
+ if len(unhandledAttrs) != 0:
+ IDLType.addExtendedAttributes(self, unhandledAttrs)
+
+ def _getDependentObjects(self):
+ return set([self._returnType] + self._arguments)
+
+class IDLMethodOverload:
+ """
+ A class that represents a single overload of a WebIDL method. This is not
+ quite the same as an element of the "effective overload set" in the spec,
+ because separate IDLMethodOverloads are not created based on arguments being
+ optional. Rather, when multiple methods have the same name, there is an
+ IDLMethodOverload for each one, all hanging off an IDLMethod representing
+ the full set of overloads.
+ """
+ def __init__(self, returnType, arguments, location):
+ self.returnType = returnType
+ # Clone the list of arguments, just in case
+ self.arguments = list(arguments)
+ self.location = location
+
+ def _getDependentObjects(self):
+ deps = set(self.arguments)
+ deps.add(self.returnType)
+ return deps
+
+class IDLMethod(IDLInterfaceMember, IDLScope):
+
+ Special = enum(
+ 'Getter',
+ 'Setter',
+ 'Creator',
+ 'Deleter',
+ 'LegacyCaller',
+ base=IDLInterfaceMember.Special
+ )
+
+ TypeSuffixModifier = enum(
+ 'None',
+ 'QMark',
+ 'Brackets'
+ )
+
+ NamedOrIndexed = enum(
+ 'Neither',
+ 'Named',
+ 'Indexed'
+ )
+
+ def __init__(self, location, identifier, returnType, arguments,
+ static=False, getter=False, setter=False, creator=False,
+ deleter=False, specialType=NamedOrIndexed.Neither,
+ legacycaller=False, stringifier=False, jsonifier=False):
+ # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Method)
+
+ self._hasOverloads = False
+
+ assert isinstance(returnType, IDLType)
+
+ # self._overloads is a list of IDLMethodOverloads
+ self._overloads = [IDLMethodOverload(returnType, arguments, location)]
+
+ assert isinstance(static, bool)
+ self._static = static
+ assert isinstance(getter, bool)
+ 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
+ self._specialType = specialType
+ self._unforgeable = False
+
+ if static and identifier.name == "prototype":
+ raise WebIDLError("The identifier of a static operation must not be 'prototype'",
+ [location])
+
+ self.assertSignatureConstraints()
+
+ def __str__(self):
+ return "Method '%s'" % self.identifier
+
+ def assertSignatureConstraints(self):
+ if self._getter or self._deleter:
+ assert len(self._overloads) == 1
+ overload = self._overloads[0]
+ arguments = overload.arguments
+ assert len(arguments) == 1
+ assert arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or \
+ arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]
+ 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:
+ assert len(self._overloads) == 1
+ arguments = self._overloads[0].arguments
+ assert len(arguments) == 2
+ assert arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or \
+ arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]
+ assert not arguments[0].optional and not arguments[0].variadic
+ assert not arguments[1].optional and not arguments[1].variadic
+
+ if self._stringifier:
+ 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]
+
+ def isStatic(self):
+ return self._static
+
+ def isGetter(self):
+ return self._getter
+
+ def isSetter(self):
+ return self._setter
+
+ def isCreator(self):
+ return self._creator
+
+ def isDeleter(self):
+ return self._deleter
+
+ def isNamed(self):
+ assert self._specialType == IDLMethod.NamedOrIndexed.Named or \
+ self._specialType == IDLMethod.NamedOrIndexed.Indexed
+ return self._specialType == IDLMethod.NamedOrIndexed.Named
+
+ def isIndexed(self):
+ assert self._specialType == IDLMethod.NamedOrIndexed.Named or \
+ self._specialType == IDLMethod.NamedOrIndexed.Indexed
+ return self._specialType == IDLMethod.NamedOrIndexed.Indexed
+
+ def isLegacycaller(self):
+ return self._legacycaller
+
+ def isStringifier(self):
+ return self._stringifier
+
+ def isJsonifier(self):
+ return self._jsonifier
+
+ def hasOverloads(self):
+ return self._hasOverloads
+
+ def isIdentifierLess(self):
+ return self.identifier.name[:2] == "__" and self.identifier.name != "__noSuchMethod__"
+
+ def resolve(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ IDLObjectWithIdentifier.resolve(self, parentScope)
+ IDLScope.__init__(self, self.location, parentScope, self.identifier)
+ for (returnType, arguments) in self.signatures():
+ for argument in arguments:
+ argument.resolve(self)
+
+ 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])
+
+ self._overloads.extend(method._overloads)
+
+ self._hasOverloads = True
+
+ if self.isStatic() != method.isStatic():
+ raise WebIDLError("Overloaded identifier %s appears with different values of the 'static' attribute" % method.identifier,
+ [method.location])
+
+ if self.isLegacycaller() != method.isLegacycaller():
+ raise WebIDLError("Overloaded identifier %s appears with different values of the 'legacycaller' attribute" % method.identifier,
+ [method.location])
+
+ # Can't overload special things!
+ assert not self.isGetter()
+ 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()
+
+ return self
+
+ def signatures(self):
+ return [(overload.returnType, overload.arguments) for overload in
+ self._overloads]
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ overloadWithPromiseReturnType = None
+ overloadWithoutPromiseReturnType = None
+ for overload in self._overloads:
+ variadicArgument = None
+
+ arguments = overload.arguments
+ for (idx, argument) in enumerate(arguments):
+ if not argument.isComplete():
+ argument.complete(scope)
+ assert argument.type.isComplete()
+
+ if (argument.type.isDictionary() or
+ (argument.type.isUnion() and
+ argument.type.unroll().hasDictionaryType)):
+ # Dictionaries and unions containing dictionaries at the
+ # end of the list or followed by 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 "
+ "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",
+ [argument.location])
+
+ # Only the last argument can be variadic
+ if variadicArgument:
+ raise WebIDLError("Variadic argument is not last argument",
+ [variadicArgument.location])
+ if argument.variadic:
+ variadicArgument = argument
+
+ returnType = overload.returnType
+ if not returnType.isComplete():
+ returnType = returnType.complete(scope)
+ assert not isinstance(returnType, IDLUnresolvedType)
+ assert not isinstance(returnType, IDLTypedefType)
+ assert not isinstance(returnType.name, IDLUnresolvedIdentifier)
+ overload.returnType = returnType
+
+ if returnType.isPromise():
+ overloadWithPromiseReturnType = overload
+ else:
+ overloadWithoutPromiseReturnType = overload
+
+ # Make sure either all our overloads return Promises or none do
+ if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType:
+ raise WebIDLError("We have overloads with both Promise and "
+ "non-Promise return types",
+ [overloadWithPromiseReturnType.location,
+ overloadWithoutPromiseReturnType.location])
+
+ if overloadWithPromiseReturnType and self._legacycaller:
+ raise WebIDLError("May not have a Promise return type for a "
+ "legacycaller.",
+ [overloadWithPromiseReturnType.location])
+
+ # Now compute various information that will be used by the
+ # WebIDL overload resolution algorithm.
+ self.maxArgCount = max(len(s[1]) for s in self.signatures())
+ self.allowedArgCounts = [ i for i in range(self.maxArgCount+1)
+ if len(self.signaturesForArgCount(i)) != 0 ]
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ # Make sure our overloads are properly distinguishable and don't have
+ # different argument types before the distinguishing args.
+ for argCount in self.allowedArgCounts:
+ possibleOverloads = self.overloadsForArgCount(argCount)
+ if len(possibleOverloads) == 1:
+ continue
+ distinguishingIndex = self.distinguishingIndexForArgCount(argCount)
+ for idx in range(distinguishingIndex):
+ firstSigType = possibleOverloads[0].arguments[idx].type
+ for overload in possibleOverloads[1:]:
+ if overload.arguments[idx].type != firstSigType:
+ raise WebIDLError(
+ "Signatures for method '%s' with %d arguments have "
+ "different types of arguments at index %d, which "
+ "is before distinguishing index %d" %
+ (self.identifier.name, argCount, idx,
+ distinguishingIndex),
+ [self.location, overload.location])
+
+ for overload in self._overloads:
+ if not overload.returnType.unroll().isExposedInAllOf(self.exposureSet):
+ raise WebIDLError("Overload returns a type that is not exposed "
+ "everywhere where the method is exposed",
+ [overload.location])
+
+ def overloadsForArgCount(self, argc):
+ return [overload for overload in self._overloads if
+ len(overload.arguments) == argc or
+ (len(overload.arguments) > argc and
+ all(arg.optional for arg in overload.arguments[argc:])) or
+ (len(overload.arguments) < argc and
+ len(overload.arguments) > 0 and
+ overload.arguments[-1].variadic)]
+
+ def signaturesForArgCount(self, argc):
+ return [(overload.returnType, overload.arguments) for overload
+ in self.overloadsForArgCount(argc)]
+
+ def locationsForArgCount(self, argc):
+ return [overload.location for overload in self.overloadsForArgCount(argc)]
+
+ def distinguishingIndexForArgCount(self, argc):
+ def isValidDistinguishingIndex(idx, signatures):
+ for (firstSigIndex, (firstRetval, firstArgs)) in enumerate(signatures[:-1]):
+ for (secondRetval, secondArgs) in signatures[firstSigIndex+1:]:
+ if idx < len(firstArgs):
+ firstType = firstArgs[idx].type
+ else:
+ assert(firstArgs[-1].variadic)
+ firstType = firstArgs[-1].type
+ if idx < len(secondArgs):
+ secondType = secondArgs[idx].type
+ else:
+ assert(secondArgs[-1].variadic)
+ secondType = secondArgs[-1].type
+ if not firstType.isDistinguishableFrom(secondType):
+ return False
+ return True
+ signatures = self.signaturesForArgCount(argc)
+ for idx in range(argc):
+ if isValidDistinguishingIndex(idx, signatures):
+ return idx
+ # No valid distinguishing index. Time to throw
+ locations = self.locationsForArgCount(argc)
+ raise WebIDLError("Signatures with %d arguments for method '%s' are not "
+ "distinguishable" % (argc, self.identifier.name),
+ locations)
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "GetterThrows":
+ 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]",
+ [attr.location, self.location])
+ elif identifier == "Unforgeable":
+ if self.isStatic():
+ raise WebIDLError("[Unforgeable] is only allowed on non-static "
+ "methods", [attr.location, self.location])
+ self._unforgeable = True
+ elif identifier == "SameObject":
+ raise WebIDLError("Methods must not be flagged as [SameObject]",
+ [attr.location, self.location]);
+ elif identifier == "Constant":
+ raise WebIDLError("Methods must not be flagged as [Constant]",
+ [attr.location, self.location]);
+ elif identifier == "PutForwards":
+ raise WebIDLError("Only attributes support [PutForwards]",
+ [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():
+ raise WebIDLError("[LenientFloat] used on a non-void method",
+ [attr.location, self.location])
+ if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
+ raise WebIDLError("[LenientFloat] used on an operation with no "
+ "restricted float type arguments",
+ [attr.location, self.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif (identifier == "Pure" or
+ identifier == "CrossOriginCallable" or
+ identifier == "WebGLHandlesContextLoss"):
+ # Known no-argument attributes.
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ elif (identifier == "Throws" or
+ identifier == "NewObject" or
+ identifier == "ChromeOnly" or
+ identifier == "Pref" or
+ identifier == "Func" or
+ identifier == "AvailableIn" or
+ identifier == "CheckPermissions"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on method" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def returnsPromise(self):
+ return self._overloads[0].returnType.isPromise()
+
+ def isUnforgeable(self):
+ return self._unforgeable
+
+ def _getDependentObjects(self):
+ deps = set()
+ for overload in self._overloads:
+ deps.union(overload._getDependentObjects())
+ return deps
+
+class IDLImplementsStatement(IDLObject):
+ def __init__(self, location, implementor, implementee):
+ IDLObject.__init__(self, location)
+ self.implementor = implementor;
+ self.implementee = implementee
+
+ def finish(self, scope):
+ 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
+ # 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 "
+ "interface",
+ [self.implementee.location])
+ if implementee.isCallback():
+ raise WebIDLError("Right-hand side of 'implements' is a callback "
+ "interface",
+ [self.implementee.location])
+ implementor.addImplementedInterface(implementee)
+
+ def validate(self):
+ pass
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+class IDLExtendedAttribute(IDLObject):
+ """
+ A class to represent IDL extended attributes so we can give them locations
+ """
+ def __init__(self, location, tuple):
+ IDLObject.__init__(self, location)
+ self._tuple = tuple
+
+ def identifier(self):
+ return self._tuple[0]
+
+ def noArguments(self):
+ return len(self._tuple) == 1
+
+ def hasValue(self):
+ return len(self._tuple) >= 2 and isinstance(self._tuple[1], str)
+
+ def value(self):
+ assert(self.hasValue())
+ return self._tuple[1]
+
+ def hasArgs(self):
+ return (len(self._tuple) == 2 and isinstance(self._tuple[1], list) or
+ len(self._tuple) == 3)
+
+ def args(self):
+ assert(self.hasArgs())
+ # Our args are our last element
+ return self._tuple[-1]
+
+ def listValue(self):
+ """
+ Backdoor for storing random data in _extendedAttrDict
+ """
+ return list(self._tuple)[1:]
+
+# Parser
+
+class Tokenizer(object):
+ tokens = [
+ "INTEGER",
+ "FLOATLITERAL",
+ "IDENTIFIER",
+ "STRING",
+ "WHITESPACE",
+ "OTHER"
+ ]
+
+ def t_FLOATLITERAL(self, t):
+ r'(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+|Infinity))|NaN'
+ t.value = float(t.value)
+ return t
+
+ def t_INTEGER(self, t):
+ r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)'
+ try:
+ # Can't use int(), because that doesn't handle octal properly.
+ t.value = parseInt(t.value)
+ except:
+ raise WebIDLError("Invalid integer literal",
+ [Location(lexer=self.lexer,
+ lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos,
+ filename=self._filename)])
+ return t
+
+ def t_IDENTIFIER(self, t):
+ r'[A-Z_a-z][0-9A-Z_a-z-]*'
+ t.type = self.keywords.get(t.value, 'IDENTIFIER')
+ return t
+
+ def t_STRING(self, t):
+ r'"[^"]*"'
+ t.value = t.value[1:-1]
+ return t
+
+ def t_WHITESPACE(self, t):
+ r'[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+'
+ pass
+
+ def t_ELLIPSIS(self, t):
+ r'\.\.\.'
+ t.type = self.keywords.get(t.value)
+ return t
+
+ def t_OTHER(self, t):
+ r'[^\t\n\r 0-9A-Z_a-z]'
+ t.type = self.keywords.get(t.value, 'OTHER')
+ return t
+
+ keywords = {
+ "module": "MODULE",
+ "interface": "INTERFACE",
+ "partial": "PARTIAL",
+ "dictionary": "DICTIONARY",
+ "exception": "EXCEPTION",
+ "enum": "ENUM",
+ "callback": "CALLBACK",
+ "typedef": "TYPEDEF",
+ "implements": "IMPLEMENTS",
+ "const": "CONST",
+ "null": "NULL",
+ "true": "TRUE",
+ "false": "FALSE",
+ "serializer": "SERIALIZER",
+ "stringifier": "STRINGIFIER",
+ "jsonifier": "JSONIFIER",
+ "unrestricted": "UNRESTRICTED",
+ "attribute": "ATTRIBUTE",
+ "readonly": "READONLY",
+ "inherit": "INHERIT",
+ "static": "STATIC",
+ "getter": "GETTER",
+ "setter": "SETTER",
+ "creator": "CREATOR",
+ "deleter": "DELETER",
+ "legacycaller": "LEGACYCALLER",
+ "optional": "OPTIONAL",
+ "...": "ELLIPSIS",
+ "::": "SCOPE",
+ "Date": "DATE",
+ "DOMString": "DOMSTRING",
+ "ByteString": "BYTESTRING",
+ "ScalarValueString": "SCALARVALUESTRING",
+ "any": "ANY",
+ "boolean": "BOOLEAN",
+ "byte": "BYTE",
+ "double": "DOUBLE",
+ "float": "FLOAT",
+ "long": "LONG",
+ "object": "OBJECT",
+ "octet": "OCTET",
+ "optional": "OPTIONAL",
+ "Promise": "PROMISE",
+ "sequence": "SEQUENCE",
+ "MozMap": "MOZMAP",
+ "short": "SHORT",
+ "unsigned": "UNSIGNED",
+ "void": "VOID",
+ ":": "COLON",
+ ";": "SEMICOLON",
+ "{": "LBRACE",
+ "}": "RBRACE",
+ "(": "LPAREN",
+ ")": "RPAREN",
+ "[": "LBRACKET",
+ "]": "RBRACKET",
+ "?": "QUESTIONMARK",
+ ",": "COMMA",
+ "=": "EQUALS",
+ "<": "LT",
+ ">": "GT",
+ "ArrayBuffer": "ARRAYBUFFER",
+ "or": "OR"
+ }
+
+ tokens.extend(keywords.values())
+
+ def t_error(self, t):
+ raise WebIDLError("Unrecognized Input",
+ [Location(lexer=self.lexer,
+ lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos,
+ filename = self.filename)])
+
+ def __init__(self, outputdir, lexer=None):
+ if lexer:
+ self.lexer = lexer
+ else:
+ self.lexer = lex.lex(object=self,
+ outputdir=outputdir,
+ lextab='webidllex',
+ reflags=re.DOTALL)
+
+class SqueakyCleanLogger(object):
+ errorWhitelist = [
+ # Web IDL defines the WHITESPACE 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",
+ # Web IDL defines a OtherOrComma rule that's only used in
+ # ExtendedAttributeInner, which we don't use yet.
+ "Rule 'OtherOrComma' defined, but not used",
+ # And an unused rule
+ "There is 1 unused rule",
+ # And the OtherOrComma grammar symbol is unreachable.
+ "Symbol 'OtherOrComma' is unreachable",
+ # Which means the Other symbol is unreachable.
+ "Symbol 'Other' is unreachable",
+ ]
+ def __init__(self):
+ self.errors = []
+ def debug(self, msg, *args, **kwargs):
+ pass
+ info = debug
+ def warning(self, msg, *args, **kwargs):
+ if msg == "%s:%d: Rule '%s' defined, but not used":
+ # Munge things so we don't have to hardcode filenames and
+ # line numbers in our whitelist.
+ whitelistmsg = "Rule '%s' defined, but not used"
+ whitelistargs = args[2:]
+ else:
+ whitelistmsg = msg
+ whitelistargs = args
+ if (whitelistmsg % whitelistargs) not in SqueakyCleanLogger.errorWhitelist:
+ self.errors.append(msg % args)
+ error = warning
+
+ def reportGrammarErrors(self):
+ if self.errors:
+ raise WebIDLError("\n".join(self.errors), [])
+
+class Parser(Tokenizer):
+ def getLocation(self, p, i):
+ return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename)
+
+ def globalScope(self):
+ return self._globalScope
+
+ # The p_Foo functions here must match the WebIDL spec's grammar.
+ # It's acceptable to split things at '|' boundaries.
+ def p_Definitions(self, p):
+ """
+ Definitions : ExtendedAttributeList Definition Definitions
+ """
+ if p[2]:
+ p[0] = [p[2]]
+ p[2].addExtendedAttributes(p[1])
+ else:
+ assert not p[1]
+ p[0] = []
+
+ p[0].extend(p[3])
+
+ def p_DefinitionsEmpty(self, p):
+ """
+ Definitions :
+ """
+ p[0] = []
+
+ def p_Definition(self, p):
+ """
+ Definition : CallbackOrInterface
+ | PartialInterface
+ | Dictionary
+ | Exception
+ | Enum
+ | Typedef
+ | ImplementsStatement
+ """
+ p[0] = p[1]
+ assert p[1] # We might not have implemented something ...
+
+ def p_CallbackOrInterfaceCallback(self, p):
+ """
+ CallbackOrInterface : CALLBACK CallbackRestOrInterface
+ """
+ if p[2].isInterface():
+ assert isinstance(p[2], IDLInterface)
+ p[2].setCallback(True)
+
+ p[0] = p[2]
+
+ def p_CallbackOrInterfaceInterface(self, p):
+ """
+ CallbackOrInterface : Interface
+ """
+ p[0] = p[1]
+
+ def p_CallbackRestOrInterface(self, p):
+ """
+ CallbackRestOrInterface : CallbackRest
+ | Interface
+ """
+ assert p[1]
+ p[0] = p[1]
+
+ def p_Interface(self, p):
+ """
+ Interface : INTERFACE 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]
+
+ try:
+ existingObj = self.globalScope()._lookupIdentifier(identifier)
+ if existingObj:
+ p[0] = existingObj
+ if not isinstance(p[0], IDLInterface):
+ raise WebIDLError("Interface has the same name as "
+ "non-interface object",
+ [location, p[0].location])
+ p[0].setNonPartial(location, parent, members)
+ return
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ p[0] = IDLInterface(location, self.globalScope(), identifier, parent,
+ members, isKnownNonPartial=True)
+
+ def p_InterfaceForwardDecl(self, p):
+ """
+ Interface : INTERFACE IDENTIFIER SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+
+ try:
+ if self.globalScope()._lookupIdentifier(identifier):
+ p[0] = self.globalScope()._lookupIdentifier(identifier)
+ if not isinstance(p[0], IDLExternalInterface):
+ raise WebIDLError("Name collision between external "
+ "interface declaration for identifier "
+ "%s and %s" % (identifier.name, p[0]),
+ [location, p[0].location])
+ return
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
+
+ def p_PartialInterface(self, p):
+ """
+ PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 2)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
+ members = p[5]
+
+ nonPartialInterface = None
+ try:
+ nonPartialInterface = self.globalScope()._lookupIdentifier(identifier)
+ if nonPartialInterface:
+ if not isinstance(nonPartialInterface, IDLInterface):
+ raise WebIDLError("Partial interface has the same name as "
+ "non-interface object",
+ [location, nonPartialInterface.location])
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ if not nonPartialInterface:
+ nonPartialInterface = IDLInterface(location, self.globalScope(),
+ identifier, None,
+ [], isKnownNonPartial=False)
+ partialInterface = IDLPartialInterface(location, identifier, members,
+ nonPartialInterface)
+ p[0] = partialInterface
+
+ def p_Inheritance(self, p):
+ """
+ Inheritance : COLON ScopedName
+ """
+ p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2])
+
+ def p_InheritanceEmpty(self, p):
+ """
+ Inheritance :
+ """
+ pass
+
+ def p_InterfaceMembers(self, p):
+ """
+ InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
+ """
+ p[0] = [p[2]] if p[2] else []
+
+ assert not p[1] or p[2]
+ p[2].addExtendedAttributes(p[1])
+
+ p[0].extend(p[3])
+
+ def p_InterfaceMembersEmpty(self, p):
+ """
+ InterfaceMembers :
+ """
+ p[0] = []
+
+ def p_InterfaceMember(self, p):
+ """
+ InterfaceMember : Const
+ | AttributeOrOperation
+ """
+ p[0] = p[1]
+
+ def p_Dictionary(self, p):
+ """
+ Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[5]
+ p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members)
+
+ def p_DictionaryMembers(self, p):
+ """
+ DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
+ |
+ """
+ if len(p) == 1:
+ # 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):
+ """
+ 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]
+
+ p[0] = IDLArgument(self.getLocation(p, 2), identifier, t, optional=True,
+ defaultValue=defaultValue, variadic=False,
+ dictionaryMember=True)
+
+ def p_Default(self, p):
+ """
+ Default : EQUALS DefaultValue
+ |
+ """
+ if len(p) > 1:
+ p[0] = p[2]
+ else:
+ p[0] = None
+
+ def p_DefaultValue(self, p):
+ """
+ DefaultValue : ConstValue
+ | LBRACKET RBRACKET
+ """
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ assert len(p) == 3 # Must be []
+ p[0] = IDLEmptySequenceValue(self.getLocation(p, 1))
+
+ def p_Exception(self, p):
+ """
+ Exception : EXCEPTION IDENTIFIER Inheritance LBRACE ExceptionMembers RBRACE SEMICOLON
+ """
+ pass
+
+ def p_Enum(self, p):
+ """
+ Enum : ENUM IDENTIFIER LBRACE EnumValueList RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+
+ values = p[4]
+ assert values
+ p[0] = IDLEnum(location, self.globalScope(), identifier, values)
+
+ def p_EnumValueList(self, p):
+ """
+ EnumValueList : STRING EnumValueListComma
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_EnumValueListComma(self, p):
+ """
+ EnumValueListComma : COMMA EnumValueListString
+ """
+ p[0] = p[2]
+
+ def p_EnumValueListCommaEmpty(self, p):
+ """
+ EnumValueListComma :
+ """
+ p[0] = []
+
+ def p_EnumValueListString(self, p):
+ """
+ EnumValueListString : STRING EnumValueListComma
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_EnumValueListStringEmpty(self, p):
+ """
+ EnumValueListString :
+ """
+ p[0] = []
+
+ def p_CallbackRest(self, p):
+ """
+ CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+ p[0] = IDLCallbackType(self.getLocation(p, 1), self.globalScope(),
+ identifier, p[3], p[5])
+
+ def p_ExceptionMembers(self, p):
+ """
+ ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
+ |
+ """
+ pass
+
+ def p_Typedef(self, p):
+ """
+ Typedef : TYPEDEF Type IDENTIFIER SEMICOLON
+ """
+ typedef = IDLTypedefType(self.getLocation(p, 1), p[2], p[3])
+ typedef.resolve(self.globalScope())
+ p[0] = typedef
+
+ def p_ImplementsStatement(self, p):
+ """
+ ImplementsStatement : ScopedName IMPLEMENTS 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)
+
+ def p_Const(self, p):
+ """
+ Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ type = p[2]
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
+ value = p[5]
+ p[0] = IDLConst(location, identifier, type, value)
+
+ def p_ConstValueBoolean(self, p):
+ """
+ ConstValue : BooleanLiteral
+ """
+ location = self.getLocation(p, 1)
+ booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean]
+ p[0] = IDLValue(location, booleanType, p[1])
+
+ def p_ConstValueInteger(self, p):
+ """
+ ConstValue : INTEGER
+ """
+ location = self.getLocation(p, 1)
+
+ # We don't know ahead of time what type the integer literal is.
+ # Determine the smallest type it could possibly fit in and use that.
+ integerType = matchIntegerValueToType(p[1])
+ if integerType == None:
+ raise WebIDLError("Integer literal out of range", [location])
+
+ p[0] = IDLValue(location, integerType, p[1])
+
+ def p_ConstValueFloat(self, p):
+ """
+ ConstValue : FLOATLITERAL
+ """
+ location = self.getLocation(p, 1)
+ p[0] = IDLValue(location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1])
+
+ def p_ConstValueString(self, p):
+ """
+ ConstValue : STRING
+ """
+ location = self.getLocation(p, 1)
+ 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
+ """
+ p[0] = True
+
+ def p_BooleanLiteralFalse(self, p):
+ """
+ BooleanLiteral : FALSE
+ """
+ p[0] = False
+
+ def p_AttributeOrOperation(self, p):
+ """
+ AttributeOrOperation : Attribute
+ | Operation
+ """
+ p[0] = p[1]
+
+ def p_AttributeWithQualifier(self, p):
+ """
+ Attribute : Qualifier AttributeRest
+ """
+ static = IDLInterfaceMember.Special.Static in p[1]
+ stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly, static=static,
+ stringifier=stringifier)
+
+ def p_Attribute(self, p):
+ """
+ Attribute : Inherit AttributeRest
+ """
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly, inherit=p[1])
+
+ def p_AttributeRest(self, p):
+ """
+ AttributeRest : ReadOnly ATTRIBUTE Type IDENTIFIER SEMICOLON
+ """
+ location = self.getLocation(p, 2)
+ readonly = p[1]
+ t = p[3]
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 4), p[4])
+ p[0] = (location, identifier, t, readonly)
+
+ def p_ReadOnly(self, p):
+ """
+ ReadOnly : READONLY
+ """
+ p[0] = True
+
+ def p_ReadOnlyEmpty(self, p):
+ """
+ ReadOnly :
+ """
+ p[0] = False
+
+ def p_Inherit(self, p):
+ """
+ Inherit : INHERIT
+ """
+ p[0] = True
+
+ def p_InheritEmpty(self, p):
+ """
+ Inherit :
+ """
+ p[0] = False
+
+ def p_Operation(self, p):
+ """
+ Operation : Qualifiers OperationRest
+ """
+ qualifiers = p[1]
+
+ # Disallow duplicates in the qualifier set
+ if not len(set(qualifiers)) == len(qualifiers):
+ raise WebIDLError("Duplicate qualifiers are not allowed",
+ [self.getLocation(p, 1)])
+
+ static = IDLInterfaceMember.Special.Static in p[1]
+ # If static is there that's all that's allowed. This is disallowed
+ # by the parser, so we can assert here.
+ assert not static or len(qualifiers) == 1
+
+ stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+ # If stringifier is there that's all that's allowed. This is disallowed
+ # by the parser, so we can assert here.
+ assert not stringifier or len(qualifiers) == 1
+
+ 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",
+ [self.getLocation(p, 1)])
+
+ (returnType, identifier, arguments) = p[2]
+
+ assert isinstance(returnType, IDLType)
+
+ specialType = IDLMethod.NamedOrIndexed.Neither
+
+ if getter or deleter:
+ if len(arguments) != 1:
+ raise WebIDLError("%s has wrong number of arguments" %
+ ("getter" if getter else "deleter"),
+ [self.getLocation(p, 2)])
+ argType = arguments[0].type
+ if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
+ specialType = IDLMethod.NamedOrIndexed.Named
+ elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
+ specialType = IDLMethod.NamedOrIndexed.Indexed
+ else:
+ raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
+ ("getter" if getter else "deleter"),
+ [arguments[0].location])
+ if arguments[0].optional or arguments[0].variadic:
+ raise WebIDLError("%s cannot have %s argument" %
+ ("getter" if getter else "deleter",
+ "optional" if arguments[0].optional else "variadic"),
+ [arguments[0].location])
+ if getter:
+ if returnType.isVoid():
+ raise WebIDLError("getter cannot have void return type",
+ [self.getLocation(p, 2)])
+ if setter or creator:
+ if len(arguments) != 2:
+ raise WebIDLError("%s has wrong number of arguments" %
+ ("setter" if setter else "creator"),
+ [self.getLocation(p, 2)])
+ argType = arguments[0].type
+ if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
+ specialType = IDLMethod.NamedOrIndexed.Named
+ 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"),
+ [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"),
+ [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"),
+ [arguments[1].location])
+
+ if stringifier:
+ if len(arguments) != 0:
+ raise WebIDLError("stringifier has wrong number of arguments",
+ [self.getLocation(p, 2)])
+ if not returnType.isDOMString():
+ raise WebIDLError("stringifier must have DOMString return type",
+ [self.getLocation(p, 2)])
+
+ # identifier might be None. This is only permitted for special methods.
+ if not identifier:
+ if not getter and not setter and not creator and \
+ not deleter and not legacycaller and not stringifier:
+ raise WebIDLError("Identifier required for non-special methods",
+ [self.getLocation(p, 2)])
+
+ location = BuiltinLocation("<auto-generated-identifier>")
+ identifier = IDLUnresolvedIdentifier(location, "__%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,
+ deleter=deleter, specialType=specialType,
+ legacycaller=legacycaller, stringifier=stringifier)
+ p[0] = method
+
+ def p_Stringifier(self, p):
+ """
+ Operation : STRINGIFIER SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "__stringifier",
+ allowDoubleUnderscore=True)
+ method = IDLMethod(self.getLocation(p, 1),
+ identifier,
+ returnType=BuiltinTypes[IDLBuiltinType.Types.domstring],
+ arguments=[],
+ 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
+ """
+ p[0] = [IDLInterfaceMember.Special.Static]
+
+ def p_QualifierStringifier(self, p):
+ """
+ Qualifier : STRINGIFIER
+ """
+ p[0] = [IDLInterfaceMember.Special.Stringifier]
+
+ def p_Qualifiers(self, p):
+ """
+ Qualifiers : Qualifier
+ | Specials
+ """
+ p[0] = p[1]
+
+ def p_Specials(self, p):
+ """
+ Specials : Special Specials
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_SpecialsEmpty(self, p):
+ """
+ Specials :
+ """
+ p[0] = []
+
+ def p_SpecialGetter(self, p):
+ """
+ Special : GETTER
+ """
+ p[0] = IDLMethod.Special.Getter
+
+ def p_SpecialSetter(self, p):
+ """
+ Special : SETTER
+ """
+ p[0] = IDLMethod.Special.Setter
+
+ def p_SpecialCreator(self, p):
+ """
+ Special : CREATOR
+ """
+ p[0] = IDLMethod.Special.Creator
+
+ def p_SpecialDeleter(self, p):
+ """
+ Special : DELETER
+ """
+ p[0] = IDLMethod.Special.Deleter
+
+ def p_SpecialLegacyCaller(self, p):
+ """
+ Special : LEGACYCALLER
+ """
+ p[0] = IDLMethod.Special.LegacyCaller
+
+ def p_OperationRest(self, p):
+ """
+ OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON
+ """
+ p[0] = (p[1], p[2], p[4])
+
+ def p_OptionalIdentifier(self, p):
+ """
+ OptionalIdentifier : IDENTIFIER
+ """
+ p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ def p_OptionalIdentifierEmpty(self, p):
+ """
+ OptionalIdentifier :
+ """
+ pass
+
+ def p_ArgumentList(self, p):
+ """
+ ArgumentList : Argument Arguments
+ """
+ p[0] = [p[1]] if p[1] else []
+ p[0].extend(p[2])
+
+ def p_ArgumentListEmpty(self, p):
+ """
+ ArgumentList :
+ """
+ p[0] = []
+
+ def p_Arguments(self, p):
+ """
+ Arguments : COMMA Argument Arguments
+ """
+ p[0] = [p[2]] if p[2] else []
+ p[0].extend(p[3])
+
+ def p_ArgumentsEmpty(self, p):
+ """
+ Arguments :
+ """
+ p[0] = []
+
+ def p_Argument(self, p):
+ """
+ Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName Default
+ """
+ t = p[3]
+ assert isinstance(t, IDLType)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5])
+
+ optional = p[2]
+ variadic = p[4]
+ defaultValue = p[6]
+
+ 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, 5), identifier, t, optional, defaultValue, variadic)
+ p[0].addExtendedAttributes(p[1])
+
+ def p_ArgumentName(self, p):
+ """
+ ArgumentName : IDENTIFIER
+ | ATTRIBUTE
+ | CALLBACK
+ | CONST
+ | CREATOR
+ | DELETER
+ | DICTIONARY
+ | ENUM
+ | EXCEPTION
+ | GETTER
+ | IMPLEMENTS
+ | INHERIT
+ | INTERFACE
+ | LEGACYCALLER
+ | PARTIAL
+ | SERIALIZER
+ | SETTER
+ | STATIC
+ | STRINGIFIER
+ | JSONIFIER
+ | 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_Ellipsis(self, p):
+ """
+ Ellipsis : ELLIPSIS
+ """
+ p[0] = True
+
+ def p_EllipsisEmpty(self, p):
+ """
+ Ellipsis :
+ """
+ p[0] = False
+
+ def p_ExceptionMember(self, p):
+ """
+ ExceptionMember : Const
+ | ExceptionField
+ """
+ pass
+
+ def p_ExceptionField(self, p):
+ """
+ ExceptionField : Type IDENTIFIER SEMICOLON
+ """
+ pass
+
+ def p_ExtendedAttributeList(self, p):
+ """
+ ExtendedAttributeList : LBRACKET ExtendedAttribute ExtendedAttributes RBRACKET
+ """
+ p[0] = [p[2]]
+ if p[3]:
+ p[0].extend(p[3])
+
+ def p_ExtendedAttributeListEmpty(self, p):
+ """
+ ExtendedAttributeList :
+ """
+ p[0] = []
+
+ def p_ExtendedAttribute(self, p):
+ """
+ ExtendedAttribute : ExtendedAttributeNoArgs
+ | ExtendedAttributeArgList
+ | ExtendedAttributeIdent
+ | ExtendedAttributeNamedArgList
+ | ExtendedAttributeIdentList
+ """
+ p[0] = IDLExtendedAttribute(self.getLocation(p, 1), p[1])
+
+ def p_ExtendedAttributeEmpty(self, p):
+ """
+ ExtendedAttribute :
+ """
+ pass
+
+ def p_ExtendedAttributes(self, p):
+ """
+ ExtendedAttributes : COMMA ExtendedAttribute ExtendedAttributes
+ """
+ p[0] = [p[2]] if p[2] else []
+ p[0].extend(p[3])
+
+ def p_ExtendedAttributesEmpty(self, p):
+ """
+ ExtendedAttributes :
+ """
+ p[0] = []
+
+ def p_Other(self, p):
+ """
+ Other : INTEGER
+ | FLOATLITERAL
+ | IDENTIFIER
+ | STRING
+ | OTHER
+ | ELLIPSIS
+ | COLON
+ | SCOPE
+ | SEMICOLON
+ | LT
+ | EQUALS
+ | GT
+ | QUESTIONMARK
+ | DATE
+ | DOMSTRING
+ | BYTESTRING
+ | SCALARVALUESTRING
+ | ANY
+ | ATTRIBUTE
+ | BOOLEAN
+ | BYTE
+ | LEGACYCALLER
+ | CONST
+ | CREATOR
+ | DELETER
+ | DOUBLE
+ | EXCEPTION
+ | FALSE
+ | FLOAT
+ | GETTER
+ | IMPLEMENTS
+ | INHERIT
+ | INTERFACE
+ | LONG
+ | MODULE
+ | NULL
+ | OBJECT
+ | OCTET
+ | OPTIONAL
+ | SEQUENCE
+ | MOZMAP
+ | SETTER
+ | SHORT
+ | STATIC
+ | STRINGIFIER
+ | JSONIFIER
+ | TRUE
+ | TYPEDEF
+ | UNSIGNED
+ | VOID
+ """
+ pass
+
+ def p_OtherOrComma(self, p):
+ """
+ OtherOrComma : Other
+ | COMMA
+ """
+ pass
+
+ def p_TypeSingleType(self, p):
+ """
+ Type : SingleType
+ """
+ p[0] = p[1]
+
+ def p_TypeUnionType(self, p):
+ """
+ Type : UnionType TypeSuffix
+ """
+ p[0] = self.handleModifiers(p[1], p[2])
+
+ def p_SingleTypeNonAnyType(self, p):
+ """
+ SingleType : NonAnyType
+ """
+ p[0] = p[1]
+
+ def p_SingleTypeAnyType(self, p):
+ """
+ SingleType : ANY TypeSuffixStartingWithArray
+ """
+ p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.any], p[2])
+
+ def p_UnionType(self, p):
+ """
+ UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN
+ """
+ types = [p[2], p[4]]
+ types.extend(p[5])
+ p[0] = IDLUnionType(self.getLocation(p, 1), types)
+
+ def p_UnionMemberTypeNonAnyType(self, p):
+ """
+ UnionMemberType : NonAnyType
+ """
+ p[0] = p[1]
+
+ def p_UnionMemberTypeArrayOfAny(self, p):
+ """
+ UnionMemberTypeArrayOfAny : ANY LBRACKET RBRACKET
+ """
+ p[0] = IDLArrayType(self.getLocation(p, 2),
+ BuiltinTypes[IDLBuiltinType.Types.any])
+
+ def p_UnionMemberType(self, p):
+ """
+ UnionMemberType : UnionType TypeSuffix
+ | UnionMemberTypeArrayOfAny TypeSuffix
+ """
+ p[0] = self.handleModifiers(p[1], p[2])
+
+ def p_UnionMemberTypes(self, p):
+ """
+ UnionMemberTypes : OR UnionMemberType UnionMemberTypes
+ """
+ p[0] = [p[2]]
+ p[0].extend(p[3])
+
+ def p_UnionMemberTypesEmpty(self, p):
+ """
+ UnionMemberTypes :
+ """
+ p[0] = []
+
+ def p_NonAnyType(self, p):
+ """
+ NonAnyType : PrimitiveOrStringType TypeSuffix
+ | ARRAYBUFFER TypeSuffix
+ | OBJECT TypeSuffix
+ """
+ if p[1] == "object":
+ type = BuiltinTypes[IDLBuiltinType.Types.object]
+ elif p[1] == "ArrayBuffer":
+ type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
+ else:
+ type = BuiltinTypes[p[1]]
+
+ p[0] = self.handleModifiers(type, p[2])
+
+ def p_NonAnyTypeSequenceType(self, p):
+ """
+ NonAnyType : SEQUENCE LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLSequenceType(self.getLocation(p, 1), innerType)
+ if p[5]:
+ type = IDLNullableType(self.getLocation(p, 5), type)
+ p[0] = type
+
+ # 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):
+ """
+ NonAnyType : PROMISE LT ReturnType GT Null
+ """
+ innerType = p[3]
+ promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
+ type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
+ if p[5]:
+ type = IDLNullableType(self.getLocation(p, 5), type)
+ p[0] = type
+
+ def p_NonAnyTypeMozMapType(self, p):
+ """
+ NonAnyType : MOZMAP LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLMozMapType(self.getLocation(p, 1), innerType)
+ if p[5]:
+ type = IDLNullableType(self.getLocation(p, 5), type)
+ p[0] = type
+
+ def p_NonAnyTypeScopedName(self, p):
+ """
+ NonAnyType : ScopedName TypeSuffix
+ """
+ assert isinstance(p[1], IDLUnresolvedIdentifier)
+
+ if p[1].name == "Promise":
+ raise WebIDLError("Promise used without saying what it's "
+ "parametrized over",
+ [self.getLocation(p, 1)])
+
+ type = None
+
+ try:
+ if self.globalScope()._lookupIdentifier(p[1]):
+ obj = self.globalScope()._lookupIdentifier(p[1])
+ if obj.isType():
+ type = obj
+ else:
+ type = IDLWrapperType(self.getLocation(p, 1), p[1])
+ p[0] = self.handleModifiers(type, p[2])
+ return
+ except:
+ pass
+
+ type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
+ p[0] = self.handleModifiers(type, p[2])
+
+ def p_NonAnyTypeDate(self, p):
+ """
+ NonAnyType : DATE TypeSuffix
+ """
+ p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.date],
+ p[2])
+
+ def p_ConstType(self, p):
+ """
+ ConstType : PrimitiveOrStringType Null
+ """
+ type = BuiltinTypes[p[1]]
+ if p[2]:
+ type = IDLNullableType(self.getLocation(p, 1), type)
+ p[0] = type
+
+ def p_ConstTypeIdentifier(self, p):
+ """
+ ConstType : IDENTIFIER Null
+ """
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
+ if p[2]:
+ type = IDLNullableType(self.getLocation(p, 1), type)
+ p[0] = type
+
+ def p_PrimitiveOrStringTypeUint(self, p):
+ """
+ PrimitiveOrStringType : UnsignedIntegerType
+ """
+ p[0] = p[1]
+
+ def p_PrimitiveOrStringTypeBoolean(self, p):
+ """
+ PrimitiveOrStringType : BOOLEAN
+ """
+ p[0] = IDLBuiltinType.Types.boolean
+
+ def p_PrimitiveOrStringTypeByte(self, p):
+ """
+ PrimitiveOrStringType : BYTE
+ """
+ p[0] = IDLBuiltinType.Types.byte
+
+ def p_PrimitiveOrStringTypeOctet(self, p):
+ """
+ PrimitiveOrStringType : OCTET
+ """
+ p[0] = IDLBuiltinType.Types.octet
+
+ def p_PrimitiveOrStringTypeFloat(self, p):
+ """
+ PrimitiveOrStringType : FLOAT
+ """
+ p[0] = IDLBuiltinType.Types.float
+
+ def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
+ """
+ PrimitiveOrStringType : UNRESTRICTED FLOAT
+ """
+ p[0] = IDLBuiltinType.Types.unrestricted_float
+
+ def p_PrimitiveOrStringTypeDouble(self, p):
+ """
+ PrimitiveOrStringType : DOUBLE
+ """
+ p[0] = IDLBuiltinType.Types.double
+
+ def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
+ """
+ PrimitiveOrStringType : UNRESTRICTED DOUBLE
+ """
+ p[0] = IDLBuiltinType.Types.unrestricted_double
+
+ def p_PrimitiveOrStringTypeDOMString(self, p):
+ """
+ PrimitiveOrStringType : DOMSTRING
+ """
+ p[0] = IDLBuiltinType.Types.domstring
+
+ def p_PrimitiveOrStringTypeBytestring(self, p):
+ """
+ PrimitiveOrStringType : BYTESTRING
+ """
+ p[0] = IDLBuiltinType.Types.bytestring
+
+ def p_PrimitiveOrStringTypeScalarValueString(self, p):
+ """
+ PrimitiveOrStringType : SCALARVALUESTRING
+ """
+ p[0] = IDLBuiltinType.Types.scalarvaluestring
+
+ def p_UnsignedIntegerTypeUnsigned(self, p):
+ """
+ UnsignedIntegerType : UNSIGNED IntegerType
+ """
+ p[0] = p[2] + 1 # Adding one to a given signed integer type
+ # gets you the unsigned type.
+
+ def p_UnsignedIntegerType(self, p):
+ """
+ UnsignedIntegerType : IntegerType
+ """
+ p[0] = p[1]
+
+ def p_IntegerTypeShort(self, p):
+ """
+ IntegerType : SHORT
+ """
+ p[0] = IDLBuiltinType.Types.short
+
+ def p_IntegerTypeLong(self, p):
+ """
+ IntegerType : LONG OptionalLong
+ """
+ if p[2]:
+ p[0] = IDLBuiltinType.Types.long_long
+ else:
+ p[0] = IDLBuiltinType.Types.long
+
+ def p_OptionalLong(self, p):
+ """
+ OptionalLong : LONG
+ """
+ p[0] = True
+
+ def p_OptionalLongEmpty(self, p):
+ """
+ OptionalLong :
+ """
+ p[0] = False
+
+ def p_TypeSuffixBrackets(self, p):
+ """
+ TypeSuffix : LBRACKET RBRACKET TypeSuffix
+ """
+ p[0] = [(IDLMethod.TypeSuffixModifier.Brackets, self.getLocation(p, 1))]
+ p[0].extend(p[3])
+
+ def p_TypeSuffixQMark(self, p):
+ """
+ TypeSuffix : QUESTIONMARK TypeSuffixStartingWithArray
+ """
+ p[0] = [(IDLMethod.TypeSuffixModifier.QMark, self.getLocation(p, 1))]
+ p[0].extend(p[2])
+
+ def p_TypeSuffixEmpty(self, p):
+ """
+ TypeSuffix :
+ """
+ p[0] = []
+
+ def p_TypeSuffixStartingWithArray(self, p):
+ """
+ TypeSuffixStartingWithArray : LBRACKET RBRACKET TypeSuffix
+ """
+ p[0] = [(IDLMethod.TypeSuffixModifier.Brackets, self.getLocation(p, 1))]
+ p[0].extend(p[3])
+
+ def p_TypeSuffixStartingWithArrayEmpty(self, p):
+ """
+ TypeSuffixStartingWithArray :
+ """
+ p[0] = []
+
+ def p_Null(self, p):
+ """
+ Null : QUESTIONMARK
+ |
+ """
+ if len(p) > 1:
+ p[0] = True
+ else:
+ p[0] = False
+
+ def p_ReturnTypeType(self, p):
+ """
+ ReturnType : Type
+ """
+ p[0] = p[1]
+
+ def p_ReturnTypeVoid(self, p):
+ """
+ ReturnType : VOID
+ """
+ p[0] = BuiltinTypes[IDLBuiltinType.Types.void]
+
+ def p_ScopedName(self, p):
+ """
+ ScopedName : AbsoluteScopedName
+ | RelativeScopedName
+ """
+ p[0] = p[1]
+
+ def p_AbsoluteScopedName(self, p):
+ """
+ AbsoluteScopedName : SCOPE IDENTIFIER ScopedNameParts
+ """
+ assert False
+ pass
+
+ def p_RelativeScopedName(self, p):
+ """
+ RelativeScopedName : IDENTIFIER ScopedNameParts
+ """
+ assert not p[2] # Not implemented!
+
+ p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ def p_ScopedNameParts(self, p):
+ """
+ ScopedNameParts : SCOPE IDENTIFIER ScopedNameParts
+ """
+ assert False
+ pass
+
+ def p_ScopedNamePartsEmpty(self, p):
+ """
+ ScopedNameParts :
+ """
+ p[0] = None
+
+ def p_ExtendedAttributeNoArgs(self, p):
+ """
+ ExtendedAttributeNoArgs : IDENTIFIER
+ """
+ p[0] = (p[1],)
+
+ def p_ExtendedAttributeArgList(self, p):
+ """
+ ExtendedAttributeArgList : IDENTIFIER LPAREN ArgumentList RPAREN
+ """
+ p[0] = (p[1], p[3])
+
+ def p_ExtendedAttributeIdent(self, p):
+ """
+ ExtendedAttributeIdent : IDENTIFIER EQUALS STRING
+ | IDENTIFIER EQUALS IDENTIFIER
+ """
+ p[0] = (p[1], p[3])
+
+ def p_ExtendedAttributeNamedArgList(self, p):
+ """
+ ExtendedAttributeNamedArgList : IDENTIFIER EQUALS IDENTIFIER LPAREN ArgumentList RPAREN
+ """
+ p[0] = (p[1], p[3], p[5])
+
+ def p_ExtendedAttributeIdentList(self, p):
+ """
+ ExtendedAttributeIdentList : IDENTIFIER EQUALS LPAREN IdentifierList RPAREN
+ """
+ p[0] = (p[1], p[4])
+
+ def p_IdentifierList(self, p):
+ """
+ IdentifierList : IDENTIFIER Identifiers
+ """
+ idents = list(p[2])
+ idents.insert(0, p[1])
+ p[0] = idents
+
+ def p_IdentifiersList(self, p):
+ """
+ Identifiers : COMMA IDENTIFIER Identifiers
+ """
+ idents = list(p[3])
+ idents.insert(0, p[2])
+ p[0] = idents
+
+ def p_IdentifiersEmpty(self, p):
+ """
+ Identifiers :
+ """
+ p[0] = []
+
+ def p_error(self, p):
+ if not p:
+ raise WebIDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both",
+ [self._filename])
+ else:
+ raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)])
+
+ def __init__(self, outputdir='', lexer=None):
+ Tokenizer.__init__(self, outputdir, lexer)
+
+ logger = SqueakyCleanLogger()
+ self.parser = yacc.yacc(module=self,
+ outputdir=outputdir,
+ tabmodule='webidlyacc',
+ errorlog=logger
+ # 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'
+ )
+ 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")
+ self._installBuiltins(self._globalScope)
+ self._productions = []
+
+ self._filename = "<builtin>"
+ self.lexer.input(Parser._builtins)
+ self._filename = None
+
+ self.parser.parse(lexer=self.lexer,tracking=True)
+
+ def _installBuiltins(self, scope):
+ assert isinstance(scope, IDLScope)
+
+ # xrange omits the last value.
+ for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1):
+ builtin = BuiltinTypes[x]
+ name = builtin.name
+
+ typedef = IDLTypedefType(BuiltinLocation("<builtin type>"), builtin, name)
+ typedef.resolve(scope)
+
+ @ staticmethod
+ def handleModifiers(type, modifiers):
+ for (modifier, modifierLocation) in modifiers:
+ assert modifier == IDLMethod.TypeSuffixModifier.QMark or \
+ modifier == IDLMethod.TypeSuffixModifier.Brackets
+
+ if modifier == IDLMethod.TypeSuffixModifier.QMark:
+ type = IDLNullableType(modifierLocation, type)
+ elif modifier == IDLMethod.TypeSuffixModifier.Brackets:
+ type = IDLArrayType(modifierLocation, type)
+
+ return type
+
+ def parse(self, t, filename=None):
+ self.lexer.input(t)
+
+ #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
+
+ def finish(self):
+ # First, finish all the IDLImplementsStatements. In particular, we
+ # have to make sure we do those before we do 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)]
+ otherStatements = [ p for p in self._productions if
+ not isinstance(p, IDLImplementsStatement)]
+ for production in implementsStatements:
+ production.finish(self.globalScope())
+ for production in otherStatements:
+ production.finish(self.globalScope())
+
+ # Do any post-finish validation we need to do
+ for production in self._productions:
+ production.validate()
+
+ # De-duplicate self._productions, without modifying its order.
+ seen = set()
+ result = []
+ for p in self._productions:
+ if p not in seen:
+ seen.add(p)
+ result.append(p)
+ return result
+
+ def reset(self):
+ return Parser(lexer=self.lexer)
+
+ # Builtin IDL defined by WebIDL
+ _builtins = """
+ typedef unsigned long long DOMTimeStamp;
+ """
+
+def main():
+ # Parse arguments.
+ from optparse import OptionParser
+ usageString = "usage: %prog [options] 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("--verbose-errors", action='store_true', default=False,
+ help="When an error happens, display the Python traceback.")
+ (options, args) = o.parse_args()
+
+ if len(args) < 1:
+ o.error(usageString)
+
+ fileList = args
+ baseDir = os.getcwd()
+
+ # Parse the WebIDL.
+ parser = Parser(options.cachedir)
+ try:
+ for filename in fileList:
+ fullPath = os.path.normpath(os.path.join(baseDir, filename))
+ f = open(fullPath, 'rb')
+ lines = f.readlines()
+ f.close()
+ print fullPath
+ parser.parse(''.join(lines), fullPath)
+ parser.finish()
+ except WebIDLError, e:
+ if options.verbose_errors:
+ traceback.print_exc()
+ else:
+ print e
+
+if __name__ == '__main__':
+ main()
diff --git a/components/script/dom/bindings/codegen/parser/external.patch b/components/script/dom/bindings/codegen/parser/external.patch
new file mode 100644
index 00000000000..9464511a9d0
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/external.patch
@@ -0,0 +1,49 @@
+--- WebIDL.py
++++ WebIDL.py
+@@ -450,44 +450,8 @@ class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
+
+ 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)
+- IDLObjectWithIdentifier.resolve(self, parentScope)
+-
+- def finish(self, scope):
+- pass
+-
+- def validate(self):
+- pass
+-
+- def isExternal(self):
+- return True
+-
+- def isInterface(self):
+- return True
+-
+- def isConsequential(self):
+- return False
+-
+- def addExtendedAttributes(self, attrs):
+- assert len(attrs) == 0
+-
+- def resolve(self, parentScope):
+- pass
+-
+- def getJSImplementation(self):
+- return None
+-
+- def isJSImplemented(self):
+- return False
+-
+- def getNavigatorProperty(self):
+- return None
+-
+- def _getDependentObjects(self):
+- return set()
++ raise WebIDLError("Servo does not support external interfaces.",
++ [self.location])
+
+ class IDLPartialInterface(IDLObject):
+ def __init__(self, location, name, members, nonPartialInterface):
diff --git a/components/script/dom/bindings/codegen/parser/module.patch b/components/script/dom/bindings/codegen/parser/module.patch
new file mode 100644
index 00000000000..977947b4c63
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/module.patch
@@ -0,0 +1,12 @@
+--- WebIDL.py
++++ WebIDL.py
+@@ -3398,6 +3398,9 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
+ self._treatNonCallableAsNull = False
+ self._treatNonObjectAsNull = False
+
++ def module(self):
++ return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding'
++
+ def isCallback(self):
+ return True
+
diff --git a/components/script/dom/bindings/codegen/parser/runtests.py b/components/script/dom/bindings/codegen/parser/runtests.py
new file mode 100644
index 00000000000..98a7d2b81d3
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/runtests.py
@@ -0,0 +1,79 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os, sys
+import glob
+import optparse
+import traceback
+import WebIDL
+
+class TestHarness(object):
+ def __init__(self, test, verbose):
+ self.test = test
+ self.verbose = verbose
+ self.printed_intro = False
+
+ def start(self):
+ if self.verbose:
+ self.maybe_print_intro()
+
+ def finish(self):
+ if self.verbose or self.printed_intro:
+ print "Finished test %s" % self.test
+
+ def maybe_print_intro(self):
+ if not self.printed_intro:
+ print "Starting test %s" % self.test
+ self.printed_intro = True
+
+ def test_pass(self, msg):
+ if self.verbose:
+ print "TEST-PASS | %s" % msg
+
+ def test_fail(self, msg):
+ self.maybe_print_intro()
+ print "TEST-UNEXPECTED-FAIL | %s" % msg
+
+ def ok(self, condition, msg):
+ if condition:
+ self.test_pass(msg)
+ else:
+ self.test_fail(msg)
+
+ def check(self, a, b, msg):
+ if a == b:
+ self.test_pass(msg)
+ else:
+ self.test_fail(msg)
+ print "\tGot %s expected %s" % (a, b)
+
+def run_tests(tests, verbose):
+ testdir = os.path.join(os.path.dirname(__file__), 'tests')
+ if not tests:
+ tests = glob.iglob(os.path.join(testdir, "*.py"))
+ sys.path.append(testdir)
+
+ for test in tests:
+ (testpath, ext) = os.path.splitext(os.path.basename(test))
+ _test = __import__(testpath, globals(), locals(), ['WebIDLTest'])
+
+ harness = TestHarness(test, verbose)
+ harness.start()
+ try:
+ _test.WebIDLTest.__call__(WebIDL.Parser(), harness)
+ except Exception, ex:
+ print "TEST-UNEXPECTED-FAIL | Unhandled exception in test %s: %s" % (testpath, ex)
+ traceback.print_exc()
+ finally:
+ harness.finish()
+
+if __name__ == '__main__':
+ usage = """%prog [OPTIONS] [TESTS]
+ Where TESTS are relative to the tests directory."""
+ parser = optparse.OptionParser(usage=usage)
+ parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
+ help="Don't print passing tests.")
+ options, tests = parser.parse_args()
+
+ run_tests(tests, verbose=options.verbose)
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_any_null.py b/components/script/dom/bindings/codegen/parser/tests/test_any_null.py
new file mode 100644
index 00000000000..e3b690bf6f1
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_any_null.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DoubleNull {
+ attribute any? foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_argument_identifier_conflicts.py b/components/script/dom/bindings/codegen/parser/tests/test_argument_identifier_conflicts.py
new file mode 100644
index 00000000000..eb1f6d3c92e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_argument_identifier_conflicts.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface ArgumentIdentifierConflict {
+ void foo(boolean arg1, boolean arg1);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_argument_novoid.py b/components/script/dom/bindings/codegen/parser/tests/test_argument_novoid.py
new file mode 100644
index 00000000000..ef8c2229aed
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_argument_novoid.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface VoidArgument1 {
+ void foo(void arg2);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_array_of_interface.py b/components/script/dom/bindings/codegen/parser/tests/test_array_of_interface.py
new file mode 100644
index 00000000000..26528984595
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_array_of_interface.py
@@ -0,0 +1,13 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface A {
+ attribute long a;
+ };
+
+ interface B {
+ attribute A[] b;
+ };
+ """);
+ parser.finish()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_arraybuffer.py b/components/script/dom/bindings/codegen/parser/tests/test_arraybuffer.py
new file mode 100644
index 00000000000..5b8e56f86ca
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_arraybuffer.py
@@ -0,0 +1,84 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestArrayBuffer {
+ attribute ArrayBuffer bufferAttr;
+ void bufferMethod(ArrayBuffer arg1, ArrayBuffer? arg2, ArrayBuffer[] arg3, sequence<ArrayBuffer> arg4);
+
+ attribute ArrayBufferView viewAttr;
+ void viewMethod(ArrayBufferView arg1, ArrayBufferView? arg2, ArrayBufferView[] arg3, sequence<ArrayBufferView> arg4);
+
+ attribute Int8Array int8ArrayAttr;
+ void int8ArrayMethod(Int8Array arg1, Int8Array? arg2, Int8Array[] arg3, sequence<Int8Array> arg4);
+
+ attribute Uint8Array uint8ArrayAttr;
+ void uint8ArrayMethod(Uint8Array arg1, Uint8Array? arg2, Uint8Array[] arg3, sequence<Uint8Array> arg4);
+
+ attribute Uint8ClampedArray uint8ClampedArrayAttr;
+ void uint8ClampedArrayMethod(Uint8ClampedArray arg1, Uint8ClampedArray? arg2, Uint8ClampedArray[] arg3, sequence<Uint8ClampedArray> arg4);
+
+ attribute Int16Array int16ArrayAttr;
+ void int16ArrayMethod(Int16Array arg1, Int16Array? arg2, Int16Array[] arg3, sequence<Int16Array> arg4);
+
+ attribute Uint16Array uint16ArrayAttr;
+ void uint16ArrayMethod(Uint16Array arg1, Uint16Array? arg2, Uint16Array[] arg3, sequence<Uint16Array> arg4);
+
+ attribute Int32Array int32ArrayAttr;
+ void int32ArrayMethod(Int32Array arg1, Int32Array? arg2, Int32Array[] arg3, sequence<Int32Array> arg4);
+
+ attribute Uint32Array uint32ArrayAttr;
+ void uint32ArrayMethod(Uint32Array arg1, Uint32Array? arg2, Uint32Array[] arg3, sequence<Uint32Array> arg4);
+
+ attribute Float32Array float32ArrayAttr;
+ void float32ArrayMethod(Float32Array arg1, Float32Array? arg2, Float32Array[] arg3, sequence<Float32Array> arg4);
+
+ attribute Float64Array float64ArrayAttr;
+ void float64ArrayMethod(Float64Array arg1, Float64Array? arg2, Float64Array[] arg3, sequence<Float64Array> arg4);
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[0]
+
+ harness.ok(True, "TestArrayBuffer interface parsed without error")
+ harness.check(len(iface.members), 22, "Interface should have twenty two members")
+
+ members = iface.members
+
+ def checkStuff(attr, method, t):
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Expect an IDLAttribute")
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Expect an IDLMethod")
+
+ harness.check(str(attr.type), t, "Expect an ArrayBuffer type")
+ harness.ok(attr.type.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ (retType, arguments) = method.signatures()[0]
+ harness.ok(retType.isVoid(), "Should have a void return type")
+ harness.check(len(arguments), 4, "Expect 4 arguments")
+
+ harness.check(str(arguments[0].type), t, "Expect an ArrayBuffer type")
+ harness.ok(arguments[0].type.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ harness.check(str(arguments[1].type), t + "OrNull", "Expect an ArrayBuffer type")
+ harness.ok(arguments[1].type.inner.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ harness.check(str(arguments[2].type), t + "Array", "Expect an ArrayBuffer type")
+ harness.ok(arguments[2].type.inner.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ harness.check(str(arguments[3].type), t + "Sequence", "Expect an ArrayBuffer type")
+ harness.ok(arguments[3].type.inner.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+
+ checkStuff(members[0], members[1], "ArrayBuffer")
+ checkStuff(members[2], members[3], "ArrayBufferView")
+ checkStuff(members[4], members[5], "Int8Array")
+ checkStuff(members[6], members[7], "Uint8Array")
+ checkStuff(members[8], members[9], "Uint8ClampedArray")
+ checkStuff(members[10], members[11], "Int16Array")
+ checkStuff(members[12], members[13], "Uint16Array")
+ checkStuff(members[14], members[15], "Int32Array")
+ checkStuff(members[16], members[17], "Uint32Array")
+ checkStuff(members[18], members[19], "Float32Array")
+ checkStuff(members[20], members[21], "Float64Array")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attr.py b/components/script/dom/bindings/codegen/parser/tests/test_attr.py
new file mode 100644
index 00000000000..6b6142b6243
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_attr.py
@@ -0,0 +1,302 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ testData = [("::TestAttr%s::b", "b", "Byte%s", False),
+ ("::TestAttr%s::rb", "rb", "Byte%s", True),
+ ("::TestAttr%s::o", "o", "Octet%s", False),
+ ("::TestAttr%s::ro", "ro", "Octet%s", True),
+ ("::TestAttr%s::s", "s", "Short%s", False),
+ ("::TestAttr%s::rs", "rs", "Short%s", True),
+ ("::TestAttr%s::us", "us", "UnsignedShort%s", False),
+ ("::TestAttr%s::rus", "rus", "UnsignedShort%s", True),
+ ("::TestAttr%s::l", "l", "Long%s", False),
+ ("::TestAttr%s::rl", "rl", "Long%s", True),
+ ("::TestAttr%s::ul", "ul", "UnsignedLong%s", False),
+ ("::TestAttr%s::rul", "rul", "UnsignedLong%s", True),
+ ("::TestAttr%s::ll", "ll", "LongLong%s", False),
+ ("::TestAttr%s::rll", "rll", "LongLong%s", True),
+ ("::TestAttr%s::ull", "ull", "UnsignedLongLong%s", False),
+ ("::TestAttr%s::rull", "rull", "UnsignedLongLong%s", True),
+ ("::TestAttr%s::str", "str", "String%s", False),
+ ("::TestAttr%s::rstr", "rstr", "String%s", True),
+ ("::TestAttr%s::obj", "obj", "Object%s", False),
+ ("::TestAttr%s::robj", "robj", "Object%s", True),
+ ("::TestAttr%s::object", "object", "Object%s", False),
+ ("::TestAttr%s::f", "f", "Float%s", False),
+ ("::TestAttr%s::rf", "rf", "Float%s", True)]
+
+ parser.parse("""
+ interface TestAttr {
+ attribute byte b;
+ readonly attribute byte rb;
+ attribute octet o;
+ readonly attribute octet ro;
+ attribute short s;
+ readonly attribute short rs;
+ attribute unsigned short us;
+ readonly attribute unsigned short rus;
+ attribute long l;
+ readonly attribute long rl;
+ attribute unsigned long ul;
+ readonly attribute unsigned long rul;
+ attribute long long ll;
+ readonly attribute long long rll;
+ attribute unsigned long long ull;
+ readonly attribute unsigned long long rull;
+ attribute DOMString str;
+ readonly attribute DOMString rstr;
+ attribute object obj;
+ readonly attribute object robj;
+ attribute object _object;
+ attribute float f;
+ readonly attribute float rf;
+ };
+
+ interface TestAttrNullable {
+ attribute byte? b;
+ readonly attribute byte? rb;
+ attribute octet? o;
+ readonly attribute octet? ro;
+ attribute short? s;
+ readonly attribute short? rs;
+ attribute unsigned short? us;
+ readonly attribute unsigned short? rus;
+ attribute long? l;
+ readonly attribute long? rl;
+ attribute unsigned long? ul;
+ readonly attribute unsigned long? rul;
+ attribute long long? ll;
+ readonly attribute long long? rll;
+ attribute unsigned long long? ull;
+ readonly attribute unsigned long long? rull;
+ attribute DOMString? str;
+ readonly attribute DOMString? rstr;
+ attribute object? obj;
+ readonly attribute object? robj;
+ attribute object? _object;
+ attribute float? f;
+ readonly attribute float? rf;
+ };
+
+ interface TestAttrArray {
+ attribute byte[] b;
+ readonly attribute byte[] rb;
+ attribute octet[] o;
+ readonly attribute octet[] ro;
+ attribute short[] s;
+ readonly attribute short[] rs;
+ attribute unsigned short[] us;
+ readonly attribute unsigned short[] rus;
+ attribute long[] l;
+ readonly attribute long[] rl;
+ attribute unsigned long[] ul;
+ readonly attribute unsigned long[] rul;
+ attribute long long[] ll;
+ readonly attribute long long[] rll;
+ attribute unsigned long long[] ull;
+ readonly attribute unsigned long long[] rull;
+ attribute DOMString[] str;
+ readonly attribute DOMString[] rstr;
+ attribute object[] obj;
+ readonly attribute object[] robj;
+ attribute object[] _object;
+ attribute float[] f;
+ readonly attribute float[] rf;
+ };
+
+ interface TestAttrNullableArray {
+ attribute byte[]? b;
+ readonly attribute byte[]? rb;
+ attribute octet[]? o;
+ readonly attribute octet[]? ro;
+ attribute short[]? s;
+ readonly attribute short[]? rs;
+ attribute unsigned short[]? us;
+ readonly attribute unsigned short[]? rus;
+ attribute long[]? l;
+ readonly attribute long[]? rl;
+ attribute unsigned long[]? ul;
+ readonly attribute unsigned long[]? rul;
+ attribute long long[]? ll;
+ readonly attribute long long[]? rll;
+ attribute unsigned long long[]? ull;
+ readonly attribute unsigned long long[]? rull;
+ attribute DOMString[]? str;
+ readonly attribute DOMString[]? rstr;
+ attribute object[]? obj;
+ readonly attribute object[]? robj;
+ attribute object[]? _object;
+ attribute float[]? f;
+ readonly attribute float[]? rf;
+ };
+
+ interface TestAttrArrayOfNullableTypes {
+ attribute byte?[] b;
+ readonly attribute byte?[] rb;
+ attribute octet?[] o;
+ readonly attribute octet?[] ro;
+ attribute short?[] s;
+ readonly attribute short?[] rs;
+ attribute unsigned short?[] us;
+ readonly attribute unsigned short?[] rus;
+ attribute long?[] l;
+ readonly attribute long?[] rl;
+ attribute unsigned long?[] ul;
+ readonly attribute unsigned long?[] rul;
+ attribute long long?[] ll;
+ readonly attribute long long?[] rll;
+ attribute unsigned long long?[] ull;
+ readonly attribute unsigned long long?[] rull;
+ attribute DOMString?[] str;
+ readonly attribute DOMString?[] rstr;
+ attribute object?[] obj;
+ readonly attribute object?[] robj;
+ attribute object?[] _object;
+ attribute float?[] f;
+ readonly attribute float?[] rf;
+ };
+
+ interface TestAttrNullableArrayOfNullableTypes {
+ attribute byte?[]? b;
+ readonly attribute byte?[]? rb;
+ attribute octet?[]? o;
+ readonly attribute octet?[]? ro;
+ attribute short?[]? s;
+ readonly attribute short?[]? rs;
+ attribute unsigned short?[]? us;
+ readonly attribute unsigned short?[]? rus;
+ attribute long?[]? l;
+ readonly attribute long?[]? rl;
+ attribute unsigned long?[]? ul;
+ readonly attribute unsigned long?[]? rul;
+ attribute long long?[]? ll;
+ readonly attribute long long?[]? rll;
+ attribute unsigned long long?[]? ull;
+ readonly attribute unsigned long long?[]? rull;
+ attribute DOMString?[]? str;
+ readonly attribute DOMString?[]? rstr;
+ attribute object?[]? obj;
+ readonly attribute object?[]? robj;
+ attribute object?[]? _object;
+ attribute float?[]? f;
+ readonly attribute float?[]? rf;
+ };
+ """)
+
+ results = parser.finish()
+
+ def checkAttr(attr, QName, name, type, readonly):
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Attr is an Attr")
+ 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(), QName, "Attr has the right QName")
+ harness.check(attr.identifier.name, name, "Attr has the right name")
+ harness.check(str(attr.type), type, "Attr has the right type")
+ harness.check(attr.readonly, readonly, "Attr's readonly state is correct")
+
+ harness.ok(True, "TestAttr interface parsed without error.")
+ harness.check(len(results), 6, "Should be six productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttr", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttr", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "", name, type % "", readonly)
+
+ iface = results[1]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrNullable", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrNullable", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "Nullable", name, type % "OrNull", readonly)
+
+ iface = results[2]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrArray", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrArray", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "Array", name, type % "Array", readonly)
+
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrNullableArray", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrNullableArray", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "NullableArray", name, type % "ArrayOrNull", readonly)
+
+ iface = results[4]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrArrayOfNullableTypes", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrArrayOfNullableTypes", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "ArrayOfNullableTypes", name, type % "OrNullArray", readonly)
+
+ iface = results[5]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrNullableArrayOfNullableTypes", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrNullableArrayOfNullableTypes", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "NullableArrayOfNullableTypes", name, type % "OrNullArrayOrNull", readonly)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SetterInfallible] readonly attribute boolean foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterInfallible] on readonly attributes")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attr_sequence_type.py b/components/script/dom/bindings/codegen/parser/tests/test_attr_sequence_type.py
new file mode 100644
index 00000000000..fb1b97812bc
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_attr_sequence_type.py
@@ -0,0 +1,67 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrSequenceType {
+ attribute sequence<object> foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Attribute type must not be a sequence type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrUnionWithSequenceType {
+ attribute (sequence<object> or DOMString) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union with a sequence member type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrNullableUnionWithSequenceType {
+ attribute (sequence<object>? or DOMString) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union with a nullable sequence "
+ "member type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrUnionWithUnionWithSequenceType {
+ attribute ((sequence<object> or DOMString) or AttrUnionWithUnionWithSequenceType) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union type with a union member "
+ "type that has a sequence member type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_builtin_filename.py b/components/script/dom/bindings/codegen/parser/tests/test_builtin_filename.py
new file mode 100644
index 00000000000..631e52eba0b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_builtin_filename.py
@@ -0,0 +1,11 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Test {
+ attribute long b;
+ };
+ """);
+
+ attr = parser.finish()[0].members[0]
+ harness.check(attr.type.filename(), '<builtin>', 'Filename on builtin type')
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_builtins.py b/components/script/dom/bindings/codegen/parser/tests/test_builtins.py
new file mode 100644
index 00000000000..f8563fc2d9b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_builtins.py
@@ -0,0 +1,41 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestBuiltins {
+ attribute boolean b;
+ attribute byte s8;
+ attribute octet u8;
+ attribute short s16;
+ attribute unsigned short u16;
+ attribute long s32;
+ attribute unsigned long u32;
+ attribute long long s64;
+ attribute unsigned long long u64;
+ attribute DOMTimeStamp ts;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestBuiltins interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::TestBuiltins", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestBuiltins", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 10, "Should be one production")
+
+ names = ["b", "s8", "u8", "s16", "u16", "s32", "u32", "s64", "u64", "ts"]
+ types = ["Boolean", "Byte", "Octet", "Short", "UnsignedShort", "Long", "UnsignedLong", "LongLong", "UnsignedLongLong", "UnsignedLongLong"]
+ for i in range(10):
+ attr = members[i]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestBuiltins::" + names[i], "Attr has correct QName")
+ harness.check(attr.identifier.name, names[i], "Attr has correct name")
+ harness.check(str(attr.type), types[i], "Attr type is the correct name")
+ harness.ok(attr.type.isPrimitive(), "Should be a primitive type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_callback.py b/components/script/dom/bindings/codegen/parser/tests/test_callback.py
new file mode 100644
index 00000000000..267d27dc087
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_callback.py
@@ -0,0 +1,34 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestCallback {
+ attribute CallbackType? listener;
+ };
+
+ callback CallbackType = boolean (unsigned long arg);
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestCallback interface parsed without error.")
+ harness.check(len(results), 2, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestCallback", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestCallback", "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(), "::TestCallback::listener", "Attr has the right QName")
+ harness.check(attr.identifier.name, "listener", "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")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_callback_interface.py b/components/script/dom/bindings/codegen/parser/tests/test_callback_interface.py
new file mode 100644
index 00000000000..80896ca1edb
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_callback_interface.py
@@ -0,0 +1,47 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ callback interface TestCallbackInterface {
+ attribute boolean bool;
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[0]
+
+ harness.ok(iface.isCallback(), "Interface should be a callback")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ callback interface TestCallbackInterface : TestInterface {
+ attribute boolean bool;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow non-callback parent of callback interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface : TestCallbackInterface {
+ };
+ callback interface TestCallbackInterface {
+ attribute boolean bool;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow callback parent of non-callback interface")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_const.py b/components/script/dom/bindings/codegen/parser/tests/test_const.py
new file mode 100644
index 00000000000..12f411363fb
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_const.py
@@ -0,0 +1,64 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestConsts {
+ const byte zero = 0;
+ const byte b = -1;
+ const octet o = 2;
+ const short s = -3;
+ const unsigned short us = 0x4;
+ const long l = -0X5;
+ const unsigned long ul = 6;
+ const unsigned long long ull = 7;
+ 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;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestConsts interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestConsts", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestConsts", "Interface has the right name")
+ harness.check(len(iface.members), 14, "Expect 14 members")
+
+ consts = iface.members
+
+ def checkConst(const, QName, name, type, value):
+ harness.ok(isinstance(const, WebIDL.IDLConst),
+ "Should be an IDLConst")
+ harness.ok(const.isConst(), "Const is a const")
+ harness.ok(not const.isAttr(), "Const is not an attr")
+ harness.ok(not const.isMethod(), "Const is not a method")
+ harness.check(const.identifier.QName(), QName, "Const has the right QName")
+ harness.check(const.identifier.name, name, "Const has the right name")
+ harness.check(str(const.type), type, "Const has the right type")
+ harness.ok(const.type.isPrimitive(), "All consts should be primitive")
+ harness.check(str(const.value.type), str(const.type),
+ "Const's value has the same type as the type")
+ harness.check(const.value.value, value, "Const value has the right value.")
+
+ checkConst(consts[0], "::TestConsts::zero", "zero", "Byte", 0)
+ checkConst(consts[1], "::TestConsts::b", "b", "Byte", -1)
+ checkConst(consts[2], "::TestConsts::o", "o", "Octet", 2)
+ checkConst(consts[3], "::TestConsts::s", "s", "Short", -3)
+ checkConst(consts[4], "::TestConsts::us", "us", "UnsignedShort", 4)
+ checkConst(consts[5], "::TestConsts::l", "l", "Long", -5)
+ checkConst(consts[6], "::TestConsts::ul", "ul", "UnsignedLong", 6)
+ checkConst(consts[7], "::TestConsts::ull", "ull", "UnsignedLongLong", 7)
+ checkConst(consts[8], "::TestConsts::ll", "ll", "LongLong", -8)
+ checkConst(consts[9], "::TestConsts::t", "t", "Boolean", True)
+ checkConst(consts[10], "::TestConsts::f", "f", "Boolean", False)
+ checkConst(consts[11], "::TestConsts::n", "n", "BooleanOrNull", None)
+ checkConst(consts[12], "::TestConsts::nt", "nt", "BooleanOrNull", True)
+ checkConst(consts[13], "::TestConsts::nf", "nf", "BooleanOrNull", False)
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
new file mode 100644
index 00000000000..6ec1be1871b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
@@ -0,0 +1,75 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ def checkArgument(argument, QName, name, type, optional, variadic):
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), QName, "Argument has the right QName")
+ harness.check(argument.identifier.name, name, "Argument has the right name")
+ harness.check(str(argument.type), type, "Argument has the right return type")
+ harness.check(argument.optional, optional, "Argument has the right optional value")
+ 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,
+ deleter=False, legacycaller=False, stringifier=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.ok(method.isMethod(), "Method is a method")
+ harness.ok(not method.isAttr(), "Method is not an attr")
+ harness.ok(not method.isConst(), "Method is not a const")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ 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(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
+
+ sigpairs = zip(method.signatures(), signatures)
+ for (gotSignature, expectedSignature) in sigpairs:
+ (gotRetType, gotArgs) = gotSignature
+ (expectedRetType, expectedArgs) = expectedSignature
+
+ harness.check(str(gotRetType), expectedRetType,
+ "Method has the expected return type.")
+
+ for i in range(0, len(gotArgs)):
+ (QName, name, type, optional, variadic) = expectedArgs[i]
+ checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+
+ parser.parse("""
+ [Constructor]
+ interface TestConstructorNoArgs {
+ };
+
+ [Constructor(DOMString name)]
+ interface TestConstructorWithArgs {
+ };
+
+ [Constructor(object foo), Constructor(boolean bar)]
+ interface TestConstructorOverloads {
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 3, "Should be two productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], 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)])])
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
new file mode 100644
index 00000000000..192c5f6f97b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py
@@ -0,0 +1,28 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ [Constructor, NoInterfaceObject]
+ interface TestConstructorNoInterfaceObject {
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ [NoInterfaceObject, Constructor]
+ interface TestConstructorNoInterfaceObject {
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_deduplicate.py b/components/script/dom/bindings/codegen/parser/tests/test_deduplicate.py
new file mode 100644
index 00000000000..6249d36fb8f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_deduplicate.py
@@ -0,0 +1,15 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Foo;
+ interface Bar;
+ interface Foo;
+ """);
+
+ results = parser.finish()
+
+ # There should be no duplicate interfaces in the result.
+ expectedNames = sorted(['Foo', 'Bar'])
+ actualNames = sorted(map(lambda iface: iface.identifier.name, results))
+ harness.check(actualNames, expectedNames, "Parser shouldn't output duplicate names.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py b/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py
new file mode 100644
index 00000000000..9ae9eb2b66f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py
@@ -0,0 +1,198 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict2 : Dict1 {
+ long child = 5;
+ Dict1 aaandAnother;
+ };
+ dictionary Dict1 {
+ long parent;
+ double otherParent;
+ };
+ """)
+ results = parser.finish()
+
+ dict1 = results[1];
+ dict2 = results[0];
+
+ harness.check(len(dict1.members), 2, "Dict1 has two members")
+ harness.check(len(dict2.members), 2, "Dict2 has four members")
+
+ harness.check(dict1.members[0].identifier.name, "otherParent",
+ "'o' comes before 'p'")
+ harness.check(dict1.members[1].identifier.name, "parent",
+ "'o' really comes before 'p'")
+ harness.check(dict2.members[0].identifier.name, "aaandAnother",
+ "'a' comes before 'c'")
+ harness.check(dict2.members[1].identifier.name, "child",
+ "'a' really comes before 'c'")
+
+ # Now reset our parser
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Dict {
+ long prop = 5;
+ long prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow name duplication in a dictionary")
+
+ # Now reset our parser again
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Dict1 : Dict2 {
+ long prop = 5;
+ };
+ dictionary Dict2 : Dict3 {
+ long prop2;
+ };
+ dictionary Dict3 {
+ double prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow name duplication in a dictionary and "
+ "its ancestor")
+
+ # More reset
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Iface {};
+ dictionary Dict : Iface {
+ long prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow non-dictionary parents for dictionaries")
+
+ # Even more reset
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A : B {};
+ dictionary B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles in dictionary inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ [TreatNullAs=EmptyString] DOMString foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow [TreatNullAs] on dictionary members");
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ [TreatUndefinedAs=EmptyString] DOMString foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow [TreatUndefinedAs] on dictionary members");
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Trailing dictionary arg must be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg1, optional long arg2);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg followed by optional arg must be optional")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg1, long arg2);
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Dictionary arg followed by required arg can be required")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional A? arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg must not be nullable")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo((A or long)? arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg must not be in a nullable union")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py
new file mode 100644
index 00000000000..86847800631
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py
@@ -0,0 +1,150 @@
+def firstArgType(method):
+ return method.signatures()[0][1][0].type
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict {
+ };
+ callback interface Foo {
+ };
+ interface Bar {
+ // Bit of a pain to get things that have dictionary types
+ void passDict(optional Dict arg);
+ void passFoo(Foo arg);
+ void passNullableUnion((object? or DOMString) arg);
+ void passNullable(Foo? arg);
+ };
+ """)
+ results = parser.finish()
+
+ iface = results[2]
+ harness.ok(iface.isInterface(), "Should have interface")
+ dictMethod = iface.members[0]
+ ifaceMethod = iface.members[1]
+ nullableUnionMethod = iface.members[2]
+ nullableIfaceMethod = iface.members[3]
+
+ dictType = firstArgType(dictMethod)
+ ifaceType = firstArgType(ifaceMethod)
+
+ harness.ok(dictType.isDictionary(), "Should have dictionary type");
+ harness.ok(ifaceType.isInterface(), "Should have interface type");
+ harness.ok(ifaceType.isCallbackInterface(), "Should have callback interface type");
+
+ harness.ok(not dictType.isDistinguishableFrom(ifaceType),
+ "Dictionary not distinguishable from callback interface")
+ harness.ok(not ifaceType.isDistinguishableFrom(dictType),
+ "Callback interface not distinguishable from dictionary")
+
+ nullableUnionType = firstArgType(nullableUnionMethod)
+ nullableIfaceType = firstArgType(nullableIfaceMethod)
+
+ harness.ok(nullableUnionType.isUnion(), "Should have union type");
+ harness.ok(nullableIfaceType.isInterface(), "Should have interface type");
+ harness.ok(nullableIfaceType.nullable(), "Should have nullable type");
+
+ harness.ok(not nullableUnionType.isDistinguishableFrom(nullableIfaceType),
+ "Nullable type not distinguishable from union with nullable "
+ "member type")
+ harness.ok(not nullableIfaceType.isDistinguishableFrom(nullableUnionType),
+ "Union with nullable member type not distinguishable from "
+ "nullable type")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestIface {
+ 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);
+ void passArrayBuffer(ArrayBufferView arg);
+ };
+
+ interface Kid : Parent {};
+ interface Parent : Grandparent {};
+ interface Grandparent {};
+ interface Implemented : ImplementedParent {};
+ Parent implements Implemented;
+ interface ImplementedParent {};
+ interface Unrelated1 {};
+ interface Unrelated2 {};
+ """)
+ results = parser.finish()
+
+ iface = results[0]
+ harness.ok(iface.isInterface(), "Should have interface")
+ argTypes = [firstArgType(method) for method in iface.members]
+ unrelatedTypes = [firstArgType(method) for method in iface.members[-3:]]
+
+ for type1 in argTypes:
+ for type2 in argTypes:
+ distinguishable = (type1 is not type2 and
+ (type1 in unrelatedTypes or
+ type2 in unrelatedTypes))
+
+ harness.check(type1.isDistinguishableFrom(type2),
+ distinguishable,
+ "Type %s should %sbe distinguishable from type %s" %
+ (type1, "" if distinguishable else "not ", type2))
+ harness.check(type2.isDistinguishableFrom(type1),
+ distinguishable,
+ "Type %s should %sbe distinguishable from type %s" %
+ (type2, "" if distinguishable else "not ", type1))
+
+ parser = parser.reset()
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(long arg1, Dummy arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[1].members), 1,
+ "Should look like we have one method")
+ harness.check(len(results[1].members[0].signatures()), 4,
+ "Should have four signatures")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(any arg1, Dummy arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should throw when args before the distinguishing arg are not "
+ "all the same type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(any arg1, DOMString arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should throw when there is no distinguishing index")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_double_null.py b/components/script/dom/bindings/codegen/parser/tests/test_double_null.py
new file mode 100644
index 00000000000..700c7eade00
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_double_null.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DoubleNull {
+ attribute byte?? foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
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
new file mode 100644
index 00000000000..799f2e0e0ed
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py
@@ -0,0 +1,84 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers1 {
+ getter getter byte foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers2 {
+ setter setter 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 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);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers5 {
+ getter deleter getter byte foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ 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_enum.py b/components/script/dom/bindings/codegen/parser/tests/test_empty_enum.py
new file mode 100644
index 00000000000..ee0079f06da
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_empty_enum.py
@@ -0,0 +1,14 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum TestEmptyEnum {
+ };
+ """)
+
+ harness.ok(False, "Should have thrown!")
+ except:
+ harness.ok(True, "Parsing TestEmptyEnum enum should fail")
+
+ results = parser.finish()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_enum.py b/components/script/dom/bindings/codegen/parser/tests/test_enum.py
new file mode 100644
index 00000000000..69a6932062d
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_enum.py
@@ -0,0 +1,81 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ enum TestEnum {
+ "",
+ "foo",
+ "bar"
+ };
+
+ interface TestEnumInterface {
+ TestEnum doFoo(boolean arg);
+ readonly attribute TestEnum foo;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestEnumInterfaces interface parsed without error.")
+ harness.check(len(results), 2, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLEnum),
+ "Should be an IDLEnum")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ enum = results[0]
+ harness.check(enum.identifier.QName(), "::TestEnum", "Enum has the right QName")
+ harness.check(enum.identifier.name, "TestEnum", "Enum has the right name")
+ harness.check(enum.values(), ["", "foo", "bar"], "Enum has the right values")
+
+ iface = results[1]
+
+ harness.check(iface.identifier.QName(), "::TestEnumInterface", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestEnumInterface", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 2, "Should be one production")
+ harness.ok(isinstance(members[0], WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ method = members[0]
+ harness.check(method.identifier.QName(), "::TestEnumInterface::doFoo",
+ "Method has correct QName")
+ harness.check(method.identifier.name, "doFoo", "Method has correct name")
+
+ signatures = method.signatures()
+ harness.check(len(signatures), 1, "Expect one signature")
+
+ (returnType, arguments) = signatures[0]
+ harness.check(str(returnType), "TestEnum (Wrapper)", "Method type is the correct name")
+ harness.check(len(arguments), 1, "Method has the right number of arguments")
+ arg = arguments[0]
+ harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument")
+ harness.check(str(arg.type), "Boolean", "Argument has the right type")
+
+ attr = members[1]
+ harness.check(attr.identifier.QName(), "::TestEnumInterface::foo",
+ "Attr has correct QName")
+ harness.check(attr.identifier.name, "foo", "Attr has correct name")
+
+ harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name")
+
+ # Now reset our parser
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ enum Enum {
+ "a",
+ "b",
+ "c"
+ };
+ interface TestInterface {
+ void foo(optional Enum e = "d");
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow a bogus default value for an enum")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_enum_duplicate_values.py b/components/script/dom/bindings/codegen/parser/tests/test_enum_duplicate_values.py
new file mode 100644
index 00000000000..51205d209e7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_enum_duplicate_values.py
@@ -0,0 +1,13 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum TestEnumDuplicateValue {
+ "",
+ ""
+ };
+ """)
+ harness.ok(False, "Should have thrown!")
+ except:
+ harness.ok(True, "Enum TestEnumDuplicateValue should throw")
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
new file mode 100644
index 00000000000..ca0674aec04
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py
@@ -0,0 +1,20 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Check that error messages put the '^' in the right place.
+
+ threw = False
+ input = 'interface ?'
+ try:
+ parser.parse(input)
+ results = parser.finish()
+ except WebIDL.WebIDLError, e:
+ threw = True
+ lines = str(e).split('\n')
+
+ harness.check(len(lines), 3, 'Expected number of lines in error message')
+ harness.check(lines[1], input, 'Second line shows error')
+ harness.check(lines[2], ' ' * (len(input) - 1) + '^',
+ 'Correct column pointer in error message')
+
+ harness.ok(threw, "Should have thrown.")
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
new file mode 100644
index 00000000000..f11222e7a4d
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py
@@ -0,0 +1,28 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Check that error messages put the '^' in the right place.
+
+ threw = False
+ input = """\
+// This is a comment.
+interface Foo {
+};
+
+/* This is also a comment. */
+interface ?"""
+ try:
+ parser.parse(input)
+ results = parser.finish()
+ except WebIDL.WebIDLError, e:
+ threw = True
+ lines = str(e).split('\n')
+
+ harness.check(len(lines), 3, 'Expected number of lines in error message')
+ harness.ok(lines[0].endswith('line 6:10'), 'First line of error should end with "line 6:10", but was "%s".' % lines[0])
+ harness.check(lines[1], 'interface ?', 'Second line of error message is the line which caused the error.')
+ harness.check(lines[2], ' ' * (len('interface ?') - 1) + '^',
+ 'Correct column pointer in error message.')
+
+ harness.ok(threw, "Should have thrown.")
+
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
new file mode 100644
index 00000000000..5c6887331e7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py
@@ -0,0 +1,107 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [Flippety]
+ interface TestExtendedAttr {
+ [Foopy] attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+
+ parser = parser.reset()
+ parser.parse("""
+ [Flippety="foo.bar",Floppety=flop]
+ interface TestExtendedAttr {
+ [Foopy="foo.bar"] attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestLenientThis {
+ [LenientThis] attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+ harness.ok(results[0].members[0].hasLenientThis(),
+ "Should have a lenient this")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestLenientThis2 {
+ [LenientThis=something] attribute byte b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[LenientThis] must take no arguments")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestClamp {
+ void testClamp([Clamp] long foo);
+ void testNotClamp(long foo);
+ };
+ """)
+
+ 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,
+ "Should be clamped")
+ harness.ok(not results[0].members[1].signatures()[0][1][0].clamp,
+ "Should not be clamped")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestClamp2 {
+ void testClamp([Clamp=something] long foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[Clamp] must take no arguments")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestEnforceRange {
+ void testEnforceRange([EnforceRange] long foo);
+ void testNotEnforceRange(long foo);
+ };
+ """)
+
+ 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,
+ "Should be enforceRange")
+ harness.ok(not results[0].members[1].signatures()[0][1][0].enforceRange,
+ "Should not be enforceRange")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestEnforceRange2 {
+ void testEnforceRange([EnforceRange=something] long foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[EnforceRange] must take no arguments")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_forward_decl.py b/components/script/dom/bindings/codegen/parser/tests/test_forward_decl.py
new file mode 100644
index 00000000000..cac24c832cc
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_forward_decl.py
@@ -0,0 +1,15 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface ForwardDeclared;
+ interface ForwardDeclared;
+
+ interface TestForwardDecl {
+ attribute ForwardDeclared foo;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestForwardDeclared interface parsed without error.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_implements.py b/components/script/dom/bindings/codegen/parser/tests/test_implements.py
new file mode 100644
index 00000000000..04c47d92abe
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_implements.py
@@ -0,0 +1,216 @@
+# 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_incomplete_parent.py b/components/script/dom/bindings/codegen/parser/tests/test_incomplete_parent.py
new file mode 100644
index 00000000000..1f520a28e16
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_incomplete_parent.py
@@ -0,0 +1,18 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestIncompleteParent : NotYetDefined {
+ void foo();
+ };
+
+ interface NotYetDefined : EvenHigherOnTheChain {
+ };
+
+ interface EvenHigherOnTheChain {
+ };
+ """)
+
+ parser.finish()
+
+ harness.ok(True, "TestIncompleteParent interface parsed without error.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_incomplete_types.py b/components/script/dom/bindings/codegen/parser/tests/test_incomplete_types.py
new file mode 100644
index 00000000000..fdc39604070
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_incomplete_types.py
@@ -0,0 +1,44 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestIncompleteTypes {
+ attribute FooInterface attr1;
+
+ FooInterface method1(FooInterface arg);
+ };
+
+ interface FooInterface {
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestIncompleteTypes 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(), "::TestIncompleteTypes", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestIncompleteTypes", "Interface has the right name")
+ harness.check(len(iface.members), 2, "Expect 2 members")
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ method = iface.members[1]
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+
+ harness.check(attr.identifier.QName(), "::TestIncompleteTypes::attr1",
+ "Attribute has the right QName")
+ harness.check(attr.type.name, "FooInterface",
+ "Previously unresolved type has the right name")
+
+ harness.check(method.identifier.QName(), "::TestIncompleteTypes::method1",
+ "Attribute has the right QName")
+ (returnType, args) = method.signatures()[0]
+ harness.check(returnType.name, "FooInterface",
+ "Previously unresolved type has the right name")
+ harness.check(args[0].type.name, "FooInterface",
+ "Previously unresolved type has the right name")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface.py b/components/script/dom/bindings/codegen/parser/tests/test_interface.py
new file mode 100644
index 00000000000..5b07172c636
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interface.py
@@ -0,0 +1,188 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("interface Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::Foo", "Interface has the right QName")
+ harness.check(iface.identifier.name, "Foo", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ parser.parse("interface Bar : Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[1]
+ harness.check(iface.identifier.QName(), "::Bar", "Interface has the right QName")
+ harness.check(iface.identifier.name, "Bar", "Interface has the right name")
+ harness.ok(isinstance(iface.parent, WebIDL.IDLInterface),
+ "Interface has a parent")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface QNameBase {
+ attribute long foo;
+ };
+
+ interface QNameDerived : QNameBase {
+ attribute long long foo;
+ attribute byte bar;
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 2, "Should be two productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(results[1].parent, results[0], "Inheritance chain is right")
+ harness.check(len(results[0].members), 1, "Expect 1 productions")
+ harness.check(len(results[1].members), 2, "Expect 2 productions")
+ base = results[0]
+ derived = results[1]
+ harness.check(base.members[0].identifier.QName(), "::QNameBase::foo",
+ "Member has the right QName")
+ harness.check(derived.members[0].identifier.QName(), "::QNameDerived::foo",
+ "Member has the right QName")
+ harness.check(derived.members[1].identifier.QName(), "::QNameDerived::bar",
+ "Member has the right QName")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles in interface inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : C {};
+ interface C : B {};
+ interface B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirect cycles in interface inheritance chains")
+
+ parser = parser.reset()
+ 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 {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow inheriting from an interface that is only forward declared")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface_const_identifier_conflicts.py b/components/script/dom/bindings/codegen/parser/tests/test_interface_const_identifier_conflicts.py
new file mode 100644
index 00000000000..db944e7aaf7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interface_const_identifier_conflicts.py
@@ -0,0 +1,15 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflict {
+ const byte thing1 = 1;
+ const unsigned long thing1 = 1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface_identifier_conflicts_across_members.py b/components/script/dom/bindings/codegen/parser/tests/test_interface_identifier_conflicts_across_members.py
new file mode 100644
index 00000000000..1a73fb917ed
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interface_identifier_conflicts_across_members.py
@@ -0,0 +1,60 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers1 {
+ const byte thing1 = 1;
+ readonly attribute long thing1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers2 {
+ readonly attribute long thing1;
+ const byte thing1 = 1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers3 {
+ getter boolean thing1(DOMString name);
+ readonly attribute long thing1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers1 {
+ const byte thing1 = 1;
+ long thing1();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_method.py b/components/script/dom/bindings/codegen/parser/tests/test_method.py
new file mode 100644
index 00000000000..40b2d2cf8b9
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_method.py
@@ -0,0 +1,145 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestMethods {
+ void basic();
+ static void basicStatic();
+ void basicWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
+ boolean basicBoolean();
+ static boolean basicStaticBoolean();
+ boolean basicBooleanWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
+ void optionalArg(optional byte? arg1, optional sequence<byte> arg2);
+ void variadicArg(byte?... arg1);
+ void crazyTypes(sequence<long?[]>? arg1, boolean?[][]? arg2);
+ object getObject();
+ void setObject(object arg1);
+ void setAny(any arg1);
+ float doFloats(float arg1);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestMethods interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestMethods", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestMethods", "Interface has the right name")
+ harness.check(len(iface.members), 13, "Expect 13 members")
+
+ methods = iface.members
+
+ def checkArgument(argument, QName, name, type, optional, variadic):
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), QName, "Argument has the right QName")
+ harness.check(argument.identifier.name, name, "Argument has the right name")
+ harness.check(str(argument.type), type, "Argument has the right return type")
+ harness.check(argument.optional, optional, "Argument has the right optional value")
+ 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,
+ deleter=False, legacycaller=False, stringifier=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.ok(method.isMethod(), "Method is a method")
+ harness.ok(not method.isAttr(), "Method is not an attr")
+ harness.ok(not method.isConst(), "Method is not a const")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ 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(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
+
+ sigpairs = zip(method.signatures(), signatures)
+ for (gotSignature, expectedSignature) in sigpairs:
+ (gotRetType, gotArgs) = gotSignature
+ (expectedRetType, expectedArgs) = expectedSignature
+
+ harness.check(str(gotRetType), expectedRetType,
+ "Method has the expected return type.")
+
+ for i in range(0, len(gotArgs)):
+ (QName, name, type, optional, variadic) = expectedArgs[i]
+ checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+
+ checkMethod(methods[0], "::TestMethods::basic", "basic", [("Void", [])])
+ checkMethod(methods[1], "::TestMethods::basicStatic", "basicStatic",
+ [("Void", [])], static=True)
+ checkMethod(methods[2], "::TestMethods::basicWithSimpleArgs",
+ "basicWithSimpleArgs",
+ [("Void",
+ [("::TestMethods::basicWithSimpleArgs::arg1", "arg1", "Boolean", False, False),
+ ("::TestMethods::basicWithSimpleArgs::arg2", "arg2", "Byte", False, False),
+ ("::TestMethods::basicWithSimpleArgs::arg3", "arg3", "UnsignedLong", False, False)])])
+ checkMethod(methods[3], "::TestMethods::basicBoolean", "basicBoolean", [("Boolean", [])])
+ checkMethod(methods[4], "::TestMethods::basicStaticBoolean", "basicStaticBoolean", [("Boolean", [])], static=True)
+ checkMethod(methods[5], "::TestMethods::basicBooleanWithSimpleArgs",
+ "basicBooleanWithSimpleArgs",
+ [("Boolean",
+ [("::TestMethods::basicBooleanWithSimpleArgs::arg1", "arg1", "Boolean", False, False),
+ ("::TestMethods::basicBooleanWithSimpleArgs::arg2", "arg2", "Byte", False, False),
+ ("::TestMethods::basicBooleanWithSimpleArgs::arg3", "arg3", "UnsignedLong", False, False)])])
+ checkMethod(methods[6], "::TestMethods::optionalArg",
+ "optionalArg",
+ [("Void",
+ [("::TestMethods::optionalArg::arg1", "arg1", "ByteOrNull", True, False),
+ ("::TestMethods::optionalArg::arg2", "arg2", "ByteSequence", True, False)])])
+ checkMethod(methods[7], "::TestMethods::variadicArg",
+ "variadicArg",
+ [("Void",
+ [("::TestMethods::variadicArg::arg1", "arg1", "ByteOrNull", True, True)])])
+ checkMethod(methods[8], "::TestMethods::crazyTypes",
+ "crazyTypes",
+ [("Void",
+ [("::TestMethods::crazyTypes::arg1", "arg1", "LongOrNullArraySequenceOrNull", False, False),
+ ("::TestMethods::crazyTypes::arg2", "arg2", "BooleanOrNullArrayArrayOrNull", False, False)])])
+ checkMethod(methods[9], "::TestMethods::getObject",
+ "getObject", [("Object", [])])
+ checkMethod(methods[10], "::TestMethods::setObject",
+ "setObject",
+ [("Void",
+ [("::TestMethods::setObject::arg1", "arg1", "Object", False, False)])])
+ checkMethod(methods[11], "::TestMethods::setAny",
+ "setAny",
+ [("Void",
+ [("::TestMethods::setAny::arg1", "arg1", "Any", False, False)])])
+ checkMethod(methods[12], "::TestMethods::doFloats",
+ "doFloats",
+ [("Float",
+ [("::TestMethods::doFloats::arg1", "arg1", "Float", False, False)])])
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [GetterInfallible] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [GetterInfallible] on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SetterInfallible] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterInfallible] on methods")
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
new file mode 100644
index 00000000000..3366b9fbbbd
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py
@@ -0,0 +1,126 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestNullableEquivalency1 {
+ attribute long a;
+ attribute long? b;
+ };
+
+ interface TestNullableEquivalency2 {
+ attribute ArrayBuffer a;
+ attribute ArrayBuffer? b;
+ };
+
+ /* Can't have dictionary-valued attributes, so can't test that here */
+
+ enum TestNullableEquivalency4Enum {
+ "Foo",
+ "Bar"
+ };
+
+ interface TestNullableEquivalency4 {
+ attribute TestNullableEquivalency4Enum a;
+ attribute TestNullableEquivalency4Enum? b;
+ };
+
+ interface TestNullableEquivalency5 {
+ attribute TestNullableEquivalency4 a;
+ attribute TestNullableEquivalency4? b;
+ };
+
+ interface TestNullableEquivalency6 {
+ attribute boolean a;
+ attribute boolean? b;
+ };
+
+ interface TestNullableEquivalency7 {
+ attribute DOMString a;
+ attribute DOMString? b;
+ };
+
+ /* Not implemented. */
+ /*interface TestNullableEquivalency8 {
+ attribute float a;
+ attribute float? b;
+ };*/
+
+ interface TestNullableEquivalency8 {
+ attribute double a;
+ attribute double? b;
+ };
+
+ interface TestNullableEquivalency9 {
+ attribute object a;
+ attribute object? b;
+ };
+
+ interface TestNullableEquivalency10 {
+ attribute double[] a;
+ attribute double[]? b;
+ };
+
+ interface TestNullableEquivalency11 {
+ attribute TestNullableEquivalency9[] a;
+ attribute TestNullableEquivalency9[]? b;
+ };
+ """)
+
+ for decl in parser.finish():
+ if decl.isInterface():
+ checkEquivalent(decl, harness)
+
+def checkEquivalent(iface, harness):
+ type1 = iface.members[0].type
+ type2 = iface.members[1].type
+
+ harness.check(type1.nullable(), False, 'attr1 should not be nullable')
+ harness.check(type2.nullable(), True, 'attr2 should be nullable')
+
+ # We don't know about type1, but type2, the nullable type, definitely
+ # shouldn't be builtin.
+ harness.check(type2.builtin, False, 'attr2 should not be builtin')
+
+ # Ensure that all attributes of type2 match those in type1, except for:
+ # - names on an ignore list,
+ # - names beginning with '_',
+ # - functions which throw when called with no args, and
+ # - class-level non-callables ("static variables").
+ #
+ # Yes, this is an ugly, fragile hack. But it finds bugs...
+ for attr in dir(type1):
+ if attr.startswith('_') or \
+ attr in ['nullable', 'builtin', 'filename', 'location',
+ 'inner', 'QName'] or \
+ (hasattr(type(type1), attr) and not callable(getattr(type1, attr))):
+ continue
+
+ a1 = getattr(type1, attr)
+
+ if callable(a1):
+ try:
+ v1 = a1()
+ except:
+ # Can't call a1 with no args, so skip this attriute.
+ continue
+
+ try:
+ a2 = getattr(type2, attr)
+ except:
+ harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
+ continue
+
+ if not callable(a2):
+ harness.ok(False, "%s attribute on type %s in %s wasn't callable" % (attr, type2, iface))
+ continue
+
+ v2 = a2()
+ harness.check(v2, v1, '%s method return value' % attr)
+ else:
+ try:
+ a2 = getattr(type2, attr)
+ except:
+ harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
+ continue
+
+ harness.check(a2, a1, '%s attribute should match' % attr)
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_nullable_void.py b/components/script/dom/bindings/codegen/parser/tests/test_nullable_void.py
new file mode 100644
index 00000000000..961ff825e9f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_nullable_void.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface NullableVoid {
+ void? foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_optional_constraints.py b/components/script/dom/bindings/codegen/parser/tests/test_optional_constraints.py
new file mode 100644
index 00000000000..1dcdc7fb8a5
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_optional_constraints.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface OptionalConstraints1 {
+ void foo(optional byte arg1, byte arg2);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_overload.py b/components/script/dom/bindings/codegen/parser/tests/test_overload.py
new file mode 100644
index 00000000000..59d9be54e53
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_overload.py
@@ -0,0 +1,47 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestOverloads {
+ void basic();
+ void basic(long arg1);
+ boolean abitharder(TestOverloads foo);
+ boolean abitharder(boolean foo);
+ void abitharder(ArrayBuffer? foo);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestOverloads interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestOverloads", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestOverloads", "Interface has the right name")
+ harness.check(len(iface.members), 2, "Expect %s members" % 2)
+
+ member = iface.members[0]
+ harness.check(member.identifier.QName(), "::TestOverloads::basic", "Method has the right QName")
+ harness.check(member.identifier.name, "basic", "Method has the right name")
+ harness.check(member.hasOverloads(), True, "Method has overloads")
+
+ signatures = member.signatures()
+ harness.check(len(signatures), 2, "Method should have 2 signatures")
+
+ (retval, argumentSet) = signatures[0]
+
+ harness.check(str(retval), "Void", "Expect a void retval")
+ harness.check(len(argumentSet), 0, "Expect an empty argument set")
+
+ (retval, argumentSet) = signatures[1]
+ harness.check(str(retval), "Void", "Expect a void retval")
+ harness.check(len(argumentSet), 1, "Expect an argument set with one argument")
+
+ argument = argumentSet[0]
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), "::TestOverloads::basic::arg1", "Argument has the right QName")
+ harness.check(argument.identifier.name, "arg1", "Argument has the right name")
+ harness.check(str(argument.type), "Long", "Argument has the right type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_sanity.py b/components/script/dom/bindings/codegen/parser/tests/test_sanity.py
new file mode 100644
index 00000000000..d3184c00731
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_sanity.py
@@ -0,0 +1,7 @@
+def WebIDLTest(parser, harness):
+ parser.parse("")
+ parser.finish()
+ harness.ok(True, "Parsing nothing doesn't throw.")
+ parser.parse("interface Foo {};")
+ parser.finish()
+ harness.ok(True, "Parsing a silly interface doesn't throw.")
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
new file mode 100644
index 00000000000..5ea1743d36a
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py
@@ -0,0 +1,294 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch1 {
+ getter long long foo(long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch2 {
+ getter void foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch3 {
+ getter boolean foo(unsigned long index, boolean extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch4 {
+ getter boolean foo(unsigned long... index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch5 {
+ getter boolean foo(optional unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch6 {
+ getter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch7 {
+ deleter long long foo(long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch9 {
+ deleter boolean foo(unsigned long index, boolean extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch10 {
+ deleter boolean foo(unsigned long... index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch11 {
+ deleter boolean foo(optional unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch12 {
+ deleter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch13 {
+ setter 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 SpecialMethodSignatureMismatch15 {
+ setter 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 SpecialMethodSignatureMismatch16 {
+ setter 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 SpecialMethodSignatureMismatch17 {
+ setter 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 SpecialMethodSignatureMismatch18 {
+ setter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ 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
new file mode 100644
index 00000000000..695cfe4f250
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py
@@ -0,0 +1,73 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ 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);
+ };
+
+ 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,
+ deleter=False, legacycaller=False, stringifier=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ 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(len(results), 2, "Expect 2 interfaces")
+
+ iface = results[0]
+ harness.check(len(iface.members), 8, "Expect 8 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",
+ getter=True)
+ checkMethod(iface.members[5], "::SpecialMethods::__namedsetter", "__namedsetter",
+ setter=True)
+ checkMethod(iface.members[6], "::SpecialMethods::__namedcreator", "__namedcreator",
+ creator=True)
+ checkMethod(iface.members[7], "::SpecialMethods::__nameddeleter", "__nameddeleter",
+ deleter=True)
+
+ iface = results[1]
+ harness.check(len(iface.members), 4, "Expect 4 members")
+
+ 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",
+ "__namedgetterdeleter", getter=True, deleter=True)
+ checkMethod(iface.members[3], "::SpecialMethodsCombination::__namedsettercreator",
+ "__namedsettercreator", setter=True, creator=True)
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
new file mode 100644
index 00000000000..42e2c5bb71b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py
@@ -0,0 +1,62 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ getter deleter boolean (DOMString name);
+ getter boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ deleter boolean (DOMString name);
+ getter deleter boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ 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);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_treatNonCallableAsNull.py b/components/script/dom/bindings/codegen/parser/tests/test_treatNonCallableAsNull.py
new file mode 100644
index 00000000000..3d0e5ca479f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_treatNonCallableAsNull.py
@@ -0,0 +1,56 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull1 {
+ [TreatNonCallableAsNull] attribute Function? onfoo;
+ attribute Function? onbar;
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[1]
+ attr = iface.members[0]
+ harness.check(attr.type.treatNonCallableAsNull(), True, "Got the expected value")
+ attr = iface.members[1]
+ harness.check(attr.type.treatNonCallableAsNull(), False, "Got the expected value")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull2 {
+ [TreatNonCallableAsNull] attribute Function onfoo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ callback Function = any(any... arguments);
+
+ [TreatNonCallableAsNull]
+ interface TestTreatNonCallableAsNull3 {
+ attribute Function onfoo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_typedef.py b/components/script/dom/bindings/codegen/parser/tests/test_typedef.py
new file mode 100644
index 00000000000..9d2f3b3c2ce
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_typedef.py
@@ -0,0 +1,76 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ typedef long mylong;
+ typedef long? mynullablelong;
+ interface Foo {
+ const mylong X = 5;
+ const mynullablelong Y = 7;
+ const mynullablelong Z = null;
+ void foo(mylong arg);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(results[2].members[1].type.name, "Long",
+ "Should expand typedefs")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef long? mynullablelong;
+ interface Foo {
+ void foo(mynullablelong? Y);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on nullable inside nullable arg.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef long? mynullablelong;
+ interface Foo {
+ const mynullablelong? X = 5;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on nullable inside nullable const.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ const mynullablelong? X = 5;
+ };
+ typedef long? mynullablelong;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on nullable inside nullable const typedef "
+ "after interface.")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface Foo {
+ const mylong X = 5;
+ };
+ typedef long mylong;
+ """)
+
+ results = parser.finish()
+
+ harness.check(results[0].members[0].type.name, "Long",
+ "Should expand typedefs that come before interface")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_union.py b/components/script/dom/bindings/codegen/parser/tests/test_union.py
new file mode 100644
index 00000000000..68c2bcade8c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_union.py
@@ -0,0 +1,169 @@
+import WebIDL
+import itertools
+import string
+
+# We'd like to use itertools.chain but it's 2.6 or higher.
+def chain(*iterables):
+ # chain('ABC', 'DEF') --> A B C D E F
+ for it in iterables:
+ for element in it:
+ yield element
+
+# We'd like to use itertools.combinations but it's 2.6 or higher.
+def combinations(iterable, r):
+ # combinations('ABCD', 2) --> AB AC AD BC BD CD
+ # combinations(range(4), 3) --> 012 013 023 123
+ pool = tuple(iterable)
+ n = len(pool)
+ if r > n:
+ return
+ indices = range(r)
+ yield tuple(pool[i] for i in indices)
+ while True:
+ for i in reversed(range(r)):
+ if indices[i] != i + n - r:
+ break
+ else:
+ return
+ indices[i] += 1
+ for j in range(i+1, r):
+ indices[j] = indices[j-1] + 1
+ yield tuple(pool[i] for i in indices)
+
+# We'd like to use itertools.combinations_with_replacement but it's 2.7 or
+# higher.
+def combinations_with_replacement(iterable, r):
+ # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
+ pool = tuple(iterable)
+ n = len(pool)
+ if not n and r:
+ return
+ indices = [0] * r
+ yield tuple(pool[i] for i in indices)
+ while True:
+ for i in reversed(range(r)):
+ if indices[i] != n - 1:
+ break
+ else:
+ return
+ indices[i:] = [indices[i] + 1] * (r - i)
+ yield tuple(pool[i] for i in indices)
+
+def WebIDLTest(parser, harness):
+ types = ["float",
+ "double",
+ "short",
+ "unsigned short",
+ "long",
+ "unsigned long",
+ "long long",
+ "unsigned long long",
+ "boolean",
+ "byte",
+ "octet",
+ "DOMString",
+ #"sequence<float>",
+ "object",
+ "ArrayBuffer",
+ #"Date",
+ "TestInterface1",
+ "TestInterface2"]
+
+ testPre = """
+ interface TestInterface1 {
+ };
+ interface TestInterface2 {
+ };
+ """
+
+ interface = testPre + """
+ interface PrepareForTest {
+ """
+ for (i, type) in enumerate(types):
+ interface += string.Template("""
+ readonly attribute ${type} attr${i};
+ """).substitute(i=i, type=type)
+ interface += """
+ };
+ """
+
+ parser.parse(interface)
+ results = parser.finish()
+
+ iface = results[2]
+
+ parser = parser.reset()
+
+ def typesAreDistinguishable(t):
+ return all(u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
+ def typesAreNotDistinguishable(t):
+ return any(not u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
+ def unionTypeName(t):
+ if len(t) > 2:
+ t[0:2] = [unionTypeName(t[0:2])]
+ return "(" + " or ".join(t) + ")"
+
+ # typeCombinations is an iterable of tuples containing the name of the type
+ # as a string and the parsed IDL type.
+ def unionTypes(typeCombinations, predicate):
+ for c in typeCombinations:
+ if predicate(t[1] for t in c):
+ yield unionTypeName([t[0] for t in c])
+
+ # We limit invalid union types with a union member type to the subset of 3
+ # types with one invalid combination.
+ # typeCombinations is an iterable of tuples containing the name of the type
+ # as a string and the parsed IDL type.
+ def invalidUnionWithUnion(typeCombinations):
+ for c in typeCombinations:
+ if (typesAreNotDistinguishable((c[0][1], c[1][1])) and
+ typesAreDistinguishable((c[1][1], c[2][1])) and
+ typesAreDistinguishable((c[0][1], c[2][1]))):
+ yield unionTypeName([t[0] for t in c])
+
+ # Create a list of tuples containing the name of the type as a string and
+ # the parsed IDL type.
+ types = zip(types, (a.type for a in iface.members))
+
+ validUnionTypes = chain(unionTypes(combinations(types, 2), typesAreDistinguishable),
+ unionTypes(combinations(types, 3), typesAreDistinguishable))
+ invalidUnionTypes = chain(unionTypes(combinations_with_replacement(types, 2), typesAreNotDistinguishable),
+ invalidUnionWithUnion(combinations(types, 3)))
+ interface = testPre + """
+ interface TestUnion {
+ """
+ for (i, type) in enumerate(validUnionTypes):
+ interface += string.Template("""
+ void method${i}(${type} arg);
+ ${type} returnMethod${i}();
+ attribute ${type} attr${i};
+ void arrayMethod${i}(${type}[] arg);
+ ${type}[] arrayReturnMethod${i}();
+ attribute ${type}[] arrayAttr${i};
+ void optionalMethod${i}(${type}? arg);
+ """).substitute(i=i, type=type)
+ interface += """
+ };
+ """
+ parser.parse(interface)
+ results = parser.finish()
+
+ parser = parser.reset()
+
+ for invalid in invalidUnionTypes:
+ interface = testPre + string.Template("""
+ interface TestUnion {
+ void method(${type} arg);
+ };
+ """).substitute(type=invalid)
+
+ threw = False
+ try:
+ parser.parse(interface)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_union_any.py b/components/script/dom/bindings/codegen/parser/tests/test_union_any.py
new file mode 100644
index 00000000000..e34cadab470
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_union_any.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface AnyNotInUnion {
+ void foo((any or DOMString) arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_union_nullable.py b/components/script/dom/bindings/codegen/parser/tests/test_union_nullable.py
new file mode 100644
index 00000000000..08430a94a2e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_union_nullable.py
@@ -0,0 +1,53 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface OneNullableInUnion {
+ void foo((object? or DOMString?) arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Two nullable member types of a union should have thrown.")
+
+ parser.reset()
+ threw = False
+
+ try:
+ parser.parse("""
+ interface NullableInNullableUnion {
+ void foo((object? or DOMString)? arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "A nullable union type with a nullable member type should have "
+ "thrown.")
+
+ parser.reset()
+ threw = False
+
+ try:
+ parser.parse("""
+ interface NullableInUnionNullableUnionHelper {
+ };
+ interface NullableInUnionNullableUnion {
+ void foo(((object? or DOMString) or NullableInUnionNullableUnionHelper)? arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "A nullable union type with a nullable member type should have "
+ "thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_variadic_callback.py b/components/script/dom/bindings/codegen/parser/tests/test_variadic_callback.py
new file mode 100644
index 00000000000..d9a78db2043
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_variadic_callback.py
@@ -0,0 +1,10 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ callback TestVariadicCallback = any(any... arguments);
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestVariadicCallback callback parsed without error.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_variadic_constraints.py b/components/script/dom/bindings/codegen/parser/tests/test_variadic_constraints.py
new file mode 100644
index 00000000000..9cba22c5842
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_variadic_constraints.py
@@ -0,0 +1,39 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ results = parser.parse("""
+ interface VariadicConstraints1 {
+ void foo(byte... arg1, byte arg2);
+ };
+ """)
+
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ results = parser.parse("""
+ interface VariadicConstraints2 {
+ void foo(byte... arg1, optional byte arg2);
+ };
+ """)
+
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ results = parser.parse("""
+ interface VariadicConstraints3 {
+ void foo(optional byte... arg1);
+ };
+ """)
+
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/update.sh b/components/script/dom/bindings/codegen/parser/update.sh
new file mode 100755
index 00000000000..5dd513812e1
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/update.sh
@@ -0,0 +1,3 @@
+wget https://mxr.mozilla.org/mozilla-central/source/dom/bindings/parser/WebIDL.py?raw=1 -O WebIDL.py
+patch < external.patch
+patch < module.patch
diff --git a/components/script/dom/bindings/codegen/ply/COPYING b/components/script/dom/bindings/codegen/ply/COPYING
new file mode 100644
index 00000000000..3b107de4508
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/COPYING
@@ -0,0 +1,28 @@
+Copyright (C) 2001-2009,
+David M. Beazley (Dabeaz LLC)
+All rights reserved.
+
+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 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
+ endorse or promote products derived from this software without
+ 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
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/components/script/dom/bindings/codegen/ply/README b/components/script/dom/bindings/codegen/ply/README
new file mode 100644
index 00000000000..2459c490197
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/README
@@ -0,0 +1,9 @@
+David Beazley's PLY (Python Lex-Yacc)
+http://www.dabeaz.com/ply/
+
+Licensed under BSD.
+
+This directory contains just the code and license from PLY version 3.3;
+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
new file mode 100644
index 00000000000..853a985542b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/ply/__init__.py
@@ -0,0 +1,4 @@
+# PLY package
+# Author: David Beazley (dave@dabeaz.com)
+
+__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
new file mode 100644
index 00000000000..267ec100fc2
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/ply/lex.py
@@ -0,0 +1,1058 @@
+# -----------------------------------------------------------------------------
+# ply: lex.py
+#
+# Copyright (C) 2001-2009,
+# David M. Beazley (Dabeaz LLC)
+# All rights reserved.
+#
+# 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 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
+# endorse or promote products derived from this software without
+# 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
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# 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.
+
+if sys.version_info[0] < 3:
+ def func_code(f):
+ return f.func_code
+else:
+ def func_code(f):
+ return f.__code__
+
+# 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
+
+# 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)
+
+# This object is a stand-in for a logging object created by the
+# logging module.
+
+class PlyLogger(object):
+ 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 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 ===
+#
+# The following Lexer class implements the lexer runtime. There are only
+# a few public methods and attributes:
+#
+# input() - Store a new string in the lexer
+# token() - Get the next token
+# clone() - Clone the lexer
+#
+# lineno - Current line number
+# lexpos - Current position in the input string
+# -----------------------------------------------------------------------------
+
+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
+ 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.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.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.lextokens = None # List of valid tokens
+ 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):
+ c = copy.copy(self)
+
+ # If the object parameter has been supplied, it means we are attaching the
+ # lexer to a new object. In this case, we have to rebind all methods in
+ # the lexstatere and lexstateerrorf tables.
+
+ if object:
+ 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))
+ newtab[key] = newre
+ c.lexstatere = newtab
+ c.lexstateerrorf = { }
+ for key, ef in self.lexstateerrorf.items():
+ 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")
+ self.lexdata = s
+ self.lexpos = 0
+ self.lexlen = len(s)
+
+ # ------------------------------------------------------------
+ # begin() - Changes the lexing state
+ # ------------------------------------------------------------
+ def begin(self,state):
+ if not state in self.lexstatere:
+ raise ValueError("Undefined state")
+ self.lexre = self.lexstatere[state]
+ self.lexretext = self.lexstateretext[state]
+ self.lexignore = self.lexstateignore.get(state,"")
+ self.lexerrorf = self.lexstateerrorf.get(state,None)
+ self.lexstate = state
+
+ # ------------------------------------------------------------
+ # push_state() - Changes the lexing state and saves old on stack
+ # ------------------------------------------------------------
+ def push_state(self,state):
+ self.lexstatestack.append(self.lexstate)
+ self.begin(state)
+
+ # ------------------------------------------------------------
+ # pop_state() - Restores the previous state
+ # ------------------------------------------------------------
+ def pop_state(self):
+ self.begin(self.lexstatestack.pop())
+
+ # ------------------------------------------------------------
+ # current_state() - Returns the current lexing state
+ # ------------------------------------------------------------
+ def current_state(self):
+ return self.lexstate
+
+ # ------------------------------------------------------------
+ # skip() - Skip ahead n characters
+ # ------------------------------------------------------------
+ def skip(self,n):
+ self.lexpos += n
+
+ # ------------------------------------------------------------
+ # opttoken() - 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
+ # you are doing
+ # ------------------------------------------------------------
+ def token(self):
+ # Make local copies of frequently referenced attributes
+ lexpos = self.lexpos
+ lexlen = self.lexlen
+ lexignore = self.lexignore
+ lexdata = self.lexdata
+
+ while lexpos < lexlen:
+ # This code provides some short-circuit code for whitespace, tabs, and other ignored characters
+ if lexdata[lexpos] in lexignore:
+ lexpos += 1
+ continue
+
+ # Look for a regular expression match
+ for lexre,lexindexfunc in self.lexre:
+ m = lexre.match(lexdata,lexpos)
+ if not m: continue
+
+ # Create a token for return
+ tok = LexToken()
+ tok.value = m.group()
+ tok.lineno = self.lineno
+ tok.lexpos = lexpos
+
+ i = m.lastindex
+ 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
+
+ lexpos = m.end()
+
+ # If token is processed by a function, call it
+
+ tok.lexer = self # Set additional attributes useful in token rules
+ self.lexmatch = m
+ self.lexpos = lexpos
+
+ newtok = func(tok)
+
+ # 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
+ if lexdata[lexpos] in self.lexliterals:
+ tok = LexToken()
+ tok.value = lexdata[lexpos]
+ tok.lineno = self.lineno
+ tok.type = tok.value
+ tok.lexpos = lexpos
+ self.lexpos = lexpos + 1
+ return tok
+
+ # No match. Call t_error() if defined.
+ if self.lexerrorf:
+ tok = LexToken()
+ tok.value = self.lexdata[lexpos:]
+ tok.lineno = self.lineno
+ 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:])
+ lexpos = self.lexpos
+ if not newtok: continue
+ return newtok
+
+ self.lexpos = lexpos
+ raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:])
+
+ self.lexpos = lexpos + 1
+ if self.lexdata is None:
+ raise RuntimeError("No input string given with input()")
+ return None
+
+ # Iterator interface
+ def __iter__(self):
+ return self
+
+ def next(self):
+ t = self.token()
+ if t is None:
+ raise StopIteration
+ return t
+
+ __next__ = next
+
+# -----------------------------------------------------------------------------
+# ==== Lex Builder ===
+#
+# The functions and classes below are used to collect lexing information
+# and build a Lexer object from it.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# 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
+
+# -----------------------------------------------------------------------------
+# _form_master_re()
+#
+# This function takes a list of all of the regex components and attempts to
+# 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)
+ try:
+ lexre = re.compile(regex,re.VERBOSE | reflags)
+
+ # Build the index to function map for the matching engine
+ lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1)
+ lexindexnames = lexindexfunc[:]
+
+ 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])
+ lexindexnames[i] = f
+ elif handle is not None:
+ lexindexnames[i] = f
+ if f.find("ignore_") > 0:
+ lexindexfunc[i] = (None,None)
+ else:
+ lexindexfunc[i] = (None, toknames[f])
+
+ 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
+
+# -----------------------------------------------------------------------------
+# def _statetoken(s,names)
+#
+# Given a declaration name s of the form "t_" and a dictionary whose keys are
+# state names, this function returns a tuple (states,tokenname) where states
+# 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):
+ 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])
+ else:
+ states = ('INITIAL',)
+
+ if 'ANY' in states:
+ states = tuple(names)
+
+ tokenname = "_".join(parts[i:])
+ return (states,tokenname)
+
+
+# -----------------------------------------------------------------------------
+# LexerReflect()
+#
+# This class represents information needed to build a lexer as extracted from a
+# user's input file.
+# -----------------------------------------------------------------------------
+class LexerReflect(object):
+ 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
+
+ # Get all of the basic information
+ def get_all(self):
+ self.get_tokens()
+ self.get_literals()
+ self.get_states()
+ self.get_rules()
+
+ # Validate all of the information
+ def validate_all(self):
+ self.validate_tokens()
+ self.validate_literals()
+ self.validate_rules()
+ return self.error
+
+ # Get the tokens map
+ def get_tokens(self):
+ tokens = self.ldict.get("tokens",None)
+ if not tokens:
+ self.log.error("No token list is defined")
+ self.error = 1
+ return
+
+ if not isinstance(tokens,(list, tuple)):
+ self.log.error("tokens must be a list or tuple")
+ self.error = 1
+ return
+
+ if not tokens:
+ self.log.error("tokens is empty")
+ self.error = 1
+ return
+
+ self.tokens = tokens
+
+ # Validate the tokens
+ def validate_tokens(self):
+ terminals = {}
+ for n in self.tokens:
+ if not _is_identifier.match(n):
+ self.log.error("Bad token name '%s'",n)
+ self.error = 1
+ if n in terminals:
+ self.log.warning("Token '%s' multiply defined", n)
+ terminals[n] = 1
+
+ # Get the literals specifier
+ def get_literals(self):
+ self.literals = self.ldict.get("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
+
+ except TypeError:
+ self.log.error("Invalid literals specification. literals must be a sequence of characters")
+ self.error = 1
+
+ def get_states(self):
+ 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
+
+ # 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_' ]
+
+ # 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
+
+ for s in self.stateinfo:
+ 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
+ return
+
+ for f in tsymbols:
+ t = self.ldict[f]
+ states, tokname = _statetoken(f,self.stateinfo)
+ self.toknames[f] = tokname
+
+ if hasattr(t,"__call__"):
+ if tokname == 'error':
+ for s in states:
+ self.errorf[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
+ else:
+ 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)
+
+ elif tokname == 'error':
+ self.log.error("Rule '%s' must be defined as a function", f)
+ self.error = 1
+ else:
+ 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
+
+ # 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)
+
+ # 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)
+
+ # 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
+
+ tokname = self.toknames[fname]
+ if isinstance(f, types.MethodType):
+ reqargs = 2
+ else:
+ reqargs = 1
+ nargs = func_code(f).co_argcount
+ if nargs > reqargs:
+ self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
+ self.error = 1
+ continue
+
+ if nargs < reqargs:
+ self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
+ self.error = 1
+ continue
+
+ if not f.__doc__:
+ self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__)
+ self.error = 1
+ 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
+
+ # Validate all rules defined by strings
+ 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
+ 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
+ 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)
+ if '#' in r:
+ self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name)
+ self.error = 1
+
+ if not self.funcsym[state] and not self.strsym[state]:
+ self.log.error("No rules defined for state '%s'",state)
+ self.error = 1
+
+ # Validate the error function
+ 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
+
+ if isinstance(f, types.MethodType):
+ reqargs = 2
+ else:
+ reqargs = 1
+ nargs = func_code(f).co_argcount
+ if nargs > reqargs:
+ self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
+ self.error = 1
+
+ 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)
+
+
+ # -----------------------------------------------------------------------------
+ # validate_file()
+ #
+ # 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.
+ # -----------------------------------------------------------------------------
+
+ 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
+
+ try:
+ f = open(filename)
+ lines = f.readlines()
+ f.close()
+ except IOError:
+ return # Couldn't find the file. Don't worry about it
+
+ 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)
+ if not m:
+ m = sre.match(l)
+ 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
+ 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):
+ global lexer
+ ldict = None
+ stateinfo = { 'INITIAL' : 'inclusive'}
+ lexobj = Lexer()
+ lexobj.lexoptimize = optimize
+ global token,input
+
+ if errorlog is None:
+ errorlog = PlyLogger(sys.stderr)
+
+ if debug:
+ if debuglog is None:
+ debuglog = PlyLogger(sys.stderr)
+
+ # Get the module dictionary used for the lexer
+ if object: module = object
+
+ if module:
+ _items = [(k,getattr(module,k)) for k in dir(module)]
+ ldict = dict(_items)
+ else:
+ ldict = get_caller_module_dict(2)
+
+ # Collect parser information from the dictionary
+ 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
+
+ # 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)
+
+ # Build a dictionary of valid token names
+ lexobj.lextokens = { }
+ for n in linfo.tokens:
+ lexobj.lextokens[n] = 1
+
+ # Get literals specification
+ if isinstance(linfo.literals,(list,tuple)):
+ lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
+ else:
+ lexobj.lexliterals = linfo.literals
+
+ # Get the stateinfo dictionary
+ stateinfo = linfo.stateinfo
+
+ 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__))
+ if debug:
+ debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state)
+
+ # Now add all of the simple rules
+ 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)
+
+ regexs[state] = regex_list
+
+ # Build the master regular expressions
+
+ if debug:
+ debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====")
+
+ for state in regexs:
+ 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 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'])
+
+ lexobj.lexstateinfo = stateinfo
+ 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","")
+
+ # Set up error functions
+ lexobj.lexstateerrorf = linfo.errorf
+ lexobj.lexerrorf = linfo.errorf.get("INITIAL",None)
+ if not lexobj.lexerrorf:
+ errorlog.warning("No t_error rule is defined")
+
+ # Check state information for ignore and error rules
+ 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)
+ 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","")
+
+ # 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
+
+# -----------------------------------------------------------------------------
+# runmain()
+#
+# This runs the lexer as a main program
+# -----------------------------------------------------------------------------
+
+def runmain(lexer=None,data=None):
+ if not data:
+ try:
+ filename = sys.argv[1]
+ f = open(filename)
+ data = f.read()
+ f.close()
+ except IndexError:
+ sys.stdout.write("Reading from standard input (type EOF to end):\n")
+ data = sys.stdin.read()
+
+ if lexer:
+ _input = lexer.input
+ else:
+ _input = input
+ _input(data)
+ if lexer:
+ _token = lexer.token
+ else:
+ _token = token
+
+ while 1:
+ tok = _token()
+ if not tok: break
+ sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos))
+
+# -----------------------------------------------------------------------------
+# @TOKEN(regex)
+#
+# This decorator function can be used to set the regex expression on a function
+# when its docstring might need to be set in an alternative way
+# -----------------------------------------------------------------------------
+
+def TOKEN(r):
+ def set_doc(f):
+ if hasattr(r,"__call__"):
+ f.__doc__ = r.__doc__
+ else:
+ f.__doc__ = r
+ return f
+ return set_doc
+
+# Alternative spelling of the TOKEN decorator
+Token = TOKEN
+
diff --git a/components/script/dom/bindings/codegen/ply/ply/yacc.py b/components/script/dom/bindings/codegen/ply/ply/yacc.py
new file mode 100644
index 00000000000..e9f5c657551
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/ply/yacc.py
@@ -0,0 +1,3276 @@
+# -----------------------------------------------------------------------------
+# ply: yacc.py
+#
+# Copyright (C) 2001-2009,
+# David M. Beazley (Dabeaz LLC)
+# All rights reserved.
+#
+# 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 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
+# endorse or promote products derived from this software without
+# 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
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------------------------
+#
+# This implements an LR parser that is constructed from grammar rules defined
+# as Python functions. The grammer 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.
+#
+# The current implementation is only somewhat object-oriented. The
+# LR parser itself is defined in terms of an object (which allows multiple
+# parsers to co-exist). However, most of the variables used during table
+# construction are defined in terms of global variables. Users shouldn't
+# notice unless they are trying to define multiple parsers at the same
+# time using threads (in which case they should have their head examined).
+#
+# This implementation supports both SLR and LALR(1) parsing. LALR(1)
+# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu),
+# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles,
+# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced
+# by the more efficient DeRemer and Pennello algorithm.
+#
+# :::::::: WARNING :::::::
+#
+# Construction of LR parsing tables is fairly complicated and expensive.
+# To make this module run fast, a *LOT* of work has been put into
+# optimization---often at the expensive of readability and what might
+# consider to be good Python "coding style." Modify the code at your
+# own risk!
+# ----------------------------------------------------------------------------
+
+__version__ = "3.3"
+__tabversion__ = "3.2" # Table version
+
+#-----------------------------------------------------------------------------
+# === User configurable parameters ===
+#
+# 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
+
+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
+
+# 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
+# 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):
+ 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 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):
+ return self
+ def __call__(self,*args,**kwargs):
+ return self
+
+# Exception raised for yacc-related errors
+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 len(repr_str) > resultlimit:
+ 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 len(repr_str) < 16:
+ return repr_str
+ else:
+ return "<%s @ 0x%x>" % (type(r).__name__,id(r))
+
+#-----------------------------------------------------------------------------
+# === LR Parsing Engine ===
+#
+# The following classes are used for the LR parser itself. These are not
+# used during table construction and are independent of the actual LR
+# table generation algorithm
+#-----------------------------------------------------------------------------
+
+# This class is used to hold non-terminal grammar symbols during parsing.
+# It normally has the following attributes set:
+# .type = Grammar symbol type
+# .value = Symbol value
+# .lineno = Starting line number
+# .endlineno = Ending line number (optional, set automatically)
+# .lexpos = Starting lex position
+# .endlexpos = Ending lex position (optional, set automatically)
+
+class YaccSymbol:
+ 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
+# .value attribute of the underlying YaccSymbol object.
+# The lineno() method returns the line number of a given
+# item (or 0 if not defined). The linespan() method returns
+# a tuple of (startline,endline) representing the range of lines
+# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos)
+# representing the range of positional information for a symbol.
+
+class YaccProduction:
+ 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
+
+ def __setitem__(self,n,v):
+ self.slice[n].value = v
+
+ 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 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 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 error(self):
+ raise SyntaxError
+
+
+# -----------------------------------------------------------------------------
+# == LRParser ==
+#
+# The LR Parsing engine.
+# -----------------------------------------------------------------------------
+
+class LRParser:
+ def __init__(self,lrtab,errorf):
+ self.productions = lrtab.lr_productions
+ self.action = lrtab.lr_action
+ self.goto = lrtab.lr_goto
+ self.errorfunc = errorf
+
+ def errok(self):
+ self.errorok = 1
+
+ def restart(self):
+ del self.statestack[:]
+ del self.symstack[:]
+ sym = YaccSymbol()
+ sym.type = '$end'
+ 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().
+ #
+ # 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:
+ #
+ # #--! 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
+
+ # --! DEBUG
+ debug.info("PLY: PARSE DEBUG START")
+ # --! DEBUG
+
+ # 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
+
+ # --! DEBUG
+ debug.debug('')
+ debug.debug('State : %s', state)
+ # --! DEBUG
+
+ 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"
+
+ # --! DEBUG
+ debug.debug('Stack : %s',
+ ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
+ # --! DEBUG
+
+ # 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
+
+ # --! DEBUG
+ debug.debug("Action : Shift and goto state %s", t)
+ # --! DEBUG
+
+ 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
+
+ # --! 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 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)
+ # --! 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
+ 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
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ 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
+
+ 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
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ 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
+
+ # 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 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
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ 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
+
+ # 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")
+
+# -----------------------------------------------------------------------------
+# === Grammar Representation ===
+#
+# The following functions, classes, and variables are used to represent and
+# manipulate the rules that make up a grammar.
+# -----------------------------------------------------------------------------
+
+import re
+
+# regex matching identifiers
+_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
+
+# -----------------------------------------------------------------------------
+# class Production:
+#
+# 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
+#
+# Here are the basic attributes defined on all productions
+#
+# name - Name of the production. For example 'expr'
+# prod - A list of symbols on the right side ['expr','PLUS','term']
+# prec - Production precedence level
+# number - Production number.
+# func - Function that executes on reduce
+# file - File where production function is defined
+# lineno - Line number where production function is defined
+#
+# The following attributes are defined or optional.
+#
+# len - Length of the production (number of symbols on right hand side)
+# usyms - Set of unique symbols found in the production
+# -----------------------------------------------------------------------------
+
+class Production(object):
+ reduced = 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
+ self.func = func
+ self.callable = None
+ self.file = file
+ self.line = line
+ 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 = [ ]
+ for s in self.prod:
+ if s not in self.usyms:
+ self.usyms.append(s)
+
+ # List of all LR items for the production
+ self.lr_items = []
+ self.lr_next = None
+
+ # Create a string representation
+ if self.prod:
+ self.str = "%s -> %s" % (self.name," ".join(self.prod))
+ else:
+ self.str = "%s -> <empty>" % self.name
+
+ def __str__(self):
+ return self.str
+
+ def __repr__(self):
+ return "Production("+str(self)+")"
+
+ def __len__(self):
+ return len(self.prod)
+
+ def __nonzero__(self):
+ return 1
+
+ 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
+ try:
+ p.lr_after = 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):
+ if self.func:
+ self.callable = pdict[self.func]
+
+
+# -----------------------------------------------------------------------------
+# class LRItem
+#
+# This class represents a specific stage of parsing a production rule. For
+# example:
+#
+# expr : expr . PLUS term
+#
+# In the above, the "." represents the current location of the parse. Here
+# basic attributes:
+#
+# name - Name of the production. For example 'expr'
+# prod - A list of symbols on the right side ['expr','.', 'PLUS','term']
+# number - Production number.
+#
+# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term'
+# then lr_next refers to 'expr -> expr PLUS . term'
+# lr_index - LR item index (location of the ".") in the prod list.
+# lookaheads - LALR lookahead symbols for this item
+# len - Length of the production (number of symbols on right hand side)
+# lr_after - List of all productions that immediately follow
+# lr_before - Grammar symbol immediately before
+# -----------------------------------------------------------------------------
+
+class LRItem(object):
+ 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.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))
+ else:
+ s = "%s -> <empty>" % self.name
+ return s
+
+ def __repr__(self):
+ return "LRItem("+str(self)+")"
+
+# -----------------------------------------------------------------------------
+# rightmost_terminal()
+#
+# Return the rightmost terminal from a list of symbols. Used in add_production()
+# -----------------------------------------------------------------------------
+def rightmost_terminal(symbols, terminals):
+ i = len(symbols) - 1
+ while i >= 0:
+ if symbols[i] in terminals:
+ return symbols[i]
+ i -= 1
+ return None
+
+# -----------------------------------------------------------------------------
+# === GRAMMAR CLASS ===
+#
+# The following class represents the contents of the specified grammar along
+# with various computed properties such as first sets, follow sets, LR items, etc.
+# This data is used for critical parts of the table generation process later.
+# -----------------------------------------------------------------------------
+
+class GrammarError(YaccError): pass
+
+class Grammar(object):
+ 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
+
+ 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.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.First = { } # A dictionary of precomputed FIRST(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.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.Start = None # Starting symbol for the grammar
+
+
+ def __len__(self):
+ return len(self.Productions)
+
+ def __getitem__(self,index):
+ return self.Productions[index]
+
+ # -----------------------------------------------------------------------------
+ # set_precedence()
+ #
+ # Sets the precedence for a given terminal. assoc is the associativity such as
+ # 'left','right', or 'nonassoc'. level is a numeric level.
+ #
+ # -----------------------------------------------------------------------------
+
+ 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("Associativity must be one of 'left','right', or 'nonassoc'")
+ self.Precedence[term] = (assoc,level)
+
+ # -----------------------------------------------------------------------------
+ # add_production()
+ #
+ # Given an action function, this function assembles a production rule and
+ # computes its precedence level.
+ #
+ # The production rule is supplied as a list of symbols. For example,
+ # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and
+ # symbols ['expr','PLUS','term'].
+ #
+ # Precedence is determined by the precedence of the right-most non-terminal
+ # or the precedence of a terminal specified by %prec.
+ #
+ # A variety of error checks are performed to make sure production symbols
+ # are valid and that %prec is used correctly.
+ # -----------------------------------------------------------------------------
+
+ 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))
+ if prodname == 'error':
+ raise GrammarError("%s:%d: Illegal rule name '%s'. 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))
+
+ # 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
+ if not _is_identifier.match(s) and s != '%prec':
+ raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (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))
+ if syms[-2] != '%prec':
+ 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)
+ if not prodprec:
+ raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname))
+ else:
+ self.UsedPrecedence[precname] = 1
+ 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))
+
+ # See if the rule is already in the rulemap
+ 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))
+
+ # 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] = [ ]
+
+ # 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] = [ ]
+ 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)
+ self.Productions.append(p)
+ self.Prodmap[map] = p
+
+ # Add to the global productions list
+ try:
+ self.Prodnames[prodname].append(p)
+ except KeyError:
+ self.Prodnames[prodname] = [ p ]
+ return 0
+
+ # -----------------------------------------------------------------------------
+ # set_start()
+ #
+ # 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):
+ 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])
+ self.Nonterminals[start].append(0)
+ self.Start = start
+
+ # -----------------------------------------------------------------------------
+ # find_unreachable()
+ #
+ # Find all of the nonterminal symbols that can't be reached from the starting
+ # symbol. Returns a list of nonterminals that can't be reached.
+ # -----------------------------------------------------------------------------
+
+ 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.
+ return
+ reachable[s] = 1
+ 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
+
+ mark_reachable_from( self.Productions[0].prod[0] )
+
+ return [s for s in list(self.Nonterminals)
+ if not reachable[s]]
+
+ # -----------------------------------------------------------------------------
+ # infinite_cycles()
+ #
+ # This function looks at the various parsing rules and tries to detect
+ # infinite recursion cycles (grammar rules where there is no possible way
+ # to derive a string of only terminals).
+ # -----------------------------------------------------------------------------
+
+ def infinite_cycles(self):
+ terminates = {}
+
+ # Terminals:
+ for t in self.Terminals:
+ terminates[t] = 1
+
+ terminates['$end'] = 1
+
+ # Nonterminals:
+
+ # Initialize to false:
+ for n in self.Nonterminals:
+ terminates[n] = 0
+
+ # Then propagate termination until no change:
+ while 1:
+ some_change = 0
+ 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.
+ for s in p.prod:
+ if not terminates[s]:
+ # The symbol s does not terminate,
+ # so production p does not terminate.
+ p_terminates = 0
+ break
+ else:
+ # didn't break from the loop,
+ # so every symbol s terminates
+ # so production p terminates.
+ p_terminates = 1
+
+ if p_terminates:
+ # symbol n terminates!
+ if not terminates[n]:
+ terminates[n] = 1
+ some_change = 1
+ # Don't need to consider any more productions for this n.
+ break
+
+ if not some_change:
+ break
+
+ infinite = []
+ for (s,term) in terminates.items():
+ if not term:
+ if not s in self.Prodnames and not s 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
+ else:
+ infinite.append(s)
+
+ 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.
+ # -----------------------------------------------------------------------------
+ def undefined_symbols(self):
+ result = []
+ for p in self.Productions:
+ 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))
+ return result
+
+ # -----------------------------------------------------------------------------
+ # unused_terminals()
+ #
+ # Find all terminals that were defined, but not used by the grammar. Returns
+ # a list of all symbols.
+ # -----------------------------------------------------------------------------
+ def unused_terminals(self):
+ unused_tok = []
+ for s,v in self.Terminals.items():
+ if s != 'error' and not v:
+ unused_tok.append(s)
+
+ return unused_tok
+
+ # ------------------------------------------------------------------------------
+ # unused_rules()
+ #
+ # Find all grammar rules that were defined, but not used (maybe not reachable)
+ # Returns a list of productions.
+ # ------------------------------------------------------------------------------
+
+ def unused_rules(self):
+ unused_prod = []
+ for s,v in self.Nonterminals.items():
+ if not v:
+ p = self.Prodnames[s][0]
+ unused_prod.append(p)
+ return unused_prod
+
+ # -----------------------------------------------------------------------------
+ # unused_precedence()
+ #
+ # 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.
+ # -----------------------------------------------------------------------------
+
+ 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]))
+
+ return unused
+
+ # -------------------------------------------------------------------------
+ # _first()
+ #
+ # Compute the value of FIRST1(beta) where beta is a tuple of symbols.
+ #
+ # 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):
+
+ # We are computing First(x1,x2,x3,...,xn)
+ result = [ ]
+ for x in beta:
+ x_produces_empty = 0
+
+ # 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
+ else:
+ if f not in result: result.append(f)
+
+ if x_produces_empty:
+ # We have to consider the next x in beta,
+ # i.e. stay in the loop.
+ pass
+ else:
+ # We don't have to consider any further symbols in beta.
+ break
+ else:
+ # There was no 'break' from the loop,
+ # so x_produces_empty was true for all x in beta,
+ # so beta produces empty as well.
+ result.append('<empty>')
+
+ return result
+
+ # -------------------------------------------------------------------------
+ # compute_first()
+ #
+ # Compute the value of FIRST1(X) for all symbols
+ # -------------------------------------------------------------------------
+ def compute_first(self):
+ if self.First:
+ return self.First
+
+ # Terminals:
+ for t in self.Terminals:
+ self.First[t] = [t]
+
+ self.First['$end'] = ['$end']
+
+ # Nonterminals:
+
+ # Initialize to the empty set:
+ for n in self.Nonterminals:
+ self.First[n] = []
+
+ # Then propagate symbols until no change:
+ while 1:
+ some_change = 0
+ 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
+ if not some_change:
+ break
+
+ return self.First
+
+ # ---------------------------------------------------------------------
+ # compute_follow()
+ #
+ # Computes all of the follow sets for every non-terminal symbol. The
+ # 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):
+ # If already computed, return the result
+ if self.Follow:
+ return self.Follow
+
+ # If first sets not computed yet, do that first.
+ if not self.First:
+ self.compute_first()
+
+ # Add '$end' to the follow list of the start symbol
+ for k in self.Nonterminals:
+ self.Follow[k] = [ ]
+
+ if not start:
+ start = self.Productions[1].name
+
+ self.Follow[start] = [ '$end' ]
+
+ while 1:
+ didadd = 0
+ for p in self.Productions[1:]:
+ # Here is the production set
+ for i in range(len(p.prod)):
+ B = p.prod[i]
+ if B in self.Nonterminals:
+ # Okay. We got a non-terminal in a production
+ fst = self._first(p.prod[i+1:])
+ hasempty = 0
+ for f in fst:
+ if f != '<empty>' and f not in self.Follow[B]:
+ self.Follow[B].append(f)
+ didadd = 1
+ if f == '<empty>':
+ hasempty = 1
+ 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
+ return self.Follow
+
+
+ # -----------------------------------------------------------------------------
+ # build_lritems()
+ #
+ # This function walks the list of productions and builds a complete set of the
+ # LR items. The LR items are stored in two ways: First, they are uniquely
+ # numbered and placed in the list _lritems. Second, a linked list of LR items
+ # is built for each production. For example:
+ #
+ # E -> E PLUS E
+ #
+ # Creates the list
+ #
+ # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ]
+ # -----------------------------------------------------------------------------
+
+ def build_lritems(self):
+ for p in self.Productions:
+ lastlri = p
+ i = 0
+ lr_items = []
+ while 1:
+ if i > len(p):
+ lri = None
+ else:
+ lri = LRItem(p,i)
+ # Precompute the list of productions immediately following
+ try:
+ lri.lr_after = self.Prodnames[lri.prod[i+1]]
+ except (IndexError,KeyError):
+ lri.lr_after = []
+ try:
+ lri.lr_before = lri.prod[i-1]
+ except IndexError:
+ lri.lr_before = None
+
+ lastlri.lr_next = lri
+ 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
+# a grammar.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# digraph()
+# traverse()
+#
+# The following two functions are used to compute set valued functions
+# of the form:
+#
+# F(x) = F'(x) U U{F(y) | x R y}
+#
+# This is used to compute the values of Read() sets as well as FOLLOW sets
+# in LALR(1) generation.
+#
+# Inputs: X - An input set
+# R - A relation
+# FP - Set-valued function
+# ------------------------------------------------------------------------------
+
+def digraph(X,R,FP):
+ N = { }
+ for x in X:
+ N[x] = 0
+ stack = []
+ F = { }
+ for x in X:
+ if N[x] == 0: traverse(x,N,stack,F,X,R,FP)
+ return F
+
+def traverse(x,N,stack,F,X,R,FP):
+ stack.append(x)
+ d = len(stack)
+ N[x] = d
+ F[x] = FP(x) # F(X) <- F'(x)
+
+ 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)
+ 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()
+
+class LALRError(YaccError): pass
+
+# -----------------------------------------------------------------------------
+# == LRGeneratedTable ==
+#
+# This class implements the LR table generation algorithm. There are no
+# public methods except for write()
+# -----------------------------------------------------------------------------
+
+class LRGeneratedTable(LRTable):
+ def __init__(self,grammar,method='LALR',log=None):
+ if method not in ['SLR','LALR']:
+ raise LALRError("Unsupported method %s" % method)
+
+ self.grammar = grammar
+ self.lr_method = method
+
+ # Set up the logger
+ if not log:
+ log = NullLogger()
+ self.log = log
+
+ # Internal attributes
+ self.lr_action = {} # Action table
+ self.lr_goto = {} # Goto table
+ self.lr_productions = grammar.Productions # Copy of grammar Production array
+ self.lr_goto_cache = {} # Cache of computed gotos
+ self.lr0_cidhash = {} # Cache of closures
+
+ self._add_count = 0 # Internal counter used to detect cycles
+
+ # Diagonistic information filled in by the table generator
+ self.sr_conflict = 0
+ self.rr_conflict = 0
+ self.conflicts = [] # List of conflicts
+
+ self.sr_conflicts = []
+ self.rr_conflicts = []
+
+ # Build the tables
+ self.grammar.build_lritems()
+ self.grammar.compute_first()
+ self.grammar.compute_follow()
+ self.lr_parse_table()
+
+ # Compute the LR(0) closure operation on I, where I is a set of LR(0) items.
+
+ def lr0_closure(self,I):
+ self._add_count += 1
+
+ # Add everything in I to J
+ J = I[:]
+ didadd = 1
+ while didadd:
+ didadd = 0
+ for j in J:
+ for x in j.lr_after:
+ 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
+
+ return J
+
+ # Compute the LR(0) goto function goto(I,X) where I is a set
+ # of LR(0) items and X is a grammar symbol. This function is written
+ # in a way that guarantees uniqueness of the generated goto sets
+ # (i.e. the same goto set will never be returned as two different Python
+ # objects). With uniqueness, we can later do fast set comparisons using
+ # id(obj) instead of element-wise comparison.
+
+ 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
+
+ # Now we generate the goto set in a way that guarantees uniqueness
+ # of the result
+
+ s = self.lr_goto_cache.get(x,None)
+ if not s:
+ s = { }
+ self.lr_goto_cache[x] = s
+
+ gs = [ ]
+ for p in I:
+ n = p.lr_next
+ if n and n.lr_before == x:
+ s1 = s.get(id(n),None)
+ if not s1:
+ s1 = { }
+ s[id(n)] = s1
+ gs.append(n)
+ s = s1
+ g = s.get('$end',None)
+ 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
+ return g
+
+ # Compute the LR(0) sets of item function
+ def lr0_items(self):
+
+ C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ]
+ i = 0
+ for I in C:
+ self.lr0_cidhash[id(I)] = i
+ i += 1
+
+ # Loop over the items in C and each grammar symbols
+ i = 0
+ while i < len(C):
+ I = C[i]
+ i += 1
+
+ # Collect all of the symbols that could possibly be in the goto(I,X) sets
+ 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
+ self.lr0_cidhash[id(g)] = len(C)
+ C.append(g)
+
+ return C
+
+ # -----------------------------------------------------------------------------
+ # ==== LALR(1) Parsing ====
+ #
+ # LALR(1) parsing is almost exactly the same as SLR except that instead of
+ # relying upon Follow() sets when performing reductions, a more selective
+ # lookahead set that incorporates the state of the LR(0) machine is utilized.
+ # Thus, we mainly just have to focus on calculating the lookahead sets.
+ #
+ # The method used here is due to DeRemer and Pennelo (1982).
+ #
+ # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1)
+ # Lookahead Sets", ACM Transactions on Programming Languages and Systems,
+ # Vol. 4, No. 4, Oct. 1982, pp. 615-649
+ #
+ # Further details can also be found in:
+ #
+ # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing",
+ # McGraw-Hill Book Company, (1985).
+ #
+ # -----------------------------------------------------------------------------
+
+ # -----------------------------------------------------------------------------
+ # compute_nullable_nonterminals()
+ #
+ # Creates a dictionary containing all of the non-terminals that might produce
+ # an empty production.
+ # -----------------------------------------------------------------------------
+
+ def compute_nullable_nonterminals(self):
+ nullable = {}
+ num_nullable = 0
+ while 1:
+ for p in self.grammar.Productions[1:]:
+ if p.len == 0:
+ nullable[p.name] = 1
+ 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)
+ return nullable
+
+ # -----------------------------------------------------------------------------
+ # find_nonterminal_trans(C)
+ #
+ # Given a set of LR(0) items, this functions finds all of the non-terminal
+ # transitions. These are transitions in which a dot appears immediately before
+ # a non-terminal. Returns a list of tuples of the form (state,N) where state
+ # is the state number and N is the nonterminal symbol.
+ #
+ # 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
+
+ # -----------------------------------------------------------------------------
+ # dr_relation()
+ #
+ # Computes the DR(p,A) relationships for non-terminal transitions. The input
+ # is a tuple (state,N) where state is a number and N is a nonterminal symbol.
+ #
+ # Returns a list of terminals.
+ # -----------------------------------------------------------------------------
+
+ def dr_relation(self,C,trans,nullable):
+ dr_set = { }
+ state,N = trans
+ terms = []
+
+ 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)
+
+ # This extra bit is to handle the start state
+ if state == 0 and N == self.grammar.Productions[0].prod[0]:
+ terms.append('$end')
+
+ return terms
+
+ # -----------------------------------------------------------------------------
+ # reads_relation()
+ #
+ # Computes the READS() relation (p,A) READS (t,C).
+ # -----------------------------------------------------------------------------
+
+ 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)
+ 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))
+
+ return rel
+
+ # -----------------------------------------------------------------------------
+ # compute_lookback_includes()
+ #
+ # Determines the lookback and includes relations
+ #
+ # LOOKBACK:
+ #
+ # This relation is determined by running the LR(0) state machine forward.
+ # For example, starting with a production "N : . A B C", we run it forward
+ # to obtain "N : A B C ." We then build a relationship between this final
+ # state and the starting state. These relationships are stored in a dictionary
+ # lookdict.
+ #
+ # INCLUDES:
+ #
+ # Computes the INCLUDE() relation (p,A) INCLUDES (p',B).
+ #
+ # This relation is used to determine non-terminal transitions that occur
+ # inside of other non-terminal transition states. (p,A) INCLUDES (p', B)
+ # if the following holds:
+ #
+ # B -> LAT, where T -> epsilon and p' -L-> p
+ #
+ # L is essentially a prefix (which may be empty), T is a suffix that must be
+ # able to derive an empty string. State p' must lead to state p with the string L.
+ #
+ # -----------------------------------------------------------------------------
+
+ def compute_lookback_includes(self,C,trans,nullable):
+
+ lookdict = {} # Dictionary of lookback relations
+ includedict = {} # Dictionary of include relations
+
+ # Make a dictionary of non-terminal transitions
+ dtrans = {}
+ for t in trans:
+ dtrans[t] = 1
+
+ # Loop over all transitions and compute lookbacks and includes
+ for state,N in trans:
+ lookb = []
+ includes = []
+ for p in C[state]:
+ 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
+
+ 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
+
+ # 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))
+ for i in includes:
+ if not i in includedict: includedict[i] = []
+ includedict[i].append((state,N))
+ lookdict[(state,N)] = lookb
+
+ return lookdict,includedict
+
+ # -----------------------------------------------------------------------------
+ # compute_read_sets()
+ #
+ # Given a set of LR(0) items, this function computes the read sets.
+ #
+ # Inputs: C = Set of LR(0) items
+ # ntrans = Set of nonterminal transitions
+ # nullable = Set of empty transitions
+ #
+ # 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)
+ return F
+
+ # -----------------------------------------------------------------------------
+ # compute_follow_sets()
+ #
+ # Given a set of LR(0) items, a set of non-terminal transitions, a readset,
+ # and an include set, this function computes the follow sets
+ #
+ # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)}
+ #
+ # Inputs:
+ # ntrans = Set of nonterminal transitions
+ # readsets = Readset (previously computed)
+ # inclsets = Include sets (previously computed)
+ #
+ # 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
+
+ # -----------------------------------------------------------------------------
+ # add_lookaheads()
+ #
+ # Attaches the lookahead symbols to grammar rules.
+ #
+ # Inputs: lookbacks - Set of lookback relations
+ # followset - Computed follow set
+ #
+ # This function directly attaches the lookaheads to productions contained
+ # in the lookbacks set
+ # -----------------------------------------------------------------------------
+
+ 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)
+
+ # -----------------------------------------------------------------------------
+ # add_lalr_lookaheads()
+ #
+ # This function does all of the work of adding lookahead information for use
+ # with LALR parsing
+ # -----------------------------------------------------------------------------
+
+ def add_lalr_lookaheads(self,C):
+ # Determine all of the nullable nonterminals
+ nullable = self.compute_nullable_nonterminals()
+
+ # Find all non-terminal transitions
+ trans = self.find_nonterminal_transitions(C)
+
+ # Compute read sets
+ readsets = self.compute_read_sets(C,trans,nullable)
+
+ # Compute lookback/includes relations
+ lookd, included = self.compute_lookback_includes(C,trans,nullable)
+
+ # Compute LALR FOLLOW sets
+ followsets = self.compute_follow_sets(trans,readsets,included)
+
+ # Add all of the lookaheads
+ self.add_lookaheads(lookd,followsets)
+
+ # -----------------------------------------------------------------------------
+ # lr_parse_table()
+ #
+ # This function constructs the parse tables for SLR or LALR
+ # -----------------------------------------------------------------------------
+ def lr_parse_table(self):
+ Productions = self.grammar.Productions
+ Precedence = self.grammar.Precedence
+ goto = self.lr_goto # Goto array
+ 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)
+
+ # 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)
+
+ # 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("")
+ for p in I:
+ log.info(" (%d) %s", p.number, str(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])
+ else:
+ 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,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'))
+
+ else:
+ 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:
+ if a in st_action:
+ if p is st_actionp[a]:
+ 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:
+ if a in st_action:
+ if p is not st_actionp[a]:
+ if not (a,m) in _actprint:
+ log.debug(" ! %-15s [ %s ]",a,m)
+ not_used = 1
+ _actprint[(a,m)] = 1
+ if not_used:
+ log.debug("")
+
+ # Construct the goto table for this state
+
+ 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)
+ if j >= 0:
+ st_goto[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 ===
+#
+# The following functions and classes are used to implement the PLY
+# introspection features followed by the yacc() function itself.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# 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
+
+# -----------------------------------------------------------------------------
+# parse_grammar()
+#
+# This takes a raw grammar rule string and parses it into production data
+# -----------------------------------------------------------------------------
+def parse_grammar(doc,file,line):
+ grammar = []
+ # Split the doc string into lines
+ pstrings = doc.splitlines()
+ lastp = None
+ dline = line
+ for ps in pstrings:
+ dline += 1
+ p = ps.split()
+ 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))
+ prodname = lastp
+ syms = p[1:]
+ else:
+ prodname = p[0]
+ lastp = prodname
+ syms = p[2:]
+ assign = p[1]
+ if assign != ':' and assign != '::=':
+ raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline))
+
+ grammar.append((file,dline,prodname,syms))
+ except SyntaxError:
+ raise
+ except Exception:
+ raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip()))
+
+ return grammar
+
+# -----------------------------------------------------------------------------
+# ParserReflect()
+#
+# This class represents information extracted for building a parser including
+# start symbol, error function, tokens, precedence list, action functions,
+# etc.
+# -----------------------------------------------------------------------------
+class ParserReflect(object):
+ def __init__(self,pdict,log=None):
+ self.pdict = pdict
+ self.start = None
+ self.error_func = None
+ self.tokens = None
+ self.files = {}
+ self.grammar = []
+ self.error = 0
+
+ if log is None:
+ self.log = PlyLogger(sys.stderr)
+ else:
+ self.log = log
+
+ # Get all of the basic information
+ def get_all(self):
+ self.get_start()
+ self.get_error_func()
+ self.get_tokens()
+ self.get_precedence()
+ self.get_pfunctions()
+
+ # Validate all of the information
+ def validate_all(self):
+ self.validate_start()
+ self.validate_error_func()
+ self.validate_tokens()
+ self.validate_precedence()
+ self.validate_pfunctions()
+ self.validate_files()
+ return self.error
+
+ # Compute a signature over the grammar
+ def signature(self):
+ try:
+ from hashlib import md5
+ except ImportError:
+ from md5 import md5
+ try:
+ sig = md5()
+ if self.start:
+ sig.update(self.start.encode('latin-1'))
+ if self.prec:
+ sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1'))
+ if self.tokens:
+ sig.update(" ".join(self.tokens).encode('latin-1'))
+ for f in self.pfuncs:
+ if f[3]:
+ sig.update(f[3].encode('latin-1'))
+ except (TypeError,ValueError):
+ pass
+ return sig.digest()
+
+ # -----------------------------------------------------------------------------
+ # validate_file()
+ #
+ # 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
+ # users to make mistakes by cutting and pasting code fragments (and it's a real
+ # bugger to try and figure out why the resulting parser doesn't work). Therefore,
+ # we just do a little regular expression pattern matching of def statements
+ # to try and detect duplicates.
+ # -----------------------------------------------------------------------------
+
+ def validate_files(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.
+
+ try:
+ f = open(filename)
+ lines = f.readlines()
+ f.close()
+ except IOError:
+ continue
+
+ counthash = { }
+ for linen,l in enumerate(lines):
+ linen += 1
+ m = fre.match(l)
+ 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)
+
+ # Get the start symbol
+ def get_start(self):
+ self.start = self.pdict.get('start')
+
+ # Validate the start symbol
+ def validate_start(self):
+ if self.start is not None:
+ if not isinstance(self.start,str):
+ self.log.error("'start' must be a string")
+
+ # Look for error handler
+ def get_error_func(self):
+ self.error_func = self.pdict.get('p_error')
+
+ # Validate the error function
+ def validate_error_func(self):
+ if self.error_func:
+ 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
+ return
+
+ eline = func_code(self.error_func).co_firstlineno
+ efile = func_code(self.error_func).co_filename
+ self.files[efile] = 1
+
+ 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
+
+ # Get the tokens map
+ def get_tokens(self):
+ tokens = self.pdict.get("tokens",None)
+ if not tokens:
+ self.log.error("No token list is defined")
+ self.error = 1
+ return
+
+ if not isinstance(tokens,(list, tuple)):
+ self.log.error("tokens must be a list or tuple")
+ self.error = 1
+ return
+
+ if not tokens:
+ self.log.error("tokens is empty")
+ self.error = 1
+ return
+
+ self.tokens = 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
+ return
+
+ terminals = {}
+ for n in self.tokens:
+ if n in terminals:
+ self.log.warning("Token '%s' multiply defined", n)
+ terminals[n] = 1
+
+ # Get the precedence map (if any)
+ def get_precedence(self):
+ self.prec = self.pdict.get("precedence",None)
+
+ # 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
+ return
+ for level,p in enumerate(self.prec):
+ if not isinstance(p,(list,tuple)):
+ self.log.error("Bad precedence table")
+ self.error = 1
+ return
+
+ if len(p) < 2:
+ self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p)
+ self.error = 1
+ return
+ assoc = p[0]
+ if not isinstance(assoc,str):
+ self.log.error("precedence associativity must be a string")
+ self.error = 1
+ return
+ for term in p[1:]:
+ if not isinstance(term,str):
+ self.log.error("precedence items must be strings")
+ self.error = 1
+ return
+ 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()
+ 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:
+ 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
+ elif not func.__doc__:
+ self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__)
+ else:
+ try:
+ parsed_g = parse_grammar(doc,file,line)
+ for g in parsed_g:
+ grammar.append((name, g))
+ except SyntaxError:
+ e = sys.exc_info()[1]
+ self.log.error(str(e))
+ self.error = 1
+
+ # Looks like a valid grammar rule
+ # Mark the file in which defined.
+ self.files[file] = 1
+
+ # 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
+
+ self.grammar = grammar
+
+# -----------------------------------------------------------------------------
+# yacc(module)
+#
+# 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
+
+ if picklefile:
+ write_tables = 0
+
+ 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)]
+ pdict = dict(_items)
+ else:
+ pdict = get_caller_module_dict(2)
+
+ # Collect parser information from the dictionary
+ 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
+
+ if debuglog is None:
+ if debug:
+ debuglog = PlyLogger(open(debugfile,"w"))
+ else:
+ debuglog = NullLogger()
+
+ debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__)
+
+
+ errors = 0
+
+ # Validate the parser information
+ if pinfo.validate_all():
+ raise YaccError("Unable to build parser")
+
+ if not pinfo.error_func:
+ errorlog.warning("no p_error() function is defined")
+
+ # Create a grammar object
+ grammar = Grammar(pinfo.tokens)
+
+ # 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))
+
+ # 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
+
+ # Set the grammar start symbols
+ try:
+ if start is None:
+ grammar.set_start(pinfo.start)
+ else:
+ grammar.set_start(start)
+ except GrammarError:
+ e = sys.exc_info()[1]
+ errorlog.error(str(e))
+ errors = 1
+
+ if errors:
+ 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
+
+ unused_terminals = grammar.unused_terminals()
+ if unused_terminals:
+ 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)
+
+ # 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)
+
+ # 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)
+
+ if len(unused_terminals) == 1:
+ errorlog.warning("There is 1 unused token")
+ if len(unused_terminals) > 1:
+ errorlog.warning("There are %d unused tokens", len(unused_terminals))
+
+ if len(unused_rules) == 1:
+ errorlog.warning("There is 1 unused rule")
+ if len(unused_rules) > 1:
+ errorlog.warning("There are %d unused rules", len(unused_rules))
+
+ if debug:
+ 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("")
+ 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("")
+
+ if check_recursion:
+ unreachable = grammar.find_unreachable()
+ for u in unreachable:
+ errorlog.warning("Symbol '%s' is unreachable",u)
+
+ infinite = grammar.infinite_cycles()
+ for inf in infinite:
+ errorlog.error("Infinite recursion detected for symbol '%s'", inf)
+ errors = 1
+
+ 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
+
+ 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)
+
+ 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")
+ elif num_sr > 1:
+ errorlog.warning("%d shift/reduce conflicts", num_sr)
+
+ num_rr = len(lr.rr_conflicts)
+ if num_rr == 1:
+ errorlog.warning("1 reduce/reduce conflict")
+ elif num_rr > 1:
+ 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("")
+
+ 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 = {}
+ for state, rule, rejected in lr.rr_conflicts:
+ 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
+
+ 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)
+ 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)
+
+ parse = parser.parse
+ return parser
diff --git a/components/script/dom/bindings/codegen/pythonpath.py b/components/script/dom/bindings/codegen/pythonpath.py
new file mode 100644
index 00000000000..49b2d2f740f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/pythonpath.py
@@ -0,0 +1,60 @@
+# 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, os
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/components/script/dom/bindings/codegen/stubgenerator/Skeleton.cpp b/components/script/dom/bindings/codegen/stubgenerator/Skeleton.cpp
new file mode 100644
index 00000000000..dfa17d23400
--- /dev/null
+++ b/components/script/dom/bindings/codegen/stubgenerator/Skeleton.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "Skeleton.h"
+#include "mozilla/dom/SkeletonBinding.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Skeleton)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Skeleton)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Skeleton)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Skeleton)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+Skeleton::Skeleton()
+{
+ SetIsDOMBinding();
+}
+
+Skeleton::~Skeleton()
+{
+}
+
+JSObject*
+Skeleton::WrapObject(JSContext* aCx, JSObject* aScope,
+ bool* aTriedToWrap)
+{
+ return SkeletonBinding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+}
+}
+
diff --git a/components/script/dom/bindings/codegen/stubgenerator/Skeleton.h b/components/script/dom/bindings/codegen/stubgenerator/Skeleton.h
new file mode 100644
index 00000000000..286cff9af4a
--- /dev/null
+++ b/components/script/dom/bindings/codegen/stubgenerator/Skeleton.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#pragma once
+
+#include "nsWrapperCache.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Attributes.h"
+
+struct JSContext;
+
+namespace mozilla {
+namespace dom {
+
+class Skeleton MOZ_FINAL : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ Skeleton();
+ ~Skeleton();
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Skeleton)
+
+ void* GetParentObject() const
+ {
+ // TODO: return something sensible here, and change the return type
+ return somethingSensible;
+ }
+
+ virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
+ bool* aTriedToWrap);
+};
+
+}
+}
+
diff --git a/components/script/dom/bindings/codegen/stubgenerator/generate.sh b/components/script/dom/bindings/codegen/stubgenerator/generate.sh
new file mode 100644
index 00000000000..52577f6f42f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/stubgenerator/generate.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# This script creates a skeleton implementation for a C++ class which
+# implements a Web IDL interface.
+
+# This script is released into the public domain.
+
+if [ -z "$1" ]; then
+ echo usage: ./generate.sh ClassName
+ exit 1
+fi
+
+expression="s/Skeleton/$1/g"
+
+sed "$expression" < Skeleton.h > "$1.h"
+sed "$expression" < Skeleton.cpp > "$1.cpp"
+
diff --git a/components/script/dom/bindings/codegen/test/Makefile.in b/components/script/dom/bindings/codegen/test/Makefile.in
new file mode 100644
index 00000000000..d8104db5ffd
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/Makefile.in
@@ -0,0 +1,87 @@
+# 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/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+relativesrcdir = @relativesrcdir@
+
+MODULE = dom
+LIBRARY_NAME = dombindings_test_s
+LIBXUL_LIBRARY = 1
+FORCE_STATIC_LIB = 1
+# Do NOT export this library. We don't actually want our test code
+# being added to libxul or anything.
+
+include $(DEPTH)/config/autoconf.mk
+
+# Need this to find all our DOM source files.
+include $(topsrcdir)/dom/dom-config.mk
+
+# And need this for $(test_webidl_files)
+include $(topsrcdir)/dom/webidl/WebIDL.mk
+
+# But the webidl actually lives in our parent dir
+test_webidl_files := $(addprefix ../,$(test_webidl_files))
+
+CPPSRCS := $(subst .webidl,Binding.cpp,$(test_webidl_files))
+
+LOCAL_INCLUDES += \
+ -I$(topsrcdir)/js/xpconnect/src \
+ -I$(topsrcdir)/js/xpconnect/wrappers \
+ -I$(topsrcdir)/dom/bindings \
+ $(NULL)
+
+
+# If you change bindinggen_dependencies here, change it in
+# dom/bindings/Makefile.in too. But note that we include ../Makefile
+# here manually, since $(GLOBAL_DEPS) won't cover it.
+bindinggen_dependencies := \
+ ../BindingGen.py \
+ ../Bindings.conf \
+ ../Configuration.py \
+ ../Codegen.py \
+ ../parser/WebIDL.py \
+ ../ParserResults.pkl \
+ ../Makefile \
+ $(GLOBAL_DEPS) \
+ $(NULL)
+
+MOCHITEST_FILES := \
+ test_bug773326.html \
+ test_enums.html \
+ test_integers.html \
+ test_interfaceToString.html \
+ test_lookupGetter.html \
+ test_InstanceOf.html \
+ test_traceProtos.html \
+ test_forOf.html \
+ forOf_iframe.html \
+ test_sequence_wrapping.html \
+ file_bug775543.html \
+ test_bug788369.html \
+ $(NULL)
+
+MOCHITEST_CHROME_FILES = \
+ test_bug775543.html \
+ $(NULL)
+
+# Include rules.mk before any of our targets so our first target is coming from
+# rules.mk and running make with no target in this dir does the right thing.
+include $(topsrcdir)/config/rules.mk
+
+$(CPPSRCS): ../%Binding.cpp: $(bindinggen_dependencies) \
+ ../%.webidl \
+ $(NULL)
+ $(MAKE) -C .. $*Binding.h
+ $(MAKE) -C .. $*Binding.cpp
+
+check::
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py
+
+check-interactive:
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py -q
diff --git a/components/script/dom/bindings/codegen/test/TestBindingHeader.h b/components/script/dom/bindings/codegen/test/TestBindingHeader.h
new file mode 100644
index 00000000000..1fbab0a9fb8
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/TestBindingHeader.h
@@ -0,0 +1,653 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+#ifndef TestBindingHeader_h
+#define TestBindingHeader_h
+
+#include "nsWrapperCache.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/TypedArray.h"
+#include "nsCOMPtr.h"
+// We don't export TestCodeGenBinding.h, but it's right in our parent dir.
+#include "../TestCodeGenBinding.h"
+#include "mozilla/dom/UnionTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+// IID for the TestNonCastableInterface
+#define NS_TEST_NONCASTABLE_INTERFACE_IID \
+{ 0x7c9f8ee2, 0xc9bf, 0x46ca, \
+ { 0xa0, 0xa9, 0x03, 0xa8, 0xd6, 0x30, 0x0e, 0xde } }
+
+class TestNonCastableInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEST_NONCASTABLE_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+};
+
+// IID for the IndirectlyImplementedInterface
+#define NS_INDIRECTLY_IMPLEMENTED_INTERFACE_IID \
+{ 0xfed55b69, 0x7012, 0x4849, \
+ { 0xaf, 0x56, 0x4b, 0xa9, 0xee, 0x41, 0x30, 0x89 } }
+
+class IndirectlyImplementedInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_INDIRECTLY_IMPLEMENTED_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ bool IndirectlyImplementedProperty();
+ void IndirectlyImplementedProperty(bool);
+ void IndirectlyImplementedMethod();
+};
+
+// IID for the TestExternalInterface
+#define NS_TEST_EXTERNAL_INTERFACE_IID \
+{ 0xd5ba0c99, 0x9b1d, 0x4e71, \
+ { 0x8a, 0x94, 0x56, 0x38, 0x6c, 0xa3, 0xda, 0x3d } }
+class TestExternalInterface : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEST_EXTERNAL_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+};
+
+// IID for the TestCallbackInterface
+#define NS_TEST_CALLBACK_INTERFACE_IID \
+{ 0xbf711ba4, 0xc8f6, 0x46cf, \
+ { 0xba, 0x5b, 0xaa, 0xe2, 0x78, 0x18, 0xe6, 0x4a } }
+class TestCallbackInterface : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEST_CALLBACK_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+};
+
+class TestNonWrapperCacheInterface : public nsISupports
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ virtual JSObject* WrapObject(JSContext* cx, JSObject* scope);
+};
+
+class OnlyForUseInConstructor : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+};
+
+class TestInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ // And now our actual WebIDL API
+ // Constructors
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*, ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*, const nsAString&,
+ ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*, uint32_t,
+ Nullable<bool>&, ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*, TestInterface*,
+ ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*,
+ TestNonCastableInterface&,
+ ErrorResult&);
+ /* static
+ already_AddRefed<TestInterface> Constructor(nsISupports*,
+ uint32_t, uint32_t,
+ const TestInterfaceOrOnlyForUseInConstructor&,
+ ErrorResult&);
+ */
+
+ // Integer types
+ int8_t ReadonlyByte();
+ int8_t WritableByte();
+ void SetWritableByte(int8_t);
+ void PassByte(int8_t);
+ int8_t ReceiveByte();
+ void PassOptionalByte(const Optional<int8_t>&);
+ void PassOptionalByteWithDefault(int8_t);
+ void PassNullableByte(Nullable<int8_t>&);
+ void PassOptionalNullableByte(const Optional< Nullable<int8_t> >&);
+
+ int16_t ReadonlyShort();
+ int16_t WritableShort();
+ void SetWritableShort(int16_t);
+ void PassShort(int16_t);
+ int16_t ReceiveShort();
+ void PassOptionalShort(const Optional<int16_t>&);
+ void PassOptionalShortWithDefault(int16_t);
+
+ int32_t ReadonlyLong();
+ int32_t WritableLong();
+ void SetWritableLong(int32_t);
+ void PassLong(int32_t);
+ int16_t ReceiveLong();
+ void PassOptionalLong(const Optional<int32_t>&);
+ void PassOptionalLongWithDefault(int32_t);
+
+ int64_t ReadonlyLongLong();
+ int64_t WritableLongLong();
+ void SetWritableLongLong(int64_t);
+ void PassLongLong(int64_t);
+ int64_t ReceiveLongLong();
+ void PassOptionalLongLong(const Optional<int64_t>&);
+ void PassOptionalLongLongWithDefault(int64_t);
+
+ uint8_t ReadonlyOctet();
+ uint8_t WritableOctet();
+ void SetWritableOctet(uint8_t);
+ void PassOctet(uint8_t);
+ uint8_t ReceiveOctet();
+ void PassOptionalOctet(const Optional<uint8_t>&);
+ void PassOptionalOctetWithDefault(uint8_t);
+
+ uint16_t ReadonlyUnsignedShort();
+ uint16_t WritableUnsignedShort();
+ void SetWritableUnsignedShort(uint16_t);
+ void PassUnsignedShort(uint16_t);
+ uint16_t ReceiveUnsignedShort();
+ void PassOptionalUnsignedShort(const Optional<uint16_t>&);
+ void PassOptionalUnsignedShortWithDefault(uint16_t);
+
+ uint32_t ReadonlyUnsignedLong();
+ uint32_t WritableUnsignedLong();
+ void SetWritableUnsignedLong(uint32_t);
+ void PassUnsignedLong(uint32_t);
+ uint32_t ReceiveUnsignedLong();
+ void PassOptionalUnsignedLong(const Optional<uint32_t>&);
+ void PassOptionalUnsignedLongWithDefault(uint32_t);
+
+ uint64_t ReadonlyUnsignedLongLong();
+ uint64_t WritableUnsignedLongLong();
+ void SetWritableUnsignedLongLong(uint64_t);
+ void PassUnsignedLongLong(uint64_t);
+ uint64_t ReceiveUnsignedLongLong();
+ void PassOptionalUnsignedLongLong(const Optional<uint64_t>&);
+ void PassOptionalUnsignedLongLongWithDefault(uint64_t);
+
+ // Interface types
+ already_AddRefed<TestInterface> ReceiveSelf();
+ already_AddRefed<TestInterface> ReceiveNullableSelf();
+ TestInterface* ReceiveWeakSelf();
+ TestInterface* ReceiveWeakNullableSelf();
+ void PassSelf(TestInterface&);
+ void PassSelf2(NonNull<TestInterface>&);
+ void PassNullableSelf(TestInterface*);
+ already_AddRefed<TestInterface> NonNullSelf();
+ void SetNonNullSelf(TestInterface&);
+ already_AddRefed<TestInterface> GetNullableSelf();
+ void SetNullableSelf(TestInterface*);
+ void PassOptionalSelf(const Optional<TestInterface*> &);
+ void PassOptionalNonNullSelf(const Optional<NonNull<TestInterface> >&);
+ void PassOptionalSelfWithDefault(TestInterface*);
+
+ already_AddRefed<TestNonWrapperCacheInterface> ReceiveNonWrapperCacheInterface();
+ already_AddRefed<TestNonWrapperCacheInterface> ReceiveNullableNonWrapperCacheInterface();
+ void ReceiveNonWrapperCacheInterfaceSequence(nsTArray<nsRefPtr<TestNonWrapperCacheInterface> >&);
+ void ReceiveNullableNonWrapperCacheInterfaceSequence(nsTArray<nsRefPtr<TestNonWrapperCacheInterface> >&);
+ void ReceiveNonWrapperCacheInterfaceNullableSequence(Nullable<nsTArray<nsRefPtr<TestNonWrapperCacheInterface> > >&);
+ void ReceiveNullableNonWrapperCacheInterfaceNullableSequence(Nullable<nsTArray<nsRefPtr<TestNonWrapperCacheInterface> > >&);
+
+ already_AddRefed<TestNonCastableInterface> ReceiveOther();
+ already_AddRefed<TestNonCastableInterface> ReceiveNullableOther();
+ TestNonCastableInterface* ReceiveWeakOther();
+ TestNonCastableInterface* ReceiveWeakNullableOther();
+ void PassOther(TestNonCastableInterface&);
+ void PassOther2(NonNull<TestNonCastableInterface>&);
+ void PassNullableOther(TestNonCastableInterface*);
+ already_AddRefed<TestNonCastableInterface> NonNullOther();
+ void SetNonNullOther(TestNonCastableInterface&);
+ already_AddRefed<TestNonCastableInterface> GetNullableOther();
+ void SetNullableOther(TestNonCastableInterface*);
+ void PassOptionalOther(const Optional<TestNonCastableInterface*>&);
+ void PassOptionalNonNullOther(const Optional<NonNull<TestNonCastableInterface> >&);
+ void PassOptionalOtherWithDefault(TestNonCastableInterface*);
+
+ already_AddRefed<TestExternalInterface> ReceiveExternal();
+ already_AddRefed<TestExternalInterface> ReceiveNullableExternal();
+ TestExternalInterface* ReceiveWeakExternal();
+ TestExternalInterface* ReceiveWeakNullableExternal();
+ void PassExternal(TestExternalInterface*);
+ void PassExternal2(TestExternalInterface*);
+ void PassNullableExternal(TestExternalInterface*);
+ already_AddRefed<TestExternalInterface> NonNullExternal();
+ void SetNonNullExternal(TestExternalInterface*);
+ already_AddRefed<TestExternalInterface> GetNullableExternal();
+ void SetNullableExternal(TestExternalInterface*);
+ void PassOptionalExternal(const Optional<TestExternalInterface*>&);
+ void PassOptionalNonNullExternal(const Optional<TestExternalInterface*>&);
+ void PassOptionalExternalWithDefault(TestExternalInterface*);
+
+ already_AddRefed<TestCallbackInterface> ReceiveCallbackInterface();
+ already_AddRefed<TestCallbackInterface> ReceiveNullableCallbackInterface();
+ TestCallbackInterface* ReceiveWeakCallbackInterface();
+ TestCallbackInterface* ReceiveWeakNullableCallbackInterface();
+ void PassCallbackInterface(TestCallbackInterface&);
+ void PassCallbackInterface2(OwningNonNull<TestCallbackInterface>);
+ void PassNullableCallbackInterface(TestCallbackInterface*);
+ already_AddRefed<TestCallbackInterface> NonNullCallbackInterface();
+ void SetNonNullCallbackInterface(TestCallbackInterface&);
+ already_AddRefed<TestCallbackInterface> GetNullableCallbackInterface();
+ void SetNullableCallbackInterface(TestCallbackInterface*);
+ void PassOptionalCallbackInterface(const Optional<nsRefPtr<TestCallbackInterface> >&);
+ void PassOptionalNonNullCallbackInterface(const Optional<OwningNonNull<TestCallbackInterface> >&);
+ void PassOptionalCallbackInterfaceWithDefault(TestCallbackInterface*);
+
+ already_AddRefed<IndirectlyImplementedInterface> ReceiveConsequentialInterface();
+ void PassConsequentialInterface(IndirectlyImplementedInterface&);
+
+ // Sequence types
+ void ReceiveSequence(nsTArray<int32_t>&);
+ void ReceiveNullableSequence(Nullable< nsTArray<int32_t> >&);
+ void ReceiveSequenceOfNullableInts(nsTArray< Nullable<int32_t> >&);
+ void ReceiveNullableSequenceOfNullableInts(Nullable< nsTArray< Nullable<int32_t> > >&);
+ void PassSequence(const Sequence<int32_t> &);
+ void PassNullableSequence(const Nullable< Sequence<int32_t> >&);
+ void PassSequenceOfNullableInts(const Sequence<Nullable<int32_t> >&);
+ void PassOptionalSequenceOfNullableInts(const Optional<Sequence<Nullable<int32_t> > > &);
+ void PassOptionalNullableSequenceOfNullableInts(const Optional<Nullable<Sequence<Nullable<int32_t> > > > &);
+ void ReceiveCastableObjectSequence(nsTArray< nsRefPtr<TestInterface> > &);
+ void ReceiveNullableCastableObjectSequence(nsTArray< nsRefPtr<TestInterface> > &);
+ void ReceiveCastableObjectNullableSequence(Nullable< nsTArray< nsRefPtr<TestInterface> > >&);
+ void ReceiveNullableCastableObjectNullableSequence(Nullable< nsTArray< nsRefPtr<TestInterface> > >&);
+ void ReceiveWeakCastableObjectSequence(nsTArray<TestInterface*> &);
+ void ReceiveWeakNullableCastableObjectSequence(nsTArray<TestInterface*> &);
+ void ReceiveWeakCastableObjectNullableSequence(Nullable< nsTArray<TestInterface*> >&);
+ void ReceiveWeakNullableCastableObjectNullableSequence(Nullable< nsTArray<TestInterface*> >&);
+ void PassCastableObjectSequence(const Sequence< OwningNonNull<TestInterface> >&);
+ void PassNullableCastableObjectSequence(const Sequence< nsRefPtr<TestInterface> > &);
+ void PassCastableObjectNullableSequence(const Nullable< Sequence< OwningNonNull<TestInterface> > >&);
+ void PassNullableCastableObjectNullableSequence(const Nullable< Sequence< nsRefPtr<TestInterface> > >&);
+ void PassOptionalSequence(const Optional<Sequence<int32_t> >&);
+ void PassOptionalNullableSequence(const Optional<Nullable<Sequence<int32_t> > >&);
+ void PassOptionalNullableSequenceWithDefaultValue(const Nullable< Sequence<int32_t> >&);
+ void PassOptionalObjectSequence(const Optional<Sequence<OwningNonNull<TestInterface> > >&);
+
+ void ReceiveStringSequence(nsTArray<nsString>&);
+ void PassStringSequence(const Sequence<nsString>&);
+
+ void ReceiveAnySequence(JSContext*, nsTArray<JS::Value>&);
+ void ReceiveNullableAnySequence(JSContext*, Nullable<nsTArray<JS::Value> >);
+
+ // Typed array types
+ void PassArrayBuffer(ArrayBuffer&);
+ void PassNullableArrayBuffer(ArrayBuffer*);
+ void PassOptionalArrayBuffer(const Optional<ArrayBuffer>&);
+ void PassOptionalNullableArrayBuffer(const Optional<ArrayBuffer*>&);
+ void PassOptionalNullableArrayBufferWithDefaultValue(ArrayBuffer*);
+ void PassArrayBufferView(ArrayBufferView&);
+ void PassInt8Array(Int8Array&);
+ void PassInt16Array(Int16Array&);
+ void PassInt32Array(Int32Array&);
+ void PassUint8Array(Uint8Array&);
+ void PassUint16Array(Uint16Array&);
+ void PassUint32Array(Uint32Array&);
+ void PassUint8ClampedArray(Uint8ClampedArray&);
+ void PassFloat32Array(Float32Array&);
+ void PassFloat64Array(Float64Array&);
+ JSObject* ReceiveUint8Array(JSContext*);
+
+ // String types
+ void PassString(const nsAString&);
+ void PassNullableString(const nsAString&);
+ void PassOptionalString(const Optional<nsAString>&);
+ void PassOptionalStringWithDefaultValue(const nsAString&);
+ void PassOptionalNullableString(const Optional<nsAString>&);
+ void PassOptionalNullableStringWithDefaultValue(const nsAString&);
+
+ // Enumarated types
+ void PassEnum(TestEnum);
+ void PassOptionalEnum(const Optional<TestEnum>&);
+ void PassEnumWithDefault(TestEnum);
+ TestEnum ReceiveEnum();
+ TestEnum EnumAttribute();
+ TestEnum ReadonlyEnumAttribute();
+ void SetEnumAttribute(TestEnum);
+
+ // Callback types
+ void PassCallback(JSContext*, JSObject*);
+ void PassNullableCallback(JSContext*, JSObject*);
+ void PassOptionalCallback(JSContext*, const Optional<JSObject*>&);
+ void PassOptionalNullableCallback(JSContext*, const Optional<JSObject*>&);
+ void PassOptionalNullableCallbackWithDefaultValue(JSContext*, JSObject*);
+ JSObject* ReceiveCallback(JSContext*);
+ JSObject* ReceiveNullableCallback(JSContext*);
+
+ // Any types
+ void PassAny(JSContext*, JS::Value);
+ void PassOptionalAny(JSContext*, const Optional<JS::Value>&);
+ void PassAnyDefaultNull(JSContext*, JS::Value);
+ JS::Value ReceiveAny(JSContext*);
+
+ // object types
+ void PassObject(JSContext*, JSObject&);
+ void PassNullableObject(JSContext*, JSObject*);
+ void PassOptionalObject(JSContext*, const Optional<NonNull<JSObject> >&);
+ void PassOptionalNullableObject(JSContext*, const Optional<JSObject*>&);
+ void PassOptionalNullableObjectWithDefaultValue(JSContext*, JSObject*);
+ JSObject* ReceiveObject(JSContext*);
+ JSObject* ReceiveNullableObject(JSContext*);
+
+ // Union types
+ void PassUnion(JSContext*, const ObjectOrLong& arg);
+ void PassUnionWithNullable(JSContext*, const ObjectOrNullOrLong& arg)
+ {
+ ObjectOrLong returnValue;
+ if (arg.IsNull()) {
+ } else if (arg.IsObject()) {
+ JSObject& obj = (JSObject&)arg.GetAsObject();
+ JS_GetClass(&obj);
+ //returnValue.SetAsObject(&obj);
+ } else {
+ int32_t i = arg.GetAsLong();
+ i += 1;
+ }
+ }
+ void PassNullableUnion(JSContext*, const Nullable<ObjectOrLong>&);
+ void PassOptionalUnion(JSContext*, const Optional<ObjectOrLong>&);
+ void PassOptionalNullableUnion(JSContext*, const Optional<Nullable<ObjectOrLong> >&);
+ void PassOptionalNullableUnionWithDefaultValue(JSContext*, const Nullable<ObjectOrLong>&);
+ //void PassUnionWithInterfaces(const TestInterfaceOrTestExternalInterface& arg);
+ //void PassUnionWithInterfacesAndNullable(const TestInterfaceOrNullOrTestExternalInterface& arg);
+ void PassUnionWithArrayBuffer(const ArrayBufferOrLong&);
+ void PassUnionWithString(JSContext*, const StringOrObject&);
+ //void PassUnionWithEnum(JSContext*, const TestEnumOrObject&);
+ void PassUnionWithCallback(JSContext*, const TestCallbackOrLong&);
+ void PassUnionWithObject(JSContext*, const ObjectOrLong&);
+
+ // binaryNames tests
+ void MethodRenamedTo();
+ void MethodRenamedTo(int8_t);
+ int8_t AttributeGetterRenamedTo();
+ int8_t AttributeRenamedTo();
+ void SetAttributeRenamedTo(int8_t);
+
+ // Dictionary tests
+ void PassDictionary(const Dict&);
+ void PassOtherDictionary(const GrandparentDict&);
+ void PassSequenceOfDictionaries(const Sequence<Dict>&);
+ void PassDictionaryOrLong(const Dict&);
+ void PassDictionaryOrLong(int32_t);
+ void PassDictContainingDict(const DictContainingDict&);
+ void PassDictContainingSequence(const DictContainingSequence&);
+
+ // Typedefs
+ void ExerciseTypedefInterfaces1(TestInterface&);
+ already_AddRefed<TestInterface> ExerciseTypedefInterfaces2(TestInterface*);
+ void ExerciseTypedefInterfaces3(TestInterface&);
+
+ // Miscellania
+ int32_t AttrWithLenientThis();
+ void SetAttrWithLenientThis(int32_t);
+
+ // Methods and properties imported via "implements"
+ bool ImplementedProperty();
+ void SetImplementedProperty(bool);
+ void ImplementedMethod();
+ bool ImplementedParentProperty();
+ void SetImplementedParentProperty(bool);
+ void ImplementedParentMethod();
+ bool IndirectlyImplementedProperty();
+ void SetIndirectlyImplementedProperty(bool);
+ void IndirectlyImplementedMethod();
+ uint32_t DiamondImplementedProperty();
+
+ // Test EnforceRange/Clamp
+ void DontEnforceRangeOrClamp(int8_t);
+ void DoEnforceRange(int8_t);
+ void DoClamp(int8_t);
+
+private:
+ // We add signatures here that _could_ start matching if the codegen
+ // got data types wrong. That way if it ever does we'll have a call
+ // to these private deleted methods and compilation will fail.
+ void SetReadonlyByte(int8_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableByte(T) MOZ_DELETE;
+ template<typename T>
+ void PassByte(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalByte(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalByteWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyShort(int16_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableShort(T) MOZ_DELETE;
+ template<typename T>
+ void PassShort(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalShort(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalShortWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyLong(int32_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalLong(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalLongWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyLongLong(int64_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableLongLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassLongLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalLongLong(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalLongLongWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyOctet(uint8_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableOctet(T) MOZ_DELETE;
+ template<typename T>
+ void PassOctet(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalOctet(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalOctetWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyUnsignedShort(uint16_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableUnsignedShort(T) MOZ_DELETE;
+ template<typename T>
+ void PassUnsignedShort(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedShort(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedShortWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyUnsignedLong(uint32_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableUnsignedLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassUnsignedLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedLong(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedLongWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyUnsignedLongLong(uint64_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableUnsignedLongLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassUnsignedLongLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedLongLong(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedLongLongWithDefault(T) MOZ_DELETE;
+
+ // Enforce that only const things are passed for sequences
+ void PassSequence(Sequence<int32_t> &) MOZ_DELETE;
+ void PassNullableSequence(Nullable< Sequence<int32_t> >&) MOZ_DELETE;
+ void PassOptionalNullableSequenceWithDefaultValue(Nullable< Sequence<int32_t> >&) MOZ_DELETE;
+
+ // Enforce that only const things are passed for optional
+ void PassOptionalByte(Optional<int8_t>&) MOZ_DELETE;
+ void PassOptionalNullableByte(Optional<Nullable<int8_t> >&) MOZ_DELETE;
+ void PassOptionalShort(Optional<int16_t>&) MOZ_DELETE;
+ void PassOptionalLong(Optional<int32_t>&) MOZ_DELETE;
+ void PassOptionalLongLong(Optional<int64_t>&) MOZ_DELETE;
+ void PassOptionalOctet(Optional<uint8_t>&) MOZ_DELETE;
+ void PassOptionalUnsignedShort(Optional<uint16_t>&) MOZ_DELETE;
+ void PassOptionalUnsignedLong(Optional<uint32_t>&) MOZ_DELETE;
+ void PassOptionalUnsignedLongLong(Optional<uint64_t>&) MOZ_DELETE;
+ void PassOptionalSelf(Optional<TestInterface*> &) MOZ_DELETE;
+ void PassOptionalNonNullSelf(Optional<NonNull<TestInterface> >&) MOZ_DELETE;
+ void PassOptionalOther(Optional<TestNonCastableInterface*>&);
+ void PassOptionalNonNullOther(Optional<NonNull<TestNonCastableInterface> >&);
+ void PassOptionalExternal(Optional<TestExternalInterface*>&) MOZ_DELETE;
+ void PassOptionalNonNullExternal(Optional<TestExternalInterface*>&) MOZ_DELETE;
+ void PassOptionalSequence(Optional<Sequence<int32_t> >&) MOZ_DELETE;
+ void PassOptionalNullableSequence(Optional<Nullable<Sequence<int32_t> > >&) MOZ_DELETE;
+ void PassOptionalObjectSequence(Optional<Sequence<OwningNonNull<TestInterface> > >&) MOZ_DELETE;
+ void PassOptionalArrayBuffer(Optional<ArrayBuffer>&) MOZ_DELETE;
+ void PassOptionalNullableArrayBuffer(Optional<ArrayBuffer*>&) MOZ_DELETE;
+ void PassOptionalEnum(Optional<TestEnum>&) MOZ_DELETE;
+ void PassOptionalCallback(JSContext*, Optional<JSObject*>&) MOZ_DELETE;
+ void PassOptionalNullableCallback(JSContext*, Optional<JSObject*>&) MOZ_DELETE;
+ void PassOptionalAny(Optional<JS::Value>&) MOZ_DELETE;
+
+ // And test that string stuff is always const
+ void PassString(nsAString&) MOZ_DELETE;
+ void PassNullableString(nsAString&) MOZ_DELETE;
+ void PassOptionalString(Optional<nsAString>&) MOZ_DELETE;
+ void PassOptionalStringWithDefaultValue(nsAString&) MOZ_DELETE;
+ void PassOptionalNullableString(Optional<nsAString>&) MOZ_DELETE;
+ void PassOptionalNullableStringWithDefaultValue(nsAString&) MOZ_DELETE;
+
+};
+
+class TestIndexedGetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ uint32_t IndexedGetter(uint32_t, bool&);
+ uint32_t IndexedGetter(uint32_t&) MOZ_DELETE;
+ uint32_t Item(uint32_t&);
+ uint32_t Item(uint32_t, bool&) MOZ_DELETE;
+ uint32_t Length();
+};
+
+class TestNamedGetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void NamedGetter(const nsAString&, bool&, nsAString&);
+};
+
+class TestIndexedAndNamedGetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ uint32_t IndexedGetter(uint32_t, bool&);
+ void NamedGetter(const nsAString&, bool&, nsAString&);
+ void NamedItem(const nsAString&, nsAString&);
+ uint32_t Length();
+};
+
+class TestIndexedSetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void IndexedSetter(uint32_t, const nsAString&);
+ void SetItem(uint32_t, const nsAString&);
+};
+
+class TestNamedSetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void NamedSetter(const nsAString&, TestIndexedSetterInterface&);
+};
+
+class TestIndexedAndNamedSetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void IndexedSetter(uint32_t, TestIndexedSetterInterface&);
+ void NamedSetter(const nsAString&, TestIndexedSetterInterface&);
+ void SetNamedItem(const nsAString&, TestIndexedSetterInterface&);
+};
+
+class TestIndexedAndNamedGetterAndSetterInterface : public TestIndexedSetterInterface
+{
+public:
+ uint32_t IndexedGetter(uint32_t, bool&);
+ uint32_t Item(uint32_t);
+ void NamedGetter(const nsAString&, bool&, nsAString&);
+ void NamedItem(const nsAString&, nsAString&);
+ void IndexedSetter(uint32_t, int32_t&);
+ void IndexedSetter(uint32_t, const nsAString&) MOZ_DELETE;
+ void NamedSetter(const nsAString&, const nsAString&);
+ void Stringify(nsAString&);
+ uint32_t Length();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* TestBindingHeader_h */
diff --git a/components/script/dom/bindings/codegen/test/TestCodeGen.webidl b/components/script/dom/bindings/codegen/test/TestCodeGen.webidl
new file mode 100644
index 00000000000..8c2b3c1b6b4
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/TestCodeGen.webidl
@@ -0,0 +1,442 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+typedef long myLong;
+typedef TestInterface AnotherNameForTestInterface;
+typedef TestInterface? NullableTestInterface;
+
+interface TestExternalInterface;
+
+interface TestNonCastableInterface {
+};
+
+callback interface TestCallbackInterface {
+ readonly attribute long foo;
+ void doSomething();
+};
+
+enum TestEnum {
+ "a",
+ "b"
+};
+
+callback TestCallback = void();
+
+TestInterface implements ImplementedInterface;
+
+// This interface is only for use in the constructor below
+interface OnlyForUseInConstructor {
+};
+
+[Constructor,
+ Constructor(DOMString str),
+ Constructor(unsigned long num, boolean? bool),
+ Constructor(TestInterface? iface),
+ Constructor(TestNonCastableInterface iface)
+ // , Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3)
+ ]
+interface TestInterface {
+ // Integer types
+ // XXXbz add tests for throwing versions of all the integer stuff
+ readonly attribute byte readonlyByte;
+ attribute byte writableByte;
+ void passByte(byte arg);
+ byte receiveByte();
+ void passOptionalByte(optional byte arg);
+ void passOptionalByteWithDefault(optional byte arg = 0);
+ void passNullableByte(byte? arg);
+ void passOptionalNullableByte(optional byte? arg);
+
+ readonly attribute short readonlyShort;
+ attribute short writableShort;
+ void passShort(short arg);
+ short receiveShort();
+ void passOptionalShort(optional short arg);
+ void passOptionalShortWithDefault(optional short arg = 5);
+
+ readonly attribute long readonlyLong;
+ attribute long writableLong;
+ void passLong(long arg);
+ long receiveLong();
+ void passOptionalLong(optional long arg);
+ void passOptionalLongWithDefault(optional long arg = 7);
+
+ readonly attribute long long readonlyLongLong;
+ attribute long long writableLongLong;
+ void passLongLong(long long arg);
+ long long receiveLongLong();
+ void passOptionalLongLong(optional long long arg);
+ void passOptionalLongLongWithDefault(optional long long arg = -12);
+
+ readonly attribute octet readonlyOctet;
+ attribute octet writableOctet;
+ void passOctet(octet arg);
+ octet receiveOctet();
+ void passOptionalOctet(optional octet arg);
+ void passOptionalOctetWithDefault(optional octet arg = 19);
+
+ readonly attribute unsigned short readonlyUnsignedShort;
+ attribute unsigned short writableUnsignedShort;
+ void passUnsignedShort(unsigned short arg);
+ unsigned short receiveUnsignedShort();
+ void passOptionalUnsignedShort(optional unsigned short arg);
+ void passOptionalUnsignedShortWithDefault(optional unsigned short arg = 2);
+
+ readonly attribute unsigned long readonlyUnsignedLong;
+ attribute unsigned long writableUnsignedLong;
+ void passUnsignedLong(unsigned long arg);
+ unsigned long receiveUnsignedLong();
+ void passOptionalUnsignedLong(optional unsigned long arg);
+ void passOptionalUnsignedLongWithDefault(optional unsigned long arg = 6);
+
+ readonly attribute unsigned long long readonlyUnsignedLongLong;
+ attribute unsigned long long writableUnsignedLongLong;
+ void passUnsignedLongLong(unsigned long long arg);
+ unsigned long long receiveUnsignedLongLong();
+ void passOptionalUnsignedLongLong(optional unsigned long long arg);
+ void passOptionalUnsignedLongLongWithDefault(optional unsigned long long arg = 17);
+
+ // Castable interface types
+ // XXXbz add tests for throwing versions of all the castable interface stuff
+ TestInterface receiveSelf();
+ TestInterface? receiveNullableSelf();
+ TestInterface receiveWeakSelf();
+ TestInterface? receiveWeakNullableSelf();
+ // A verstion to test for casting to TestInterface&
+ void passSelf(TestInterface arg);
+ // A version we can use to test for the exact type passed in
+ void passSelf2(TestInterface arg);
+ void passNullableSelf(TestInterface? arg);
+ attribute TestInterface nonNullSelf;
+ attribute TestInterface? nullableSelf;
+ // Optional arguments
+ void passOptionalSelf(optional TestInterface? arg);
+ void passOptionalNonNullSelf(optional TestInterface arg);
+ void passOptionalSelfWithDefault(optional TestInterface? arg = null);
+
+ // Non-wrapper-cache interface types
+ [Creator]
+ TestNonWrapperCacheInterface receiveNonWrapperCacheInterface();
+ [Creator]
+ TestNonWrapperCacheInterface? receiveNullableNonWrapperCacheInterface();
+ [Creator]
+ sequence<TestNonWrapperCacheInterface> receiveNonWrapperCacheInterfaceSequence();
+ [Creator]
+ sequence<TestNonWrapperCacheInterface?> receiveNullableNonWrapperCacheInterfaceSequence();
+ [Creator]
+ sequence<TestNonWrapperCacheInterface>? receiveNonWrapperCacheInterfaceNullableSequence();
+ [Creator]
+ sequence<TestNonWrapperCacheInterface?>? receiveNullableNonWrapperCacheInterfaceNullableSequence();
+
+ // Non-castable interface types
+ TestNonCastableInterface receiveOther();
+ TestNonCastableInterface? receiveNullableOther();
+ TestNonCastableInterface receiveWeakOther();
+ TestNonCastableInterface? receiveWeakNullableOther();
+ // A verstion to test for casting to TestNonCastableInterface&
+ void passOther(TestNonCastableInterface arg);
+ // A version we can use to test for the exact type passed in
+ void passOther2(TestNonCastableInterface arg);
+ void passNullableOther(TestNonCastableInterface? arg);
+ attribute TestNonCastableInterface nonNullOther;
+ attribute TestNonCastableInterface? nullableOther;
+ // Optional arguments
+ void passOptionalOther(optional TestNonCastableInterface? arg);
+ void passOptionalNonNullOther(optional TestNonCastableInterface arg);
+ void passOptionalOtherWithDefault(optional TestNonCastableInterface? arg = null);
+
+ // External interface types
+ TestExternalInterface receiveExternal();
+ TestExternalInterface? receiveNullableExternal();
+ TestExternalInterface receiveWeakExternal();
+ TestExternalInterface? receiveWeakNullableExternal();
+ // A verstion to test for casting to TestExternalInterface&
+ void passExternal(TestExternalInterface arg);
+ // A version we can use to test for the exact type passed in
+ void passExternal2(TestExternalInterface arg);
+ void passNullableExternal(TestExternalInterface? arg);
+ attribute TestExternalInterface nonNullExternal;
+ attribute TestExternalInterface? nullableExternal;
+ // Optional arguments
+ void passOptionalExternal(optional TestExternalInterface? arg);
+ void passOptionalNonNullExternal(optional TestExternalInterface arg);
+ void passOptionalExternalWithDefault(optional TestExternalInterface? arg = null);
+
+ // Callback interface types
+ TestCallbackInterface receiveCallbackInterface();
+ TestCallbackInterface? receiveNullableCallbackInterface();
+ TestCallbackInterface receiveWeakCallbackInterface();
+ TestCallbackInterface? receiveWeakNullableCallbackInterface();
+ // A verstion to test for casting to TestCallbackInterface&
+ void passCallbackInterface(TestCallbackInterface arg);
+ // A version we can use to test for the exact type passed in
+ void passCallbackInterface2(TestCallbackInterface arg);
+ void passNullableCallbackInterface(TestCallbackInterface? arg);
+ attribute TestCallbackInterface nonNullCallbackInterface;
+ attribute TestCallbackInterface? nullableCallbackInterface;
+ // Optional arguments
+ void passOptionalCallbackInterface(optional TestCallbackInterface? arg);
+ void passOptionalNonNullCallbackInterface(optional TestCallbackInterface arg);
+ void passOptionalCallbackInterfaceWithDefault(optional TestCallbackInterface? arg = null);
+
+ // Miscellaneous interface tests
+ IndirectlyImplementedInterface receiveConsequentialInterface();
+ void passConsequentialInterface(IndirectlyImplementedInterface arg);
+
+ // Sequence types
+ sequence<long> receiveSequence();
+ sequence<long>? receiveNullableSequence();
+ sequence<long?> receiveSequenceOfNullableInts();
+ sequence<long?>? receiveNullableSequenceOfNullableInts();
+ void passSequence(sequence<long> arg);
+ void passNullableSequence(sequence<long>? arg);
+ void passSequenceOfNullableInts(sequence<long?> arg);
+ void passOptionalSequenceOfNullableInts(optional sequence<long?> arg);
+ void passOptionalNullableSequenceOfNullableInts(optional sequence<long?>? arg);
+ sequence<TestInterface> receiveCastableObjectSequence();
+ sequence<TestInterface?> receiveNullableCastableObjectSequence();
+ sequence<TestInterface>? receiveCastableObjectNullableSequence();
+ sequence<TestInterface?>? receiveNullableCastableObjectNullableSequence();
+ sequence<TestInterface> receiveWeakCastableObjectSequence();
+ sequence<TestInterface?> receiveWeakNullableCastableObjectSequence();
+ sequence<TestInterface>? receiveWeakCastableObjectNullableSequence();
+ sequence<TestInterface?>? receiveWeakNullableCastableObjectNullableSequence();
+ void passCastableObjectSequence(sequence<TestInterface> arg);
+ void passNullableCastableObjectSequence(sequence<TestInterface?> arg);
+ void passCastableObjectNullableSequence(sequence<TestInterface>? arg);
+ void passNullableCastableObjectNullableSequence(sequence<TestInterface?>? arg);
+ void passOptionalSequence(optional sequence<long> arg);
+ void passOptionalNullableSequence(optional sequence<long>? arg);
+ void passOptionalNullableSequenceWithDefaultValue(optional sequence<long>? arg = null);
+ void passOptionalObjectSequence(optional sequence<TestInterface> arg);
+
+ sequence<DOMString> receiveStringSequence();
+ void passStringSequence(sequence<DOMString> arg);
+
+ sequence<any> receiveAnySequence();
+ sequence<any>? receiveNullableAnySequence();
+
+ // Typed array types
+ void passArrayBuffer(ArrayBuffer arg);
+ void passNullableArrayBuffer(ArrayBuffer? arg);
+ void passOptionalArrayBuffer(optional ArrayBuffer arg);
+ void passOptionalNullableArrayBuffer(optional ArrayBuffer? arg);
+ void passOptionalNullableArrayBufferWithDefaultValue(optional ArrayBuffer? arg= null);
+ void passArrayBufferView(ArrayBufferView arg);
+ void passInt8Array(Int8Array arg);
+ void passInt16Array(Int16Array arg);
+ void passInt32Array(Int32Array arg);
+ void passUint8Array(Uint8Array arg);
+ void passUint16Array(Uint16Array arg);
+ void passUint32Array(Uint32Array arg);
+ void passUint8ClampedArray(Uint8ClampedArray arg);
+ void passFloat32Array(Float32Array arg);
+ void passFloat64Array(Float64Array arg);
+ Uint8Array receiveUint8Array();
+
+ // String types
+ void passString(DOMString arg);
+ void passNullableString(DOMString? arg);
+ void passOptionalString(optional DOMString arg);
+ void passOptionalStringWithDefaultValue(optional DOMString arg = "abc");
+ void passOptionalNullableString(optional DOMString? arg);
+ void passOptionalNullableStringWithDefaultValue(optional DOMString? arg = null);
+
+ // Enumerated types
+ void passEnum(TestEnum arg);
+ // No support for nullable enums yet
+ // void passNullableEnum(TestEnum? arg);
+ void passOptionalEnum(optional TestEnum arg);
+ void passEnumWithDefault(optional TestEnum arg = "a");
+ // void passOptionalNullableEnum(optional TestEnum? arg);
+ // void passOptionalNullableEnumWithDefaultValue(optional TestEnum? arg = null);
+ TestEnum receiveEnum();
+ attribute TestEnum enumAttribute;
+ readonly attribute TestEnum readonlyEnumAttribute;
+
+ // Callback types
+ void passCallback(TestCallback arg);
+ void passNullableCallback(TestCallback? arg);
+ void passOptionalCallback(optional TestCallback arg);
+ void passOptionalNullableCallback(optional TestCallback? arg);
+ void passOptionalNullableCallbackWithDefaultValue(optional TestCallback? arg = null);
+ TestCallback receiveCallback();
+ TestCallback? receiveNullableCallback();
+
+ // Any types
+ void passAny(any arg);
+ void passOptionalAny(optional any arg);
+ void passAnyDefaultNull(optional any arg = null);
+ any receiveAny();
+
+ // object types
+ void passObject(object arg);
+ void passNullableObject(object? arg);
+ void passOptionalObject(optional object arg);
+ void passOptionalNullableObject(optional object? arg);
+ void passOptionalNullableObjectWithDefaultValue(optional object? arg = null);
+ object receiveObject();
+ object? receiveNullableObject();
+
+ // Union types
+ void passUnion((object or long) arg);
+ void passUnionWithNullable((object? or long) arg);
+ void passNullableUnion((object or long)? arg);
+ void passOptionalUnion(optional (object or long) arg);
+ void passOptionalNullableUnion(optional (object or long)? arg);
+ void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null);
+ //void passUnionWithInterfaces((TestInterface or TestExternalInterface) arg);
+ //void passUnionWithInterfacesAndNullable((TestInterface? or TestExternalInterface) arg);
+ //void passUnionWithSequence((sequence<object> or long) arg);
+ void passUnionWithArrayBuffer((ArrayBuffer or long) arg);
+ void passUnionWithString((DOMString or object) arg);
+ //void passUnionWithEnum((TestEnum or object) arg);
+ void passUnionWithCallback((TestCallback or long) arg);
+ void passUnionWithObject((object or long) arg);
+ //void passUnionWithDict((Dict or long) arg);
+
+ // binaryNames tests
+ void methodRenamedFrom();
+ void methodRenamedFrom(byte argument);
+ readonly attribute byte attributeGetterRenamedFrom;
+ attribute byte attributeRenamedFrom;
+
+ void passDictionary(optional Dict x);
+ void passOtherDictionary(optional GrandparentDict x);
+ void passSequenceOfDictionaries(sequence<Dict> x);
+ void passDictionaryOrLong(optional Dict x);
+ void passDictionaryOrLong(long x);
+
+ void passDictContainingDict(optional DictContainingDict arg);
+ void passDictContainingSequence(optional DictContainingSequence arg);
+
+ // EnforceRange/Clamp tests
+ void dontEnforceRangeOrClamp(byte arg);
+ void doEnforceRange([EnforceRange] byte arg);
+ void doClamp([Clamp] byte arg);
+
+ // Typedefs
+ const myLong myLongConstant = 5;
+ void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
+ AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
+ void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
+
+ // Miscellania
+ [LenientThis] attribute long attrWithLenientThis;
+};
+
+interface TestNonWrapperCacheInterface {
+};
+
+interface ImplementedInterfaceParent {
+ void implementedParentMethod();
+ attribute boolean implementedParentProperty;
+
+ const long implementedParentConstant = 8;
+};
+
+ImplementedInterfaceParent implements IndirectlyImplementedInterface;
+
+[NoInterfaceObject]
+interface IndirectlyImplementedInterface {
+ void indirectlyImplementedMethod();
+ attribute boolean indirectlyImplementedProperty;
+
+ const long indirectlyImplementedConstant = 9;
+};
+
+interface ImplementedInterface : ImplementedInterfaceParent {
+ void implementedMethod();
+ attribute boolean implementedProperty;
+
+ const long implementedConstant = 5;
+};
+
+interface DiamondImplements {
+ readonly attribute long diamondImplementedProperty;
+};
+interface DiamondBranch1A {
+};
+interface DiamondBranch1B {
+};
+interface DiamondBranch2A : DiamondImplements {
+};
+interface DiamondBranch2B : DiamondImplements {
+};
+TestInterface implements DiamondBranch1A;
+TestInterface implements DiamondBranch1B;
+TestInterface implements DiamondBranch2A;
+TestInterface implements DiamondBranch2B;
+DiamondBranch1A implements DiamondImplements;
+DiamondBranch1B implements DiamondImplements;
+
+dictionary Dict : ParentDict {
+ TestEnum someEnum;
+ long x;
+ long a;
+ long b = 8;
+ long z = 9;
+ DOMString str;
+ DOMString empty = "";
+ TestEnum otherEnum = "b";
+ DOMString otherStr = "def";
+ DOMString? yetAnotherStr = null;
+};
+
+dictionary ParentDict : GrandparentDict {
+ long c = 5;
+ TestInterface someInterface;
+ TestExternalInterface someExternalInterface;
+};
+
+dictionary DictContainingDict {
+ Dict memberDict;
+};
+
+dictionary DictContainingSequence {
+ sequence<long> ourSequence;
+};
+
+interface TestIndexedGetterInterface {
+ getter long item(unsigned long index);
+ [Infallible]
+ readonly attribute unsigned long length;
+};
+
+interface TestNamedGetterInterface {
+ getter DOMString (DOMString name);
+};
+
+interface TestIndexedAndNamedGetterInterface {
+ getter long (unsigned long index);
+ getter DOMString namedItem(DOMString name);
+ [Infallible]
+ readonly attribute unsigned long length;
+};
+
+interface TestIndexedSetterInterface {
+ setter creator void setItem(unsigned long index, DOMString item);
+};
+
+interface TestNamedSetterInterface {
+ setter creator void (DOMString name, TestIndexedSetterInterface item);
+};
+
+interface TestIndexedAndNamedSetterInterface {
+ setter creator void (unsigned long index, TestIndexedSetterInterface item);
+ setter creator void setNamedItem(DOMString name, TestIndexedSetterInterface item);
+};
+
+interface TestIndexedAndNamedGetterAndSetterInterface : TestIndexedSetterInterface {
+ getter long item(unsigned long index);
+ getter DOMString namedItem(DOMString name);
+ setter creator void (unsigned long index, long item);
+ setter creator void (DOMString name, DOMString item);
+ [Infallible]
+ stringifier DOMString ();
+ [Infallible]
+ readonly attribute unsigned long length;
+};
diff --git a/components/script/dom/bindings/codegen/test/TestDictionary.webidl b/components/script/dom/bindings/codegen/test/TestDictionary.webidl
new file mode 100644
index 00000000000..3dd91bd6500
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/TestDictionary.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+dictionary GrandparentDict {
+ double someNum;
+}; \ No newline at end of file
diff --git a/components/script/dom/bindings/codegen/test/TestTypedef.webidl b/components/script/dom/bindings/codegen/test/TestTypedef.webidl
new file mode 100644
index 00000000000..7f758c79e8f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/TestTypedef.webidl
@@ -0,0 +1,7 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+typedef TestInterface YetAnotherNameForTestInterface;
diff --git a/components/script/dom/bindings/codegen/test/file_bug775543.html b/components/script/dom/bindings/codegen/test/file_bug775543.html
new file mode 100644
index 00000000000..ee8c14c4d9c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/file_bug775543.html
@@ -0,0 +1,5 @@
+<body>
+<script>
+worker = new Worker("a");
+</script>
+</body>
diff --git a/components/script/dom/bindings/codegen/test/forOf_iframe.html b/components/script/dom/bindings/codegen/test/forOf_iframe.html
new file mode 100644
index 00000000000..91417aba0e8
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/forOf_iframe.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>iframe content for test_forOf_iframe.html</title>
+</head>
+<body>
+ <div id="basket">
+ <span id="egg0"></span>
+ <span id="egg1"><span id="duckling1"></span></span>
+ <span id="egg2"></span>
+ </div>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_InstanceOf.html b/components/script/dom/bindings/codegen/test/test_InstanceOf.html
new file mode 100644
index 00000000000..3a5a76b1b21
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_InstanceOf.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=748983
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 748983</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=748983">Mozilla Bug 748983</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 748983 **/
+ok(document instanceof EventTarget, "document is an event target")
+ok(new XMLHttpRequest() instanceof XMLHttpRequest, "instanceof should work on XHR");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_bug773326.html b/components/script/dom/bindings/codegen/test/test_bug773326.html
new file mode 100644
index 00000000000..2e3b1ea304d
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_bug773326.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for Bug 773326</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ new Worker("data:text/javascript,new XMLHttpRequest(42)");
+}, "Should not crash")
+</script>
diff --git a/components/script/dom/bindings/codegen/test/test_bug775543.html b/components/script/dom/bindings/codegen/test/test_bug775543.html
new file mode 100644
index 00000000000..d8df05f630f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_bug775543.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=775543
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 775543</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=775543">Mozilla Bug 775543</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/tests/dom/bindings/test/file_bug775543.html" onload="test();"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 775543 **/
+
+function test()
+{
+ var a = XPCNativeWrapper(document.getElementById("t").contentWindow.wrappedJSObject.worker);
+ isnot(XPCNativeWrapper.unwrap(a), a, "XPCNativeWrapper(Worker) should be an Xray wrapper");
+ a.toString();
+ ok(true, "Shouldn't crash when calling a method on an Xray wrapper around a worker");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_bug788369.html b/components/script/dom/bindings/codegen/test/test_bug788369.html
new file mode 100644
index 00000000000..787bd28fe34
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_bug788369.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=788369
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 788369</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=788369">Mozilla Bug 788369</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 788369 **/
+try {
+ var xhr = new(window.ActiveXObject || XMLHttpRequest)("Microsoft.XMLHTTP");
+ ok(xhr instanceof XMLHttpRequest, "Should have an XHR object");
+} catch (e) {
+ ok(false, "Should not throw exception when constructing: " + e);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_enums.html b/components/script/dom/bindings/codegen/test/test_enums.html
new file mode 100644
index 00000000000..e5dc519a0c9
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_enums.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Enums</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("get", "foo")
+ assert_equals(xhr.responseType, "");
+ xhr.responseType = "foo";
+ assert_equals(xhr.responseType, "");
+}, "Assigning an invalid value to an enum attribute should not throw.");
+</script>
diff --git a/components/script/dom/bindings/codegen/test/test_forOf.html b/components/script/dom/bindings/codegen/test/test_forOf.html
new file mode 100644
index 00000000000..b1a3032a385
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_forOf.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=725907
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 725907</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=725907">Mozilla Bug 725907</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<div id="basket">
+ <span id="egg0"></span>
+ <span id="egg1"><span id="duckling1"></span></span>
+ <span id="egg2"></span>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 725907 **/
+
+function runTestsForDocument(document, msgSuffix) {
+ function is(a, b, msg) { SimpleTest.is(a, b, msg + msgSuffix); }
+ function isnot(a, b, msg) { SimpleTest.isnot(a, b, msg + msgSuffix); }
+
+ var basket = document.getElementById("basket");
+ var egg3 = document.createElement("span");
+ egg3.id = "egg3";
+
+ var log = '';
+ for (var x of basket.childNodes) {
+ if (x.nodeType != x.TEXT_NODE)
+ log += x.id + ";";
+ }
+ is(log, "egg0;egg1;egg2;", "'for (x of div.childNodes)' should iterate over child nodes");
+
+ log = '';
+ for (var x of basket.childNodes) {
+ if (x.nodeType != x.TEXT_NODE) {
+ log += x.id + ";";
+ if (x.id == "egg1")
+ basket.appendChild(egg3);
+ }
+ }
+ is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.childNodes)' should see elements added during iteration");
+
+ var iter1 = basket.childNodes.iterator();
+ var iter2 = basket.childNodes.iterator();
+ isnot(iter1, iter2, "nodelist.iterator() returns a new iterator each time");
+
+ log = '';
+ basket.appendChild(document.createTextNode("some text"));
+ for (var x of basket.children)
+ log += x.id + ";";
+ is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.children)' should iterate over child elements");
+
+ var iter1 = basket.children.iterator();
+ var iter2 = basket.children.iterator();
+ isnot(iter1, iter2, ".iterator() returns a new iterator each time");
+
+ var count = 0;
+ for (var x of document.getElementsByClassName("hazardous-materials"))
+ count++;
+ is(count, 0, "'for (x of emptyNodeList)' loop should run zero times");
+
+ var log = '';
+ for (var x of document.querySelectorAll("span"))
+ log += x.id + ";";
+ is(log, "egg0;egg1;duckling1;egg2;egg3;", "for-of loop should work with a querySelectorAll() NodeList");
+}
+
+/* All the tests run twice. First, in this document, so without any wrappers. */
+runTestsForDocument(document, "");
+
+/* And once using the document of an iframe, so working with cross-compartment wrappers. */
+SimpleTest.waitForExplicitFinish();
+function iframeLoaded(iframe) {
+ runTestsForDocument(iframe.contentWindow.document, " (in iframe)");
+ SimpleTest.finish();
+}
+
+</script>
+
+<iframe src="forOf_iframe.html" onload="iframeLoaded(this)"></iframe>
+
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_integers.html b/components/script/dom/bindings/codegen/test/test_integers.html
new file mode 100644
index 00000000000..6799fd791a8
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_integers.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <canvas id="c" width="1" height="1"></canvas>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ function testInt64NonFinite(arg) {
+ // We can use a WebGLRenderingContext to test conversion to 64-bit signed
+ // ints edge cases.
+ try {
+ var gl = $("c").getContext("experimental-webgl");
+ } catch (ex) {
+ // No WebGL support on MacOS 10.5. Just skip this test
+ todo(false, "WebGL not supported");
+ return;
+ }
+ is(gl.getError(), 0, "Should not start in an error state");
+
+ var b = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, b);
+
+ var a = new Float32Array(1);
+ gl.bufferData(gl.ARRAY_BUFFER, a, gl.STATIC_DRAW);
+
+ gl.bufferSubData(gl.ARRAY_BUFFER, arg, a);
+
+ is(gl.getError(), 0, "Should have treated non-finite double as 0");
+ }
+
+ testInt64NonFinite(NaN);
+ testInt64NonFinite(Infinity);
+ testInt64NonFinite(-Infinity);
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_interfaceToString.html b/components/script/dom/bindings/codegen/test/test_interfaceToString.html
new file mode 100644
index 00000000000..cf670bf2d54
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_interfaceToString.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=742156
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 742156</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=742156">Mozilla Bug 742156</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 742156 **/
+
+var nativeToString = ("" + String.replace).replace("replace", "EventTarget");
+try {
+ var eventTargetToString = "" + EventTarget;
+ is(eventTargetToString, nativeToString,
+ "Stringifying a DOM interface object should return the same string" +
+ "as stringifying a native function.");
+}
+catch (e) {
+ ok(false, "Stringifying a DOM interface object shouldn't throw.");
+}
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_lookupGetter.html b/components/script/dom/bindings/codegen/test/test_lookupGetter.html
new file mode 100644
index 00000000000..306ee4f643c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_lookupGetter.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=462428
+-->
+<head>
+ <title>Test for Bug 462428</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462428">Mozilla Bug 462428</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 462428 **/
+var x = new XMLHttpRequest;
+x.open("GET", "");
+var getter = x.__lookupGetter__('readyState');
+ok(getter !== undefined, "But able to look it up the normal way");
+ok(!x.hasOwnProperty('readyState'), "property should still be on the prototype");
+
+var sawProp = false;
+for (var i in x) {
+ if (i === "readyState") {
+ sawProp = true;
+ }
+}
+
+ok(sawProp, "property should be enumerable");
+
+is(getter.call(x), 1, "the getter actually works");
+
+Object.getPrototypeOf(x).__defineSetter__('readyState', function() {});
+is(getter.call(x), 1, "the getter works after defineSetter");
+
+is(x.responseType, "", "Should have correct responseType up front");
+var setter = x.__lookupSetter__('responseType');
+setter.call(x, "document");
+is(x.responseType, "document", "the setter is bound correctly");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_sequence_wrapping.html b/components/script/dom/bindings/codegen/test/test_sequence_wrapping.html
new file mode 100644
index 00000000000..e4f18f9986c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_sequence_wrapping.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=775852
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 775852</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=775852">Mozilla Bug 775852</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <canvas width="1" height="1" id="c"></canvas>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 775852 **/
+function doTest() {
+ try {
+ var gl = $("c").getContext("experimental-webgl");
+ } catch (e) {
+ // No WebGL support on MacOS 10.5. Just skip this test
+ todo(false, "WebGL not supported");
+ return;
+ }
+ var setterCalled = false;
+
+ extLength = gl.getSupportedExtensions().length;
+ ok(extLength > 0,
+ "This test won't work right if we have no supported extensions");
+
+ Object.defineProperty(Array.prototype, "0",
+ {
+ set: function(val) {
+ setterCalled = true;
+ }
+ });
+
+ // Test that our property got defined correctly
+ var arr = []
+ arr[0] = 5;
+ is(setterCalled, true, "Setter should be called when setting prop on array");
+
+ setterCalled = false;
+
+ is(gl.getSupportedExtensions().length, extLength,
+ "We should still have the same number of extensions");
+
+ is(setterCalled, false,
+ "Setter should not be called when getting supported extensions");
+}
+doTest();
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_traceProtos.html b/components/script/dom/bindings/codegen/test/test_traceProtos.html
new file mode 100644
index 00000000000..195876744d6
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_traceProtos.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=744772
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 744772</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=744772">Mozilla Bug 744772</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 744772 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function callback() {
+ new XMLHttpRequest().upload;
+ ok(true, "Accessing unreferenced DOM interface objects shouldn't crash");
+ SimpleTest.finish();
+}
+
+delete window.XMLHttpRequestUpload;
+SpecialPowers.exactGC(window, callback);
+
+</script>
+</pre>
+</body>
+</html>