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
33namespace KJS {
34
35Interpreter* 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
48void 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
86void ExecState::mark()
87{
88 for (ExecState* exec = this; exec; exec = exec->m_callingExec)
89 exec->markSelf();
90}
91
92ExecState::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
117ExecState::~ExecState()
118{
119 m_interpreter->setExecState(m_savedExec);
120}
121
122void ExecState::pushExceptionHandler(HandlerType type, Addr addr)
123{
124 m_exceptionHandlers.append(ExceptionHandler(type, addr));
125}
126
127void ExecState::popExceptionHandler()
128{
129 m_exceptionHandlers.removeLast();
130}
131
132
133JSValue* 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
164void ExecState::setException(JSValue* e)
165{
166 if (e)
167 setAbruptCompletion(Completion(Throw, e));
168 else
169 clearException();
170}
171
172
173void 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
216void 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
239GlobalExecState::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
247InterpreterExecState::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
260EvalExecState::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
283FunctionExecState::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