@@ -20,6 +20,7 @@ const { |
20 | 20 | NumberIsNaN, |
21 | 21 | NumberMAX_SAFE_INTEGER, |
22 | 22 | NumberMIN_SAFE_INTEGER, |
| 23 | + ObjectPrototypeHasOwnProperty, |
23 | 24 | ObjectPrototypeIsPrototypeOf, |
24 | 25 | SafeArrayIterator, |
25 | 26 | SafeSet, |
@@ -699,16 +700,47 @@ function createDictionaryConverter( |
699 | 700 | const dictionaries = ArrayIsArray(members[0]) ? members : [members]; |
700 | 701 | const sortedDictionaries = []; |
701 | 702 |
|
| 703 | + function ownMember(member, key) { |
| 704 | + return ObjectPrototypeHasOwnProperty(member, key) ? member[key] : undefined; |
| 705 | + } |
| 706 | + |
| 707 | + // Dictionaries with no defaults and no required members skip steps |
| 708 | + // 4.1.5/4.1.6 entirely, keeping the absent-member path free. |
| 709 | + let anyMissingMemberHandling = false; |
| 710 | + |
702 | 711 | // Web IDL dictionary conversion steps 3-4 process inherited dictionaries |
703 | 712 | // from least-derived to most-derived and sort only within each dictionary. |
704 | 713 | // Callers with inheritance pass one member array per dictionary level. |
705 | 714 | for (let i = 0; i < dictionaries.length; i++) { |
706 | | - ArrayPrototypePush( |
707 | | - sortedDictionaries, |
708 | | - ArrayPrototypeToSorted(dictionaries[i], compareMembers), |
| 715 | + const sortedMembers = ArrayPrototypeToSorted( |
| 716 | + dictionaries[i], |
| 717 | + compareMembers, |
709 | 718 | ); |
| 719 | + // Definition sites spell out only the members they need, so reading the |
| 720 | + // optional ones below would resolve through %Object.prototype%. |
| 721 | + // Re-materialize each descriptor once with every key present, copied from |
| 722 | + // own properties only. The ordinary prototype is deliberate: nothing |
| 723 | + // consults it now, and detaching it measurably slows these reads down. |
| 724 | + for (let j = 0; j < sortedMembers.length; j++) { |
| 725 | + const member = sortedMembers[j]; |
| 726 | + const defaultValue = ownMember(member, 'defaultValue'); |
| 727 | + const required = ownMember(member, 'required'); |
| 728 | + if (typeof defaultValue === 'function' || required) { |
| 729 | + anyMissingMemberHandling = true; |
| 730 | + } |
| 731 | + sortedMembers[j] = { |
| 732 | + key: ownMember(member, 'key'), |
| 733 | + converter: ownMember(member, 'converter'), |
| 734 | + defaultValue, |
| 735 | + required, |
| 736 | + validator: ownMember(member, 'validator'), |
| 737 | + }; |
| 738 | + } |
| 739 | + ArrayPrototypePush(sortedDictionaries, sortedMembers); |
710 | 740 | } |
711 | 741 |
|
| 742 | + const hasMissingMemberHandling = anyMissingMemberHandling; |
| 743 | + |
712 | 744 | return function(jsDict, options = kEmptyObject) { |
713 | 745 | // Step 1: reject non-object, non-null, non-undefined values. |
714 | 746 | if (jsDict != null && type(jsDict) !== 'Object') { |
@@ -747,14 +779,16 @@ function createDictionaryConverter( |
747 | 779 | member.validator?.(idlMemberValue, jsDict); |
748 | 780 | // Step 4.1.4.2: set idlDict[key] to the IDL value. |
749 | 781 | idlDict[key] = idlMemberValue; |
750 | | - } else if (typeof member.defaultValue === 'function') { |
751 | | - // Step 4.1.5: store the member default value. |
752 | | - idlDict[key] = member.defaultValue(); |
753 | | - } else if (member.required) { |
754 | | - // Step 4.1.6: required missing members throw. |
755 | | - throw makeException( |
756 | | - missingDictionaryMemberMessage(dictionaryName, key), |
757 | | - makeOptions(options, options.context, 'ERR_MISSING_OPTION')); |
| 782 | + } else if (hasMissingMemberHandling) { |
| 783 | + if (typeof member.defaultValue === 'function') { |
| 784 | + // Step 4.1.5: store the member default value. |
| 785 | + idlDict[key] = member.defaultValue(); |
| 786 | + } else if (member.required) { |
| 787 | + // Step 4.1.6: required missing members throw. |
| 788 | + throw makeException( |
| 789 | + missingDictionaryMemberMessage(dictionaryName, key), |
| 790 | + makeOptions(options, options.context, 'ERR_MISSING_OPTION')); |
| 791 | + } |
758 | 792 | } |
759 | 793 | } |
760 | 794 | } |
|