Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/testing/web-platform/tests/resources/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 142 kB image not shown  

SSL idlharness.js   Sprache: JAVA

 
/* For user documentation see docs/_writing-tests/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";
// Support subsetTestByKey from /common/subset-tests-by-key.js, but make it optional
if (!('subsetTestByKey' in self)) {
    self.subsetTestByKey = function(key, callback, ...args) {
      return callback(...args);
    }
    self.shouldRunSubTest = () => true;
}
/// 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;
    if (cnt.type === "number"return +cnt.value;
    return cnt.value;
}

function minOverloadLength(overloads)
{
    // "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."
    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); });
}

// A helper to get the global of a Function object.  This is needed to determine
// which global exceptions the function throws will come from.
function globalOf(func)
{
    try {
        // Use the fact that .constructor for a Function object is normally the
        // Function constructor, which can be used to mint a new function in the
        // right global.
        return func.constructor("return this;")();
    } catch (e) {
    }
    // If the above fails, because someone gave us a non-function, or a function
    // with a weird proto chain or weird .constructor property, just fall back
    // to 'self'.
    return self;
}

// https://esdiscuss.org/topic/isconstructor#content-11
function isConstructor(o) {
    try {
        new (new Proxy(o, {construct: () => ({})}));
        return true;
    } catch(e) {
        return false;
    }
}

function throwOrReject(a_test, operation, fn, obj, args, message, cb)
{
    if (operation.idlType.generic !== "Promise") {
        assert_throws_js(globalOf(fn).TypeError, function() {
            fn.apply(obj, args);
        }, message);
        cb();
    } else {
        try {
            promise_rejects_js(a_test, TypeError, fn.apply(obj, args), message).then(cb, cb);
        } catch (e){
            a_test.step(function() {
                assert_unreached("Throws \"" + e + "\" instead of rejecting promise");
                cb();
            });
        }
    }
}

function awaitNCallbacks(n, cb, ctx)
{
    var counter = 0;
    return function() {
        counter++;
        if (counter >= n) {
            cb();
        }
    };
}

/// IdlHarnessError ///
// Entry point
self.IdlHarnessError = function(message)
{
    /**
     * Message to be printed as the error's toString invocation.
     */

    this.message = message;
};

IdlHarnessError.prototype = Object.create(Error.prototype);

IdlHarnessError.prototype.toString = function()
{
    return this.message;
};


/// 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 includes statement that depends
     * on a later one.  Save these up and handle them right before we run
     * tests.
     *
     * Both this.partials and this.includes will be the objects as parsed by
     * WebIDLParser.js, not wrapped in IdlInterface or similar.
     */

    this.partials = [];
    this.includes = [];

    /**
     * Record of skipped IDL items, in case we later realize that they are a
     * dependency (to retroactively process them).
     */

    this.skipped = new Map();
};

IdlArray.prototype.add_idls = function(raw_idls, options)
{
    /** Entry point.  See documentation at beginning of file. */
    this.internal_add_idls(WebIDL2.parse(raw_idls), options);
};

IdlArray.prototype.add_untested_idls = function(raw_idls, options)
{
    /** Entry point.  See documentation at beginning of file. */
    var parsed_idls = WebIDL2.parse(raw_idls);
    this.mark_as_untested(parsed_idls);
    this.internal_add_idls(parsed_idls, options);
};

IdlArray.prototype.mark_as_untested = function (parsed_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;
            }
        }
    }
};

IdlArray.prototype.is_excluded_by_options = function (name, options)
{
    return options &&
        (options.except && options.except.includes(name)
         || options.only && !options.only.includes(name));
};

IdlArray.prototype.add_dependency_idls = function(raw_idls, options)
{
    return this.internal_add_dependency_idls(WebIDL2.parse(raw_idls), options);
};

