aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2014-04-23 17:52:20 -0700
committerAlon Zakai <alonzakai@gmail.com>2014-04-23 17:52:20 -0700
commit28ed391308c78e8cda92bfbe22da88ce9767cd25 (patch)
tree41fde9dc11818abd67b836b054f90dc47cffdd0e
parent55c1dfd2c1548ebfd023e4f7d1c2416a11a534f8 (diff)
webidl binder
-rw-r--r--src/settings.js2
-rw-r--r--tests/test_core.py16
-rw-r--r--tests/webidl/output.txt56
-rw-r--r--tests/webidl/post.js117
-rw-r--r--tests/webidl/test.cpp8
-rw-r--r--tests/webidl/test.h72
-rw-r--r--tests/webidl/test.idl64
-rw-r--r--third_party/WebIDL.py5030
-rw-r--r--third_party/__init__.py2
-rw-r--r--tools/webidl_binder.py426
10 files changed, 5792 insertions, 1 deletions
diff --git a/src/settings.js b/src/settings.js
index bf16290b..a9a72425 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -338,7 +338,7 @@ var EXPORTED_FUNCTIONS = ['_main', '_malloc'];
var EXPORT_ALL = 0; // If true, we export all the symbols. Note that this does *not* affect LLVM, so it can
// still eliminate functions as dead. This just exports them on the Module object.
var EXPORT_BINDINGS = 0; // Export all bindings generator functions (prefixed with emscripten_bind_). This
- // is necessary to use the bindings generator with asm.js
+ // is necessary to use the WebIDL binder or bindings generator with asm.js
var RETAIN_COMPILER_SETTINGS = 0; // Remembers the values of these settings, and makes them accessible
// through Runtime.getCompilerSetting and emscripten_get_compiler_setting.
// To see what is retained, look for compilerSettings in the generated code.
diff --git a/tests/test_core.py b/tests/test_core.py
index 1b116c4a..b5024309 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -5971,6 +5971,22 @@ def process(filename):
'''
self.do_run(src, '|hello|43|world|41|', post_build=post)
+ def test_webidl(self):
+ if self.emcc_args is None: return self.skip('requires emcc')
+
+ output = Popen([PYTHON, path_from_root('tools', 'webidl_binder.py'),
+ path_from_root('tests', 'webidl', 'test.idl'),
+ 'glue']).communicate()[0]
+ assert os.path.exists('glue.cpp')
+ assert os.path.exists('glue.js')
+
+ self.emcc_args += ['--post-js', 'glue.js',
+ '--post-js', path_from_root('tests', 'webidl', 'post.js')]
+ shutil.copyfile(path_from_root('tests', 'webidl', 'test.h'), self.in_dir('test.h'))
+ shutil.copyfile(path_from_root('tests', 'webidl', 'test.cpp'), self.in_dir('test.cpp'))
+ src = open('test.cpp').read()
+ self.do_run(src, open(path_from_root('tests', 'webidl', 'output.txt')).read())
+
def test_typeinfo(self):
if os.environ.get('EMCC_FAST_COMPILER') != '0': return self.skip('fastcomp does not support RUNTIME_TYPE_INFO')
diff --git a/tests/webidl/output.txt b/tests/webidl/output.txt
new file mode 100644
index 00000000..b874d928
--- /dev/null
+++ b/tests/webidl/output.txt
@@ -0,0 +1,56 @@
+Parent:42
+*
+84
+c1
+Parent:7
+Child1:7
+7
+14
+196
+588
+14
+28
+c1 v2
+Parent:16
+Child1:15
+15
+30
+900
+2700
+c2
+Parent:9
+Child2:9
+9
+18
+5832
+0
+0
+1
+*static*
+*virtualf*
+*virtualf*
+*virtualf2*
+Parent:9
+Child2:9
+*js virtualf replacement*
+*js virtualf replacement*
+*js virtualf2 replacement*
+*js virtualf3 replacement 123*
+caught: a JSImplementation must implement all functions, you forgot Child2JS::virtualFunc4.
+*virtualf*
+*virtualf*
+*virtualf2*
+*ok*
+|hello|43|world|41|
+12.35
+10
+object
+10
+11
+object
+10
+11
+21.12
+198
+
+done.
diff --git a/tests/webidl/post.js b/tests/webidl/post.js
new file mode 100644
index 00000000..444efcd1
--- /dev/null
+++ b/tests/webidl/post.js
@@ -0,0 +1,117 @@
+
+// Part 1
+
+var sme = new Module.Parent(42);
+sme.mulVal(2);
+Module.print('*')
+Module.print(sme.getVal());
+
+Module.print('c1');
+
+var c1 = new Module.Child1();
+Module.print(c1.getVal());
+c1.mulVal(2);
+Module.print(c1.getVal());
+Module.print(c1.getValSqr());
+Module.print(c1.getValSqr(3));
+Module.print(c1.getValTimes()); // default argument should be 1
+Module.print(c1.getValTimes(2));
+
+Module.print('c1 v2');
+
+c1 = new Module.Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2
+Module.print(c1.getVal());
+c1.mulVal(2);
+Module.print(c1.getVal());
+Module.print(c1.getValSqr());
+Module.print(c1.getValSqr(3));
+
+Module.print('c2')
+
+var c2 = new Module.Child2();
+Module.print(c2.getVal());
+c2.mulVal(2);
+Module.print(c2.getVal());
+Module.print(c2.getValCube());
+var succeeded;
+try {
+ succeeded = 0;
+ Module.print(c2.doSomethingSecret()); // should fail since private
+ succeeded = 1;
+} catch(e) {}
+Module.print(succeeded);
+try {
+ succeeded = 0;
+ Module.print(c2.getValSqr()); // function from the other class
+ succeeded = 1;
+} catch(e) {}
+Module.print(succeeded);
+try {
+ succeeded = 0;
+ c2.getValCube(); // sanity
+ succeeded = 1;
+} catch(e) {}
+Module.print(succeeded);
+
+Module.Child2.prototype.printStatic(); // static calls go through the prototype
+
+// virtual function
+c2.virtualFunc();
+Module.Child2.prototype.runVirtualFunc(c2);
+c2.virtualFunc2();
+
+// extend a class from JS
+var c3 = new Module.Child2JS;
+
+c3.virtualFunc = function() {
+ Module.print('*js virtualf replacement*');
+};
+c3.virtualFunc2 = function() {
+ Module.print('*js virtualf2 replacement*');
+};
+c3.virtualFunc3 = function(x) {
+ Module.print('*js virtualf3 replacement ' + x + '*');
+};
+
+c3.virtualFunc();
+Module.Child2.prototype.runVirtualFunc(c3);
+c3.virtualFunc2();
+c3.virtualFunc3(123); // this one is not replaced!
+try {
+ c3.virtualFunc4(123);
+} catch(e) {
+ Module.print('caught: ' + e);
+}
+
+c2.virtualFunc(); // original should remain the same
+Module.Child2.prototype.runVirtualFunc(c2);
+c2.virtualFunc2();
+Module.print('*ok*');
+
+// Part 2
+
+var suser = new Module.StringUser("hello", 43);
+suser.Print(41, "world");
+suser.PrintFloat(12.3456);
+
+var bv = new Module.RefUser(10);
+var bv2 = new Module.RefUser(11);
+Module.print(bv2.getValue(bv));
+
+Module.print(typeof bv2.getMe());
+Module.print(bv2.getMe().getValue(bv));
+Module.print(bv2.getMe().getValue(bv2));
+
+Module.print(typeof bv2.getCopy());
+Module.print(bv2.getCopy().getValue(bv));
+Module.print(bv2.getCopy().getValue(bv2));
+
+bv2.getAnother().PrintFloat(21.12);
+
+Module.print(new Module.Inner().get());
+new Module.Inner().mul(2);
+
+//
+
+Module.print('\ndone.')
+
diff --git a/tests/webidl/test.cpp b/tests/webidl/test.cpp
new file mode 100644
index 00000000..8a2b5c72
--- /dev/null
+++ b/tests/webidl/test.cpp
@@ -0,0 +1,8 @@
+#include "test.h"
+
+Parent::Parent(int val) : value(val) { printf("Parent:%d\n", val); }
+Parent::Parent(Parent *p, Parent *q) : value(p->value + q->value) { printf("Parent:%d\n", value); }
+void Parent::mulVal(int mul) { value *= mul; }
+
+#include "glue.cpp"
+
diff --git a/tests/webidl/test.h b/tests/webidl/test.h
new file mode 100644
index 00000000..903f8f78
--- /dev/null
+++ b/tests/webidl/test.h
@@ -0,0 +1,72 @@
+#include <stdio.h>
+
+// Part 1
+
+class Parent {
+protected:
+ int value;
+public:
+ Parent(int val);
+ Parent(Parent *p, Parent *q); // overload constructor
+ int getVal() { return value; }; // inline should work just fine here, unlike Way 1 before
+ void mulVal(int mul);
+};
+
+class Child1 : public Parent {
+public:
+ Child1() : Parent(7) { printf("Child1:%d\n", value); };
+ Child1(int val) : Parent(val*2) { value -= 1; printf("Child1:%d\n", value); };
+ int getValSqr() { return value*value; }
+ int getValSqr(int more) { return value*value*more; }
+ int getValTimes(int times=1) { return value*times; }
+};
+
+// Child2 has vtable, parent does not. Checks we cast child->parent properly - (Parent*)child is not a no-op, must offset
+class Child2 : public Parent {
+public:
+ Child2() : Parent(9) { printf("Child2:%d\n", value); };
+ int getValCube() { return value*value*value; }
+ static void printStatic() { printf("*static*\n"); }
+
+ virtual void virtualFunc() { printf("*virtualf*\n"); }
+ virtual void virtualFunc2() { printf("*virtualf2*\n"); }
+ static void runVirtualFunc(Child2 *self) { self->virtualFunc(); };
+ virtual void virtualFunc3(int x) { printf("*virtualf3: %d*\n", x); }
+ virtual void virtualFunc4(int x) { printf("*virtualf4: %d*\n", x); }
+
+private:
+ void doSomethingSecret() { printf("security breached!\n"); }; // we should not be able to do this
+};
+
+// Part 2
+
+#include <string.h>
+
+class StringUser {
+ char *s;
+ int i;
+public:
+ StringUser(char *string="NO", int integer=99) : s(strdup(string)), i(integer) {}
+ void Print(int anotherInteger, char *anotherString) {
+ printf("|%s|%d|%s|%d|\n", s, i, anotherString, anotherInteger);
+ }
+ void PrintFloat(float f) { printf("%.2f\n", f); }
+};
+
+struct RefUser {
+ int value;
+ RefUser(int x = 77) : value(x) {}
+ int getValue(RefUser b) { return b.value; }
+ RefUser &getMe() { return *this; }
+ RefUser getCopy() { return RefUser(value*2); }
+ StringUser getAnother() { return StringUser("another", 5); }
+};
+
+namespace Space {
+ struct Inner {
+ Inner() {}
+ int get() { return 198; }
+ Inner& operator*=(float x) { return *this; }
+ };
+}
+
diff --git a/tests/webidl/test.idl b/tests/webidl/test.idl
new file mode 100644
index 00000000..98ab5070
--- /dev/null
+++ b/tests/webidl/test.idl
@@ -0,0 +1,64 @@
+
+// Part 1
+
+interface Parent {
+ void Parent(long val);
+ long getVal();
+ void mulVal(long mul);
+};
+
+interface Child1 {
+ void Child1(optional long val);
+ long getValSqr(optional long more);
+ long getValTimes(optional long times=1);
+};
+
+Child1 implements Parent;
+
+interface Child2 {
+ void Child2();
+ long getValCube();
+ static void printStatic();
+ void virtualFunc();
+ void virtualFunc2();
+ void virtualFunc3(long x);
+ void virtualFunc4(long x);
+ static void runVirtualFunc(Child2 self);
+};
+
+Child2 implements Parent;
+
+[JSImplementation="Child2"]
+interface Child2JS {
+ void Child2JS();
+ void virtualFunc();
+ void virtualFunc2();
+ void virtualFunc3(long x);
+ void virtualFunc4(long x);
+};
+
+// Part 2
+
+interface StringUser {
+ void StringUser();
+ void StringUser(DOMString str, long i);
+ void Print(long anotherInteger, DOMString anotherString);
+ void PrintFloat(float f);
+};
+
+interface RefUser {
+ void RefUser();
+ void RefUser(long value);
+ long getValue([Ref] RefUser b);
+ [Ref] RefUser getMe();
+ [Value] RefUser getCopy(); // must have zero-arg constructor
+ [Value] StringUser getAnother();
+};
+
+[Prefix="Space::"]
+interface Inner {
+ void Inner();
+ long get();
+ [Operator="*=", Ref] Inner mul(float x);
+};
+
diff --git a/third_party/WebIDL.py b/third_party/WebIDL.py
new file mode 100644
index 00000000..867a7cbc
--- /dev/null
+++ b/third_party/WebIDL.py
@@ -0,0 +1,5030 @@
+# from http://mxr.mozilla.org/mozilla-central/source/dom/bindings/parser/WebIDL.py
+# rev 501baeb3a034
+
+# 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
+
+# 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 = {}
+
+ 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), [])
+
+ # 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):
+ 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()
+
+class IDLInterface(IDLObjectWithScope):
+ def __init__(self, location, parentScope, name, parent, members,
+ isPartial):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+ assert not isPartial or not parent
+
+ self.parent = None
+ self._callback = False
+ self._finished = False
+ self.members = []
+ # 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._isPartial = True
+ # 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
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+
+ if not isPartial:
+ self.setNonPartial(location, parent, members)
+ else:
+ # Just remember our members for now
+ self.members = 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 self._isPartial:
+ raise WebIDLError("Interface %s does not have a non-partial "
+ "declaration" % self.identifier.name,
+ [self.location])
+
+ 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] must not have anything inherit from them
+ if self.parent.getExtendedAttribute("Global"):
+ # Note: This is not a self.parent.isOnGlobalProtoChain() check
+ # because ancestors of a [Global] interface can have other
+ # descendants.
+ raise WebIDLError("[Global] interface has another interface "
+ "inheriting from it",
+ [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)
+
+ ctor = self.ctor()
+ if ctor is not None:
+ ct