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

Last change on this file since 33972 was 33972, checked in by Darin Adler, 17 years ago

JavaScriptCore:

2008-05-21 Darin Adler <Darin Adler>

Reviewed by Anders.

  • fix <rdar://problem/5952721> bug in JavaScript arguments object property lookup

Test: fast/js/arguments-bad-index.html

  • kjs/function.cpp: (KJS::IndexToNameMap::IndexToNameMap): Use unsigned instead of int. (KJS::IndexToNameMap::isMapped): Use unsigned instead of int, and also use the strict version of the numeric conversion function, since we don't want to allow trailing junk. (KJS::IndexToNameMap::unMap): Ditto. (KJS::IndexToNameMap::operator[]): Ditto.
  • kjs/function.h: Changed IndexToNameMap::size type from int to unsigned.

LayoutTests:

2008-05-21 Darin Adler <Darin Adler>

Reviewed by Anders.

  • test for <rdar://problem/5952721> bug in JavaScript arguments object property lookup
  • fast/js/arguments-bad-index-expected.txt: Added.
  • fast/js/arguments-bad-index.html: Added.
  • fast/js/resources/arguments-bad-index.js: Added.
  • 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, 2008 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, 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 unsigned 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{
236 delete [] _map;
237}
238
239bool IndexToNameMap::isMapped(const Identifier& index) const
240{
241 bool indexIsNumber;
242 unsigned indexAsNumber = index.toStrictUInt32(&indexIsNumber);
243
244 if (!indexIsNumber)
245 return false;
246
247 if (indexAsNumber >= size)
248 return false;
249
250 if (_map[indexAsNumber].isNull())
251 return false;
252
253 return true;
254}
255
256void IndexToNameMap::unMap(const Identifier& index)
257{
258 bool indexIsNumber;
259 unsigned indexAsNumber = index.toStrictUInt32(&indexIsNumber);
260
261 ASSERT(indexIsNumber && indexAsNumber < size);
262
263 _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier;
264}
265
266Identifier& IndexToNameMap::operator[](const Identifier& index)
267{
268 bool indexIsNumber;
269 unsigned indexAsNumber = index.toStrictUInt32(&indexIsNumber);
270
271 ASSERT(indexIsNumber && indexAsNumber < size);
272
273 return _map[indexAsNumber];
274}
275
276// ------------------------------ Arguments ---------------------------------
277
278const ClassInfo Arguments::info = { "Arguments", 0, 0, 0 };
279
280// ECMA 10.1.8
281Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act)
282 : JSObject(exec->lexicalGlobalObject()->objectPrototype())
283 , _activationObject(act)
284 , indexToNameMap(func, args)
285{
286 putDirect(exec->propertyNames().callee, func, DontEnum);
287 putDirect(exec->propertyNames().length, args.size(), DontEnum);
288
289 int i = 0;
290 List::const_iterator end = args.end();
291 for (List::const_iterator it = args.begin(); it != end; ++it, ++i) {
292 Identifier name = Identifier::from(i);
293 if (!indexToNameMap.isMapped(name))
294 putDirect(name, *it, DontEnum);
295 }
296}
297
298void Arguments::mark()
299{
300 JSObject::mark();
301 if (_activationObject && !_activationObject->marked())
302 _activationObject->mark();
303}
304
305JSValue* Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
306{
307 Arguments* thisObj = static_cast<Arguments*>(slot.slotBase());
308 return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
309}
310
311bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
312{
313 if (indexToNameMap.isMapped(propertyName)) {
314 slot.setCustom(this, mappedIndexGetter);
315 return true;
316 }
317
318 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
319}
320
321void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
322{
323 if (indexToNameMap.isMapped(propertyName))
324 _activationObject->put(exec, indexToNameMap[propertyName], value);
325 else
326 JSObject::put(exec, propertyName, value);
327}
328
329bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName)
330{
331 if (indexToNameMap.isMapped(propertyName)) {
332 indexToNameMap.unMap(propertyName);
333 return true;
334 } else {
335 return JSObject::deleteProperty(exec, propertyName);
336 }
337}
338
339// ------------------------------ ActivationImp --------------------------------
340
341const ClassInfo ActivationImp::info = { "Activation", 0, 0, 0 };
342
343ActivationImp::ActivationImp(const ActivationData& oldData, bool leaveRelic)
344{
345 JSVariableObject::d = new ActivationData(oldData);
346 d()->leftRelic = leaveRelic;
347}
348
349ActivationImp::~ActivationImp()
350{
351 if (!d()->isOnStack)
352 delete d();
353}
354
355JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
356{
357 ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase());
358
359 if (!thisObj->d()->argumentsObject)
360 thisObj->createArgumentsObject(exec);
361
362 return thisObj->d()->argumentsObject;
363}
364
365PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
366{
367 return ActivationImp::argumentsGetter;
368}
369
370bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
371{
372 if (symbolTableGet(propertyName, slot))
373 return true;
374
375 if (JSValue** location = getDirectLocation(propertyName)) {
376 slot.setValueSlot(this, location);
377 return true;
378 }
379
380 // Only return the built-in arguments object if it wasn't overridden above.
381 if (propertyName == exec->propertyNames().arguments) {
382 for (ExecState* e = exec; e; e = e->callingExecState())
383 if (e->function() == d()->function) {
384 e->dynamicGlobalObject()->tearOffActivation(e, e != exec);
385 ActivationImp* newActivation = e->activationObject();
386 slot.setCustom(newActivation, newActivation->getArgumentsGetter());
387 return true;
388 }
389
390 slot.setCustom(this, getArgumentsGetter());
391 return true;
392 }
393
394 // We don't call through to JSObject because there's no way to give an
395 // activation object getter properties or a prototype.
396 ASSERT(!_prop.hasGetterSetterProperties());
397 ASSERT(prototype() == jsNull());
398 return false;
399}
400
401bool ActivationImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
402{
403 if (propertyName == exec->propertyNames().arguments)
404 return false;
405
406 return JSVariableObject::deleteProperty(exec, propertyName);
407}
408
409void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value)
410{
411 if (symbolTablePut(propertyName, value))
412 return;
413
414 // We don't call through to JSObject because __proto__ and getter/setter
415 // properties are non-standard extensions that other implementations do not
416 // expose in the activation object.
417 ASSERT(!_prop.hasGetterSetterProperties());
418 _prop.put(propertyName, value, 0, true);
419}
420
421void ActivationImp::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
422{
423 if (symbolTablePutWithAttributes(propertyName, value, attributes))
424 return;
425
426 // We don't call through to JSObject because __proto__ and getter/setter
427 // properties are non-standard extensions that other implementations do not
428 // expose in the activation object.
429 ASSERT(!_prop.hasGetterSetterProperties());
430 _prop.put(propertyName, value, attributes, true);
431}
432
433void ActivationImp::markChildren()
434{
435 LocalStorage& localStorage = d()->localStorage;
436 size_t size = localStorage.size();
437
438 for (size_t i = 0; i < size; ++i) {
439 JSValue* value = localStorage[i].value;
440
441 if (!value->marked())
442 value->mark();
443 }
444
445 if (!d()->function->marked())
446 d()->function->mark();
447
448 if (d()->argumentsObject && !d()->argumentsObject->marked())
449 d()->argumentsObject->mark();
450}
451
452void ActivationImp::mark()
453{
454 JSObject::mark();
455 markChildren();
456}
457
458void ActivationImp::createArgumentsObject(ExecState* exec)
459{
460 // Since "arguments" is only accessible while a function is being called,
461 // we can retrieve our argument list from the ExecState for our function
462 // call instead of storing the list ourselves.
463 d()->argumentsObject = new Arguments(exec, d()->exec->function(), *d()->exec->arguments(), this);
464}
465
466JSObject* ActivationImp::toThisObject(ExecState* exec) const
467{
468 return exec->globalThisValue();
469}
470
471ActivationImp::ActivationData::ActivationData(const ActivationData& old)
472 : JSVariableObjectData(old)
473 , exec(old.exec)
474 , function(old.function)
475 , argumentsObject(old.argumentsObject)
476 , isOnStack(false)
477{
478}
479
480bool ActivationImp::isDynamicScope() const
481{
482 return d()->function->body->usesEval();
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 = 0;
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]) && isASCIIHexDigit(p[2])) {
519 const char b0 = Lexer::convertHex(p[1], p[2]);
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]) && isASCIIHexDigit(q[2]))
528 sequence[i] = Lexer::convertHex(q[1], q[2]);
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<UChar>(0xD800 | ((character - 0x10000) >> 10)));
542 u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF));
543 } else {
544 u = static_cast<UChar>(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]) && isASCIIHexDigit(p[3])
556 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
557 charLen = 6;
558 u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]);
559 }
560 }
561 if (charLen && (u == 0 || u >= 128 || !strchr(do_not_unescape, u))) {
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 c > 0xff && 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])) {
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], 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 = 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])) {
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
718#if JAVASCRIPT_PROFILING
719 Profiler::profiler()->willExecute(exec, UString(), 0);
720#endif
721
722 RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg);
723
724 Debugger* dbg = exec->dynamicGlobalObject()->debugger();
725 if (dbg) {
726 bool cont = dbg->sourceParsed(exec, sourceId, UString(), s, 0, errLine, errMsg);
727 if (!cont)
728 return jsUndefined();
729 }
730
731 if (!evalNode)
732 return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
733
734 EvalExecState newExec(globalObject, thisObj, evalNode.get(), exec, scopeChain, variableObject);
735
736 JSValue* value = evalNode->execute(&newExec);
737
738#if JAVASCRIPT_PROFILING
739 Profiler::profiler()->didExecute(exec, UString(), 0);
740#endif
741
742 if (newExec.completionType() == Throw) {
743 exec->setException(value);
744 return value;
745 }
746
747 return value ? value : jsUndefined();
748}
749
750JSValue* globalFuncEval(ExecState* exec, PrototypeReflexiveFunction* function, JSObject* thisObj, const List& args)
751{
752 JSGlobalObject* globalObject = thisObj->toGlobalObject(exec);
753
754 if (!globalObject || globalObject->evalFunction() != function)
755 return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated");
756
757 ScopeChain scopeChain(globalObject);
758 return eval(exec, scopeChain, globalObject, globalObject, function->cachedGlobalObject()->toThisObject(exec), args);
759}
760
761JSValue* globalFuncParseInt(ExecState* exec, JSObject*, const List& args)
762{
763 return jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
764}
765
766JSValue* globalFuncParseFloat(ExecState* exec, JSObject*, const List& args)
767{
768 return jsNumber(parseFloat(args[0]->toString(exec)));
769}
770
771JSValue* globalFuncIsNaN(ExecState* exec, JSObject*, const List& args)
772{
773 return jsBoolean(isnan(args[0]->toNumber(exec)));
774}
775
776JSValue* globalFuncIsFinite(ExecState* exec, JSObject*, const List& args)
777{
778 double n = args[0]->toNumber(exec);
779 return jsBoolean(!isnan(n) && !isinf(n));
780}
781
782JSValue* globalFuncDecodeURI(ExecState* exec, JSObject*, const List& args)
783{
784 static const char do_not_unescape_when_decoding_URI[] =
785 "#$&+,/:;=?@";
786
787 return decode(exec, args, do_not_unescape_when_decoding_URI, true);
788}
789
790JSValue* globalFuncDecodeURIComponent(ExecState* exec, JSObject*, const List& args)
791{
792 return decode(exec, args, "", true);
793}
794
795JSValue* globalFuncEncodeURI(ExecState* exec, JSObject*, const List& args)
796{
797 static const char do_not_escape_when_encoding_URI[] =
798 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
799 "abcdefghijklmnopqrstuvwxyz"
800 "0123456789"
801 "!#$&'()*+,-./:;=?@_~";
802
803 return encode(exec, args, do_not_escape_when_encoding_URI);
804}
805
806JSValue* globalFuncEncodeURIComponent(ExecState* exec, JSObject*, const List& args)
807{
808 static const char do_not_escape_when_encoding_URI_component[] =
809 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
810 "abcdefghijklmnopqrstuvwxyz"
811 "0123456789"
812 "!'()*-._~";
813
814 return encode(exec, args, do_not_escape_when_encoding_URI_component);
815}
816
817JSValue* globalFuncEscape(ExecState* exec, JSObject*, const List& args)
818{
819 static const char do_not_escape[] =
820 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
821 "abcdefghijklmnopqrstuvwxyz"
822 "0123456789"
823 "*+-./@_";
824
825 UString r = "", s, str = args[0]->toString(exec);
826 const UChar* c = str.data();
827 for (int k = 0; k < str.size(); k++, c++) {
828 int u = c[0];
829 if (u > 255) {
830 char tmp[7];
831 sprintf(tmp, "%%u%04X", u);
832 s = UString(tmp);
833 } else if (u != 0 && strchr(do_not_escape, (char)u))
834 s = UString(c, 1);
835 else {
836 char tmp[4];
837 sprintf(tmp, "%%%02X", u);
838 s = UString(tmp);
839 }
840 r += s;
841 }
842
843 return jsString(r);
844}
845
846JSValue* globalFuncUnescape(ExecState* exec, JSObject*, const List& args)
847{
848 UString s = "", str = args[0]->toString(exec);
849 int k = 0, len = str.size();
850 while (k < len) {
851 const UChar* c = str.data() + k;
852 UChar u;
853 if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
854 if (Lexer::isHexDigit(c[2]) && Lexer::isHexDigit(c[3]) && Lexer::isHexDigit(c[4]) && Lexer::isHexDigit(c[5])) {
855 u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]);
856 c = &u;
857 k += 5;
858 }
859 } else if (c[0] == '%' && k <= len - 3 && Lexer::isHexDigit(c[1]) && Lexer::isHexDigit(c[2])) {
860 u = UChar(Lexer::convertHex(c[1], c[2]));
861 c = &u;
862 k += 2;
863 }
864 k++;
865 s += UString(c, 1);
866 }
867
868 return jsString(s);
869}
870
871#ifndef NDEBUG
872JSValue* globalFuncKJSPrint(ExecState* exec, JSObject*, const List& args)
873{
874 CStringBuffer string;
875 args[0]->toString(exec).getCString(string);
876 puts(string.data());
877 return jsUndefined();
878}
879#endif
880
881// ------------------------------ PrototypeFunction -------------------------------
882
883PrototypeFunction::PrototypeFunction(ExecState* exec, int len, const Identifier& name, JSMemberFunction function)
884 : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name)
885 , m_function(function)
886{
887 ASSERT_ARG(function, function);
888 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
889}
890
891PrototypeFunction::PrototypeFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function)
892 : InternalFunctionImp(functionPrototype, name)
893 , m_function(function)
894{
895 ASSERT_ARG(function, function);
896 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
897}
898
899JSValue* PrototypeFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
900{
901 return m_function(exec, thisObj, args);
902}
903
904// ------------------------------ PrototypeReflexiveFunction -------------------------------
905
906PrototypeReflexiveFunction::PrototypeReflexiveFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function, JSGlobalObject* cachedGlobalObject)
907 : InternalFunctionImp(functionPrototype, name)
908 , m_function(function)
909 , m_cachedGlobalObject(cachedGlobalObject)
910{
911 ASSERT_ARG(function, function);
912 ASSERT_ARG(cachedGlobalObject, cachedGlobalObject);
913 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
914}
915
916JSValue* PrototypeReflexiveFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
917{
918 return m_function(exec, this, thisObj, args);
919}
920
921void PrototypeReflexiveFunction::mark()
922{
923 InternalFunctionImp::mark();
924 if (!m_cachedGlobalObject->marked())
925 m_cachedGlobalObject->mark();
926}
927
928} // namespace KJS
Note: See TracBrowser for help on using the repository browser.