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

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

JavaScriptCore:

2008-06-03 Darin Adler <Darin Adler>

Reviewed by Geoff.

Makes standalone SunSpider 1.025x as fast as before.

The getOwnPropertySlot virtual function now takes care of the toObject call
for get. Similarly, the put function (and later deleteProperty) does the
same for those operations. To do this, the virtual functions were moved from
the JSObject class to the JSCell class. Also, since the caller no longer knows
the identity of the "original object", which is used by JavaScript-function
based getters, changed the PropertySlot class so the original object is
already stored in the slot when getOwnPropertySlot is called, if the caller
intends to call getValue.

This affected the old interpreter code enough that the easiest thing for me
was to just delete it. While I am not certain the mysterious slowdown is not
still occurring, the net change is definitely a significant speedup.

  • VM/Machine.cpp: Moved the UNLIKELY macro into AlwaysInline.h. (KJS::resolve): Set up the originalObject in the PropertySlot before calling getPropertySlot. Also removed the originalObject argument from getValue. (KJS::resolve_skip): Ditto. (KJS::resolveBaseAndProperty): Ditto. (KJS::resolveBaseAndFunc): Ditto. (KJS::Machine::privateExecute): Removed the toObject calls from the get and put functions where possible, instead calling directly with JSValue and letting the JSValue and JSCell calls handle toObject. Same for toThisObject.
  • kjs/ExecState.h: Removed OldInterpreterExecState.
  • API/JSBase.cpp: Updated includes.
  • kjs/LocalStorageEntry.h: Removed contents. Later we can remove the file too.
  • kjs/array_instance.cpp: (KJS::ArrayInstance::lengthGetter): Removed originalObject argumet. (KJS::ArrayInstance::inlineGetOwnPropertySlot): Don't pass a base value to setValueSlot. Also use UNLIKELY around the "getting elements past the end of the array" code path; less common than successfully getting an element.
  • kjs/array_object.cpp: (KJS::getProperty): Initialize the PropertySlot with the original object. Don't pass the original object to the get function. (KJS::arrayProtoFuncFilter): Ditto. (KJS::arrayProtoFuncMap): Ditto. (KJS::arrayProtoFuncEvery): Ditto. (KJS::arrayProtoFuncForEach): Ditto. (KJS::arrayProtoFuncSome): Ditto.
  • kjs/function_object.cpp: (KJS::FunctionObjectImp::construct): Removed an obsolete comment.
  • kjs/grammar.y: Eliminated support for some of the node types that were used to optimize executing from the syntax tree.
  • kjs/internal.cpp: (KJS::StringImp::toThisObject): Added. Same as toObject. (KJS::NumberImp::toThisObject): Ditto. (KJS::GetterSetterImp::getOwnPropertySlot): Added. Not reached. (KJS::GetterSetterImp::put): Ditto. (KJS::GetterSetterImp::toThisObject): Ditto.
  • kjs/internal.h: Added toThisObject to NumberImp for speed.
  • kjs/lexer.cpp: (KJS::Lexer::shift): Changed shift to just do a single character, to unroll the loop and especially to make the one character case faster. (KJS::Lexer::setCode): Call shift multiple times instead of passing a number. (KJS::Lexer::lex): Ditto. (KJS::Lexer::matchPunctuator): Ditto. Also removed unneeded elses after returns. (KJS::Lexer::scanRegExp): Ditto.
  • kjs/lexer.h: Removed the count argument from shift.
  • kjs/math_object.cpp: (KJS::mathProtoFuncPow): Call jsNaN instead of jsNumber(NaN).
  • kjs/nodes.cpp: Removed some of the things needed only for the pre-SquirrelFish execution model. (KJS::ForNode::emitCode): Handle cases where some expressions are missing by not emitting any code at all. The old way was to emit code for "true", but this is an unnecessary remnant of the old way of doing things.
  • kjs/nodes.h: Removed some of the things needed only for the pre-SquirrelFish execution model.
  • kjs/object.cpp: (KJS::JSObject::fillGetterPropertySlot): Changed to only pass in the getter function. The old code passed in a base, but it was never used when actually getting the property; the toThisObject call was pointless. Also changed to not pass a base for setUndefined.
  • kjs/object.h: Added the new JSCell operations to GetterSetterImp. Never called. (KJS::JSObject::get): Initialize the object in the PropertySlot and don't pass it in getValue. (KJS::JSObject::getOwnPropertySlotForWrite): Removed the base argument in calls to setValueSlot. (KJS::JSObject::getOwnPropertySlot): Ditto. (KJS::JSValue::get): Added. Here because it calls through to JSObject. A version of JSObject::get that also handles the other types of JSValue by creating the appropriate wrapper. Saves the virtual call to toObject. (KJS::JSValue::put): Ditto. (KJS::JSValue::deleteProperty): Ditto.
  • kjs/property_slot.cpp: (KJS::PropertySlot::undefinedGetter): Removed the originalObject argument. (KJS::PropertySlot::ungettableGetter): Ditto. (KJS::PropertySlot::functionGetter): Ditto. Use the value in the base as the "this" object, which will be set to the original object by the new PropertySlot initialization code. Also call toThisObject. The old code did not do this, but needed to so we can properly handle the activation object like the other similar code paths.
  • kjs/property_slot.h: (KJS::PropertySlot::PropertySlot): Added a constructor that takes a base object. In debug builds, set the base to 0 if you don't pass one. (KJS::PropertySlot::getValue): Don't take or pass the originalObject. (KJS::PropertySlot::setValueSlot): Don't take a base object, and clear the base object in debug builds. (KJS::PropertySlot::setGetterSlot): Ditto. (KJS::PropertySlot::setUndefined): Ditto. (KJS::PropertySlot::setUngettable): Ditto. (KJS::PropertySlot::slotBase): Assert that a base object is present. This will fire if someone actually calls the get function without having passed in a base object and the getter needs it. (KJS::PropertySlot::setBase): Added. Used by the code that implements toObject so it can supply the original object after the fact. (KJS::PropertySlot::clearBase): Added. Clears the base, but is debug-only code because it's an error to fetch the base if you don't have a guarantee it was set.
  • API/JSCallbackObject.h:
  • API/JSCallbackObjectFunctions.h: (KJS::JSCallbackObject::cachedValueGetter): (KJS::JSCallbackObject::staticValueGetter): (KJS::JSCallbackObject::staticFunctionGetter): (KJS::JSCallbackObject::callbackGetter):
  • kjs/JSActivation.cpp: (KJS::JSActivation::getOwnPropertySlot): (KJS::JSActivation::argumentsGetter):
  • kjs/JSActivation.h:
  • kjs/JSVariableObject.h: (KJS::JSVariableObject::symbolTableGet):
  • kjs/array_instance.h:
  • kjs/function.cpp: (KJS::FunctionImp::argumentsGetter): (KJS::FunctionImp::callerGetter): (KJS::FunctionImp::lengthGetter): (KJS::Arguments::mappedIndexGetter):
  • kjs/function.h:
  • kjs/lookup.h: (KJS::staticFunctionGetter): (KJS::staticValueGetter):
  • kjs/string_object.cpp: (KJS::StringInstance::lengthGetter): (KJS::StringInstance::indexGetter): (KJS::stringInstanceNumericPropertyGetter):
  • kjs/string_object.h: Removed originalObject arguments from getters. Don't pass base values to the various PropertySlot functions that no longer take them.
  • kjs/value.cpp: (KJS::JSCell::getOwnPropertySlot): Added. Calls toObject and then sets the slot. This function has to always return true, because the caller can't walk the prototype chain. Because of that, we do a getPropertySlot, not getOwnPropertySlot, which works for the caller. This is private, only called by getOwnPropertySlotInternal. (KJS::JSCell::put): Added. Calls toObject and then put. (KJS::JSCell::toThisObject): Added. Calls toObject.
  • kjs/value.h: Added get, put, and toThisObject to both JSValue and JSCell. These take care of the toObject operation without an additional virtual function call, and so make the common "already an object" case faster.
  • wtf/AlwaysInline.h: Moved the UNLIKELY macro here for now. Maybe we can find a better place later, or rename this header.

