diff --git a/lib/internal/webidl.js b/lib/internal/webidl.js index 9e4b20435bf831..f832afced30740 100644 --- a/lib/internal/webidl.js +++ b/lib/internal/webidl.js @@ -12,6 +12,7 @@ const { NumberMAX_SAFE_INTEGER, NumberMIN_SAFE_INTEGER, ObjectAssign, + ObjectPrototypeIsPrototypeOf, SafeSet, String, SymbolIterator, @@ -20,6 +21,7 @@ const { const { codes: { + ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, }, } = require('internal/errors'); @@ -275,7 +277,7 @@ function createSequenceConverter(converter) { const val = converter(res.value, { __proto__: null, ...opts, - context: `${opts.context}, index ${array.length}`, + context: `${opts.context}[${array.length}]`, }); ArrayPrototypePush(array, val); }; @@ -283,12 +285,26 @@ function createSequenceConverter(converter) { }; } +// https://webidl.spec.whatwg.org/#js-interface +function createInterfaceConverter(name, prototype) { + return (V, opts = kEmptyObject) => { + // 1. If V implements I, then return the IDL interface type value that + // represents a reference to that platform object. + if (ObjectPrototypeIsPrototypeOf(prototype, V)) return V; + // 2. Throw a TypeError. + throw new ERR_INVALID_ARG_TYPE( + typeof opts.context === 'string' ? opts.context : '', name, V, + ); + }; +} + module.exports = { type, converters, convertToInt, createEnumConverter, + createInterfaceConverter, createSequenceConverter, evenRound, makeException, diff --git a/test/parallel/test-internal-webidl.js b/test/parallel/test-internal-webidl.js new file mode 100644 index 00000000000000..c4073541072ca8 --- /dev/null +++ b/test/parallel/test-internal-webidl.js @@ -0,0 +1,35 @@ +// Flags: --expose-internals +'use strict'; + +require('../common'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); +const { createInterfaceConverter } = require('internal/webidl'); + +describe('internal/webidl', () => { + class Test { t() {} } + class SubTest extends Test {} + const testConverter = createInterfaceConverter('Test', Test.prototype); + + it('converts objects that implement the required interface', () => { + const subTest = new SubTest(); + const test = new Test(); + assert.strictEqual(subTest, testConverter(subTest)); + assert.strictEqual(test, testConverter(test)); + }); + + it('throws TypeError when converting objects that do not implement the required interface', () => { + const expectedError = { code: 'ERR_INVALID_ARG_TYPE' }; + [ + { t: () => {} }, + null, + undefined, + {}, + [], + 1, + '123', + ].forEach((c) => { + assert.throws(() => { testConverter(c); }, expectedError); + }); + }); +});