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

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

JavaScriptCore:

2008-04-16 Sam Weinig <[email protected]>

Reviewed by Geoffrey Garen.

  • To keep the behavior of the WebKit and JavaScriptCore API's the same, we need to hide the fact that the global object and the window object are no longer the same thing, and the the global object now changes on navigations. To do this, only the wrapper should ever be exposed. This fixes the two remaining spots where the internal global object is exposed, the windowScriptObject returned from [WebFrame windowObject] and the object return by calling JSContextGetGlobalObject on [WebFrame globalContext].
  • API/JSContextRef.cpp: (JSContextGetGlobalObject): This is a bit of a hack, this returns the "this" representation of the globalObject which will be the WrapperWindow for WebCore and the globalObject for non-WebCore.
  • API/JSObjectRef.cpp: (JSObjectSetProperty): Call the new putWithAttributes method instead of relying on lower-level calls. This is needed so that the window wrapper can forward the calls.
  • JavaScriptCore.exp:
  • kjs/Activation.h:
  • kjs/JSGlobalObject.cpp: (KJS::JSGlobalObject::putWithAttributes):
  • kjs/JSGlobalObject.h:
  • kjs/JSVariableObject.h: (KJS::JSVariableObject::symbolTablePutWithAttributes):
  • kjs/function.cpp: (KJS::ActivationImp::putWithAttributes):
  • kjs/nodes.cpp: (KJS::ConstDeclNode::handleSlowCase): (KJS::ConstDeclNode::evaluateSingle): (KJS::EvalNode::processDeclarations):
  • kjs/object.cpp: (KJS::JSObject::putWithAttributes):
  • kjs/object.h: Rename initializeVariable to putWithAttributes and move it down to JSObject so it can be used for JSObjectSetProperty.

WebCore:

2008-04-16 Sam Weinig <[email protected]>

Reviewed by Geoffrey Garen.

  • To keep the behavior of the WebKit and JavaScriptCore API's the same, we need to hide the fact that the global object and the window object are no longer the same thing, and the the global object now changes on navigations. To do this, only the wrapper should ever be exposed. This fixes the two remaining spots where the internal global object is exposed, the windowScriptObject returned from [WebFrame windowObject] and the object return by calling JSContextGetGlobalObject on [WebFrame globalContext]
  • bindings/js/JSDOMWindowWrapper.cpp: (WebCore::JSDOMWindowWrapper::putWithAttributes):
  • bindings/js/JSDOMWindowWrapper.h: Forward this new method so that JSObjectSetProperty doesn't set properties on the wrapper.
  • page/mac/FrameMac.mm: (WebCore::Frame::windowScriptObject): Return the wrapper instead of the global object.
  • Property svn:eol-style set to native
