source: webkit/trunk/JavaScriptCore/kjs/function.cpp@ 15026

Last change on this file since 15026 was 15026, checked in by thatcher, 19 years ago

JavaScriptCore:

Reviewed by Darin.

Bug 9574: Drosera should show inline scripts within the original HTML
http://bugzilla.opendarwin.org/show_bug.cgi?id=9574

Pass the starting line number and error message to the debugger.

  • kjs/debugger.cpp: (Debugger::sourceParsed):
  • kjs/debugger.h:
  • kjs/function.cpp: (KJS::GlobalFuncImp::callAsFunction):
  • kjs/function_object.cpp: (FunctionObjectImp::construct):
  • kjs/interpreter.cpp: (KJS::Interpreter::evaluate):

WebCore:

Reviewed by Darin.

Bug 9574: Drosera should show inline scripts within the original HTML
http://bugzilla.opendarwin.org/show_bug.cgi?id=9574

  • Pass the starting line number and error message to the debugger.
  • Call parsedSource even if there was a script parse error so the debugger can show the parse error.
  • Pass NSURL objects to the ObjC delegate for the script URLs.
  • bridge/mac/WebCoreScriptDebugger.h:
  • bridge/mac/WebCoreScriptDebugger.mm: (toNSURL): (WebCoreScriptDebuggerImp::sourceParsed):

WebKit:

Reviewed by Darin.

Bug 9574: Drosera should show inline scripts within the original HTML
http://bugzilla.opendarwin.org/show_bug.cgi?id=9574

  • Adds a new version of the didParseSource delegate callback with base line number.
  • Adds a new delegate callback for when a script fails to parse.
  • These new callbacks use NSURLs for the url parameter.
  • Adds a new script listener callback to notify when the main resource loads.
  • Adds a WebScriptErrorDomian and other keys for use with NSError.
  • DefaultDelegates/WebDefaultScriptDebugDelegate.m: (-[WebDefaultScriptDebugDelegate webView:didParseSource:baseLineNumber:fromURL:sourceId:forWebFrame:]): (-[WebDefaultScriptDebugDelegate webView:failedToParseSource:baseLineNumber:fromURL:withError:forWebFrame:]):
  • DefaultDelegates/WebScriptDebugServer.h:
  • DefaultDelegates/WebScriptDebugServer.m: (-[WebScriptDebugServer webView:didLoadMainResourceForDataSource:]): (-[WebScriptDebugServer webView:didParseSource:baseLineNumber:fromURL:sourceId:forWebFrame:]): (-[WebScriptDebugServer webView:failedToParseSource:baseLineNumber:fromURL:withError:forWebFrame:]):
  • DefaultDelegates/WebScriptDebugServerPrivate.h:
  • WebKit.exp:
  • WebView/WebDataSource.m: (-[WebDataSource _setPrimaryLoadComplete:]):
  • WebView/WebScriptDebugDelegate.h:
  • WebView/WebScriptDebugDelegate.m: (-[WebScriptCallFrame parsedSource:fromURL:sourceId:startLine:errorLine:errorMessage:]):

WebKitTools:

Reviewed by Darin.

Bug 9574: Drosera should show inline scripts within the original HTML
http://bugzilla.opendarwin.org/show_bug.cgi?id=9574

Refactor the JavaScript code to have a distinction between files
and scripts. Show the script in the context of the HTML file if
it's URL is the same as the frame's main resource. At the time of
the disParseScript callback the main resource might not be completely
loaded, but Drosera needs to show whatever we have at the time. Once
the main resource is finished, update the file source and reload the file.

  • Drosera/DebuggerDocument.m: (-[DebuggerDocument pause]): (-[DebuggerDocument webView:didLoadMainResourceForDataSource:]): (-[DebuggerDocument webView:didParseSource:baseLineNumber:fromURL:sourceId:forWebFrame:]): (-[DebuggerDocument webView:failedToParseSource:baseLineNumber:fromURL:withError:forWebFrame:]):
  • Drosera/debugger.css:
  • Drosera/debugger.js:
  • Property allow-tabs set to x
  • Property svn:eol-style set to native
