Skip to content

Commit 24f9449

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

File tree

2 files changed

+509
-10
lines changed

2 files changed

+509
-10
lines changed

lib/constructs/interface.js

+184-6
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class Interface {
2222
this.ctx = ctx;
2323
this.idl = idl;
2424
this.name = idl.name;
25-
this.factory = Boolean(utils.getExtAttr(this.idl.extAttrs, "WebIDL2JSFactory"));
25+
this.factory = utils.isGlobal(this.idl) || Boolean(utils.getExtAttr(this.idl.extAttrs, "WebIDL2JSFactory"));
2626
for (const member of this.idl.members) {
2727
member.definingInterface = this.name;
2828
}
@@ -347,15 +347,19 @@ class Interface {
347347
`;
348348
}
349349

350+
if (utils.isGlobal(this.idl) && this.supportsNamedProperties) {
351+
this.generateNamedPropertiesObject();
352+
this.str += `Object.setPrototypeOf(${this.name}.prototype, namedPropertiesObject);`;
353+
} else if (this.idl.inheritance) {
354+
this.str += `Object.setPrototypeOf(${this.name}.prototype, ${this.idl.inheritance}.interface.prototype);`;
355+
} else if (utils.getExtAttr(this.idl.extAttrs, "LegacyArrayClass")) {
356+
this.str += `Object.setPrototypeOf(${this.name}.prototype, Array.prototype);`;
357+
}
358+
350359
if (this.idl.inheritance) {
351360
this.str += `
352-
Object.setPrototypeOf(${this.name}.prototype, ${this.idl.inheritance}.interface.prototype);
353361
Object.setPrototypeOf(${this.name}, ${this.idl.inheritance}.interface);
354362
`;
355-
} else if (utils.getExtAttr(this.idl.extAttrs, "LegacyArrayClass")) {
356-
this.str += `
357-
Object.setPrototypeOf(${this.name}.prototype, Array.prototype);
358-
`;
359363
}
360364

361365
this.str += `
@@ -544,6 +548,180 @@ class Interface {
544548
return conditions.join(" && ");
545549
}
546550

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

0 commit comments

Comments
 (0)