1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

Fix #4609: support new.target (#5106)

* support new.target

* check token type
This commit is contained in:
Julian Rosse 2018-09-16 16:52:47 -04:00 committed by Geoffrey Booth
parent c4245e50c2
commit 6225627579
6 changed files with 68 additions and 1 deletions

View file

@ -202,6 +202,8 @@
} else if (tag === 'PROPERTY' && prev) {
if (prev.spaced && (ref7 = prev[0], indexOf.call(CALLABLE, ref7) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens.length > 1 && ((ref8 = this.tokens[this.tokens.length - 2][0]) !== '.' && ref8 !== '?.' && ref8 !== '@')) {
this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
} else if (prev[0] === '.' && this.tokens.length > 1 && (prevprev = this.tokens[this.tokens.length - 2])[0] === 'UNARY' && prevprev[1] === 'new') {
prevprev[0] = 'IDENTIFIER';
} else if (this.tokens.length > 2) {
prevprev = this.tokens[this.tokens.length - 2];
if (((ref9 = prev[0]) === '@' || ref9 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && ((ref10 = this.tokens[this.tokens.length - 3][0]) !== '.' && ref10 !== '?.' && ref10 !== '@')) {

View file

@ -1445,6 +1445,7 @@
// evaluate anything twice when building the soak chain.
compileNode(o) {
var fragments, j, len1, prop, props;
this.checkNewTarget(o);
this.base.front = this.front;
props = this.properties;
if (props.length && (this.base.cached != null)) {
@ -1468,6 +1469,19 @@
return fragments;
}
checkNewTarget(o) {
if (!(this.base instanceof IdentifierLiteral && this.base.value === 'new' && this.properties.length)) {
return;
}
if (this.properties[0] instanceof Access && this.properties[0].name.value === 'target') {
if (o.scope.parent == null) {
return this.error("new.target can only occur inside functions");
}
} else {
return this.error("the only valid meta property for new is new.target");
}
}
// Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
unfoldSoak(o) {
return this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (() => {

View file

@ -196,6 +196,8 @@ exports.Lexer = class Lexer
@tokens.length > 1 and @tokens[@tokens.length - 2][0] not in ['.', '?.', '@']
@error "'#{prev[1]}' cannot be used as a keyword, or as a function call
without parentheses", prev[2]
else if prev[0] is '.' and @tokens.length > 1 and (prevprev = @tokens[@tokens.length - 2])[0] is 'UNARY' and prevprev[1] is 'new'
prevprev[0] = 'IDENTIFIER'
else if @tokens.length > 2
prevprev = @tokens[@tokens.length - 2]
if prev[0] in ['@', 'THIS'] and prevprev and prevprev.spaced and

View file

@ -965,6 +965,7 @@ exports.Value = class Value extends Base
# operators `?.` interspersed. Then we have to take care not to accidentally
# evaluate anything twice when building the soak chain.
compileNode: (o) ->
@checkNewTarget o
@base.front = @front
props = @properties
if props.length and @base.cached?
@ -984,6 +985,14 @@ exports.Value = class Value extends Base
fragments
checkNewTarget: (o) ->
return unless @base instanceof IdentifierLiteral and @base.value is 'new' and @properties.length
if @properties[0] instanceof Access and @properties[0].name.value is 'target'
unless o.scope.parent?
@error "new.target can only occur inside functions"
else
@error "the only valid meta property for new is new.target"
# Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
unfoldSoak: (o) ->
@unfoldedSoak ?= do =>

View file

@ -1899,4 +1899,26 @@ test "#4868: Incorrect Cant call super with @params error", ->
super class then constructor: (@a) -> @a = 3
d = new (new D).c
eq 3, d.a
eq 3, d.a
test "#4609: Support new.target", ->
class A
constructor: ->
@calledAs = new.target.name
class B extends A
b = new B
eq b.calledAs, 'B'
newTarget = null
Foo = ->
newTarget = !!new.target
Foo()
eq newTarget, no
newTarget = null
new Foo()
eq newTarget, yes

View file

@ -1907,3 +1907,21 @@ test "#3933: prevent implicit calls when cotrol flow is missing `THEN`", ->
when a ->
^^
'''
test "`new.target` outside of a function", ->
assertErrorFormat '''
new.target
''', '''
[stdin]:1:1: error: new.target can only occur inside functions
new.target
^^^^^^^^^^
'''
test "`new.target` is only allowed meta property", ->
assertErrorFormat '''
-> new.something
''', '''
[stdin]:1:4: error: the only valid meta property for new is new.target
-> new.something
^^^^^^^^^^^^^
'''