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

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

JavaScriptCore:

Reviewed by Darin Adler.


Fixed <rdar://problem/5689093> Stricter (ES4) eval semantics


The basic rule is:


  • "eval(s)" is treated as an operator that gives the ES3 eval behavior.

... but only if there is no overriding declaration of "eval" in scope.

  • All other invocations treat eval as a function that evaluates a script in the context of its "this" object.

... but if its "this" object is not the global object it was
originally associated with, eval throws an exception.


Because only expressions of the form "eval(s)" have access to local
scope, the compiler can now statically determine whether a function
needs local scope to be dynamic.

  • kjs/nodes.h: Added FunctionCallEvalNode. It works just like FuncationCallResolveNode, except it statically indicates that the node may execute eval in the ES3 way.
  • kjs/nodes.cpp:
  • kjs/nodes2string.cpp:
  • tests/mozilla/expected.html: This patch happens to fix a Mozilla JS test, but it's a bit of a pyrrhic victory. The test intends to test Mozilla's generic API for calling eval on any object, but, in reality, we only support calling eval on the global object.

LayoutTests:

Reviewed by Darin Adler.

Tests for <rdar://problem/5689093> Stricter (ES4) eval semantics


  • fast/js/eval-cross-window-expected.txt: Added.
  • fast/js/eval-cross-window.html: Added.
  • fast/js/eval-keyword-vs-function-expected.txt: Added.
  • fast/js/eval-keyword-vs-function.html: Added.
  • fast/js/eval-overriding-expected.txt: Added.
  • fast/js/eval-overriding.html: Added.


Tests to make sure not to regress security:

  • http/tests/security/resources/xss-eval2.html: Added.
  • http/tests/security/resources/xss-eval3.html: Added.
  • http/tests/security/xss-eval-expected.txt: Added.
  • http/tests/security/xss-eval.html: Added.

I removed these tests because we no longer match the behavior they
expected, and the new tests are more comprehensive:


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