PageRenderTime 71ms CodeModel.GetById 11ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

/test/assignment.coffee

http://github.com/jashkenas/coffee-script
CoffeeScript | 722 lines | 588 code | 116 blank | 18 comment | 19 complexity | 63fee391f6b368e71678dc5f427f42f9 MD5 | raw file
  1# Assignment
  2# ----------
  3
  4# * Assignment
  5# * Compound Assignment
  6# * Destructuring Assignment
  7# * Context Property (@) Assignment
  8# * Existential Assignment (?=)
  9# * Assignment to variables similar to generated variables
 10
 11test "context property assignment (using @)", ->
 12  nonce = {}
 13  addMethod = ->
 14    @method = -> nonce
 15    this
 16  eq nonce, addMethod.call({}).method()
 17
 18test "unassignable values", ->
 19  nonce = {}
 20  for nonref in ['', '""', '0', 'f()'].concat CoffeeScript.RESERVED
 21    eq nonce, (try CoffeeScript.compile "#{nonref} = v" catch e then nonce)
 22
 23# Compound Assignment
 24
 25test "boolean operators", ->
 26  nonce = {}
 27
 28  a  = 0
 29  a or= nonce
 30  eq nonce, a
 31
 32  b  = 1
 33  b or= nonce
 34  eq 1, b
 35
 36  c = 0
 37  c and= nonce
 38  eq 0, c
 39
 40  d = 1
 41  d and= nonce
 42  eq nonce, d
 43
 44  # ensure that RHS is treated as a group
 45  e = f = false
 46  e and= f or true
 47  eq false, e
 48
 49test "compound assignment as a sub expression", ->
 50  [a, b, c] = [1, 2, 3]
 51  eq 6, (a + b += c)
 52  eq 1, a
 53  eq 5, b
 54  eq 3, c
 55
 56# *note: this test could still use refactoring*
 57test "compound assignment should be careful about caching variables", ->
 58  count = 0
 59  list = []
 60
 61  list[++count] or= 1
 62  eq 1, list[1]
 63  eq 1, count
 64
 65  list[++count] ?= 2
 66  eq 2, list[2]
 67  eq 2, count
 68
 69  list[count++] and= 6
 70  eq 6, list[2]
 71  eq 3, count
 72
 73  base = ->
 74    ++count
 75    base
 76
 77  base().four or= 4
 78  eq 4, base.four
 79  eq 4, count
 80
 81  base().five ?= 5
 82  eq 5, base.five
 83  eq 5, count
 84
 85  eq 5, base().five ?= 6
 86  eq 6, count
 87
 88test "compound assignment with implicit objects", ->
 89  obj = undefined
 90  obj ?=
 91    one: 1
 92
 93  eq 1, obj.one
 94
 95  obj and=
 96    two: 2
 97
 98  eq undefined, obj.one
 99  eq         2, obj.two
