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)
154 parameters.append(Parameter(n));
157 UString FunctionImp::parameterString() const
161 Vector<Parameter>::const_iterator end = parameters.end();
162 for(Vector<Parameter>::const_iterator it = parameters.begin(); it < end; it++) {
165 s += it->name.ustring();
173 void FunctionImp::processParameters(ExecState *exec, const List &args)
175 JSObject* variable = exec->context()->variableObject();
178 fprintf(stderr, "---------------------------------------------------\n"
179 "processing parameters for %s call\n",
180 name().isEmpty() ? "(internal)" : name().ascii());
183 ListIterator it = args.begin();
186 Vector<Parameter>::const_iterator end = parameters.end();
187 for(Vector<Parameter>::iterator pit = parameters.begin(); pit < end; pit++) {
188 if (it != args.end()) {
190 fprintf(stderr, "setting parameter %s ", p->name.ascii());
191 printInfo(exec,"to", *it);
193 variable->put(exec, pit->name, v);
196 variable->put(exec, pit->name, jsUndefined());
200 for (int i = 0; i < args.size(); i++)
201 printInfo(exec,"setting argument", args[i]);
206 void FunctionImp::processVarDecls(ExecState */*exec*/)
210 JSValue *FunctionImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
212 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
213 Context *context = exec->m_context;
215 if (context->function() == thisObj) {
216 return static_cast<ActivationImp *>(context->activationObject())->get(exec, propertyName);
218 context = context->callingContext();
223 JSValue *FunctionImp::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
225 FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
226 return jsNumber(thisObj->parameters.size());
229 bool FunctionImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
231 // Find the arguments from the closest context.
232 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
233 slot.setCustom(this, argumentsGetter);
237 // Compute length of parameters.
238 if (propertyName == lengthPropertyName) {
239 slot.setCustom(this, lengthGetter);
243 return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
246 void FunctionImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
248 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier() || propertyName == lengthPropertyName)
250 InternalFunctionImp::put(exec, propertyName, value, attr);
253 bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
255 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier() || propertyName == lengthPropertyName)
257 return InternalFunctionImp::deleteProperty(exec, propertyName);
260 /* Returns the parameter name corresponding to the given index. eg:
261 * function f1(x, y, z): getParameterName(0) --> x
263 * If a name appears more than once, only the last index at which
264 * it appears associates with it. eg:
265 * function f2(x, x): getParameterName(0) --> null
267 Identifier FunctionImp::getParameterName(int index)
269 if (static_cast<size_t>(index) > parameters.size())
270 return Identifier::null();
272 Identifier name = parameters[index].name;
274 // Are there any subsequent parameters with the same name?
275 Vector<Parameter>::const_iterator end = parameters.end();
276 for (Vector<Parameter>::iterator it = &(parameters[index+1]); it < end; it++)
277 if (it->name == name)
278 return Identifier::null();
283 // ------------------------------ DeclaredFunctionImp --------------------------
285 // ### is "Function" correct here?
286 const ClassInfo DeclaredFunctionImp::info = {"Function", &FunctionImp::info, 0, 0};
288 DeclaredFunctionImp::DeclaredFunctionImp(ExecState *exec, const Identifier &n,
289 FunctionBodyNode *b, const ScopeChain &sc)
290 : FunctionImp(exec, n, b)
295 bool DeclaredFunctionImp::implementsConstruct() const
300 // ECMA 13.2.2 [[Construct]]
301 JSObject *DeclaredFunctionImp::construct(ExecState *exec, const List &args)
304 JSValue *p = get(exec,prototypePropertyName);
306 proto = static_cast<JSObject*>(p);
308 proto = exec->lexicalInterpreter()->builtinObjectPrototype();
310 JSObject *obj(new JSObject(proto));
312 JSValue *res = call(exec,obj,args);
315 return static_cast<JSObject *>(res);
320 Completion DeclaredFunctionImp::execute(ExecState *exec)
322 Completion result = body->execute(exec);
324 if (result.complType() == Throw || result.complType() == ReturnValue)
326 return Completion(Normal, jsUndefined()); // TODO: or ReturnValue ?
329 void DeclaredFunctionImp::processVarDecls(ExecState *exec)
331 body->processVarDecls(exec);
334 // ------------------------------ IndexToNameMap ---------------------------------
336 // We map indexes in the arguments array to their corresponding argument names.
337 // Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
339 // Once we have an argument name, we can get and set the argument's value in the
340 // activation object.
342 // We use Identifier::null to indicate that a given argument's value
343 // isn't stored in the activation object.
345 IndexToNameMap::IndexToNameMap(FunctionImp *func, const List &args)
347 _map = new Identifier[args.size()];
348 this->size = args.size();
351 ListIterator iterator = args.begin();
352 for (; iterator != args.end(); i++, iterator++)
353 _map[i] = func->getParameterName(i); // null if there is no corresponding parameter
356 IndexToNameMap::~IndexToNameMap() {
360 bool IndexToNameMap::isMapped(const Identifier &index) const
363 int indexAsNumber = index.toUInt32(&indexIsNumber);
368 if (indexAsNumber >= size)
371 if (_map[indexAsNumber].isNull())
377 void IndexToNameMap::unMap(const Identifier &index)
380 int indexAsNumber = index.toUInt32(&indexIsNumber);
382 assert(indexIsNumber && indexAsNumber < size);
384 _map[indexAsNumber] = Identifier::null();
387 Identifier& IndexToNameMap::operator[](int index)
392 Identifier& IndexToNameMap::operator[](const Identifier &index)
395 int indexAsNumber = index.toUInt32(&indexIsNumber);
397 assert(indexIsNumber && indexAsNumber < size);
399 return (*this)[indexAsNumber];
402 // ------------------------------ Arguments ---------------------------------
404 const ClassInfo Arguments::info = {"Arguments", 0, 0, 0};
407 Arguments::Arguments(ExecState *exec, FunctionImp *func, const List &args, ActivationImp *act)
408 : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()),
409 _activationObject(act),
410 indexToNameMap(func, args)
412 putDirect(calleePropertyName, func, DontEnum);
413 putDirect(lengthPropertyName, args.size(), DontEnum);
416 ListIterator iterator = args.begin();
417 for (; iterator != args.end(); i++, iterator++) {
418 if (!indexToNameMap.isMapped(Identifier::from(i))) {
419 JSObject::put(exec, Identifier::from(i), *iterator, DontEnum);
424 void Arguments::mark()
427 if (_activationObject && !_activationObject->marked())
428 _activationObject->mark();
431 JSValue *Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
433 Arguments *thisObj = static_cast<Arguments *>(slot.slotBase());
434 return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
437 bool Arguments::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
439 if (indexToNameMap.isMapped(propertyName)) {
440 slot.setCustom(this, mappedIndexGetter);
444 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
447 void Arguments::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
449 if (indexToNameMap.isMapped(propertyName)) {
450 _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
452 JSObject::put(exec, propertyName, value, attr);
456 bool Arguments::deleteProperty(ExecState *exec, const Identifier &propertyName)
458 if (indexToNameMap.isMapped(propertyName)) {
459 indexToNameMap.unMap(propertyName);
462 return JSObject::deleteProperty(exec, propertyName);
466 // ------------------------------ ActivationImp --------------------------------
468 const ClassInfo ActivationImp::info = {"Activation", 0, 0, 0};
471 ActivationImp::ActivationImp(FunctionImp *function, const List &arguments)
472 : _function(function), _arguments(true), _argumentsObject(0)
474 _arguments.copyFrom(arguments);
475 // FIXME: Do we need to support enumerating the arguments property?
478 JSValue *ActivationImp::argumentsGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
480 ActivationImp *thisObj = static_cast<ActivationImp *>(slot.slotBase());
482 // default: return builtin arguments array
483 if (!thisObj->_argumentsObject)
484 thisObj->createArgumentsObject(exec);
486 return thisObj->_argumentsObject;
489 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
491 return ActivationImp::argumentsGetter;
494 bool ActivationImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
496 // do this first so property map arguments property wins over the below
497 // we don't call JSObject because we won't have getter/setter properties
498 // and we don't want to support __proto__
500 if (JSValue **location = getDirectLocation(propertyName)) {
501 slot.setValueSlot(this, location);
505 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier()) {
506 slot.setCustom(this, getArgumentsGetter());
513 bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
515 if (propertyName == exec->dynamicInterpreter()->argumentsIdentifier())
517 return JSObject::deleteProperty(exec, propertyName);
520 void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* value, int attr)
522 // There's no way that an activation object can have a prototype or getter/setter properties
523 assert(!_prop.hasGetterSetterProperties());
524 assert(prototype() == jsNull());
526 _prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));
529 void ActivationImp::mark()
531 if (_function && !_function->marked())
534 if (_argumentsObject && !_argumentsObject->marked())
535 _argumentsObject->mark();
539 void ActivationImp::createArgumentsObject(ExecState *exec) const
541 _argumentsObject = new Arguments(exec, _function, _arguments, const_cast<ActivationImp *>(this));
544 // ------------------------------ GlobalFunc -----------------------------------
547 GlobalFuncImp::GlobalFuncImp(ExecState*, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
548 : InternalFunctionImp(funcProto, name)
551 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
554 CodeType GlobalFuncImp::codeType() const
556 return id == Eval ? EvalCode : codeType();
559 static JSValue *encode(ExecState *exec, const List &args, const char *do_not_escape)
561 UString r = "", s, str = args[0]->toString(exec);
562 CString cstr = str.UTF8String();
563 const char *p = cstr.c_str();
564 for (size_t k = 0; k < cstr.size(); k++, p++) {
566 if (c && strchr(do_not_escape, c)) {
570 sprintf(tmp, "%%%02X", (unsigned char)c);
577 static JSValue *decode(ExecState *exec, const List &args, const char *do_not_unescape, bool strict)
579 UString s = "", str = args[0]->toString(exec);
580 int k = 0, len = str.size();
581 const UChar *d = str.data();
584 const UChar *p = d + k;
588 if (k <= len - 3 && isxdigit(p[1].uc) && isxdigit(p[2].uc)) {
589 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
590 const int sequenceLen = UTF8SequenceLength(b0);
591 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
592 charLen = sequenceLen * 3;
595 for (int i = 1; i < sequenceLen; ++i) {
596 const UChar *q = p + i * 3;
597 if (q[0] == '%' && isxdigit(q[1].uc) && isxdigit(q[2].uc))
598 sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
605 sequence[sequenceLen] = 0;
606 const int character = decodeUTF8Sequence(sequence);
607 if (character < 0 || character >= 0x110000) {
609 } else if (character >= 0x10000) {
610 // Convert to surrogate pair.
611 s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
612 u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
614 u = static_cast<unsigned short>(character);
621 return throwError(exec, URIError);
622 // The only case where we don't use "strict" mode is the "unescape" function.
623 // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE.
624 if (k <= len - 6 && p[1] == 'u'
625 && isxdigit(p[2].uc) && isxdigit(p[3].uc)
626 && isxdigit(p[4].uc) && isxdigit(p[5].uc)) {
628 u = Lexer::convertUnicode(p[2].uc, p[3].uc, p[4].uc, p[5].uc);
631 if (charLen && (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low()))) {
642 static bool isStrWhiteSpace(unsigned short c)
656 return WTF::Unicode::isSeparatorSpace(c);
660 static int parseDigit(unsigned short c, int radix)
664 if (c >= '0' && c <= '9') {
666 } else if (c >= 'A' && c <= 'Z') {
667 digit = c - 'A' + 10;
668 } else if (c >= 'a' && c <= 'z') {
669 digit = c - 'a' + 10;
677 static double parseInt(const UString &s, int radix)
679 int length = s.size();
682 while (p < length && isStrWhiteSpace(s[p].uc)) {
690 } else if (s[p] == '-') {
696 if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
699 } else if (radix == 0) {
700 if (p < length && s[p] == '0')
706 if (radix < 2 || radix > 36)
709 bool sawDigit = false;
712 int digit = parseDigit(s[p].uc, radix);
724 return sign * number;
727 static double parseFloat(const UString &s)
729 // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
730 // Need to skip any whitespace and then one + or - sign.
731 int length = s.size();
733 while (p < length && isStrWhiteSpace(s[p].uc)) {
736 if (p < length && (s[p] == '+' || s[p] == '-')) {
739 if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
743 return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
746 JSValue *GlobalFuncImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
748 JSValue *res = jsUndefined();
750 static const char do_not_escape[] =
751 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
752 "abcdefghijklmnopqrstuvwxyz"
756 static const char do_not_escape_when_encoding_URI_component[] =
757 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
758 "abcdefghijklmnopqrstuvwxyz"
761 static const char do_not_escape_when_encoding_URI[] =
762 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
763 "abcdefghijklmnopqrstuvwxyz"
765 "!#$&'()*+,-./:;=?@_~";
766 static const char do_not_unescape_when_decoding_URI[] =
770 case Eval: { // eval()
771 JSValue *x = args[0];
775 UString s = x->toString(exec);
780 RefPtr<ProgramNode> progNode(Parser::parse(UString(), 0, s.data(),s.size(),&sid,&errLine,&errMsg));
782 Debugger *dbg = exec->dynamicInterpreter()->debugger();
784 bool cont = dbg->sourceParsed(exec, sid, UString(), s, 0, errLine, errMsg);
786 return jsUndefined();
789 // no program node means a syntax occurred
791 return throwError(exec, SyntaxError, errMsg, errLine, sid, NULL);
793 // enter a new execution context
794 JSObject *thisVal = static_cast<JSObject *>(exec->context()->thisValue());
795 Context ctx(exec->dynamicInterpreter()->globalObject(),
796 exec->dynamicInterpreter(),
801 ExecState newExec(exec->dynamicInterpreter(), &ctx);
802 if (exec->hadException())
803 newExec.setException(exec->exception());
804 ctx.setExecState(&newExec);
807 progNode->processVarDecls(&newExec);
808 Completion c = progNode->execute(&newExec);
810 // if an exception occured, propogate it back to the previous execution object
811 if (newExec.hadException())
812 exec->setException(newExec.exception());
815 if (c.complType() == Throw)
816 exec->setException(c.value());
817 else if (c.isValueCompletion())
823 res = jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
826 res = jsNumber(parseFloat(args[0]->toString(exec)));
829 res = jsBoolean(isNaN(args[0]->toNumber(exec)));
832 double n = args[0]->toNumber(exec);
833 res = jsBoolean(!isNaN(n) && !isInf(n));
837 res = decode(exec, args, do_not_unescape_when_decoding_URI, true);
839 case DecodeURIComponent:
840 res = decode(exec, args, "", true);
843 res = encode(exec, args, do_not_escape_when_encoding_URI);
845 case EncodeURIComponent:
846 res = encode(exec, args, do_not_escape_when_encoding_URI_component);
850 UString r = "", s, str = args[0]->toString(exec);
851 const UChar *c = str.data();
852 for (int k = 0; k < str.size(); k++, c++) {
856 sprintf(tmp, "%%u%04X", u);
858 } else if (u != 0 && strchr(do_not_escape, (char)u)) {
862 sprintf(tmp, "%%%02X", u);
872 UString s = "", str = args[0]->toString(exec);
873 int k = 0, len = str.size();
875 const UChar *c = str.data() + k;
877 if (*c == UChar('%') && k <= len - 6 && *(c+1) == UChar('u')) {
878 if (Lexer::isHexDigit((c+2)->uc) && Lexer::isHexDigit((c+3)->uc) &&
879 Lexer::isHexDigit((c+4)->uc) && Lexer::isHexDigit((c+5)->uc)) {
880 u = Lexer::convertUnicode((c+2)->uc, (c+3)->uc,
881 (c+4)->uc, (c+5)->uc);
885 } else if (*c == UChar('%') && k <= len - 3 &&
886 Lexer::isHexDigit((c+1)->uc) && Lexer::isHexDigit((c+2)->uc)) {
887 u = UChar(Lexer::convertHex((c+1)->uc, (c+2)->uc));
899 puts(args[0]->toString(exec).ascii());
907 UString escapeStringForPrettyPrinting(const UString& s)
909 UString escapedString;
911 for (int i = 0; i < s.size(); i++) {
912 unsigned short c = s.data()[i].unicode();
916 escapedString += "\\\"";
919 escapedString += "\\n";
922 escapedString += "\\r";
925 escapedString += "\\t";
928 escapedString += "\\\\";
931 if (c < 128 && WTF::Unicode::isPrintableChar(c))
932 escapedString.append(c);
937 _snprintf(hexValue, 7, "\\u%04x", c);
939 snprintf(hexValue, 7, "\\u%04x", c);
941 escapedString += hexValue;
946 return escapedString;