diff --git a/lib/coffee-script/helpers.js b/lib/coffee-script/helpers.js index b0a997b8d6..18a2fad20b 100644 --- a/lib/coffee-script/helpers.js +++ b/lib/coffee-script/helpers.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 1.3.3 (function() { - var extend, flatten; + var extend, flatten, _ref; exports.starts = function(string, literal, start) { return literal === string.substr(start, literal.length); @@ -74,4 +74,15 @@ return array[array.length - (back || 0) - 1]; }; + exports.some = (_ref = Array.prototype.some) != null ? _ref : function(fn) { + var e, _i, _len; + for (_i = 0, _len = this.length; _i < _len; _i++) { + e = this[_i]; + if (fn(e)) { + return true; + } + } + return false; + }; + }).call(this); diff --git a/lib/coffee-script/nodes.js b/lib/coffee-script/nodes.js index 799b68eb47..9b2b43c879 100644 --- a/lib/coffee-script/nodes.js +++ b/lib/coffee-script/nodes.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 1.3.3 (function() { - var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, 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, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, unfoldSoak, utility, _ref, _ref1, + var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, 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, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, some, starts, unfoldSoak, utility, _ref, _ref1, __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; }; @@ -9,7 +9,7 @@ _ref = require('./lexer'), RESERVED = _ref.RESERVED, STRICT_PROSCRIBED = _ref.STRICT_PROSCRIBED; - _ref1 = require('./helpers'), compact = _ref1.compact, flatten = _ref1.flatten, extend = _ref1.extend, merge = _ref1.merge, del = _ref1.del, starts = _ref1.starts, ends = _ref1.ends, last = _ref1.last; + _ref1 = require('./helpers'), compact = _ref1.compact, flatten = _ref1.flatten, extend = _ref1.extend, merge = _ref1.merge, del = _ref1.del, starts = _ref1.starts, ends = _ref1.ends, last = _ref1.last, some = _ref1.some; exports.extend = extend; @@ -1172,9 +1172,42 @@ Obj.prototype.children = ['properties']; Obj.prototype.compileNode = function(o) { - var i, idt, indent, join, lastNoncom, node, obj, prop, propName, propNames, props, _i, _j, _len, _len1, _ref2; + var i, idt, indent, isDuplicate, join, lastNoncom, node, normalise, obj, prop, propName, propNames, props, _i, _j, _len, _len1, _ref2; props = this.properties; propNames = []; + normalise = function(s) { + switch (s[0]) { + case '"': + return s.slice(1, -1).replace(/\\"/g, '"'); + case "'": + return s.slice(1, -1).replace(/\\'/g, "'"); + } + }; + isDuplicate = function(x) { + var mx; + mx = x.match(/^['"]/); + return function(y) { + var my; + if (y === x || +y === +x) { + return true; + } + my = y.match(/^['"]/); + if (mx && my) { + if (normalise(x) === normalise(y)) { + return true; + } + } else if (mx) { + if (y === x.slice(1, -1)) { + return true; + } + } else if (my) { + if (x === y.slice(1, -1)) { + return true; + } + } + return false; + }; + }; _ref2 = this.properties; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { prop = _ref2[_i]; @@ -1183,7 +1216,7 @@ } if (prop != null) { propName = prop.unwrapAll().value.toString(); - if (__indexOf.call(propNames, propName) >= 0) { + if (some.call(propNames, isDuplicate(propName))) { throw SyntaxError("multiple object literal properties named \"" + propName + "\""); } propNames.push(propName); diff --git a/src/helpers.coffee b/src/helpers.coffee index 20916eafc5..924c055610 100644 --- a/src/helpers.coffee +++ b/src/helpers.coffee @@ -54,3 +54,8 @@ exports.del = (obj, key) -> # Gets the last item of an array(-like) object. exports.last = (array, back) -> array[array.length - (back or 0) - 1] + +# Typical Array::some +exports.some = Array::some ? (fn) -> + return true for e in this when fn e + false diff --git a/src/nodes.coffee b/src/nodes.coffee index 1573ac55a5..5e96bae60c 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -7,7 +7,7 @@ {RESERVED, STRICT_PROSCRIBED} = require './lexer' # Import the helpers we plan to use. -{compact, flatten, extend, merge, del, starts, ends, last} = require './helpers' +{compact, flatten, extend, merge, del, starts, ends, last, some} = require './helpers' exports.extend = extend # for parser @@ -802,11 +802,26 @@ exports.Obj = class Obj extends Base compileNode: (o) -> props = @properties propNames = [] + normalise = (s) -> switch s[0] + when '"' then s[1...-1].replace /\\"/g, '"' + when "'" then s[1...-1].replace /\\'/g, "'" + isDuplicate = (x) -> + mx = x.match /^['"]/ + (y) -> + return true if y is x or +y is +x + my = y.match /^['"]/ + if mx and my + return true if normalise(x) is normalise y + else if mx + return true if y is x[1...-1] + else if my + return true if x is y[1...-1] + false for prop in @properties prop = prop.variable if prop.isComplex() if prop? propName = prop.unwrapAll().value.toString() - if propName in propNames + if some.call propNames, isDuplicate propName throw SyntaxError "multiple object literal properties named \"#{propName}\"" propNames.push propName return (if @front then '({})' else '{}') unless props.length diff --git a/test/strict.coffee b/test/strict.coffee index 20894f16f8..1ca3a57b11 100644 --- a/test/strict.coffee +++ b/test/strict.coffee @@ -57,9 +57,23 @@ test "octal escape sequences prohibited", -> test "duplicate property definitions in object literals are prohibited", -> - strict 'o = {x:1,x:1}' + strict 'o = {x:1, x:1}' strict 'x = 1; o = {x, x: 2}' +test "#2333: more duplicate property prohibitions", -> + strict '{a:0, "a":0}' + strict "{'a':0, a:0}" + strict '{\'a\':0, "a":0}' + strict '{0:0, 0x0:0}' + strict '{0:0, "\\x30":0}' + strict '{.1:0, 0.1:0}' + strict '{.1:0, 1e-1:0}' + strict '{100:0, 1e2:0}' + strict '{"\\0":0, "\\x00":0}' + strict 'a = 0; {a, "a":0}' + strictOk '{0:0, "0x0":0}' + strictOk '{"a":0, "\'a\'":0}' + test "duplicate formal parameters are prohibited", -> nonce = {}