-
-
Notifications
You must be signed in to change notification settings - Fork 29
/
operation.js
118 lines (99 loc) · 3.54 KB
/
operation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
"use strict";
const conversions = require("webidl-conversions");
const utils = require("../utils");
const Overloads = require("../overloads");
const Parameters = require("../parameters");
class Operation {
constructor(ctx, I, idl) {
this.ctx = ctx;
this.interface = I;
this.idls = [idl];
this.name = idl.name;
this.static = idl.special === "static";
}
isOnInstance() {
const firstOverloadOnInstance = utils.isOnInstance(this.idls[0], this.interface.idl);
for (const overload of this.idls.slice(1)) {
if (utils.isOnInstance(overload, this.interface.idl) !== firstOverloadOnInstance) {
throw new Error(`[Unforgeable] is not applied uniformly to operation "${this.name}" on ${this.interface.name}`);
}
}
return firstOverloadOnInstance;
}
fixUpArgsExtAttrs() {
for (const idl of this.idls) {
for (const arg of idl.arguments) {
if (arg.extAttrs.length) {
// Overwrite rather than push to workaround old webidl2 array-sharing bug
// It's safe as the two cannot coexist.
arg.idlType.extAttrs = [...arg.extAttrs];
}
}
}
}
generate() {
const requires = new utils.RequiresMap(this.ctx);
this.fixUpArgsExtAttrs();
let str = "";
if (!this.name) {
throw new Error(`Internal error: this operation does not have a name (in interface ${this.interface.name})`);
}
const onInstance = this.isOnInstance();
const type = this.static ? "static operation" : "regular operation";
const overloads = Overloads.getEffectiveOverloads(type, this.name, 0, this.interface);
let minOp = overloads[0];
for (let i = 1; i < overloads.length; ++i) {
if (overloads[i].nameList.length < minOp.nameList.length) {
minOp = overloads[i];
}
}
const argNames = minOp.nameList;
if (!this.static) {
str += `
if (!this || !module.exports.is(this)) {
throw new TypeError("Illegal invocation");
}
`;
}
const callOn = this.static ? "Impl.implementation" : "this[impl]";
// In case of stringifiers, use the named implementation function rather than hardcoded "toString".
// All overloads will have the same name, so pick the first one.
const implFunc = this.idls[0].name || this.name;
const parameterConversions = Parameters.generateOverloadConversions(
this.ctx, type, this.name, this.interface, `Failed to execute '${this.name}' on '${this.interface.name}': `);
const argsSpread = parameterConversions.hasArgs ? "...args" : "";
requires.merge(parameterConversions.requires);
str += parameterConversions.body;
let invocation;
if (overloads.every(overload => conversions[overload.operation.idlType.idlType])) {
invocation = `
return ${callOn}.${implFunc}(${argsSpread});
`;
} else {
invocation = `
return utils.tryWrapperForImpl(${callOn}.${implFunc}(${argsSpread}));
`;
}
if (utils.hasCEReactions(this.idls[0])) {
invocation = this.ctx.invokeProcessCEReactions(invocation, {
requires
});
}
str += invocation;
if (this.static) {
this.interface.addStaticMethod(this.name, argNames, str);
} else {
const forgeable = !utils.getExtAttr(this.idls[0].extAttrs, "Unforgeable");
this.interface.addMethod(
onInstance ? "instance" : "prototype",
this.name,
argNames,
str,
"regular",
{ configurable: forgeable, writable: forgeable }
);
}
return { requires };
}
}
module.exports = Operation;