Allow computed class properties (#5206)

* fix broken JS output

* static computed properties
This commit is contained in:
Julian Rosse
2019-04-28 18:45:57 -04:00
committed by Geoffrey Booth
parent 0574b664e8
commit 71750554c3
10 changed files with 266 additions and 198 deletions

View File

@@ -298,6 +298,12 @@
function() {
return new Value(new ComputedPropertyName($2));
}),
o('@ [ Expression ]',
function() {
return new Value(LOC(1)(new ThisLiteral($1)),
[LOC(3)(new ComputedPropertyName($3))],
'this');
}),
o('AlphaNumeric')
],
// Object literal spread properties.

View File

@@ -2993,7 +2993,7 @@
// The class scope is not available yet, so return the assignment to update later
assign = this.externalCtor = new Assign(new Value, value);
} else if (!assign.variable.this) {
name = new (base.shouldCache() ? Index : Access)(base);
name = base instanceof ComputedPropertyName ? new Index(base.value) : new (base.shouldCache() ? Index : Access)(base);
prototype = new Access(new PropertyName('prototype'));
variable = new Value(new ThisLiteral(), [prototype, name]);
assign.variable = variable;

File diff suppressed because one or more lines are too long

View File

@@ -196,16 +196,23 @@
// The lexer has tagged the opening bracket of an indexing operation call.
// Match it with its paired close.
closeOpenIndexes() {
var action, condition;
var action, condition, startToken;
startToken = null;
condition = function(token, i) {
var ref;
return (ref = token[0]) === ']' || ref === 'INDEX_END';
};
action = function(token, i) {
return token[0] = 'INDEX_END';
if (this.tokens.length >= i && this.tokens[i + 1][0] === ':') {
startToken[0] = '[';
return token[0] = ']';
} else {
return token[0] = 'INDEX_END';
}
};
return this.scanTokens(function(token, i) {
if (token[0] === 'INDEX_START') {
startToken = token;
this.detectEnd(i + 1, condition, action);
}
return 1;
@@ -281,7 +288,7 @@
stack = [];
start = null;
return this.scanTokens(function(token, i, tokens) {
var endImplicitCall, endImplicitObject, forward, implicitObjectContinues, inControlFlow, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, isImplicit, isImplicitCall, isImplicitObject, k, newLine, nextTag, nextToken, offset, prevTag, prevToken, ref, ref1, ref2, s, sameLine, stackIdx, stackItem, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag;
var endImplicitCall, endImplicitObject, forward, implicitObjectContinues, inControlFlow, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, isImplicit, isImplicitCall, isImplicitObject, k, newLine, nextTag, nextToken, offset, prevTag, prevToken, ref, ref1, ref2, s, sameLine, stackIdx, stackItem, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startIndex, startTag, startsLine, tag;
[tag] = token;
[prevTag] = prevToken = i > 0 ? tokens[i - 1] : [];
[nextTag] = nextToken = i < tokens.length - 1 ? tokens[i + 1] : [];
@@ -481,7 +488,13 @@
var ref1;
switch (false) {
case ref1 = this.tag(i - 1), indexOf.call(EXPRESSION_END, ref1) < 0:
return start[1];
[startTag, startIndex] = start;
if (startTag === '[' && startIndex > 0 && this.tag(startIndex - 1) === '@' && !tokens[startIndex - 1].spaced) {
return startIndex - 1;
} else {
return startIndex;
}
break;
case this.tag(i - 2) !== '@':
return i - 2;
default:

View File

@@ -222,6 +222,7 @@ grammar =
ObjAssignable: [
o 'SimpleObjAssignable'
o '[ Expression ]', -> new Value new ComputedPropertyName $2
o '@ [ Expression ]', -> new Value LOC(1)(new ThisLiteral $1), [LOC(3)(new ComputedPropertyName($3))], 'this'
o 'AlphaNumeric'
]

View File

@@ -2007,7 +2007,11 @@ exports.ExecutableClassBody = class ExecutableClassBody extends Base
# The class scope is not available yet, so return the assignment to update later
assign = @externalCtor = new Assign new Value, value
else if not assign.variable.this
name = new (if base.shouldCache() then Index else Access) base
name =
if base instanceof ComputedPropertyName
new Index base.value
else
new (if base.shouldCache() then Index else Access) base
prototype = new Access new PropertyName 'prototype'
variable = new Value new ThisLiteral(), [ prototype, name ]

View File

@@ -118,14 +118,21 @@ exports.Rewriter = class Rewriter
# The lexer has tagged the opening bracket of an indexing operation call.
# Match it with its paired close.
closeOpenIndexes: ->
startToken = null
condition = (token, i) ->
token[0] in [']', 'INDEX_END']
action = (token, i) ->
token[0] = 'INDEX_END'
if @tokens.length >= i and @tokens[i + 1][0] is ':'
startToken[0] = '['
token[0] = ']'
else
token[0] = 'INDEX_END'
@scanTokens (token, i) ->
@detectEnd i + 1, condition, action if token[0] is 'INDEX_START'
if token[0] is 'INDEX_START'
startToken = token
@detectEnd i + 1, condition, action
1
# Match tags in token stream starting at `i` with `pattern`.
@@ -321,7 +328,12 @@ exports.Rewriter = class Rewriter
if tag is ':'
# Go back to the (implicit) start of the object.
s = switch
when @tag(i - 1) in EXPRESSION_END then start[1]
when @tag(i - 1) in EXPRESSION_END
[startTag, startIndex] = start
if startTag is '[' and startIndex > 0 and @tag(startIndex - 1) is '@' and not tokens[startIndex - 1].spaced
startIndex - 1
else
startIndex
when @tag(i - 2) is '@' then i - 2
else i - 1

View File

@@ -1935,3 +1935,20 @@ test "#5085: Bug: @ reference to class not maintained in do block", ->
eq thisFoo, 'foo assigned in class'
eq thisBar, 'foo assigned in class'
test "#5204: Computed class property", ->
foo = 'bar'
class A
[foo]: 'baz'
a = new A()
eq a.bar, 'baz'
eq A::bar, 'baz'
test "#5204: Static computed class property", ->
foo = 'bar'
qux = 'quux'
class A
@[foo]: 'baz'
@[qux]: -> 3
eq A.bar, 'baz'
eq A.quux(), 3

View File

@@ -855,6 +855,13 @@ test "invalid object keys", ->
{a=2}
^
'''
assertErrorFormat '''
@[a]: 1
''', '''
[stdin]:1:1: error: invalid object key
@[a]: 1
^^^^
'''
test "invalid destructuring default target", ->
assertErrorFormat '''

View File

@@ -903,3 +903,6 @@ test "#4579: Postfix for/while/until in first line of implicit object literals",
baz: 1337
arrayEq [4, 3, 2, 1, 0], six.foo.bar
eq 1337, six.foo.baz
test "#5204: not parsed as static property", ->
doesNotThrow -> CoffeeScript.compile "@ [b]: 2"