3106fbb74734aa96ad3fa82931f76412f39085da
[WebKit-https.git] / Source / JavaScriptCore / debugger / DebuggerScope.cpp
1 /*
2  * Copyright (C) 2008-2009, 2014, 2016 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 #include "config.h"
27 #include "DebuggerScope.h"
28
29 #include "JSLexicalEnvironment.h"
30 #include "JSCInlines.h"
31 #include "JSWithScope.h"
32
33 namespace JSC {
34
35 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DebuggerScope);
36
37 const ClassInfo DebuggerScope::s_info = { "DebuggerScope", &Base::s_info, 0, CREATE_METHOD_TABLE(DebuggerScope) };
38
39 DebuggerScope* DebuggerScope::create(VM& vm, JSScope* scope)
40 {
41     Structure* structure = scope->globalObject()->debuggerScopeStructure();
42     DebuggerScope* debuggerScope = new (NotNull, allocateCell<DebuggerScope>(vm.heap)) DebuggerScope(vm, structure, scope);
43     debuggerScope->finishCreation(vm);
44     return debuggerScope;
45 }
46
47 DebuggerScope::DebuggerScope(VM& vm, Structure* structure, JSScope* scope)
48     : JSNonFinalObject(vm, structure)
49 {
50     ASSERT(scope);
51     m_scope.set(vm, this, scope);
52 }
53
54 void DebuggerScope::finishCreation(VM& vm)
55 {
56     Base::finishCreation(vm);
57 }
58
59 void DebuggerScope::visitChildren(JSCell* cell, SlotVisitor& visitor)
60 {
61     DebuggerScope* thisObject = jsCast<DebuggerScope*>(cell);
62     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
63     JSObject::visitChildren(thisObject, visitor);
64     visitor.append(&thisObject->m_scope);
65     visitor.append(&thisObject->m_next);
66 }
67
68 String DebuggerScope::className(const JSObject* object)
69 {
70     const DebuggerScope* scope = jsCast<const DebuggerScope*>(object);
71     // We cannot assert that scope->isValid() because the TypeProfiler may encounter an invalidated
72     // DebuggerScope in its log entries. We just need to handle it appropriately as below.
73     if (!scope->isValid())
74         return String();
75     JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
76     return thisObject->methodTable()->className(thisObject);
77 }
78
79 bool DebuggerScope::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
80 {
81     DebuggerScope* scope = jsCast<DebuggerScope*>(object);
82     if (!scope->isValid())
83         return false;
84     JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
85     slot.setThisValue(JSValue(thisObject));
86
87     // By default, JSObject::getPropertySlot() will look in the DebuggerScope's prototype
88     // chain and not the wrapped scope, and JSObject::getPropertySlot() cannot be overridden
89     // to behave differently for the DebuggerScope.
90     //
91     // Instead, we'll treat all properties in the wrapped scope and its prototype chain as
92     // the own properties of the DebuggerScope. This is fine because the WebInspector
93     // does not presently need to distinguish between what's owned at each level in the
94     // prototype chain. Hence, we'll invoke getPropertySlot() on the wrapped scope here
95     // instead of getOwnPropertySlot().
96     bool result = thisObject->getPropertySlot(exec, propertyName, slot);
97     if (result && slot.isValue() && slot.getValue(exec, propertyName) == jsTDZValue()) {
98         // FIXME:
99         // We hit a scope property that has the TDZ empty value.
100         // Currently, we just lie to the inspector and claim that this property is undefined.
101         // This is not ideal and we should fix it.
102         // https://bugs.webkit.org/show_bug.cgi?id=144977
103         slot.setValue(slot.slotBase(), DontEnum, jsUndefined());
104         return true;
105     }
106     return result;
107 }
108
109 bool DebuggerScope::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
110 {
111     DebuggerScope* scope = jsCast<DebuggerScope*>(cell);
112     ASSERT(scope->isValid());
113     if (!scope->isValid())
114         return false;
115     JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
116     slot.setThisValue(JSValue(thisObject));
117     return thisObject->methodTable()->put(thisObject, exec, propertyName, value, slot);
118 }
119
120 bool DebuggerScope::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
121 {
122     DebuggerScope* scope = jsCast<DebuggerScope*>(cell);
123     ASSERT(scope->isValid());
124     if (!scope->isValid())
125         return false;
126     JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
127     return thisObject->methodTable()->deleteProperty(thisObject, exec, propertyName);
128 }
129
130 void DebuggerScope::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
131 {
132     DebuggerScope* scope = jsCast<DebuggerScope*>(object);
133     ASSERT(scope->isValid());
134     if (!scope->isValid())
135         return;
136     JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
137     thisObject->methodTable()->getPropertyNames(thisObject, exec, propertyNames, mode);
138 }
139
140 bool DebuggerScope::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
141 {
142     DebuggerScope* scope = jsCast<DebuggerScope*>(object);
143     ASSERT(scope->isValid());
144     if (!scope->isValid())
145         return false;
146     JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
147     return thisObject->methodTable()->defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
148 }
149
150 DebuggerScope* DebuggerScope::next()
151 {
152     ASSERT(isValid());
153     if (!m_next && m_scope->next()) {
154         VM& vm = *m_scope->vm();
155         DebuggerScope* nextScope = create(vm, m_scope->next());
156         m_next.set(vm, this, nextScope);
157     }
158     return m_next.get();
159 }
160
161 void DebuggerScope::invalidateChain()
162 {
163     if (!isValid())
164         return;
165
166     DebuggerScope* scope = this;
167     while (scope) {
168         DebuggerScope* nextScope = scope->m_next.get();
169         scope->m_next.clear();
170         scope->m_scope.clear(); // This also marks this scope as invalid.
171         scope = nextScope;
172     }
173 }
174
175 bool DebuggerScope::isCatchScope() const
176 {
177     return m_scope->isCatchScope();
178 }
179
180 bool DebuggerScope::isFunctionNameScope() const
181 {
182     return m_scope->isFunctionNameScopeObject();
183 }
184
185 bool DebuggerScope::isWithScope() const
186 {
187     return m_scope->isWithScope();
188 }
189
190 bool DebuggerScope::isGlobalScope() const
191 {
192     return m_scope->isGlobalObject();
193 }
194
195 bool DebuggerScope::isGlobalLexicalEnvironment() const
196 {
197     return m_scope->isGlobalLexicalEnvironment();
198 }
199
200 bool DebuggerScope::isClosureScope() const
201 {
202     // In the current debugger implementation, every function or eval will create an
203     // lexical environment object. Hence, a lexical environment object implies a
204     // function or eval scope.
205     return m_scope->isVarScope() || m_scope->isLexicalScope();
206 }
207
208 bool DebuggerScope::isNestedLexicalScope() const
209 {
210     return m_scope->isNestedLexicalScope();
211 }
212
213 String DebuggerScope::name() const
214 {
215     SymbolTable* symbolTable = m_scope->symbolTable();
216     if (!symbolTable)
217         return String();
218
219     CodeBlock* codeBlock = symbolTable->rareDataCodeBlock();
220     if (!codeBlock)
221         return String();
222
223     return String::fromUTF8(codeBlock->inferredName());
224 }
225
226 DebuggerLocation DebuggerScope::location() const
227 {
228     SymbolTable* symbolTable = m_scope->symbolTable();
229     if (!symbolTable)
230         return DebuggerLocation();
231
232     CodeBlock* codeBlock = symbolTable->rareDataCodeBlock();
233     if (!codeBlock)
234         return DebuggerLocation();
235
236     ScriptExecutable* executable = codeBlock->ownerScriptExecutable();
237     return DebuggerLocation(executable);
238 }
239
240 JSValue DebuggerScope::caughtValue(ExecState* exec) const
241 {
242     ASSERT(isCatchScope());
243     JSLexicalEnvironment* catchEnvironment = jsCast<JSLexicalEnvironment*>(m_scope.get());
244     SymbolTable* catchSymbolTable = catchEnvironment->symbolTable();
245     RELEASE_ASSERT(catchSymbolTable->size() == 1);
246     PropertyName errorName(catchSymbolTable->begin(catchSymbolTable->m_lock)->key.get());
247     PropertySlot slot(m_scope.get(), PropertySlot::InternalMethodType::Get);
248     bool success = catchEnvironment->getOwnPropertySlot(catchEnvironment, exec, errorName, slot);
249     RELEASE_ASSERT(success && slot.isValue());
250     return slot.getValue(exec, errorName);
251 }
252
253 } // namespace JSC