Skip to content

Commit ddb63d2

Browse files
Emit computed properties in ES3/ES5 properly.
1 parent d6b2c6d commit ddb63d2

File tree

47 files changed

+545
-253
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+545
-253
lines changed

src/compiler/emitter.ts

+162-11
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ module ts {
271271
});
272272
}
273273

274-
function getAllAccessorDeclarations(node: ClassDeclaration, accessor: AccessorDeclaration) {
274+
function getAllAccessorDeclarations(declarations: NodeArray<Declaration>, accessor: AccessorDeclaration) {
275275
var firstAccessor: AccessorDeclaration;
276276
var getAccessor: AccessorDeclaration;
277277
var setAccessor: AccessorDeclaration;
@@ -288,7 +288,7 @@ module ts {
288288
}
289289
}
290290
else {
291-
forEach(node.members,(member: Declaration) => {
291+
forEach(declarations, (member: Declaration) => {
292292
if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) &&
293293
(<Identifier>member.name).text === (<Identifier>accessor.name).text &&
294294
(member.flags & NodeFlags.Static) === (accessor.flags & NodeFlags.Static)) {
@@ -1116,8 +1116,8 @@ module ts {
11161116
if (hasDynamicName(node)) {
11171117
return;
11181118
}
1119-
1120-
var accessors = getAllAccessorDeclarations(<ClassDeclaration>node.parent, node);
1119+
1120+
var accessors = getAllAccessorDeclarations((<ClassDeclaration>node.parent).members, node);
11211121
if (node === accessors.firstAccessor) {
11221122
emitJsDocComments(accessors.getAccessor);
11231123
emitJsDocComments(accessors.setAccessor);
@@ -2220,7 +2220,10 @@ module ts {
22202220

22212221
// This function specifically handles numeric/string literals for enum and accessor 'identifiers'.
22222222
// In a sense, it does not actually emit identifiers as much as it declares a name for a specific property.
2223+
// For example, this is utilized when feeding in a result to Object.defineProperty.
22232224
function emitExpressionForPropertyName(node: DeclarationName) {
2225+
Debug.assert(node.kind !== SyntaxKind.BindingElement);
2226+
22242227
if (node.kind === SyntaxKind.StringLiteral) {
22252228
emitLiteral(<LiteralExpression>node);
22262229
}
@@ -2417,22 +2420,167 @@ module ts {
24172420
}
24182421
}
24192422

2420-
function emitObjectLiteral(node: ObjectLiteralExpression) {
2423+
function emitObjectLiteralBody(node: ObjectLiteralExpression, numElements: number) {
24212424
write("{");
2422-
var properties = node.properties;
2423-
if (properties.length) {
2425+
2426+
if (numElements > 0) {
2427+
var properties = node.properties;
24242428
var multiLine = (node.flags & NodeFlags.MultiLine) !== 0;
24252429
if (!multiLine) {
24262430
write(" ");
24272431
}
2428-
emitList(properties, 0, properties.length, /*multiLine*/ multiLine,
2432+
emitList(properties, 0, numElements, /*multiLine*/ multiLine,
24292433
/*trailingComma*/ properties.hasTrailingComma && languageVersion >= ScriptTarget.ES5);
24302434
if (!multiLine) {
24312435
write(" ");
24322436
}
24332437
}
2438+
24342439
write("}");
24352440
}
2441+
2442+
function emitDownlevelObjectLiteralWithComputedProperties(node: ObjectLiteralExpression, firstComputedPropertyIndex: number) {
2443+
var multiLine = (node.flags & NodeFlags.MultiLine) !== 0;
2444+
var properties = node.properties;
2445+
2446+
write("(");
2447+
2448+
// For computed properties, we need to create a unique handle to the object
2449+
// literal so we can modify it without risking internal assignments tainting the object.
2450+
var tempVar = createTempVariable(node);
2451+
recordTempDeclaration(tempVar);
2452+
2453+
// Write out the first non-computed properties
2454+
// (or all properties if none of them are computed),
2455+
// then emit the rest through indexing on the temp variable.
2456+
emit(tempVar)
2457+
write(" = ");
2458+
emitObjectLiteralBody(node, firstComputedPropertyIndex);
2459+
2460+
if (multiLine) {
2461+
increaseIndent();
2462+
}
2463+
2464+
for (var i = firstComputedPropertyIndex, n = properties.length; i < n; i++) {
2465+
writeSeparator();
2466+
2467+
var property = properties[i];
2468+
2469+
emitStart(property)
2470+
if (property.kind === SyntaxKind.GetAccessor || property.kind === SyntaxKind.SetAccessor) {
2471+
// TODO (drosen): Reconcile with 'emitMemberFunctions'.
2472+
var accessors = getAllAccessorDeclarations(node.properties, <AccessorDeclaration>property);
2473+
write("Object.defineProperty(");
2474+
emit(tempVar);
2475+
write(", ");
2476+
emitStart(node.name);
2477+
emitExpressionForPropertyName(property.name);
2478+
emitEnd(property.name);
2479+
write(", {");
2480+
increaseIndent();
2481+
if (accessors.getAccessor) {
2482+
writeLine()
2483+
emitLeadingComments(accessors.getAccessor);
2484+
write("get: ");
2485+
emitStart(accessors.getAccessor);
2486+
write("function ");
2487+
emitSignatureAndBody(accessors.getAccessor);
2488+
emitEnd(accessors.getAccessor);
2489+
emitTrailingComments(accessors.getAccessor);
2490+
write(",");
2491+
}
2492+
if (accessors.setAccessor) {
2493+
writeLine();
2494+
emitLeadingComments(accessors.setAccessor);
2495+
write("set: ");
2496+
emitStart(accessors.setAccessor);
2497+
write("function ");
2498+
emitSignatureAndBody(accessors.setAccessor);
2499+
emitEnd(accessors.setAccessor);
2500+
emitTrailingComments(accessors.setAccessor);
2501+
write(",");
2502+
}
2503+
writeLine();
2504+
write("enumerable: true,");
2505+
writeLine();
2506+
write("configurable: true");
2507+
decreaseIndent();
2508+
writeSeparator();
2509+
write("})");
2510+
emitEnd(property);
2511+
}
2512+
else {
2513+
emitLeadingComments(property);
2514+
emitStart(property.name);
2515+
emit(tempVar);
2516+
emitMemberAccessForPropertyName(property.name);
2517+
emitEnd(property.name);
2518+
2519+
write(" = ");
2520+
2521+
if (property.kind === SyntaxKind.PropertyAssignment) {
2522+
emit((<PropertyAssignment>property).initializer);
2523+
}
2524+
else if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
2525+
emitExpressionIdentifier((<ShorthandPropertyAssignment>property).name);
2526+
}
2527+
else if (property.kind === SyntaxKind.MethodDeclaration) {
2528+
emitFunctionDeclaration(<MethodDeclaration>property);
2529+
}
2530+
else {
2531+
Debug.fail("ObjectLiteralElement type not accounted for: " + property.kind);
2532+
}
2533+
}
2534+
2535+
emitEnd(property);
2536+
}
2537+
2538+
writeSeparator();
2539+
emit(tempVar);
2540+
2541+
write(")");
2542+
2543+
if (multiLine) {
2544+
decreaseIndent();
2545+
}
2546+
2547+
function writeSeparator() {
2548+
if (multiLine) {
2549+
write(",");
2550+
writeLine();
2551+
}
2552+
else {
2553+
write(", ");
2554+
}
2555+
}
2556+
}
2557+
2558+
function emitObjectLiteral(node: ObjectLiteralExpression) {
2559+
if (languageVersion >= ScriptTarget.ES6) {
2560+
emitObjectLiteralBody(node, node.properties.length);
2561+
return;
2562+
}
2563+
2564+
var properties = node.properties;
2565+
2566+
// Find the first computed property.
2567+
// Everything until that point can be emitted as part of the initial object literal.
2568+
var numInitialNonComputedProperties = properties.length;
2569+
forEach(properties, (property, i) => {
2570+
if (hasDynamicName(properties[i])) {
2571+
numInitialNonComputedProperties = i;
2572+
return true;
2573+
}
2574+
});
2575+
2576+
var hasComputedProperty = numInitialNonComputedProperties !== properties.length;
2577+
if (hasComputedProperty) {
2578+
emitDownlevelObjectLiteralWithComputedProperties(node, numInitialNonComputedProperties);
2579+
}
2580+
else {
2581+
emitObjectLiteralBody(node, properties.length);
2582+
}
2583+
}
24362584

24372585
function emitComputedPropertyName(node: ComputedPropertyName) {
24382586
write("[");
@@ -2464,7 +2612,7 @@ module ts {
24642612
// export var obj = { y };
24652613
// }
24662614
// The short-hand property in obj need to emit as such ... = { y : m.y } regardless of the TargetScript version
2467-
if (languageVersion < ScriptTarget.ES6 || resolver.getExpressionNamePrefix(node.name)) {
2615+
if (languageVersion <= ScriptTarget.ES5 || resolver.getExpressionNamePrefix(node.name)) {
24682616
// Emit identifier as an identifier
24692617
write(": ");
24702618
// Even though this is stored as identifier treat it as an expression
@@ -3446,6 +3594,7 @@ module ts {
34463594
}
34473595

34483596
function emitMemberAccessForPropertyName(memberName: DeclarationName) {
3597+
// TODO: (jfreeman,drosen): comment on why this is emitNode instead of emit here.
34493598
if (memberName.kind === SyntaxKind.StringLiteral || memberName.kind === SyntaxKind.NumericLiteral) {
34503599
write("[");
34513600
emitNode(memberName);
@@ -3495,13 +3644,14 @@ module ts {
34953644
emitLeadingComments(member);
34963645
emitStart(member);
34973646
emitStart((<MethodDeclaration>member).name);
3498-
emitNode(node.name);
3647+
emitNode(node.name); // TODO (shkamat,drosen): comment for why emitNode instead of emit.
34993648
if (!(member.flags & NodeFlags.Static)) {
35003649
write(".prototype");
35013650
}
35023651
emitMemberAccessForPropertyName((<MethodDeclaration>member).name);
35033652
emitEnd((<MethodDeclaration>member).name);
35043653
write(" = ");
3654+
// TODO (drosen): Should we performing emitStart twice on emitStart(member)?
35053655
emitStart(member);
35063656
emitFunctionDeclaration(<MethodDeclaration>member);
35073657
emitEnd(member);
@@ -3510,7 +3660,7 @@ module ts {
35103660
emitTrailingComments(member);
35113661
}
35123662
else if (member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) {
3513-
var accessors = getAllAccessorDeclarations(node, <AccessorDeclaration>member);
3663+
var accessors = getAllAccessorDeclarations(node.members, <AccessorDeclaration>member);
35143664
if (member === accessors.firstAccessor) {
35153665
writeLine();
35163666
emitStart(member);
@@ -3521,6 +3671,7 @@ module ts {
35213671
write(".prototype");
35223672
}
35233673
write(", ");
3674+
// TODO: Shouldn't emitStart on name occur *here*?
35243675
emitExpressionForPropertyName((<AccessorDeclaration>member).name);
35253676
emitEnd((<AccessorDeclaration>member).name);
35263677
write(", {");

tests/baselines/reference/FunctionDeclaration8_es6.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
var v = { [yield]: foo }
33

44
//// [FunctionDeclaration8_es6.js]
5-
var v = { [yield]: foo };
5+
var v = (_a = {}, _a[yield] = foo, _a);
6+
var _a;

tests/baselines/reference/FunctionDeclaration9_es6.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ function * foo() {
55

66
//// [FunctionDeclaration9_es6.js]
77
function foo() {
8-
var v = { []: foo };
8+
var v = (_a = {}, _a[] = foo, _a);
9+
var _a;
910
}

tests/baselines/reference/FunctionPropertyAssignments5_es6.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
var v = { *[foo()]() { } }
33

44
//// [FunctionPropertyAssignments5_es6.js]
5-
var v = { [foo()]: function () {
6-
} };
5+
var v = (_a = {}, _a[foo()] = function () {
6+
}, _a);
7+
var _a;

tests/baselines/reference/computedPropertyNames10_ES5.js

+15-14
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,28 @@ var v = {
2020
var s;
2121
var n;
2222
var a;
23-
var v = {
24-
[s]: function () {
23+
var v = (_a = {},
24+
_a[s] = function () {
2525
},
26-
[n]: function () {
26+
_a[n] = function () {
2727
},
28-
[s + s]: function () {
28+
_a[s + s] = function () {
2929
},
30-
[s + n]: function () {
30+
_a[s + n] = function () {
3131
},
32-
[+s]: function () {
32+
_a[+s] = function () {
3333
},
34-
[""]: function () {
34+
_a[""] = function () {
3535
},
36-
[0]: function () {
36+
_a[0] = function () {
3737
},
38-
[a]: function () {
38+
_a[a] = function () {
3939
},
40-
[true]: function () {
40+
_a[true] = function () {
4141
},
42-
["hello bye"]: function () {
42+
_a["hello bye"] = function () {
4343
},
44-
["hello " + a + " bye"]: function () {
45-
}
46-
};
44+
_a["hello " + a + " bye"] = function () {
45+
},
46+
_a);
47+
var _a;

0 commit comments

Comments
 (0)