source: webkit/trunk/JavaScriptCore/kjs/JSGlobalObjectFunctions.cpp@ 37622

Last change on this file since 37622 was 37622, checked in by [email protected], 17 years ago

JavaScriptCore:

2008-10-15 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Fixed https://bugs.webkit.org/show_bug.cgi?id=21345
Start the debugger without reloading the inspected page

  • VM/CodeBlock.h: (JSC::EvalCodeCache::get): Updated for tweak to parsing API.
  • kjs/CollectorHeapIterator.h: Added. An iterator for the object heap, which we use to find all the live functions and recompile them.
  • kjs/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate): Updated for tweak to parsing API.
  • kjs/FunctionConstructor.cpp: (JSC::constructFunction): Updated for tweak to parsing API.
  • kjs/JSFunction.cpp: (JSC::JSFunction::JSFunction): Try to validate our SourceCode in debug builds by ASSERTing that it's syntactically valid. This doesn't catch all SourceCode bugs, but it catches a lot of them.
  • kjs/JSGlobalObjectFunctions.cpp: (JSC::globalFuncEval): Updated for tweak to parsing API.
  • kjs/Parser.cpp: (JSC::Parser::parse):
  • kjs/Parser.h: (JSC::Parser::parse): Tweaked the parser to make it possible to parse without an ExecState, and to allow the client to specify a debugger to notify (or not) about the source we parse. This allows the inspector to recompile even though no JavaScript is executing, then notify the debugger about all source code when it's done.
  • kjs/Shell.cpp: (prettyPrintScript): Updated for tweak to parsing API.
  • kjs/SourceRange.h: (JSC::SourceCode::isNull): Added to help with ASSERTs.
  • kjs/collector.cpp: (JSC::Heap::heapAllocate): (JSC::Heap::sweep): (JSC::Heap::primaryHeapBegin): (JSC::Heap::primaryHeapEnd):
  • kjs/collector.h: (JSC::): Moved a bunch of declarations around to enable compilation of CollectorHeapIterator.
  • kjs/interpreter.cpp: (JSC::Interpreter::checkSyntax): (JSC::Interpreter::evaluate): Updated for tweak to parsing API.
  • kjs/lexer.h: (JSC::Lexer::sourceCode): BUG FIX: Calculate SourceCode ranges relative to the SourceCode range in which we're lexing, otherwise nested functions that are compiled individually get SourceCode ranges that don't reflect their nesting.
  • kjs/nodes.cpp: (JSC::FunctionBodyNode::FunctionBodyNode): (JSC::FunctionBodyNode::finishParsing): (JSC::FunctionBodyNode::create): (JSC::FunctionBodyNode::copyParameters):
  • kjs/nodes.h: (JSC::ScopeNode::setSource): (JSC::FunctionBodyNode::parameterCount): Added some helper functions for copying one FunctionBodyNode's parameters to another. The recompiler uses these when calling "finishParsing".

WebCore:

2008-10-15 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Fixed https://bugs.webkit.org/show_bug.cgi?id=21345
Start the debugger without reloading the inspected page

  • WebCore.base.exp: New symbols.
  • ForwardingHeaders/kjs/CollectorHeapIterator.h: Copied from ForwardingHeaders/kjs/ustring.h.
  • ForwardingHeaders/kjs/Parser.h: Copied from ForwardingHeaders/kjs/ustring.h.
  • WebCore.xcodeproj/project.pbxproj: New forwarding headers.
  • inspector/InspectorController.cpp: (WebCore::InspectorController::setWindowVisible): (WebCore::InspectorController::windowScriptObjectAvailable): (WebCore::InspectorController::startDebugging):
  • inspector/InspectorController.h: Renamed startDebuggingAndReloadInspectedPage to startDebugging, and changed its behavior to match.
  • inspector/JavaScriptDebugListener.h:
  • inspector/JavaScriptDebugServer.cpp: (WebCore::JavaScriptDebugServer::JavaScriptDebugServer): (WebCore::JavaScriptDebugServer::addListener): (WebCore::JavaScriptDebugServer::removeListener): (WebCore::JavaScriptDebugServer::recompileAllJSFunctions): (WebCore::JavaScriptDebugServer::willAddFirstListener): (WebCore::JavaScriptDebugServer::didRemoveLastListener):
  • inspector/JavaScriptDebugServer.h: Refactored the JavaScriptDebugServer to centralize handling of adding the first listener and removing the last. Then, added a feature to recompile all JS functions in these cases. This allows us to dynamically add and remove hooks like the debugger hooks without reloading the page.
  • inspector/front-end/ScriptsPanel.js:
  • English.lproj/localizedStrings.js: Updated for startDebuggingAndReloadInspectedPage => startDebugging rename. Removed all UI that claimed that starting the debugger would reload the page.

