From 2ff6c4c3fcb853acf364227e1f58cd3487bb611b Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 Aug 2011 15:42:37 -0400 Subject: [PATCH] fixes #1630: `in` should check indices of the right operand when it can pass a `hasOwnProperty` check for them --- lib/coffee-script/lexer.js | 4 ++-- lib/coffee-script/nodes.js | 25 +++++++++++++++++-------- lib/coffee-script/rewriter.js | 4 ++-- src/nodes.coffee | 23 +++++++++++------------ test/operators.coffee | 3 +++ 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/lib/coffee-script/lexer.js b/lib/coffee-script/lexer.js index 06378a94..ebacc5a5 100644 --- a/lib/coffee-script/lexer.js +++ b/lib/coffee-script/lexer.js @@ -1,8 +1,8 @@ (function() { var ASSIGNED, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref; - var __indexOf = Array.prototype.indexOf || function(item) { + var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { - if (this[i] === item) return i; + if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; }; diff --git a/lib/coffee-script/nodes.js b/lib/coffee-script/nodes.js index 77ebed9b..f555bd76 100644 --- a/lib/coffee-script/nodes.js +++ b/lib/coffee-script/nodes.js @@ -9,7 +9,7 @@ return child; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { - if (this[i] === item) return i; + if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; }; @@ -680,7 +680,6 @@ } Extends.prototype.children = ['child', 'parent']; Extends.prototype.compile = function(o) { - utility('hasProp'); return new Call(new Value(new Literal(utility('extends'))), [this.child, this.parent]).compile(o); }; return Extends; @@ -2083,11 +2082,21 @@ return ifn; }; UTILITIES = { - "extends": 'function(child, parent) {\n for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }\n function ctor() { this.constructor = child; }\n ctor.prototype = parent.prototype;\n child.prototype = new ctor;\n child.__super__ = parent.prototype;\n return child;\n}', - bind: 'function(fn, me){ return function(){ return fn.apply(me, arguments); }; }', - indexOf: 'Array.prototype.indexOf || function(item) {\n for (var i = 0, l = this.length; i < l; i++) {\n if (this[i] === item) return i;\n }\n return -1;\n}', - hasProp: 'Object.prototype.hasOwnProperty', - slice: 'Array.prototype.slice' + "extends": function() { + return "function(child, parent) {\n for (var key in parent) { if (" + (utility('hasProp')) + ".call(parent, key)) child[key] = parent[key]; }\n function ctor() { this.constructor = child; }\n ctor.prototype = parent.prototype;\n child.prototype = new ctor;\n child.__super__ = parent.prototype;\n return child;\n}"; + }, + bind: function() { + return 'function(fn, me){ return function(){ return fn.apply(me, arguments); }; }'; + }, + indexOf: function() { + return "Array.prototype.indexOf || function(item) {\n for (var i = 0, l = this.length; i < l; i++) {\n if (" + (utility('hasProp')) + ".call(this, i) && this[i] === item) return i;\n }\n return -1;\n}"; + }, + hasProp: function() { + return 'Object.prototype.hasOwnProperty'; + }, + slice: function() { + return 'Array.prototype.slice'; + } }; LEVEL_TOP = 1; LEVEL_PAREN = 2; @@ -2104,7 +2113,7 @@ utility = function(name) { var ref; ref = "__" + name; - Scope.root.assign(ref, UTILITIES[name]); + Scope.root.assign(ref, UTILITIES[name]()); return ref; }; multident = function(code, tab) { diff --git a/lib/coffee-script/rewriter.js b/lib/coffee-script/rewriter.js index feb490cc..b2b259ea 100644 --- a/lib/coffee-script/rewriter.js +++ b/lib/coffee-script/rewriter.js @@ -1,8 +1,8 @@ (function() { var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref; - var __indexOf = Array.prototype.indexOf || function(item) { + var __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { - if (this[i] === item) return i; + if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; }, __slice = Array.prototype.slice; diff --git a/src/nodes.coffee b/src/nodes.coffee index 250296b4..954ec9d1 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -590,7 +590,6 @@ exports.Extends = class Extends extends Base # Hooks one constructor into another's prototype chain. compile: (o) -> - utility 'hasProp' new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compile o #### Access @@ -1593,7 +1592,7 @@ exports.For = class For extends Base varPart = "\n#{idt1}#{namePart};" if namePart if @object forPart = "#{ivar} in #{svar}" - guardPart = "\n#{idt1}if (!#{utility('hasProp')}.call(#{svar}, #{ivar})) continue;" if @own + guardPart = "\n#{idt1}if (!#{utility 'hasProp'}.call(#{svar}, #{ivar})) continue;" if @own body = body.compile merge(o, indent: idt1), LEVEL_TOP body = '\n' + body + '\n' if body """ @@ -1801,35 +1800,35 @@ UTILITIES = # Correctly set up a prototype chain for inheritance, including a reference # to the superclass for `super()` calls, and copies of any static properties. - extends: ''' + extends: -> """ function(child, parent) { - for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + for (var key in parent) { if (#{utility '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; } - ''' + """ # Create a function bound to the current value of "this". - bind: ''' + bind: -> ''' function(fn, me){ return function(){ return fn.apply(me, arguments); }; } ''' # Discover if an item is in an array. - indexOf: ''' + indexOf: -> """ Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { - if (this[i] === item) return i; + if (#{utility 'hasProp'}.call(this, i) && this[i] === item) return i; } return -1; } - ''' + """ # Shortcuts to speed up the lookup time for native functions. - hasProp: 'Object.prototype.hasOwnProperty' - slice : 'Array.prototype.slice' + hasProp: -> 'Object.prototype.hasOwnProperty' + slice : -> 'Array.prototype.slice' # Levels indicate a node's position in the AST. Useful for knowing if # parens are necessary or superfluous. @@ -1871,7 +1870,7 @@ IS_STRING = /^['"]/ # Helper for ensuring that utility functions are assigned at the top level. utility = (name) -> ref = "__#{name}" - Scope.root.assign ref, UTILITIES[name] + Scope.root.assign ref, UTILITIES[name]() ref multident = (code, tab) -> diff --git a/test/operators.coffee b/test/operators.coffee index 09d7edbe..a32447cd 100644 --- a/test/operators.coffee +++ b/test/operators.coffee @@ -197,6 +197,9 @@ test "#1100: precedence in or-test compilation of `in`", -> ok 0 in [1, 1 and 0] ok not (0 in [1, 0 or 1]) +test "#1630: `in` should check `hasOwnProperty`", -> + ok undefined not in length: 1 + # Chained Comparison