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