IdlArray.prototype.internal_add_dependency_idls = function(parsed_idls, options)
{
    const new_options = { only: [] }

    const all_deps = new Set();
    Object.values(this.members).forEach(v => {
        if (v.base) {
            all_deps.add(v.base);
        }
    });
    // Add both 'A' and 'B' for each 'A includes B' entry.
    this.includes.forEach(i => {
        all_deps.add(i.target);
        all_deps.add(i.includes);
    });
    this.partials.forEach(p => all_deps.add(p.name));
    // Add 'TypeOfType' for each "typedef TypeOfType MyType;" entry.
    Object.entries(this.members).forEach(([k, v]) => {
        if (v instanceof IdlTypedef) {
            let defs = v.idlType.union
                ? v.idlType.idlType.map(t => t.idlType)
                : [v.idlType.idlType];
            defs.forEach(d => all_deps.add(d));
        }
    });

    // Add the attribute idlTypes of all the nested members of idls.
    const attrDeps = parsedIdls => {
        return parsedIdls.reduce((deps, parsed) => {
            if (parsed.members) {
                for (const attr of Object.values(parsed.members).filter(m => m.type === 'attribute')) {
                    let attrType = attr.idlType;
                    // Check for generic members (e.g. FrozenArray<MyType>)
                    if (attrType.generic) {
                        deps.add(attrType.generic);
                        attrType = attrType.idlType;
                    }
                    deps.add(attrType.idlType);
                }
            }
            if (parsed.base in this.members) {
                attrDeps([this.members[parsed.base]]).forEach(dep => deps.add(dep));
            }
            return deps;
        }, new Set());
    };

    const testedMembers = Object.values(this.members).filter(m => !m.untested && m.members);
    attrDeps(testedMembers).forEach(dep => all_deps.add(dep));

    const testedPartials = this.partials.filter(m => !m.untested && m.members);
    attrDeps(testedPartials).forEach(dep => all_deps.add(dep));


    if (options && options.except && options.only) {
        throw new IdlHarnessError("The only and except options can't be used together.");
    }

    const defined_or_untested = name => {
        // NOTE: Deps are untested, so we're lenient, and skip re-encountered definitions.
        // e.g. for 'idl' containing A:B, B:C, C:D
        //      array.add_idls(idl, {only: ['A','B']}).
        //      array.add_dependency_idls(idl);
        // B would be encountered as tested, and encountered as a dep, so we ignore.
        return name in this.members
            || this.is_excluded_by_options(name, options);
    }
    // Maps name -> [parsed_idl, ...]
    const process = function(parsed) {
        var deps = [];
        if (parsed.name) {
            deps.push(parsed.name);
        } else if (parsed.type === "includes") {
            deps.push(parsed.target);
            deps.push(parsed.includes);
        }

        deps = deps.filter(function(name) {
            if (!name
                || name === parsed.name && defined_or_untested(name)
                || !all_deps.has(name)) {
                // Flag as skipped, if it's not already processed, so we can
                // come back to it later if we retrospectively call it a dep.
                if (name && !(name in this.members)) {
                    this.skipped.has(name)
                        ? this.skipped.get(name).push(parsed)
                        : this.skipped.set(name, [parsed]);
                }
                return false;
            }
            return true;
        }.bind(this));

        deps.forEach(function(name) {
            if (!new_options.only.includes(name)) {
                new_options.only.push(name);
            }

            const follow_up = new Set();
            for (const dep_type of ["inheritance""includes"]) {
                if (parsed[dep_type]) {
                    const inheriting = parsed[dep_type];
                    const inheritor = parsed.name || parsed.target;
                    const deps = [inheriting];
                    // For A includes B, we can ignore A, unless B (or some of its
                    // members) is being tested.
                    if (dep_type !== "includes"
                        || inheriting in this.members && !this.members[inheriting].untested
                        || this.partials.some(function(p) {
                                return p.name === inheriting;
                            })) {
                        deps.push(inheritor);
                    }
                    for (const dep of deps) {
                        if (!new_options.only.includes(dep)) {
                            new_options.only.push(dep);
                        }
                        all_deps.add(dep);
                        follow_up.add(dep);
                    }
                }
            }

            for (const deferred of follow_up) {
                if (this.skipped.has(deferred)) {
                    const next = this.skipped.get(deferred);
                    this.skipped.delete(deferred);
                    next.forEach(process);
                }
            }
        }.bind(this));
    }.bind(this);

    for (let parsed of parsed_idls) {
        process(parsed);
    }

    this.mark_as_untested(parsed_idls);

    if (new_options.only.length) {
        this.internal_add_idls(parsed_idls, new_options);
    }
}

