Skip to content

Commit a94deca

Browse files
committed
perf_hooks: hrtime idlharness
1. Enforce receiver checks on IDL interface `Performance.now`. 2. Avoid prototype manipulation on constructing `Performance` with `ReflectConstruct`. 3. `defineReplaceableAttribute` should create IDL getter/setter.
1 parent 82cfb50 commit a94deca

File tree

10 files changed

+187
-151
lines changed

10 files changed

+187
-151
lines changed

lib/internal/bootstrap/browser.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ exposeInterface(globalThis, 'Blob', buffer.Blob);
7575
// https://www.w3.org/TR/hr-time-2/#the-performance-attribute
7676
const perf_hooks = require('perf_hooks');
7777
exposeInterface(globalThis, 'Performance', perf_hooks.Performance);
78-
defineReplacableAttribute(globalThis, 'performance',
79-
perf_hooks.performance);
78+
defineReplaceableAttribute(globalThis, 'performance',
79+
perf_hooks.performance);
8080

8181
function createGlobalConsole() {
8282
const consoleFromNode =
@@ -114,14 +114,33 @@ function exposeGetterAndSetter(target, name, getter, setter = undefined) {
114114
});
115115
}
116116

117-
// https://heycam.github.io/webidl/#Replaceable
118-
function defineReplacableAttribute(target, name, value) {
117+
// https://webidl.spec.whatwg.org/#Replaceable
118+
function defineReplaceableAttribute(target, name, value) {
119+
let slot = value;
120+
121+
// https://webidl.spec.whatwg.org/#dfn-attribute-getter
122+
function get() {
123+
return slot;
124+
}
125+
ObjectDefineProperty(get, 'name', {
126+
__proto__: null,
127+
value: `get ${name}`,
128+
});
129+
130+
function set(value) {
131+
slot = value;
132+
}
133+
ObjectDefineProperty(set, 'name', {
134+
__proto__: null,
135+
value: `set ${name}`,
136+
});
137+
119138
ObjectDefineProperty(target, name, {
120139
__proto__: null,
121-
writable: true,
122140
enumerable: true,
123141
configurable: true,
124-
value,
142+
get,
143+
set,
125144
});
126145
}
127146

lib/internal/perf/performance.js

Lines changed: 101 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
22

33
const {
4-
ObjectDefineProperty,
54
ObjectDefineProperties,
6-
ObjectSetPrototypeOf,
5+
ReflectConstruct,
6+
SymbolToStringTag,
77
} = primordials;
88

99
const {
@@ -17,9 +17,13 @@ const {
1717
EventTarget,
1818
Event,
1919
kTrustEvent,
20+
initEventTarget,
2021
} = require('internal/event_target');
2122

22-
const { now } = require('internal/perf/utils');
23+
const {
24+
now,
25+
kPerformanceBrand,
26+
} = require('internal/perf/utils');
2327

2428
const { markResourceTiming } = require('internal/perf/resource_timing');
2529

@@ -38,8 +42,9 @@ const {
3842
const { eventLoopUtilization } = require('internal/perf/event_loop_utilization');
3943
const nodeTiming = require('internal/perf/nodetiming');
4044
const timerify = require('internal/perf/timerify');
41-
const { customInspectSymbol: kInspect } = require('internal/util');
45+
const { customInspectSymbol: kInspect, kEnumerableProperty, kEmptyObject } = require('internal/util');
4246
const { inspect } = require('util');
47+
const { validateInternalField } = require('internal/validators');
4348

4449
const {
4550
getTimeOriginTimestamp
@@ -63,177 +68,147 @@ class Performance extends EventTarget {
6368
timeOrigin: this.timeOrigin,
6469
}, opts)}`;
6570
}
66-
}
6771

68-
function toJSON() {
69-
return {
70-
nodeTiming: this.nodeTiming,
71-
timeOrigin: this.timeOrigin,
72-
eventLoopUtilization: this.eventLoopUtilization()
73-
};
74-
}
72+
clearMarks(name) {
73+
if (name !== undefined) {
74+
name = `${name}`;
75+
}
76+
clearMarkTimings(name);
77+
clearEntriesFromBuffer('mark', name);
78+
}
7579

76-
function clearMarks(name) {
77-
if (name !== undefined) {
78-
name = `${name}`;
80+
clearMeasures(name) {
81+
if (name !== undefined) {
82+
name = `${name}`;
83+
}
84+
clearEntriesFromBuffer('measure', name);
7985
}
80-
clearMarkTimings(name);
81-
clearEntriesFromBuffer('mark', name);
82-
}
8386

84-
function clearMeasures(name) {
85-
if (name !== undefined) {
86-
name = `${name}`;
87+
clearResourceTimings(name = undefined) {
88+
if (name !== undefined) {
89+
name = `${name}`;
90+
}
91+
clearEntriesFromBuffer('resource', name);
92+
}
93+
94+
getEntries() {
95+
return filterBufferMapByNameAndType();
8796
}
88-
clearEntriesFromBuffer('measure', name);
89-
}
9097

91-
function clearResourceTimings(name) {
92-
if (name !== undefined) {
98+
getEntriesByName(name) {
99+
if (arguments.length === 0) {
100+
throw new ERR_MISSING_ARGS('name');
101+
}
93102
name = `${name}`;
103+
return filterBufferMapByNameAndType(name, undefined);
94104
}
95-
clearEntriesFromBuffer('resource', name);
96-
}
97105

98-
function getEntries() {
99-
return filterBufferMapByNameAndType();
100-
}
106+
getEntriesByType(type) {
107+
if (arguments.length === 0) {
108+
throw new ERR_MISSING_ARGS('type');
109+
}
110+
type = `${type}`;
111+
return filterBufferMapByNameAndType(undefined, type);
112+
}
101113

102-
function getEntriesByName(name) {
103-
if (arguments.length === 0) {
104-
throw new ERR_MISSING_ARGS('name');
114+
mark(name, options = kEmptyObject) {
115+
return mark(name, options);
105116
}
106-
name = `${name}`;
107-
return filterBufferMapByNameAndType(name, undefined);
108-
}
109117

110-
function getEntriesByType(type) {
111-
if (arguments.length === 0) {
112-
throw new ERR_MISSING_ARGS('type');
118+
measure(name, startOrMeasureOptions, endMark) {
119+
return measure(name, startOrMeasureOptions, endMark);
113120
}
114-
type = `${type}`;
115-
return filterBufferMapByNameAndType(undefined, type);
116-
}
117121

118-
class InternalPerformance extends EventTarget {}
119-
InternalPerformance.prototype.constructor = Performance.prototype.constructor;
120-
ObjectSetPrototypeOf(InternalPerformance.prototype, Performance.prototype);
122+
now() {
123+
validateInternalField(this, kPerformanceBrand, 'Performance');
124+
return now();
125+
}
126+
127+
setResourceTimingBufferSize(maxSize) {
128+
return setResourceTimingBufferSize(maxSize);
129+
}
130+
131+
get timeOrigin() {
132+
validateInternalField(this, kPerformanceBrand, 'Performance');
133+
return getTimeOriginTimestamp();
134+
}
135+
136+
toJSON() {
137+
validateInternalField(this, kPerformanceBrand, 'Performance');
138+
return {
139+
nodeTiming: this.nodeTiming,
140+
timeOrigin: this.timeOrigin,
141+
eventLoopUtilization: this.eventLoopUtilization()
142+
};
143+
}
144+
}
121145

122146
ObjectDefineProperties(Performance.prototype, {
123-
clearMarks: {
124-
__proto__: null,
125-
configurable: true,
147+
clearMarks: kEnumerableProperty,
148+
clearMeasures: kEnumerableProperty,
149+
clearResourceTimings: kEnumerableProperty,
150+
getEntries: kEnumerableProperty,
151+
getEntriesByName: kEnumerableProperty,
152+
getEntriesByType: kEnumerableProperty,
153+
mark: kEnumerableProperty,
154+
measure: kEnumerableProperty,
155+
now: kEnumerableProperty,
156+
timeOrigin: kEnumerableProperty,
157+
toJSON: kEnumerableProperty,
158+
setResourceTimingBufferSize: kEnumerableProperty,
159+
[SymbolToStringTag]: {
160+
__proto__: null,
161+
writable: false,
126162
enumerable: false,
127-
value: clearMarks,
128-
},
129-
clearMeasures: {
130-
__proto__: null,
131163
configurable: true,
132-
enumerable: false,
133-
value: clearMeasures,
134-
},
135-
clearResourceTimings: {
136-
__proto__: null,
137-
configurable: true,
138-
enumerable: false,
139-
value: clearResourceTimings,
164+
value: 'Performance',
140165
},
166+
167+
// Node.js specific extensions.
141168
eventLoopUtilization: {
142169
__proto__: null,
143170
configurable: true,
171+
// Node.js specific extensions.
144172
enumerable: false,
173+
writable: true,
145174
value: eventLoopUtilization,
146175
},
147-
getEntries: {
148-
__proto__: null,
149-
configurable: true,
150-
enumerable: false,
151-
value: getEntries,
152-
},
153-
getEntriesByName: {
154-
__proto__: null,
155-
configurable: true,
156-
enumerable: false,
157-
value: getEntriesByName,
158-
},
159-
getEntriesByType: {
160-
__proto__: null,
161-
configurable: true,
162-
enumerable: false,
163-
value: getEntriesByType,
164-
},
165-
mark: {
166-
__proto__: null,
167-
configurable: true,
168-
enumerable: false,
169-
value: mark,
170-
},
171-
measure: {
172-
__proto__: null,
173-
configurable: true,
174-
enumerable: false,
175-
value: measure,
176-
},
177176
nodeTiming: {
178177
__proto__: null,
179178
configurable: true,
179+
// Node.js specific extensions.
180180
enumerable: false,
181+
writable: true,
181182
value: nodeTiming,
182183
},
183184
// In the browser, this function is not public. However, it must be used inside fetch
184185
// which is a Node.js dependency, not a internal module
185186
markResourceTiming: {
186187
__proto__: null,
187188
configurable: true,
189+
// Node.js specific extensions.
188190
enumerable: false,
191+
writable: true,
189192
value: markResourceTiming,
190193
},
191-
now: {
192-
__proto__: null,
193-
configurable: true,
194-
enumerable: false,
195-
value: now,
196-
},
197-
setResourceTimingBufferSize: {
198-
__proto__: null,
199-
configurable: true,
200-
enumerable: false,
201-
value: setResourceTimingBufferSize
202-
},
203194
timerify: {
204195
__proto__: null,
205196
configurable: true,
197+
// Node.js specific extensions.
206198
enumerable: false,
199+
writable: true,
207200
value: timerify,
208201
},
209-
// This would be updated during pre-execution in case
210-
// the process is launched from a snapshot.
211-
// TODO(joyeecheung): we may want to warn about access to
212-
// this during snapshot building.
213-
timeOrigin: {
214-
__proto__: null,
215-
configurable: true,
216-
enumerable: true,
217-
value: getTimeOriginTimestamp(),
218-
},
219-
toJSON: {
220-
__proto__: null,
221-
configurable: true,
222-
enumerable: true,
223-
value: toJSON,
224-
}
225202
});
226203

227-
function refreshTimeOrigin() {
228-
ObjectDefineProperty(Performance.prototype, 'timeOrigin', {
229-
__proto__: null,
230-
configurable: true,
231-
enumerable: true,
232-
value: getTimeOriginTimestamp(),
233-
});
204+
function createPerformance() {
205+
return ReflectConstruct(function Performance() {
206+
initEventTarget(this);
207+
this[kPerformanceBrand] = true;
208+
}, [], Performance);
234209
}
235210

236-
const performance = new InternalPerformance();
211+
const performance = createPerformance();
237212

238213
function dispatchBufferFull(type) {
239214
const event = new Event(type, {
@@ -246,5 +221,4 @@ setDispatchBufferFull(dispatchBufferFull);
246221
module.exports = {
247222
Performance,
248223
performance,
249-
refreshTimeOrigin
250224
};

lib/internal/perf/usertiming.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class PerformanceMeasure extends InternalPerformanceEntry {
9494
}
9595
}
9696

97-
function mark(name, options = kEmptyObject) {
97+
function mark(name, options) {
9898
const mark = new PerformanceMark(name, options);
9999
enqueue(mark);
100100
bufferUserTiming(mark);

lib/internal/perf/utils.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
'use strict';
22

3+
const {
4+
Symbol,
5+
} = primordials;
6+
37
const binding = internalBinding('performance');
48
const {
59
milestones,
@@ -9,6 +13,7 @@ const {
913
// TODO(joyeecheung): we may want to warn about access to
1014
// this during snapshot building.
1115
let timeOrigin = getTimeOrigin();
16+
const kPerformanceBrand = Symbol('performance');
1217

1318
function now() {
1419
const hr = process.hrtime();
@@ -29,5 +34,6 @@ function refreshTimeOrigin() {
2934
module.exports = {
3035
now,
3136
getMilestoneTimestamp,
32-
refreshTimeOrigin
37+
refreshTimeOrigin,
38+
kPerformanceBrand,
3339
};

0 commit comments

Comments
 (0)