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 Apple Computer, Inc. |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Library General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Library General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Library General Public License |
18 | * along with this library; see the file COPYING.LIB. If not, write to |
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | * Boston, MA 02110-1301, USA. |
21 | * |
22 | */ |
23 | |
24 | #ifndef _KJS_INTERPRETER_H_ |
25 | #define _KJS_INTERPRETER_H_ |
26 | |
27 | #include "ExecState.h" |
28 | #include "protect.h" |
29 | #include "value.h" |
30 | #include "types.h" |
31 | #include <wtf/HashMap.h> |
32 | |
33 | namespace KJS { |
34 | class Debugger; |
35 | class SavedBuiltins; |
36 | class TimeoutChecker; |
37 | class Package; |
38 | class ActivationImp; |
39 | class JSGlobalObject; |
40 | class StringImp; |
41 | |
42 | #if USE(BINDINGS) |
43 | namespace Bindings { |
44 | class RootObject; |
45 | } |
46 | #endif |
47 | |
48 | /** |
49 | * Interpreter objects can be used to evaluate ECMAScript code. Each |
50 | * interpreter has a global object which is used for the purposes of code |
51 | * evaluation, and also provides access to built-in properties such as |
52 | * " Object" and "Number". |
53 | */ |
54 | class KJS_EXPORT Interpreter { |
55 | friend class Collector; |
56 | friend class TimeoutChecker; |
57 | public: |
58 | /** |
59 | * Creates a new interpreter. The supplied object will be used as the global |
60 | * object for all scripts executed with this interpreter. During |
61 | * construction, all the standard properties such as "Object" and "Number" |
62 | * will be added to the global object. |
63 | * |
64 | * Note: You should not use the same global object for multiple |
65 | * interpreters. |
66 | * |
67 | * This is due do the fact that the built-in properties are set in the |
68 | * constructor, and if these objects have been modified from another |
69 | * interpreter (e.g. a script modifying String.prototype), the changes will |
70 | * be overridden. |
71 | * |
72 | * @param globalObject The object to use as the global object for this interpreter |
73 | */ |
74 | Interpreter(JSGlobalObject* globalObject); |
75 | /** |
76 | * Creates a new interpreter. A global object will be created and |
77 | * initialized with the standard global properties. |
78 | */ |
79 | Interpreter(); |
80 | |
81 | /** |
82 | * Returns the object that is used as the global object during all script |
83 | * execution performed by this interpreter |
84 | */ |
85 | JSGlobalObject* globalObject() const; |
86 | void initGlobalObject(); |
87 | |
88 | /** |
89 | * Returns the execution state object which can be used to execute |
90 | * scripts using this interpreter at a the "global" level, i.e. one |
91 | * with a execution context that has the global object as the "this" |
92 | * value, and who's scope chain contains only the global object. |
93 | * |
94 | * Note: this pointer remains constant for the life of the interpreter |
95 | * and should not be manually deleted. |
96 | * |
97 | * @return The interpreter global execution state object |
98 | */ |
99 | virtual ExecState *globalExec(); |
100 | |
101 | /** |
102 | * Sets the package instance that will be used to resolve the |
103 | * first level of identifiers of import statements. |
104 | * |
105 | * If no package is set which will make any "import" script |
106 | * statement fail with an error. This is the default in e.g. a |
107 | * Web browser where package imports should be disabled for |
108 | * security reasons. |
109 | */ |
110 | void setGlobalPackage(Package* p); |
111 | |
112 | /** |
113 | * Returns the package that was installed to handle top level |
114 | * package requests. Returns 0, the default, when no package was |
115 | * set. |
116 | * |
117 | * @return The global package |
118 | */ |
119 | Package* globalPackage(); |
120 | |
121 | /** |
122 | * Parses the supplied ECMAScript code and checks for syntax errors. |
123 | * |
124 | * @param code The code to check |
125 | * @param sourceURL A URL denoting the origin of the code |
126 | * @param startingLineNumber The line offset within an embedding context |
127 | * @return A normal completion if there were no syntax errors in the code, |
128 | * otherwise a throw completion with the syntax error as its value. |
129 | */ |
130 | Completion checkSyntax(const UString& sourceURL, int startingLineNumber, const UString& code); |
131 | Completion checkSyntax(const UString& sourceURL, int startingLineNumber, const UChar* code, int codeLength); |
132 | |
133 | /** |
134 | * Evaluates the supplied ECMAScript code. |
135 | * |
136 | * Since this method returns a Completion, you should check the type of |
137 | * completion to detect an error or before attempting to access the returned |
138 | * value. For example, if an error occurs during script execution and is not |
139 | * caught by the script, the completion type will be Throw. |
140 | * |
141 | * If the supplied code is invalid, a SyntaxError will be thrown. |
142 | * |
143 | * @param sourceURL A URL denoting the origin of the code |
144 | * @param startingLineNumber The line offset within an embedding context |
145 | * @param code The code to evaluate |
146 | * @param codeLength The length of the code to evaluate |
147 | * @param thisV The value to pass in as the "this" value for the script |
148 | * execution. This should either be jsNull() or an Object. |
149 | * @return A completion object representing the result of the execution. |
150 | */ |
151 | Completion evaluate(const UString& sourceURL, int startingLineNumber, const UChar* code, int codeLength, JSValue* thisV = 0); |
152 | Completion evaluate(const UString& sourceURL, int startingLineNumber, const UString& code, JSValue* thisV = 0); |
153 | |
154 | /** |
155 | * Pretty-prints the supplied ECMAScript code after checking it |
156 | * for syntax errors. |
157 | * |
158 | * @param sourceURL A URL denoting the origin of the code |
159 | * @param startingLineNumber The line offset within an embedding context |
160 | * @param codeIn The code to check |
161 | * @param codeIn Pointer to string that will contain reformatted code |
162 | * upon successful parsing. |
163 | * @return A normal completion if there were no syntax errors in the code, |
164 | * otherwise a throw completion with the syntax error as its value. |
165 | */ |
166 | static bool normalizeCode(const UString& codeIn, UString* codeOut, |
167 | int* errLine = 0, UString* errMsg = 0); |
168 | |
169 | /** |
170 | * Returns the builtin "Object" object. This is the object that was set |
171 | * as a property of the global object during construction; if the property |
172 | * is replaced by script code, this method will still return the original |
173 | * object. |
174 | * |
175 | * @return The builtin "Object" object |
176 | */ |
177 | JSObject *builtinObject() const; |
178 | |
179 | /** |
180 | * Returns the builtin "Function" object. |
181 | */ |
182 | JSObject *builtinFunction() const; |
183 | |
184 | /** |
185 | * Returns the builtin "Array" object. |
186 | */ |
187 | JSObject *builtinArray() const; |
188 | |
189 | /** |
190 | * Returns the builtin "Boolean" object. |
191 | */ |
192 | JSObject *builtinBoolean() const; |
193 | |
194 | /** |
195 | * Returns the builtin "String" object. |
196 | */ |
197 | JSObject *builtinString() const; |
198 | |
199 | /** |
200 | * Returns the builtin "Number" object. |
201 | */ |
202 | JSObject *builtinNumber() const; |
203 | |
204 | /** |
205 | * Returns the builtin "Date" object. |
206 | */ |
207 | JSObject *builtinDate() const; |
208 | |
209 | /** |
210 | * Returns the builtin "RegExp" object. |
211 | */ |
212 | JSObject *builtinRegExp() const; |
213 | |
214 | /** |
215 | * Returns the builtin "Error" object. |
216 | */ |
217 | JSObject *builtinError() const; |
218 | |
219 | /** |
220 | * Returns the builtin "Object.prototype" object. |
221 | */ |
222 | JSObject *builtinObjectPrototype() const; |
223 | |
224 | /** |
225 | * Returns the builtin "Function.prototype" object. |
226 | */ |
227 | JSObject *builtinFunctionPrototype() const; |
228 | |
229 | /** |
230 | * Returns the builtin "Array.prototype" object. |
231 | */ |
232 | JSObject *builtinArrayPrototype() const; |
233 | |
234 | /** |
235 | * Returns the builtin "Boolean.prototype" object. |
236 | */ |
237 | JSObject *builtinBooleanPrototype() const; |
238 | |
239 | /** |
240 | * Returns the builtin "String.prototype" object. |
241 | */ |
242 | JSObject *builtinStringPrototype() const; |
243 | |
244 | /** |
245 | * Returns the builtin "Number.prototype" object. |
246 | */ |
247 | JSObject *builtinNumberPrototype() const; |
248 | |
249 | /** |
250 | * Returns the builtin "Date.prototype" object. |
251 | */ |
252 | JSObject *builtinDatePrototype() const; |
253 | |
254 | /** |
255 | * Returns the builtin "RegExp.prototype" object. |
256 | */ |
257 | JSObject *builtinRegExpPrototype() const; |
258 | |
259 | /** |
260 | * Returns the builtin "Error.prototype" object. |
261 | */ |
262 | JSObject *builtinErrorPrototype() const; |
263 | |
264 | /** |
265 | * The initial value of "Error" global property |
266 | */ |
267 | JSObject *builtinEvalError() const; |
268 | JSObject *builtinRangeError() const; |
269 | JSObject *builtinReferenceError() const; |
270 | JSObject *builtinSyntaxError() const; |
271 | JSObject *builtinTypeError() const; |
272 | JSObject *builtinURIError() const; |
273 | |
274 | JSObject *builtinEvalErrorPrototype() const; |
275 | JSObject *builtinRangeErrorPrototype() const; |
276 | JSObject *builtinReferenceErrorPrototype() const; |
277 | JSObject *builtinSyntaxErrorPrototype() const; |
278 | JSObject *builtinTypeErrorPrototype() const; |
279 | JSObject *builtinURIErrorPrototype() const; |
280 | |
281 | enum CompatMode { NativeMode, IECompat, NetscapeCompat }; |
282 | /** |
283 | * Call this to enable a compatibility mode with another browser. |
284 | * (by default konqueror is in "native mode"). |
285 | * Currently, in KJS, this only changes the behavior of Date::getYear() |
286 | * which returns the full year under IE. |
287 | */ |
288 | void setCompatMode(CompatMode mode) { m_compatMode = mode; } |
289 | CompatMode compatMode() const { return m_compatMode; } |
290 | |
291 | /** |
292 | * Run the garbage collection. Returns true when at least one object |
293 | * was collected; false otherwise. |
294 | */ |
295 | static bool collect(); |
296 | |
297 | /** |
298 | * Called during the mark phase of the garbage collector. Subclasses |
299 | * implementing custom mark methods must make sure to chain to this one. |
300 | */ |
301 | virtual void mark(bool currentThreadIsMainThread); |
302 | |
303 | /** |
304 | * This marks all GC heap resources stored as optimizations; |
305 | * and which have their lifetime managed by the appropriate AST. |
306 | * It's static since code can survive the interpreter by a bit. |
307 | */ |
308 | static void markSourceCachedObjects(); |
309 | |
310 | /** |
311 | * Provides a way to distinguish derived classes. |
312 | * Only useful if you reimplement Interpreter and if different kind of |
313 | * interpreters are created in the same process. |
314 | * The base class returns 0, the ECMA-bindings interpreter returns 1. |
315 | */ |
316 | virtual int rtti() { return 0; } |
317 | |
318 | static bool shouldPrintExceptions(); |
319 | static void setShouldPrintExceptions(bool); |
320 | |
321 | void saveBuiltins (SavedBuiltins&) const; |
322 | void restoreBuiltins (const SavedBuiltins&); |
323 | |
324 | /** |
325 | * Determine if the it is 'safe' to execute code in the target interpreter from an |
326 | * object that originated in this interpreter. This check is used to enforce WebCore |
327 | * cross frame security rules. In particular, attempts to access 'bound' objects are |
328 | * not allowed unless isSafeScript returns true. |
329 | */ |
330 | virtual bool isSafeScript(const Interpreter*) { return true; } |
331 | |
332 | #if USE(BINDINGS) |
333 | virtual void *createLanguageInstanceForValue(ExecState*, int language, JSObject* value, const Bindings::RootObject* origin, const Bindings::RootObject* current); |
334 | #endif |
335 | |
336 | // Chained list of interpreters (ring) |
337 | static Interpreter* firstInterpreter() { return s_hook; } |
338 | Interpreter* nextInterpreter() const { return next; } |
339 | Interpreter* prevInterpreter() const { return prev; } |
340 | |
341 | Debugger* debugger() const { return m_debugger; } |
342 | void setDebugger(Debugger* d) { m_debugger = d; } |
343 | |
344 | void setExecState(ExecState* e) { m_execState = e; } |
345 | |
346 | // Note: may be 0, if in globalExec |
347 | ExecState* execState() { return m_execState ? m_execState : &m_globalExec; } |
348 | |
349 | void setTimeoutTime(unsigned timeoutTime) { m_timeoutTime = timeoutTime; } |
350 | |
351 | void startTimeoutCheck(); |
352 | void stopTimeoutCheck(); |
353 | |
354 | // Resets the timer to full time if it's running |
355 | void restartTimeoutCheck(); |
356 | |
357 | void pauseTimeoutCheck(); |
358 | void resumeTimeoutCheck(); |
359 | |
360 | bool checkTimeout(); |
361 | |
362 | void ref() { ++m_refCount; } |
363 | void deref() { if (--m_refCount <= 0) delete this; } |
364 | int refCount() const { return m_refCount; } |
365 | |
366 | unsigned char* stackAlloc(size_t size) { |
367 | unsigned char* nextPtr = stackPtr + size; |
368 | if (nextPtr <= stackEnd) { |
369 | unsigned char* toRet = stackPtr; |
370 | stackPtr = nextPtr; |
371 | return toRet; |
372 | } |
373 | return extendStack(size); |
374 | } |
375 | |
376 | void stackFree(size_t size) { stackPtr-= size; } // ### shrink it? |
377 | |
378 | ActivationImp* getRecycledActivation() { |
379 | ActivationImp* out = 0; |
380 | if (m_numCachedActivations) { |
381 | m_numCachedActivations--; |
382 | out = m_cachedActivations[m_numCachedActivations]; |
383 | } |
384 | return out; |
385 | } |
386 | |
387 | void recycleActivation(ActivationImp* act); |
388 | |
389 | // Global string table management. This is used from StringNode |
390 | // to cache StringImp's for string literals. We keep refcounts |
391 | // to permit multiple ones to use the same value. |
392 | static StringImp* internString(const UString& literal); |
393 | static void releaseInternedString(const UString& literal); |
394 | |
395 | typedef WTF::HashMap<UString::Rep*, std::pair<KJS::StringImp*, int> > InternedStringsTable; |
396 | private: |
397 | static void markInternedStringsTable(); |
398 | |
399 | // This creates a table if needed |
400 | static void initInternedStringsTable(); |
401 | |
402 | static InternedStringsTable* s_internedStrings; |
403 | |
404 | protected: |
405 | virtual ~Interpreter(); // only deref should delete us |
406 | virtual bool shouldInterruptScript() const { return true; } |
407 | |
408 | long m_timeoutTime; |
409 | |
410 | private: |
411 | bool handleTimeout(); |
412 | void init(); |
413 | void printException(const Completion& c, const UString& sourceURL); |
414 | |
415 | /** |
416 | * This constructor is not implemented, in order to prevent |
417 | * copy-construction of Interpreter objects. You should always pass around |
418 | * pointers to an interpreter instance instead. |
419 | */ |
420 | Interpreter(const Interpreter&); |
421 | |
422 | /** |
423 | * This operator is not implemented, in order to prevent assignment of |
424 | * Interpreter objects. You should always pass around pointers to an |
425 | * interpreter instance instead. |
426 | */ |
427 | Interpreter operator=(const Interpreter&); |
428 | |
429 | int m_refCount; |
430 | |
431 | JSGlobalObject* m_globalObject; |
432 | GlobalExecState m_globalExec; |
433 | Package *globPkg; |
434 | |
435 | // Execution stack stuff for this interpreter. |
436 | unsigned char* stackBase; // lowest address in the array |
437 | unsigned char* stackPtr; // current top/next to allocate |
438 | unsigned char* stackEnd; // last address in the stack |
439 | unsigned char* extendStack(size_t needed); |
440 | |
441 | // A list of cached activations |
442 | enum {MaxCachedActivations = 32}; |
443 | |
444 | ActivationImp* m_cachedActivations[MaxCachedActivations]; |
445 | int m_numCachedActivations; |
446 | |
447 | // Chained list of interpreters (ring) - for collector |
448 | static Interpreter* s_hook; |
449 | Interpreter *next, *prev; |
450 | |
451 | int m_recursion; |
452 | |
453 | Debugger* m_debugger; |
454 | ExecState* m_execState; |
455 | CompatMode m_compatMode; |
456 | |
457 | TimeoutChecker* m_timeoutChecker; |
458 | bool m_timedOut; |
459 | |
460 | unsigned m_startTimeoutCheckCount; |
461 | unsigned m_pauseTimeoutCheckCount; |
462 | |
463 | // Helper for setting constructors, making sure their function names are OK |
464 | void putNamedConstructor(const char* name, JSObject* value); |
465 | |
466 | ProtectedPtr<JSObject> m_Object; |
467 | ProtectedPtr<JSObject> m_Function; |
468 | ProtectedPtr<JSObject> m_Array; |
469 | ProtectedPtr<JSObject> m_Boolean; |
470 | ProtectedPtr<JSObject> m_String; |
471 | ProtectedPtr<JSObject> m_Number; |
472 | ProtectedPtr<JSObject> m_Date; |
473 | ProtectedPtr<JSObject> m_RegExp; |
474 | ProtectedPtr<JSObject> m_Error; |
475 | |
476 | ProtectedPtr<JSObject> m_ObjectPrototype; |
477 | ProtectedPtr<JSObject> m_FunctionPrototype; |
478 | ProtectedPtr<JSObject> m_ArrayPrototype; |
479 | ProtectedPtr<JSObject> m_BooleanPrototype; |
480 | ProtectedPtr<JSObject> m_StringPrototype; |
481 | ProtectedPtr<JSObject> m_NumberPrototype; |
482 | ProtectedPtr<JSObject> m_DatePrototype; |
483 | ProtectedPtr<JSObject> m_RegExpPrototype; |
484 | ProtectedPtr<JSObject> m_ErrorPrototype; |
485 | |
486 | ProtectedPtr<JSObject> m_EvalError; |
487 | ProtectedPtr<JSObject> m_RangeError; |
488 | ProtectedPtr<JSObject> m_ReferenceError; |
489 | ProtectedPtr<JSObject> m_SyntaxError; |
490 | ProtectedPtr<JSObject> m_TypeError; |
491 | ProtectedPtr<JSObject> m_UriError; |
492 | |
493 | ProtectedPtr<JSObject> m_EvalErrorPrototype; |
494 | ProtectedPtr<JSObject> m_RangeErrorPrototype; |
495 | ProtectedPtr<JSObject> m_ReferenceErrorPrototype; |
496 | ProtectedPtr<JSObject> m_SyntaxErrorPrototype; |
497 | ProtectedPtr<JSObject> m_TypeErrorPrototype; |
498 | ProtectedPtr<JSObject> m_UriErrorPrototype; |
499 | }; |
500 | |
501 | inline bool Interpreter::checkTimeout() |
502 | { |
503 | if (!m_timedOut) |
504 | return false; |
505 | |
506 | return handleTimeout(); |
507 | } |
508 | |
509 | /** |
510 | * Interface to set enhanced Unicode support functions. By default |
511 | * the interpreter will use the standard C library functions. |
512 | * |
513 | * @internal |
514 | */ |
515 | class KJS_EXPORT UnicodeSupport |
516 | { |
517 | public: |
518 | UnicodeSupport(); |
519 | |
520 | typedef bool (*CharCategoryFunction)(int c); |
521 | static void setIdentStartChecker(CharCategoryFunction f); |
522 | static void setIdentPartChecker(CharCategoryFunction f); |
523 | |
524 | typedef int (*StringConversionFunction)(uint16_t* str, int strLength, |
525 | uint16_t*& destIfNeeded); |
526 | static void setToLowerFunction(StringConversionFunction f); |
527 | static void setToUpperFunction(StringConversionFunction f); |
528 | }; |
529 | |
530 | /** |
531 | * Define a Qt-based version of the Unicode support functions. |
532 | * |
533 | * @internal |
534 | */ |
535 | #define KJS_QT_UNICODE_IMPL \ |
536 | namespace KJS { \ |
537 | static bool qtIdentStart(int c) { if (c & 0xffff0000) return false; QChar::Category cat = QChar((unsigned short)c).category(); return cat == QChar::Letter_Uppercase || cat == QChar::Letter_Lowercase || cat == QChar::Letter_Titlecase || cat == QChar::Letter_Modifier || cat == QChar::Letter_Other || c == '$' || c == '_'; } \ |
538 | static bool qtIdentPart(int c) { if (c & 0xffff0000) return false; QChar::Category cat = QChar((unsigned short)c).category(); return cat == QChar::Letter_Uppercase || cat == QChar::Letter_Lowercase || cat == QChar::Letter_Titlecase || cat == QChar::Letter_Modifier || cat == QChar::Letter_Other || cat == QChar::Mark_NonSpacing || cat == QChar::Mark_SpacingCombining || cat == QChar::Number_DecimalDigit || cat == QChar::Punctuation_Connector || c == '$' || c == '_'; } \ |
539 | static int qtToLower(uint16_t* str, int strLength, uint16_t*& destIfNeeded) { \ |
540 | destIfNeeded = 0; \ |
541 | for (int i = 0; i < strLength; ++i) \ |
542 | str[i] = QChar(str[i]).toLower().unicode(); \ |
543 | return strLength; } \ |
544 | static int qtToUpper(uint16_t* str, int strLength, uint16_t*& destIfNeeded) { \ |
545 | destIfNeeded = 0; \ |
546 | for (int i = 0; i < strLength; ++i) \ |
547 | str[i] = QChar(str[i]).toUpper().unicode(); \ |
548 | return strLength; } \ |
549 | } |
550 | |
551 | /** |
552 | * Set the Qt-based version of the Unicode support functions. |
553 | * |
554 | * @internal |
555 | */ |
556 | #define KJS_QT_UNICODE_SET \ |
557 | { KJS::UnicodeSupport::setIdentStartChecker(KJS::qtIdentStart); \ |
558 | KJS::UnicodeSupport::setIdentPartChecker(KJS::qtIdentPart); \ |
559 | KJS::UnicodeSupport::setToLowerFunction(KJS::qtToLower); \ |
560 | KJS::UnicodeSupport::setToUpperFunction(KJS::qtToUpper); } |
561 | |
562 | } // namespace |
563 | |
564 | #endif // _KJS_INTERPRETER_H_ |
565 | |