IdlArray.prototype.internal_add_idls = function(parsed_idls, options)
{
    /**
     * 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.
     *
     * options is a dictionary that can have an only or except member which are
     * arrays. If only is given then only members, partials and interface
     * targets listed will be added, and if except is given only those that
     * aren't listed will be added. Only one of only and except can be used.
     */


    if (options && options.only && options.except)
    {
        throw new IdlHarnessError("The only and except options can't be used together.");
    }

    var should_skip = name => {
        return this.is_excluded_by_options(name, options);
    }

    parsed_idls.forEach(function(parsed_idl)
    {
        var partial_types = [
            "interface",
            "interface mixin",
            "dictionary",
            "namespace",
        ];
        if (parsed_idl.partial && partial_types.includes(parsed_idl.type))
        {
            if (should_skip(parsed_idl.name))
            {
                return;
            }
            this.partials.push(parsed_idl);
            return;
        }

        if (parsed_idl.type == "includes")
        {
            if (should_skip(parsed_idl.target))
            {
                return;
            }
            this.includes.push(parsed_idl);
            return;
        }

        parsed_idl.array = this;
        if (should_skip(parsed_idl.name))
        {
            return;
        }
        if (parsed_idl.name in this.members)
        {
            throw new IdlHarnessError("Duplicate identifier " + parsed_idl.name);
        }

        switch(parsed_idl.type)
        {
        case "interface":
            this.members[parsed_idl.name] =
                new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ false);
            break;

        case "interface mixin":
            this.members[parsed_idl.name] =
                new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ true);
            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":
            this.members[parsed_idl.name] = new IdlCallback(parsed_idl);
            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, /* is_mixin = */ false);
            break;

        case "namespace":
            this.members[parsed_idl.name] = new IdlNamespace(parsed_idl);
            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.is_json_type = function(type)
{
    /**
     * Checks whether type is a JSON type as per
     * https://webidl.spec.whatwg.org/#dfn-json-types
     */


    var idlType = type.idlType;

    if (type.generic == "Promise") { return false; }

    //  nullable and annotated types don't need to be handled separately,
    //  as webidl2 doesn't represent them wrapped-up (as they're described
    //  in WebIDL).

    // union and record types
    if (type.union || type.generic == "record") {
        return idlType.every(this.is_json_type, this);
    }

    // sequence types
    if (type.generic == "sequence" || type.generic == "FrozenArray") {
        return this.is_json_type(idlType[0]);
    }

    if (typeof idlType != "string") { throw new Error("Unexpected type " + JSON.stringify(idlType)); }

    switch (idlType)
    {
       //  Numeric types
       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":
       // boolean
       case "boolean":
       // string types
       case "DOMString":
       case "ByteString":
       case "USVString":
       // object type
       case "object":
           return true;
       case "Error":
       case "DOMException":
       case "Int8Array":
       case "Int16Array":
       case "Int32Array":
       case "Uint8Array":
       case "Uint16Array":
       case "Uint32Array":
       case "Uint8ClampedArray":
       case "BigInt64Array":
       case "BigUint64Array":
       case "Float16Array":
       case "Float32Array":
       case "Float64Array":
       case "ArrayBuffer":
       case "DataView":
       case "any":
           return false;
       default:
           var thing = this.members[idlType];
           if (!thing) { throw new Error("Type " + idlType + " not found"); }
           if (thing instanceof IdlEnum) { return true; }

           if (thing instanceof IdlTypedef) {
               return this.is_json_type(thing.idlType);
           }

           //  dictionaries where all of their members are JSON types
           if (thing instanceof IdlDictionary) {
               const map = new Map();
               for (const dict of thing.get_reverse_inheritance_stack()) {
                   for (const m of dict.members) {
                       map.set(m.name, m.idlType);
                   }
               }
               return Array.from(map.values()).every(this.is_json_type, this);
           }

           //  interface types that have a toJSON operation declared on themselves or
           //  one of their inherited interfaces.
           if (thing instanceof IdlInterface) {
               var base;
               while (thing)
               {
                   if (thing.has_to_json_regular_operation()) { return true; }
                   var mixins = this.includes[thing.name];
                   if (mixins) {
                       mixins = mixins.map(function(id) {
                           var mixin = this.members[id];
                           if (!mixin) {
                               throw new Error("Interface " + id + " not found (implemented by " + thing.name + ")");
                           }
                           return mixin;
                       }, this);
                       if (mixins.some(function(m) { return m.has_to_json_regular_operation() } )) { return true; }
                   }
                   if (!thing.base) { return false; }
                   base = this.members[thing.base];
                   if (!base) {
                       throw new Error("Interface " + thing.base + " not found (inherited by " + thing.name + ")");
                   }
                   thing = base;
               }
               return false;
           }
           return false;
    }
};

function exposure_set(object, default_set) {
    var exposed = object.extAttrs && object.extAttrs.filter(a => a.name === "Exposed");
    if (exposed && exposed.length > 1) {
        throw new IdlHarnessError(
            `Multiple 'Exposed' extended attributes on ${object.name}`);
    }

    let result = default_set || ["Window"];
    if (result && !(result instanceof Set)) {
        result = new Set(result);
    }
    if (exposed && exposed.length) {
        const { rhs } = exposed[0];
        // Could be a list or a string.
        const set =
            rhs.type === "*" ?
            [ "*" ] :
            rhs.type === "identifier-list" ?
            rhs.value.map(id => id.value) :
            [ rhs.value ];
        result = new Set(set);
    }
    if (result && result.has("*")) {
        return "*";
    }
    if (result && result.has("Worker")) {
        result.delete("Worker");
        result.add("DedicatedWorker");
        result.add("ServiceWorker");
        result.add("SharedWorker");
    }
    return result;
}

function exposed_in(globals) {
    if (globals === "*") {
        return true;
    }
    if ('Window' in self) {
        return globals.has("Window");
    }
    if ('DedicatedWorkerGlobalScope' in self &&
        self instanceof DedicatedWorkerGlobalScope) {
        return globals.has("DedicatedWorker");
    }
    if ('SharedWorkerGlobalScope' in self &&
        self instanceof SharedWorkerGlobalScope) {
        return globals.has("SharedWorker");
    }
    if ('ServiceWorkerGlobalScope' in self &&
        self instanceof ServiceWorkerGlobalScope) {
        return globals.has("ServiceWorker");
    }
    if (Object.getPrototypeOf(self) === Object.prototype) {
        // ShadowRealm - only exposed with `"*"`.
        return false;
    }
    throw new IdlHarnessError("Unexpected global object");
}

/**
 * Asserts that the given error message is thrown for the given function.
 * @param {string|IdlHarnessError} error Expected Error message.
 * @param {Function} idlArrayFunc Function operating on an IdlArray that should throw.
 */

IdlArray.prototype.assert_throws = function(error, idlArrayFunc)
{
    try {
        idlArrayFunc.call(thisthis);
    } catch (e) {
        if (e instanceof AssertionError) {
            throw e;
        }
        // Assertions for behaviour of the idlharness.js engine.
        if (error instanceof IdlHarnessError) {
            error = error.message;
        }
        if (e.message !== error) {
            throw new IdlHarnessError(`${idlArrayFunc} threw "${e}", not the expected IdlHarnessErro"${error}"`);
        }
        return;
    }
    throw new IdlHarnessError(`${idlArrayFunc} did not throw the expected IdlHarnessError`);
}

IdlArray.prototype.test = function()
{
    /** Entry point.  See documentation at beginning of file. */

    // First merge in all partial definitions and interface mixins.
    this.merge_partials();
    this.merge_mixins();

    // Assert B defined for A : B
    for (const member of Object.values(this.members).filter(m => m.base)) {
        const lhs = member.name;
        const rhs = member.base;
        if (!(rhs in this.members)) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is undefined.`);
        const lhs_is_interface = this.members[lhs] instanceof IdlInterface;
        const rhs_is_interface = this.members[rhs] instanceof IdlInterface;
        if (rhs_is_interface != lhs_is_interface) {
            if (!lhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${lhs} is not an interface.`);
            if (!rhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is not an interface.`);
        }
        // Check for circular dependencies.
        member.get_reverse_inheritance_stack();
    }

    Object.getOwnPropertyNames(this.members).forEach(function(memberName) {
        var member = this.members[memberName];
        if (!(member instanceof IdlInterface || member instanceof IdlNamespace)) {
            return;
        }

        var globals = exposure_set(member);
        member.exposed = exposed_in(globals);
        member.exposureSet = globals;
    }.bind(this));

    // 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)
        {
            const objects = this.objects[name];
            if (!objects || !Array.isArray(objects)) {
                throw new IdlHarnessError(`Invalid or empty objects for member ${name}`);
            }
            objects.forEach(function(str)
            {
                if (!this.members[name] || !(this.members[name] instanceof IdlInterface)) {
                    throw new IdlHarnessError(`Invalid object member name ${name}`);
                }
                this.members[name].test_object(str);
            }.bind(this));
        }
    }
};

IdlArray.prototype.merge_partials = function()
{
    const testedPartials = new Map();
    this.partials.forEach(function(parsed_idl)
    {
        const originalExists = parsed_idl.name in this.members
            && (this.members[parsed_idl.name] instanceof IdlInterface
                || this.members[parsed_idl.name] instanceof IdlDictionary
                || this.members[parsed_idl.name] instanceof IdlNamespace);

        // Ensure unique test name in case of multiple partials.
        let partialTestName = parsed_idl.name;
        let partialTestCount = 1;
        if (testedPartials.has(parsed_idl.name)) {
            partialTestCount += testedPartials.get(parsed_idl.name);
            partialTestName = `${partialTestName}[${partialTestCount}]`;
        }
        testedPartials.set(parsed_idl.name, partialTestCount);

        if (!parsed_idl.untested) {
            test(function () {
                assert_true(originalExists, `Original ${parsed_idl.type} should be defined`);

                var expected;
                switch (parsed_idl.type) {
                    case 'dictionary': expected = IdlDictionary; break;
                    case 'namespace': expected = IdlNamespace; break;
                    case 'interface':
                    case 'interface mixin':
                    default:
                        expected = IdlInterface; break;
                }
                assert_true(
                    expected.prototype.isPrototypeOf(this.members[parsed_idl.name]),
                    `Original ${parsed_idl.name} definition should have type ${parsed_idl.type}`);
            }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: original ${parsed_idl.type} defined`);
        }
        if (!originalExists) {
            // Not good.. but keep calm and carry on.
            return;
        }

        if (parsed_idl.extAttrs)
        {
            // Special-case "Exposed". Must be a subset of original interface's exposure.
            // Exposed on a partial is the equivalent of having the same Exposed on all nested members.
            // See https://github.com/heycam/webidl/issues/154 for discrepency between Exposed and
            // other extended attributes on partial interfaces.
            const exposureAttr = parsed_idl.extAttrs.find(a => a.name === "Exposed");
            if (exposureAttr) {
                if (!parsed_idl.untested) {
                    test(function () {
                        const partialExposure = exposure_set(parsed_idl);
                        const memberExposure = exposure_set(this.members[parsed_idl.name]);
                        if (memberExposure === "*") {
                            return;
                        }
                        if (partialExposure === "*") {
                            throw new IdlHarnessError(
                                `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed everywhere, the original ${parsed_idl.type} is not.`);
                        }
                        partialExposure.forEach(name => {
                            if (!memberExposure || !memberExposure.has(name)) {
                                throw new IdlHarnessError(
                                    `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed to '${name}', the original ${parsed_idl.type} is not.`);
                            }
                        });
                    }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: valid exposure set`);
                }
                parsed_idl.members.forEach(function (member) {
                    member.extAttrs.push(exposureAttr);
                }.bind(this));
            }

            parsed_idl.extAttrs.forEach(function(extAttr)
            {
                // "Exposed" already handled above.
                if (extAttr.name === "Exposed") {
                    return;
                }
                this.members[parsed_idl.name].extAttrs.push(extAttr);
            }.bind(this));
        }
        if (parsed_idl.members.length) {
            test(function () {
                var clash = parsed_idl.members.find(function(member) {
                    return this.members[parsed_idl.name].members.find(function(m) {
                        return this.are_duplicate_members(m, member);
                    }.bind(this));
                }.bind(this));
                parsed_idl.members.forEach(function(member)
                {
                    this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
                }.bind(this));
                assert_true(!clash, "member " + (clash && clash.name) + " is unique");
            }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: member names are unique`);
        }
    }.bind(this));
    this.partials = [];
}

