diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/imptests/idlharness.js | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/imptests/idlharness.js')
-rw-r--r-- | dom/imptests/idlharness.js | 1706 |
1 files changed, 1706 insertions, 0 deletions
diff --git a/dom/imptests/idlharness.js b/dom/imptests/idlharness.js new file mode 100644 index 000000000..8e41703e6 --- /dev/null +++ b/dom/imptests/idlharness.js @@ -0,0 +1,1706 @@ +/* +Distributed under both the W3C Test Suite License [1] and the W3C +3-clause BSD License [2]. To contribute to a W3C Test Suite, see the +policies and contribution forms [3]. + +[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license +[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license +[3] http://www.w3.org/2004/10/27-testcases +*/ + +/* For user documentation see docs/idlharness.md */ + +/** + * Notes for people who want to edit this file (not just use it as a library): + * + * Most of the interesting stuff happens in the derived classes of IdlObject, + * especially IdlInterface. The entry point for all IdlObjects is .test(), + * which is called by IdlArray.test(). An IdlObject is conceptually just + * "thing we want to run tests on", and an IdlArray is an array of IdlObjects + * with some additional data thrown in. + * + * The object model is based on what WebIDLParser.js produces, which is in turn + * based on its pegjs grammar. If you want to figure out what properties an + * object will have from WebIDLParser.js, the best way is to look at the + * grammar: + * + * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg + * + * So for instance: + * + * // interface definition + * interface + * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w + * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; } + * + * This means that an "interface" object will have a .type property equal to + * the string "interface", a .name property equal to the identifier that the + * parser found, an .inheritance property equal to either null or the result of + * the "ifInheritance" production found elsewhere in the grammar, and so on. + * After each grammatical production is a JavaScript function in curly braces + * that gets called with suitable arguments and returns some JavaScript value. + * + * (Note that the version of WebIDLParser.js we use might sometimes be + * out-of-date or forked.) + * + * The members and methods of the classes defined by this file are all at least + * briefly documented, hopefully. + */ +(function(){ +"use strict"; +/// Helpers /// +function constValue (cnt) { + if (cnt.type === "null") return null; + if (cnt.type === "NaN") return NaN; + if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity; + return cnt.value; +} + +function minOverloadLength(overloads) { + if (!overloads.length) { + return 0; + } + + return overloads.map(function(attr) { + return attr.arguments ? attr.arguments.filter(function(arg) { + return !arg.optional && !arg.variadic; + }).length : 0; + }) + .reduce(function(m, n) { return Math.min(m, n); }); +} + +/// IdlArray /// +// Entry point +self.IdlArray = function() +//@{ +{ + /** + * A map from strings to the corresponding named IdlObject, such as + * IdlInterface or IdlException. These are the things that test() will run + * tests on. + */ + this.members = {}; + + /** + * A map from strings to arrays of strings. The keys are interface or + * exception names, and are expected to also exist as keys in this.members + * (otherwise they'll be ignored). This is populated by add_objects() -- + * see documentation at the start of the file. The actual tests will be + * run by calling this.members[name].test_object(obj) for each obj in + * this.objects[name]. obj is a string that will be eval'd to produce a + * JavaScript value, which is supposed to be an object implementing the + * given IdlObject (interface, exception, etc.). + */ + this.objects = {}; + + /** + * When adding multiple collections of IDLs one at a time, an earlier one + * might contain a partial interface or implements statement that depends + * on a later one. Save these up and handle them right before we run + * tests. + * + * .partials is simply an array of objects from WebIDLParser.js' + * "partialinterface" production. .implements maps strings to arrays of + * strings, such that + * + * A implements B; + * A implements C; + * D implements E; + * + * results in { A: ["B", "C"], D: ["E"] }. + */ + this.partials = []; + this["implements"] = {}; +}; + +//@} +IdlArray.prototype.add_idls = function(raw_idls) +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + this.internal_add_idls(WebIDL2.parse(raw_idls)); +}; + +//@} +IdlArray.prototype.add_untested_idls = function(raw_idls) +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + var parsed_idls = WebIDL2.parse(raw_idls); + for (var i = 0; i < parsed_idls.length; i++) + { + parsed_idls[i].untested = true; + if ("members" in parsed_idls[i]) + { + for (var j = 0; j < parsed_idls[i].members.length; j++) + { + parsed_idls[i].members[j].untested = true; + } + } + } + this.internal_add_idls(parsed_idls); +}; + +//@} +IdlArray.prototype.internal_add_idls = function(parsed_idls) +//@{ +{ + /** + * Internal helper called by add_idls() and add_untested_idls(). + * parsed_idls is an array of objects that come from WebIDLParser.js's + * "definitions" production. The add_untested_idls() entry point + * additionally sets an .untested property on each object (and its + * .members) so that they'll be skipped by test() -- they'll only be + * used for base interfaces of tested interfaces, return types, etc. + */ + parsed_idls.forEach(function(parsed_idl) + { + if (parsed_idl.type == "interface" && parsed_idl.partial) + { + this.partials.push(parsed_idl); + return; + } + + if (parsed_idl.type == "implements") + { + if (!(parsed_idl.target in this["implements"])) + { + this["implements"][parsed_idl.target] = []; + } + this["implements"][parsed_idl.target].push(parsed_idl["implements"]); + return; + } + + parsed_idl.array = this; + if (parsed_idl.name in this.members) + { + throw "Duplicate identifier " + parsed_idl.name; + } + switch(parsed_idl.type) + { + case "interface": + this.members[parsed_idl.name] = + new IdlInterface(parsed_idl, /* is_callback = */ false); + break; + + case "dictionary": + // Nothing to test, but we need the dictionary info around for type + // checks + this.members[parsed_idl.name] = new IdlDictionary(parsed_idl); + break; + + case "typedef": + this.members[parsed_idl.name] = new IdlTypedef(parsed_idl); + break; + + case "callback": + // TODO + console.log("callback not yet supported"); + break; + + case "enum": + this.members[parsed_idl.name] = new IdlEnum(parsed_idl); + break; + + case "callback interface": + this.members[parsed_idl.name] = + new IdlInterface(parsed_idl, /* is_callback = */ true); + break; + + default: + throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported"; + } + }.bind(this)); +}; + +//@} +IdlArray.prototype.add_objects = function(dict) +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + for (var k in dict) + { + if (k in this.objects) + { + this.objects[k] = this.objects[k].concat(dict[k]); + } + else + { + this.objects[k] = dict[k]; + } + } +}; + +//@} +IdlArray.prototype.prevent_multiple_testing = function(name) +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + this.members[name].prevent_multiple_testing = true; +}; + +//@} +IdlArray.prototype.recursively_get_implements = function(interface_name) +//@{ +{ + /** + * Helper function for test(). Returns an array of things that implement + * interface_name, so if the IDL contains + * + * A implements B; + * B implements C; + * B implements D; + * + * then recursively_get_implements("A") should return ["B", "C", "D"]. + */ + var ret = this["implements"][interface_name]; + if (ret === undefined) + { + return []; + } + for (var i = 0; i < this["implements"][interface_name].length; i++) + { + ret = ret.concat(this.recursively_get_implements(ret[i])); + if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i])) + { + throw "Circular implements statements involving " + ret[i]; + } + } + return ret; +}; + +//@} +IdlArray.prototype.test = function() +//@{ +{ + /** Entry point. See documentation at beginning of file. */ + + // First merge in all the partial interfaces and implements statements we + // encountered. + this.partials.forEach(function(parsed_idl) + { + if (!(parsed_idl.name in this.members) + || !(this.members[parsed_idl.name] instanceof IdlInterface)) + { + throw "Partial interface " + parsed_idl.name + " with no original interface"; + } + if (parsed_idl.extAttrs) + { + parsed_idl.extAttrs.forEach(function(extAttr) + { + this.members[parsed_idl.name].extAttrs.push(extAttr); + }.bind(this)); + } + parsed_idl.members.forEach(function(member) + { + this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member)); + }.bind(this)); + }.bind(this)); + this.partials = []; + + for (var lhs in this["implements"]) + { + this.recursively_get_implements(lhs).forEach(function(rhs) + { + var errStr = lhs + " implements " + rhs + ", but "; + if (!(lhs in this.members)) throw errStr + lhs + " is undefined."; + if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface."; + if (!(rhs in this.members)) throw errStr + rhs + " is undefined."; + if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface."; + this.members[rhs].members.forEach(function(member) + { + this.members[lhs].members.push(new IdlInterfaceMember(member)); + }.bind(this)); + }.bind(this)); + } + this["implements"] = {}; + + // Now run test() on every member, and test_object() for every object. + for (var name in this.members) + { + this.members[name].test(); + if (name in this.objects) + { + this.objects[name].forEach(function(str) + { + this.members[name].test_object(str); + }.bind(this)); + } + } +}; + +//@} +IdlArray.prototype.assert_type_is = function(value, type) +//@{ +{ + /** + * Helper function that tests that value is an instance of type according + * to the rules of WebIDL. value is any JavaScript value, and type is an + * object produced by WebIDLParser.js' "type" production. That production + * is fairly elaborate due to the complexity of WebIDL's types, so it's + * best to look at the grammar to figure out what properties it might have. + */ + if (type.idlType == "any") + { + // No assertions to make + return; + } + + if (type.nullable && value === null) + { + // This is fine + return; + } + + if (type.array) + { + // TODO: not supported yet + return; + } + + if (type.sequence) + { + assert_true(Array.isArray(value), "is not array"); + if (!value.length) + { + // Nothing we can do. + return; + } + this.assert_type_is(value[0], type.idlType.idlType); + return; + } + + type = type.idlType; + + switch(type) + { + case "void": + assert_equals(value, undefined); + return; + + case "boolean": + assert_equals(typeof value, "boolean"); + return; + + case "byte": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]"); + return; + + case "octet": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]"); + return; + + case "short": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]"); + return; + + case "unsigned short": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]"); + return; + + case "long": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]"); + return; + + case "unsigned long": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "not an integer"); + assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]"); + return; + + case "long long": + assert_equals(typeof value, "number"); + return; + + case "unsigned long long": + case "DOMTimeStamp": + assert_equals(typeof value, "number"); + assert_true(0 <= value, "unsigned long long is negative"); + return; + + case "float": + case "double": + case "DOMHighResTimeStamp": + case "unrestricted float": + case "unrestricted double": + // TODO: distinguish these cases + assert_equals(typeof value, "number"); + return; + + case "DOMString": + case "ByteString": + case "USVString": + // TODO: https://github.com/w3c/testharness.js/issues/92 + assert_equals(typeof value, "string"); + return; + + case "object": + assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function"); + return; + } + + if (!(type in this.members)) + { + throw "Unrecognized type " + type; + } + + if (this.members[type] instanceof IdlInterface) + { + // We don't want to run the full + // IdlInterface.prototype.test_instance_of, because that could result + // in an infinite loop. TODO: This means we don't have tests for + // NoInterfaceObject interfaces, and we also can't test objects that + // come from another self. + assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function"); + if (value instanceof Object + && !this.members[type].has_extended_attribute("NoInterfaceObject") + && type in self) + { + assert_true(value instanceof self[type], "not instanceof " + type); + } + } + else if (this.members[type] instanceof IdlEnum) + { + assert_equals(typeof value, "string"); + } + else if (this.members[type] instanceof IdlDictionary) + { + // TODO: Test when we actually have something to test this on + } + else if (this.members[type] instanceof IdlTypedef) + { + // TODO: Test when we actually have something to test this on + } + else + { + throw "Type " + type + " isn't an interface or dictionary"; + } +}; +//@} + +/// IdlObject /// +function IdlObject() {} +IdlObject.prototype.test = function() +//@{ +{ + /** + * By default, this does nothing, so no actual tests are run for IdlObjects + * that don't define any (e.g., IdlDictionary at the time of this writing). + */ +}; + +//@} +IdlObject.prototype.has_extended_attribute = function(name) +//@{ +{ + /** + * This is only meaningful for things that support extended attributes, + * such as interfaces, exceptions, and members. + */ + return this.extAttrs.some(function(o) + { + return o.name == name; + }); +}; + +//@} + +/// IdlDictionary /// +// Used for IdlArray.prototype.assert_type_is +function IdlDictionary(obj) +//@{ +{ + /** + * obj is an object produced by the WebIDLParser.js "dictionary" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** An array of objects produced by the "dictionaryMember" production. */ + this.members = obj.members; + + /** + * The name (as a string) of the dictionary type we inherit from, or null + * if there is none. + */ + this.base = obj.inheritance; +} + +//@} +IdlDictionary.prototype = Object.create(IdlObject.prototype); + +/// IdlInterface /// +function IdlInterface(obj, is_callback) { + /** + * obj is an object produced by the WebIDLParser.js "exception" or + * "interface" production, as appropriate. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** A back-reference to our IdlArray. */ + this.array = obj.array; + + /** + * An indicator of whether we should run tests on the (exception) interface + * object and (exception) interface prototype object. Tests on members are + * controlled by .untested on each member, not this. + */ + this.untested = obj.untested; + + /** An array of objects produced by the "ExtAttr" production. */ + this.extAttrs = obj.extAttrs; + + /** An array of IdlInterfaceMembers. */ + this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); }); + if (this.has_extended_attribute("Unforgeable")) { + this.members + .filter(function(m) { return !m["static"] && (m.type == "attribute" || m.type == "operation"); }) + .forEach(function(m) { return m.isUnforgeable = true; }); + } + + /** + * The name (as a string) of the type we inherit from, or null if there is + * none. + */ + this.base = obj.inheritance; + + this._is_callback = is_callback; +} +IdlInterface.prototype = Object.create(IdlObject.prototype); +IdlInterface.prototype.is_callback = function() +//@{ +{ + return this._is_callback; +}; +//@} + +IdlInterface.prototype.has_constants = function() +//@{ +{ + return this.members.some(function(member) { + return member.type === "const"; + }); +}; +//@} + +IdlInterface.prototype.is_global = function() +//@{ +{ + return this.extAttrs.some(function(attribute) { + return attribute.name === "Global" || + attribute.name === "PrimaryGlobal"; + }); +}; +//@} + +IdlInterface.prototype.test = function() +//@{ +{ + if (this.has_extended_attribute("NoInterfaceObject")) + { + // No tests to do without an instance. TODO: We should still be able + // to run tests on the prototype object, if we obtain one through some + // other means. + return; + } + + if (!this.untested) + { + // First test things to do with the exception/interface object and + // exception/interface prototype object. + this.test_self(); + } + // Then test things to do with its members (constants, fields, attributes, + // operations, . . .). These are run even if .untested is true, because + // members might themselves be marked as .untested. This might happen to + // interfaces if the interface itself is untested but a partial interface + // that extends it is tested -- then the interface itself and its initial + // members will be marked as untested, but the members added by the partial + // interface are still tested. + this.test_members(); +}; +//@} + +IdlInterface.prototype.test_self = function() +//@{ +{ + test(function() + { + // This function tests WebIDL as of 2015-01-13. + // TODO: Consider [Exposed]. + + // "For every interface that is exposed in a given ECMAScript global + // environment and: + // * is a callback interface that has constants declared on it, or + // * is a non-callback interface that is not declared with the + // [NoInterfaceObject] extended attribute, + // a corresponding property MUST exist on the ECMAScript global object. + // The name of the property is the identifier of the interface, and its + // value is an object called the interface object. + // The property has the attributes { [[Writable]]: true, + // [[Enumerable]]: false, [[Configurable]]: true }." + if (this.is_callback() && !this.has_constants()) { + return; + } + + // TODO: Should we test here that the property is actually writable + // etc., or trust getOwnPropertyDescriptor? + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + var desc = Object.getOwnPropertyDescriptor(self, this.name); + assert_false("get" in desc, "self's property " + format_value(this.name) + " has getter"); + assert_false("set" in desc, "self's property " + format_value(this.name) + " has setter"); + assert_true(desc.writable, "self's property " + format_value(this.name) + " is not writable"); + assert_false(desc.enumerable, "self's property " + format_value(this.name) + " is enumerable"); + assert_true(desc.configurable, "self's property " + format_value(this.name) + " is not configurable"); + + if (this.is_callback()) { + // "The internal [[Prototype]] property of an interface object for + // a callback interface MUST be the Object.prototype object." + assert_equals(Object.getPrototypeOf(self[this.name]), Object.prototype, + "prototype of self's property " + format_value(this.name) + " is not Object.prototype"); + + return; + } + + // "The interface object for a given non-callback interface is a + // function object." + // "If an object is defined to be a function object, then it has + // characteristics as follows:" + + // Its [[Prototype]] internal property is otherwise specified (see + // below). + + // "* Its [[Get]] internal property is set as described in ECMA-262 + // section 9.1.8." + // Not much to test for this. + + // "* Its [[Construct]] internal property is set as described in + // ECMA-262 section 19.2.2.3." + // Tested below if no constructor is defined. TODO: test constructors + // if defined. + + // "* Its @@hasInstance property is set as described in ECMA-262 + // section 19.2.3.8, unless otherwise specified." + // TODO + + // ES6 (rev 30) 19.1.3.6: + // "Else, if O has a [[Call]] internal method, then let builtinTag be + // "Function"." + assert_class_string(self[this.name], "Function", "class string of " + this.name); + + // "The [[Prototype]] internal property of an interface object for a + // non-callback interface is determined as follows:" + var prototype = Object.getPrototypeOf(self[this.name]); + if (this.base) { + // "* If the interface inherits from some other interface, the + // value of [[Prototype]] is the interface object for that other + // interface." + var has_interface_object = + !this.array + .members[this.base] + .has_extended_attribute("NoInterfaceObject"); + if (has_interface_object) { + assert_own_property(self, this.base, + 'should inherit from ' + this.base + + ', but self has no such property'); + assert_equals(prototype, self[this.base], + 'prototype of ' + this.name + ' is not ' + + this.base); + } + } else { + // "If the interface doesn't inherit from any other interface, the + // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262], + // section 6.1.7.4)." + assert_equals(prototype, Function.prototype, + "prototype of self's property " + format_value(this.name) + " is not Function.prototype"); + } + + if (!this.has_extended_attribute("Constructor")) { + // "The internal [[Call]] method of the interface object behaves as + // follows . . . + // + // "If I was not declared with a [Constructor] extended attribute, + // then throw a TypeError." + assert_throws(new TypeError(), function() { + self[this.name](); + }.bind(this), "interface object didn't throw TypeError when called as a function"); + assert_throws(new TypeError(), function() { + new self[this.name](); + }.bind(this), "interface object didn't throw TypeError when called as a constructor"); + } + }.bind(this), this.name + " interface: existence and properties of interface object"); + + if (!this.is_callback()) { + test(function() { + // This function tests WebIDL as of 2014-10-25. + // https://heycam.github.io/webidl/#es-interface-call + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + // "Interface objects for non-callback interfaces MUST have a + // property named “length” with attributes { [[Writable]]: false, + // [[Enumerable]]: false, [[Configurable]]: true } whose value is + // a Number." + assert_own_property(self[this.name], "length"); + var desc = Object.getOwnPropertyDescriptor(self[this.name], "length"); + assert_false("get" in desc, this.name + ".length has getter"); + assert_false("set" in desc, this.name + ".length has setter"); + assert_false(desc.writable, this.name + ".length is writable"); + assert_false(desc.enumerable, this.name + ".length is enumerable"); + assert_true(desc.configurable, this.name + ".length is not configurable"); + + var constructors = this.extAttrs + .filter(function(attr) { return attr.name == "Constructor"; }); + var expected_length = minOverloadLength(constructors); + assert_equals(self[this.name].length, expected_length, "wrong value for " + this.name + ".length"); + }.bind(this), this.name + " interface object length"); + } + + // TODO: Test named constructors if I find any interfaces that have them. + + test(function() + { + // This function tests WebIDL as of 2015-01-21. + // https://heycam.github.io/webidl/#interface-object + + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + + // "An interface object for a non-callback interface must have a + // property named “prototype” with attributes { [[Writable]]: false, + // [[Enumerable]]: false, [[Configurable]]: false } whose value is an + // object called the interface prototype object. This object has + // properties that correspond to the regular attributes and regular + // operations defined on the interface, and is described in more detail + // in section 4.5.4 below." + assert_own_property(self[this.name], "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + var desc = Object.getOwnPropertyDescriptor(self[this.name], "prototype"); + assert_false("get" in desc, this.name + ".prototype has getter"); + assert_false("set" in desc, this.name + ".prototype has setter"); + assert_false(desc.writable, this.name + ".prototype is writable"); + assert_false(desc.enumerable, this.name + ".prototype is enumerable"); + assert_false(desc.configurable, this.name + ".prototype is configurable"); + + // Next, test that the [[Prototype]] of the interface prototype object + // is correct. (This is made somewhat difficult by the existence of + // [NoInterfaceObject].) + // TODO: Aryeh thinks there's at least other place in this file where + // we try to figure out if an interface prototype object is + // correct. Consolidate that code. + + // "The interface prototype object for a given interface A must have an + // internal [[Prototype]] property whose value is returned from the + // following steps: + // "If A is declared with the [Global] or [PrimaryGlobal] extended + // attribute, and A supports named properties, then return the named + // properties object for A, as defined in section 4.5.5 below. + // "Otherwise, if A is declared to inherit from another interface, then + // return the interface prototype object for the inherited interface. + // "Otherwise, if A is declared with the [ArrayClass] extended + // attribute, then return %ArrayPrototype% ([ECMA-262], section + // 6.1.7.4). + // "Otherwise, return %ObjectPrototype% ([ECMA-262], section 6.1.7.4). + // ([ECMA-262], section 15.2.4). + if (this.name === "Window") { + assert_class_string(Object.getPrototypeOf(self[this.name].prototype), + 'WindowProperties', + 'Class name for prototype of Window' + + '.prototype is not "WindowProperties"'); + } else { + var inherit_interface, inherit_interface_has_interface_object; + if (this.base) { + inherit_interface = this.base; + inherit_interface_has_interface_object = + !this.array + .members[inherit_interface] + .has_extended_attribute("NoInterfaceObject"); + } else if (this.has_extended_attribute('ArrayClass')) { + inherit_interface = 'Array'; + inherit_interface_has_interface_object = true; + } else { + inherit_interface = 'Object'; + inherit_interface_has_interface_object = true; + } + if (inherit_interface_has_interface_object) { + assert_own_property(self, inherit_interface, + 'should inherit from ' + inherit_interface + ', but self has no such property'); + assert_own_property(self[inherit_interface], 'prototype', + 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property'); + assert_equals(Object.getPrototypeOf(self[this.name].prototype), + self[inherit_interface].prototype, + 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype'); + } else { + // We can't test that we get the correct object, because this is the + // only way to get our hands on it. We only test that its class + // string, at least, is correct. + assert_class_string(Object.getPrototypeOf(self[this.name].prototype), + inherit_interface + 'Prototype', + 'Class name for prototype of ' + this.name + + '.prototype is not "' + inherit_interface + 'Prototype"'); + } + } + + // "The class string of an interface prototype object is the + // concatenation of the interface’s identifier and the string + // “Prototype”." + assert_class_string(self[this.name].prototype, this.name + "Prototype", + "class string of " + this.name + ".prototype"); + // String() should end up calling {}.toString if nothing defines a + // stringifier. + if (!this.has_stringifier()) { + assert_equals(String(self[this.name].prototype), "[object " + this.name + "Prototype]", + "String(" + this.name + ".prototype)"); + } + }.bind(this), this.name + " interface: existence and properties of interface prototype object"); + + test(function() + { + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + + assert_own_property(self[this.name], "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // "If the [NoInterfaceObject] extended attribute was not specified on + // the interface, then the interface prototype object must also have a + // property named “constructor” with attributes { [[Writable]]: true, + // [[Enumerable]]: false, [[Configurable]]: true } whose value is a + // reference to the interface object for the interface." + assert_own_property(self[this.name].prototype, "constructor", + this.name + '.prototype does not have own property "constructor"'); + var desc = Object.getOwnPropertyDescriptor(self[this.name].prototype, "constructor"); + assert_false("get" in desc, this.name + ".prototype.constructor has getter"); + assert_false("set" in desc, this.name + ".prototype.constructor has setter"); + assert_true(desc.writable, this.name + ".prototype.constructor is not writable"); + assert_false(desc.enumerable, this.name + ".prototype.constructor is enumerable"); + assert_true(desc.configurable, this.name + ".prototype.constructor in not configurable"); + assert_equals(self[this.name].prototype.constructor, self[this.name], + this.name + '.prototype.constructor is not the same object as ' + this.name); + }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property'); +}; + +//@} +IdlInterface.prototype.test_member_const = function(member) +//@{ +{ + test(function() + { + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + // "For each constant defined on an interface A, there must be + // a corresponding property on the interface object, if it + // exists." + assert_own_property(self[this.name], member.name); + // "The value of the property is that which is obtained by + // converting the constant’s IDL value to an ECMAScript + // value." + assert_equals(self[this.name][member.name], constValue(member.value), + "property has wrong value"); + // "The property has attributes { [[Writable]]: false, + // [[Enumerable]]: true, [[Configurable]]: false }." + var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name); + assert_false("get" in desc, "property has getter"); + assert_false("set" in desc, "property has setter"); + assert_false(desc.writable, "property is writable"); + assert_true(desc.enumerable, "property is not enumerable"); + assert_false(desc.configurable, "property is configurable"); + }.bind(this), this.name + " interface: constant " + member.name + " on interface object"); + // "In addition, a property with the same characteristics must + // exist on the interface prototype object." + test(function() + { + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + + assert_own_property(self[this.name], "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + assert_own_property(self[this.name].prototype, member.name); + assert_equals(self[this.name].prototype[member.name], constValue(member.value), + "property has wrong value"); + var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name); + assert_false("get" in desc, "property has getter"); + assert_false("set" in desc, "property has setter"); + assert_false(desc.writable, "property is writable"); + assert_true(desc.enumerable, "property is not enumerable"); + assert_false(desc.configurable, "property is configurable"); + }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object"); +}; + + +//@} +IdlInterface.prototype.test_member_attribute = function(member) +//@{ +{ + test(function() + { + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + assert_own_property(self[this.name], "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + if (member["static"]) { + assert_own_property(self[this.name], member.name, + "The interface object must have a property " + + format_value(member.name)); + } else if (this.is_global()) { + assert_own_property(self, member.name, + "The global object must have a property " + + format_value(member.name)); + assert_false(member.name in self[this.name].prototype, + "The prototype object must not have a property " + + format_value(member.name)); + + // Try/catch around the get here, since it can legitimately throw. + // If it does, we obviously can't check for equality with direct + // invocation of the getter. + var gotValue; + var propVal; + try { + propVal = self[member.name]; + gotValue = true; + } catch (e) { + gotValue = false; + } + if (gotValue) { + var getter = Object.getOwnPropertyDescriptor(self, member.name).get; + assert_equals(typeof(getter), "function", + format_value(member.name) + " must have a getter"); + assert_equals(propVal, getter.call(undefined), + "Gets on a global should not require an explicit this"); + } + this.do_interface_attribute_asserts(self, member); + } else { + assert_true(member.name in self[this.name].prototype, + "The prototype object must have a property " + + format_value(member.name)); + + if (!member.has_extended_attribute("LenientThis")) { + assert_throws(new TypeError(), function() { + self[this.name].prototype[member.name]; + }.bind(this), "getting property on prototype object must throw TypeError"); + } else { + assert_equals(self[this.name].prototype[member.name], undefined, + "getting property on prototype object must return undefined"); + } + this.do_interface_attribute_asserts(self[this.name].prototype, member); + } + }.bind(this), this.name + " interface: attribute " + member.name); +}; + +//@} +IdlInterface.prototype.test_member_operation = function(member) +//@{ +{ + test(function() + { + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + + assert_own_property(self[this.name], "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // "For each unique identifier of an operation defined on the + // interface, there must be a corresponding property on the + // interface prototype object (if it is a regular operation) or + // the interface object (if it is a static operation), unless + // the effective overload set for that identifier and operation + // and with an argument count of 0 (for the ECMAScript language + // binding) has no entries." + // + var memberHolderObject; + if (member["static"]) { + assert_own_property(self[this.name], member.name, + "interface object missing static operation"); + memberHolderObject = self[this.name]; + } else if (this.is_global()) { + assert_own_property(self, member.name, + "global object missing non-static operation"); + memberHolderObject = self; + } else { + assert_own_property(self[this.name].prototype, member.name, + "interface prototype object missing non-static operation"); + memberHolderObject = self[this.name].prototype; + } + + this.do_member_operation_asserts(memberHolderObject, member); + }.bind(this), this.name + " interface: operation " + member.name + + "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) + + ")"); +}; + +//@} +IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member) +//@{ +{ + var operationUnforgeable = member.isUnforgeable; + var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name); + // "The property has attributes { [[Writable]]: B, + // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the + // operation is unforgeable on the interface, and true otherwise". + assert_false("get" in desc, "property has getter"); + assert_false("set" in desc, "property has setter"); + assert_equals(desc.writable, !operationUnforgeable, + "property should be writable if and only if not unforgeable"); + assert_true(desc.enumerable, "property is not enumerable"); + assert_equals(desc.configurable, !operationUnforgeable, + "property should be configurable if and only if not unforgeable"); + // "The value of the property is a Function object whose + // behavior is as follows . . ." + assert_equals(typeof memberHolderObject[member.name], "function", + "property must be a function"); + // "The value of the Function object’s “length” property is + // a Number determined as follows: + // ". . . + // "Return the length of the shortest argument list of the + // entries in S." + assert_equals(memberHolderObject[member.name].length, + minOverloadLength(this.members.filter(function(m) { + return m.type == "operation" && m.name == member.name; + })), + "property has wrong .length"); + + // Make some suitable arguments + var args = member.arguments.map(function(arg) { + return create_suitable_object(arg.idlType); + }); + + // "Let O be a value determined as follows: + // ". . . + // "Otherwise, throw a TypeError." + // This should be hit if the operation is not static, there is + // no [ImplicitThis] attribute, and the this value is null. + // + // TODO: We currently ignore the [ImplicitThis] case. Except we manually + // check for globals, since otherwise we'll invoke window.close(). And we + // have to skip this test for anything that on the proto chain of "self", + // since that does in fact have implicit-this behavior. + if (!member["static"]) { + if (!this.is_global() && + memberHolderObject[member.name] != self[member.name]) + { + assert_throws(new TypeError(), function() { + memberHolderObject[member.name].apply(null, args); + }, "calling operation with this = null didn't throw TypeError"); + } + + // ". . . If O is not null and is also not a platform object + // that implements interface I, throw a TypeError." + // + // TODO: Test a platform object that implements some other + // interface. (Have to be sure to get inheritance right.) + assert_throws(new TypeError(), function() { + memberHolderObject[member.name].apply({}, args); + }, "calling operation with this = {} didn't throw TypeError"); + } +} + +//@} +IdlInterface.prototype.test_member_stringifier = function(member) +//@{ +{ + test(function() + { + if (this.is_callback() && !this.has_constants()) { + return; + } + + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + + if (this.is_callback()) { + assert_false("prototype" in self[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + + assert_own_property(self[this.name], "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // ". . . the property exists on the interface prototype object." + var interfacePrototypeObject = self[this.name].prototype; + assert_own_property(self[this.name].prototype, "toString", + "interface prototype object missing non-static operation"); + + var stringifierUnforgeable = member.isUnforgeable; + var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString"); + // "The property has attributes { [[Writable]]: B, + // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the + // stringifier is unforgeable on the interface, and true otherwise." + assert_false("get" in desc, "property has getter"); + assert_false("set" in desc, "property has setter"); + assert_equals(desc.writable, !stringifierUnforgeable, + "property should be writable if and only if not unforgeable"); + assert_true(desc.enumerable, "property is not enumerable"); + assert_equals(desc.configurable, !stringifierUnforgeable, + "property should be configurable if and only if not unforgeable"); + // "The value of the property is a Function object, which behaves as + // follows . . ." + assert_equals(typeof interfacePrototypeObject.toString, "function", + "property must be a function"); + // "The value of the Function object’s “length” property is the Number + // value 0." + assert_equals(interfacePrototypeObject.toString.length, 0, + "property has wrong .length"); + + // "Let O be the result of calling ToObject on the this value." + assert_throws(new TypeError(), function() { + self[this.name].prototype.toString.apply(null, []); + }, "calling stringifier with this = null didn't throw TypeError"); + + // "If O is not an object that implements the interface on which the + // stringifier was declared, then throw a TypeError." + // + // TODO: Test a platform object that implements some other + // interface. (Have to be sure to get inheritance right.) + assert_throws(new TypeError(), function() { + self[this.name].prototype.toString.apply({}, []); + }, "calling stringifier with this = {} didn't throw TypeError"); + }.bind(this), this.name + " interface: stringifier"); +}; + +//@} +IdlInterface.prototype.test_members = function() +//@{ +{ + for (var i = 0; i < this.members.length; i++) + { + var member = this.members[i]; + if (member.untested) { + continue; + } + + switch (member.type) { + case "const": + this.test_member_const(member); + break; + + case "attribute": + // For unforgeable attributes, we do the checks in + // test_interface_of instead. + if (!member.isUnforgeable) + { + this.test_member_attribute(member); + } + break; + + case "operation": + // TODO: Need to correctly handle multiple operations with the same + // identifier. + // For unforgeable operations, we do the checks in + // test_interface_of instead. + if (member.name) { + if (!member.isUnforgeable) + { + this.test_member_operation(member); + } + } else if (member.stringifier) { + this.test_member_stringifier(member); + } + break; + + default: + // TODO: check more member types. + break; + } + } +}; + +//@} +IdlInterface.prototype.test_object = function(desc) +//@{ +{ + var obj, exception = null; + try + { + obj = eval(desc); + } + catch(e) + { + exception = e; + } + + // TODO: WebIDLParser doesn't currently support named legacycallers, so I'm + // not sure what those would look like in the AST + var expected_typeof = this.members.some(function(member) + { + return member.legacycaller + || ("idlType" in member && member.idlType.legacycaller) + || ("idlType" in member && typeof member.idlType == "object" + && "idlType" in member.idlType && member.idlType.idlType == "legacycaller"); + }) ? "function" : "object"; + + this.test_primary_interface_of(desc, obj, exception, expected_typeof); + var current_interface = this; + while (current_interface) + { + if (!(current_interface.name in this.array.members)) + { + throw "Interface " + current_interface.name + " not found (inherited by " + this.name + ")"; + } + if (current_interface.prevent_multiple_testing && current_interface.already_tested) + { + return; + } + current_interface.test_interface_of(desc, obj, exception, expected_typeof); + current_interface = this.array.members[current_interface.base]; + } +}; + +//@} +IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof) +//@{ +{ + // We can't easily test that its prototype is correct if there's no + // interface object, or the object is from a different global environment + // (not instanceof Object). TODO: test in this case that its prototype at + // least looks correct, even if we can't test that it's actually correct. + if (!this.has_extended_attribute("NoInterfaceObject") + && (typeof obj != expected_typeof || obj instanceof Object)) + { + test(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + assert_own_property(self, this.name, + "self does not have own property " + format_value(this.name)); + assert_own_property(self[this.name], "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // "The value of the internal [[Prototype]] property of the + // platform object is the interface prototype object of the primary + // interface from the platform object’s associated global + // environment." + assert_equals(Object.getPrototypeOf(obj), + self[this.name].prototype, + desc + "'s prototype is not " + this.name + ".prototype"); + }.bind(this), this.name + " must be primary interface of " + desc); + } + + // "The class string of a platform object that implements one or more + // interfaces must be the identifier of the primary interface of the + // platform object." + test(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + assert_class_string(obj, this.name, "class string of " + desc); + if (!this.has_stringifier()) + { + assert_equals(String(obj), "[object " + this.name + "]", "String(" + desc + ")"); + } + }.bind(this), "Stringification of " + desc); +}; + +//@} +IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof) +//@{ +{ + // TODO: Indexed and named properties, more checks on interface members + this.already_tested = true; + + for (var i = 0; i < this.members.length; i++) + { + var member = this.members[i]; + if (member.type == "attribute" && member.isUnforgeable) + { + test(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + this.do_interface_attribute_asserts(obj, member); + }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"'); + } + else if (member.type == "operation" && + member.name && + member.isUnforgeable) + { + test(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + assert_own_property(obj, member.name, + "Doesn't have the unforgeable operation property"); + this.do_member_operation_asserts(obj, member); + }.bind(this), this.name + " interface: " + desc + ' must have own property "' + member.name + '"'); + } + else if ((member.type == "const" + || member.type == "attribute" + || member.type == "operation") + && member.name) + { + test(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + if (!member["static"]) { + if (!this.is_global()) { + assert_inherits(obj, member.name); + } else { + assert_own_property(obj, member.name); + } + + if (member.type == "const") + { + assert_equals(obj[member.name], constValue(member.value)); + } + if (member.type == "attribute") + { + // Attributes are accessor properties, so they might + // legitimately throw an exception rather than returning + // anything. + var property, thrown = false; + try + { + property = obj[member.name]; + } + catch (e) + { + thrown = true; + } + if (!thrown) + { + this.array.assert_type_is(property, member.idlType); + } + } + if (member.type == "operation") + { + assert_equals(typeof obj[member.name], "function"); + } + } + }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member.name + '" with the proper type (' + i + ')'); + } + // TODO: This is wrong if there are multiple operations with the same + // identifier. + // TODO: Test passing arguments of the wrong type. + if (member.type == "operation" && member.name && member.arguments.length) + { + test(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + if (!member["static"]) { + if (!this.is_global() && !member.isUnforgeable) { + assert_inherits(obj, member.name); + } else { + assert_own_property(obj, member.name); + } + } + else + { + assert_false(member.name in obj); + } + + var minLength = minOverloadLength(this.members.filter(function(m) { + return m.type == "operation" && m.name == member.name; + })); + var args = []; + for (var i = 0; i < minLength; i++) { + assert_throws(new TypeError(), function() + { + obj[member.name].apply(obj, args); + }.bind(this), "Called with " + i + " arguments"); + + args.push(create_suitable_object(member.arguments[i].idlType)); + } + }.bind(this), this.name + " interface: calling " + member.name + + "(" + member.arguments.map(function(m) { return m.idlType.idlType; }) + + ") on " + desc + " with too few arguments must throw TypeError"); + } + } +}; + +//@} +IdlInterface.prototype.has_stringifier = function() +//@{ +{ + if (this.members.some(function(member) { return member.stringifier; })) { + return true; + } + if (this.base && + this.array.members[this.base].has_stringifier()) { + return true; + } + return false; +}; + +//@} +IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member) +//@{ +{ + // This function tests WebIDL as of 2015-01-27. + // TODO: Consider [Exposed]. + + // This is called by test_member_attribute() with the prototype as obj if + // it is not a global, and the global otherwise, and by test_interface_of() + // with the object as obj. + + // "For each exposed attribute of the interface, whether it was declared on + // the interface itself or one of its consequential interfaces, there MUST + // exist a corresponding property. The characteristics of this property are + // as follows:" + + // "The name of the property is the identifier of the attribute." + assert_own_property(obj, member.name); + + // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]: + // true, [[Configurable]]: configurable }, where: + // "configurable is false if the attribute was declared with the + // [Unforgeable] extended attribute and true otherwise; + // "G is the attribute getter, defined below; and + // "S is the attribute setter, also defined below." + var desc = Object.getOwnPropertyDescriptor(obj, member.name); + assert_false("value" in desc, 'property descriptor has value but is supposed to be accessor'); + assert_false("writable" in desc, 'property descriptor has "writable" field but is supposed to be accessor'); + assert_true(desc.enumerable, "property is not enumerable"); + if (member.isUnforgeable) + { + assert_false(desc.configurable, "[Unforgeable] property must not be configurable"); + } + else + { + assert_true(desc.configurable, "property must be configurable"); + } + + + // "The attribute getter is a Function object whose behavior when invoked + // is as follows:" + assert_equals(typeof desc.get, "function", "getter must be Function"); + + // "If the attribute is a regular attribute, then:" + if (!member["static"]) { + // "If O is not a platform object that implements I, then: + // "If the attribute was specified with the [LenientThis] extended + // attribute, then return undefined. + // "Otherwise, throw a TypeError." + if (!member.has_extended_attribute("LenientThis")) { + assert_throws(new TypeError(), function() { + desc.get.call({}); + }.bind(this), "calling getter on wrong object type must throw TypeError"); + } else { + assert_equals(desc.get.call({}), undefined, + "calling getter on wrong object type must return undefined"); + } + } + + // "The value of the Function object’s “length” property is the Number + // value 0." + assert_equals(desc.get.length, 0, "getter length must be 0"); + + + // TODO: Test calling setter on the interface prototype (should throw + // TypeError in most cases). + if (member.readonly + && !member.has_extended_attribute("PutForwards") + && !member.has_extended_attribute("Replaceable")) + { + // "The attribute setter is undefined if the attribute is declared + // readonly and has neither a [PutForwards] nor a [Replaceable] + // extended attribute declared on it." + assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes"); + } + else + { + // "Otherwise, it is a Function object whose behavior when + // invoked is as follows:" + assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes"); + + // "If the attribute is a regular attribute, then:" + if (!member["static"]) { + // "If /validThis/ is false and the attribute was not specified + // with the [LenientThis] extended attribute, then throw a + // TypeError." + // "If the attribute is declared with a [Replaceable] extended + // attribute, then: ..." + // "If validThis is false, then return." + if (!member.has_extended_attribute("LenientThis")) { + assert_throws(new TypeError(), function() { + desc.set.call({}); + }.bind(this), "calling setter on wrong object type must throw TypeError"); + } else { + assert_equals(desc.set.call({}), undefined, + "calling setter on wrong object type must return undefined"); + } + } + + // "The value of the Function object’s “length” property is the Number + // value 1." + assert_equals(desc.set.length, 1, "setter length must be 1"); + } +} +//@} + +/// IdlInterfaceMember /// +function IdlInterfaceMember(obj) +//@{ +{ + /** + * obj is an object produced by the WebIDLParser.js "ifMember" production. + * We just forward all properties to this object without modification, + * except for special extAttrs handling. + */ + for (var k in obj) + { + this[k] = obj[k]; + } + if (!("extAttrs" in this)) + { + this.extAttrs = []; + } + + this.isUnforgeable = this.has_extended_attribute("Unforgeable"); +} + +//@} +IdlInterfaceMember.prototype = Object.create(IdlObject.prototype); + +/// Internal helper functions /// +function create_suitable_object(type) +//@{ +{ + /** + * type is an object produced by the WebIDLParser.js "type" production. We + * return a JavaScript value that matches the type, if we can figure out + * how. + */ + if (type.nullable) + { + return null; + } + switch (type.idlType) + { + case "any": + case "boolean": + return true; + + case "byte": case "octet": case "short": case "unsigned short": + case "long": case "unsigned long": case "long long": + case "unsigned long long": case "float": case "double": + case "unrestricted float": case "unrestricted double": + return 7; + + case "DOMString": + case "ByteString": + case "USVString": + return "foo"; + + case "object": + return {a: "b"}; + + case "Node": + return document.createTextNode("abc"); + } + return null; +} +//@} + +/// IdlEnum /// +// Used for IdlArray.prototype.assert_type_is +function IdlEnum(obj) +//@{ +{ + /** + * obj is an object produced by the WebIDLParser.js "dictionary" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** An array of values produced by the "enum" production. */ + this.values = obj.values; + +} +//@} + +IdlEnum.prototype = Object.create(IdlObject.prototype); + +/// IdlTypedef /// +// Used for IdlArray.prototype.assert_type_is +function IdlTypedef(obj) +//@{ +{ + /** + * obj is an object produced by the WebIDLParser.js "typedef" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** An array of values produced by the "typedef" production. */ + this.values = obj.values; + +} +//@} + +IdlTypedef.prototype = Object.create(IdlObject.prototype); + +}()); +// vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: |