performance: fix timerify bug#40625
Conversation
lib/internal/perf/timerify.js
Outdated
| if (fn[kTimerified]) return fn[kTimerified]; | ||
|
|
||
| const constructor = isConstructor(fn); | ||
| const isClass = (fn) => /^\s*class/.test(fn.toString()); |
There was a problem hiding this comment.
This would fail if someone is using "old" classes which can be common when transpiling cross-platform code that targets old targets (for example: using tooling that uses es5 at any point)
There was a problem hiding this comment.
This would fail if someone is using "old" classes which can be common when transpiling cross-platform code that targets old targets (for example: using tooling that uses es5 at any point)
Maybe you can add a test on test-performance-function.js or just show me an example function(I will add to the former file). I will solve the problem to ensure all tests have been passed.
There was a problem hiding this comment.
Sure
function MyClass {
this.foo = 15;
}
MyClass.prototype.sayFoo {
console.log(this.foo);
}
const instance = new MyClass(); ?
There was a problem hiding this comment.
this.foo = 15;
Hasn't it a return value ?
There was a problem hiding this comment.
Sure
function MyClass { this.foo = 15; } MyClass.prototype.sayFoo { console.log(this.foo); } const instance = new MyClass(); ?
If a function is like this. Actually, we can't judge which way is to call. Because using new to call and invoke directly is different. So I think we just invoke it directly if the first augment is a function. Using new to call if it is a class. wdyt?
There was a problem hiding this comment.
I think that will not work for a large chunk of our ecosystem unfortunately and there are people who still use "old style" constructors or have existing projects that do it. So I think for me personally this not breaking for "old style" classes is important.
lib/internal/perf/timerify.js
Outdated
| function timerified(...args) { | ||
| const start = now(); | ||
| const result = constructor ? | ||
| const result = isClass(fn) ? |
There was a problem hiding this comment.
Maybe we should change the check from "is it a constructor?" to "is it called as a constructor?". i.e. new.target !== undefined. wdyt?
There was a problem hiding this comment.
That sounds like a very reasonable approach to me
There was a problem hiding this comment.
Note new.target can be set but the return value can still be overridden by returning something different - so a constructor check is would still need to check the return value
There was a problem hiding this comment.
Maybe we should change the check from "is it a constructor?" to "is it called as a constructor?". i.e.
new.target !== undefined. wdyt?
Actually. I have considered this problem. It seems like new.target only can be used within a function. I didn't find any way to use it outside of a function.
There was a problem hiding this comment.
It seems like new.target only can be used within a function. I didn't find any way to use it outside of a function.
This should be fine. timerified is a function.
There was a problem hiding this comment.
It seems like new.target only can be used within a function. I didn't find any way to use it outside of a function.
This should be fine.
timerifiedis a function.
I think we don't need to judge if it is called a constructor. we only judge whether the argument is a function or not. if is a class we use new to call, else we invoked directly.
There was a problem hiding this comment.
I don't agree. In my opinion, timerified should be a proxy to the function and behave as much as possible the same as if the function was called directly.
There was a problem hiding this comment.
I don't agree. In my opinion,
timerifiedshould be a proxy to the function and behave as much as possible the same as if the function was called directly.
There must have been some misunderstanding in there. What I do is just like you said. Are there any conflicts?
There was a problem hiding this comment.
The conflict is that, in JavaScript, a function can be a class. What Michaël is saying is that, no matter what we can infer of the argument being a "class" or not, if timerified is invoke using new keyboard (i.e. if new.target !== undefined), we should pass that along.
| const result = isCalledAsConstructor(fn) ? | ||
| ReflectConstruct(fn, args, fn) : ReflectApply(fn, this, args); |
There was a problem hiding this comment.
| const result = isCalledAsConstructor(fn) ? | |
| ReflectConstruct(fn, args, fn) : ReflectApply(fn, this, args); | |
| const result = new.target !== undefined ? | |
| ReflectConstruct(fn, args, fn) : | |
| ReflectApply(fn, this, args); |
There was a problem hiding this comment.
You marked this conversation as resolved but didn't take my suggestion. Is it because you disagree with it, or because you forgot to push?
There was a problem hiding this comment.
@aduh95 wouldn't new.target not be undefined only if timerified() is called with new instead of fn()?
There was a problem hiding this comment.
Yes, but that's the point. We want the timerified function to behave the same as the original function in both cases.
There was a problem hiding this comment.
Ah, we are returning timerified from timerify. Yea, this makes sense 👍
benjamingr
left a comment
There was a problem hiding this comment.
Leaving a review so this doesn't land by accident before we have consensus on the semantics - I feel strongly that this should work for "old style" classes before we merge and that we should address the fact that classes can have return value (and a test) - but I am ready to be convinced otherwise.
Ok. If a function like below: If this function is a parameter to timerify. I really don't know we should use |
|
I think maybe we can add a comment to the parameter to tell users. This is the only way I can think of to solve the question. This is a bug and we need to solve it. How about this way? @benjamingr @targos |
|
I still think #40625 (comment) would be a better solution, and would align with what Benjamin and Michaël are asking. Is there a reason you don't want to implement it? |
|
I think we should use |
|
@iam-frankqiu Any updates? If not, I'd like to create another PR that fixes this issue with |
|
Superseded by #43330. |
Fix #40623