File size: 27.5 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * Copyright (C) 1999-2002 Harri Porten ([email protected])
4 * Copyright (C) 2001 Peter Kelly ([email protected])
5 * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Cameron Zwarich ([email protected])
7 * Copyright (C) 2007 Maks Orlovich
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include "config.h"
27#include "function.h"
28
29#include "Activation.h"
30#include "ExecState.h"
31#include "JSGlobalObject.h"
32#include "Parser.h"
33#include "PropertyNameArray.h"
34#include "debugger.h"
35#include "dtoa.h"
36#include "function_object.h"
37#include "internal.h"
38#include "lexer.h"
39#include "nodes.h"
40#include "operations.h"
41#include "scope_chain_mark.h"
42#include "ExecStateInlines.h"
43#include <errno.h>
44#include <profiler/Profiler.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <wtf/ASCIICType.h>
49#include <wtf/Assertions.h>
50#include <wtf/MathExtras.h>
51#include <wtf/unicode/UTF8.h>
52
53using namespace WTF;
54using namespace Unicode;
55
56namespace KJS {
57
58// ----------------------------- FunctionImp ----------------------------------
59
60const ClassInfo FunctionImp::info = { "Function", &InternalFunctionImp::info, 0 };
61
62FunctionImp::FunctionImp(ExecState* exec, const Identifier& name, FunctionBodyNode* b, const ScopeChain& sc)
63 : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name)
64 , body(b)
65 , _scope(sc)
66{
67}
68
69void FunctionImp::mark()
70{
71 InternalFunctionImp::mark();
72 _scope.mark();
73}
74
75JSValue* FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
76{
77 FunctionExecState newExec(exec->dynamicGlobalObject(), thisObj, exec->globalThisValue(), body.get(), exec, this, args);
78 JSValue* result = body->execute(&newExec);
79 if (newExec.completionType() == ReturnValue)
80 return result;
81 if (newExec.completionType() == Throw) {
82 exec->setException(result);
83 return result;
84 }
85 return jsUndefined();
86}
87
88JSValue* FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
89{
90 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
91
92 for (ExecState* e = exec; e; e = e->callingExecState())
93 if (e->function() == thisObj) {
94 e->dynamicGlobalObject()->tearOffActivation(e, e != exec);
95 return e->activationObject()->get(exec, propertyName);
96 }
97
98 return jsNull();
99}
100
101JSValue* FunctionImp::callerGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
102{
103 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
104 ExecState* e = exec;
105 while (e) {
106 if (e->function() == thisObj)
107 break;
108 e = e->callingExecState();
109 }
110
111 if (!e)
112 return jsNull();
113
114 ExecState* callingExecState = e->callingExecState();
115 if (!callingExecState)
116 return jsNull();
117
118 FunctionImp* callingFunction = callingExecState->function();
119 if (!callingFunction)
120 return jsNull();
121
122 return callingFunction;
123}
124
125JSValue* FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
126{
127 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
128 return jsNumber(thisObj->body->parameters().size());
129}
130
131bool FunctionImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
132{
133 // Find the arguments from the closest context.
134 if (propertyName == exec->propertyNames().arguments) {
135 slot.setCustom(this, argumentsGetter);
136 return true;
137 }
138
139 // Compute length of parameters.
140 if (propertyName == exec->propertyNames().length) {
141 slot.setCustom(this, lengthGetter);
142 return true;
143 }
144
145 if (propertyName == exec->propertyNames().caller) {
146 slot.setCustom(this, callerGetter);
147 return true;
148 }
149
150 return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
151}
152
153void FunctionImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
154{
155 if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
156 return;
157 InternalFunctionImp::put(exec, propertyName, value);
158}
159
160bool FunctionImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
161{
162 if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
163 return false;
164 return InternalFunctionImp::deleteProperty(exec, propertyName);
165}
166
167/* Returns the parameter name corresponding to the given index. eg:
168 * function f1(x, y, z): getParameterName(0) --> x
169 *
170 * If a name appears more than once, only the last index at which
171 * it appears associates with it. eg:
172 * function f2(x, x): getParameterName(0) --> null
173 */
174Identifier FunctionImp::getParameterName(int index)
175{
176 Vector<Identifier>& parameters = body->parameters();
177
178 if (static_cast<size_t>(index) >= body->parameters().size())
179 return CommonIdentifiers::shared()->nullIdentifier;
180
181 Identifier name = parameters[index];
182
183 // Are there any subsequent parameters with the same name?
184 size_t size = parameters.size();
185 for (size_t i = index + 1; i < size; ++i)
186 if (parameters[i] == name)
187 return CommonIdentifiers::shared()->nullIdentifier;
188
189 return name;
190}
191
192// ECMA 13.2.2 [[Construct]]
193JSObject* FunctionImp::construct(ExecState* exec, const List& args)
194{
195 JSObject* proto;
196 JSValue* p = get(exec, exec->propertyNames().prototype);
197 if (p->isObject())
198 proto = static_cast<JSObject*>(p);
199 else
200 proto = exec->lexicalGlobalObject()->objectPrototype();
201
202 JSObject* obj(new JSObject(proto));
203
204 JSValue* res = call(exec,obj,args);
205
206 if (res->isObject())
207 return static_cast<JSObject*>(res);
208 else
209 return obj;
210}
211
212// ------------------------------ IndexToNameMap ---------------------------------
213
214// We map indexes in the arguments array to their corresponding argument names.
215// Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
216
217// Once we have an argument name, we can get and set the argument's value in the
218// activation object.
219
220// We use Identifier::null to indicate that a given argument's value
221// isn't stored in the activation object.
222
223IndexToNameMap::IndexToNameMap(FunctionImp* func, const List& args)
224{
225 _map = new Identifier[args.size()];
226 this->size = args.size();
227
228 int i = 0;
229 List::const_iterator end = args.end();
230 for (List::const_iterator it = args.begin(); it != end; ++i, ++it)
231 _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
232}
233
234IndexToNameMap::~IndexToNameMap() {
235 delete [] _map;
236}
237
238bool IndexToNameMap::isMapped(const Identifier& index) const
239{
240 bool indexIsNumber;
241 int indexAsNumber = index.toUInt32(&indexIsNumber);
242
243 if (!indexIsNumber)
244 return false;
245
246 if (indexAsNumber >= size)
247 return false;
248
249 if (_map[indexAsNumber].isNull())
250 return false;
251
252 return true;
253}
254
255void IndexToNameMap::unMap(const Identifier& index)
256{
257 bool indexIsNumber;
258 int indexAsNumber = index.toUInt32(&indexIsNumber);
259
260 ASSERT(indexIsNumber && indexAsNumber < size);
261
262 _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier;
263}
264
265Identifier& IndexToNameMap::operator[](int index)
266{
267 return _map[index];
268}
269
270Identifier& IndexToNameMap::operator[](const Identifier& index)
271{
272 bool indexIsNumber;
273 int indexAsNumber = index.toUInt32(&indexIsNumber);
274
275 ASSERT(indexIsNumber && indexAsNumber < size);
276
277 return (*this)[indexAsNumber];
278}
279
280// ------------------------------ Arguments ---------------------------------
281
282const ClassInfo Arguments::info = { "Arguments", 0, 0 };
283
284// ECMA 10.1.8
285Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act)
286 : JSObject(exec->lexicalGlobalObject()->objectPrototype())
287 , _activationObject(act)
288 , indexToNameMap(func, args)
289{
290 putDirect(exec->propertyNames().callee, func, DontEnum);
291 putDirect(exec->propertyNames().length, args.size(), DontEnum);
292
293 int i = 0;
294 List::const_iterator end = args.end();
295 for (List::const_iterator it = args.begin(); it != end; ++it, ++i) {
296 Identifier name = Identifier::from(i);
297 if (!indexToNameMap.isMapped(name))
298 putDirect(name, *it, DontEnum);
299 }
300}
301
302void Arguments::mark()
303{
304 JSObject::mark();
305 if (_activationObject && !_activationObject->marked())
306 _activationObject->mark();
307}
308
309JSValue* Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
310{
311 Arguments* thisObj = static_cast<Arguments*>(slot.slotBase());
312 return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
313}
314
315bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
316{
317 if (indexToNameMap.isMapped(propertyName)) {
318 slot.setCustom(this, mappedIndexGetter);
319 return true;
320 }
321
322 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
323}
324
325void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
326{
327 if (indexToNameMap.isMapped(propertyName))
328 _activationObject->put(exec, indexToNameMap[propertyName], value);
329 else
330 JSObject::put(exec, propertyName, value);
331}
332
333bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName)
334{
335 if (indexToNameMap.isMapped(propertyName)) {
336 indexToNameMap.unMap(propertyName);
337 return true;
338 } else {
339 return JSObject::deleteProperty(exec, propertyName);
340 }
341}
342
343// ------------------------------ ActivationImp --------------------------------
344
345const ClassInfo ActivationImp::info = { "Activation", 0, 0 };
346
347ActivationImp::ActivationImp(const ActivationData& oldData, bool leaveRelic)
348{
349 JSVariableObject::d = new ActivationData(oldData);
350 d()->leftRelic = leaveRelic;
351}
352
353ActivationImp::~ActivationImp()
354{
355 if (!d()->isOnStack)
356 delete d();
357}
358
359JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
360{
361 ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase());
362
363 if (!thisObj->d()->argumentsObject)
364 thisObj->createArgumentsObject(exec);
365
366 return thisObj->d()->argumentsObject;
367}
368
369PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
370{
371 return ActivationImp::argumentsGetter;
372}
373
374bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
375{
376 if (symbolTableGet(propertyName, slot))
377 return true;
378
379 if (JSValue** location = getDirectLocation(propertyName)) {
380 slot.setValueSlot(this, location);
381 return true;
382 }
383
384 // Only return the built-in arguments object if it wasn't overridden above.
385 if (propertyName == exec->propertyNames().arguments) {
386 for (ExecState* e = exec; e; e = e->callingExecState())
387 if (e->function() == d()->function) {
388 e->dynamicGlobalObject()->tearOffActivation(e, e != exec);
389 ActivationImp* newActivation = e->activationObject();
390 slot.setCustom(newActivation, newActivation->getArgumentsGetter());
391 return true;
392 }
393
394 slot.setCustom(this, getArgumentsGetter());
395 return true;
396 }
397
398 // We don't call through to JSObject because there's no way to give an
399 // activation object getter properties or a prototype.
400 ASSERT(!_prop.hasGetterSetterProperties());
401 ASSERT(prototype() == jsNull());
402 return false;
403}
404
405bool ActivationImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
406{
407 if (propertyName == exec->propertyNames().arguments)
408 return false;
409
410 return JSVariableObject::deleteProperty(exec, propertyName);
411}
412
413void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value)
414{
415 if (symbolTablePut(propertyName, value))
416 return;
417
418 // We don't call through to JSObject because __proto__ and getter/setter
419 // properties are non-standard extensions that other implementations do not
420 // expose in the activation object.
421 ASSERT(!_prop.hasGetterSetterProperties());
422 _prop.put(propertyName, value, 0, true);
423}
424
425void ActivationImp::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
426{
427 if (symbolTablePutWithAttributes(propertyName, value, attributes))
428 return;
429
430 // We don't call through to JSObject because __proto__ and getter/setter
431 // properties are non-standard extensions that other implementations do not
432 // expose in the activation object.
433 ASSERT(!_prop.hasGetterSetterProperties());
434 _prop.put(propertyName, value, attributes, true);
435}
436
437void ActivationImp::markChildren()
438{
439 LocalStorage& localStorage = d()->localStorage;
440 size_t size = localStorage.size();
441
442 for (size_t i = 0; i < size; ++i) {
443 JSValue* value = localStorage[i].value;
444
445 if (!value->marked())
446 value->mark();
447 }
448
449 if (!d()->function->marked())
450 d()->function->mark();
451
452 if (d()->argumentsObject && !d()->argumentsObject->marked())
453 d()->argumentsObject->mark();
454}
455
456void ActivationImp::mark()
457{
458 JSObject::mark();
459 markChildren();
460}
461
462void ActivationImp::createArgumentsObject(ExecState* exec)
463{
464 // Since "arguments" is only accessible while a function is being called,
465 // we can retrieve our argument list from the ExecState for our function
466 // call instead of storing the list ourselves.
467 d()->argumentsObject = new Arguments(exec, d()->exec->function(), *d()->exec->arguments(), this);
468}
469
470JSObject* ActivationImp::toThisObject(ExecState* exec) const
471{
472 return exec->globalThisValue();
473}
474
475ActivationImp::ActivationData::ActivationData(const ActivationData& old)
476 : JSVariableObjectData(old)
477 , exec(old.exec)
478 , function(old.function)
479 , argumentsObject(old.argumentsObject)
480 , isOnStack(false)
481{
482}
483
484bool ActivationImp::isDynamicScope() const
485{
486 return d()->function->body->usesEval();
487}
488
489// ------------------------------ Global Functions -----------------------------------
490
491static JSValue* encode(ExecState* exec, const List& args, const char* do_not_escape)
492{
493 UString r = "", s, str = args[0]->toString(exec);
494 CString cstr = str.UTF8String(true);
495 if (!cstr.c_str())
496 return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
497 const char* p = cstr.c_str();
498 for (size_t k = 0; k < cstr.size(); k++, p++) {
499 char c = *p;
500 if (c && strchr(do_not_escape, c)) {
501 r.append(c);
502 } else {
503 char tmp[4];
504 sprintf(tmp, "%%%02X", (unsigned char)c);
505 r += tmp;
506 }
507 }
508 return jsString(r);
509}
510
511static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict)
512{
513 UString s = "", str = args[0]->toString(exec);
514 int k = 0, len = str.size();
515 const UChar* d = str.data();
516 UChar u = 0;
517 while (k < len) {
518 const UChar* p = d + k;
519 UChar c = *p;
520 if (c == '%') {
521 int charLen = 0;
522 if (k <= len - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) {
523 const char b0 = Lexer::convertHex(p[1], p[2]);
524 const int sequenceLen = UTF8SequenceLength(b0);
525 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
526 charLen = sequenceLen * 3;
527 char sequence[5];
528 sequence[0] = b0;
529 for (int i = 1; i < sequenceLen; ++i) {
530 const UChar* q = p + i * 3;
531 if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2]))
532 sequence[i] = Lexer::convertHex(q[1], q[2]);
533 else {
534 charLen = 0;
535 break;
536 }
537 }
538 if (charLen != 0) {
539 sequence[sequenceLen] = 0;
540 const int character = decodeUTF8Sequence(sequence);
541 if (character < 0 || character >= 0x110000) {
542 charLen = 0;
543 } else if (character >= 0x10000) {
544 // Convert to surrogate pair.
545 s.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10)));
546 u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF));
547 } else {
548 u = static_cast<UChar>(character);
549 }
550 }
551 }
552 }
553 if (charLen == 0) {
554 if (strict)
555 return throwError(exec, URIError);
556 // The only case where we don't use "strict" mode is the "unescape" function.
557 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
558 if (k <= len - 6 && p[1] == 'u'
559 && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3])
560 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
561 charLen = 6;
562 u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]);
563 }
564 }
565 if (charLen && (u == 0 || u >= 128 || !strchr(do_not_unescape, u))) {
566 c = u;
567 k += charLen - 1;
568 }
569 }
570 k++;
571 s.append(c);
572 }
573 return jsString(s);
574}
575
576static bool isStrWhiteSpace(unsigned short c)
577{
578 switch (c) {
579 case 0x0009:
580 case 0x000A:
581 case 0x000B:
582 case 0x000C:
583 case 0x000D:
584 case 0x0020:
585 case 0x00A0:
586 case 0x2028:
587 case 0x2029:
588 return true;
589 default:
590 return c > 0xff && isSeparatorSpace(c);
591 }
592}
593
594static int parseDigit(unsigned short c, int radix)
595{
596 int digit = -1;
597
598 if (c >= '0' && c <= '9') {
599 digit = c - '0';
600 } else if (c >= 'A' && c <= 'Z') {
601 digit = c - 'A' + 10;
602 } else if (c >= 'a' && c <= 'z') {
603 digit = c - 'a' + 10;
604 }
605
606 if (digit >= radix)
607 return -1;
608 return digit;
609}
610
611double parseIntOverflow(const char* s, int length, int radix)
612{
613 double number = 0.0;
614 double radixMultiplier = 1.0;
615
616 for (const char* p = s + length - 1; p >= s; p--) {
617 if (radixMultiplier == Inf) {
618 if (*p != '0') {
619 number = Inf;
620 break;
621 }
622 } else {
623 int digit = parseDigit(*p, radix);
624 number += digit * radixMultiplier;
625 }
626
627 radixMultiplier *= radix;
628 }
629
630 return number;
631}
632
633static double parseInt(const UString& s, int radix)
634{
635 int length = s.size();
636 int p = 0;
637
638 while (p < length && isStrWhiteSpace(s[p])) {
639 ++p;
640 }
641
642 double sign = 1;
643 if (p < length) {
644 if (s[p] == '+') {
645 ++p;
646 } else if (s[p] == '-') {
647 sign = -1;
648 ++p;
649 }
650 }
651
652 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
653 radix = 16;
654 p += 2;
655 } else if (radix == 0) {
656 if (p < length && s[p] == '0')
657 radix = 8;
658 else
659 radix = 10;
660 }
661
662 if (radix < 2 || radix > 36)
663 return NaN;
664
665 int firstDigitPosition = p;
666 bool sawDigit = false;
667 double number = 0;
668 while (p < length) {
669 int digit = parseDigit(s[p], radix);
670 if (digit == -1)
671 break;
672 sawDigit = true;
673 number *= radix;
674 number += digit;
675 ++p;
676 }
677
678 if (number >= mantissaOverflowLowerBound) {
679 if (radix == 10)
680 number = strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
681 else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
682 number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
683 }
684
685 if (!sawDigit)
686 return NaN;
687
688 return sign * number;
689}
690
691static double parseFloat(const UString& s)
692{
693 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
694 // Need to skip any whitespace and then one + or - sign.
695 int length = s.size();
696 int p = 0;
697 while (p < length && isStrWhiteSpace(s[p])) {
698 ++p;
699 }
700 if (p < length && (s[p] == '+' || s[p] == '-')) {
701 ++p;
702 }
703 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
704 return 0;
705 }
706
707 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
708}
709
710JSValue* eval(ExecState* exec, const ScopeChain& scopeChain, JSVariableObject* variableObject, JSGlobalObject* globalObject, JSObject* thisObj, const List& args)
711{
712 JSValue* x = args[0];
713 if (!x->isString())
714 return x;
715
716 UString s = x->toString(exec);
717
718 int sourceId;
719 int errLine;
720 UString errMsg;
721
722#if JAVASCRIPT_PROFILING
723 Profiler::profiler()->willExecute(exec, UString(), 0);
724#endif
725
726 RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg);
727
728 Debugger* dbg = exec->dynamicGlobalObject()->debugger();
729 if (dbg) {
730 bool cont = dbg->sourceParsed(exec, sourceId, UString(), s, 0, errLine, errMsg);
731 if (!cont)
732 return jsUndefined();
733 }
734
735 if (!evalNode)
736 return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
737
738 EvalExecState newExec(globalObject, thisObj, evalNode.get(), exec, scopeChain, variableObject);
739
740 JSValue* value = evalNode->execute(&newExec);
741
742#if JAVASCRIPT_PROFILING
743 Profiler::profiler()->didExecute(exec, UString(), 0);
744#endif
745
746 if (newExec.completionType() == Throw) {
747 exec->setException(value);
748 return value;
749 }
750
751 return value ? value : jsUndefined();
752}
753
754JSValue* globalFuncEval(ExecState* exec, PrototypeReflexiveFunction* function, JSObject* thisObj, const List& args)
755{
756 JSGlobalObject* globalObject = thisObj->toGlobalObject(exec);
757
758 if (!globalObject || globalObject->evalFunction() != function)
759 return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated");
760
761 ScopeChain scopeChain(globalObject);
762 return eval(exec, scopeChain, globalObject, globalObject, function->cachedGlobalObject()->toThisObject(exec), args);
763}
764
765JSValue* globalFuncParseInt(ExecState* exec, JSObject*, const List& args)
766{
767 return jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
768}
769
770JSValue* globalFuncParseFloat(ExecState* exec, JSObject*, const List& args)
771{
772 return jsNumber(parseFloat(args[0]->toString(exec)));
773}
774
775JSValue* globalFuncIsNaN(ExecState* exec, JSObject*, const List& args)
776{
777 return jsBoolean(isnan(args[0]->toNumber(exec)));
778}
779
780JSValue* globalFuncIsFinite(ExecState* exec, JSObject*, const List& args)
781{
782 double n = args[0]->toNumber(exec);
783 return jsBoolean(!isnan(n) && !isinf(n));
784}
785
786JSValue* globalFuncDecodeURI(ExecState* exec, JSObject*, const List& args)
787{
788 static const char do_not_unescape_when_decoding_URI[] =
789 "#$&+,/:;=?@";
790
791 return decode(exec, args, do_not_unescape_when_decoding_URI, true);
792}
793
794JSValue* globalFuncDecodeURIComponent(ExecState* exec, JSObject*, const List& args)
795{
796 return decode(exec, args, "", true);
797}
798
799JSValue* globalFuncEncodeURI(ExecState* exec, JSObject*, const List& args)
800{
801 static const char do_not_escape_when_encoding_URI[] =
802 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
803 "abcdefghijklmnopqrstuvwxyz"
804 "0123456789"
805 "!#$&'()*+,-./:;=?@_~";
806
807 return encode(exec, args, do_not_escape_when_encoding_URI);
808}
809
810JSValue* globalFuncEncodeURIComponent(ExecState* exec, JSObject*, const List& args)
811{
812 static const char do_not_escape_when_encoding_URI_component[] =
813 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
814 "abcdefghijklmnopqrstuvwxyz"
815 "0123456789"
816 "!'()*-._~";
817
818 return encode(exec, args, do_not_escape_when_encoding_URI_component);
819}
820
821JSValue* globalFuncEscape(ExecState* exec, JSObject*, const List& args)
822{
823 static const char do_not_escape[] =
824 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
825 "abcdefghijklmnopqrstuvwxyz"
826 "0123456789"
827 "*+-./@_";
828
829 UString r = "", s, str = args[0]->toString(exec);
830 const UChar* c = str.data();
831 for (int k = 0; k < str.size(); k++, c++) {
832 int u = c[0];
833 if (u > 255) {
834 char tmp[7];
835 sprintf(tmp, "%%u%04X", u);
836 s = UString(tmp);
837 } else if (u != 0 && strchr(do_not_escape, (char)u))
838 s = UString(c, 1);
839 else {
840 char tmp[4];
841 sprintf(tmp, "%%%02X", u);
842 s = UString(tmp);
843 }
844 r += s;
845 }
846
847 return jsString(r);
848}
849
850JSValue* globalFuncUnescape(ExecState* exec, JSObject*, const List& args)
851{
852 UString s = "", str = args[0]->toString(exec);
853 int k = 0, len = str.size();
854 while (k < len) {
855 const UChar* c = str.data() + k;
856 UChar u;
857 if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
858 if (Lexer::isHexDigit(c[2]) && Lexer::isHexDigit(c[3]) && Lexer::isHexDigit(c[4]) && Lexer::isHexDigit(c[5])) {
859 u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]);
860 c = &u;
861 k += 5;
862 }
863 } else if (c[0] == '%' && k <= len - 3 && Lexer::isHexDigit(c[1]) && Lexer::isHexDigit(c[2])) {
864 u = UChar(Lexer::convertHex(c[1], c[2]));
865 c = &u;
866 k += 2;
867 }
868 k++;
869 s += UString(c, 1);
870 }
871
872 return jsString(s);
873}
874
875#ifndef NDEBUG
876JSValue* globalFuncKJSPrint(ExecState* exec, JSObject*, const List& args)
877{
878 puts(args[0]->toString(exec).ascii());
879 return jsUndefined();
880}
881#endif
882
883// ------------------------------ PrototypeFunction -------------------------------
884
885PrototypeFunction::PrototypeFunction(ExecState* exec, int len, const Identifier& name, JSMemberFunction function)
886 : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name)
887 , m_function(function)
888{
889 ASSERT_ARG(function, function);
890 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
891}
892
893PrototypeFunction::PrototypeFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function)
894 : InternalFunctionImp(functionPrototype, name)
895 , m_function(function)
896{
897 ASSERT_ARG(function, function);
898 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
899}
900
901JSValue* PrototypeFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
902{
903 return m_function(exec, thisObj, args);
904}
905
906// ------------------------------ PrototypeReflexiveFunction -------------------------------
907
908PrototypeReflexiveFunction::PrototypeReflexiveFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function, JSGlobalObject* cachedGlobalObject)
909 : InternalFunctionImp(functionPrototype, name)
910 , m_function(function)
911 , m_cachedGlobalObject(cachedGlobalObject)
912{
913 ASSERT_ARG(function, function);
914 ASSERT_ARG(cachedGlobalObject, cachedGlobalObject);
915 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
916}
917
918JSValue* PrototypeReflexiveFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
919{
920 return m_function(exec, this, thisObj, args);
921}
922
923void PrototypeReflexiveFunction::mark()
924{
925 InternalFunctionImp::mark();
926 if (!m_cachedGlobalObject->marked())
927 m_cachedGlobalObject->mark();
928}
929
930} // namespace KJS
Note: See TracBrowser for help on using the repository browser.