1 // -*- c-basic-offset: 2 -*-
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
5 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
6 * Copyright (C) 2003 Apple Computer, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
29 #include "function_object.h"
32 #include "operations.h"
43 #include <wtf/unicode/Unicode.h>
47 // ----------------------------- FunctionImp ----------------------------------
49 const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, 0, 0};
53 Parameter(const Identifier &n) : name(n) { }
55 OwnPtr<Parameter> next;
58 FunctionImp::FunctionImp(ExecState *exec, const Identifier &n, FunctionBodyNode* b)
59 : InternalFunctionImp(static_cast<FunctionPrototype*>
60 (exec->lexicalInterpreter()->builtinFunctionPrototype()), n)
65 FunctionImp::~FunctionImp()
69 JSValue *FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
71 JSObject *globalObj = exec->dynamicInterpreter()->globalObject();
73 // enter a new execution context
74 Context ctx(globalObj, exec->dynamicInterpreter(), thisObj, body.get(),
75 codeType(), exec->context(), this, &args);
76 ExecState newExec(exec->dynamicInterpreter(), &ctx);
77 if (exec->hadException())
78 newExec.setException(exec->exception());
80 // assign user supplied arguments to parameters
81 processParameters(&newExec, args);
82 // add variable declarations (initialized to undefined)
83 processVarDecls(&newExec);
85 Debugger *dbg = exec->dynamicInterpreter()->debugger();
89 if (inherits(&DeclaredFunctionImp::info)) {
90 sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
91 lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
94 bool cont = dbg->callEvent(&newExec,sid,lineno,this,args);
101 Completion comp = execute(&newExec);
103 // if an exception occured, propogate it back to the previous execution object
104 if (newExec.hadException())
105 comp = Completion(Throw, newExec.exception());
108 if (comp.complType() == Throw)
109 printInfo(exec,"throwing", comp.value());
110 else if (comp.complType() == ReturnValue)
111 printInfo(exec,"returning", comp.value());
113 fprintf(stderr, "returning: undefined\n");
116 // The debugger may have been deallocated by now if the WebFrame
117 // we were running in has been destroyed, so refetch it.
118 // See http://bugzilla.opendarwin.org/show_bug.cgi?id=9477
119 dbg = exec->dynamicInterpreter()->debugger();
122 if (inherits(&DeclaredFunctionImp::info))
123 lineno = static_cast<DeclaredFunctionImp*>(this)->body->lastLine();
125 if (comp.complType() == Throw)
126 newExec.setException(comp.value());
128 int cont = dbg->returnEvent(&newExec,sid,lineno,this);
131 return jsUndefined();
135 if (comp.complType() == Throw) {
136 exec->setException(comp.value());
139 else if (comp.complType() == ReturnValue)
142 return jsUndefined();
145 void FunctionImp::addParameter(const Identifier &n)
147 OwnPtr<Parameter> *p = ¶m;
151 p->set(new Parameter(n));
154 UString FunctionImp::parameterString() const
157 const Parameter *p = param.get();
161 s += p->name.ustring();
170 void FunctionImp::processParameters(ExecState *exec, const List &args)
172 JSObject* variable = exec->context()->variableObject();
175 fprintf(stderr, "---------------------------------------------------\n"
176 "processing parameters for %s call\n",
177 name().isEmpty() ? "(internal)" : name().ascii());
181 ListIterator it = args.begin();
182 Parameter *p = param.get();
185 if (it != args.end()) {
187 fprintf(stderr, "setting parameter %s ", p->name.ascii());
188 printInfo(exec,"to", *it);
190 variable->put(exec, p->name, v);
193 variable->put(exec, p->name, jsUndefined());
199 for (int i = 0; i < args.size(); i++)
200 printInfo(exec,"setting argument", args[i]);
205 void FunctionImp::processVarDecls(ExecState */*exec*/)
209 JSValue *FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
211 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
212 Context *context = exec->m_context;
214 if (context->function() == thisObj) {
215 return static_cast<ActivationImp *>(context->activationObject())->get(exec, propertyName);
217 context = context->callingContext();
222 JSValue *FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
224 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
225 const Parameter *p = thisObj->param.get();
231 return jsNumber(count);
234 bool FunctionImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
236 // Find the arguments from the closest context.
237 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
238 slot.setCustom(this, argumentsGetter);
242 // Compute length of parameters.
243 if (propertyName == lengthPropertyName) {
244 slot.setCustom(this, lengthGetter);
248 return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
251 void FunctionImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
253 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier() || propertyName == lengthPropertyName)
255 InternalFunctionImp::put(exec, propertyName, value, attr);
258 bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
260 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier() || propertyName == lengthPropertyName)
262 return InternalFunctionImp::deleteProperty(exec, propertyName);
265 /* Returns the parameter name corresponding to the given index. eg:
266 * function f1(x, y, z): getParameterName(0) --> x
268 * If a name appears more than once, only the last index at which
269 * it appears associates with it. eg:
270 * function f2(x, x): getParameterName(0) --> null
272 Identifier FunctionImp::getParameterName(int index)
275 Parameter *p = param.get();
278 return Identifier::null();
280 // skip to the parameter we want
281 while (i++ < index && (p = p->next.get()))
285 return Identifier::null();
287 Identifier name = p->name;
289 // Are there any subsequent parameters with the same name?
290 while ((p = p->next.get()))
292 return Identifier::null();
297 // ------------------------------ DeclaredFunctionImp --------------------------
299 // ### is "Function" correct here?
300 const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
302 DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
303 FunctionBodyNode *b, const ScopeChain &sc)
304 : FunctionImp(exec, n, b)
309 bool DeclaredFunctionImp::implementsConstruct() const
314 // ECMA 13.2.2 [[Construct]]
315 JSObject *DeclaredFunctionImp::construct(ExecState *exec, const List &args)
318 JSValue *p = get(exec,prototypePropertyName);
320 proto = static_cast<JSObject*>(p);
322 proto = exec->lexicalInterpreter()->builtinObjectPrototype();
324 JSObject *obj(new JSObject(proto));
326 JSValue *res = call(exec,obj,args);
329 return static_cast<JSObject *>(res);
334 Completion DeclaredFunctionImp::execute(ExecState *exec)
336 Completion result = body->execute(exec);
338 if (result.complType() == Throw || result.complType() == ReturnValue)
340 return Completion(Normal, jsUndefined()); // TODO: or ReturnValue ?
343 void DeclaredFunctionImp::processVarDecls(ExecState *exec)
345 body->processVarDecls(exec);
348 // ------------------------------ IndexToNameMap ---------------------------------
350 // We map indexes in the arguments array to their corresponding argument names.
351 // Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
353 // Once we have an argument name, we can get and set the argument's value in the
354 // activation object.
356 // We use Identifier::null to indicate that a given argument's value
357 // isn't stored in the activation object.
359 IndexToNameMap::IndexToNameMap(FunctionImp *func, const List &args)
361 _map = new Identifier[args.size()];
362 this->size = args.size();
365 ListIterator iterator = args.begin();
366 for (; iterator != args.end(); i++, iterator++)
367 _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
370 IndexToNameMap::~IndexToNameMap() {
374 bool IndexToNameMap::isMapped(const Identifier &index) const
377 int indexAsNumber = index.toUInt32(&indexIsNumber);
382 if (indexAsNumber >= size)
385 if (_map[indexAsNumber].isNull())
391 void IndexToNameMap::unMap(const Identifier &index)
394 int indexAsNumber = index.toUInt32(&indexIsNumber);
396 assert(indexIsNumber && indexAsNumber < size);
398 _map[indexAsNumber] = Identifier::null();
401 Identifier& IndexToNameMap::operator[](int index)
406 Identifier& IndexToNameMap::operator[](const Identifier &index)
409 int indexAsNumber = index.toUInt32(&indexIsNumber);
411 assert(indexIsNumber && indexAsNumber < size);
413 return (*this)[indexAsNumber];
416 // ------------------------------ Arguments ---------------------------------
418 const ClassInfo Arguments::info = {"Arguments", 0, 0, 0};
421 Arguments::Arguments(ExecState *exec, FunctionImp *func, const List &args, ActivationImp *act)
422 : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()),
423 _activationObject(act),
424 indexToNameMap(func, args)
426 putDirect(calleePropertyName, func, DontEnum);
427 putDirect(lengthPropertyName, args.size(), DontEnum);
430 ListIterator iterator = args.begin();
431 for (; iterator != args.end(); i++, iterator++) {
432 if (!indexToNameMap.isMapped(Identifier::from(i))) {
433 JSObject::put(exec, Identifier::from(i), *iterator, DontEnum);
438 void Arguments::mark()
441 if (_activationObject && !_activationObject->marked())
442 _activationObject->mark();
445 JSValue *Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
447 Arguments *thisObj = static_cast<Arguments *>(slot.slotBase());
448 return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
451 bool Arguments::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
453 if (indexToNameMap.isMapped(propertyName)) {
454 slot.setCustom(this, mappedIndexGetter);
458 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
461 void Arguments::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
463 if (indexToNameMap.isMapped(propertyName)) {
464 _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
466 JSObject::put(exec, propertyName, value, attr);
470 bool Arguments::deleteProperty(ExecState *exec, const Identifier &propertyName)
472 if (indexToNameMap.isMapped(propertyName)) {
473 indexToNameMap.unMap(propertyName);
476 return JSObject::deleteProperty(exec, propertyName);
480 // ------------------------------ ActivationImp --------------------------------
482 const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
485 ActivationImp::ActivationImp(FunctionImp *function, const List &arguments)
486 : _function(function), _arguments(true), _argumentsObject(0)
488 _arguments.copyFrom(arguments);
489 // FIXME: Do we need to support enumerating the arguments property?
492 JSValue *ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
494 ActivationImp *thisObj = static_cast<ActivationImp *>(slot.slotBase());
496 // default: return builtin arguments array
497 if (!thisObj->_argumentsObject)
498 thisObj->createArgumentsObject(exec);
500 return thisObj->_argumentsObject;
503 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
505 return ActivationImp::argumentsGetter;
508 bool ActivationImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
510 // do this first so property map arguments property wins over the below
511 // we don't call JSObject because we won't have getter/setter properties
512 // and we don't want to support __proto__
514 if (JSValue **location = getDirectLocation(propertyName)) {
515 slot.setValueSlot(this, location);
519 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
520 slot.setCustom(this, getArgumentsGetter());
527 bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
529 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier())
531 return JSObject::deleteProperty(exec, propertyName);
534 void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
536 // There's no way that an activation object can have a prototype or getter/setter properties
537 assert(!_prop.hasGetterSetterProperties());
538 assert(prototype() == jsNull());
540 _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
543 void ActivationImp::mark()
545 if (_function && !_function->marked())
548 if (_argumentsObject && !_argumentsObject->marked())
549 _argumentsObject->mark();
553 void ActivationImp::createArgumentsObject(ExecState *exec) const
555 _argumentsObject = new Arguments(exec, _function, _arguments, const_cast<ActivationImp *>(this));
558 // ------------------------------ GlobalFunc -----------------------------------
561 GlobalFuncImp::GlobalFuncImp(ExecState*, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
562 : InternalFunctionImp(funcProto, name)
565 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
568 CodeType GlobalFuncImp::codeType() const
570 return id == Eval ? EvalCode : codeType();
573 static JSValue *encode(ExecState *exec, const List &args, const char *do_not_escape)
575 UString r = "", s, str = args[0]->toString(exec);
576 CString cstr = str.UTF8String();
577 const char *p = cstr.c_str();
578 for (size_t k = 0; k < cstr.size(); k++, p++) {
580 if (c && strchr(do_not_escape, c)) {
584 sprintf(tmp, "%%%02X", (unsigned char)c);
591 static JSValue *decode(ExecState *exec, const List &args, const char *do_not_unescape, bool strict)
593 UString s = "", str = args[0]->toString(exec);
594 int k = 0, len = str.size();
595 const UChar *d = str.data();
598 const UChar *p = d + k;
602 if (k <= len - 3 && isxdigit(p[1].uc) && isxdigit(p[2].uc)) {
603 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
604 const int sequenceLen = UTF8SequenceLength(b0);
605 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
606 charLen = sequenceLen * 3;
609 for (int i = 1; i < sequenceLen; ++i) {
610 const UChar *q = p + i * 3;
611 if (q[0] == '%' && isxdigit(q[1].uc) && isxdigit(q[2].uc))
612 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
619 sequence[sequenceLen] = 0;
620 const int character = decodeUTF8Sequence(sequence);
621 if (character < 0 || character >= 0x110000) {
623 } else if (character >= 0x10000) {
624 // Convert to surrogate pair.
625 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
626 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
628 u = static_cast<unsigned short>(character);
635 return throwError(exec, URIError);
636 // The only case where we don't use "strict" mode is the "unescape" function.
637 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
638 if (k <= len - 6 && p[1] == 'u'
639 && isxdigit(p[2].uc) && isxdigit(p[3].uc)
640 && isxdigit(p[4].uc) && isxdigit(p[5].uc)) {
642 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
645 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
656 static bool isStrWhiteSpace(unsigned short c)
670 return WTF::Unicode::isSeparatorSpace(c);
674 static int parseDigit(unsigned short c, int radix)
678 if (c >= '0' && c <= '9') {
680 } else if (c >= 'A' && c <= 'Z') {
681 digit = c - 'A' + 10;
682 } else if (c >= 'a' && c <= 'z') {
683 digit = c - 'a' + 10;
691 static double parseInt(const UString &s, int radix)
693 int length = s.size();
696 while (p < length && isStrWhiteSpace(s[p].uc)) {
704 } else if (s[p] == '-') {
710 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
713 } else if (radix == 0) {
714 if (p < length && s[p] == '0')
720 if (radix < 2 || radix > 36)
723 bool sawDigit = false;
726 int digit = parseDigit(s[p].uc, radix);
738 return sign * number;
741 static double parseFloat(const UString &s)
743 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
744 // Need to skip any whitespace and then one + or - sign.
745 int length = s.size();
747 while (p < length && isStrWhiteSpace(s[p].uc)) {
750 if (p < length && (s[p] == '+' || s[p] == '-')) {
753 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
757 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
760 JSValue *GlobalFuncImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
762 JSValue *res = jsUndefined();
764 static const char do_not_escape[] =
765 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
766 "abcdefghijklmnopqrstuvwxyz"
770 static const char do_not_escape_when_encoding_URI_component[] =
771 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
772 "abcdefghijklmnopqrstuvwxyz"
775 static const char do_not_escape_when_encoding_URI[] =
776 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
777 "abcdefghijklmnopqrstuvwxyz"
779 "!#$&'()*+,-./:;=?@_~";
780 static const char do_not_unescape_when_decoding_URI[] =
784 case Eval: { // eval()
785 JSValue *x = args[0];
789 UString s = x->toString(exec);
794 RefPtr<ProgramNode> progNode(Parser::parse(UString(), 0, s.data(),s.size(),&sid,&errLine,&errMsg));
796 Debugger *dbg = exec->dynamicInterpreter()->debugger();
798 bool cont = dbg->sourceParsed(exec, sid, UString(), s, 0, errLine, errMsg);
800 return jsUndefined();
803 // no program node means a syntax occurred
805 return throwError(exec, SyntaxError, errMsg, errLine, sid, NULL);
807 // enter a new execution context
808 JSObject *thisVal = static_cast<JSObject *>(exec->context()->thisValue());
809 Context ctx(exec->dynamicInterpreter()->globalObject(),
810 exec->dynamicInterpreter(),
816 ExecState newExec(exec->dynamicInterpreter(), &ctx);
817 if (exec->hadException())
818 newExec.setException(exec->exception());
821 progNode->processVarDecls(&newExec);
822 Completion c = progNode->execute(&newExec);
824 // if an exception occured, propogate it back to the previous execution object
825 if (newExec.hadException())
826 exec->setException(newExec.exception());
829 if (c.complType() == Throw)
830 exec->setException(c.value());
831 else if (c.isValueCompletion())
837 res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
840 res = jsNumber(parseFloat(args[0]->toString(exec)));
843 res = jsBoolean(isNaN(args[0]->toNumber(exec)));
846 double n = args[0]->toNumber(exec);
847 res = jsBoolean(!isNaN(n) && !isInf(n));
851 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
853 case DecodeURIComponent:
854 res = decode(exec, args, "", true);
857 res = encode(exec, args, do_not_escape_when_encoding_URI);
859 case EncodeURIComponent:
860 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
864 UString r = "", s, str = args[0]->toString(exec);
865 const UChar *c = str.data();
866 for (int k = 0; k < str.size(); k++, c++) {
870 sprintf(tmp, "%%u%04X", u);
872 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
876 sprintf(tmp, "%%%02X", u);
886 UString s = "", str = args[0]->toString(exec);
887 int k = 0, len = str.size();
889 const UChar *c = str.data() + k;
891 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
892 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
893 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
894 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
895 (c+4)->uc, (c+5)->uc);
899 } else if (*c == UChar('%') && k <= len - 3 &&
900 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
901 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
913 puts(args[0]->toString(exec).ascii());