@@ -385,6 +385,10 @@ class Interface {
385385 return ! this . isGlobal && ( this . supportsIndexedProperties || this . supportsNamedProperties ) ;
386386 }
387387
388+ get needsNamedPropertiesObject ( ) {
389+ return this . isGlobal && this . supportsNamedProperties ;
390+ }
391+
388392 includes ( source ) {
389393 const mixin = this . ctx . interfaceMixins . get ( source ) ;
390394 if ( ! mixin ) {
@@ -1124,6 +1128,170 @@ class Interface {
11241128 ` ;
11251129 }
11261130
1131+ // https://heycam.github.io/webidl/#named-properties-object
1132+ generateNamedPropertiesObject ( ) {
1133+ const { idl, name, namedGetter } = this ;
1134+ const overrideBuiltins = Boolean ( utils . getExtAttr ( idl . extAttrs , "OverrideBuiltins" ) ) ;
1135+
1136+ function supportsPropertyName ( O , P , namedValue ) {
1137+ let unsupportedValue = utils . getExtAttr ( namedGetter . extAttrs , "WebIDL2JSValueAsUnsupported" ) ;
1138+ if ( unsupportedValue ) {
1139+ unsupportedValue = unsupportedValue . rhs . value ;
1140+ }
1141+ if ( unsupportedValue ) {
1142+ const func = namedGetter . name ? `.${ namedGetter . name } ` : "[utils.namedGet]" ;
1143+ const value = namedValue || `${ O } [impl]${ func } (${ P } )` ;
1144+ return `${ value } !== ${ unsupportedValue } ` ;
1145+ }
1146+ return `${ O } [impl][utils.supportsPropertyName](${ P } )` ;
1147+ }
1148+
1149+ // "named property visibility algorithm"
1150+ // If `supports` is true then skip the supportsPropertyName check.
1151+ function namedPropertyVisible ( P , O , supports = false ) {
1152+ const conditions = [ ] ;
1153+ if ( ! supports ) {
1154+ conditions . push ( supportsPropertyName ( O , P ) ) ;
1155+ }
1156+ if ( overrideBuiltins ) {
1157+ conditions . push ( `!utils.hasOwn(${ O } , ${ P } )` ) ;
1158+ } else {
1159+ // TODO: create a named properties object.
1160+ conditions . push ( `!(${ P } in ${ O } )` ) ;
1161+ }
1162+ return conditions . join ( " && " ) ;
1163+ }
1164+
1165+ this . str += `
1166+ const NamedPropertiesObject = new Proxy(
1167+ Object.create(
1168+ globalObject.${ idl . inheritance ? idl . inheritance : "Object" } .prototype,
1169+ { [Symbol.toStringTag]: { value: "${ name } Properties", configurable: true } }
1170+ ),
1171+ {
1172+ ` ;
1173+
1174+ // [[Get]] (necessary because of proxy semantics)
1175+ this . str += `
1176+ get(target, P, receiver) {
1177+ if (typeof P === "symbol") {
1178+ return Reflect.get(target, P, receiver);
1179+ }
1180+ const desc = this.getOwnPropertyDescriptor(target, P);
1181+ if (desc === undefined) {
1182+ const parent = Object.getPrototypeOf(target);
1183+ if (parent === null) {
1184+ return undefined;
1185+ }
1186+ return Reflect.get(parent, P, receiver);
1187+ }
1188+ if (!desc.get && !desc.set) {
1189+ return desc.value;
1190+ }
1191+ const getter = desc.get;
1192+ if (getter === undefined) {
1193+ return undefined;
1194+ }
1195+ return Reflect.apply(getter, receiver, []);
1196+ },
1197+ ` ;
1198+
1199+ // [[HasProperty]] (necessary because of proxy semantics)
1200+ this . str += `
1201+ has(target, P) {
1202+ if (typeof P === "symbol") {
1203+ return Reflect.has(target, P);
1204+ }
1205+ const desc = this.getOwnPropertyDescriptor(target, P);
1206+ if (desc !== undefined) {
1207+ return true;
1208+ }
1209+ const parent = Object.getPrototypeOf(target);
1210+ if (parent !== null) {
1211+ return Reflect.has(parent, P);
1212+ }
1213+ return false;
1214+ },
1215+ ` ;
1216+
1217+ // [[GetOwnProperty]]
1218+ this . str += `
1219+ getOwnPropertyDescriptor(target, P) {
1220+ const object = globalObject;
1221+ if (typeof P === "symbol") {
1222+ return Reflect.getOwnPropertyDescriptor(target, P);
1223+ }
1224+ ` ;
1225+
1226+ const func = namedGetter . name ? `.${ namedGetter . name } ` : "[utils.namedGet]" ;
1227+ const enumerable = ! utils . getExtAttr ( idl . extAttrs , "LegacyUnenumerableNamedProperties" ) ;
1228+ let preamble = "" ;
1229+ const conditions = [ ] ;
1230+ if ( utils . getExtAttr ( namedGetter . extAttrs , "WebIDL2JSValueAsUnsupported" ) ) {
1231+ this . str += `
1232+ const namedValue = object[impl]${ func } (P);
1233+ ` ;
1234+ conditions . push ( supportsPropertyName ( "object" , "index" , "namedValue" ) ) ;
1235+ conditions . push ( namedPropertyVisible ( "P" , "object" , true ) ) ;
1236+ } else {
1237+ preamble = `
1238+ const namedValue = object[impl]${ func } (P);
1239+ ` ;
1240+ conditions . push ( namedPropertyVisible ( "P" , "object" ) ) ;
1241+ }
1242+ this . str += `
1243+ if (${ conditions . join ( " && " ) } ) {
1244+ ${ preamble }
1245+ return {
1246+ writable: true,
1247+ enumerable: ${ enumerable } ,
1248+ configurable: true,
1249+ value: utils.tryWrapperForImpl(namedValue)
1250+ };
1251+ }
1252+ return Reflect.getOwnPropertyDescriptor(target, P);
1253+ },
1254+ ` ;
1255+
1256+ // [[DefineOwnProperty]]
1257+ this . str += `
1258+ defineProperty() {
1259+ return false;
1260+ },
1261+ ` ;
1262+
1263+ // [[Delete]]
1264+ this . str += `
1265+ deleteProperty() {
1266+ return false;
1267+ },
1268+ ` ;
1269+
1270+ // [[SetPrototypeOf]]
1271+ this . str += `
1272+ setPrototypeOf(O, V) {
1273+ // 1. Return ? SetImmutablePrototype(O, V).
1274+ return Reflect.getPrototypeOf(O) === V;
1275+ },
1276+ ` ;
1277+
1278+ // [[PreventExtensions]]
1279+ this . str += `
1280+ preventExtensions() {
1281+ return false;
1282+ }
1283+ ` ;
1284+
1285+ this . str += `
1286+ }
1287+ );
1288+ ` ;
1289+
1290+ this . str += `
1291+ Object.setPrototypeOf(${ name } .prototype, NamedPropertiesObject);
1292+ ` ;
1293+ }
1294+
11271295 generateIface ( ) {
11281296 this . str += `
11291297 exports.create = function create(globalObject, constructorArgs, privateData) {
@@ -1470,6 +1638,10 @@ class Interface {
14701638
14711639 this . generateOffInstanceAfterClass ( ) ;
14721640
1641+ if ( this . needsNamedPropertiesObject ) {
1642+ this . generateNamedPropertiesObject ( ) ;
1643+ }
1644+
14731645 this . str += `
14741646 if (globalObject[ctorRegistry] === undefined) {
14751647 globalObject[ctorRegistry] = Object.create(null);
0 commit comments