1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#ifndef DFGDesiredWatchpoints_h
27#define DFGDesiredWatchpoints_h
28
29#if ENABLE(DFG_JIT)
30
31#include "CodeOrigin.h"
32#include "DFGCommonData.h"
33#include "JSArrayBufferView.h"
34#include "Watchpoint.h"
35#include <wtf/HashMap.h>
36#include <wtf/HashSet.h>
37#include <wtf/Noncopyable.h>
38#include <wtf/Vector.h>
39
40namespace JSC { namespace DFG {
41
42template<typename WatchpointSetType>
43struct WatchpointForGenericWatchpointSet {
44    WatchpointForGenericWatchpointSet()
45        : m_exitKind(ExitKindUnset)
46        , m_set(0)
47    {
48    }
49
50    WatchpointForGenericWatchpointSet(
51        CodeOrigin codeOrigin, ExitKind exitKind, WatchpointSetType* set)
52        : m_codeOrigin(codeOrigin)
53        , m_exitKind(exitKind)
54        , m_set(set)
55    {
56    }
57
58    CodeOrigin m_codeOrigin;
59    ExitKind m_exitKind;
60    WatchpointSetType* m_set;
61};
62
63template<typename T>
64struct GenericSetAdaptor {
65    static void add(CodeBlock*, T* set, Watchpoint* watchpoint)
66    {
67        return set->add(watchpoint);
68    }
69    static bool hasBeenInvalidated(T* set) { return set->hasBeenInvalidated(); }
70};
71
72struct ArrayBufferViewWatchpointAdaptor {
73    static void add(CodeBlock*, JSArrayBufferView*, Watchpoint*);
74    static bool hasBeenInvalidated(JSArrayBufferView* view)
75    {
76        bool result = !view->length();
77        WTF::loadLoadFence();
78        return result;
79    }
80};
81
82template<typename WatchpointSetType, typename Adaptor = GenericSetAdaptor<WatchpointSetType>>
83class GenericDesiredWatchpoints {
84#if !ASSERT_DISABLED
85    typedef HashMap<WatchpointSetType*, bool> StateMap;
86#endif
87public:
88    GenericDesiredWatchpoints()
89        : m_reallyAdded(false)
90    {
91    }
92
93    void addLazily(WatchpointSetType* set)
94    {
95        m_sets.add(set);
96    }
97
98    void addLazily(CodeOrigin codeOrigin, ExitKind exitKind, WatchpointSetType* set)
99    {
100        m_profiledWatchpoints.append(
101            WatchpointForGenericWatchpointSet<WatchpointSetType>(codeOrigin, exitKind, set));
102    }
103
104    void reallyAdd(CodeBlock* codeBlock, CommonData& common)
105    {
106        RELEASE_ASSERT(!m_reallyAdded);
107
108        typename HashSet<WatchpointSetType*>::iterator iter = m_sets.begin();
109        typename HashSet<WatchpointSetType*>::iterator end = m_sets.end();
110        for (; iter != end; ++iter) {
111            common.watchpoints.append(CodeBlockJettisoningWatchpoint(codeBlock));
112            Adaptor::add(codeBlock, *iter, &common.watchpoints.last());
113        }
114
115        for (unsigned i = m_profiledWatchpoints.size(); i--;) {
116            WatchpointForGenericWatchpointSet<WatchpointSetType> watchpoint =
117                m_profiledWatchpoints[i];
118            common.profiledWatchpoints.append(
119                ProfiledCodeBlockJettisoningWatchpoint(watchpoint.m_codeOrigin, watchpoint.m_exitKind, codeBlock));
120            Adaptor::add(codeBlock, watchpoint.m_set, &common.profiledWatchpoints.last());
121        }
122
123        m_reallyAdded = true;
124    }
125
126    bool areStillValid() const
127    {
128        typename HashSet<WatchpointSetType*>::iterator iter = m_sets.begin();
129        typename HashSet<WatchpointSetType*>::iterator end = m_sets.end();
130        for (; iter != end; ++iter) {
131            if (Adaptor::hasBeenInvalidated(*iter))
132                return false;
133        }
134
135        for (unsigned i = m_profiledWatchpoints.size(); i--;) {
136            if (Adaptor::hasBeenInvalidated(m_profiledWatchpoints[i].m_set))
137                return false;
138        }
139
140        return true;
141    }
142
143#if ASSERT_DISABLED
144    bool isStillValid(WatchpointSetType* set)
145    {
146        return !Adaptor::hasBeenInvalidated(set);
147    }
148
149    bool shouldAssumeMixedState(WatchpointSetType*)
150    {
151        return true;
152    }
153#else
154    bool isStillValid(WatchpointSetType* set)
155    {
156        bool result = !Adaptor::hasBeenInvalidated(set);
157        m_firstKnownState.add(set, result);
158        return result;
159    }
160
161    bool shouldAssumeMixedState(WatchpointSetType* set)
162    {
163        typename StateMap::iterator iter = m_firstKnownState.find(set);
164        if (iter == m_firstKnownState.end())
165            return false;
166
167        return iter->value != !Adaptor::hasBeenInvalidated(set);
168    }
169#endif
170
171    bool isValidOrMixed(WatchpointSetType* set)
172    {
173        return isStillValid(set) || shouldAssumeMixedState(set);
174    }
175
176private:
177    Vector<WatchpointForGenericWatchpointSet<WatchpointSetType>> m_profiledWatchpoints;
178    HashSet<WatchpointSetType*> m_sets;
179#if !ASSERT_DISABLED
180    StateMap m_firstKnownState;
181#endif
182    bool m_reallyAdded;
183};
184
185class DesiredWatchpoints {
186public:
187    DesiredWatchpoints();
188    ~DesiredWatchpoints();
189
190    void addLazily(WatchpointSet*);
191    void addLazily(InlineWatchpointSet&);
192    void addLazily(JSArrayBufferView*);
193    void addLazily(CodeOrigin, ExitKind, WatchpointSet*);
194    void addLazily(CodeOrigin, ExitKind, InlineWatchpointSet&);
195
196    void reallyAdd(CodeBlock*, CommonData&);
197
198    bool areStillValid() const;
199
200    bool isStillValid(WatchpointSet* set)
201    {
202        return m_sets.isStillValid(set);
203    }
204    bool isStillValid(InlineWatchpointSet& set)
205    {
206        return m_inlineSets.isStillValid(&set);
207    }
208    bool isStillValid(JSArrayBufferView* view)
209    {
210        return m_bufferViews.isStillValid(view);
211    }
212    bool shouldAssumeMixedState(WatchpointSet* set)
213    {
214        return m_sets.shouldAssumeMixedState(set);
215    }
216    bool shouldAssumeMixedState(InlineWatchpointSet& set)
217    {
218        return m_inlineSets.shouldAssumeMixedState(&set);
219    }
220    bool shouldAssumeMixedState(JSArrayBufferView* view)
221    {
222        return m_bufferViews.shouldAssumeMixedState(view);
223    }
224    bool isValidOrMixed(WatchpointSet* set)
225    {
226        return m_sets.isValidOrMixed(set);
227    }
228    bool isValidOrMixed(InlineWatchpointSet& set)
229    {
230        return m_inlineSets.isValidOrMixed(&set);
231    }
232    bool isValidOrMixed(JSArrayBufferView* view)
233    {
234        return m_bufferViews.isValidOrMixed(view);
235    }
236
237private:
238    GenericDesiredWatchpoints<WatchpointSet> m_sets;
239    GenericDesiredWatchpoints<InlineWatchpointSet> m_inlineSets;
240    GenericDesiredWatchpoints<JSArrayBufferView, ArrayBufferViewWatchpointAdaptor> m_bufferViews;
241};
242
243} } // namespace JSC::DFG
244
245#endif // ENABLE(DFG_JIT)
246
247#endif // DFGDesiredWatchpoints_h
248
249