Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 6659fd5

Browse files
committed
feat(wtf): add wtf support to (set/clear)Timeout/Interval/Immediate
This rewrites the (set/clear)Timeout/Interval/Immediate method to render information in WTF. It also makes the code more explicit, by removing the need for bind method and instead creating explicit callbacks. The resulting code is faster, and easier to read and reason about.
1 parent c6cfdb4 commit 6659fd5

12 files changed

+291
-93
lines changed

Diff for: karma-browserify.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = function (config) {
55
basePath: '',
66
files: [
77
'test/util.js',
8+
'test/wtf_mock.js',
89
'test/commonjs.spec.js',
910
{pattern: 'test/assets/**/*.*', watched: true, served: true, included: false},
1011
{pattern: 'lib/**/*.js', watched: true, served: false, included: false}

Diff for: karma-microtasks.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = function (config) {
55
basePath: '',
66
files: [
77
'test/util.js',
8+
'test/wtf_mock.js',
89
'test/setup-microtask.js',
910
'examples/js/*.js',
1011
'test/**/*.spec.js',

Diff for: karma.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = function (config) {
55
basePath: '',
66
files: [
77
'test/util.js',
8+
'test/wtf_mock.js',
89
'test/setup.js',
910
'example/js/*.js',
1011
//'test/lib/brick.js',

Diff for: lib/core.js

+29-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
var keys = require('./keys');
44

5+
var deprecated = {};
6+
7+
function deprecatedWarning(key, text) {
8+
if (!deprecated.hasOwnProperty(key)) {
9+
deprecated[key] = true;
10+
console.warn("DEPRECATION WARNING: '" + key +
11+
"' is no longer supported and will be removed in next major release. " + text);
12+
}
13+
}
14+
515
function Zone(parentZone, data) {
616
var zone = (arguments.length) ? Object.create(parentZone) : this;
717

@@ -70,7 +80,9 @@ Zone.prototype = {
7080
};
7181
},
7282

83+
/// @deprecated
7384
bindOnce: function (fn) {
85+
deprecatedWarning('bindOnce', 'There is no replacement.');
7486
var boundZone = this;
7587
return this.bind(function () {
7688
var result = fn.apply(this, arguments);
@@ -120,8 +132,23 @@ Zone.prototype = {
120132
beforeTask: function () {},
121133
onZoneCreated: function () {},
122134
afterTask: function () {},
123-
enqueueTask: function () {},
124-
dequeueTask: function () {},
135+
136+
enqueueTask: function () {
137+
deprecatedWarning('enqueueTask', 'Use addTask/addRepeatingTask/addMicroTask');
138+
},
139+
dequeueTask: function () {
140+
deprecatedWarning('dequeueTask', 'Use removeTask/removeRepeatingTask/removeMicroTask');
141+
},
142+
143+
addTask: function(taskFn) { this.enqueueTask(taskFn); },
144+
removeTask: function(taskFn) { this.dequeueTask(taskFn); },
145+
146+
addRepeatingTask: function(taskFn) { this.enqueueTask(taskFn); },
147+
removeRepeatingTask: function(taskFn) { this.dequeueTask(taskFn); },
148+
149+
addMicrotask: function(taskFn) { this.enqueueTask(taskFn); },
150+
removeMicrotask: function(taskFn) { this.dequeueTask(taskFn); },
151+
125152
addEventListener: function () {
126153
return this[keys.common.addEventListener].apply(this, arguments);
127154
},

Diff for: lib/patch/browser.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ var fileReaderPatch = require('./file-reader');
1313

1414
function apply() {
1515
fnPatch.patchSetClearFunction(global, [
16-
'timeout',
17-
'interval',
18-
'immediate'
16+
'Timeout',
17+
'Interval',
18+
'Immediate'
1919
]);
2020

2121
fnPatch.patchRequestAnimationFrame(global, [

Diff for: lib/patch/functions.js

+76-57
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,98 @@
11
'use strict';
22

33
var utils = require('../utils');
4+
var wtf = require('../wtf');
5+
var Zone = require('../core').Zone;
46

5-
function patchSetClearFunction(obj, fnNames) {
6-
fnNames.map(function (name) {
7-
return name[0].toUpperCase() + name.substr(1);
8-
}).forEach(function (name) {
7+
function patchSetClearFunction(window, fnNames) {
8+
fnNames.forEach(function (name) {
9+
var repeating = name == 'Interval';
910
var setName = 'set' + name;
10-
var delegate = obj[setName];
11-
12-
if (delegate) {
13-
var clearName = 'clear' + name;
14-
var ids = {};
11+
var clearName = 'clear' + name;
12+
var setNative = window[setName];
13+
var clearNative = window[clearName];
14+
var ids = {};
15+
16+
if (setNative) {
17+
var wtfSetEventFn = wtf.createEvent('Zone#' + setName + '(uint32 zone, uint32 id, uint32 delay)');
18+
var wtfClearEventFn = wtf.createEvent('Zone#' + clearName + '(uint32 zone, uint32 id)');
19+
var wtfCallbackFn = wtf.createScope('Zone#cb:' + name + '(uint32 zone, uint32 id, uint32 delay)');
20+
21+
// Forward all calls from the window through the zone.
22+
window[setName] = function () {
23+
return global.zone[setName].apply(global.zone, arguments);
24+
};
25+
window[clearName] = function () {
26+
return global.zone[clearName].apply(global.zone, arguments);
27+
};
1528

16-
var bindArgs = setName === 'setInterval' ? utils.bindArguments : utils.bindArgumentsOnce;
1729

18-
global.zone[setName] = function (fn) {
19-
var id, fnRef = fn;
20-
arguments[0] = function () {
21-
delete ids[id];
22-
return fnRef.apply(this, arguments);
30+
// Set up zone processing for the set function.
31+
Zone.prototype[setName] = function (fn, delay) {
32+
// We need to save `fn` in var different then argument. This is because
33+
// in IE9 `argument[0]` and `fn` have same identity, and assigning to
34+
// `argument[0]` changes `fn`.
35+
var callbackFn = fn;
36+
if (typeof callbackFn !== 'function') {
37+
// force the error by calling the method with wrong args
38+
setNative.apply(window, arguments);
39+
}
40+
var zone = this;
41+
var setId = null;
42+
// wrap the callback function into the zone.
43+
arguments[0] = function() {
44+
var callbackZone = zone.isRootZone() || isRaf ? zone : zone.fork();
45+
var callbackThis = this;
46+
var callbackArgs = arguments;
47+
return wtf.leaveScope(
48+
wtfCallbackFn(callbackZone.$id, setId, delay),
49+
callbackZone.run(function() {
50+
if (!repeating) {
51+
delete ids[setId];
52+
callbackZone.removeTask(callbackFn);
53+
}
54+
return callbackFn.apply(callbackThis, callbackArgs);
55+
})
56+
);
2357
};
24-
var args = bindArgs(arguments);
25-
id = delegate.apply(obj, args);
26-
ids[id] = true;
27-
return id;
58+
if (repeating) {
59+
zone.addRepeatingTask(callbackFn);
60+
} else {
61+
zone.addTask(callbackFn);
62+
}
63+
setId = setNative.apply(window, arguments);
64+
ids[setId] = callbackFn;
65+
wtfSetEventFn(zone.$id, setId, delay);
66+
return setId;
2867
};
2968

30-
obj[setName] = function () {
31-
return global.zone[setName].apply(this, arguments);
69+
Zone.prototype[setName + 'Unpatched'] = function() {
70+
return setNative.apply(window, arguments);
3271
};
3372

34-
var clearDelegate = obj[clearName];
35-
36-
global.zone[clearName] = function (id) {
37-
if (ids[id]) {
73+
// Set up zone processing for the clear function.
74+
Zone.prototype[clearName] = function (id) {
75+
var scope = wtfClearEventFn(this.$id, id);
76+
if (ids.hasOwnProperty(id)) {
77+
var callbackFn = ids[id];
3878
delete ids[id];
39-
global.zone.dequeueTask();
79+
if (repeating) {
80+
this.removeRepeatingTask(callbackFn);
81+
} else {
82+
this.removeTask(callbackFn);
83+
}
4084
}
41-
return clearDelegate.apply(this, arguments);
85+
return clearNative.apply(window, arguments);
4286
};
4387

44-
global.zone[setName + 'Unpatched'] = function() {
45-
return delegate.apply(obj, arguments);
88+
Zone.prototype[clearName + 'Unpatched'] = function() {
89+
return clearNative.apply(window, arguments);
4690
};
4791

48-
global.zone[clearName + 'Unpatched'] = function() {
49-
return clearDelegate.apply(obj, arguments);
50-
};
51-
52-
obj[clearName] = function () {
53-
return global.zone[clearName].apply(this, arguments);
54-
};
5592
}
93+
}
94+
fnNames.forEach(function(args) {
95+
patchMacroTaskMethod.apply(null, args);
5696
});
5797
};
5898

@@ -83,26 +123,6 @@ function patchRequestAnimationFrame(obj, fnNames) {
83123
});
84124
};
85125

86-
function patchSetFunction(obj, fnNames) {
87-
fnNames.forEach(function (name) {
88-
var delegate = obj[name];
89-
90-
if (delegate) {
91-
global.zone[name] = function (fn) {
92-
arguments[0] = function () {
93-
return fn.apply(this, arguments);
94-
};
95-
var args = utils.bindArgumentsOnce(arguments);
96-
return delegate.apply(obj, args);
97-
};
98-
99-
obj[name] = function () {
100-
return zone[name].apply(this, arguments);
101-
};
102-
}
103-
});
104-
};
105-
106126
function patchFunction(obj, fnNames) {
107127
fnNames.forEach(function (name) {
108128
var delegate = obj[name];
@@ -119,7 +139,6 @@ function patchFunction(obj, fnNames) {
119139

120140
module.exports = {
121141
patchSetClearFunction: patchSetClearFunction,
122-
patchSetFunction: patchSetFunction,
123142
patchRequestAnimationFrame: patchRequestAnimationFrame,
124143
patchFunction: patchFunction
125144
};

Diff for: lib/utils.js

-10
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,6 @@ function bindArguments(args) {
1111
return args;
1212
};
1313

14-
function bindArgumentsOnce(args) {
15-
for (var i = args.length - 1; i >= 0; i--) {
16-
if (typeof args[i] === 'function') {
17-
args[i] = global.zone.bindOnce(args[i]);
18-
}
19-
}
20-
return args;
21-
};
22-
2314
function patchPrototype(obj, fnNames) {
2415
fnNames.forEach(function (name) {
2516
var delegate = obj[name];
@@ -199,7 +190,6 @@ function patchClass(className) {
199190

200191
module.exports = {
201192
bindArguments: bindArguments,
202-
bindArgumentsOnce: bindArgumentsOnce,
203193
patchPrototype: patchPrototype,
204194
patchProperty: patchProperty,
205195
patchProperties: patchProperties,

Diff for: lib/wtf.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
// Detect and setup WTF.
4+
var wtfTrace = null;
5+
var wtfEvents = null;
6+
var wtfEnabled = (function () {
7+
var wtf = global['wtf'];
8+
if (wtf) {
9+
wtfTrace = wtf['trace'];
10+
if (wtfTrace) {
11+
wtfEvents = wtfTrace['events'];
12+
return true;
13+
}
14+
}
15+
return false;
16+
})();
17+
18+
function noop() {
19+
}
20+
21+
if (wtfEnabled) {
22+
module.exports = {
23+
enabled: true,
24+
createScope: function (signature, flags) {
25+
return wtfEvents.createScope(signature, flags);
26+
},
27+
createEvent: function (signature, flags) {
28+
return wtfEvents.createInstance(signature, flags);
29+
},
30+
leaveScope: function (scope, returnValue) {
31+
wtfTrace.leaveScope(scope, returnValue);
32+
return returnValue;
33+
},
34+
beginTimeRange:function (rangeType, action) {
35+
return wtfTrace.beginTimeRange(rangeType, action);
36+
},
37+
endTimeRange: function (range) {
38+
wtfTrace.endTimeRange(range);
39+
}
40+
};
41+
} else {
42+
module.exports = {
43+
enabled: false,
44+
createScope: function (s, f) { return noop; },
45+
createEvent: function (s, f) { return noop; },
46+
leaveScope: function (s, v) { return v; },
47+
beginTimeRange: function (t, a) { return null; },
48+
endTimeRange: function (r) { }
49+
};
50+
}
51+

Diff for: test/patch/setInterval.spec.js

+27-4
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,39 @@
33
describe('setInterval', function () {
44

55
it('should work with setInterval', function (done) {
6-
var testZone = zone.fork();
6+
var testZone = zone.fork({
7+
addTask: function(fn) { wtfMock.log.push('addTask ' + fn.id ); },
8+
removeTask: function(fn) { wtfMock.log.push('removeTask ' + fn.id); },
9+
addRepeatingTask: function(fn) { wtfMock.log.push('addRepeatingTask ' + fn.id); },
10+
removeRepeatingTask: function(fn) { wtfMock.log.push('removeRepeatingTask ' + fn.id); },
11+
});
712

13+
var zId;
14+
var setIntervalId = '?';
815
testZone.run(function() {
9-
var cancelId = setInterval(function() {
16+
zId = zone.$id;
17+
var intervalFn = function () {
18+
var zCallbackId = zone.$id;
1019
// creates implied zone in all callbacks.
1120
expect(zone).toBeDirectChildOf(testZone);
1221

1322
clearInterval(cancelId);
14-
done();
15-
}, 10);
23+
zone.setTimeoutUnpatched(function() {
24+
expect(wtfMock.log).toEqual([
25+
'addRepeatingTask abc',
26+
'# Zone#setInterval(' + zId + ', ' + cancelId + ', 10)',
27+
'> Zone#cb:Interval(' + zCallbackId + ', ' + cancelId + ', 10)',
28+
'# Zone#clearInterval(' + zCallbackId + ', ' + cancelId + ')',
29+
'removeRepeatingTask abc',
30+
'< Zone#cb:Interval'
31+
]);
32+
done();
33+
});
34+
};
35+
intervalFn.id = 'abc';
36+
var cancelId = setInterval(intervalFn, 10);
37+
expect(wtfMock.log[0]).toEqual('addRepeatingTask abc');
38+
expect(wtfMock.log[1]).toEqual('# Zone#setInterval(' + zId + ', ' + cancelId + ', 10)');
1639
});
1740
});
1841

0 commit comments

Comments
 (0)