Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Use strict. For real. #2337

Open
wants to merge 4 commits into from

7 participants

@paulmillr

This will auto-add 'use strict'; to every coffeescript file if —bare wasn't passed to options.

Closes #2335.

@paulmillr

Interesting, coffeescript code doesn't pass strict mode checker. Working on this.

failed 2 and passed 428 tests in 1.01 seconds 

  --bare 
  TypeError: Cannot assign to read only property 'length' of function (path) {
          return Module._load(path, _module, true);
        }
    at Object.eval (/Users/paul/Development/coffee-script/lib/coffee-script/coffee-script.js:125:25)
    at Function.<anonymous> (/Users/paul/Development/coffee-script/test/compilation.coffee:23:47)
    at /Users/paul/Development/coffee-script/Cakefile:219:12
    at Object.<anonymous> (/Users/paul/Development/coffee-script/test/compilation.coffee:19:3)
    at Object.<anonymous> (/Users/paul/Development/coffee-script/test/compilation.coffee:99:4)
    at Module._compile (module.js:441:26)
    at Object.run (/Users/paul/Development/coffee-script/lib/coffee-script/coffee-script.js:80:25)
    at /Users/paul/Development/coffee-script/Cakefile:303:22
    at /Users/paul/Development/coffee-script/Cakefile:98:14
    at ChildProcess.<anonymous> (/Users/paul/Development/coffee-script/Cakefile:46:16) 
  test/compilation.js: line unknown, column unknown 
  function () {
    eq(-1, CoffeeScript.compile('x = y', {
      bare: true
    }).indexOf('function'));
    return ok('passed' === CoffeeScript["eval"]('"passed"', {
      bare: true,
      filename: 'test'
    }));
  }

  #855: execution context for `func arr...` should be `null` 
  AssertionError: false == true
    at /Users/paul/Development/coffee-script/Cakefile:259:14
    at /Users/paul/Development/coffee-script/test/function_invocation.coffee:419:14
    at Function.<anonymous> (/Users/paul/Development/coffee-script/test/function_invocation.coffee:422:5)
    at /Users/paul/Development/coffee-script/Cakefile:219:12
    at Object.<anonymous> (/Users/paul/Development/coffee-script/test/function_invocation.coffee:416:3)
    at Object.<anonymous> (/Users/paul/Development/coffee-script/test/function_invocation.coffee:818:4)
    at Module._compile (module.js:441:26)
    at Object.run (/Users/paul/Development/coffee-script/lib/coffee-script/coffee-script.js:80:25)
    at /Users/paul/Development/coffee-script/Cakefile:303:22
    at /Users/paul/Development/coffee-script/Cakefile:98:14 
  test/function_invocation.js: line unknown, column unknown 
  function () {
    var array, contextTest;
    contextTest = function() {
      return eq(this, typeof window !== "undefined" && window !== null ? window : global);
    };
    array = [];
    contextTest(array);
    contextTest.apply(null, array);
    return contextTest.apply(null, array);
  }
@michaelficarra
Collaborator

@paulmillr: Both of those are obvious failures. The first is because it's looking for a specific output. The second is because a null context in strict mode actually runs the function with a null context, not the global object.

@paulmillr

Yep, fixed.

@paulmillr

nope, not really. still fails browser tests because it has different execution context.

@paulmillr

@michaelficarra can test for #855 be removed at all? Doesn't seem to be very useful in our context.

@paulmillr
contextTest array # this is undefined
contextTest.apply undefined, array # this is undefined
contextTest array... # this is null

Should we pass undefined as execution context to func arr... for consistency with es5?

test/function_invocation.coffee
@@ -305,10 +305,11 @@ test "Prefix unary assignment operators are allowed in parenless calls.", ->
ok (func --val) is 5
test "#855: execution context for `func arr...` should be `null`", ->
- contextTest = -> eq @, if window? then window else global
+ contextTest = ->
+ eq this, window ? global if this?
@michaelficarra Collaborator

This now no longer does the test at all in strict mode. That's not desirable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@geraldalewis

Love strict, but I don't think it's a good idea to enable it by default. There are subtle semantic differences in strict mode that will trip people up if they're not watching out/are unaware they're using strict mode. For instance, in non-strict mode, most browsers bind arguments to elements on the arguments object e.g.:

((x) -> x = 2; arguments[0] is 2)(1) #true

and more problematic/far reaching: undefined is no longer coerced to global scope.

Amazon bitten by strict
Intel bitton by strict
Strict Quirks Mode

@paulmillr

@geraldalewis any other issues?

