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

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

JavaScriptCore:

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

Reviewed by Geoffrey Garen.

First step in implementing the "split window"

  • Add a GlobalThisValue to ExecState which should be used in places that used to implement the "use the global object as this if null" rule.
  • Factor out lookupGetter/lookupSetter into virtual methods on JSObject so that they can be forwarded.
  • Make defineGetter/defineSetter virtual methods for the same reason.
  • Have PrototypeReflexiveFunction store the globalObject used to create it so that it can be used to get the correct thisObject for eval.
  • API/JSObjectRef.cpp: (JSObjectCallAsFunction):
  • JavaScriptCore.exp:
  • kjs/Activation.h:
  • kjs/ExecState.cpp: (KJS::ExecState::ExecState): (KJS::GlobalExecState::GlobalExecState):
  • kjs/ExecState.h: (KJS::ExecState::globalThisValue):
  • kjs/ExecStateInlines.h: (KJS::ExecState::ExecState): (KJS::FunctionExecState::FunctionExecState):
  • kjs/JSGlobalObject.cpp: (KJS::JSGlobalObject::reset): (KJS::JSGlobalObject::toGlobalObject):
  • kjs/JSGlobalObject.h: (KJS::JSGlobalObject::JSGlobalObjectData::JSGlobalObjectData): (KJS::JSGlobalObject::JSGlobalObject):
  • kjs/array_instance.cpp: (KJS::CompareWithCompareFunctionArguments::CompareWithCompareFunctionArguments): (KJS::compareWithCompareFunctionForQSort):
  • kjs/array_object.cpp: (KJS::arrayProtoFuncSort): (KJS::arrayProtoFuncFilter): (KJS::arrayProtoFuncMap): (KJS::arrayProtoFuncEvery): (KJS::arrayProtoFuncForEach): (KJS::arrayProtoFuncSome):
  • kjs/function.cpp: (KJS::FunctionImp::callAsFunction): (KJS::ActivationImp::toThisObject): (KJS::globalFuncEval): (KJS::PrototypeReflexiveFunction::PrototypeReflexiveFunction): (KJS::PrototypeReflexiveFunction::mark):
  • kjs/function.h: (KJS::PrototypeReflexiveFunction::cachedGlobalObject):
  • kjs/function_object.cpp: (KJS::functionProtoFuncApply): (KJS::functionProtoFuncCall):
  • kjs/nodes.cpp: (KJS::ExpressionNode::resolveAndCall): (KJS::FunctionCallValueNode::evaluate): (KJS::LocalVarFunctionCallNode::inlineEvaluate): (KJS::ScopedVarFunctionCallNode::inlineEvaluate): (KJS::FunctionCallBracketNode::evaluate): (KJS::FunctionCallDotNode::inlineEvaluate):
  • kjs/object.cpp: (KJS::JSObject::call): (KJS::JSObject::put): (KJS::tryGetAndCallProperty): (KJS::JSObject::lookupGetter): (KJS::JSObject::lookupSetter): (KJS::JSObject::toThisObject): (KJS::JSObject::toGlobalObject): (KJS::JSObject::fillGetterPropertySlot):
  • kjs/object.h:
  • kjs/object_object.cpp: (KJS::objectProtoFuncLookupGetter): (KJS::objectProtoFuncLookupSetter):
  • kjs/string_object.cpp: (KJS::replace):

WebCore:

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

Reviewed by Geoffrey Garen.

First step in implementing the "split window"

  • This patch takes the first step in changing the window navigation model from clearing the window properties on navigation, to replacing an inner window. This is necessary to safely perform security checks using the lexical global object.

This first step adds a new class called JSDOMWindowWrapper, which wraps
the real window object. All JS calls that would go to the window object
now go to it, which it forwards to the current inner window. To accomplish
this, the wrapper window is used as the ThisValue wherever the window was used
before.

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