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};
54 Parameter(const Identifier &n) : name(n) { }
58 FunctionImp::FunctionImp(ExecState *exec, const Identifier &n, FunctionBodyNode* b)
59 : InternalFunctionImp(static_cast<FunctionPrototype*>
60 (exec->lexicalInterpreter()->builtinFunctionPrototype()), n)
65 void FunctionImp::mark()
67 InternalFunctionImp::mark();
71 FunctionImp::~FunctionImp()
75 JSValue *FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
77 JSObject *globalObj = exec->dynamicInterpreter()->globalObject();
79 // enter a new execution context
80 Context ctx(globalObj, exec->dynamicInterpreter(), thisObj, body.get(),
81 codeType(), exec->context(), this, &args);
82 ExecState newExec(exec->dynamicInterpreter(), &ctx);
83 if (exec->hadException())
84 newExec.setException(exec->exception());
85 ctx.setExecState(&newExec);
87 // assign user supplied arguments to parameters
88 processParameters(&newExec, args);
89 // add variable declarations (initialized to undefined)
90 processVarDecls(&newExec);
92 Debugger *dbg = exec->dynamicInterpreter()->debugger();
96 if (inherits(&DeclaredFunctionImp::info)) {
97 sid = static_cast<DeclaredFunctionImp*>(this)->body->sourceId();
98 lineno = static_cast<DeclaredFunctionImp*>(this)->body->firstLine();
101 bool cont = dbg->callEvent(&newExec,sid,lineno,this,args);
104 return jsUndefined();
108 Completion comp = execute(&newExec);
110 // if an exception occured, propogate it back to the previous execution object
111 if (newExec.hadException())
112 comp = Completion(Throw, newExec.exception());
115 if (comp.complType() == Throw)
116 printInfo(exec,"throwing", comp.value());
117 else if (comp.complType() == ReturnValue)
118 printInfo(exec,"returning", comp.value());
120 fprintf(stderr, "returning: undefined\n");
123 // The debugger may have been deallocated by now if the WebFrame
124 // we were running in has been destroyed, so refetch it.
125 // See http://bugzilla.opendarwin.org/show_bug.cgi?id=9477
126 dbg = exec->dynamicInterpreter()->debugger();
129 if (inherits(&DeclaredFunctionImp::info))
130 lineno = static_cast<DeclaredFunctionImp*>(this)->body->lastLine();
132 if (comp.complType() == Throw)
133 newExec.setException(comp.value());
135 int cont = dbg->returnEvent(&newExec,sid,lineno,this);
138 return jsUndefined();
142 if (comp.complType() == Throw) {
143 exec->setException(comp.value());
146 else if (comp.complType() == ReturnValue)
149 return jsUndefined();
152 void FunctionImp::addParameter(const Identifier &n)
155 parameters.set(new Vector<Parameter>);
157 parameters->append(Parameter(n));
160 UString FunctionImp::parameterString() const
167 for (size_t i = 0; i < parameters->size(); ++i) {
170 s += parameters->at(i).name.ustring();
178 void FunctionImp::processParameters(ExecState *exec, const List &args)
183 JSObject* variable = exec->context()->variableObject();
186 fprintf(stderr, "---------------------------------------------------\n"
187 "processing parameters for %s call\n",
188 name().isEmpty() ? "(internal)" : name().ascii());
191 ListIterator it = args.begin();
194 for (size_t i = 0; i < parameters->size(); ++i) {
195 if (it != args.end()) {
197 fprintf(stderr, "setting parameter %s ", parameters->at(i).name.ascii());
198 printInfo(exec, "to", *it);
200 variable->put(exec, parameters->at(i).name, v);
203 variable->put(exec, parameters->at(i).name, jsUndefined());
207 for (int i = 0; i < args.size(); ++i)
208 printInfo(exec,"setting argument", args[i]);
213 void FunctionImp::processVarDecls(ExecState*)
217 JSValue *FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
219 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
220 Context *context = exec->m_context;
222 if (context->function() == thisObj) {
223 return static_cast<ActivationImp *>(context->activationObject())->get(exec, propertyName);
225 context = context->callingContext();
230 JSValue *FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
232 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
233 return jsNumber(thisObj->parameters ? thisObj->parameters->size() : 0);
236 bool FunctionImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
238 // Find the arguments from the closest context.
239 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
240 slot.setCustom(this, argumentsGetter);
244 // Compute length of parameters.
245 if (propertyName == lengthPropertyName) {
246 slot.setCustom(this, lengthGetter);
250 return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
253 void FunctionImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
255 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier() || propertyName == lengthPropertyName)
257 InternalFunctionImp::put(exec, propertyName, value, attr);
260 bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
262 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier() || propertyName == lengthPropertyName)
264 return InternalFunctionImp::deleteProperty(exec, propertyName);
267 /* Returns the parameter name corresponding to the given index. eg:
268 * function f1(x, y, z): getParameterName(0) --> x
270 * If a name appears more than once, only the last index at which
271 * it appears associates with it. eg:
272 * function f2(x, x): getParameterName(0) --> null
274 Identifier FunctionImp::getParameterName(int index)
277 return Identifier::null();
279 if (static_cast<size_t>(index) >= parameters->size())
280 return Identifier::null();
282 Identifier name = parameters->at(index).name;
284 // Are there any subsequent parameters with the same name?
285 for (size_t i = index + 1; i < parameters->size(); ++i)
286 if (parameters->at(i).name == name)
287 return Identifier::null();
292 // ------------------------------ DeclaredFunctionImp --------------------------
294 // ### is "Function" correct here?
295 const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
297 DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
298 FunctionBodyNode *b, const ScopeChain &sc)
299 : FunctionImp(exec, n, b)
304 bool DeclaredFunctionImp::implementsConstruct() const
309 // ECMA 13.2.2 [[Construct]]
310 JSObject *DeclaredFunctionImp::construct(ExecState *exec, const List &args)
313 JSValue *p = get(exec,prototypePropertyName);
315 proto = static_cast<JSObject*>(p);
317 proto = exec->lexicalInterpreter()->builtinObjectPrototype();
319 JSObject *obj(new JSObject(proto));
321 JSValue *res = call(exec,obj,args);
324 return static_cast<JSObject *>(res);
329 Completion DeclaredFunctionImp::execute(ExecState *exec)
331 Completion result = body->execute(exec);
333 if (result.complType() == Throw || result.complType() == ReturnValue)
335 return Completion(Normal, jsUndefined()); // TODO: or ReturnValue ?
338 void DeclaredFunctionImp::processVarDecls(ExecState *exec)
340 body->processVarDecls(exec);
343 // ------------------------------ IndexToNameMap ---------------------------------
345 // We map indexes in the arguments array to their corresponding argument names.
346 // Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
348 // Once we have an argument name, we can get and set the argument's value in the
349 // activation object.
351 // We use Identifier::null to indicate that a given argument's value
352 // isn't stored in the activation object.
354 IndexToNameMap::IndexToNameMap(FunctionImp *func, const List &args)
356 _map = new Identifier[args.size()];
357 this->size = args.size();
360 ListIterator iterator = args.begin();
361 for (; iterator != args.end(); i++, iterator++)
362 _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
365 IndexToNameMap::~IndexToNameMap() {
369 bool IndexToNameMap::isMapped(const Identifier &index) const
372 int indexAsNumber = index.toUInt32(&indexIsNumber);
377 if (indexAsNumber >= size)
380 if (_map[indexAsNumber].isNull())
386 void IndexToNameMap::unMap(const Identifier &index)
389 int indexAsNumber = index.toUInt32(&indexIsNumber);
391 assert(indexIsNumber && indexAsNumber < size);
393 _map[indexAsNumber] = Identifier::null();
396 Identifier& IndexToNameMap::operator[](int index)
401 Identifier& IndexToNameMap::operator[](const Identifier &index)
404 int indexAsNumber = index.toUInt32(&indexIsNumber);
406 assert(indexIsNumber && indexAsNumber < size);
408 return (*this)[indexAsNumber];
411 // ------------------------------ Arguments ---------------------------------
413 const ClassInfo Arguments::info = {"Arguments", 0, 0, 0};
416 Arguments::Arguments(ExecState *exec, FunctionImp *func, const List &args, ActivationImp *act)
417 : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()),
418 _activationObject(act),
419 indexToNameMap(func, args)
421 putDirect(calleePropertyName, func, DontEnum);
422 putDirect(lengthPropertyName, args.size(), DontEnum);
425 ListIterator iterator = args.begin();
426 for (; iterator != args.end(); i++, iterator++) {
427 if (!indexToNameMap.isMapped(Identifier::from(i))) {
428 JSObject::put(exec, Identifier::from(i), *iterator, DontEnum);
433 void Arguments::mark()
436 if (_activationObject && !_activationObject->marked())
437 _activationObject->mark();
440 JSValue *Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
442 Arguments *thisObj = static_cast<Arguments *>(slot.slotBase());
443 return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
446 bool Arguments::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
448 if (indexToNameMap.isMapped(propertyName)) {
449 slot.setCustom(this, mappedIndexGetter);
453 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
456 void Arguments::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
458 if (indexToNameMap.isMapped(propertyName)) {
459 _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
461 JSObject::put(exec, propertyName, value, attr);
465 bool Arguments::deleteProperty(ExecState *exec, const Identifier &propertyName)
467 if (indexToNameMap.isMapped(propertyName)) {
468 indexToNameMap.unMap(propertyName);
471 return JSObject::deleteProperty(exec, propertyName);
475 // ------------------------------ ActivationImp --------------------------------
477 const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
480 ActivationImp::ActivationImp(FunctionImp *function, const List &arguments)
481 : _function(function), _arguments(true), _argumentsObject(0)
483 _arguments.copyFrom(arguments);
484 // FIXME: Do we need to support enumerating the arguments property?
487 JSValue *ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
489 ActivationImp *thisObj = static_cast<ActivationImp *>(slot.slotBase());
491 // default: return builtin arguments array
492 if (!thisObj->_argumentsObject)
493 thisObj->createArgumentsObject(exec);
495 return thisObj->_argumentsObject;
498 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
500 return ActivationImp::argumentsGetter;
503 bool ActivationImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
505 // do this first so property map arguments property wins over the below
506 // we don't call JSObject because we won't have getter/setter properties
507 // and we don't want to support __proto__
509 if (JSValue **location = getDirectLocation(propertyName)) {
510 slot.setValueSlot(this, location);
514 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
515 slot.setCustom(this, getArgumentsGetter());
522 bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
524 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier())
526 return JSObject::deleteProperty(exec, propertyName);
529 void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
531 // There's no way that an activation object can have a prototype or getter/setter properties
532 assert(!_prop.hasGetterSetterProperties());
533 assert(prototype() == jsNull());
535 _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
538 void ActivationImp::mark()
540 if (_function && !_function->marked())
543 if (_argumentsObject && !_argumentsObject->marked())
544 _argumentsObject->mark();
548 void ActivationImp::createArgumentsObject(ExecState *exec) const
550 _argumentsObject = new Arguments(exec, _function, _arguments, const_cast<ActivationImp *>(this));
553 // ------------------------------ GlobalFunc -----------------------------------
556 GlobalFuncImp::GlobalFuncImp(ExecState*, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
557 : InternalFunctionImp(funcProto, name)
560 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
563 CodeType GlobalFuncImp::codeType() const
565 return id == Eval ? EvalCode : codeType();
568 static JSValue *encode(ExecState *exec, const List &args, const char *do_not_escape)
570 UString r = "", s, str = args[0]->toString(exec);
571 CString cstr = str.UTF8String();
572 const char *p = cstr.c_str();
573 for (size_t k = 0; k < cstr.size(); k++, p++) {
575 if (c && strchr(do_not_escape, c)) {
579 sprintf(tmp, "%%%02X", (unsigned char)c);
586 static JSValue *decode(ExecState *exec, const List &args, const char *do_not_unescape, bool strict)
588 UString s = "", str = args[0]->toString(exec);
589 int k = 0, len = str.size();
590 const UChar *d = str.data();
593 const UChar *p = d + k;
597 if (k <= len - 3 && isxdigit(p[1].uc) && isxdigit(p[2].uc)) {
598 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
599 const int sequenceLen = UTF8SequenceLength(b0);
600 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
601 charLen = sequenceLen * 3;
604 for (int i = 1; i < sequenceLen; ++i) {
605 const UChar *q = p + i * 3;
606 if (q[0] == '%' && isxdigit(q[1].uc) && isxdigit(q[2].uc))
607 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
614 sequence[sequenceLen] = 0;
615 const int character = decodeUTF8Sequence(sequence);
616 if (character < 0 || character >= 0x110000) {
618 } else if (character >= 0x10000) {
619 // Convert to surrogate pair.
620 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
621 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
623 u = static_cast<unsigned short>(character);
630 return throwError(exec, URIError);
631 // The only case where we don't use "strict" mode is the "unescape" function.
632 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
633 if (k <= len - 6 && p[1] == 'u'
634 && isxdigit(p[2].uc) && isxdigit(p[3].uc)
635 && isxdigit(p[4].uc) && isxdigit(p[5].uc)) {
637 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
640 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
651 static bool isStrWhiteSpace(unsigned short c)
665 return WTF::Unicode::isSeparatorSpace(c);
669 static int parseDigit(unsigned short c, int radix)
673 if (c >= '0' && c <= '9') {
675 } else if (c >= 'A' && c <= 'Z') {
676 digit = c - 'A' + 10;
677 } else if (c >= 'a' && c <= 'z') {
678 digit = c - 'a' + 10;
686 static double parseInt(const UString &s, int radix)
688 int length = s.size();
691 while (p < length && isStrWhiteSpace(s[p].uc)) {
699 } else if (s[p] == '-') {
705 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
708 } else if (radix == 0) {
709 if (p < length && s[p] == '0')
715 if (radix < 2 || radix > 36)
718 bool sawDigit = false;
721 int digit = parseDigit(s[p].uc, radix);
733 return sign * number;
736 static double parseFloat(const UString &s)
738 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
739 // Need to skip any whitespace and then one + or - sign.
740 int length = s.size();
742 while (p < length && isStrWhiteSpace(s[p].uc)) {
745 if (p < length && (s[p] == '+' || s[p] == '-')) {
748 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
752 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
755 JSValue *GlobalFuncImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args)
757 JSValue *res = jsUndefined();
759 static const char do_not_escape[] =
760 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
761 "abcdefghijklmnopqrstuvwxyz"
765 static const char do_not_escape_when_encoding_URI_component[] =
766 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
767 "abcdefghijklmnopqrstuvwxyz"
770 static const char do_not_escape_when_encoding_URI[] =
771 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
772 "abcdefghijklmnopqrstuvwxyz"
774 "!#$&'()*+,-./:;=?@_~";
775 static const char do_not_unescape_when_decoding_URI[] =
779 case Eval: { // eval()
780 JSValue *x = args[0];
784 UString s = x->toString(exec);
789 RefPtr<ProgramNode> progNode(Parser::parse(UString(), 0, s.data(),s.size(),&sid,&errLine,&errMsg));
791 Debugger *dbg = exec->dynamicInterpreter()->debugger();
793 bool cont = dbg->sourceParsed(exec, sid, UString(), s, 0, errLine, errMsg);
795 return jsUndefined();
798 // no program node means a syntax occurred
800 return throwError(exec, SyntaxError, errMsg, errLine, sid, NULL);
802 // enter a new execution context
803 JSObject *thisVal = static_cast<JSObject *>(exec->context()->thisValue());
804 Context ctx(exec->dynamicInterpreter()->globalObject(),
805 exec->dynamicInterpreter(),
810 ExecState newExec(exec->dynamicInterpreter(), &ctx);
811 if (exec->hadException())
812 newExec.setException(exec->exception());
813 ctx.setExecState(&newExec);
816 progNode->processVarDecls(&newExec);
817 Completion c = progNode->execute(&newExec);
819 // if an exception occured, propogate it back to the previous execution object
820 if (newExec.hadException())
821 exec->setException(newExec.exception());
824 if (c.complType() == Throw)
825 exec->setException(c.value());
826 else if (c.isValueCompletion())
832 res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
835 res = jsNumber(parseFloat(args[0]->toString(exec)));
838 res = jsBoolean(isNaN(args[0]->toNumber(exec)));
841 double n = args[0]->toNumber(exec);
842 res = jsBoolean(!isNaN(n) && !isInf(n));
846 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
848 case DecodeURIComponent:
849 res = decode(exec, args, "", true);
852 res = encode(exec, args, do_not_escape_when_encoding_URI);
854 case EncodeURIComponent:
855 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
859 UString r = "", s, str = args[0]->toString(exec);
860 const UChar *c = str.data();
861 for (int k = 0; k < str.size(); k++, c++) {
865 sprintf(tmp, "%%u%04X", u);
867 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
871 sprintf(tmp, "%%%02X", u);
881 UString s = "", str = args[0]->toString(exec);
882 int k = 0, len = str.size();
884 const UChar *c = str.data() + k;
886 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
887 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
888 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
889 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
890 (c+4)->uc, (c+5)->uc);
894 } else if (*c == UChar('%') && k <= len - 3 &&
895 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
896 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
908 puts(args[0]->toString(exec).ascii());
916 UString escapeStringForPrettyPrinting(const UString& s)
918 UString escapedString;
920 for (int i = 0; i < s.size(); i++) {
921 unsigned short c = s.data()[i].unicode();
925 escapedString += "\\\"";
928 escapedString += "\\n";
931 escapedString += "\\r";
934 escapedString += "\\t";
937 escapedString += "\\\\";
940 if (c < 128 && WTF::Unicode::isPrintableChar(c))
941 escapedString.append(c);
946 _snprintf(hexValue, 7, "\\u%04x", c);
948 snprintf(hexValue, 7, "\\u%04x", c);
950 escapedString += hexValue;
955 return escapedString;