These were a long time ago and right now every stable version of every browser (ie isn't one) supports strict mode, so it would be quite hard to run into any of these in production env not spotting them in dev env.

@TazeTSchnitzel

I think it's important to add use strict because forgetting it can make code do things you wouldn't always immediately expect, often erroneous, and worse still, continue plodding on until it hits some other error, making code difficult to debug. For instance, forgetting a new when using a constructor in non-strict mode isn't an error, and the program will often continue executing.

@paulmillr paulmillr referenced this pull request in satyr/coco
Closed

use strict #146

@eventualbuddha

What's the status of this? I agree that it should not be the default, but I think it'd be great to have it as an option. Is that the consensus and we're just waiting for an updated patch, or is there some doubt about whether this should be included even as an option?

@meryn

Having wished for having "use strict" enabled for the first time of my life (chasing a global which turned out to be coming from a missing double arrow and doing @var = "value" somewhere), I would really like to have this as an option.

@gonsfx

Whats the current state of this issue?

I want TypeErrors when trying to add properties to sealed objects - without 'use strict' statement, these fail silently.

Therefore i'd like to have an option that generates 'use strict' statements in the top-level function safety wrapper.

@TazeTSchnitzel

Writing 'use strict'; at the top of a CoffeeScript file's really inelegant, and I don't even know if it works properly.

Would be nice if CS had a proper directive for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 16, 2012
  1. @paulmillr

    Use strict.

    paulmillr authored
  2. @paulmillr

    Fix test failures.

    paulmillr authored
  3. @paulmillr

    Fix browser errors.

    paulmillr authored
  4. @paulmillr

    Test in strict properly.

    paulmillr authored
This page is out of date. Refresh to see the latest.
View
2  extras/coffee-script.js
1 addition, 1 deletion not shown
View
1  lib/coffee-script/browser.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var CoffeeScript, runScripts;
CoffeeScript = require('./coffee-script');
View
1  lib/coffee-script/cake.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
fs = require('fs');
View
12 lib/coffee-script/coffee-script.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var Lexer, RESERVED, compile, fs, lexer, parser, path, vm, _ref,
__hasProp = {}.hasOwnProperty;
@@ -83,7 +84,7 @@
};
exports["eval"] = function(code, options) {
- var Module, Script, js, k, o, r, sandbox, v, _i, _len, _module, _ref1, _ref2, _require;
+ var Module, Script, descriptor, js, k, o, r, sandbox, v, _i, _len, _module, _ref1, _ref2, _require;
if (options == null) {
options = {};
}
@@ -120,9 +121,14 @@
_ref2 = Object.getOwnPropertyNames(require);
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
r = _ref2[_i];
- if (r !== 'paths') {
- _require[r] = require[r];
+ if (!(r !== 'paths')) {
+ continue;
}
+ descriptor = Object.getOwnPropertyDescriptor(require, r);
+ if (!descriptor.writable) {
+ continue;
+ }
+ _require[r] = require[r];
}
_require.paths = _module.paths = Module._nodeModulePaths(process.cwd());
_require.resolve = function(request) {
View
1  lib/coffee-script/command.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, forkNode, fs, helpers, hidden, joinTimeout, lint, loadRequires, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, version, wait, watch, watchDir, watchers, writeJs, _ref;
fs = require('fs');
View
1  lib/coffee-script/grammar.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
Parser = require('jison').Parser;
View
1  lib/coffee-script/helpers.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var extend, flatten, _ref;
exports.starts = function(string, literal, start) {
View
1  lib/coffee-script/index.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var key, val, _ref;
_ref = require('./coffee-script');
View
1  lib/coffee-script/lexer.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref, _ref1,
__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; };
View
3  lib/coffee-script/nodes.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
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; },
@@ -365,7 +366,7 @@
if (o.bare) {
return code;
}
- return "" + prelude + "(function() {\n" + code + "\n}).call(this);\n";
+ return "" + prelude + "(function() {\n" + TAB + "'use strict';\n" + code + "\n}).call(this);\n";
};
Block.prototype.compileWithDeclarations = function(o) {
View
1  lib/coffee-script/optparse.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments;
exports.OptionParser = OptionParser = (function() {
View
1  lib/coffee-script/repl.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, REPL_PROMPT_MULTILINE, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, inspect, multilineMode, pipedInput, readline, repl, run, stdin, stdout,
__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; };
View
1  lib/coffee-script/rewriter.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
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,
__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; },
__slice = [].slice;
View
1  lib/coffee-script/scope.js
@@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.3.3
(function() {
+ 'use strict';
var Scope, extend, last, _ref;
_ref = require('./helpers'), extend = _ref.extend, last = _ref.last;
View
5 src/coffee-script.coffee
@@ -99,7 +99,10 @@ exports.eval = (code, options = {}) ->
sandbox.module = _module = new Module(options.modulename || 'eval')
sandbox.require = _require = (path) -> Module._load path, _module, true
_module.filename = sandbox.__filename
- _require[r] = require[r] for r in Object.getOwnPropertyNames require when r isnt 'paths'
+ for r in Object.getOwnPropertyNames require when r isnt 'paths'
+ descriptor = Object.getOwnPropertyDescriptor require, r
+ continue unless descriptor.writable
+ _require[r] = require[r]
# use the same hack node currently uses for their own REPL
_require.paths = _module.paths = Module._nodeModulePaths process.cwd()
_require.resolve = (request) -> Module._resolveFilename request, _module
View
2  src/nodes.coffee
@@ -261,7 +261,7 @@ exports.Block = class Block extends Base
@expressions = rest
code = @compileWithDeclarations o
return code if o.bare
- "#{prelude}(function() {\n#{code}\n}).call(this);\n"
+ "#{prelude}(function() {\n#{TAB}'use strict';\n#{code}\n}).call(this);\n"
# Compile the expressions body for the contents of a function, with
# declarations of all inner variables pushed up to the top.
View
10 test/function_invocation.coffee
@@ -305,10 +305,16 @@ test "Prefix unary assignment operators are allowed in parenless calls.", ->
ok (func --val) is 5
test "#855: execution context for `func arr...` should be `null`", ->
- contextTest = -> eq @, if window? then window else global
+ contextTest = ->
+ if this is undefined
+ eq this, undefined
+ else if this is null
+ eq this, null
+ else
+ eq this, window ? global
array = []
contextTest array
- contextTest.apply null, array
+ contextTest.apply undefined, array
contextTest array...
test "#904: Destructuring function arguments with same-named variables in scope", ->
Something went wrong with that request. Please try again.