/test/assignment.coffee
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