WebKit/mac:

2008-10-15 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Fixed https://bugs.webkit.org/show_bug.cgi?id=21345
Start the debugger without reloading the inspected page

WebKit/win:

2008-10-15 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Fixed https://bugs.webkit.org/show_bug.cgi?id=21345
Start the debugger without reloading the inspected page

  • WebInspector.cpp: (WebInspector::toggleDebuggingJavaScript): Updated for rename.
  • Property svn:eol-style set to native
File size: 13.3 KB
Line 
1/*
2 * Copyright (C) 1999-2002 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Cameron Zwarich ([email protected])
6 * Copyright (C) 2007 Maks Orlovich
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 "config.h"
26#include "JSGlobalObjectFunctions.h"
27
28#include "ExecState.h"
29#include "GlobalEvalFunction.h"
30#include "JSGlobalObject.h"
31#include "JSString.h"
32#include "Machine.h"
33#include "Parser.h"
34#include "dtoa.h"
35#include "lexer.h"
36#include "nodes.h"
37#include <errno.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <wtf/ASCIICType.h>
42#include <wtf/Assertions.h>
43#include <wtf/MathExtras.h>
44#include <wtf/unicode/UTF8.h>
45
46using namespace WTF;
47using namespace Unicode;
48
49namespace JSC {
50
51static JSValue* encode(ExecState* exec, const ArgList& args, const char* doNotEscape)
52{
53 UString str = args.at(exec, 0)->toString(exec);
54 CString cstr = str.UTF8String(true);
55 if (!cstr.c_str())
56 return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
57
58 UString result = "";
59 const char* p = cstr.c_str();
60 for (size_t k = 0; k < cstr.size(); k++, p++) {
61 char c = *p;
62 if (c && strchr(doNotEscape, c))
63 result.append(c);
64 else {
65 char tmp[4];
66 sprintf(tmp, "%%%02X", static_cast<unsigned char>(c));
67 result += tmp;
68 }
69 }
70 return jsString(exec, result);
71}
72
73static JSValue* decode(ExecState* exec, const ArgList& args, const char* doNotUnescape, bool strict)
74{
75 UString result = "";
76 UString str = args.at(exec, 0)->toString(exec);
77 int k = 0;
78 int len = str.size();
79 const UChar* d = str.data();
80 UChar u = 0;
81 while (k < len) {
82 const UChar* p = d + k;
83 UChar c = *p;
84 if (c == '%') {
85 int charLen = 0;
86 if (k <= len - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) {
87 const char b0 = Lexer::convertHex(p[1], p[2]);
88 const int sequenceLen = UTF8SequenceLength(b0);
89 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
90 charLen = sequenceLen * 3;
91 char sequence[5];
92 sequence[0] = b0;
93 for (int i = 1; i < sequenceLen; ++i) {
94 const UChar* q = p + i * 3;
95 if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2]))
96 sequence[i] = Lexer::convertHex(q[1], q[2]);
97 else {
98 charLen = 0;
99 break;
100 }
101 }
102 if (charLen != 0) {
103 sequence[sequenceLen] = 0;
104 const int character = decodeUTF8Sequence(sequence);
105 if (character < 0 || character >= 0x110000)
106 charLen = 0;
107 else if (character >= 0x10000) {
108 // Convert to surrogate pair.
109 result.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10)));
110 u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF));
111 } else
112 u = static_cast<UChar>(character);
113 }
114 }
115 }
116 if (charLen == 0) {
117 if (strict)
118 return throwError(exec, URIError);
119 // The only case where we don't use "strict" mode is the "unescape" function.
120 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
121 if (k <= len - 6 && p[1] == 'u'
122 && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3])
123 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
124 charLen = 6;
125 u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]);
126 }
127 }
128 if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) {
129 c = u;
130 k += charLen - 1;
131 }
132 }
133 k++;
134 result.append(c);
135 }
136 return jsString(exec, result);
137}
138
139bool isStrWhiteSpace(UChar c)
140{
141 switch (c) {
142 case 0x0009:
143 case 0x000A:
144 case 0x000B:
145 case 0x000C:
146 case 0x000D:
147 case 0x0020:
148 case 0x00A0:
149 case 0x2028:
150 case 0x2029:
151 return true;
152 default:
153 return c > 0xff && isSeparatorSpace(c);
154 }
155}
156
157static int parseDigit(unsigned short c, int radix)
158{
159 int digit = -1;
160
161 if (c >= '0' && c <= '9')
162 digit = c - '0';
163 else if (c >= 'A' && c <= 'Z')
164 digit = c - 'A' + 10;
165 else if (c >= 'a' && c <= 'z')
166 digit = c - 'a' + 10;
167
168 if (digit >= radix)
169 return -1;
170 return digit;
171}
172
173double parseIntOverflow(const char* s, int length, int radix)
174{
175 double number = 0.0;
176 double radixMultiplier = 1.0;
177
178 for (const char* p = s + length - 1; p >= s; p--) {
179 if (radixMultiplier == Inf) {
180 if (*p != '0') {
181 number = Inf;
182 break;
183 }
184 } else {
185 int digit = parseDigit(*p, radix);
186 number += digit * radixMultiplier;
187 }
188
189 radixMultiplier *= radix;
190 }
191
192 return number;
193}
194
195static double parseInt(const UString& s, int radix)
196{
197 int length = s.size();
198 const UChar* data = s.data();
199 int p = 0;
200
201 while (p < length && isStrWhiteSpace(data[p]))
202 ++p;
203
204 double sign = 1;
205 if (p < length) {
206 if (data[p] == '+')
207 ++p;
208 else if (data[p] == '-') {
209 sign = -1;
210 ++p;
211 }
212 }
213
214 if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) {
215 radix = 16;
216 p += 2;
217 } else if (radix == 0) {
218 if (p < length && data[p] == '0')
219 radix = 8;
220 else
221 radix = 10;
222 }
223
224 if (radix < 2 || radix > 36)
225 return NaN;
226
227 int firstDigitPosition = p;
228 bool sawDigit = false;
229 double number = 0;
230 while (p < length) {
231 int digit = parseDigit(data[p], radix);
232 if (digit == -1)
233 break;
234 sawDigit = true;
235 number *= radix;
236 number += digit;
237 ++p;
238 }
239
240 if (number >= mantissaOverflowLowerBound) {
241 if (radix == 10)
242 number = strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
243 else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
244 number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
245 }
246
247 if (!sawDigit)
248 return NaN;
249
250 return sign * number;
251}
252
253static double parseFloat(const UString& s)
254{
255 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
256 // Need to skip any whitespace and then one + or - sign.
257 int length = s.size();
258 const UChar* data = s.data();
259 int p = 0;
260 while (p < length && isStrWhiteSpace(data[p]))
261 ++p;
262
263 if (p < length && (data[p] == '+' || data[p] == '-'))
264 ++p;
265
266 if (length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X'))
267 return 0;
268
269 return s.toDouble(true /*tolerant*/, false /* NaN for empty string */);
270}
271
272JSValue* globalFuncEval(ExecState* exec, JSObject* function, JSValue* thisValue, const ArgList& args)
273{
274 JSObject* thisObject = thisValue->toThisObject(exec);
275 JSGlobalObject* globalObject = thisObject->toGlobalObject(exec);
276 if (!globalObject || globalObject->evalFunction() != function)
277 return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated");
278
279 JSValue* x = args.at(exec, 0);
280 if (!x->isString())
281 return x;
282
283 UString s = x->toString(exec);
284
285 int errLine;
286 UString errMsg;
287
288 SourceCode source = makeSource(s);
289 RefPtr<EvalNode> evalNode = exec->globalData().parser->parse<EvalNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg);
290
291 if (!evalNode)
292 return throwError(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), NULL);
293
294 return exec->machine()->execute(evalNode.get(), exec, thisObject, globalObject->globalScopeChain().node(), exec->exceptionSlot());
295}
296
297JSValue* globalFuncParseInt(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
298{
299 JSValue* value = args.at(exec, 0);
300 int32_t radix = args.at(exec, 1)->toInt32(exec);
301
302 if (value->isNumber() && (radix == 0 || radix == 10)) {
303 if (JSImmediate::isImmediate(value))
304 return value;
305 double d = value->uncheckedGetNumber();
306 if (!isfinite(d))
307 return JSImmediate::zeroImmediate();
308 return jsNumber(exec, floor(d));
309 }
310
311 return jsNumber(exec, parseInt(value->toString(exec), radix));
312}
313
314JSValue* globalFuncParseFloat(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
315{
316 return jsNumber(exec, parseFloat(args.at(exec, 0)->toString(exec)));
317}
318
319JSValue* globalFuncIsNaN(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
320{
321 return jsBoolean(isnan(args.at(exec, 0)->toNumber(exec)));
322}
323
324JSValue* globalFuncIsFinite(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
325{
326 double n = args.at(exec, 0)->toNumber(exec);
327 return jsBoolean(!isnan(n) && !isinf(n));
328}
329
330JSValue* globalFuncDecodeURI(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
331{
332 static const char do_not_unescape_when_decoding_URI[] =
333 "#$&+,/:;=?@";
334
335 return decode(exec, args, do_not_unescape_when_decoding_URI, true);
336}
337
338JSValue* globalFuncDecodeURIComponent(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
339{
340 return decode(exec, args, "", true);
341}
342
343JSValue* globalFuncEncodeURI(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
344{
345 static const char do_not_escape_when_encoding_URI[] =
346 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
347 "abcdefghijklmnopqrstuvwxyz"
348 "0123456789"
349 "!#$&'()*+,-./:;=?@_~";
350
351 return encode(exec, args, do_not_escape_when_encoding_URI);
352}
353
354JSValue* globalFuncEncodeURIComponent(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
355{
356 static const char do_not_escape_when_encoding_URI_component[] =
357 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
358 "abcdefghijklmnopqrstuvwxyz"
359 "0123456789"
360 "!'()*-._~";
361
362 return encode(exec, args, do_not_escape_when_encoding_URI_component);
363}
364
365JSValue* globalFuncEscape(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
366{
367 static const char do_not_escape[] =
368 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
369 "abcdefghijklmnopqrstuvwxyz"
370 "0123456789"
371 "*+-./@_";
372
373 UString result = "";
374 UString s;
375 UString str = args.at(exec, 0)->toString(exec);
376 const UChar* c = str.data();
377 for (int k = 0; k < str.size(); k++, c++) {
378 int u = c[0];
379 if (u > 255) {
380 char tmp[7];
381 sprintf(tmp, "%%u%04X", u);
382 s = UString(tmp);
383 } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u)))
384 s = UString(c, 1);
385 else {
386 char tmp[4];
387 sprintf(tmp, "%%%02X", u);
388 s = UString(tmp);
389 }
390 result += s;
391 }
392
393 return jsString(exec, result);
394}
395
396JSValue* globalFuncUnescape(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
397{
398 UString result = "";
399 UString str = args.at(exec, 0)->toString(exec);
400 int k = 0;
401 int len = str.size();
402 while (k < len) {
403 const UChar* c = str.data() + k;
404 UChar u;
405 if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
406 if (Lexer::isHexDigit(c[2]) && Lexer::isHexDigit(c[3]) && Lexer::isHexDigit(c[4]) && Lexer::isHexDigit(c[5])) {
407 u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]);
408 c = &u;
409 k += 5;
410 }
411 } else if (c[0] == '%' && k <= len - 3 && Lexer::isHexDigit(c[1]) && Lexer::isHexDigit(c[2])) {
412 u = UChar(Lexer::convertHex(c[1], c[2]));
413 c = &u;
414 k += 2;
415 }
416 k++;
417 result.append(*c);
418 }
419
420 return jsString(exec, result);
421}
422
423#ifndef NDEBUG
424JSValue* globalFuncKJSPrint(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
425{
426 CStringBuffer string;
427 args.at(exec, 0)->toString(exec).getCString(string);
428 puts(string.data());
429 return jsUndefined();
430}
431#endif
432
433} // namespace JSC
Note: See TracBrowser for help on using the repository browser.