IdlArray.prototype.merge_mixins = function()
{
    for (const parsed_idl of this.includes)
    {
        const lhs = parsed_idl.target;
        const rhs = parsed_idl.includes;

        var errStr = lhs + " includes " + 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.";

        if (this.members[rhs].members.length) {
            test(function () {
                var clash = this.members[rhs].members.find(function(member) {
                    return this.members[lhs].members.find(function(m) {
                        return this.are_duplicate_members(m, member);
                    }.bind(this));
                }.bind(this));
                this.members[rhs].members.forEach(function(member) {
                    assert_true(
                        this.members[lhs].members.every(m => !this.are_duplicate_members(m, member)),
                        "member " + member.name + " is unique");
                    this.members[lhs].members.push(new IdlInterfaceMember(member));
                }.bind(this));
                assert_true(!clash, "member " + (clash && clash.name) + " is unique");
            }.bind(this), lhs + " includes " + rhs + ": member names are unique");
        }
    }
    this.includes = [];
}

IdlArray.prototype.are_duplicate_members = function(m1, m2) {
    if (m1.name !== m2.name) {
        return false;
    }
    if (m1.type === 'operation' && m2.type === 'operation'
        && m1.arguments.length !== m2.arguments.length) {
        // Method overload. TODO: Deep comparison of arguments.
        return false;
    }
    return true;
}

