1 | // -*- c-basic-offset: 2 -*- |
2 | /* |
3 | * This file is part of the KDE libraries |
4 | * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) |
5 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) |
6 | * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. |
7 | * Copyright (C) 2008 Maksim Orlovich (maksim@kde.org) |
8 | * |
9 | * This library is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU Library General Public |
11 | * License as published by the Free Software Foundation; either |
12 | * version 2 of the License, or (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * Library General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Library General Public License |
20 | * along with this library; see the file COPYING.LIB. If not, write to |
21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
22 | * Boston, MA 02110-1301, USA. |
23 | * |
24 | */ |
25 | |
26 | #include "ExecState.h" |
27 | #include "function.h" |
28 | #include "scriptfunction.h" |
29 | #include "internal.h" |
30 | #include "nodes.h" |
31 | #include "debugger.h" |
32 | |
33 | namespace KJS { |
34 | |
35 | Interpreter* ExecState::lexicalInterpreter() const |
36 | { |
37 | JSObject* outerScope = scopeChain().bottom(); |
38 | assert(outerScope->isGlobalObject()); |
39 | |
40 | Interpreter* result = static_cast<JSGlobalObject*>(outerScope)->interpreter(); |
41 | |
42 | if (!result) |
43 | return dynamicInterpreter(); |
44 | |
45 | return result; |
46 | } |
47 | |
48 | void ExecState::markSelf() |
49 | { |
50 | if (m_codeType != FunctionCode && m_localStore) { |
51 | //### some code dupe here with JSVariableObject::mark. Not sure how to best |
52 | // restructure. |
53 | |
54 | // Note: the m_localStore check is needed here, since for non-function code, |
55 | // we may create function object in declaration elaboration stage, before |
56 | // compilation and set up of this |
57 | size_t size = m_localStoreSize; |
58 | LocalStorageEntry* entries = m_localStore; |
59 | |
60 | for (size_t i = 0; i < size; ++i) { |
61 | JSValue* value = entries[i].val.valueVal; |
62 | if (!(entries[i].attributes & DontMark) && !value->marked()) |
63 | value->mark(); |
64 | } |
65 | } |
66 | |
67 | for (size_t i = 0; i < m_deferredCompletions.size(); ++i) { |
68 | JSValue* e = m_deferredCompletions[i].value(); |
69 | if (e && !e->marked()) |
70 | e->mark(); |
71 | } |
72 | |
73 | JSValue* e = m_completion.value(); |
74 | if (e && !e->marked()) |
75 | e->mark(); |
76 | |
77 | scope.mark(); |
78 | |
79 | // Propagate up to other eval chains.. |
80 | if (m_savedExec && m_savedExec != m_callingExec) { |
81 | ASSERT(m_savedExec != this); |
82 | m_savedExec->mark(); |
83 | } |
84 | } |
85 | |
86 | void ExecState::mark() |
87 | { |
88 | for (ExecState* exec = this; exec; exec = exec->m_callingExec) |
89 | exec->markSelf(); |
90 | } |
91 | |
92 | ExecState::ExecState(Interpreter* intp, ExecState* save) : |
93 | m_interpreter(intp), |
94 | m_propertyNames(CommonIdentifiers::shared()), |
95 | m_callingExec(0), |
96 | m_savedExec(save), |
97 | m_currentBody(0), |
98 | m_function(0), |
99 | m_localStore(0), |
100 | m_pcBase(0), |
101 | m_pc(0), |
102 | m_machineLocalStore(0) |
103 | { |
104 | /** |
105 | The reason we need m_savedExec and can't just be content with m_callingExec is two-fold. |
106 | First of all, in many cases KHTML (and ktranscript) invoke functions such as event handlers |
107 | on globalExec. When that happens, we still need to be able to mark the previous call-chain. |
108 | Also, it is possible for the client to call Interpreter::evaluate again; and we still |
109 | need to mark things from the outside when that happens |
110 | */ |
111 | |
112 | if (m_callingExec && m_savedExec && m_callingExec != m_savedExec) |
113 | assert(m_callingExec == intp->globalExec()); |
114 | m_interpreter->setExecState(this); |
115 | } |
116 | |
117 | ExecState::~ExecState() |
118 | { |
119 | m_interpreter->setExecState(m_savedExec); |
120 | } |
121 | |
122 | void ExecState::pushExceptionHandler(HandlerType type, Addr addr) |
123 | { |
124 | m_exceptionHandlers.append(ExceptionHandler(type, addr)); |
125 | } |
126 | |
127 | void ExecState::popExceptionHandler() |
128 | { |
129 | m_exceptionHandlers.removeLast(); |
130 | } |
131 | |
132 | |
133 | JSValue* ExecState::reactivateCompletion(bool insideTryFinally) |
134 | { |
135 | // First, unwind and get the old completion.. |
136 | ASSERT(m_exceptionHandlers.last().type == RemoveDeferred); |
137 | popExceptionHandler(); |
138 | Completion comp = m_deferredCompletions.last(); |
139 | m_deferredCompletions.removeLast(); |
140 | |
141 | // Now, our behavior behaves on whether we're inside an another |
142 | // try..finally or not. If we're, we must route even |
143 | // continue/break/return completions via the EH machinery; |
144 | // if not, we execute them directly |
145 | if (comp.complType() == Normal) { |
146 | // We just straight fell into 'finally'. Nothing fancy to do. |
147 | return 0; |
148 | } |
149 | |
150 | if (comp.complType() == Throw || insideTryFinally) { |
151 | setAbruptCompletion(comp); |
152 | } else { |
153 | if (comp.complType() == ReturnValue) { |
154 | return comp.value(); |
155 | } else { |
156 | assert(comp.complType() == Break || comp.complType() == Continue); |
157 | *m_pc = m_pcBase + comp.target(); |
158 | } |
159 | } |
160 | |
161 | return 0; |
162 | } |
163 | |
164 | void ExecState::setException(JSValue* e) |
165 | { |
166 | if (e) |
167 | setAbruptCompletion(Completion(Throw, e)); |
168 | else |
169 | clearException(); |
170 | } |
171 | |
172 | |
173 | void ExecState::setAbruptCompletion(Completion comp) |
174 | { |
175 | // If we already had an exception, merely update the object, to permit |
176 | // users to refine the exception, being careful not to double-unwind. |
177 | // However, warn about it in debug builds. |
178 | if (hadException()) { |
179 | #ifndef NDEBUG |
180 | printInfo(this, "warning: overriding already set exception " , m_completion.value()); |
181 | printInfo(this, "with " , comp.value()); |
182 | #endif |
183 | |
184 | m_completion = comp; |
185 | return; |
186 | } |
187 | |
188 | // Trace to debugger if needed. |
189 | Debugger* dbg = dynamicInterpreter()->debugger(); |
190 | if (dbg && comp.complType() == Throw) |
191 | dbg->reportException(this, comp.value()); |
192 | |
193 | m_completion = comp; |
194 | |
195 | while (!m_exceptionHandlers.isEmpty()) { |
196 | switch (m_exceptionHandlers.last().type) { |
197 | case JumpToCatch: |
198 | *m_pc = m_pcBase + m_exceptionHandlers.last().dest; |
199 | m_exceptionHandlers.removeLast(); |
200 | return; // done handling it |
201 | case PopScope: |
202 | popScope(); |
203 | m_exceptionHandlers.removeLast(); |
204 | continue; // get the next handler |
205 | case RemoveDeferred: |
206 | m_deferredCompletions.removeLast(); |
207 | m_exceptionHandlers.removeLast(); |
208 | continue; // get the next handler |
209 | case Silent: |
210 | // Exception blocked by tracing code. nothing to do. |
211 | return; |
212 | } |
213 | } |
214 | } |
215 | |
216 | void ExecState::quietUnwind(int depth) |
217 | { |
218 | ASSERT(m_exceptionHandlers.size() >= size_t(depth)); |
219 | for (int e = 0; e < depth; ++e) { |
220 | HandlerType type = m_exceptionHandlers.last().type; |
221 | m_exceptionHandlers.removeLast(); |
222 | |
223 | switch (type) { |
224 | case JumpToCatch: |
225 | break; //Nothing to do here! |
226 | case PopScope: |
227 | popScope(); |
228 | break; |
229 | case RemoveDeferred: |
230 | m_deferredCompletions.removeLast(); |
231 | break; |
232 | case Silent: |
233 | ASSERT(0); // Should not happen in the middle of the code. |
234 | break; |
235 | } |
236 | } |
237 | } |
238 | |
239 | GlobalExecState::GlobalExecState(Interpreter* intp, JSGlobalObject* glob): ExecState(intp, 0 /* nothing else constructed yet*/) |
240 | { |
241 | scope.push(glob); |
242 | m_codeType = GlobalCode; |
243 | m_variable = glob; |
244 | m_thisVal = glob; |
245 | } |
246 | |
247 | InterpreterExecState::InterpreterExecState(Interpreter* intp, JSGlobalObject* glob, |
248 | JSObject* thisObject, ProgramNode* body): |
249 | ExecState(intp, intp->execState()) |
250 | { |
251 | m_currentBody = body; |
252 | scope.push(glob); |
253 | m_codeType = GlobalCode; |
254 | m_variable = glob; |
255 | // Per 10.2.1, we should use the global object here, but |
256 | // Interpreter::evaluate permits it to be overridden, e.g. for LiveConnect. |
257 | m_thisVal = thisObject; |
258 | } |
259 | |
260 | EvalExecState::EvalExecState(Interpreter* intp, JSGlobalObject* glob, |
261 | ProgramNode* body, ExecState* callingExecState): |
262 | ExecState(intp, intp->execState()) |
263 | { |
264 | m_currentBody = body; |
265 | m_codeType = EvalCode; |
266 | m_callingExec = callingExecState; |
267 | if (m_callingExec) { |
268 | scope = m_callingExec->scopeChain(); |
269 | m_variable = m_callingExec->variableObject(); |
270 | m_thisVal = m_callingExec->thisValue(); |
271 | return; |
272 | } |
273 | |
274 | // 10.2.2 talks about the behavior w/o a calling context here, |
275 | // saying it should be like global code. This can not happen |
276 | // in actual JS code, but it may be synthesized by e.g. |
277 | // the JS debugger calling 'eval' itself, from globalExec |
278 | m_thisVal = glob; |
279 | m_variable = glob; |
280 | scope.push(glob); |
281 | } |
282 | |
283 | FunctionExecState::FunctionExecState(Interpreter* intp, JSObject* thisObject, |
284 | FunctionBodyNode* body, ExecState* callingExecState, |
285 | FunctionImp* function): ExecState(intp, intp->execState()) |
286 | { |
287 | m_function = function; |
288 | m_currentBody = body; |
289 | |
290 | m_codeType = FunctionCode; |
291 | m_callingExec = callingExecState; |
292 | scope = function->scope(); // Activation will push itself when setting up |
293 | m_variable = m_interpreter->getRecycledActivation();// TODO: DontDelete ? (ECMA 10.2.3) |
294 | if (!m_variable) |
295 | m_variable = new ActivationImp(); |
296 | m_thisVal = thisObject; |
297 | } |
298 | |
299 | } // namespace KJS |
300 | |
301 | // kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on; |
302 | |