100
101test "compound assignment (math operators)", ->
102  num = 10
103  num -= 5
104  eq 5, num
105
106  num *= 10
107  eq 50, num
108
109  num /= 10
110  eq 5, num
111
112  num %= 3
113  eq 2, num
114
115test "more compound assignment", ->
116  a = {}
117  val = undefined
118  val ||= a
119  val ||= true
120  eq a, val
121
122  b = {}
123  val &&= true
124  eq val, true
125  val &&= b
126  eq b, val
127
128  c = {}
129  val = null
130  val ?= c
131  val ?= true
132  eq c, val
133
134test "#1192: assignment starting with object literals", ->
135  doesNotThrow (-> CoffeeScript.run "{}.p = 0")
136  doesNotThrow (-> CoffeeScript.run "{}.p++")
137  doesNotThrow (-> CoffeeScript.run "{}[0] = 1")
138  doesNotThrow (-> CoffeeScript.run """{a: 1, 'b', "#{1}": 2}.p = 0""")
139  doesNotThrow (-> CoffeeScript.run "{a:{0:{}}}.a[0] = 0")
140
141
142# Destructuring Assignment
143
144test "empty destructuring assignment", ->
145  {} = {}
146  [] = []
147
148test "chained destructuring assignments", ->
149  [a] = {0: b} = {'0': c} = [nonce={}]
150  eq nonce, a
151  eq nonce, b
152  eq nonce, c
153
154test "variable swapping to verify caching of RHS values when appropriate", ->
155  a = nonceA = {}
156  b = nonceB = {}
157  c = nonceC = {}
158  [a, b, c] = [b, c, a]
159  eq nonceB, a
160  eq nonceC, b
161  eq nonceA, c
162  [a, b, c] = [b, c, a]
163  eq nonceC, a
164  eq nonceA, b
165  eq nonceB, c
166  fn = ->
167    [a, b, c] = [b, c, a]
168  arrayEq [nonceA,nonceB,nonceC], fn()
169  eq nonceA, a
170  eq nonceB, b
171  eq nonceC, c
172
173test "#713: destructuring assignment should return right-hand-side value", ->
174  nonces = [nonceA={},nonceB={}]
175  eq nonces, [a, b] = [c, d] = nonces
176  eq nonceA, a
177  eq nonceA, c
178  eq nonceB, b
179  eq nonceB, d
180
181test "#4787 destructuring of objects within arrays", ->
182  arr = [1, {a:1, b:2}]
183  [...,{a, b}] = arr
184  eq a, 1
185  eq b, arr[1].b
186  deepEqual {a, b}, arr[1]
187
188test "destructuring assignment with splats", ->
189  a = {}; b = {}; c = {}; d = {}; e = {}
190  [x,y...,z] = [a,b,c,d,e]
191  eq a, x
192  arrayEq [b,c,d], y
193  eq e, z
194
195  # Should not trigger implicit call, e.g. rest ... => rest(...)
196  [x,y ...,z] = [a,b,c,d,e]
197  eq a, x
198  arrayEq [b,c,d], y
199  eq e, z
200
201test "deep destructuring assignment with splats", ->
202  a={}; b={}; c={}; d={}; e={}; f={}; g={}; h={}; i={}
203  [u, [v, w..., x], y..., z] = [a, [b, c, d, e], f, g, h, i]
204  eq a, u
205  eq b, v
206  arrayEq [c,d], w
207  eq e, x
208  arrayEq [f,g,h], y
209  eq i, z
210
211test "destructuring assignment with objects", ->
212  a={}; b={}; c={}
213  obj = {a,b,c}
214  {a:x, b:y, c:z} = obj
215  eq a, x
216  eq b, y
217  eq c, z
218
219test "deep destructuring assignment with objects", ->
220  a={}; b={}; c={}; d={}
221  obj = {
222    a
223    b: {
224      'c': {
225        d: [
226          b
227          {e: c, f: d}
228        ]
229      }
230    }
231  }
232  {a: w, 'b': {c: d: [x, {'f': z, e: y}]}} = obj
233  eq a, w
234  eq b, x
235  eq c, y
236  eq d, z
237
238test "destructuring assignment with objects and splats", ->
239  a={}; b={}; c={}; d={}
240  obj = a: b: [a, b, c, d]
241  {a: b: [y, z...]} = obj
242  eq a, y
243  arrayEq [b,c,d], z
244
245  # Should not trigger implicit call, e.g. rest ... => rest(...)
246  {a: b: [y, z ...]} = obj
247  eq a, y
248  arrayEq [b,c,d], z
249
250test "destructuring assignment against an expression", ->
251  a={}; b={}
252  [y, z] = if true then [a, b] else [b, a]
253  eq a, y
254  eq b, z
255
256test "bracket insertion when necessary", ->
257  [a] = [0] ? [1]
258  eq a, 0
259
260# for implicit destructuring assignment in comprehensions, see the comprehension tests
261
262test "destructuring assignment with context (@) properties", ->
263  a={}; b={}; c={}; d={}; e={}
264  obj =
265    fn: () ->
266      local = [a, {b, c}, d, e]
267      [@a, {b: @b, c: @c}, @d, @e] = local
268  eq undefined, obj[key] for key in ['a','b','c','d','e']
269  obj.fn()
270  eq a, obj.a
271  eq b, obj.b
272  eq c, obj.c
273  eq d, obj.d
274  eq e, obj.e
275
276test "#1024: destructure empty assignments to produce javascript-like results", ->
277  eq 2 * [] = 3 + 5, 16
278
279test "#1005: invalid identifiers allowed on LHS of destructuring assignment", ->
280  disallowed = ['eval', 'arguments'].concat CoffeeScript.RESERVED
281  throwsCompileError "[#{disallowed.join ', '}] = x", null, null, 'all disallowed'
282  throwsCompileError "[#{disallowed.join '..., '}...] = x", null, null, 'all disallowed as splats'
283  t = tSplat = null
284  for v in disallowed when v isnt 'class' # `class` by itself is an expression
285    throwsCompileError t, null, null, t = "[#{v}] = x"
286    throwsCompileError tSplat, null, null, tSplat = "[#{v}...] = x"
287  for v in disallowed
288    doesNotThrowCompileError "[a.#{v}] = x"
289    doesNotThrowCompileError "[a.#{v}...] = x"
290    doesNotThrowCompileError "[@#{v}] = x"
291    doesNotThrowCompileError "[@#{v}...] = x"
292
293test "#2055: destructuring assignment with `new`", ->
294  {length} = new Array
295  eq 0, length
296
297test "#156: destructuring with expansion", ->
298  array = [1..5]
299  [first, ..., last] = array
300  eq 1, first
301  eq 5, last
302  [..., lastButOne, last] = array
303  eq 4, lastButOne
304  eq 5, last
305  [first, second, ..., last] = array
306  eq 2, second
307  [..., last] = 'strings as well -> x'
308  eq 'x', last
309  throwsCompileError "[1, ..., 3]",        null, null, "prohibit expansion outside of assignment"
310  throwsCompileError "[..., a, b...] = c", null, null, "prohibit expansion and a splat"
311  throwsCompileError "[...] = c",          null, null, "prohibit lone expansion"
312
313test "destructuring with dynamic keys", ->
314  {"#{'a'}": a, """#{'b'}""": b, c} = {a: 1, b: 2, c: 3}
315  eq 1, a
316  eq 2, b
317  eq 3, c
318  throwsCompileError '{"#{a}"} = b'
319
320test "simple array destructuring defaults", ->
321  [a = 1] = []
322  eq 1, a
323  [a = 2] = [undefined]
324  eq 2, a
325  [a = 3] = [null]
326  eq null, a # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`.
327  [a = 4] = [0]
328  eq 0, a
329  arr = [a = 5]
330  eq 5, a
331  arrayEq [5], arr
332
333test "simple object destructuring defaults", ->
334  {b = 1} = {}
335  eq b, 1
336  {b = 2} = {b: undefined}
337  eq b, 2
338  {b = 3} = {b: null}
339  eq b, null # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`.
340  {b = 4} = {b: 0}
341  eq b, 0
342
343  {b: c = 1} = {}
344  eq c, 1
345  {b: c = 2} = {b: undefined}
346  eq c, 2
347  {b: c = 3} = {b: null}
348  eq c, null # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`.
349  {b: c = 4} = {b: 0}
350  eq c, 0
351
352test "multiple array destructuring defaults", ->
353  [a = 1, b = 2, c] = [undefined, 12, 13]
354  eq a, 1
355  eq b, 12
356  eq c, 13
357  [a, b = 2, c = 3] = [undefined, 12, 13]
358  eq a, undefined
359  eq b, 12
360  eq c, 13
361  [a = 1, b, c = 3] = [11, 12]
362  eq a, 11
363  eq b, 12
364  eq c, 3
365
366test "multiple object destructuring defaults", ->
367  {a = 1, b: bb = 2, 'c': c = 3, "#{0}": d = 4} = {"#{'b'}": 12}
368  eq a, 1
369  eq bb, 12
370  eq c, 3
371  eq d, 4
372
373test "array destructuring defaults with splats", ->
374  [..., a = 9] = []
375  eq a, 9
376  [..., b = 9] = [19]
377  eq b, 19
378
379test "deep destructuring assignment with defaults", ->
380  [a, [{b = 1, c = 3}] = [c: 2]] = [0]
381  eq a, 0
382  eq b, 1
383  eq c, 2
384
385test "destructuring assignment with context (@) properties and defaults", ->
386  a={}; b={}; c={}; d={}; e={}
387  obj =
388    fn: () ->
389      local = [a, {b, c: undefined}, d]
390      [@a, {b: @b = b, @c = c}, @d, @e = e] = local
391  eq undefined, obj[key] for key in ['a','b','c','d','e']
392  obj.fn()
393  eq a, obj.a
394  eq b, obj.b
395  eq c, obj.c
396  eq d, obj.d
397  eq e, obj.e
398
399test "destructuring assignment with defaults single evaluation", ->
400  callCount = 0
401  fn = -> callCount++
402  [a = fn()] = []
403  eq 0, a
404  eq 1, callCount
405  [a = fn()] = [10]
406  eq 10, a
407  eq 1, callCount
408  {a = fn(), b: c = fn()} = {a: 20, b: undefined}
409  eq 20, a
410  eq c, 1
411  eq callCount, 2
412
413
414# Existential Assignment
415
416test "existential assignment", ->
417  nonce = {}
418  a = false
419  a ?= nonce
420  eq false, a
421  b = undefined
422  b ?= nonce
423  eq nonce, b
424  c = null
425  c ?= nonce
426  eq nonce, c
427
428test "#1627: prohibit conditional assignment of undefined variables", ->
429  throwsCompileError "x ?= 10",        null, null, "prohibit (x ?= 10)"
430  throwsCompileError "x ||= 10",       null, null, "prohibit (x ||= 10)"
431  throwsCompileError "x or= 10",       null, null, "prohibit (x or= 10)"
432  throwsCompileError "do -> x ?= 10",  null, null, "prohibit (do -> x ?= 10)"
433  throwsCompileError "do -> x ||= 10", null, null, "prohibit (do -> x ||= 10)"
434  throwsCompileError "do -> x or= 10", null, null, "prohibit (do -> x or= 10)"
435  doesNotThrowCompileError "x = null; x ?= 10",        null, "allow (x = null; x ?= 10)"
436  doesNotThrowCompileError "x = null; x ||= 10",       null, "allow (x = null; x ||= 10)"
437  doesNotThrowCompileError "x = null; x or= 10",       null, "allow (x = null; x or= 10)"
438  doesNotThrowCompileError "x = null; do -> x ?= 10",  null, "allow (x = null; do -> x ?= 10)"
439  doesNotThrowCompileError "x = null; do -> x ||= 10", null, "allow (x = null; do -> x ||= 10)"
440  doesNotThrowCompileError "x = null; do -> x or= 10", null, "allow (x = null; do -> x or= 10)"
441
442  throwsCompileError "-> -> -> x ?= 10", null, null, "prohibit (-> -> -> x ?= 10)"
443  doesNotThrowCompileError "x = null; -> -> -> x ?= 10", null, "allow (x = null; -> -> -> x ?= 10)"
444
445test "more existential assignment", ->
446  global.temp ?= 0
447  eq global.temp, 0
448  global.temp or= 100
449  eq global.temp, 100
450  delete global.temp
451
452test "#1348, #1216: existential assignment compilation", ->
453  nonce = {}
454  a = nonce
455  b = (a ?= 0)
456  eq nonce, b
457  #the first ?= compiles into a statement; the second ?= compiles to a ternary expression
458  eq a ?= b ?= 1, nonce
459
460  if a then a ?= 2 else a = 3
461  eq a, nonce
462
463test "#1591, #1101: splatted expressions in destructuring assignment must be assignable", ->
464  nonce = {}
465  for nonref in ['', '""', '0', 'f()', '(->)'].concat CoffeeScript.RESERVED
466    eq nonce, (try CoffeeScript.compile "[#{nonref}...] = v" catch e then nonce)
467
468test "#1643: splatted accesses in destructuring assignments should not be declared as variables", ->
469  nonce = {}
470  accesses = ['o.a', 'o["a"]', '(o.a)', '(o.a).a', '@o.a', 'C::a', 'f().a', 'o?.a', 'o?.a.b', 'f?().a']
471  for access in accesses
472    for i,j in [1,2,3] #position can matter
473      code =
474        """
475        nonce = {}; nonce2 = {}; nonce3 = {};
476        @o = o = new (class C then a:{}); f = -> o
477        [#{new Array(i).join('x,')}#{access}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
478        unless #{access}[0] is nonce and #{access}[1] is nonce2 and #{access}[2] is nonce3 then throw new Error('[...]')
479        """
480      eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
481  # subpatterns like `[[a]...]` and `[{a}...]`
482  subpatterns = ['[sub, sub2, sub3]', '{0: sub, 1: sub2, 2: sub3}']
483  for subpattern in subpatterns
484    for i,j in [1,2,3]
485      code =
486        """
487        nonce = {}; nonce2 = {}; nonce3 = {};
488        [#{new Array(i).join('x,')}#{subpattern}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
489        unless sub is nonce and sub2 is nonce2 and sub3 is nonce3 then throw new Error('[sub...]')
490        """
491      eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
492
493test "#1838: Regression with variable assignment", ->
494  name =
495  'dave'
496
497  eq name, 'dave'
498
499test '#2211: splats in destructured parameters', ->
500  doesNotThrowCompileError '([a...]) ->'
501  doesNotThrowCompileError '([a...],b) ->'
502  doesNotThrowCompileError '([a...],[b...]) ->'
503  throwsCompileError '([a...,[a...]]) ->'
504  doesNotThrowCompileError '([a...,[b...]]) ->'
505
506test '#2213: invocations within destructured parameters', ->
507  throwsCompileError '([a()])->'
508  throwsCompileError '([a:b()])->'
509  throwsCompileError '([a:b.c()])->'
510  throwsCompileError '({a()})->'
511  throwsCompileError '({a:b()})->'
512  throwsCompileError '({a:b.c()})->'
513
514test '#2532: compound assignment with terminator', ->
515  doesNotThrowCompileError """
516  a = "hello"
517  a +=
518  "
519  world
520  !
521  "
522  """
523
524test "#2613: parens on LHS of destructuring", ->
525  a = {}
526  [(a).b] = [1, 2, 3]
527  eq a.b, 1
528
529test "#2181: conditional assignment as a subexpression", ->
530  a = false
531  false && a or= true
532  eq false, a
533  eq false, not a or= true
534
535test "#1500: Assignment to variables similar to generated variables", ->
536  len = 0
537  x = ((results = null; n) for n in [1, 2, 3])
538  arrayEq [1, 2, 3], x
539  eq 0, len
540
541  for x in [1, 2, 3]
542    f = ->
543      i = 0
544    f()
545    eq 'undefined', typeof i
546
547  ref = 2
548  x = ref * 2 ? 1
549  eq x, 4
550  eq 'undefined', typeof ref1
551
552  x = {}
553  base = -> x
554  name = -1
555  base()[-name] ?= 2
556  eq x[1], 2
557  eq base(), x
558  eq name, -1
559
560  f = (@a, a) -> [@a, a]
561  arrayEq [1, 2], f.call scope = {}, 1, 2
562  eq 1, scope.a
563
564  try throw 'foo'
565  catch error
566    eq error, 'foo'
567
568  eq error, 'foo'
569
570  doesNotThrowCompileError '(@slice...) ->'
571
572test "Assignment to variables similar to helper functions", ->
573  f = (slice...) -> slice
574  arrayEq [1, 2, 3], f 1, 2, 3
575  eq 'undefined', typeof slice1
576
577  class A
578  class B extends A
579    extend = 3
580    hasProp = 4
581    value: 5
582    method: (bind, bind1) => [bind, bind1, extend, hasProp, @value]
583  {method} = new B
584  arrayEq [1, 2, 3, 4, 5], method 1, 2
585
586  modulo = -1 %% 3
587  eq 2, modulo
588
589  indexOf = [1, 2, 3]
590  ok 2 in indexOf
591
592test "#4566: destructuring with nested default values", ->
593  {a: {b = 1}} = a: {}
594  eq 1, b
595
596  {c: {d} = {}} = c: d: 3
597  eq 3, d
598
599  {e: {f = 5} = {}} = {}
600  eq 5, f
601
602test "#4878: Compile error when using destructuring with a splat or expansion in an array", ->
603  arr = ['a', 'b', 'c', 'd']
604
605  f1 = (list) ->
606    [first, ..., last] = list
607
608  f2 = (list) ->
609    [first..., last] = list
610
611  f3 = (list) ->
612    ([first, ...] = list); first
613
614  f4 = (list) ->
615    ([first, rest...] = list); rest
616
617  arrayEq f1(arr), arr
618  arrayEq f2(arr), arr
619  arrayEq f3(arr), 'a'
620  arrayEq f4(arr), ['b', 'c', 'd']
621
622  foo = (list) ->
623    ret =
624      if list?.length > 0
625        [first, ..., last] = list
626        [first, last]
627      else
628        []
629
630  arrayEq foo(arr), ['a', 'd']
631
632  bar = (list) ->
633    ret =
634      if list?.length > 0
635        [first, rest...] = list
636        [first, rest]
637      else
638        []
639
640  arrayEq bar(arr), ['a', ['b', 'c', 'd']]
641
642test "destructuring assignment with an empty array in object", ->
643  obj =
644    a1: [1, 2]
645    b1: 3
646
647  {a1:[], b1} = obj
648  eq 'undefined', typeof a1
649  eq b1, 3
650
651  obj =
652    a2:
653      b2: [1, 2]
654    c2: 3
655
656  {a2: {b2:[]}, c2} = obj
657  eq 'undefined', typeof b2
658  eq c2, 3
659
660test "#5004: array destructuring with accessors", ->
661  obj =
662    arr: ['a', 'b', 'c', 'd']
663    list: {}
664    f1: ->
665      [@first, @rest...] = @arr
666    f2: ->
667      [@second, @third..., @last] = @rest
668    f3: ->
669      [@list.a, @list.middle..., @list.d] = @arr
670
671  obj.f1()
672  eq obj.first, 'a'
673  arrayEq obj.rest, ['b', 'c', 'd']
674
675  obj.f2()
676  eq obj.second, 'b'
677  arrayEq obj.third, ['c']
678  eq obj.last, 'd'
679
680  obj.f3()
681  eq obj.list.a, 'a'
682  arrayEq obj.list.middle, ['b', 'c']
683  eq obj.list.d, 'd'
684
685  [obj.list.middle..., d] = obj.arr
686  eq d, 'd'
687  arrayEq obj.list.middle, ['a', 'b', 'c']
688
689test "#4884: destructured object splat", ->
690  [{length}...] = [1, 2, 3]
691  eq length, 3
692  [{length: len}...] = [1, 2, 3]
693  eq len, 3
694  [{length}..., three] = [1, 2, 3]
695  eq length, 2
696  eq three, 3
697  [{length: len}..., three] = [1, 2, 3]
698  eq len, 2
699  eq three, 3
700  x = [{length}..., three] = [1, 2, 3]
701  eq length, 2
702  eq three, 3
703  eq x[2], 3
704  x = [{length: len}..., three] = [1, 2, 3]
705  eq len, 2
706  eq three, 3
707  eq x[2], 3
708
709test "#4884: destructured array splat", ->
710  [[one, two, three]...] = [1, 2, 3]
711  eq one, 1
712  eq two, 2
713  eq three, 3
714  [[one, two]..., three] = [1, 2, 3]
715  eq one, 1
716  eq two, 2
717  eq three, 3
718  x = [[one, two]..., three] = [1, 2, 3]
719  eq one, 1
720  eq two, 2
721  eq three, 3
722  eq x[2], 3