-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New with spread #3066
New with spread #3066
Conversation
d34674a
to
ce95286
Compare
I had some serious trouble with I have to turn off autocrlf:
In order for my test to pass. Have there been any experiencing the same problem as me? using Mac? |
Yes. Don't use autocrlf :) It's not appropriate for our project because line endings are an important thing we track and use in our tests. |
@@ -21,6 +21,7 @@ tests/services/baselines/local/* | |||
tests/baselines/prototyping/local/* | |||
tests/baselines/rwc/* | |||
tests/baselines/test262/* | |||
tests/baselines/reference/projectOutput/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why was this added to the .gitignore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CyrusNajmabadi i think this is a fair one to add, ppl have made this mistake multiple times already. mainly because of a mocha issue on node 0.10.*
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I don't think there's any real harm in having it
👍 |
I probably should re-style my tests to use |
@@ -1881,13 +1860,77 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { | |||
} | |||
} | |||
|
|||
function emitCallOrNewExpressionTarget(node: Expression): CallTarget { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is a little subtle that this function emits one thing (the target), and then returns two things, one of which it emitted and one of which it did not. Can you make it just return the target and the accessor, and then the caller will emit the target?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1. It should be named properly as well to convey that it's just computing hte call target, and not actually emitting anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JsonFreeman sound like a good idea. I will address this later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just tried to address this. The reason I did it was so that I don't need to have multiple node kind checks — one for the getter and one for the emitter. We already have in emitCallOrNewExpressionTarget()
the node kind checks:
if (node.kind === SyntaxKind.PropertyAccessExpression) {
...
}
else if (node.kind === SyntaxKind.ElementAccessExpression) {
...
}
else if (node.kind === SyntaxKind.SuperKeyword) {
...
}
else {
...
}
If we would have one for getter and emitter the same node kind checks need to be applied mulitple times.
I don't have any perfect solution to this. Any idea how to fix this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just found a better way to do this. We can just call it emitCallTarget()
and return the target and have a separate function for the emit of accessor or just inline the emit logic in emitNewExpression
since we only use it there.
The problem is though, that it already exists one function with emitCallTarget()
. Any chance I can just rename it to emitCallTargetWithOptionalTempAssignment()
?
function emitCallTarget(node: Expression): Expression {
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword) {
emit(node);
return node;
}
let temp = createAndRecordTempVariable(TempFlags.Auto);
write("(");
emit(temp);
write(" = ");
emit(node);
write(")");
return temp;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well the functionality below looks a lot like emitCallWithSpread
, which calls the emitCallTarget
you've mentioned above, and you want to now call this emitCallTarget
, so we should find some unifying scheme.
Also, on the issue of subtlety in behavior - while the name is important, I'd rather see documentation, which we are currently lacking in. Regardless of what this is called, please add a nice JSDoc comment to point out its peculiar behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested my latest idea. It also requires additional node checks. emitCallWithSpread()
also emits the accessor and writing the extra node checks is not worth it IMO. Can't we just rename it to emitCallTargetAndAccessor()
? It optionally emits and optionally returns the target and accessor. It is the best in terms of DRY:ness and also number of operations/comparison for the job.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tinganho I don't think we're opposed to it, but let's see what the change looks like; it's hard to get a feel of it without seeing the end result. Worst-case scenario, we'll just revert the commits
Revision 2I've just updated the function name to Bug1 (accessor is a string literal) new object["a-b"](1, 2, a..)
new (a_ = object)["a-b"].bind.apply(a_."a-b", [null]); // should be a["a-b"]; Bug2 (accessor is a numeric literal) new object[1](1, 2, a..)
new (a_ = object)[1].bind.apply(a_.1, [null]); // should be a[1]; Old comments: |
acfae7d
to
94dc37f
Compare
@JsonFreeman I addressed the problems in the old revision. I only use Also there was no wrong with the new expression call syntax Travis is pulling in a wrong commit that's why it is failing. But this one has the right commit. (I think I can only fix this with a new PR though) I contacted travis to address this. |
@tinganho Thanks for iterating on this implementation so well. One thing that would be helpful in the future is when you change something, push the changes as new commits instead of amending your old commits. That way it is very easy to see what changed over the course of the discussion. |
// Call expression | ||
new f(1, 2, "string")(); | ||
new (Function.bind.apply(f, [void 0].concat([1, 2].concat(a))))(); | ||
new (Function.bind.apply(f, [void 0].concat([1, 2].concat(a, ["string"]))))(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think that these two lines are incorrect because these really translate to
new f(1, 2, ...a);
and not
new f(1, 2, ...a)();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I understand here can you elaborate? They have ()
at the end? Or does the outer parentheses (Function...)
messing with it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the outer parentheses are messing with it, because it causes the final ()
to be consumed as a part of the new expression. You need an extra ()
to make it a call expression. So you should emit:
new (Function.bind.apply(f, [void 0].concat([1, 2].concat(a))))()();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JsonFreeman I guess if the call have arguments it should be compiled to:
new (Function.bind.apply(f, [void 0].concat([1, 2].concat(a))))()(a, b);
The one caveat with using Function as I suggested is that there could be a local declaration called Function that shadows the global function. Maybe the Function idea won't work then 😞. Unless we emit some sort of capture, which is overkill I think. Do you think we should go back to the temp idea? Sorry to be fickle. |
No problem, Ok I can change to your other idea with using temp for "target+accessor". I will push them as new commits instead of cleaned ones. |
Thanks. In terms of the implementation code, I guess my suggestion is that instead of using emitCallTargetAndAccessor, you just call emitCallTarget (the already existing one), and pass in node.expression as the argument (where That way you don't need to worry about the target and accessor. |
hmm, I will try it out and see if there are any problem. |
I think it should still give you |
@JsonFreeman it is still pulling in the wrong commit bde2491 instead of b88d542 |
Let's ask @DanielRosenwasser, since he knows more about Travis |
@tinganho I pulled down your branch and I'm getting the same error:
This is related to some recent changes; you need to add the parameter |
write(".bind.apply("); | ||
emit(target); | ||
write(", [void 0].concat("); | ||
emitListWithSpread(node.arguments, /*multiline*/false, /*trailingComma*/false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add spaces after each of these comments?
@DanielRosenwasser I could get the errors now. Let me just fix it. |
Oh wait, I noticed that new f(...a, ...b); will emit as new (f.bind.apply(f, [void 0].concat(a.concat(b)))(); And with alwaysCopy set to true, it would do new f(...a) as new (f.bind.apply(f, [void 0].concat(a.slice()))(); Can you confirm that this is what happens? This is not what we want, we don't want the slice or the second concat. Can you add tests for this if it is not already affected? |
write(".bind.apply("); | ||
emit(target); | ||
write(", [void 0].concat("); | ||
emitListWithSpread(node.arguments, /*alwaysCopy*/true, /*multiline*/false, /*trailingComma*/false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you actually rename alwaysCopy
to needsUniqueCopy
and replace the relevant comments elsewhere? I realized that we don't actually always copy if the initial element is an array literal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I can rename it.
I think the reason the tests did not catch this is because all the tests have at least one non-spread argument before the spread. |
@JsonFreeman I was a little bit fast with the the push button. I think alwaysCopy should be set to false instead of true. Because it was false before. I can add a test for it. |
Sure, but you will still have the double concat problem. |
@JsonFreeman is that doable to do If it is doable what should the outputted code be like? |
Regarding function f(...a, ...b) { } But I meant if you have a function called f, and you call it with two spread arguments: function f(...p: any[]) { }
var a: string[];
var b: string[];
f(...a, ...b); And that is legal. |
@JsonFreeman I just tested this and it is outputting: new (f.bind.apply(f, [void 0].concat(a.concat(b)))(); I can't see what's wrong with it? What is the proposed emit? Or should b not be concatenated? |
That emit works, but I think it's nicer to emit: new (f.bind.apply(f, [void 0].concat(a, b)))(); You can achieve this by passing a boolean to emitListWithSpread to make it suppress the concat. |
Ok I fixed it on a new commit. |
Sweet! Thanks @tinganho! I sign off. Please fix my one nit, and then I can merge it in. |
OK @JsonFreeman I fixed it on a new commit. Sorry for not seeing it earlier. |
Thanks @tinganho! |
Implements #2369. Adds ES5 support for spread operator in
new expressions
. ES3 targets should still output warning as before, because it doesn't supportFunction.prototype.bind
which I use in this ES5 implementation. I refactored some of the code from previouscall with spread implementation
#1931 so I could reuse it to on my implementation.I have also manually tested the permutations of the outputted JavaScript.