diff --git a/lib/coffee-script/nodes.js b/lib/coffee-script/nodes.js index b707e0d7..054f3186 100644 --- a/lib/coffee-script/nodes.js +++ b/lib/coffee-script/nodes.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 1.6.3 (function() { - var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, CodeFragment, Comment, Existence, Extends, For, HEXNUM, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, last, locationDataToString, merge, multident, parseNum, some, starts, throwSyntaxError, unfoldSoak, utility, _ref, _ref1, _ref2, _ref3, + var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, CodeFragment, Comment, Existence, Extends, For, HEXNUM, IDENTIFIER, IDENTIFIER_STR, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, last, locationDataToString, merge, multident, parseNum, some, starts, throwSyntaxError, unfoldSoak, utility, _ref, _ref1, _ref2, _ref3, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, @@ -678,8 +678,16 @@ return !!this.properties.length; }; + Value.prototype.bareLiteral = function(type) { + return !this.properties.length && this.base instanceof type; + }; + Value.prototype.isArray = function() { - return !this.properties.length && this.base instanceof Arr; + return this.bareLiteral(Arr); + }; + + Value.prototype.isRange = function() { + return this.bareLiteral(Range); }; Value.prototype.isComplex = function() { @@ -691,11 +699,15 @@ }; Value.prototype.isSimpleNumber = function() { - return this.base instanceof Literal && SIMPLENUM.test(this.base.value); + return this.bareLiteral(Literal) && SIMPLENUM.test(this.base.value); }; Value.prototype.isString = function() { - return this.base instanceof Literal && IS_STRING.test(this.base.value); + return this.bareLiteral(Literal) && IS_STRING.test(this.base.value); + }; + + Value.prototype.isRegex = function() { + return this.bareLiteral(Literal) && IS_REGEX.test(this.base.value); }; Value.prototype.isAtomic = function() { @@ -710,6 +722,10 @@ return true; }; + Value.prototype.isNotCallable = function() { + return this.isSimpleNumber() || this.isString() || this.isRegex() || this.isArray() || this.isRange() || this.isSplice() || this.isObject(); + }; + Value.prototype.isStatement = function(o) { return !this.properties.length && this.base.isStatement(o); }; @@ -847,6 +863,9 @@ this.isNew = false; this.isSuper = variable === 'super'; this.variable = this.isSuper ? null : variable; + if (variable instanceof Value && variable.isNotCallable()) { + variable.error("literal is not a function"); + } } Call.prototype.children = ['variable', 'args']; @@ -1742,7 +1761,7 @@ fromDecl = fromRef = '0'; } if (to) { - if ((from != null ? from.isSimpleNumber() : void 0) && to.isSimpleNumber()) { + if (from && from instanceof Value && (from != null ? from.isSimpleNumber() : void 0) && to instanceof Value && to.isSimpleNumber()) { to = +to.compile(o) - +fromRef; if (!exclusive) { to += 1; @@ -3057,6 +3076,8 @@ IS_STRING = /^['"]/; + IS_REGEX = /^\//; + utility = function(name) { var ref; ref = "__" + name; diff --git a/src/nodes.coffee b/src/nodes.coffee index 525bb8ec..e5783dc4 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -469,17 +469,25 @@ exports.Value = class Value extends Base hasProperties: -> !!@properties.length + bareLiteral: (type) -> + not @properties.length and @base instanceof type + # Some boolean checks for the benefit of other nodes. - isArray : -> not @properties.length and @base instanceof Arr + isArray : -> @bareLiteral(Arr) + isRange : -> @bareLiteral(Range) isComplex : -> @hasProperties() or @base.isComplex() isAssignable : -> @hasProperties() or @base.isAssignable() - isSimpleNumber : -> @base instanceof Literal and SIMPLENUM.test @base.value - isString : -> @base instanceof Literal and IS_STRING.test @base.value + isSimpleNumber : -> @bareLiteral(Literal) and SIMPLENUM.test @base.value + isString : -> @bareLiteral(Literal) and IS_STRING.test @base.value + isRegex : -> @bareLiteral(Literal) and IS_REGEX.test @base.value isAtomic : -> for node in @properties.concat @base return no if node.soak or node instanceof Call yes + isNotCallable : -> @isSimpleNumber() or @isString() or @isRegex() or + @isArray() or @isRange() or @isSplice() or @isObject() + isStatement : (o) -> not @properties.length and @base.isStatement o assigns : (name) -> not @properties.length and @base.assigns name jumps : (o) -> not @properties.length and @base.jumps o @@ -570,6 +578,8 @@ exports.Call = class Call extends Base @isNew = false @isSuper = variable is 'super' @variable = if @isSuper then null else variable + if variable instanceof Value and variable.isNotCallable() + variable.error "literal is not a function" children: ['variable', 'args'] @@ -1257,7 +1267,8 @@ exports.Assign = class Assign extends Base else fromDecl = fromRef = '0' if to - if from?.isSimpleNumber() and to.isSimpleNumber() + if from and from instanceof Value and from?.isSimpleNumber() and + to instanceof Value and to.isSimpleNumber() to = +to.compile(o) - +fromRef to += 1 unless exclusive else @@ -2182,8 +2193,9 @@ METHOD_DEF = /// $ /// -# Is a literal value a string? +# Is a literal value a string/regex? IS_STRING = /^['"]/ +IS_REGEX = /^\// # Utility Functions # ----------------- diff --git a/test/function_invocation.coffee b/test/function_invocation.coffee index 4f514eab..efda21b2 100644 --- a/test/function_invocation.coffee +++ b/test/function_invocation.coffee @@ -9,6 +9,9 @@ # shared identity function id = (_) -> if arguments.length is 1 then _ else [arguments...] +# helper to assert that a string should fail compilation +cantCompile = (code) -> + throws -> CoffeeScript.compile code test "basic argument passing", -> @@ -649,3 +652,25 @@ test "Loose tokens inside of explicit call lists", -> bar = first( first one: 1) eq bar.one, 1 + +test "Non-callable literals shouldn't compile", -> + cantCompile '1(2)' + cantCompile '1 2' + cantCompile '/t/(2)' + cantCompile '/t/ 2' + cantCompile '///t///(2)' + cantCompile '///t/// 2' + cantCompile "''(2)" + cantCompile "'' 2" + cantCompile '""(2)' + cantCompile '"" 2' + cantCompile '""""""(2)' + cantCompile '"""""" 2' + cantCompile '{}(2)' + cantCompile '{} 2' + cantCompile '[](2)' + cantCompile '[] 2' + cantCompile '[2..9] 2' + cantCompile '[2..9](2)' + cantCompile '[1..10][2..9] 2' + cantCompile '[1..10][2..9](2)' diff --git a/test/slicing_and_splicing.coffee b/test/slicing_and_splicing.coffee index 5d37d840..a126ec00 100644 --- a/test/slicing_and_splicing.coffee +++ b/test/slicing_and_splicing.coffee @@ -144,3 +144,12 @@ test "#1723: operator precedence in unbounded splice compilation", -> list = [0..9] list[..if n then n else 0] = n arrayEq [n..9], list + +test "#2953: methods on endpoints in assignment from array splice literal", -> + list = [0..9] + + Number.prototype.same = -> this + list[1.same()...9.same()] = 5 + delete Number.prototype.same + + arrayEq [0, 5, 9], list diff --git a/test/soaks.coffee b/test/soaks.coffee index 97130c4d..330a99b2 100644 --- a/test/soaks.coffee +++ b/test/soaks.coffee @@ -130,5 +130,5 @@ test "soaked constructor invocations with caching and property access", -> eq 1, semaphore test "soaked function invocation safe on non-functions", -> - eq undefined, 0?(1) - eq undefined, 0? 1, 2 + eq undefined, (0)?(1) + eq undefined, (0)? 1, 2