|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef vm_Interpreter_h |
|
8 #define vm_Interpreter_h |
|
9 |
|
10 /* |
|
11 * JS interpreter interface. |
|
12 */ |
|
13 |
|
14 #include "jsiter.h" |
|
15 #include "jspubtd.h" |
|
16 |
|
17 #include "vm/Stack.h" |
|
18 |
|
19 namespace js { |
|
20 |
|
21 class ScopeIter; |
|
22 |
|
23 /* |
|
24 * Announce to the debugger that the thread has entered a new JavaScript frame, |
|
25 * |frame|. Call whatever hooks have been registered to observe new frames, and |
|
26 * return a JSTrapStatus code indication how execution should proceed: |
|
27 * |
|
28 * - JSTRAP_CONTINUE: Continue execution normally. |
|
29 * |
|
30 * - JSTRAP_THROW: Throw an exception. ScriptDebugPrologue has set |cx|'s |
|
31 * pending exception to the value to be thrown. |
|
32 * |
|
33 * - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated |
|
34 * for running too long). ScriptDebugPrologue has cleared |cx|'s pending |
|
35 * exception. |
|
36 * |
|
37 * - JSTRAP_RETURN: Return from the new frame immediately. ScriptDebugPrologue |
|
38 * has set |frame|'s return value appropriately. |
|
39 */ |
|
40 extern JSTrapStatus |
|
41 ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc); |
|
42 |
|
43 /* |
|
44 * Announce to the debugger that the thread has exited a JavaScript frame, |frame|. |
|
45 * If |ok| is true, the frame is returning normally; if |ok| is false, the frame |
|
46 * is throwing an exception or terminating. |
|
47 * |
|
48 * Call whatever hooks have been registered to observe frame exits. Change cx's |
|
49 * current exception and |frame|'s return value to reflect the changes in behavior |
|
50 * the hooks request, if any. Return the new error/success value. |
|
51 * |
|
52 * This function may be called twice for the same outgoing frame; only the |
|
53 * first call has any effect. (Permitting double calls simplifies some |
|
54 * cases where an onPop handler's resumption value changes a return to a |
|
55 * throw, or vice versa: we can redirect to a complete copy of the |
|
56 * alternative path, containing its own call to ScriptDebugEpilogue.) |
|
57 */ |
|
58 extern bool |
|
59 ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool ok); |
|
60 |
|
61 /* |
|
62 * Announce to the debugger that an exception has been thrown and propagated |
|
63 * to |frame|. Call whatever hooks have been registered to observe this and |
|
64 * return a JSTrapStatus code indication how execution should proceed: |
|
65 * |
|
66 * - JSTRAP_CONTINUE: Continue throwing the current exception. |
|
67 * |
|
68 * - JSTRAP_THROW: Throw another value. DebugExceptionUnwind has set |cx|'s |
|
69 * pending exception to the new value. |
|
70 * |
|
71 * - JSTRAP_ERROR: Terminate execution. DebugExceptionUnwind has cleared |cx|'s |
|
72 * pending exception. |
|
73 * |
|
74 * - JSTRAP_RETURN: Return from |frame|. DebugExceptionUnwind has cleared |
|
75 * |cx|'s pending exception and set |frame|'s return value. |
|
76 */ |
|
77 extern JSTrapStatus |
|
78 DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc); |
|
79 |
|
80 /* |
|
81 * For a given |call|, convert null/undefined |this| into the global object for |
|
82 * the callee and replace other primitives with boxed versions. This assumes |
|
83 * that call.callee() is not strict mode code. This is the special/slow case of |
|
84 * ComputeThis. |
|
85 */ |
|
86 extern bool |
|
87 BoxNonStrictThis(JSContext *cx, const CallReceiver &call); |
|
88 |
|
89 extern JSObject * |
|
90 BoxNonStrictThis(JSContext *cx, HandleValue thisv); |
|
91 |
|
92 /* |
|
93 * Ensure that fp->thisValue() is the correct value of |this| for the scripted |
|
94 * call represented by |fp|. ComputeThis is necessary because fp->thisValue() |
|
95 * may be set to 'undefined' when 'this' should really be the global object (as |
|
96 * an optimization to avoid global-this computation). |
|
97 */ |
|
98 inline bool |
|
99 ComputeThis(JSContext *cx, AbstractFramePtr frame); |
|
100 |
|
101 enum MaybeConstruct { |
|
102 NO_CONSTRUCT = INITIAL_NONE, |
|
103 CONSTRUCT = INITIAL_CONSTRUCT |
|
104 }; |
|
105 |
|
106 /* |
|
107 * numToSkip is the number of stack values the expression decompiler should skip |
|
108 * before it reaches |v|. If it's -1, the decompiler will search the stack. |
|
109 */ |
|
110 extern bool |
|
111 ReportIsNotFunction(JSContext *cx, HandleValue v, int numToSkip = -1, |
|
112 MaybeConstruct construct = NO_CONSTRUCT); |
|
113 |
|
114 /* See ReportIsNotFunction comment for the meaning of numToSkip. */ |
|
115 extern JSObject * |
|
116 ValueToCallable(JSContext *cx, HandleValue v, int numToSkip = -1, |
|
117 MaybeConstruct construct = NO_CONSTRUCT); |
|
118 |
|
119 /* |
|
120 * Invoke assumes that the given args have been pushed on the top of the |
|
121 * VM stack. |
|
122 */ |
|
123 extern bool |
|
124 Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct = NO_CONSTRUCT); |
|
125 |
|
126 /* |
|
127 * This Invoke overload places the least requirements on the caller: it may be |
|
128 * called at any time and it takes care of copying the given callee, this, and |
|
129 * arguments onto the stack. |
|
130 */ |
|
131 extern bool |
|
132 Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, const Value *argv, |
|
133 MutableHandleValue rval); |
|
134 |
|
135 /* |
|
136 * This helper takes care of the infinite-recursion check necessary for |
|
137 * getter/setter calls. |
|
138 */ |
|
139 extern bool |
|
140 InvokeGetterOrSetter(JSContext *cx, JSObject *obj, Value fval, unsigned argc, Value *argv, |
|
141 MutableHandleValue rval); |
|
142 |
|
143 /* |
|
144 * InvokeConstructor implement a function call from a constructor context |
|
145 * (e.g. 'new') handling the the creation of the new 'this' object. |
|
146 */ |
|
147 extern bool |
|
148 InvokeConstructor(JSContext *cx, CallArgs args); |
|
149 |
|
150 /* See the fval overload of Invoke. */ |
|
151 extern bool |
|
152 InvokeConstructor(JSContext *cx, Value fval, unsigned argc, Value *argv, Value *rval); |
|
153 |
|
154 /* |
|
155 * Executes a script with the given scopeChain/this. The 'type' indicates |
|
156 * whether this is eval code or global code. To support debugging, the |
|
157 * evalFrame parameter can point to an arbitrary frame in the context's call |
|
158 * stack to simulate executing an eval in that frame. |
|
159 */ |
|
160 extern bool |
|
161 ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChain, const Value &thisv, |
|
162 ExecuteType type, AbstractFramePtr evalInFrame, Value *result); |
|
163 |
|
164 /* Execute a script with the given scopeChain as global code. */ |
|
165 extern bool |
|
166 Execute(JSContext *cx, HandleScript script, JSObject &scopeChain, Value *rval); |
|
167 |
|
168 class ExecuteState; |
|
169 class InvokeState; |
|
170 class GeneratorState; |
|
171 |
|
172 // RunState is passed to RunScript and RunScript then eiter passes it to the |
|
173 // interpreter or to the JITs. RunState contains all information we need to |
|
174 // construct an interpreter or JIT frame. |
|
175 class RunState |
|
176 { |
|
177 protected: |
|
178 enum Kind { Execute, Invoke, Generator }; |
|
179 Kind kind_; |
|
180 |
|
181 RootedScript script_; |
|
182 |
|
183 explicit RunState(JSContext *cx, Kind kind, JSScript *script) |
|
184 : kind_(kind), |
|
185 script_(cx, script) |
|
186 { } |
|
187 |
|
188 public: |
|
189 bool isExecute() const { return kind_ == Execute; } |
|
190 bool isInvoke() const { return kind_ == Invoke; } |
|
191 bool isGenerator() const { return kind_ == Generator; } |
|
192 |
|
193 ExecuteState *asExecute() const { |
|
194 JS_ASSERT(isExecute()); |
|
195 return (ExecuteState *)this; |
|
196 } |
|
197 InvokeState *asInvoke() const { |
|
198 JS_ASSERT(isInvoke()); |
|
199 return (InvokeState *)this; |
|
200 } |
|
201 GeneratorState *asGenerator() const { |
|
202 JS_ASSERT(isGenerator()); |
|
203 return (GeneratorState *)this; |
|
204 } |
|
205 |
|
206 JSScript *script() const { return script_; } |
|
207 |
|
208 virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx) = 0; |
|
209 virtual void setReturnValue(Value v) = 0; |
|
210 |
|
211 private: |
|
212 RunState(const RunState &other) MOZ_DELETE; |
|
213 RunState(const ExecuteState &other) MOZ_DELETE; |
|
214 RunState(const InvokeState &other) MOZ_DELETE; |
|
215 RunState(const GeneratorState &other) MOZ_DELETE; |
|
216 void operator=(const RunState &other) MOZ_DELETE; |
|
217 }; |
|
218 |
|
219 // Eval or global script. |
|
220 class ExecuteState : public RunState |
|
221 { |
|
222 ExecuteType type_; |
|
223 |
|
224 RootedValue thisv_; |
|
225 RootedObject scopeChain_; |
|
226 |
|
227 AbstractFramePtr evalInFrame_; |
|
228 Value *result_; |
|
229 |
|
230 public: |
|
231 ExecuteState(JSContext *cx, JSScript *script, const Value &thisv, JSObject &scopeChain, |
|
232 ExecuteType type, AbstractFramePtr evalInFrame, Value *result) |
|
233 : RunState(cx, Execute, script), |
|
234 type_(type), |
|
235 thisv_(cx, thisv), |
|
236 scopeChain_(cx, &scopeChain), |
|
237 evalInFrame_(evalInFrame), |
|
238 result_(result) |
|
239 { } |
|
240 |
|
241 Value *addressOfThisv() { return thisv_.address(); } |
|
242 JSObject *scopeChain() const { return scopeChain_; } |
|
243 ExecuteType type() const { return type_; } |
|
244 |
|
245 virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx); |
|
246 |
|
247 virtual void setReturnValue(Value v) { |
|
248 if (result_) |
|
249 *result_ = v; |
|
250 } |
|
251 }; |
|
252 |
|
253 // Data to invoke a function. |
|
254 class InvokeState : public RunState |
|
255 { |
|
256 CallArgs &args_; |
|
257 InitialFrameFlags initial_; |
|
258 bool useNewType_; |
|
259 |
|
260 public: |
|
261 InvokeState(JSContext *cx, CallArgs &args, InitialFrameFlags initial) |
|
262 : RunState(cx, Invoke, args.callee().as<JSFunction>().nonLazyScript()), |
|
263 args_(args), |
|
264 initial_(initial), |
|
265 useNewType_(false) |
|
266 { } |
|
267 |
|
268 bool useNewType() const { return useNewType_; } |
|
269 void setUseNewType() { useNewType_ = true; } |
|
270 |
|
271 bool constructing() const { return InitialFrameFlagsAreConstructing(initial_); } |
|
272 CallArgs &args() const { return args_; } |
|
273 |
|
274 virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx); |
|
275 |
|
276 virtual void setReturnValue(Value v) { |
|
277 args_.rval().set(v); |
|
278 } |
|
279 }; |
|
280 |
|
281 // Generator script. |
|
282 class GeneratorState : public RunState |
|
283 { |
|
284 JSContext *cx_; |
|
285 JSGenerator *gen_; |
|
286 JSGeneratorState futureState_; |
|
287 bool entered_; |
|
288 |
|
289 public: |
|
290 GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState); |
|
291 ~GeneratorState(); |
|
292 |
|
293 virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx); |
|
294 virtual void setReturnValue(Value) { } |
|
295 |
|
296 JSGenerator *gen() const { return gen_; } |
|
297 }; |
|
298 |
|
299 extern bool |
|
300 RunScript(JSContext *cx, RunState &state); |
|
301 |
|
302 extern bool |
|
303 StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal); |
|
304 |
|
305 extern bool |
|
306 LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal); |
|
307 |
|
308 /* === except that NaN is the same as NaN and -0 is not the same as +0. */ |
|
309 extern bool |
|
310 SameValue(JSContext *cx, const Value &v1, const Value &v2, bool *same); |
|
311 |
|
312 extern JSType |
|
313 TypeOfObject(JSObject *obj); |
|
314 |
|
315 extern JSType |
|
316 TypeOfValue(const Value &v); |
|
317 |
|
318 extern bool |
|
319 HasInstance(JSContext *cx, HandleObject obj, HandleValue v, bool *bp); |
|
320 |
|
321 // Unwind scope chain and iterator to match the static scope corresponding to |
|
322 // the given bytecode position. |
|
323 extern void |
|
324 UnwindScope(JSContext *cx, ScopeIter &si, jsbytecode *pc); |
|
325 |
|
326 /* |
|
327 * Unwind for an uncatchable exception. This means not running finalizers, etc; |
|
328 * just preserving the basic engine stack invariants. |
|
329 */ |
|
330 extern void |
|
331 UnwindForUncatchableException(JSContext *cx, const InterpreterRegs ®s); |
|
332 |
|
333 extern bool |
|
334 OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval, MutableHandleValue vp); |
|
335 |
|
336 class TryNoteIter |
|
337 { |
|
338 const InterpreterRegs ®s; |
|
339 RootedScript script; /* TryNotIter is always stack allocated. */ |
|
340 uint32_t pcOffset; |
|
341 JSTryNote *tn, *tnEnd; |
|
342 |
|
343 void settle(); |
|
344 |
|
345 public: |
|
346 explicit TryNoteIter(JSContext *cx, const InterpreterRegs ®s); |
|
347 bool done() const; |
|
348 void operator++(); |
|
349 JSTryNote *operator*() const { return tn; } |
|
350 }; |
|
351 |
|
352 /************************************************************************/ |
|
353 |
|
354 bool |
|
355 Throw(JSContext *cx, HandleValue v); |
|
356 |
|
357 bool |
|
358 GetProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp); |
|
359 |
|
360 bool |
|
361 CallProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp); |
|
362 |
|
363 bool |
|
364 GetScopeName(JSContext *cx, HandleObject obj, HandlePropertyName name, MutableHandleValue vp); |
|
365 |
|
366 bool |
|
367 GetScopeNameForTypeOf(JSContext *cx, HandleObject obj, HandlePropertyName name, |
|
368 MutableHandleValue vp); |
|
369 |
|
370 JSObject * |
|
371 Lambda(JSContext *cx, HandleFunction fun, HandleObject parent); |
|
372 |
|
373 JSObject * |
|
374 LambdaArrow(JSContext *cx, HandleFunction fun, HandleObject parent, HandleValue thisv); |
|
375 |
|
376 bool |
|
377 GetElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res); |
|
378 |
|
379 bool |
|
380 CallElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res); |
|
381 |
|
382 bool |
|
383 SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value, |
|
384 bool strict); |
|
385 bool |
|
386 SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value, |
|
387 bool strict, HandleScript script, jsbytecode *pc); |
|
388 |
|
389 bool |
|
390 InitElementArray(JSContext *cx, jsbytecode *pc, |
|
391 HandleObject obj, uint32_t index, HandleValue value); |
|
392 |
|
393 bool |
|
394 AddValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); |
|
395 |
|
396 bool |
|
397 SubValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); |
|
398 |
|
399 bool |
|
400 MulValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); |
|
401 |
|
402 bool |
|
403 DivValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); |
|
404 |
|
405 bool |
|
406 ModValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); |
|
407 |
|
408 bool |
|
409 UrshValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); |
|
410 |
|
411 template <bool strict> |
|
412 bool |
|
413 SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value); |
|
414 |
|
415 template <bool strict> |
|
416 bool |
|
417 DeleteProperty(JSContext *ctx, HandleValue val, HandlePropertyName name, bool *bv); |
|
418 |
|
419 template <bool strict> |
|
420 bool |
|
421 DeleteElement(JSContext *cx, HandleValue val, HandleValue index, bool *bv); |
|
422 |
|
423 bool |
|
424 DefFunOperation(JSContext *cx, HandleScript script, HandleObject scopeChain, HandleFunction funArg); |
|
425 |
|
426 bool |
|
427 SetCallOperation(JSContext *cx); |
|
428 |
|
429 bool |
|
430 GetAndClearException(JSContext *cx, MutableHandleValue res); |
|
431 |
|
432 bool |
|
433 DeleteNameOperation(JSContext *cx, HandlePropertyName name, HandleObject scopeObj, |
|
434 MutableHandleValue res); |
|
435 |
|
436 bool |
|
437 ImplicitThisOperation(JSContext *cx, HandleObject scopeObj, HandlePropertyName name, |
|
438 MutableHandleValue res); |
|
439 |
|
440 bool |
|
441 IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, MutableHandleValue rval); |
|
442 |
|
443 bool |
|
444 IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval); |
|
445 |
|
446 bool |
|
447 RunOnceScriptPrologue(JSContext *cx, HandleScript script); |
|
448 |
|
449 bool |
|
450 InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id, |
|
451 HandleObject val); |
|
452 |
|
453 bool |
|
454 InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandlePropertyName name, |
|
455 HandleObject val); |
|
456 |
|
457 bool |
|
458 EnterWithOperation(JSContext *cx, AbstractFramePtr frame, HandleValue val, HandleObject staticWith); |
|
459 |
|
460 |
|
461 bool |
|
462 InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleValue idval, |
|
463 HandleObject val); |
|
464 |
|
465 inline bool |
|
466 SetConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName name, HandleValue rval) |
|
467 { |
|
468 return JSObject::defineProperty(cx, varobj, name, rval, |
|
469 JS_PropertyStub, JS_StrictPropertyStub, |
|
470 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY); |
|
471 } |
|
472 |
|
473 } /* namespace js */ |
|
474 |
|
475 #endif /* vm_Interpreter_h */ |