File size: 25.5 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2002 Harri Porten ([email protected])
5 * Copyright (C) 2001 Peter Kelly ([email protected])
6 * Copyright (C) 2003 Apple Computer, Inc.
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 "function.h"
27
28#include "internal.h"
29#include "function_object.h"
30#include "lexer.h"
31#include "nodes.h"
32#include "operations.h"
33#include "debugger.h"
34#include "context.h"
35
36#include <stdio.h>
37#include <errno.h>
38#include <stdlib.h>
39#include <assert.h>
40#include <string.h>
41#include <ctype.h>
42
43#include <wtf/unicode/Unicode.h>
44
45namespace KJS {
46
47// ----------------------------- FunctionImp ----------------------------------
48
49const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
50
51 class Parameter {
52 public:
53 Parameter(const Identifier &n) : name(n) { }
54 Identifier name;
55 OwnPtr<Parameter> next;
56 };
57
58FunctionImp::FunctionImp(ExecState *exec, const Identifier &n, FunctionBodyNode* b)
59 : InternalFunctionImp(static_cast<FunctionPrototype*>
60 (exec->lexicalInterpreter()->builtinFunctionPrototype()), n)
61 , body(b)
62{
63}
64
65FunctionImp::~FunctionImp()
66{
67}
68
69JSValue *FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
70{
71 JSObject *globalObj = exec->dynamicInterpreter()->globalObject();
72
73 // enter a new execution context
74 Context ctx(globalObj, exec->dynamicInterpreter(), thisObj, body.get(),
75 codeType(), exec->context(), this, &args);
76 ExecState newExec(exec->dynamicInterpreter(), &ctx);
77 if (exec->hadException())
78 newExec.setException(exec->exception());
79
80 // assign user supplied arguments to parameters
81 processParameters(&newExec, args);
82 // add variable declarations (initialized to undefined)
83 processVarDecls(&newExec);
84
85 Debugger *dbg = exec->dynamicInterpreter()->debugger();
86 int sid = -1;
87 int lineno = -1;
88 if (dbg) {
89 if (inherits(&DeclaredFunctionImp::info)) {
90 sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
91 lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
92 }
93
94 bool cont = dbg->callEvent(&newExec,sid,lineno,this,args);
95 if (!cont) {
96 dbg->imp()->abort();
97 return jsUndefined();
98 }
99 }
100
101 Completion comp = execute(&newExec);
102
103 // if an exception occured, propogate it back to the previous execution object
104 if (newExec.hadException())
105 comp = Completion(Throw, newExec.exception());
106
107#ifdef KJS_VERBOSE
108 if (comp.complType() == Throw)
109 printInfo(exec,"throwing", comp.value());
110 else if (comp.complType() == ReturnValue)
111 printInfo(exec,"returning", comp.value());
112 else
113 fprintf(stderr, "returning: undefined\n");
114#endif
115
116 // The debugger may have been deallocated by now if the WebFrame
117 // we were running in has been destroyed, so refetch it.
118 // See http://bugzilla.opendarwin.org/show_bug.cgi?id=9477
119 dbg = exec->dynamicInterpreter()->debugger();
120
121 if (dbg) {
122 if (inherits(&DeclaredFunctionImp::info))
123 lineno = static_cast<DeclaredFunctionImp*>(this)->body->lastLine();
124
125 if (comp.complType() == Throw)
126 newExec.setException(comp.value());
127
128 int cont = dbg->returnEvent(&newExec,sid,lineno,this);
129 if (!cont) {
130 dbg->imp()->abort();
131 return jsUndefined();
132 }
133 }
134
135 if (comp.complType() == Throw) {
136 exec->setException(comp.value());
137 return comp.value();
138 }
139 else if (comp.complType() == ReturnValue)
140 return comp.value();
141 else
142 return jsUndefined();
143}
144
145void FunctionImp::addParameter(const Identifier &n)
146{
147 OwnPtr<Parameter> *p = &param;
148 while (*p)
149 p = &(*p)->next;
150
151 p->set(new Parameter(n));
152}
153
154UString FunctionImp::parameterString() const
155{
156 UString s;
157 const Parameter *p = param.get();
158 while (p) {
159 if (!s.isEmpty())
160 s += ", ";
161 s += p->name.ustring();
162 p = p->next.get();
163 }
164
165 return s;
166}
167
168
169// ECMA 10.1.3q
170void FunctionImp::processParameters(ExecState *exec, const List &args)
171{
172 JSObject* variable = exec->context()->variableObject();
173
174#ifdef KJS_VERBOSE
175 fprintf(stderr, "---------------------------------------------------\n"
176 "processing parameters for %s call\n",
177 name().isEmpty() ? "(internal)" : name().ascii());
178#endif
179
180 if (param) {
181 ListIterator it = args.begin();
182 Parameter *p = param.get();
183 JSValue *v = *it;
184 while (p) {
185 if (it != args.end()) {
186#ifdef KJS_VERBOSE
187 fprintf(stderr, "setting parameter %s ", p->name.ascii());
188 printInfo(exec,"to", *it);
189#endif
190 variable->put(exec, p->name, v);
191 v = ++it;
192 } else
193 variable->put(exec, p->name, jsUndefined());
194 p = p->next.get();
195 }
196 }
197#ifdef KJS_VERBOSE
198 else {
199 for (int i = 0; i < args.size(); i++)
200 printInfo(exec,"setting argument", args[i]);
201 }
202#endif
203}
204
205void FunctionImp::processVarDecls(ExecState */*exec*/)
206{
207}
208
209JSValue *FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
210{
211 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
212 Context *context = exec->m_context;
213 while (context) {
214 if (context->function() == thisObj) {
215 return static_cast<ActivationImp *>(context->activationObject())->get(exec, propertyName);
216 }
217 context = context->callingContext();
218 }
219 return jsNull();
220}
221
222JSValue *FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
223{
224 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
225 const Parameter *p = thisObj->param.get();
226 int count = 0;
227 while (p) {
228 ++count;
229 p = p->next.get();
230 }
231 return jsNumber(count);
232}
233
234bool FunctionImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
235{
236 // Find the arguments from the closest context.
237 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
238 slot.setCustom(this, argumentsGetter);
239 return true;
240 }
241
242 // Compute length of parameters.
243 if (propertyName == lengthPropertyName) {
244 slot.setCustom(this, lengthGetter);
245 return true;
246 }
247
248 return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
249}
250
251void FunctionImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
252{
253 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier() || propertyName == lengthPropertyName)
254 return;
255 InternalFunctionImp::put(exec, propertyName, value, attr);
256}
257
258bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
259{
260 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier() || propertyName == lengthPropertyName)
261 return false;
262 return InternalFunctionImp::deleteProperty(exec, propertyName);
263}
264
265/* Returns the parameter name corresponding to the given index. eg:
266 * function f1(x, y, z): getParameterName(0) --> x
267 *
268 * If a name appears more than once, only the last index at which
269 * it appears associates with it. eg:
270 * function f2(x, x): getParameterName(0) --> null
271 */
272Identifier FunctionImp::getParameterName(int index)
273{
274 int i = 0;
275 Parameter *p = param.get();
276
277 if(!p)
278 return Identifier::null();
279
280 // skip to the parameter we want
281 while (i++ < index && (p = p->next.get()))
282 ;
283
284 if (!p)
285 return Identifier::null();
286
287 Identifier name = p->name;
288
289 // Are there any subsequent parameters with the same name?
290 while ((p = p->next.get()))
291 if (p->name == name)
292 return Identifier::null();
293
294 return name;
295}
296
297// ------------------------------ DeclaredFunctionImp --------------------------
298
299// ### is "Function" correct here?
300const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
301
302DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
303 FunctionBodyNode *b, const ScopeChain &sc)
304 : FunctionImp(exec, n, b)
305{
306 setScope(sc);
307}
308
309bool DeclaredFunctionImp::implementsConstruct() const
310{
311 return true;
312}
313
314// ECMA 13.2.2 [[Construct]]
315JSObject *DeclaredFunctionImp::construct(ExecState *exec, const List &args)
316{
317 JSObject *proto;
318 JSValue *p = get(exec,prototypePropertyName);
319 if (p->isObject())
320 proto = static_cast<JSObject*>(p);
321 else
322 proto = exec->lexicalInterpreter()->builtinObjectPrototype();
323
324 JSObject *obj(new JSObject(proto));
325
326 JSValue *res = call(exec,obj,args);
327
328 if (res->isObject())
329 return static_cast<JSObject *>(res);
330 else
331 return obj;
332}
333
334Completion DeclaredFunctionImp::execute(ExecState *exec)
335{
336 Completion result = body->execute(exec);
337
338 if (result.complType() == Throw || result.complType() == ReturnValue)
339 return result;
340 return Completion(Normal, jsUndefined()); // TODO: or ReturnValue ?
341}
342
343void DeclaredFunctionImp::processVarDecls(ExecState *exec)
344{
345 body->processVarDecls(exec);
346}
347
348// ------------------------------ IndexToNameMap ---------------------------------
349
350// We map indexes in the arguments array to their corresponding argument names.
351// Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
352
353// Once we have an argument name, we can get and set the argument's value in the
354// activation object.
355
356// We use Identifier::null to indicate that a given argument's value
357// isn't stored in the activation object.
358
359IndexToNameMap::IndexToNameMap(FunctionImp *func, const List &args)
360{
361 _map = new Identifier[args.size()];
362 this->size = args.size();
363
364 int i = 0;
365 ListIterator iterator = args.begin();
366 for (; iterator != args.end(); i++, iterator++)
367 _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
368}
369
370IndexToNameMap::~IndexToNameMap() {
371 delete [] _map;
372}
373
374bool IndexToNameMap::isMapped(const Identifier &index) const
375{
376 bool indexIsNumber;
377 int indexAsNumber = index.toUInt32(&indexIsNumber);
378
379 if (!indexIsNumber)
380 return false;
381
382 if (indexAsNumber >= size)
383 return false;
384
385 if (_map[indexAsNumber].isNull())
386 return false;
387
388 return true;
389}
390
391void IndexToNameMap::unMap(const Identifier &index)
392{
393 bool indexIsNumber;
394 int indexAsNumber = index.toUInt32(&indexIsNumber);
395
396 assert(indexIsNumber && indexAsNumber < size);
397
398 _map[indexAsNumber] = Identifier::null();
399}
400
401Identifier& IndexToNameMap::operator[](int index)
402{
403 return _map[index];
404}
405
406Identifier& IndexToNameMap::operator[](const Identifier &index)
407{
408 bool indexIsNumber;
409 int indexAsNumber = index.toUInt32(&indexIsNumber);
410
411 assert(indexIsNumber && indexAsNumber < size);
412
413 return (*this)[indexAsNumber];
414}
415
416// ------------------------------ Arguments ---------------------------------
417
418const ClassInfo Arguments::info = {"Arguments", 0, 0, 0};
419
420// ECMA 10.1.8
421Arguments::Arguments(ExecState *exec, FunctionImp *func, const List &args, ActivationImp *act)
422: JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()),
423_activationObject(act),
424indexToNameMap(func, args)
425{
426 putDirect(calleePropertyName, func, DontEnum);
427 putDirect(lengthPropertyName, args.size(), DontEnum);
428
429 int i = 0;
430 ListIterator iterator = args.begin();
431 for (; iterator != args.end(); i++, iterator++) {
432 if (!indexToNameMap.isMapped(Identifier::from(i))) {
433 JSObject::put(exec, Identifier::from(i), *iterator, DontEnum);
434 }
435 }
436}
437
438void Arguments::mark()
439{
440 JSObject::mark();
441 if (_activationObject && !_activationObject->marked())
442 _activationObject->mark();
443}
444
445JSValue *Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
446{
447 Arguments *thisObj = static_cast<Arguments *>(slot.slotBase());
448 return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
449}
450
451bool Arguments::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
452{
453 if (indexToNameMap.isMapped(propertyName)) {
454 slot.setCustom(this, mappedIndexGetter);
455 return true;
456 }
457
458 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
459}
460
461void Arguments::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
462{
463 if (indexToNameMap.isMapped(propertyName)) {
464 _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
465 } else {
466 JSObject::put(exec, propertyName, value, attr);
467 }
468}
469
470bool Arguments::deleteProperty(ExecState *exec, const Identifier &propertyName)
471{
472 if (indexToNameMap.isMapped(propertyName)) {
473 indexToNameMap.unMap(propertyName);
474 return true;
475 } else {
476 return JSObject::deleteProperty(exec, propertyName);
477 }
478}
479
480// ------------------------------ ActivationImp --------------------------------
481
482const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
483
484// ECMA 10.1.6
485ActivationImp::ActivationImp(FunctionImp *function, const List &arguments)
486 : _function(function), _arguments(true), _argumentsObject(0)
487{
488 _arguments.copyFrom(arguments);
489 // FIXME: Do we need to support enumerating the arguments property?
490}
491
492JSValue *ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
493{
494 ActivationImp *thisObj = static_cast<ActivationImp *>(slot.slotBase());
495
496 // default: return builtin arguments array
497 if (!thisObj->_argumentsObject)
498 thisObj->createArgumentsObject(exec);
499
500 return thisObj->_argumentsObject;
501}
502
503PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
504{
505 return ActivationImp::argumentsGetter;
506}
507
508bool ActivationImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
509{
510 // do this first so property map arguments property wins over the below
511 // we don't call JSObject because we won't have getter/setter properties
512 // and we don't want to support __proto__
513
514 if (JSValue **location = getDirectLocation(propertyName)) {
515 slot.setValueSlot(this, location);
516 return true;
517 }
518
519 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
520 slot.setCustom(this, getArgumentsGetter());
521 return true;
522 }
523
524 return false;
525}
526
527bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
528{
529 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier())
530 return false;
531 return JSObject::deleteProperty(exec, propertyName);
532}
533
534void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
535{
536 // There's no way that an activation object can have a prototype or getter/setter properties
537 assert(!_prop.hasGetterSetterProperties());
538 assert(prototype() == jsNull());
539
540 _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
541}
542
543void ActivationImp::mark()
544{
545 if (_function && !_function->marked())
546 _function->mark();
547 _arguments.mark();
548 if (_argumentsObject && !_argumentsObject->marked())
549 _argumentsObject->mark();
550 JSObject::mark();
551}
552
553void ActivationImp::createArgumentsObject(ExecState *exec) const
554{
555 _argumentsObject = new Arguments(exec, _function, _arguments, const_cast<ActivationImp *>(this));
556}
557
558// ------------------------------ GlobalFunc -----------------------------------
559
560
561GlobalFuncImp::GlobalFuncImp(ExecState*, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
562 : InternalFunctionImp(funcProto, name)
563 , id(i)
564{
565 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
566}
567
568CodeType GlobalFuncImp::codeType() const
569{
570 return id == Eval ? EvalCode : codeType();
571}
572
573static JSValue *encode(ExecState *exec, const List &args, const char *do_not_escape)
574{
575 UString r = "", s, str = args[0]->toString(exec);
576 CString cstr = str.UTF8String();
577 const char *p = cstr.c_str();
578 for (size_t k = 0; k < cstr.size(); k++, p++) {
579 char c = *p;
580 if (c && strchr(do_not_escape, c)) {
581 r.append(c);
582 } else {
583 char tmp[4];
584 sprintf(tmp, "%%%02X", (unsigned char)c);
585 r += tmp;
586 }
587 }
588 return jsString(r);
589}
590
591static JSValue *decode(ExecState *exec, const List &args, const char *do_not_unescape, bool strict)
592{
593 UString s = "", str = args[0]->toString(exec);
594 int k = 0, len = str.size();
595 const UChar *d = str.data();
596 UChar u;
597 while (k < len) {
598 const UChar *p = d + k;
599 UChar c = *p;
600 if (c == '%') {
601 int charLen = 0;
602 if (k <= len - 3 && isxdigit(p[1].uc) && isxdigit(p[2].uc)) {
603 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
604 const int sequenceLen = UTF8SequenceLength(b0);
605 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
606 charLen = sequenceLen * 3;
607 char sequence[5];
608 sequence[0] = b0;
609 for (int i = 1; i < sequenceLen; ++i) {
610 const UChar *q = p + i * 3;
611 if (q[0] == '%' && isxdigit(q[1].uc) && isxdigit(q[2].uc))
612 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
613 else {
614 charLen = 0;
615 break;
616 }
617 }
618 if (charLen != 0) {
619 sequence[sequenceLen] = 0;
620 const int character = decodeUTF8Sequence(sequence);
621 if (character < 0 || character >= 0x110000) {
622 charLen = 0;
623 } else if (character >= 0x10000) {
624 // Convert to surrogate pair.
625 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
626 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
627 } else {
628 u = static_cast<unsigned short>(character);
629 }
630 }
631 }
632 }
633 if (charLen == 0) {
634 if (strict)
635 return throwError(exec, URIError);
636 // The only case where we don't use "strict" mode is the "unescape" function.
637 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
638 if (k <= len - 6 && p[1] == 'u'
639 && isxdigit(p[2].uc) && isxdigit(p[3].uc)
640 && isxdigit(p[4].uc) && isxdigit(p[5].uc)) {
641 charLen = 6;
642 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
643 }
644 }
645 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
646 c = u;
647 k += charLen - 1;
648 }
649 }
650 k++;
651 s.append(c);
652 }
653 return jsString(s);
654}
655
656static bool isStrWhiteSpace(unsigned short c)
657{
658 switch (c) {
659 case 0x0009:
660 case 0x000A:
661 case 0x000B:
662 case 0x000C:
663 case 0x000D:
664 case 0x0020:
665 case 0x00A0:
666 case 0x2028:
667 case 0x2029:
668 return true;
669 default:
670 return WTF::Unicode::isSeparatorSpace(c);
671 }
672}
673
674static int parseDigit(unsigned short c, int radix)
675{
676 int digit = -1;
677
678 if (c >= '0' && c <= '9') {
679 digit = c - '0';
680 } else if (c >= 'A' && c <= 'Z') {
681 digit = c - 'A' + 10;
682 } else if (c >= 'a' && c <= 'z') {
683 digit = c - 'a' + 10;
684 }
685
686 if (digit >= radix)
687 return -1;
688 return digit;
689}
690
691static double parseInt(const UString &s, int radix)
692{
693 int length = s.size();
694 int p = 0;
695
696 while (p < length && isStrWhiteSpace(s[p].uc)) {
697 ++p;
698 }
699
700 double sign = 1;
701 if (p < length) {
702 if (s[p] == '+') {
703 ++p;
704 } else if (s[p] == '-') {
705 sign = -1;
706 ++p;
707 }
708 }
709
710 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
711 radix = 16;
712 p += 2;
713 } else if (radix == 0) {
714 if (p < length && s[p] == '0')
715 radix = 8;
716 else
717 radix = 10;
718 }
719
720 if (radix < 2 || radix > 36)
721 return NaN;
722
723 bool sawDigit = false;
724 double number = 0;
725 while (p < length) {
726 int digit = parseDigit(s[p].uc, radix);
727 if (digit == -1)
728 break;
729 sawDigit = true;
730 number *= radix;
731 number += digit;
732 ++p;
733 }
734
735 if (!sawDigit)
736 return NaN;
737
738 return sign * number;
739}
740
741static double parseFloat(const UString &s)
742{
743 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
744 // Need to skip any whitespace and then one + or - sign.
745 int length = s.size();
746 int p = 0;
747 while (p < length && isStrWhiteSpace(s[p].uc)) {
748 ++p;
749 }
750 if (p < length && (s[p] == '+' || s[p] == '-')) {
751 ++p;
752 }
753 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
754 return 0;
755 }
756
757 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
758}
759
760JSValue *GlobalFuncImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
761{
762 JSValue *res = jsUndefined();
763
764 static const char do_not_escape[] =
765 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
766 "abcdefghijklmnopqrstuvwxyz"
767 "0123456789"
768 "*+-./@_";
769
770 static const char do_not_escape_when_encoding_URI_component[] =
771 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
772 "abcdefghijklmnopqrstuvwxyz"
773 "0123456789"
774 "!'()*-._~";
775 static const char do_not_escape_when_encoding_URI[] =
776 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
777 "abcdefghijklmnopqrstuvwxyz"
778 "0123456789"
779 "!#$&'()*+,-./:;=?@_~";
780 static const char do_not_unescape_when_decoding_URI[] =
781 "#$&+,/:;=?@";
782
783 switch (id) {
784 case Eval: { // eval()
785 JSValue *x = args[0];
786 if (!x->isString())
787 return x;
788 else {
789 UString s = x->toString(exec);
790
791 int sid;
792 int errLine;
793 UString errMsg;
794 RefPtr<ProgramNode> progNode(Parser::parse(UString(), 0, s.data(),s.size(),&sid,&errLine,&errMsg));
795
796 Debugger *dbg = exec->dynamicInterpreter()->debugger();
797 if (dbg) {
798 bool cont = dbg->sourceParsed(exec, sid, UString(), s, 0, errLine, errMsg);
799 if (!cont)
800 return jsUndefined();
801 }
802
803 // no program node means a syntax occurred
804 if (!progNode)
805 return throwError(exec, SyntaxError, errMsg, errLine, sid, NULL);
806
807 // enter a new execution context
808 JSObject *thisVal = static_cast<JSObject *>(exec->context()->thisValue());
809 Context ctx(exec->dynamicInterpreter()->globalObject(),
810 exec->dynamicInterpreter(),
811 thisVal,
812 progNode.get(),
813 EvalCode,
814 exec->context());
815
816 ExecState newExec(exec->dynamicInterpreter(), &ctx);
817 if (exec->hadException())
818 newExec.setException(exec->exception());
819
820 // execute the code
821 progNode->processVarDecls(&newExec);
822 Completion c = progNode->execute(&newExec);
823
824 // if an exception occured, propogate it back to the previous execution object
825 if (newExec.hadException())
826 exec->setException(newExec.exception());
827
828 res = jsUndefined();
829 if (c.complType() == Throw)
830 exec->setException(c.value());
831 else if (c.isValueCompletion())
832 res = c.value();
833 }
834 break;
835 }
836 case ParseInt:
837 res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
838 break;
839 case ParseFloat:
840 res = jsNumber(parseFloat(args[0]->toString(exec)));
841 break;
842 case IsNaN:
843 res = jsBoolean(isNaN(args[0]->toNumber(exec)));
844 break;
845 case IsFinite: {
846 double n = args[0]->toNumber(exec);
847 res = jsBoolean(!isNaN(n) && !isInf(n));
848 break;
849 }
850 case DecodeURI:
851 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
852 break;
853 case DecodeURIComponent:
854 res = decode(exec, args, "", true);
855 break;
856 case EncodeURI:
857 res = encode(exec, args, do_not_escape_when_encoding_URI);
858 break;
859 case EncodeURIComponent:
860 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
861 break;
862 case Escape:
863 {
864 UString r = "", s, str = args[0]->toString(exec);
865 const UChar *c = str.data();
866 for (int k = 0; k < str.size(); k++, c++) {
867 int u = c->uc;
868 if (u > 255) {
869 char tmp[7];
870 sprintf(tmp, "%%u%04X", u);
871 s = UString(tmp);
872 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
873 s = UString(c, 1);
874 } else {
875 char tmp[4];
876 sprintf(tmp, "%%%02X", u);
877 s = UString(tmp);
878 }
879 r += s;
880 }
881 res = jsString(r);
882 break;
883 }
884 case UnEscape:
885 {
886 UString s = "", str = args[0]->toString(exec);
887 int k = 0, len = str.size();
888 while (k < len) {
889 const UChar *c = str.data() + k;
890 UChar u;
891 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
892 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
893 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
894 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
895 (c+4)->uc, (c+5)->uc);
896 c = &u;
897 k += 5;
898 }
899 } else if (*c == UChar('%') && k <= len - 3 &&
900 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
901 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
902 c = &u;
903 k += 2;
904 }
905 k++;
906 s += UString(c, 1);
907 }
908 res = jsString(s);
909 break;
910 }
911#ifndef NDEBUG
912 case KJSPrint:
913 puts(args[0]->toString(exec).ascii());
914 break;
915#endif
916 }
917
918 return res;
919}
920
921} // namespace
Note: See TracBrowser for help on using the repository browser.