Skip to content

Emit arrow function es6 #1627

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

Merged
merged 30 commits into from
Feb 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
436baaf
Add default target in compiler option of project runner
Jan 7, 2015
b0ea401
Emit Arrow function natively in ES6
Jan 7, 2015
7d0fc62
Emit this binding natively in es6
Jan 7, 2015
cb48a5e
Add testcases
Jan 7, 2015
ba239c5
Address code review
Jan 16, 2015
581beb5
Merge branch 'master' into emitArrowFunctionES6
Jan 21, 2015
de9547c
Update type checking for lexical binding due to merge with master
Jan 24, 2015
593a099
Update emitter due to merge with master
Jan 24, 2015
3115288
Update baseline and fix white space
Jan 24, 2015
f219a2d
Address code review; preserve users non-parenthesis
Jan 27, 2015
15b05e6
Merge branch 'master' into emitArrowFunctionES6
Jan 27, 2015
2e2559b
Update tests baseline from merging with master
Jan 27, 2015
ca3c1ed
Address the issue that arrow function doesn't have arguments objects
Jan 29, 2015
d5b953d
Add testcases
Jan 29, 2015
70140ef
Fix spacing due to tab
Jan 29, 2015
8d731d4
Address code review
Jan 29, 2015
2b200d4
Address code review
Jan 29, 2015
9b04180
Change tab to space
Jan 29, 2015
e4b206c
Merge branch 'master' into emitArrowFunctionES6
Jan 29, 2015
fd20695
Remove flag and compare position
Jan 30, 2015
fb2c502
Clean up the checking of position
Jan 30, 2015
cf5aadb
Address code review
Jan 30, 2015
5d0376f
Address codereview
Jan 30, 2015
6a0eaf5
Update an error
Jan 31, 2015
4162671
Address code review
Jan 31, 2015
ff038fb
Merge branch 'master' into emitArrowFunctionES6
Jan 31, 2015
a595a78
Remove tabs in json
Jan 31, 2015
53dffda
Merge branch 'master' into emitArrowFunctionES6
Feb 2, 2015
0d53542
Merge branch 'master' into emitArrowFunctionES6
Feb 2, 2015
122d587
Merge branch 'master' into emitArrowFunctionES6
Feb 2, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4861,6 +4861,16 @@ module ts {
function checkIdentifier(node: Identifier): Type {
var symbol = getResolvedSymbol(node);

// As noted in ECMAScript 6 language spec, arrow functions never have an arguments objects.
// Although in down-level emit of arrow function, we emit it using function expression which means that
// arguments objects will be bound to the inner object; emitting arrow function natively in ES6, arguments objects
// will be bound to non-arrow function that contain this arrow function. This results in inconsistent behavior.
// To avoid that we will give an error to users if they use arguments objects in arrow function so that they
// can explicitly bound arguments objects
if (symbol === argumentsSymbol && getContainingFunction(node).kind === SyntaxKind.ArrowFunction) {
error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_Consider_using_a_standard_function_expression);
}

if (symbol.flags & SymbolFlags.Import) {
var symbolLinks = getSymbolLinks(symbol);
if (!symbolLinks.referenced) {
Expand Down Expand Up @@ -4915,7 +4925,9 @@ module ts {
// Now skip arrow functions to get the "real" owner of 'this'.
if (container.kind === SyntaxKind.ArrowFunction) {
container = getThisContainer(container, /* includeArrowFunctions */ false);
needToCaptureLexicalThis = true;

// When targeting es6, arrow function lexically bind "this" so we do not need to do the work of binding "this" in emitted code
needToCaptureLexicalThis = (languageVersion < ScriptTarget.ES6);
}

switch (container.kind) {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,5 +452,6 @@ module ts {
You_cannot_rename_this_element: { code: 8000, category: DiagnosticCategory.Error, key: "You cannot rename this element." },
yield_expressions_are_not_currently_supported: { code: 9000, category: DiagnosticCategory.Error, key: "'yield' expressions are not currently supported.", isEarly: true },
Generators_are_not_currently_supported: { code: 9001, category: DiagnosticCategory.Error, key: "Generators are not currently supported.", isEarly: true },
The_arguments_object_cannot_be_referenced_in_an_arrow_function_Consider_using_a_standard_function_expression: { code: 9002, category: DiagnosticCategory.Error, key: "The 'arguments' object cannot be referenced in an arrow function. Consider using a standard function expression." },
};
}
10 changes: 7 additions & 3 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1612,7 +1612,7 @@
"Property '{0}' does not exist on 'const' enum '{1}'.": {
"category": "Error",
"code": 4088,
"isEarly": true
"isEarly": true
},
"The current host does not support the '{0}' option.": {
"category": "Error",
Expand Down Expand Up @@ -1902,11 +1902,15 @@
"'yield' expressions are not currently supported.": {
"category": "Error",
"code": 9000,
"isEarly": true
"isEarly": true
},
"Generators are not currently supported.": {
"category": "Error",
"code": 9001,
"isEarly": true
"isEarly": true
},
"The 'arguments' object cannot be referenced in an arrow function. Consider using a standard function expression.": {
"category": "Error",
"code": 9002
}
}
34 changes: 31 additions & 3 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,6 @@ module ts {

// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compilerOnSave feature
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile?: SourceFile): EmitResult {
// var program = resolver.getProgram();
var compilerOptions = host.getCompilerOptions();
var languageVersion = compilerOptions.target || ScriptTarget.ES3;
var sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap ? [] : undefined;
Expand Down Expand Up @@ -3240,6 +3239,10 @@ module ts {
emitSignatureAndBody(node);
}

function shouldEmitAsArrowFunction(node: FunctionLikeDeclaration): boolean {
return node.kind === SyntaxKind.ArrowFunction && languageVersion >= ScriptTarget.ES6;
}

function emitFunctionDeclaration(node: FunctionLikeDeclaration) {
if (nodeIsMissing(node.body)) {
return emitPinnedOrTripleSlashComments(node);
Expand All @@ -3249,7 +3252,13 @@ module ts {
// Methods will emit the comments as part of emitting method declaration
emitLeadingComments(node);
}
write("function ");

// For targeting below es6, emit functions-like declaration including arrow function using function keyword.
// When targeting ES6, emit arrow function natively in ES6 by omitting function keyword and using fat arrow instead
if (!shouldEmitAsArrowFunction(node)) {
write("function ");
}

if (node.kind === SyntaxKind.FunctionDeclaration || (node.kind === SyntaxKind.FunctionExpression && node.name)) {
emit(node.name);
}
Expand Down Expand Up @@ -3280,14 +3289,32 @@ module ts {
decreaseIndent();
}

function emitSignatureParametersForArrow(node: FunctionLikeDeclaration) {
// Check whether the parameter list needs parentheses and preserve no-parenthesis
if (node.parameters.length === 1 && node.pos === node.parameters[0].pos) {
emit(node.parameters[0]);
return;
}
emitSignatureParameters(node);
}

function emitSignatureAndBody(node: FunctionLikeDeclaration) {
var saveTempCount = tempCount;
var saveTempVariables = tempVariables;
var saveTempParameters = tempParameters;
tempCount = 0;
tempVariables = undefined;
tempParameters = undefined;
emitSignatureParameters(node);

// When targeting ES6, emit arrow function natively in ES6
if (shouldEmitAsArrowFunction(node)) {
emitSignatureParametersForArrow(node);
write(" =>");
}
else {
emitSignatureParameters(node);
}

write(" {");
scopeEmitStart(node);
increaseIndent();
Expand All @@ -3299,6 +3326,7 @@ module ts {
startIndex = emitDirectivePrologues((<Block>node.body).statements, /*startWithNewLine*/ true);
}
var outPos = writer.getTextPos();

emitCaptureThisForNodeIfNecessary(node);
emitDefaultValueAssignments(node);
emitRestParameter(node);
Expand Down
2 changes: 1 addition & 1 deletion src/harness/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ module Utils {
export function memoize<T extends Function>(f: T): T {
var cache: { [idx: string]: any } = {};

return <any>(() => {
return <any>(function () {
var key = Array.prototype.join.call(arguments);
var cachedResult = cache[key];
if (cachedResult) {
Expand Down
2 changes: 1 addition & 1 deletion src/harness/loggedIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ module Playback {

function recordReplay<T extends Function>(original: T, underlying: any) {
function createWrapper(record: T, replay: T): T {
return <any>(() => {
return <any>(function () {
if (replayLog !== undefined) {
return replay.apply(undefined, arguments);
} else if (recordLog !== undefined) {
Expand Down
2 changes: 1 addition & 1 deletion src/harness/projectsRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class ProjectRunner extends RunnerBase {
function createCompilerHost(): ts.CompilerHost {
return {
getSourceFile,
getDefaultLibFilename: options => options.target === ts.ScriptTarget.ES6 ? "lib.es6.d.ts" : "lib.d.ts",
getDefaultLibFilename: options => Harness.Compiler.defaultLibFileName,
writeFile,
getCurrentDirectory,
getCanonicalFileName: Harness.Compiler.getCanonicalFileName,
Expand Down
5 changes: 2 additions & 3 deletions tests/baselines/reference/computedPropertyNames29.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ var C = (function () {
function C() {
}
C.prototype.bar = function () {
var _this = this;
(function () {
(() => {
var obj = {
[_this.bar()]() {
[this.bar()]() {
} // needs capture
};
});
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/computedPropertyNames3.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var C = (function () {
}
C.prototype[0 + 1] = function () {
};
C[function () {
C[() => {
}] = function () {
};
Object.defineProperty(C.prototype, delete id, {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/computedPropertyNames30.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var C = (function (_super) {
__extends(C, _super);
function C() {
_super.call(this);
(function () {
(() => {
var obj = {
// Ideally, we would capture this. But the reference is
// illegal, and not capturing this is consistent with
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/computedPropertyNames31.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var C = (function (_super) {
}
C.prototype.foo = function () {
var _this = this;
(function () {
(() => {
var obj = {
[_super.prototype.bar.call(_this)]() {
} // needs capture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ var o = {
["" + 0](y) {
return y.length;
},
["" + 1]: function (y) { return y.length; }
["" + 1]: y => { return y.length; }
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ var o = {
[+"foo"](y) {
return y.length;
},
[+"bar"]: function (y) { return y.length; }
[+"bar"]: y => { return y.length; }
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ var o = {
[+"foo"](y) {
return y.length;
},
[+"bar"]: function (y) { return y.length; }
[+"bar"]: y => { return y.length; }
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ foo({
//// [computedPropertyNamesContextualType6.js]
foo({
p: "",
0: function () {
0: () => {
},
["hi" + "bye"]: true,
[0 + 1]: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ foo({
//// [computedPropertyNamesContextualType7.js]
foo({
p: "",
0: function () {
0: () => {
},
["hi" + "bye"]: true,
[0 + 1]: 0,
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/constDeclarations-scopes.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ function F() {
const c = 0;
n = c;
}
var F2 = function () {
var F2 = () => {
const c = 0;
n = c;
};
Expand Down Expand Up @@ -272,7 +272,7 @@ var o = {
const c = 0;
n = c;
},
f2: function () {
f2: () => {
const c = 0;
n = c;
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/constDeclarations-validContexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ const c18 = 0;
function F() {
const c19 = 0;
}
var F2 = function () {
var F2 = () => {
const c20 = 0;
};
var F3 = function () {
Expand Down Expand Up @@ -226,7 +226,7 @@ var o = {
f() {
const c28 = 0;
},
f2: function () {
f2: () => {
const c29 = 0;
}
};
29 changes: 29 additions & 0 deletions tests/baselines/reference/emitArrowFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//// [emitArrowFunction.ts]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be really helpful if the baseline stated the target, just like the original test.

var f1 = () => { }
var f2 = (x: string, y: string) => { }
var f3 = (x: string, y: number, ...rest) => { }
var f4 = (x: string, y: number, z = 10) => { }
function foo(func: () => boolean) { }
foo(() => true);
foo(() => { return false; });

//// [emitArrowFunction.js]
var f1 = function () {
};
var f2 = function (x, y) {
};
var f3 = function (x, y) {
var rest = [];
for (var _i = 2; _i < arguments.length; _i++) {
rest[_i - 2] = arguments[_i];
}
};
var f4 = function (x, y, z) {
if (z === void 0) { z = 10; }
};
function foo(func) {
}
foo(function () { return true; });
foo(function () {
return false;
});
39 changes: 39 additions & 0 deletions tests/baselines/reference/emitArrowFunction.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
=== tests/cases/conformance/es6/arrowFunction/emitArrowFunction.ts ===
var f1 = () => { }
>f1 : () => void
>() => { } : () => void

var f2 = (x: string, y: string) => { }
>f2 : (x: string, y: string) => void
>(x: string, y: string) => { } : (x: string, y: string) => void
>x : string
>y : string

var f3 = (x: string, y: number, ...rest) => { }
>f3 : (x: string, y: number, ...rest: any[]) => void
>(x: string, y: number, ...rest) => { } : (x: string, y: number, ...rest: any[]) => void
>x : string
>y : number
>rest : any[]

var f4 = (x: string, y: number, z = 10) => { }
>f4 : (x: string, y: number, z?: number) => void
>(x: string, y: number, z = 10) => { } : (x: string, y: number, z?: number) => void
>x : string
>y : number
>z : number

function foo(func: () => boolean) { }
>foo : (func: () => boolean) => void
>func : () => boolean

foo(() => true);
>foo(() => true) : void
>foo : (func: () => boolean) => void
>() => true : () => boolean

foo(() => { return false; });
>foo(() => { return false; }) : void
>foo : (func: () => boolean) => void
>() => { return false; } : () => boolean

13 changes: 13 additions & 0 deletions tests/baselines/reference/emitArrowFunctionAsIs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//// [emitArrowFunctionAsIs.ts]
var arrow1 = a => { };
var arrow2 = (a) => { };

var arrow3 = (a, b) => { };

//// [emitArrowFunctionAsIs.js]
var arrow1 = function (a) {
};
var arrow2 = function (a) {
};
var arrow3 = function (a, b) {
};
17 changes: 17 additions & 0 deletions tests/baselines/reference/emitArrowFunctionAsIs.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
=== tests/cases/conformance/es6/arrowFunction/emitArrowFunctionAsIs.ts ===
var arrow1 = a => { };
>arrow1 : (a: any) => void
>a => { } : (a: any) => void
>a : any

var arrow2 = (a) => { };
>arrow2 : (a: any) => void
>(a) => { } : (a: any) => void
>a : any

var arrow3 = (a, b) => { };
>arrow3 : (a: any, b: any) => void
>(a, b) => { } : (a: any, b: any) => void
>a : any
>b : any

13 changes: 13 additions & 0 deletions tests/baselines/reference/emitArrowFunctionAsIsES6.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//// [emitArrowFunctionAsIsES6.ts]
var arrow1 = a => { };
var arrow2 = (a) => { };

var arrow3 = (a, b) => { };

//// [emitArrowFunctionAsIsES6.js]
var arrow1 = a => {
};
var arrow2 = (a) => {
};
var arrow3 = (a, b) => {
};
Loading