JavaScriptGlue:

2008-06-03 Darin Adler <Darin Adler>

  • UserObjectImp.cpp: (UserObjectImp::userObjectGetter): Removed originalObject argument.
  • UserObjectImp.h: Ditto.

WebCore:

2008-06-03 Justin Garcia <[email protected]>

Reviewed by John.

<rdar://problem/5763082> GMail: Hang when removing indent from nested list
<rdar://problem/5775449> In Gmail and GoogleDocs, a hang occurs when I attempt to apply a list style to a large selection of text
<rdar://problem/5937624> 9D32: Hang in Safari. Using 100% of processor

  • editing/InsertListCommand.cpp: (WebCore::InsertListCommand::modifyRange): doApply() may operate on and remove the last paragraph of the selection from the document if it's in the same list item as startOfCurrentParagraph. Return early to avoid an infinite loop and because there is no more work to be done. Added a FIXME (<rdar://problem/5983974>) about the incorrect endingSelection()s.
  • Property svn:eol-style set to native
File size: 22.8 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 "ExecState.h"
30#include "JSActivation.h"
31#include "JSGlobalObject.h"
32#include "Machine.h"
33#include "Parser.h"
34#include "PropertyNameArray.h"
35#include "debugger.h"
36#include "dtoa.h"
37#include "function_object.h"
38#include "internal.h"
39#include "lexer.h"
40#include "nodes.h"
41#include "operations.h"
42#include "scope_chain_mark.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, ScopeChainNode* scopeChain)
63 : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name)
64 , body(b)
65 , _scope(scopeChain)
66{
67}
68
69void FunctionImp::mark()
70{
71 InternalFunctionImp::mark();
72 body->mark();
73 _scope.mark();
74}
75
76CallType FunctionImp::getCallData(CallData& callData)
77{
78 callData.js.functionBody = body.get();
79 callData.js.scopeChain = _scope.node();
80 return CallTypeJS;
81}
82
83JSValue* FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
84{
85 JSValue* exception = 0;
86 RegisterFileStack* stack = &exec->dynamicGlobalObject()->registerFileStack();
87 RegisterFile* current = stack->current();
88 if (!current->safeForReentry()) {
89 stack->pushFunctionRegisterFile();
90 JSValue* result = machine().execute(body.get(), exec, this, thisObj, args, stack, _scope.node(), &exception);
91 stack->popFunctionRegisterFile();
92 exec->setException(exception);
93 return result;
94 } else {
95 JSValue* result = machine().execute(body.get(), exec, this, thisObj, args, stack, _scope.node(), &exception);
96 current->setSafeForReentry(true);
97 exec->setException(exception);
98 return result;
99 }
100}
101
102JSValue* FunctionImp::argumentsGetter(ExecState* exec, const Identifier&, const PropertySlot& slot)
103{
104 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
105 ASSERT(exec->machine());
106 return exec->machine()->retrieveArguments(exec, thisObj);
107}
108
109JSValue* FunctionImp::callerGetter(ExecState* exec, const Identifier&, const PropertySlot& slot)
110{
111 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
112 ASSERT(exec->machine());
113 return exec->machine()->retrieveCaller(exec, thisObj);
114}
115
116JSValue* FunctionImp::lengthGetter(ExecState*, const Identifier&, const PropertySlot& slot)
117{
118 FunctionImp* thisObj = static_cast<FunctionImp*>(slot.slotBase());
119 return jsNumber(thisObj->body->parameters().size());
120}
121
122bool FunctionImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
123{
124 if (propertyName == exec->propertyNames().arguments) {
125 slot.setCustom(this, argumentsGetter);
126 return true;
127 }
128
129 if (propertyName == exec->propertyNames().length) {
130 slot.setCustom(this, lengthGetter);
131 return true;
132 }
133
134 if (propertyName == exec->propertyNames().caller) {
135 slot.setCustom(this, callerGetter);
136 return true;
137 }
138
139 return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
140}
141
142void FunctionImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
143{
144 if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
145 return;
146 InternalFunctionImp::put(exec, propertyName, value);
147}
148
149bool FunctionImp::deleteProperty(ExecState* exec, const Identifier& propertyName)
150{
151 if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
152 return false;
153 return InternalFunctionImp::deleteProperty(exec, propertyName);
154}
155
156/* Returns the parameter name corresponding to the given index. eg:
157 * function f1(x, y, z): getParameterName(0) --> x
158 *
159 * If a name appears more than once, only the last index at which
160 * it appears associates with it. eg:
161 * function f2(x, x): getParameterName(0) --> null
162 */
163Identifier FunctionImp::getParameterName(int index)
164{
165 Vector<Identifier>& parameters = body->parameters();
166
167 if (static_cast<size_t>(index) >= body->parameters().size())
168 return CommonIdentifiers::shared()->nullIdentifier;
169
170 Identifier name = parameters[index];
171
172 // Are there any subsequent parameters with the same name?
173 size_t size = parameters.size();
174 for (size_t i = index + 1; i < size; ++i)
175 if (parameters[i] == name)
176 return CommonIdentifiers::shared()->nullIdentifier;
177
178 return name;
179}
180
181// ECMA 13.2.2 [[Construct]]
182ConstructType FunctionImp::getConstructData(ConstructData& constructData)
183{
184 constructData.js.functionBody = body.get();
185 constructData.js.scopeChain = _scope.node();
186 return ConstructTypeJS;
187}
188
189JSObject* FunctionImp::construct(ExecState* exec, const List& args)
190{
191 JSObject* proto;
192 JSValue* p = get(exec, exec->propertyNames().prototype);
193 if (p->isObject())
194 proto = static_cast<JSObject*>(p);
195 else
196 proto = exec->lexicalGlobalObject()->objectPrototype();
197
198 JSObject* thisObj = new JSObject(proto);
199
200 JSValue* exception = 0;
201 JSValue* result = machine().execute(body.get(), exec, this, thisObj, args, &exec->dynamicGlobalObject()->registerFileStack(), _scope.node(), &exception);
202 if (exception) {
203 exec->setException(exception);
204 return thisObj;
205 }
206
207 if (result->isObject())
208 return static_cast<JSObject*>(result);
209 return thisObj;
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, JSActivation* 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, 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// ------------------------------ Global Functions -----------------------------------
340
341static JSValue* encode(ExecState* exec, const List& args, const char* do_not_escape)
342{
343 UString r = "", s, str = args[0]->toString(exec);
344 CString cstr = str.UTF8String(true);
345 if (!cstr.c_str())
346 return throwError(exec, URIError, "String contained an illegal UTF-16 sequence.");
347 const char* p = cstr.c_str();
348 for (size_t k = 0; k < cstr.size(); k++, p++) {
349 char c = *p;
350 if (c && strchr(do_not_escape, c)) {
351 r.append(c);
352 } else {
353 char tmp[4];
354 sprintf(tmp, "%%%02X", (unsigned char)c);
355 r += tmp;
356 }
357 }
358 return jsString(r);
359}
360
361static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict)
362{
363 UString s = "", str = args[0]->toString(exec);
364 int k = 0, len = str.size();
365 const UChar* d = str.data();
366 UChar u = 0;
367 while (k < len) {
368 const UChar* p = d + k;
369 UChar c = *p;
370 if (c == '%') {
371 int charLen = 0;
372 if (k <= len - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) {
373 const char b0 = Lexer::convertHex(p[1], p[2]);
374 const int sequenceLen = UTF8SequenceLength(b0);
375 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
376 charLen = sequenceLen * 3;
377 char sequence[5];
378 sequence[0] = b0;
379 for (int i = 1; i < sequenceLen; ++i) {
380 const UChar* q = p + i * 3;
381 if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2]))
382 sequence[i] = Lexer::convertHex(q[1], q[2]);
383 else {
384 charLen = 0;
385 break;
386 }
387 }
388 if (charLen != 0) {
389 sequence[sequenceLen] = 0;
390 const int character = decodeUTF8Sequence(sequence);
391 if (character < 0 || character >= 0x110000) {
392 charLen = 0;
393 } else if (character >= 0x10000) {
394 // Convert to surrogate pair.
395 s.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10)));
396 u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF));
397 } else {
398 u = static_cast<UChar>(character);
399 }
400 }
401 }
402 }
403 if (charLen == 0) {
404 if (strict)
405 return throwError(exec, URIError);
406 // The only case where we don't use "strict" mode is the "unescape" function.
407 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
408 if (k <= len - 6 && p[1] == 'u'
409 && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3])
410 && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) {
411 charLen = 6;
412 u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]);
413 }
414 }
415 if (charLen && (u == 0 || u >= 128 || !strchr(do_not_unescape, u))) {
416 c = u;
417 k += charLen - 1;
418 }
419 }
420 k++;
421 s.append(c);
422 }
423 return jsString(s);
424}
425
426static bool isStrWhiteSpace(unsigned short c)
427{
428 switch (c) {
429 case 0x0009:
430 case 0x000A:
431 case 0x000B:
432 case 0x000C:
433 case 0x000D:
434 case 0x0020:
435 case 0x00A0:
436 case 0x2028:
437 case 0x2029:
438 return true;
439 default:
440 return c > 0xff && isSeparatorSpace(c);
441 }
442}
443
444static int parseDigit(unsigned short c, int radix)
445{
446 int digit = -1;
447
448 if (c >= '0' && c <= '9') {
449 digit = c - '0';
450 } else if (c >= 'A' && c <= 'Z') {
451 digit = c - 'A' + 10;
452 } else if (c >= 'a' && c <= 'z') {
453 digit = c - 'a' + 10;
454 }
455
456 if (digit >= radix)
457 return -1;
458 return digit;
459}
460
461double parseIntOverflow(const char* s, int length, int radix)
462{
463 double number = 0.0;
464 double radixMultiplier = 1.0;
465
466 for (const char* p = s + length - 1; p >= s; p--) {
467 if (radixMultiplier == Inf) {
468 if (*p != '0') {
469 number = Inf;
470 break;
471 }
472 } else {
473 int digit = parseDigit(*p, radix);
474 number += digit * radixMultiplier;
475 }
476
477 radixMultiplier *= radix;
478 }
479
480 return number;
481}
482
483static double parseInt(const UString& s, int radix)
484{
485 int length = s.size();
486 int p = 0;
487
488 while (p < length && isStrWhiteSpace(s[p])) {
489 ++p;
490 }
491
492 double sign = 1;
493 if (p < length) {
494 if (s[p] == '+') {
495 ++p;
496 } else if (s[p] == '-') {
497 sign = -1;
498 ++p;
499 }
500 }
501
502 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
503 radix = 16;
504 p += 2;
505 } else if (radix == 0) {
506 if (p < length && s[p] == '0')
507 radix = 8;
508 else
509 radix = 10;
510 }
511
512 if (radix < 2 || radix > 36)
513 return NaN;
514
515 int firstDigitPosition = p;
516 bool sawDigit = false;
517 double number = 0;
518 while (p < length) {
519 int digit = parseDigit(s[p], radix);
520 if (digit == -1)
521 break;
522 sawDigit = true;
523 number *= radix;
524 number += digit;
525 ++p;
526 }
527
528 if (number >= mantissaOverflowLowerBound) {
529 if (radix == 10)
530 number = strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0);
531 else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
532 number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
533 }
534
535 if (!sawDigit)
536 return NaN;
537
538 return sign * number;
539}
540
541static double parseFloat(const UString& s)
542{
543 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
544 // Need to skip any whitespace and then one + or - sign.
545 int length = s.size();
546 int p = 0;
547 while (p < length && isStrWhiteSpace(s[p])) {
548 ++p;
549 }
550 if (p < length && (s[p] == '+' || s[p] == '-')) {
551 ++p;
552 }
553 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
554 return 0;
555 }
556
557 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
558}
559
560JSValue* globalFuncEval(ExecState* exec, PrototypeReflexiveFunction* function, JSObject* thisObj, const List& args)
561{
562 JSGlobalObject* globalObject = thisObj->toGlobalObject(exec);
563
564 if (!globalObject || globalObject->evalFunction() != function)
565 return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated");
566
567 JSValue* x = args[0];
568 if (!x->isString())
569 return x;
570
571 UString s = x->toString(exec);
572
573 int sourceId;
574 int errLine;
575 UString errMsg;
576
577 RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(exec, UString(), 1, UStringSourceProvider::create(s), &sourceId, &errLine, &errMsg);
578
579 if (!evalNode)
580 return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
581
582 JSValue* exception = 0;
583 JSValue* value = machine().execute(evalNode.get(), exec, thisObj, &exec->dynamicGlobalObject()->registerFileStack(), globalObject->globalScopeChain().node(), &exception);
584
585 if (exception) {
586 exec->setException(exception);
587 return value;
588 }
589
590 return value ? value : jsUndefined();
591}
592
593JSValue* globalFuncParseInt(ExecState* exec, JSObject*, const List& args)
594{
595 return jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
596}
597
598JSValue* globalFuncParseFloat(ExecState* exec, JSObject*, const List& args)
599{
600 return jsNumber(parseFloat(args[0]->toString(exec)));
601}
602
603JSValue* globalFuncIsNaN(ExecState* exec, JSObject*, const List& args)
604{
605 return jsBoolean(isnan(args[0]->toNumber(exec)));
606}
607
608JSValue* globalFuncIsFinite(ExecState* exec, JSObject*, const List& args)
609{
610 double n = args[0]->toNumber(exec);
611 return jsBoolean(!isnan(n) && !isinf(n));
612}
613
614JSValue* globalFuncDecodeURI(ExecState* exec, JSObject*, const List& args)
615{
616 static const char do_not_unescape_when_decoding_URI[] =
617 "#$&+,/:;=?@";
618
619 return decode(exec, args, do_not_unescape_when_decoding_URI, true);
620}
621
622JSValue* globalFuncDecodeURIComponent(ExecState* exec, JSObject*, const List& args)
623{
624 return decode(exec, args, "", true);
625}
626
627JSValue* globalFuncEncodeURI(ExecState* exec, JSObject*, const List& args)
628{
629 static const char do_not_escape_when_encoding_URI[] =
630 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
631 "abcdefghijklmnopqrstuvwxyz"
632 "0123456789"
633 "!#$&'()*+,-./:;=?@_~";
634
635 return encode(exec, args, do_not_escape_when_encoding_URI);
636}
637
638JSValue* globalFuncEncodeURIComponent(ExecState* exec, JSObject*, const List& args)
639{
640 static const char do_not_escape_when_encoding_URI_component[] =
641 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
642 "abcdefghijklmnopqrstuvwxyz"
643 "0123456789"
644 "!'()*-._~";
645
646 return encode(exec, args, do_not_escape_when_encoding_URI_component);
647}
648
649JSValue* globalFuncEscape(ExecState* exec, JSObject*, const List& args)
650{
651 static const char do_not_escape[] =
652 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
653 "abcdefghijklmnopqrstuvwxyz"
654 "0123456789"
655 "*+-./@_";
656
657 UString r = "", s, str = args[0]->toString(exec);
658 const UChar* c = str.data();
659 for (int k = 0; k < str.size(); k++, c++) {
660 int u = c[0];
661 if (u > 255) {
662 char tmp[7];
663 sprintf(tmp, "%%u%04X", u);
664 s = UString(tmp);
665 } else if (u != 0 && strchr(do_not_escape, (char)u))
666 s = UString(c, 1);
667 else {
668 char tmp[4];
669 sprintf(tmp, "%%%02X", u);
670 s = UString(tmp);
671 }
672 r += s;
673 }
674
675 return jsString(r);
676}
677
678JSValue* globalFuncUnescape(ExecState* exec, JSObject*, const List& args)
679{
680 UString s = "", str = args[0]->toString(exec);
681 int k = 0, len = str.size();
682 while (k < len) {
683 const UChar* c = str.data() + k;
684 UChar u;
685 if (c[0] == '%' && k <= len - 6 && c[1] == 'u') {
686 if (Lexer::isHexDigit(c[2]) && Lexer::isHexDigit(c[3]) && Lexer::isHexDigit(c[4]) && Lexer::isHexDigit(c[5])) {
687 u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]);
688 c = &u;
689 k += 5;
690 }
691 } else if (c[0] == '%' && k <= len - 3 && Lexer::isHexDigit(c[1]) && Lexer::isHexDigit(c[2])) {
692 u = UChar(Lexer::convertHex(c[1], c[2]));
693 c = &u;
694 k += 2;
695 }
696 k++;
697 s += UString(c, 1);
698 }
699
700 return jsString(s);
701}
702
703#ifndef NDEBUG
704JSValue* globalFuncKJSPrint(ExecState* exec, JSObject*, const List& args)
705{
706 CStringBuffer string;
707 args[0]->toString(exec).getCString(string);
708 puts(string.data());
709 return jsUndefined();
710}
711#endif
712
713// ------------------------------ PrototypeFunction -------------------------------
714
715PrototypeFunction::PrototypeFunction(ExecState* exec, int len, const Identifier& name, JSMemberFunction function)
716 : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name)
717 , m_function(function)
718{
719 ASSERT_ARG(function, function);
720 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
721}
722
723PrototypeFunction::PrototypeFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function)
724 : InternalFunctionImp(functionPrototype, name)
725 , m_function(function)
726{
727 ASSERT_ARG(function, function);
728 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
729}
730
731JSValue* PrototypeFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
732{
733 return m_function(exec, thisObj, args);
734}
735
736// ------------------------------ PrototypeReflexiveFunction -------------------------------
737
738PrototypeReflexiveFunction::PrototypeReflexiveFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function, JSGlobalObject* cachedGlobalObject)
739 : InternalFunctionImp(functionPrototype, name)
740 , m_function(function)
741 , m_cachedGlobalObject(cachedGlobalObject)
742{
743 ASSERT_ARG(function, function);
744 ASSERT_ARG(cachedGlobalObject, cachedGlobalObject);
745 putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
746}
747
748JSValue* PrototypeReflexiveFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
749{
750 return m_function(exec, this, thisObj, args);
751}
752
753void PrototypeReflexiveFunction::mark()
754{
755 InternalFunctionImp::mark();
756 if (!m_cachedGlobalObject->marked())
757 m_cachedGlobalObject->mark();
758}
759
760} // namespace KJS
Note: See TracBrowser for help on using the repository browser.