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