-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathsoaks.coffee
133 lines (109 loc) · 3.14 KB
/
soaks.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# Soaks
# -----
# * Soaked Property Access
# * Soaked Method Invocation
# * Soaked Function Invocation
# Soaked Property Access
test "soaked property access", ->
nonce = {}
obj = a: b: nonce
eq nonce , obj?.a.b
eq nonce , obj?['a'].b
eq nonce , obj.a?.b
eq nonce , obj?.a?['b']
eq undefined, obj?.a?.non?.existent?.property
test "soaked property access caches method calls", ->
nonce ={}
obj = fn: -> a: nonce
eq nonce , obj.fn()?.a
eq undefined, obj.fn()?.b
test "soaked property access caching", ->
nonce = {}
counter = 0
fn = ->
counter++
'self'
obj =
self: -> @
prop: nonce
eq nonce, obj[fn()]()[fn()]()[fn()]()?.prop
eq 3, counter
test "method calls on soaked methods", ->
nonce = {}
obj = null
eq undefined, obj?.a().b()
obj = a: -> b: -> nonce
eq nonce , obj?.a().b()
test "postfix existential operator mixes well with soaked property accesses", ->
eq false, nonexistent?.property?
test "function invocation with soaked property access", ->
id = (_) -> _
eq undefined, id nonexistent?.method()
test "if-to-ternary should safely parenthesize soaked property accesses", ->
ok (if nonexistent?.property then false else true)
test "#726: don't check for a property on a conditionally-referenced nonexistent thing", ->
eq undefined, nonexistent?[Date()]
test "#756: conditional assignment edge cases", ->
# TODO: improve this test
a = null
ok isNaN a?.b.c + 1
eq undefined, a?.b.c += 1
eq undefined, ++a?.b.c
eq undefined, delete a?.b.c
test "operations on soaked properties", ->
# TODO: improve this test
a = b: {c: 0}
eq 1, a?.b.c + 1
eq 1, a?.b.c += 1
eq 2, ++a?.b.c
eq yes, delete a?.b.c
# Soaked Method Invocation
test "soaked method invocation", ->
nonce = {}
counter = 0
obj =
self: -> @
increment: -> counter++; @
eq obj , obj.self?()
eq undefined, obj.method?()
eq nonce , obj.self?().property = nonce
eq undefined, obj.method?().property = nonce
eq obj , obj.increment().increment().self?()
eq 2 , counter
test "#733: conditional assignments", ->
a = b: {c: null}
eq a.b?.c?(), undefined
a.b?.c or= (it) -> it
eq a.b?.c?(1), 1
eq a.b?.c?([2, 3]...), 2
# Soaked Function Invocation
test "soaked function invocation", ->
nonce = {}
id = (_) -> _
eq nonce , id?(nonce)
eq nonce , (id? nonce)
eq undefined, nonexistent?(nonce)
eq undefined, (nonexistent? nonce)
test "soaked function invocation with generated functions", ->
nonce = {}
id = (_) -> _
maybe = (fn, arg) -> if typeof fn is 'function' then () -> fn(arg)
eq maybe(id, nonce)?(), nonce
eq (maybe id, nonce)?(), nonce
eq (maybe false, nonce)?(), undefined
test "soaked constructor invocation", ->
eq 42 , +new Number? 42
eq undefined, new Other? 42
test "soaked constructor invocations with caching and property access", ->
semaphore = 0
nonce = {}
class C
constructor: ->
ok false if semaphore
semaphore++
prop: nonce
eq nonce, (new C())?.prop
eq 1, semaphore
test "soaked function invocation safe on non-functions", ->
eq undefined, (0)?(1)
eq undefined, (0)? 1, 2