1/*
2 * Copyright (C) 2012, 2013, 2014 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 JSScope_h
27#define JSScope_h
28
29#include "JSObject.h"
30
31namespace JSC {
32
33class ScopeChainIterator;
34class VariableWatchpointSet;
35
36enum ResolveMode {
37    ThrowIfNotFound,
38    DoNotThrowIfNotFound
39};
40
41enum ResolveType {
42    // Lexical scope guaranteed a certain type of variable access.
43    GlobalProperty,
44    GlobalVar,
45    ClosureVar,
46
47    // Ditto, but at least one intervening scope used non-strict eval, which
48    // can inject an intercepting var delcaration at runtime.
49    GlobalPropertyWithVarInjectionChecks,
50    GlobalVarWithVarInjectionChecks,
51    ClosureVarWithVarInjectionChecks,
52
53    // Lexical scope didn't prove anything -- probably because of a 'with' scope.
54    Dynamic
55};
56
57const char* resolveModeName(ResolveMode mode);
58const char* resolveTypeName(ResolveType type);
59
60inline ResolveType makeType(ResolveType type, bool needsVarInjectionChecks)
61{
62    if (!needsVarInjectionChecks)
63        return type;
64
65    switch (type) {
66    case GlobalProperty:
67        return GlobalPropertyWithVarInjectionChecks;
68    case GlobalVar:
69        return GlobalVarWithVarInjectionChecks;
70    case ClosureVar:
71        return ClosureVarWithVarInjectionChecks;
72    case GlobalPropertyWithVarInjectionChecks:
73    case GlobalVarWithVarInjectionChecks:
74    case ClosureVarWithVarInjectionChecks:
75    case Dynamic:
76        return type;
77    }
78
79    RELEASE_ASSERT_NOT_REACHED();
80    return type;
81}
82
83inline bool needsVarInjectionChecks(ResolveType type)
84{
85    switch (type) {
86    case GlobalProperty:
87    case GlobalVar:
88    case ClosureVar:
89        return false;
90    case GlobalPropertyWithVarInjectionChecks:
91    case GlobalVarWithVarInjectionChecks:
92    case ClosureVarWithVarInjectionChecks:
93    case Dynamic:
94        return true;
95    default:
96        RELEASE_ASSERT_NOT_REACHED();
97        return true;
98    }
99}
100
101struct ResolveOp {
102    ResolveOp(ResolveType type, size_t depth, Structure* structure, JSActivation* activation, VariableWatchpointSet* watchpointSet, uintptr_t operand)
103        : type(type)
104        , depth(depth)
105        , structure(structure)
106        , activation(activation)
107        , watchpointSet(watchpointSet)
108        , operand(operand)
109    {
110    }
111
112    ResolveType type;
113    size_t depth;
114    Structure* structure;
115    JSActivation* activation;
116    VariableWatchpointSet* watchpointSet;
117    uintptr_t operand;
118};
119
120class ResolveModeAndType {
121    typedef unsigned Operand;
122public:
123    static const size_t shift = sizeof(Operand) * 8 / 2;
124    static const unsigned mask = (1 << shift) - 1;
125
126    ResolveModeAndType(ResolveMode resolveMode, ResolveType resolveType)
127        : m_operand((resolveMode << shift) | resolveType)
128    {
129    }
130
131    explicit ResolveModeAndType(unsigned operand)
132        : m_operand(operand)
133    {
134    }
135
136    ResolveMode mode() { return static_cast<ResolveMode>(m_operand >> shift); }
137    ResolveType type() { return static_cast<ResolveType>(m_operand & mask); }
138    unsigned operand() { return m_operand; }
139
140private:
141    Operand m_operand;
142};
143
144enum GetOrPut { Get, Put };
145
146class JSScope : public JSNonFinalObject {
147public:
148    typedef JSNonFinalObject Base;
149
150    friend class LLIntOffsetsExtractor;
151    static size_t offsetOfNext();
152
153    JS_EXPORT_PRIVATE static JSObject* objectAtScope(JSScope*);
154
155    static JSValue resolve(ExecState*, JSScope*, const Identifier&);
156    static ResolveOp abstractResolve(ExecState*, JSScope*, const Identifier&, GetOrPut, ResolveType);
157
158    static void visitChildren(JSCell*, SlotVisitor&);
159
160    ScopeChainIterator begin();
161    ScopeChainIterator end();
162    JSScope* next();
163    int depth();
164
165    JSGlobalObject* globalObject();
166    VM* vm();
167    JSObject* globalThis();
168
169protected:
170    JSScope(VM&, Structure*, JSScope* next);
171    static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags;
172
173private:
174    WriteBarrier<JSScope> m_next;
175};
176
177inline JSScope::JSScope(VM& vm, Structure* structure, JSScope* next)
178    : Base(vm, structure)
179    , m_next(vm, this, next, WriteBarrier<JSScope>::MayBeNull)
180{
181}
182
183class ScopeChainIterator {
184public:
185    ScopeChainIterator(JSScope* node)
186        : m_node(node)
187    {
188    }
189
190    JSObject* get() const { return JSScope::objectAtScope(m_node); }
191    JSObject* operator->() const { return JSScope::objectAtScope(m_node); }
192
193    ScopeChainIterator& operator++() { m_node = m_node->next(); return *this; }
194
195    // postfix ++ intentionally omitted
196
197    bool operator==(const ScopeChainIterator& other) const { return m_node == other.m_node; }
198    bool operator!=(const ScopeChainIterator& other) const { return m_node != other.m_node; }
199
200private:
201    JSScope* m_node;
202};
203
204inline ScopeChainIterator JSScope::begin()
205{
206    return ScopeChainIterator(this);
207}
208
209inline ScopeChainIterator JSScope::end()
210{
211    return ScopeChainIterator(0);
212}
213
214inline JSScope* JSScope::next()
215{
216    return m_next.get();
217}
218
219inline JSGlobalObject* JSScope::globalObject()
220{
221    return structure()->globalObject();
222}
223
224inline VM* JSScope::vm()
225{
226    return MarkedBlock::blockFor(this)->vm();
227}
228
229inline Register& Register::operator=(JSScope* scope)
230{
231    *this = JSValue(scope);
232    return *this;
233}
234
235inline JSScope* Register::scope() const
236{
237    return jsCast<JSScope*>(jsValue());
238}
239
240inline VM& ExecState::vm() const
241{
242    ASSERT(scope()->vm());
243    return *scope()->vm();
244}
245
246inline JSGlobalObject* ExecState::lexicalGlobalObject() const
247{
248    return scope()->globalObject();
249}
250
251inline JSObject* ExecState::globalThisValue() const
252{
253    return scope()->globalThis();
254}
255
256inline size_t JSScope::offsetOfNext()
257{
258    return OBJECT_OFFSETOF(JSScope, m_next);
259}
260
261} // namespace JSC
262
263#endif // JSScope_h
264