]>
Commit | Line | Data |
---|---|---|
b37bf2e1 A |
1 | // -*- c-basic-offset: 2 -*- |
2 | /* | |
3 | * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) | |
4 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) | |
5 | * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. | |
6 | * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) | |
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 | ||
51 | using namespace WTF; | |
52 | using namespace Unicode; | |
53 | ||
54 | namespace KJS { | |
55 | ||
56 | // ----------------------------- FunctionImp ---------------------------------- | |
57 | ||
58 | const ClassInfo FunctionImp::info = { "Function", &InternalFunctionImp::info, 0 }; | |
59 | ||
60 | FunctionImp::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 | ||
67 | void FunctionImp::mark() | |
68 | { | |
69 | InternalFunctionImp::mark(); | |
70 | _scope.mark(); | |
71 | } | |
72 | ||
73 | JSValue* 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 | ||
86 | JSValue* 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 | ||
99 | JSValue* 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 | ||
123 | JSValue* 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 | ||
129 | bool 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 | ||
151 | void FunctionImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr) | |
152 | { | |
153 | if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) | |
154 | return; | |
155 | InternalFunctionImp::put(exec, propertyName, value, attr); | |
156 | } | |
157 | ||
158 | bool 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 | */ | |
172 | Identifier 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]] | |
191 | JSObject* 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 | ||
221 | IndexToNameMap::IndexToNameMap(FunctionImp* func, const List& args) | |
222 | { | |
223 | _map = new Identifier[args.size()]; | |
224 | this->size = args.size(); | |
225 | ||
226 | unsigned 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 | ||
232 | IndexToNameMap::~IndexToNameMap() | |
233 | { | |
234 | delete [] _map; | |
235 | } | |
236 | ||
237 | bool IndexToNameMap::isMapped(const Identifier& index) const | |
238 | { | |
239 | bool indexIsNumber; | |
240 | unsigned indexAsNumber = index.toStrictUInt32(&indexIsNumber); | |
241 | ||
242 | if (!indexIsNumber) | |
243 | return false; | |
244 | ||
245 | if (indexAsNumber >= size) | |
246 | return false; | |
247 | ||
248 | if (_map[indexAsNumber].isNull()) | |
249 | return false; | |
250 | ||
251 | return true; | |
252 | } | |
253 | ||
254 | void IndexToNameMap::unMap(const Identifier& index) | |
255 | { | |
256 | bool indexIsNumber; | |
257 | unsigned indexAsNumber = index.toStrictUInt32(&indexIsNumber); | |
258 | ||
259 | ASSERT(indexIsNumber && indexAsNumber < size); | |
260 | ||
261 | _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier; | |
262 | } | |
263 | ||
264 | Identifier& IndexToNameMap::operator[](const Identifier& index) | |
265 | { | |
266 | bool indexIsNumber; | |
267 | unsigned indexAsNumber = index.toStrictUInt32(&indexIsNumber); | |
268 | ||
269 | ASSERT(indexIsNumber && indexAsNumber < size); | |
270 | ||
271 | return _map[indexAsNumber]; | |
272 | } | |
273 | ||
274 | // ------------------------------ Arguments --------------------------------- | |
275 | ||
276 | const ClassInfo Arguments::info = { "Arguments", 0, 0 }; | |
277 | ||
278 | // ECMA 10.1.8 | |
279 | Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act) | |
280 | : JSObject(exec->lexicalGlobalObject()->objectPrototype()) | |
281 | , _activationObject(act) | |
282 | , indexToNameMap(func, args) | |
283 | { | |
284 | putDirect(exec->propertyNames().callee, func, DontEnum); | |
285 | putDirect(exec->propertyNames().length, args.size(), DontEnum); | |
286 | ||
287 | int i = 0; | |
288 | List::const_iterator end = args.end(); | |
289 | for (List::const_iterator it = args.begin(); it != end; ++it, ++i) { | |
290 | Identifier name = Identifier::from(i); | |
291 | if (!indexToNameMap.isMapped(name)) | |
292 | putDirect(name, *it, DontEnum); | |
293 | } | |
294 | } | |
295 | ||
296 | void Arguments::mark() | |
297 | { | |
298 | JSObject::mark(); | |
299 | if (_activationObject && !_activationObject->marked()) | |
300 | _activationObject->mark(); | |
301 | } | |
302 | ||
303 | JSValue* Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) | |
304 | { | |
305 | Arguments* thisObj = static_cast<Arguments*>(slot.slotBase()); | |
306 | return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]); | |
307 | } | |
308 | ||
309 | bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) | |
310 | { | |
311 | if (indexToNameMap.isMapped(propertyName)) { | |
312 | slot.setCustom(this, mappedIndexGetter); | |
313 | return true; | |
314 | } | |
315 | ||
316 | return JSObject::getOwnPropertySlot(exec, propertyName, slot); | |
317 | } | |
318 | ||
319 | void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr) | |
320 | { | |
321 | if (indexToNameMap.isMapped(propertyName)) { | |
322 | _activationObject->put(exec, indexToNameMap[propertyName], value, attr); | |
323 | } else { | |
324 | JSObject::put(exec, propertyName, value, attr); | |
325 | } | |
326 | } | |
327 | ||
328 | bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName) | |
329 | { | |
330 | if (indexToNameMap.isMapped(propertyName)) { | |
331 | indexToNameMap.unMap(propertyName); | |
332 | return true; | |
333 | } else { | |
334 | return JSObject::deleteProperty(exec, propertyName); | |
335 | } | |
336 | } | |
337 | ||
338 | // ------------------------------ ActivationImp -------------------------------- | |
339 | ||
340 | const ClassInfo ActivationImp::info = { "Activation", 0, 0 }; | |
341 | ||
342 | ActivationImp::ActivationImp(const ActivationData& oldData, bool leaveRelic) | |
343 | { | |
344 | JSVariableObject::d = new ActivationData(oldData); | |
345 | d()->leftRelic = leaveRelic; | |
346 | } | |
347 | ||
348 | ActivationImp::~ActivationImp() | |
349 | { | |
350 | if (!d()->isOnStack) | |
351 | delete d(); | |
352 | } | |
353 | ||
354 | void ActivationImp::init(ExecState* exec) | |
355 | { | |
356 | d()->symbolTable = &exec->function()->body->symbolTable(); | |
357 | d()->exec = exec; | |
358 | d()->function = exec->function(); | |
359 | d()->argumentsObject = 0; | |
360 | } | |
361 | ||
362 | JSValue* ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot) | |
363 | { | |
364 | ActivationImp* thisObj = static_cast<ActivationImp*>(slot.slotBase()); | |
365 | ||
366 | if (!thisObj->d()->argumentsObject) | |
367 | thisObj->createArgumentsObject(exec); | |
368 | ||
369 | return thisObj->d()->argumentsObject; | |
370 | } | |
371 | ||
372 | PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter() | |
373 | { | |
374 | return ActivationImp::argumentsGetter; | |
375 | } | |
376 | ||
377 | bool ActivationImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) | |
378 | { | |
379 | if (symbolTableGet(propertyName, slot)) | |
380 | return true; | |
381 | ||
382 | if (JSValue** location = getDirectLocation(propertyName)) { | |
383 | slot.setValueSlot(this, location); | |
384 | return true; | |
385 | } | |
386 | ||
387 | // Only return the built-in arguments object if it wasn't overridden above. | |
388 | if (propertyName == exec->propertyNames().arguments) { | |
389 | for (ExecState* e = exec; e; e = e->callingExecState()) | |
390 | if (e->function() == d()->function) { | |
391 | e->dynamicGlobalObject()->tearOffActivation(e, e != exec); | |
392 | ActivationImp* newActivation = e->activationObject(); | |
393 | slot.setCustom(newActivation, newActivation->getArgumentsGetter()); | |
394 | return true; | |
395 | } | |
396 | ||
397 | slot.setCustom(this, getArgumentsGetter()); | |
398 | return true; | |
399 | } | |
400 | ||
401 | // We don't call through to JSObject because there's no way to give an | |
402 | // activation object getter properties or a prototype. | |
403 | ASSERT(!_prop.hasGetterSetterProperties()); | |
404 | ASSERT(prototype() == jsNull()); | |
405 | return false; | |
406 | } | |
407 | ||
408 | bool ActivationImp::deleteProperty(ExecState* exec, const Identifier& propertyName) | |
409 | { | |
410 | if (propertyName == exec->propertyNames().arguments) | |
411 | return false; | |
412 | ||
413 | return JSVariableObject::deleteProperty(exec, propertyName); | |
414 | } | |
415 | ||
416 | void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr) | |
417 | { | |
418 | // If any bits other than DontDelete are set, then we bypass the read-only check. | |
419 | bool checkReadOnly = !(attr & ~DontDelete); | |
420 | if (symbolTablePut(propertyName, value, checkReadOnly)) | |
421 | return; | |
422 | ||
423 | // We don't call through to JSObject because __proto__ and getter/setter | |
424 | // properties are non-standard extensions that other implementations do not | |
425 | // expose in the activation object. | |
426 | ASSERT(!_prop.hasGetterSetterProperties()); | |
427 | _prop.put(propertyName, value, attr, checkReadOnly); | |
428 | } | |
429 | ||
430 | void ActivationImp::markChildren() | |
431 | { | |
432 | LocalStorage& localStorage = d()->localStorage; | |
433 | size_t size = localStorage.size(); | |
434 | ||
435 | for (size_t i = 0; i < size; ++i) { | |
436 | JSValue* value = localStorage[i].value; | |
437 | ||
438 | if (!value->marked()) | |
439 | value->mark(); | |
440 | } | |
441 | ||
442 | if (!d()->function->marked()) | |
443 | d()->function->mark(); | |
444 | ||
445 | if (d()->argumentsObject && !d()->argumentsObject->marked()) | |
446 | d()->argumentsObject->mark(); | |
447 | } | |
448 | ||
449 | void ActivationImp::mark() | |
450 | { | |
451 | JSObject::mark(); | |
452 | markChildren(); | |
453 | } | |
454 | ||
455 | void ActivationImp::createArgumentsObject(ExecState* exec) | |
456 | { | |
457 | // Since "arguments" is only accessible while a function is being called, | |
458 | // we can retrieve our argument list from the ExecState for our function | |
459 | // call instead of storing the list ourselves. | |
460 | d()->argumentsObject = new Arguments(exec, d()->exec->function(), *d()->exec->arguments(), this); | |
461 | } | |
462 | ||
463 | ActivationImp::ActivationData::ActivationData(const ActivationData& old) | |
464 | : JSVariableObjectData(old) | |
465 | , exec(old.exec) | |
466 | , function(old.function) | |
467 | , argumentsObject(old.argumentsObject) | |
468 | , isOnStack(false) | |
469 | { | |
470 | } | |
471 | ||
472 | // ------------------------------ Global Functions ----------------------------------- | |
473 | ||
474 | static JSValue* encode(ExecState* exec, const List& args, const char* do_not_escape) | |
475 | { | |
476 | UString r = "", s, str = args[0]->toString(exec); | |
477 | CString cstr = str.UTF8String(true); | |
478 | if (!cstr.c_str()) | |
479 | return throwError(exec, URIError, "String contained an illegal UTF-16 sequence."); | |
480 | const char* p = cstr.c_str(); | |
481 | for (size_t k = 0; k < cstr.size(); k++, p++) { | |
482 | char c = *p; | |
483 | if (c && strchr(do_not_escape, c)) { | |
484 | r.append(c); | |
485 | } else { | |
486 | char tmp[4]; | |
487 | snprintf(tmp, sizeof(tmp), "%%%02X", (unsigned char)c); | |
488 | r += tmp; | |
489 | } | |
490 | } | |
491 | return jsString(r); | |
492 | } | |
493 | ||
494 | static JSValue* decode(ExecState* exec, const List& args, const char* do_not_unescape, bool strict) | |
495 | { | |
496 | UString s = "", str = args[0]->toString(exec); | |
497 | int k = 0, len = str.size(); | |
498 | const UChar* d = str.data(); | |
499 | UChar u; | |
500 | while (k < len) { | |
501 | const UChar* p = d + k; | |
502 | UChar c = *p; | |
503 | if (c == '%') { | |
504 | int charLen = 0; | |
505 | if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) { | |
506 | const char b0 = Lexer::convertHex(p[1].uc, p[2].uc); | |
507 | const int sequenceLen = UTF8SequenceLength(b0); | |
508 | if (sequenceLen != 0 && k <= len - sequenceLen * 3) { | |
509 | charLen = sequenceLen * 3; | |
510 | char sequence[5]; | |
511 | sequence[0] = b0; | |
512 | for (int i = 1; i < sequenceLen; ++i) { | |
513 | const UChar* q = p + i * 3; | |
514 | if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc)) | |
515 | sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc); | |
516 | else { | |
517 | charLen = 0; | |
518 | break; | |
519 | } | |
520 | } | |
521 | if (charLen != 0) { | |
522 | sequence[sequenceLen] = 0; | |
523 | const int character = decodeUTF8Sequence(sequence); | |
524 | if (character < 0 || character >= 0x110000) { | |
525 | charLen = 0; | |
526 | } else if (character >= 0x10000) { | |
527 | // Convert to surrogate pair. | |
528 | s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10))); | |
529 | u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF)); | |
530 | } else { | |
531 | u = static_cast<unsigned short>(character); | |
532 | } | |
533 | } | |
534 | } | |
535 | } | |
536 | if (charLen == 0) { | |
537 | if (strict) | |
538 | return throwError(exec, URIError); | |
539 | // The only case where we don't use "strict" mode is the "unescape" function. | |
540 | // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE. | |
541 | if (k <= len - 6 && p[1] == 'u' | |
542 | && isASCIIHexDigit(p[2].uc) && isASCIIHexDigit(p[3].uc) | |
543 | && isASCIIHexDigit(p[4].uc) && isASCIIHexDigit(p[5].uc)) { | |
544 | charLen = 6; | |
545 | u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc); | |
546 | } | |
547 | } | |
548 | if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) { | |
549 | c = u; | |
550 | k += charLen - 1; | |
551 | } | |
552 | } | |
553 | k++; | |
554 | s.append(c); | |
555 | } | |
556 | return jsString(s); | |
557 | } | |
558 | ||
559 | static bool isStrWhiteSpace(unsigned short c) | |
560 | { | |
561 | switch (c) { | |
562 | case 0x0009: | |
563 | case 0x000A: | |
564 | case 0x000B: | |
565 | case 0x000C: | |
566 | case 0x000D: | |
567 | case 0x0020: | |
568 | case 0x00A0: | |
569 | case 0x2028: | |
570 | case 0x2029: | |
571 | return true; | |
572 | default: | |
573 | return isSeparatorSpace(c); | |
574 | } | |
575 | } | |
576 | ||
577 | static int parseDigit(unsigned short c, int radix) | |
578 | { | |
579 | int digit = -1; | |
580 | ||
581 | if (c >= '0' && c <= '9') { | |
582 | digit = c - '0'; | |
583 | } else if (c >= 'A' && c <= 'Z') { | |
584 | digit = c - 'A' + 10; | |
585 | } else if (c >= 'a' && c <= 'z') { | |
586 | digit = c - 'a' + 10; | |
587 | } | |
588 | ||
589 | if (digit >= radix) | |
590 | return -1; | |
591 | return digit; | |
592 | } | |
593 | ||
594 | double parseIntOverflow(const char* s, int length, int radix) | |
595 | { | |
596 | double number = 0.0; | |
597 | double radixMultiplier = 1.0; | |
598 | ||
599 | for (const char* p = s + length - 1; p >= s; p--) { | |
600 | if (radixMultiplier == Inf) { | |
601 | if (*p != '0') { | |
602 | number = Inf; | |
603 | break; | |
604 | } | |
605 | } else { | |
606 | int digit = parseDigit(*p, radix); | |
607 | number += digit * radixMultiplier; | |
608 | } | |
609 | ||
610 | radixMultiplier *= radix; | |
611 | } | |
612 | ||
613 | return number; | |
614 | } | |
615 | ||
616 | static double parseInt(const UString& s, int radix) | |
617 | { | |
618 | int length = s.size(); | |
619 | int p = 0; | |
620 | ||
621 | while (p < length && isStrWhiteSpace(s[p].uc)) { | |
622 | ++p; | |
623 | } | |
624 | ||
625 | double sign = 1; | |
626 | if (p < length) { | |
627 | if (s[p] == '+') { | |
628 | ++p; | |
629 | } else if (s[p] == '-') { | |
630 | sign = -1; | |
631 | ++p; | |
632 | } | |
633 | } | |
634 | ||
635 | if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) { | |
636 | radix = 16; | |
637 | p += 2; | |
638 | } else if (radix == 0) { | |
639 | if (p < length && s[p] == '0') | |
640 | radix = 8; | |
641 | else | |
642 | radix = 10; | |
643 | } | |
644 | ||
645 | if (radix < 2 || radix > 36) | |
646 | return NaN; | |
647 | ||
648 | int firstDigitPosition = p; | |
649 | bool sawDigit = false; | |
650 | double number = 0; | |
651 | while (p < length) { | |
652 | int digit = parseDigit(s[p].uc, radix); | |
653 | if (digit == -1) | |
654 | break; | |
655 | sawDigit = true; | |
656 | number *= radix; | |
657 | number += digit; | |
658 | ++p; | |
659 | } | |
660 | ||
661 | if (number >= mantissaOverflowLowerBound) { | |
662 | if (radix == 10) | |
663 | number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0); | |
664 | else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) | |
665 | number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix); | |
666 | } | |
667 | ||
668 | if (!sawDigit) | |
669 | return NaN; | |
670 | ||
671 | return sign * number; | |
672 | } | |
673 | ||
674 | static double parseFloat(const UString& s) | |
675 | { | |
676 | // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0. | |
677 | // Need to skip any whitespace and then one + or - sign. | |
678 | int length = s.size(); | |
679 | int p = 0; | |
680 | while (p < length && isStrWhiteSpace(s[p].uc)) { | |
681 | ++p; | |
682 | } | |
683 | if (p < length && (s[p] == '+' || s[p] == '-')) { | |
684 | ++p; | |
685 | } | |
686 | if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) { | |
687 | return 0; | |
688 | } | |
689 | ||
690 | return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ ); | |
691 | } | |
692 | ||
693 | JSValue* globalFuncEval(ExecState* exec, JSObject* thisObj, const List& args) | |
694 | { | |
695 | JSValue* x = args[0]; | |
696 | if (!x->isString()) | |
697 | return x; | |
698 | ||
699 | UString s = x->toString(exec); | |
700 | ||
701 | int sourceId; | |
702 | int errLine; | |
703 | UString errMsg; | |
704 | RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg); | |
705 | ||
706 | Debugger* dbg = exec->dynamicGlobalObject()->debugger(); | |
707 | if (dbg) { | |
708 | bool cont = dbg->sourceParsed(exec, sourceId, UString(), s, 0, errLine, errMsg); | |
709 | if (!cont) | |
710 | return jsUndefined(); | |
711 | } | |
712 | ||
713 | // No program node means a syntax occurred | |
714 | if (!evalNode) | |
715 | return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL); | |
716 | ||
717 | bool switchGlobal = thisObj && thisObj != exec->dynamicGlobalObject() && thisObj->isGlobalObject(); | |
718 | ||
719 | // enter a new execution context | |
720 | exec->dynamicGlobalObject()->tearOffActivation(exec); | |
721 | JSGlobalObject* globalObject = switchGlobal ? static_cast<JSGlobalObject*>(thisObj) : exec->dynamicGlobalObject(); | |
722 | EvalExecState newExec(globalObject, evalNode.get(), exec); | |
723 | ||
724 | if (switchGlobal) { | |
725 | newExec.pushScope(thisObj); | |
726 | newExec.setVariableObject(static_cast<JSGlobalObject*>(thisObj)); | |
727 | } | |
728 | JSValue* value = evalNode->execute(&newExec); | |
729 | if (switchGlobal) | |
730 | newExec.popScope(); | |
731 | ||
732 | if (newExec.completionType() == Throw) { | |
733 | exec->setException(value); | |
734 | return value; | |
735 | } | |
736 | ||
737 | return value ? value : jsUndefined(); | |
738 | } | |
739 | ||
740 | JSValue* globalFuncParseInt(ExecState* exec, JSObject*, const List& args) | |
741 | { | |
742 | return jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec))); | |
743 | } | |
744 | ||
745 | JSValue* globalFuncParseFloat(ExecState* exec, JSObject*, const List& args) | |
746 | { | |
747 | return jsNumber(parseFloat(args[0]->toString(exec))); | |
748 | } | |
749 | ||
750 | JSValue* globalFuncIsNaN(ExecState* exec, JSObject*, const List& args) | |
751 | { | |
752 | return jsBoolean(isnan(args[0]->toNumber(exec))); | |
753 | } | |
754 | ||
755 | JSValue* globalFuncIsFinite(ExecState* exec, JSObject*, const List& args) | |
756 | { | |
757 | double n = args[0]->toNumber(exec); | |
758 | return jsBoolean(!isnan(n) && !isinf(n)); | |
759 | } | |
760 | ||
761 | JSValue* globalFuncDecodeURI(ExecState* exec, JSObject*, const List& args) | |
762 | { | |
763 | static const char do_not_unescape_when_decoding_URI[] = | |
764 | "#$&+,/:;=?@"; | |
765 | ||
766 | return decode(exec, args, do_not_unescape_when_decoding_URI, true); | |
767 | } | |
768 | ||
769 | JSValue* globalFuncDecodeURIComponent(ExecState* exec, JSObject*, const List& args) | |
770 | { | |
771 | return decode(exec, args, "", true); | |
772 | } | |
773 | ||
774 | JSValue* globalFuncEncodeURI(ExecState* exec, JSObject*, const List& args) | |
775 | { | |
776 | static const char do_not_escape_when_encoding_URI[] = | |
777 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
778 | "abcdefghijklmnopqrstuvwxyz" | |
779 | "0123456789" | |
780 | "!#$&'()*+,-./:;=?@_~"; | |
781 | ||
782 | return encode(exec, args, do_not_escape_when_encoding_URI); | |
783 | } | |
784 | ||
785 | JSValue* globalFuncEncodeURIComponent(ExecState* exec, JSObject*, const List& args) | |
786 | { | |
787 | static const char do_not_escape_when_encoding_URI_component[] = | |
788 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
789 | "abcdefghijklmnopqrstuvwxyz" | |
790 | "0123456789" | |
791 | "!'()*-._~"; | |
792 | ||
793 | return encode(exec, args, do_not_escape_when_encoding_URI_component); | |
794 | } | |
795 | ||
796 | JSValue* globalFuncEscape(ExecState* exec, JSObject*, const List& args) | |
797 | { | |
798 | static const char do_not_escape[] = | |
799 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
800 | "abcdefghijklmnopqrstuvwxyz" | |
801 | "0123456789" | |
802 | "*+-./@_"; | |
803 | ||
804 | UString r = "", s, str = args[0]->toString(exec); | |
805 | const UChar* c = str.data(); | |
806 | for (int k = 0; k < str.size(); k++, c++) { | |
807 | int u = c->uc; | |
808 | if (u > 255) { | |
809 | char tmp[7]; | |
810 | snprintf(tmp, sizeof(tmp), "%%u%04X", u); | |
811 | s = UString(tmp); | |
812 | } else if (u != 0 && strchr(do_not_escape, (char)u)) | |
813 | s = UString(c, 1); | |
814 | else { | |
815 | char tmp[4]; | |
816 | snprintf(tmp, sizeof(tmp), "%%%02X", u); | |
817 | s = UString(tmp); | |
818 | } | |
819 | r += s; | |
820 | } | |
821 | ||
822 | return jsString(r); | |
823 | } | |
824 | ||
825 | JSValue* globalFuncUnescape(ExecState* exec, JSObject*, const List& args) | |
826 | { | |
827 | UString s = "", str = args[0]->toString(exec); | |
828 | int k = 0, len = str.size(); | |
829 | while (k < len) { | |
830 | const UChar* c = str.data() + k; | |
831 | UChar u; | |
832 | if (*c == UChar('%') && k <= len - 6 && *(c + 1) == UChar('u')) { | |
833 | if (Lexer::isHexDigit((c + 2)->uc) && Lexer::isHexDigit((c + 3)->uc) && Lexer::isHexDigit((c + 4)->uc) && Lexer::isHexDigit((c + 5)->uc)) { | |
834 | u = Lexer::convertUnicode((c + 2)->uc, (c + 3)->uc, (c + 4)->uc, (c + 5)->uc); | |
835 | c = &u; | |
836 | k += 5; | |
837 | } | |
838 | } else if (*c == UChar('%') && k <= len - 3 && Lexer::isHexDigit((c + 1)->uc) && Lexer::isHexDigit((c + 2)->uc)) { | |
839 | u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc)); | |
840 | c = &u; | |
841 | k += 2; | |
842 | } | |
843 | k++; | |
844 | s += UString(c, 1); | |
845 | } | |
846 | ||
847 | return jsString(s); | |
848 | } | |
849 | ||
850 | #ifndef NDEBUG | |
851 | JSValue* globalFuncKJSPrint(ExecState* exec, JSObject*, const List& args) | |
852 | { | |
853 | puts(args[0]->toString(exec).ascii()); | |
854 | return jsUndefined(); | |
855 | } | |
856 | #endif | |
857 | ||
858 | // ------------------------------ PrototypeFunction ------------------------------- | |
859 | ||
860 | PrototypeFunction::PrototypeFunction(ExecState* exec, int len, const Identifier& name, JSMemberFunction function) | |
861 | : InternalFunctionImp(exec->lexicalGlobalObject()->functionPrototype(), name) | |
862 | , m_function(function) | |
863 | { | |
864 | ASSERT_ARG(function, function); | |
865 | putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum); | |
866 | } | |
867 | ||
868 | PrototypeFunction::PrototypeFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function) | |
869 | : InternalFunctionImp(functionPrototype, name) | |
870 | , m_function(function) | |
871 | { | |
872 | ASSERT_ARG(function, function); | |
873 | putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum); | |
874 | } | |
875 | ||
876 | JSValue* PrototypeFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args) | |
877 | { | |
878 | return m_function(exec, thisObj, args); | |
879 | } | |
880 | ||
881 | } // namespace KJS |