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