1 | /* |
2 | * Copyright (C) 2015 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 InferredValue_h |
27 | #define InferredValue_h |
28 | |
29 | #include "JSCell.h" |
30 | #include "Watchpoint.h" |
31 | #include "WriteBarrier.h" |
32 | |
33 | namespace JSC { |
34 | |
35 | // Allocate one of these if you'd like to infer a constant value. Writes to the value should use |
36 | // notifyWrite(). So long as exactly one value had ever been written and invalidate() has never been |
37 | // called, and you register a watchpoint, you can rely on the inferredValue() being the one true |
38 | // value. |
39 | // |
40 | // Commonly used for inferring singletons - in that case each allocation does notifyWrite(). But you |
41 | // can use it for other things as well. |
42 | |
43 | class InferredValue : public JSCell { |
44 | public: |
45 | typedef JSCell Base; |
46 | |
47 | static InferredValue* create(VM&); |
48 | |
49 | static const bool needsDestruction = true; |
50 | static void destroy(JSCell*); |
51 | |
52 | static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); |
53 | |
54 | static void visitChildren(JSCell*, SlotVisitor&); |
55 | |
56 | DECLARE_INFO; |
57 | |
58 | // For the purpose of deciding whether or not to watch this variable, you only need |
59 | // to inspect inferredValue(). If this returns something other than the empty |
60 | // value, then it means that at all future safepoints, this watchpoint set will be |
61 | // in one of these states: |
62 | // |
63 | // IsWatched: in this case, the variable's value must still be the |
64 | // inferredValue. |
65 | // |
66 | // IsInvalidated: in this case the variable's value may be anything but you'll |
67 | // either notice that it's invalidated and not install the watchpoint, or |
68 | // you will have been notified that the watchpoint was fired. |
69 | JSValue inferredValue() { return m_value.get(); } |
70 | |
71 | // Forwards some WatchpointSet methods. |
72 | WatchpointState state() const { return m_set.state(); } |
73 | bool isStillValid() const { return m_set.isStillValid(); } |
74 | bool hasBeenInvalidated() const { return m_set.hasBeenInvalidated(); } |
75 | void add(Watchpoint* watchpoint) { m_set.add(watchpoint); } |
76 | |
77 | void notifyWrite(VM& vm, JSValue value, const FireDetail& detail) |
78 | { |
79 | if (LIKELY(m_set.stateOnJSThread() == IsInvalidated)) |
80 | return; |
81 | notifyWriteSlow(vm, value, detail); |
82 | } |
83 | |
84 | void notifyWrite(VM& vm, JSValue value, const char* reason) |
85 | { |
86 | if (LIKELY(m_set.stateOnJSThread() == IsInvalidated)) |
87 | return; |
88 | notifyWriteSlow(vm, value, reason); |
89 | } |
90 | |
91 | void invalidate(const FireDetail& detail) |
92 | { |
93 | m_value.clear(); |
94 | m_set.invalidate(detail); |
95 | } |
96 | |
97 | static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags; |
98 | |
99 | // We could have used Weak<>. But we want arbitrary JSValues, not just cells. It's also somewhat |
100 | // convenient to have eager notification of death. |
101 | // |
102 | // Also note that this should be a private class, but it isn't because Windows. |
103 | class ValueCleanup : public UnconditionalFinalizer { |
104 | WTF_MAKE_FAST_ALLOCATED; |
105 | |
106 | public: |
107 | ValueCleanup(InferredValue*); |
108 | virtual ~ValueCleanup(); |
109 | |
110 | protected: |
111 | void finalizeUnconditionally() override; |
112 | |
113 | private: |
114 | InferredValue* m_owner; |
115 | }; |
116 | |
117 | private: |
118 | InferredValue(VM&); |
119 | ~InferredValue(); |
120 | |
121 | JS_EXPORT_PRIVATE void notifyWriteSlow(VM&, JSValue, const FireDetail&); |
122 | JS_EXPORT_PRIVATE void notifyWriteSlow(VM&, JSValue, const char* reason); |
123 | |
124 | friend class ValueCleanup; |
125 | |
126 | InlineWatchpointSet m_set; |
127 | WriteBarrier<Unknown> m_value; |
128 | std::unique_ptr<ValueCleanup> m_cleanup; |
129 | }; |
130 | |
131 | // FIXME: We could have an InlineInferredValue, which only allocates the InferredValue object when |
132 | // a notifyWrite() transitions us towards watching, and then clears the reference (allowing the object |
133 | // to die) when we get invalidated. |
134 | |
135 | } // namespace JSC |
136 | |
137 | #endif // InferredValue_h |
138 | |
139 | |