IdlArray.prototype.assert_type_is = function(value, type)
{
    if (type.idlType in this.members
    && this.members[type.idlType] instanceof IdlTypedef) {
        this.assert_type_is(value, this.members[type.idlType].idlType);
        return;
    }

    if (type.nullable && value === null)
    {
        // This is fine
        return;
    }

    if (type.union) {
        for (var i = 0; i < type.idlType.length; i++) {
            try {
                this.assert_type_is(value, type.idlType[i]);
                // No AssertionError, so we match one type in the union
                return;
            } catch(e) {
                if (e instanceof AssertionError) {
                    // We didn't match this type, let's try some others
                    continue;
                }
                throw e;
            }
        }
        // TODO: Is there a nice way to list the union's types in the message?
        assert_true(false"Attribute has value " + format_value(value)
                    + " which doesn't match any of the types in the union");

    }

    /**
     * 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.array)
    {
        // TODO: not supported yet
        return;
    }

    if (type.generic === "sequence" || type.generic == "ObservableArray")
    {
        assert_true(Array.isArray(value), "should be an Array");
        if (!value.length)
        {
            // Nothing we can do.
            return;
        }
        this.assert_type_is(value[0], type.idlType[0]);
        return;
    }

    if (type.generic === "Promise") {
        assert_true("then" in value, "Attribute with a Promise type should have a then property");
        // TODO: Ideally, we would check on project fulfillment
        // that we get the right type
        // but that would require making the type check async
        return;
    }

    if (type.generic === "FrozenArray") {
        assert_true(Array.isArray(value), "Value should be array");
        assert_true(Object.isFrozen(value), "Value should be frozen");
        if (!value.length)
        {
            // Nothing we can do.
            return;
        }
        this.assert_type_is(value[0], type.idlType[0]);
        return;
    }

    type = Array.isArray(type.idlType) ? type.idlType[0] : type.idlType;

    switch(type)
    {
        case "undefined":
            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), "should be an integer");
            assert_true(-128 <= value && value <= 127, "byte " + value + " should be in range [-128, 127]");
            return;

        case "octet":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "should be an integer");
            assert_true(0 <= value && value <= 255, "octet " + value + " should be in range [0, 255]");
            return;

        case "short":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "should be an integer");
            assert_true(-32768 <= value && value <= 32767, "short " + value + " should be in range [-32768, 32767]");
            return;

        case "unsigned short":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "should be an integer");
            assert_true(0 <= value && value <= 65535, "unsigned short " + value + " should be in range [0, 65535]");
            return;

        case "long":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "should be an integer");
            assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " should be in range [-2147483648, 2147483647]");
            return;

        case "unsigned long":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "should be an integer");
            assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " should be 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 should be positive");
            return;

        case "float":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.fround(value), "float rounded to 32-bit float should be itself");
            assert_not_equals(value, Infinity);
            assert_not_equals(value, -Infinity);
            assert_not_equals(value, NaN);
            return;

        case "DOMHighResTimeStamp":
        case "double":
            assert_equals(typeof value, "number");
            assert_not_equals(value, Infinity);
            assert_not_equals(value, -Infinity);
            assert_not_equals(value, NaN);
            return;

        case "unrestricted float":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.fround(value), "unrestricted float rounded to 32-bit float should be itself");
            return;

        case "unrestricted double":
            assert_equals(typeof value, "number");
            return;

        case "DOMString":
            assert_equals(typeof value, "string");
            return;

        case "ByteString":
            assert_equals(typeof value, "string");
            assert_regexp_match(value, /^[\x00-\x7F]*$/);
            return;

        case "USVString":
            assert_equals(typeof value, "string");
            assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])*$/);
            return;

        case "ArrayBufferView":
            assert_true(ArrayBuffer.isView(value));
            return;

        case "object":
            assert_in_array(typeof value, ["object""function"], "wrong type: not object or function");
            return;
    }

    // This is a catch-all for any IDL type name which follows JS class
    // semantics. This includes some non-interface IDL types (e.g. Int8Array,
    // Function, ...), as well as any interface types that are not in the IDL
    // that is fed to the harness. If an IDL type does not follow JS class
    // semantics then it should go in the switch statement above. If an IDL
    // type needs full checking, then the test should include it in the IDL it
    // feeds to the harness.
    if (!(type in this.members))
    {
        assert_true(value instanceof self[type], "wrong type: not a " + type);
        return;
    }

    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
        // LegacyNoInterfaceObject interfaces, and we also can't test objects
        // that come from another self.
        assert_in_array(typeof value, ["object""function"], "wrong type: not object or function");
        if (value instanceof Object
        && !this.members[type].has_extended_attribute("LegacyNoInterfaceObject")
        && type in self)
        {
            assert_true(value instanceof self[type], "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 IdlCallback)
    {
        assert_equals(typeof value, "function");
    }
    else
    {
        throw new IdlHarnessError("Type " + type + " isn't an interface, callback 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;

    /** A back-reference to our IdlArray. */
    this.array = obj.array;

    /** 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);

IdlDictionary.prototype.get_reverse_inheritance_stack = function() {
    return IdlInterface.prototype.get_reverse_inheritance_stack.call(this);
};

/// IdlInterface ///
function IdlInterface(obj, is_callback, is_mixin)
{
    /**
     * obj is an object produced by the WebIDLParser.js "interface" production.
     */


    /** 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 interface object and
     * 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("LegacyUnforgeable")) {
        this.members
            .filter(function(m) { return m.special !== "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;
    this._is_mixin = is_mixin;
}
IdlInterface.prototype = Object.create(IdlObject.prototype);
IdlInterface.prototype.is_callback = function()
{
    return this._is_callback;
};

IdlInterface.prototype.is_mixin = function()
{
    return this._is_mixin;
};

IdlInterface.prototype.has_constants = function()
{
    return this.members.some(function(member) {
        return member.type === "const";
    });
};

IdlInterface.prototype.get_unscopables = function()
{
    return this.members.filter(function(member) {
        return member.isUnscopable;
    });
};

IdlInterface.prototype.is_global = function()
{
    return this.extAttrs.some(function(attribute) {
        return attribute.name === "Global";
    });
};

/**
 * Value of the LegacyNamespace extended attribute, if any.
 *
 * https://webidl.spec.whatwg.org/#LegacyNamespace
 */

IdlInterface.prototype.get_legacy_namespace = function()
{
    var legacyNamespace = this.extAttrs.find(function(attribute) {
        return attribute.name === "LegacyNamespace";
    });
    return legacyNamespace ? legacyNamespace.rhs.value : undefined;
};

IdlInterface.prototype.get_interface_object_owner = function()
{
    var legacyNamespace = this.get_legacy_namespace();
    return legacyNamespace ? self[legacyNamespace] : self;
};

IdlInterface.prototype.should_have_interface_object = function()
{
    // "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
    //   [LegacyNoInterfaceObject] extended attribute,
    // a corresponding property MUST exist on the ECMAScript global object.

    return this.is_callback() ? this.has_constants() : !this.has_extended_attribute("LegacyNoInterfaceObject");
};

IdlInterface.prototype.assert_interface_object_exists = function()
{
    var owner = this.get_legacy_namespace() || "self";
    assert_own_property(self[owner], this.name, owner + " does not have own property " + format_value(this.name));
};

IdlInterface.prototype.get_interface_object = function() {
    if (!this.should_have_interface_object()) {
        var reason = this.is_callback() ? "lack of declared constants" : "declared [LegacyNoInterfaceObject] attribute";
        throw new IdlHarnessError(this.name + " has no interface object due to " + reason);
    }

    return this.get_interface_object_owner()[this.name];
};

IdlInterface.prototype.get_qualified_name = function() {
    // https://webidl.spec.whatwg.org/#qualified-name
    var legacyNamespace = this.get_legacy_namespace();
    if (legacyNamespace) {
        return legacyNamespace + "." + this.name;
    }
    return this.name;
};

IdlInterface.prototype.has_to_json_regular_operation = function() {
    return this.members.some(function(m) {
        return m.is_to_json_regular_operation();
    });
};

IdlInterface.prototype.has_default_to_json_regular_operation = function() {
    return this.members.some(function(m) {
        return m.is_to_json_regular_operation() && m.has_extended_attribute("Default");
    });
};

/**
 * Implementation of https://webidl.spec.whatwg.org/#create-an-inheritance-stack
 * with the order reversed.
 *
 * The order is reversed so that the base class comes first in the list, because
 * this is what all call sites need.
 *
 * So given:
 *
 *   A : B {};
 *   B : C {};
 *   C {};
 *
 * then A.get_reverse_inheritance_stack() returns [C, B, A],
 * and B.get_reverse_inheritance_stack() returns [C, B].
 *
 * Note: as dictionary inheritance is expressed identically by the AST,
 * this works just as well for getting a stack of inherited dictionaries.
 */

IdlInterface.prototype.get_reverse_inheritance_stack = function() {
    const stack = [this];
    let idl_interface = this;
    while (idl_interface.base) {
        const base = this.array.members[idl_interface.base];
        if (!base) {
            throw new Error(idl_interface.type + " " + idl_interface.base + " not found (inherited by " + idl_interface.name + ")");
        } else if (stack.indexOf(base) > -1) {
            stack.unshift(base);
            const dep_chain = stack.map(i => i.name).join(',');
            throw new IdlHarnessError(`${this.name} has a circular dependency: ${dep_chain}`);
        }
        idl_interface = base;
        stack.unshift(idl_interface);
    }
    return stack;
};

/**
 * Implementation of
 * https://webidl.spec.whatwg.org/#default-tojson-operation
 * for testing purposes.
 *
 * Collects the IDL types of the attributes that meet the criteria
 * for inclusion in the default toJSON operation for easy
 * comparison with actual value
 */

IdlInterface.prototype.default_to_json_operation = function() {
    const map = new Map()
    let isDefault = false;
    for (const I of this.get_reverse_inheritance_stack()) {
        if (I.has_default_to_json_regular_operation()) {
            isDefault = true;
            for (const m of I.members) {
                if (m.special !== "static" && m.type == "attribute" && I.array.is_json_type(m.idlType)) {
                    map.set(m.name, m.idlType);
                }
            }
        } else if (I.has_to_json_regular_operation()) {
            isDefault = false;
        }
    }
    return isDefault ? map : null;
};

IdlInterface.prototype.test = function()
{
    if (this.has_extended_attribute("LegacyNoInterfaceObject") || this.is_mixin())
    {
        // 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 the interface object is not exposed, only test that. Members can't be
    // tested either, but objects could still be tested in |test_object|.
    if (!this.exposed)
    {
        if (!this.untested)
        {
            subsetTestByKey(this.name, test, function() {
                assert_false(this.name in self, this.name + " interface should not exist");
            }.bind(this), this.name + " interface: existence and properties of interface object");
        }
        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.constructors = function()
{
    return this.members
        .filter(function(m) { return m.type == "constructor"; });
}

IdlInterface.prototype.test_self = function()
{
    subsetTestByKey(this.name, test, function()
    {
        if (!this.should_have_interface_object()) {
            return;
        }

        // 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 }."
        // TODO: Should we test here that the property is actually writable
        // etc., or trust getOwnPropertyDescriptor?
        this.assert_interface_object_exists();
        var desc = Object.getOwnPropertyDescriptor(this.get_interface_object_owner(), this.name);
        assert_false("get" in desc, "self's property " + format_value(this.name) + " should not have a getter");
        assert_false("set" in desc, "self's property " + format_value(this.name) + " should not have a setter");
        assert_true(desc.writable, "self's property " + format_value(this.name) + " should be writable");
        assert_false(desc.enumerable, "self's property " + format_value(this.name) + " should not be enumerable");
        assert_true(desc.configurable, "self's property " + format_value(this.name) + " should be configurable");

        if (this.is_callback()) {
            // "The internal [[Prototype]] property of an interface object for
            // a callback interface must be the Function.prototype object."
            assert_equals(Object.getPrototypeOf(this.get_interface_object()), Function.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."

        // "* 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(this.get_interface_object(), "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(this.get_interface_object());
        if (this.base) {
            // "* If the interface inherits from some other interface, the
            //    value of [[Prototype]] is the interface object for that other
            //    interface."
            var inherited_interface = this.array.members[this.base];
            if (!inherited_interface.has_extended_attribute("LegacyNoInterfaceObject")) {
                inherited_interface.assert_interface_object_exists();
                assert_equals(prototype, inherited_interface.get_interface_object(),
                              '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");
        }

        // Always test for [[Construct]]:
        // https://github.com/heycam/webidl/issues/698
        assert_true(isConstructor(this.get_interface_object()), "interface object must pass IsConstructor check");

        var interface_object = this.get_interface_object();
        assert_throws_js(globalOf(interface_object).TypeError, function() {
            interface_object();
        }, "interface object didn't throw TypeError when called as a function");

        if (!this.constructors().length) {
            assert_throws_js(globalOf(interface_object).TypeError, function() {
                new interface_object();
            }, "interface object didn't throw TypeError when called as a constructor");
        }
    }.bind(this), this.name + " interface: existence and properties of interface object");

    if (this.should_have_interface_object() && !this.is_callback()) {
        subsetTestByKey(this.name, test, function() {
            // This function tests WebIDL as of 2014-10-25.
            // https://webidl.spec.whatwg.org/#es-interface-call

            this.assert_interface_object_exists();

            // "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(this.get_interface_object(), "length");
            var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "length");
            assert_false("get" in desc, this.name + ".length should not have a getter");
            assert_false("set" in desc, this.name + ".length should not have a setter");
            assert_false(desc.writable, this.name + ".length should not be writable");
            assert_false(desc.enumerable, this.name + ".length should not be enumerable");
            assert_true(desc.configurable, this.name + ".length should be configurable");

            var constructors = this.constructors();
            var expected_length = minOverloadLength(constructors);
            assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length");
        }.bind(this), this.name + " interface object length");
    }

    if (this.should_have_interface_object()) {
        subsetTestByKey(this.name, test, function() {
            // This function tests WebIDL as of 2015-11-17.
            // https://webidl.spec.whatwg.org/#interface-object

            this.assert_interface_object_exists();

            // "All interface objects must have a property named “name” with
            // attributes { [[Writable]]: false, [[Enumerable]]: false,
            // [[Configurable]]: true } whose value is the identifier of the
            // corresponding interface."

            assert_own_property(this.get_interface_object(), "name");
            var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "name");
            assert_false("get" in desc, this.name + ".name should not have a getter");
            assert_false("set" in desc, this.name + ".name should not have a setter");
            assert_false(desc.writable, this.name + ".name should not be writable");
            assert_false(desc.enumerable, this.name + ".name should not be enumerable");
            assert_true(desc.configurable, this.name + ".name should be configurable");
            assert_equals(this.get_interface_object().name, this.name, "wrong value for " + this.name + ".name");
        }.bind(this), this.name + " interface object name");
    }


    if (this.has_extended_attribute("LegacyWindowAlias")) {
        subsetTestByKey(this.name, test, function()
        {
            var aliasAttrs = this.extAttrs.filter(function(o) { return o.name === "LegacyWindowAlias"; });
            if (aliasAttrs.length > 1) {
                throw new IdlHarnessError("Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name);
            }
            if (this.is_callback()) {
                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name);
            }
            if (!(this.exposureSet === "*" || this.exposureSet.has("Window"))) {
                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window");
            }
            // TODO: when testing of [LegacyNoInterfaceObject] interfaces is supported,
            // check that it's not specified together with LegacyWindowAlias.

            // TODO: maybe check that [LegacyWindowAlias] is not specified on a partial interface.

            var rhs = aliasAttrs[0].rhs;
            if (!rhs) {
                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier");
            }
            var aliases;
            if (rhs.type === "identifier-list") {
                aliases = rhs.value.map(id => id.value);
            } else { // rhs.type === identifier
                aliases = [ rhs.value ];
            }

            // OK now actually check the aliases...
            var alias;
            if (exposed_in(exposure_set(thisthis.exposureSet)) && 'document' in self) {
                for (alias of aliases) {
                    assert_true(alias in self, alias + " should exist");
                    assert_equals(self[alias], this.get_interface_object(), "self." + alias + " should be the same value as self." + this.get_qualified_name());
                    var desc = Object.getOwnPropertyDescriptor(self, alias);
                    assert_equals(desc.value, this.get_interface_object(), "wrong value in " + alias + " property descriptor");
                    assert_true(desc.writable, alias + " should be writable");
                    assert_false(desc.enumerable, alias + " should not be enumerable");
                    assert_true(desc.configurable, alias + " should be configurable");
                    assert_false('get' in desc, alias + " should not have a getter");
                    assert_false('set' in desc, alias + " should not have a setter");
                }
            } else {
                for (alias of aliases) {
                    assert_false(alias in self, alias + " should not exist");
                }
            }

        }.bind(this), this.name + " interface: legacy window alias");
    }

    if (this.has_extended_attribute("LegacyFactoryFunction")) {
        var constructors = this.extAttrs
            .filter(function(attr) { return attr.name == "LegacyFactoryFunction"; });
        if (constructors.length !== 1) {
            throw new IdlHarnessError("Internal error: missing support for multiple LegacyFactoryFunction extended attributes");
        }
        var constructor = constructors[0];
        var min_length = minOverloadLength([constructor]);

        subsetTestByKey(this.name, test, function()
        {
            // This function tests WebIDL as of 2019-01-14.

            // "for every [LegacyFactoryFunction] extended attribute on an exposed
            // interface, a corresponding property must exist on the ECMAScript
            // global object. The name of the property is the
            // [LegacyFactoryFunction]'s identifier, and its value is an object
            // called a named constructor, ... . The property has the attributes
            // { [[Writable]]: true, [[Enumerable]]: false,
            // [[Configurable]]: true }."
            var name = constructor.rhs.value;
            assert_own_property(self, name);
            var desc = Object.getOwnPropertyDescriptor(self, name);
            assert_equals(desc.value, self[name], "wrong value in " + name + " property descriptor");
            assert_true(desc.writable, name + " should be writable");
            assert_false(desc.enumerable, name + " should not be enumerable");
            assert_true(desc.configurable, name + " should be configurable");
            assert_false("get" in desc, name + " should not have a getter");
            assert_false("set" in desc, name + " should not have a setter");
        }.bind(this), this.name + " interface: named constructor");

        subsetTestByKey(this.name, test, function()
        {
            // This function tests WebIDL as of 2019-01-14.

            // "2. Let F be ! CreateBuiltinFunction(realm, steps,
            //     realm.[[Intrinsics]].[[%FunctionPrototype%]])."
            var name = constructor.rhs.value;
            var value = self[name];
            assert_equals(typeof value, "function""type of value in " + name + " property descriptor");
            assert_not_equals(value, this.get_interface_object(), "wrong value in " + name + " property descriptor");
            assert_equals(Object.getPrototypeOf(value), Function.prototype, "wrong value for " + name + "'s prototype");
        }.bind(this), this.name + " interface: named constructor object");

        subsetTestByKey(this.name, test, function()
        {
            // This function tests WebIDL as of 2019-01-14.

            // "7. Let proto be the interface prototype object of interface I
            //     in realm.
            // "8. Perform ! DefinePropertyOrThrow(F, "prototype",
            //     PropertyDescriptor{
            //         [[Value]]: proto, [[Writable]]: false,
            //         [[Enumerable]]: false, [[Configurable]]: false
            //     })."
            var name = constructor.rhs.value;
            var expected = this.get_interface_object().prototype;
            var desc = Object.getOwnPropertyDescriptor(self[name], "prototype");
            assert_equals(desc.value, expected, "wrong value for " + name + ".prototype");
            assert_false(desc.writable, "prototype should not be writable");
            assert_false(desc.enumerable, "prototype should not be enumerable");
            assert_false(desc.configurable, "prototype should not be configurable");
            assert_false("get" in desc, "prototype should not have a getter");
            assert_false("set" in desc, "prototype should not have a setter");
        }.bind(this), this.name + " interface: named constructor prototype property");

        subsetTestByKey(this.name, test, function()
        {
            // This function tests WebIDL as of 2019-01-14.

            // "3. Perform ! SetFunctionName(F, id)."
            var name = constructor.rhs.value;
            var desc = Object.getOwnPropertyDescriptor(self[name], "name");
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=98 G=95

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.