Skip to content

Commit a37d959

Browse files
committed
Implement the named properties object
1 parent fd2c9a0 commit a37d959

File tree

2 files changed

+512
-9
lines changed

2 files changed

+512
-9
lines changed

lib/constructs/interface.js

Lines changed: 187 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ class Interface {
236236
throw new Error(msg);
237237
}
238238
}
239+
240+
if (utils.isGlobal(this.idl) && this.namedGetter) {
241+
this.factory = true;
242+
}
239243
}
240244

241245
get supportsIndexedProperties() {
@@ -347,15 +351,19 @@ class Interface {
347351
`;
348352
}
349353

354+
if (utils.isGlobal(this.idl) && this.supportsNamedProperties) {
355+
this.generateNamedPropertiesObject();
356+
this.str += `Object.setPrototypeOf(${this.name}.prototype, namedPropertiesObject);`;
357+
} else if (this.idl.inheritance) {
358+
this.str += `Object.setPrototypeOf(${this.name}.prototype, ${this.idl.inheritance}.interface.prototype);`;
359+
} else if (utils.getExtAttr(this.idl.extAttrs, "LegacyArrayClass")) {
360+
this.str += `Object.setPrototypeOf(${this.name}.prototype, Array.prototype);`;
361+
}
362+
350363
if (this.idl.inheritance) {
351364
this.str += `
352-
Object.setPrototypeOf(${this.name}.prototype, ${this.idl.inheritance}.interface.prototype);
353365
Object.setPrototypeOf(${this.name}, ${this.idl.inheritance}.interface);
354366
`;
355-
} else if (utils.getExtAttr(this.idl.extAttrs, "LegacyArrayClass")) {
356-
this.str += `
357-
Object.setPrototypeOf(${this.name}.prototype, Array.prototype);
358-
`;
359367
}
360368

361369
this.str += `
@@ -544,6 +552,180 @@ class Interface {
544552
return conditions.join(" && ");
545553
}
546554

555+
generateNamedPropertiesObject() {
556+
const proto = (() => {
557+
if (this.idl.inheritance) {
558+
return `${this.idl.inheritance}.interface.prototype`;
559+
} else if (utils.getExtAttr(this.idl.extAttrs, "LegacyArrayClass")) {
560+
return "Array.prototype";
561+
}
562+
return "Object.prototype";
563+
})();
564+
565+
this.str += `
566+
const namedPropertiesObject = new Proxy(Object.create(${proto}, {
567+
[Symbol.toStringTag]: {
568+
value: "${this.name}Properties",
569+
writable: false,
570+
enumerable: false,
571+
configurable: true
572+
}
573+
}), {
574+
`;
575+
576+
// [[SetPrototypeOf]]
577+
this.str += `
578+
setPrototypeOf() {
579+
throw new TypeError("Immutable prototype object '#<${this.name}Properties>' cannot have their prototype set");
580+
},
581+
`;
582+
583+
// [[PreventExtensions]]
584+
this.str += `
585+
preventExtensions() {
586+
return false;
587+
},
588+
`;
589+
590+
// [[GetOwnProperty]]
591+
this.str += `
592+
getOwnPropertyDescriptor(target, P) {
593+
if (typeof P === "symbol") {
594+
return Reflect.getOwnPropertyDescriptor(target, P);
595+
}
596+
const object = defaultPrivateData.globalObject;
597+
`;
598+
599+
const func = this.namedGetter.name !== null ? `.${this.namedGetter.name}` : "[utils.namedGet]";
600+
const enumerable = !utils.getExtAttr(this.idl.extAttrs, "LegacyUnenumerableNamedProperties");
601+
let preamble = "";
602+
const conditions = [];
603+
if (utils.getExtAttr(this.namedGetter.extAttrs, "WebIDL2JSValueAsUnsupported")) {
604+
this.str += `
605+
const namedValue = object[impl]${func}(P);
606+
`;
607+
conditions.push(this._supportsPropertyName("object", "index", "namedValue"));
608+
conditions.push(this._namedPropertyVisible("P", "object", true));
609+
} else {
610+
preamble = `
611+
const namedValue = object[impl]${func}(P);
612+
`;
613+
conditions.push(this._namedPropertyVisible("P", "object", false));
614+
}
615+
616+
this.str += `
617+
if (${conditions.join(" && ")}) {
618+
${preamble}
619+
return {
620+
writable: true,
621+
enumerable: ${enumerable},
622+
configurable: true,
623+
value: utils.tryWrapperForImpl(namedValue)
624+
};
625+
}
626+
return Reflect.getOwnPropertyDescriptor(target, P);
627+
},
628+
`;
629+
630+
// [[DefineOwnProperty]]
631+
this.str += `
632+
defineProperty() {
633+
return false;
634+
},
635+
`;
636+
637+
// [[HasProperty]]
638+
this.str += `
639+
has(target, P) {
640+
if (typeof P === "symbol") {
641+
return Reflect.has(target, P);
642+
}
643+
const desc = this.getOwnPropertyDescriptor(target, P);
644+
if (desc !== undefined) {
645+
return true;
646+
}
647+
const parent = Object.getPrototypeOf(target);
648+
if (parent !== null) {
649+
return Reflect.has(parent, P);
650+
}
651+
return false;
652+
},
653+
`;
654+
655+
// [[Get]]
656+
this.str += `
657+
get(target, P, receiver) {
658+
if (typeof P === "symbol") {
659+
return Reflect.get(target, P, receiver);
660+
}
661+
const desc = this.getOwnPropertyDescriptor(target, P);
662+
if (desc === undefined) {
663+
const parent = Object.getPrototypeOf(target);
664+
if (parent === null) {
665+
return undefined;
666+
}
667+
return Reflect.get(target, P, receiver);
668+
}
669+
if (!desc.get && !desc.set) {
670+
return desc.value;
671+
}
672+
const getter = desc.get;
673+
if (getter === undefined) {
674+
return undefined;
675+
}
676+
return Reflect.apply(getter, receiver, []);
677+
},
678+
`;
679+
680+
// [[Set]]
681+
this.str += `
682+
set(target, P, V, receiver) {
683+
if (typeof P === "symbol") {
684+
return Reflect.set(target, P, V, receiver);
685+
}
686+
const ownDesc = this.getOwnPropertyDescriptor(P);
687+
if (ownDesc === undefined) {
688+
const parent = Reflect.getPrototypeOf(target);
689+
// parent is never null.
690+
return Reflect.set(parent, P, V, receiver);
691+
}
692+
// ownDesc.writable is always true.
693+
if (!utils.isObject(receiver)) {
694+
return false;
695+
}
696+
// If receiver is this Proxy object, the following will always return false. Problem is, receiver may not be
697+
// this Proxy object.
698+
const existingDesc = Reflect.getOwnPropertyDescriptor(receiver, P);
699+
let valueDesc;
700+
if (existingDesc !== undefined) {
701+
if (existingDesc.get || existingDesc.set) {
702+
return false;
703+
}
704+
if (!existingDesc.writable) {
705+
return false;
706+
}
707+
valueDesc = { value: V };
708+
} else {
709+
valueDesc = { writable: true, enumerable: true, configurable: true, value: V };
710+
}
711+
return Reflect.defineProperty(receiver, P, valueDesc);
712+
},
713+
`;
714+
715+
// [[Delete]]
716+
this.str += `
717+
deleteProperty() {
718+
return false;
719+
},
720+
`;
721+
722+
// [[OwnPropertyKeys]] not overriden
723+
724+
this.str += `
725+
});
726+
`;
727+
}
728+
547729
generateLegacyProxy() {
548730
const hasIndexedSetter = this.indexedSetter !== null;
549731
const hasNamedSetter = this.namedSetter !== null;

0 commit comments

Comments
 (0)