File indexing completed on 2024-05-12 15:43:20

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
0004  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
0005  *  Copyright (C) 2003 Apple Computer, Inc.
0006  *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
0007  *  Copyright (C) 2007 Maksim Orlovich (maksim@kde.org)
0008  *
0009  *  This library is free software; you can redistribute it and/or
0010  *  modify it under the terms of the GNU Library General Public
0011  *  License as published by the Free Software Foundation; either
0012  *  version 2 of the License, or (at your option) any later version.
0013  *
0014  *  This library is distributed in the hope that it will be useful,
0015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  *  Library General Public License for more details.
0018  *
0019  *  You should have received a copy of the GNU Library General Public License
0020  *  along with this library; see the file COPYING.LIB.  If not, write to
0021  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022  *  Boston, MA 02110-1301, USA.
0023  *
0024  */
0025 
0026 #include "function.h"
0027 #include "scriptfunction.h"
0028 #include "dtoa.h"
0029 #include "internal.h"
0030 #include "function_object.h"
0031 #include "lexer.h"
0032 #include "nodes.h"
0033 #include "operations.h"
0034 #include "debugger.h"
0035 #include "PropertyNameArray.h"
0036 #include "commonunicode.h"
0037 
0038 #include <stdio.h>
0039 #include <errno.h>
0040 #include <stdlib.h>
0041 #include <assert.h>
0042 #include <string.h>
0043 #include <string>
0044 #include "wtf/DisallowCType.h"
0045 #include "wtf/ASCIICType.h"
0046 #include "bytecode/machine.h"
0047 
0048 using namespace WTF;
0049 
0050 //#define KJS_VERBOSE
0051 
0052 namespace KJS
0053 {
0054 
0055 // ----------------------------- FunctionImp ----------------------------------
0056 
0057 const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, nullptr, nullptr};
0058 
0059 FunctionImp::FunctionImp(ExecState *exec, const Identifier &n, FunctionBodyNode *b, const ScopeChain &sc)
0060     : InternalFunctionImp(static_cast<FunctionPrototype *>
0061                           (exec->lexicalInterpreter()->builtinFunctionPrototype()), n)
0062     , body(b)
0063     , _scope(sc)
0064 {
0065 }
0066 
0067 void FunctionImp::mark()
0068 {
0069     InternalFunctionImp::mark();
0070     _scope.mark();
0071 }
0072 
0073 FunctionImp::~FunctionImp()
0074 {
0075 }
0076 
0077 void FunctionImp::initialCompile(ExecState *newExec)
0078 {
0079     FunctionBodyNode *body = this->body.get();
0080 
0081     // Reserve various slots needed for the activation object. We do it only once,
0082     // --- isCompiled() would return true even if debugging state changed
0083     body->reserveSlot(ActivationImp::LengthSlot, false);
0084     body->reserveSlot(ActivationImp::TearOffNeeded, false);
0085     body->reserveSlot(ActivationImp::ScopeLink, false /* will mark via ScopeChain::mark() */);
0086     body->reserveSlot(ActivationImp::FunctionSlot, true);
0087     body->reserveSlot(ActivationImp::ArgumentsObjectSlot, true);
0088 
0089     // Create declarations for parameters, and allocate the symbols.
0090     // We always just give them sequential positions, to make passInParameters
0091     // simple (though perhaps wasting memory in the trivial case)
0092     for (size_t i = 0; i < body->numParams(); ++i) {
0093         body->addSymbolOverwriteID(i + ActivationImp::NumReservedSlots, body->paramName(i), DontDelete);
0094     }
0095 
0096     body->processDecls(newExec);
0097     body->compile(FunctionCode, newExec->dynamicInterpreter()->debugger() ? Debug : Release);
0098 }
0099 
0100 #ifdef KJS_VERBOSE
0101 static int           callDepth;
0102 static std::string   callIndent;
0103 
0104 static const char *ind()
0105 {
0106     callIndent = "";
0107     for (int i = 0; i < callDepth; ++i) {
0108         callIndent += "     ";
0109     }
0110     return callIndent.c_str();
0111 }
0112 
0113 // Multiline print adding indentation
0114 static void printInd(const char *str)
0115 {
0116     fprintf(stderr, "%s", ind());
0117     for (const char *c = str; *c; ++c) {
0118         if (*c != '\n') {
0119             fprintf(stderr, "%c", *c);
0120         } else {
0121             fprintf(stderr, "\n%s", ind());
0122         }
0123     }
0124 }
0125 
0126 #endif
0127 
0128 JSValue *FunctionImp::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
0129 {
0130     assert(thisObj);
0131 
0132 #ifdef KJS_VERBOSE
0133     ++callDepth;
0134 #endif
0135 
0136     Debugger *dbg = exec->dynamicInterpreter()->debugger();
0137 
0138     // enter a new execution context
0139     FunctionExecState newExec(exec->dynamicInterpreter(), thisObj, body.get(), exec, this);
0140     if (exec->hadException()) {
0141         newExec.setException(exec->exception());
0142     }
0143 
0144     FunctionBodyNode *body = this->body.get();
0145 
0146     // The first time we're called, compute the set of local variables,
0147     // and compile the body. (note that parameters have been collected
0148     // during the AST build)
0149     CompileType currentState = body->compileState();
0150     if (currentState == NotCompiled) {
0151         initialCompile(&newExec);
0152     } else {
0153         // Otherwise, we may still need to recompile due to debug...
0154         CompileType desiredState = dbg ? Debug : Release;
0155         if (desiredState != currentState) {
0156             body->compile(FunctionCode, desiredState);
0157         }
0158     }
0159 
0160     size_t stackSize              = 0;
0161     LocalStorageEntry *stackSpace = nullptr;
0162 
0163     // We always allocate on stack initially, and tearoff only after we're done.
0164     int regs   = body->numLocalsAndRegisters();
0165     stackSize  = sizeof(LocalStorageEntry) * regs;
0166     stackSpace = (LocalStorageEntry *)exec->dynamicInterpreter()->stackAlloc(stackSize);
0167 
0168     ActivationImp *activation = static_cast<ActivationImp *>(newExec.activationObject());
0169     activation->setup(&newExec, this, &args, stackSpace);
0170     activation->tearOffNeededSlot() = body->tearOffAtEnd();
0171 
0172     newExec.initLocalStorage(stackSpace, regs);
0173 
0174     JSValue *result = Machine::runBlock(&newExec, body->code(), exec);
0175 
0176     // If we need to tear off now --- either due to static flag above, or
0177     // if execution requested it dynamically --- do so now.
0178     if (activation->tearOffNeededSlot()) {
0179         activation->performTearOff();
0180     } else {
0181         // Otherwise, we recycle the activation object; we must clear its
0182         // data pointer, though, since that may become dead.
0183         // (we also unlink it from the scope chain at this time)
0184         activation->scopeLink().deref();
0185         activation->localStorage = nullptr;
0186         exec->dynamicInterpreter()->recycleActivation(activation);
0187     }
0188 
0189     // Now free the stack space..
0190     exec->dynamicInterpreter()->stackFree(stackSize);
0191 
0192 #ifdef KJS_VERBOSE
0193     fprintf(stderr, "%s", ind());
0194     if (exec->exception()) {
0195         printInfo(exec, "throwing", exec->exception());
0196     } else {
0197         printInfo(exec, "returning", result);
0198     }
0199 
0200     --callDepth;
0201 #endif
0202 
0203     return result;
0204 }
0205 
0206 JSValue *FunctionImp::argumentsGetter(ExecState *exec, JSObject *, const Identifier &propertyName, const PropertySlot &slot)
0207 {
0208     FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
0209     ExecState *context = exec;
0210     while (context) {
0211         if (context->function() == thisObj) {
0212             return static_cast<ActivationImp *>(context->activationObject())->get(exec, propertyName);
0213         }
0214         context = context->callingExecState();
0215     }
0216     return jsNull();
0217 }
0218 
0219 JSValue *FunctionImp::callerGetter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot)
0220 {
0221     FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
0222     ExecState *context = exec;
0223     while (context) {
0224         if (context->function() == thisObj) {
0225             break;
0226         }
0227         context = context->callingExecState();
0228     }
0229 
0230     if (!context) {
0231         return jsNull();
0232     }
0233 
0234     ExecState *callingContext = context->callingExecState();
0235     if (!callingContext) {
0236         return jsNull();
0237     }
0238 
0239     FunctionImp *callingFunction = callingContext->function();
0240     if (!callingFunction) {
0241         return jsNull();
0242     }
0243 
0244     return callingFunction;
0245 }
0246 
0247 JSValue *FunctionImp::lengthGetter(ExecState *, JSObject *, const Identifier &, const PropertySlot &slot)
0248 {
0249     FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
0250     return jsNumber(thisObj->body->numParams());
0251 }
0252 
0253 JSValue *FunctionImp::nameGetter(ExecState *, JSObject *, const Identifier &, const PropertySlot &slot)
0254 {
0255     FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
0256     return jsString(thisObj->functionName().ustring());
0257 }
0258 
0259 bool FunctionImp::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0260 {
0261     // Find the arguments from the closest context.
0262     if (propertyName == exec->propertyNames().arguments) {
0263         slot.setCustom(this, argumentsGetter);
0264         return true;
0265     }
0266 
0267     // Compute length of parameters.
0268     if (propertyName == exec->propertyNames().length) {
0269         slot.setCustom(this, lengthGetter);
0270         return true;
0271     }
0272 
0273     // Calling function (Mozilla-extension)
0274     if (propertyName == exec->propertyNames().caller) {
0275         slot.setCustom(this, callerGetter);
0276         return true;
0277     }
0278 
0279     // Function name (Mozilla-extension)
0280     if (propertyName == exec->propertyNames().name) {
0281         slot.setCustom(this, nameGetter);
0282         return true;
0283     }
0284 
0285     return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
0286 }
0287 
0288 bool FunctionImp::getOwnPropertyDescriptor(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc)
0289 {
0290     if (propertyName == exec->propertyNames().length) {
0291         desc.setPropertyDescriptorValues(exec, jsNumber(body->numParams()), ReadOnly | DontDelete | DontEnum);
0292         return true;
0293     }
0294 
0295     return KJS::JSObject::getOwnPropertyDescriptor(exec, propertyName, desc);
0296 }
0297 
0298 void FunctionImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
0299 {
0300     if (propertyName == exec->propertyNames().arguments ||
0301             propertyName == exec->propertyNames().length ||
0302             propertyName == exec->propertyNames().name) {
0303         return;
0304     }
0305     InternalFunctionImp::put(exec, propertyName, value, attr);
0306 }
0307 
0308 bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
0309 {
0310     if (propertyName == exec->propertyNames().arguments ||
0311             propertyName == exec->propertyNames().length ||
0312             propertyName == exec->propertyNames().name) {
0313         return false;
0314     }
0315     return InternalFunctionImp::deleteProperty(exec, propertyName);
0316 }
0317 
0318 /* Returns the parameter name corresponding to the given index. eg:
0319  * function f1(x, y, z): getParameterName(0) --> x
0320  *
0321  * If a name appears more than once, only the last index at which
0322  * it appears associates with it. eg:
0323  * function f2(x, x): getParameterName(0) --> null
0324  */
0325 Identifier FunctionImp::getParameterName(size_t index)
0326 {
0327     if (index >= body->numParams()) {
0328         return CommonIdentifiers::shared()->nullIdentifier;
0329     }
0330 
0331     Identifier name = body->paramName(index);
0332 
0333     // Are there any subsequent parameters with the same name?
0334     for (size_t pos = index + 1; pos < body->numParams(); ++pos)
0335         if (body->paramName(pos) == name) {
0336             return CommonIdentifiers::shared()->nullIdentifier;
0337         }
0338 
0339     return name;
0340 }
0341 
0342 bool FunctionImp::implementsConstruct() const
0343 {
0344     return true;
0345 }
0346 
0347 // ECMA 13.2.2 [[Construct]]
0348 JSObject *FunctionImp::construct(ExecState *exec, const List &args)
0349 {
0350     JSObject *proto;
0351     JSValue *p = get(exec, exec->propertyNames().prototype);
0352     if (JSValue::isObject(p)) {
0353         proto = static_cast<JSObject *>(p);
0354     } else {
0355         proto = exec->lexicalInterpreter()->builtinObjectPrototype();
0356     }
0357 
0358     JSObject *obj(new JSObject(proto));
0359 
0360     JSValue *res = call(exec, obj, args);
0361 
0362     if (JSValue::isObject(res)) {
0363         return static_cast<JSObject *>(res);
0364     } else {
0365         return obj;
0366     }
0367 }
0368 
0369 // ------------------------------ Thrower ---------------------------------
0370 
0371 Thrower::Thrower(ErrorType type)
0372     : JSObject(),
0373       m_type(type)
0374 {
0375 }
0376 
0377 JSValue *Thrower::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List & /*args*/)
0378 {
0379     return throwError(exec, m_type);
0380 }
0381 
0382 // ------------------------------ BoundFunction ---------------------------------
0383 
0384 BoundFunction::BoundFunction(ExecState *exec, JSObject *targetFunction, JSObject *boundThis, KJS::List boundArgs)
0385     : InternalFunctionImp(static_cast<FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype())),
0386       m_targetFunction(targetFunction),
0387       m_boundThis(boundThis),
0388       m_boundArgs(boundArgs)
0389 {
0390 }
0391 
0392 // ECMAScript Edition 5.1r6 - 15.3.4.5.2
0393 JSObject *BoundFunction::construct(ExecState *exec, const List &extraArgs)
0394 {
0395     JSObject *target = m_targetFunction;
0396     if (!target->implementsConstruct()) {
0397         return throwError(exec, TypeError);
0398     }
0399     List boundArgs = m_boundArgs;
0400 
0401     List args;
0402     for (int i = 0; i < boundArgs.size(); ++i) {
0403         args.append(boundArgs.at(i));
0404     }
0405     for (int i = 0; i < extraArgs.size(); ++i) {
0406         args.append(extraArgs.at(i));
0407     }
0408 
0409     return target->construct(exec, args);
0410 }
0411 
0412 // ECMAScript Edition 5.1r6 - 15.3.4.5.1
0413 JSValue *BoundFunction::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &extraArgs)
0414 {
0415     List boundArgs = m_boundArgs;
0416     JSObject *boundThis = m_boundThis;
0417     JSObject *target = m_targetFunction;
0418 
0419     List args;
0420     for (int i = 0; i < boundArgs.size(); ++i) {
0421         args.append(boundArgs.at(i));
0422     }
0423     for (int i = 0; i < extraArgs.size(); ++i) {
0424         args.append(extraArgs.at(i));
0425     }
0426 
0427     return target->callAsFunction(exec, boundThis, args);
0428 }
0429 
0430 // ECMAScript Edition 5.1r6 - 15.3.4.5.3
0431 bool BoundFunction::hasInstance(ExecState *exec, JSValue *value)
0432 {
0433     JSObject *target = m_targetFunction;
0434     if (!target->implementsHasInstance()) {
0435         return throwError(exec, TypeError);
0436     }
0437 
0438     return target->hasInstance(exec, value);
0439 }
0440 
0441 void BoundFunction::setTargetFunction(JSObject *targetFunction)
0442 {
0443     m_targetFunction = targetFunction;
0444 }
0445 
0446 void BoundFunction::setBoundArgs(const List &boundArgs)
0447 {
0448     m_boundArgs = boundArgs;
0449 }
0450 
0451 void BoundFunction::setBoundThis(JSObject *boundThis)
0452 {
0453     m_boundThis = boundThis;
0454 }
0455 
0456 // ------------------------------ IndexToNameMap ---------------------------------
0457 
0458 // We map indexes in the arguments array to their corresponding argument names.
0459 // Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
0460 
0461 // Once we have an argument name, we can get and set the argument's value in the
0462 // activation object.
0463 
0464 // We use Identifier::null to indicate that a given argument's value
0465 // isn't stored in the activation object.
0466 
0467 IndexToNameMap::IndexToNameMap(FunctionImp *func, const List &args)
0468 {
0469     _map = new Identifier[args.size()];
0470     this->_size = args.size();
0471 
0472     size_t i = 0;
0473     ListIterator iterator = args.begin();
0474     for (; iterator != args.end(); i++, iterator++) {
0475         _map[i] = func->getParameterName(i);    // null if there is no corresponding parameter
0476     }
0477 }
0478 
0479 IndexToNameMap::~IndexToNameMap()
0480 {
0481     delete [] _map;
0482 }
0483 
0484 bool IndexToNameMap::isMapped(const Identifier &index) const
0485 {
0486     bool indexIsNumber;
0487     int indexAsNumber = index.toStrictUInt32(&indexIsNumber);
0488 
0489     if (!indexIsNumber) {
0490         return false;
0491     }
0492 
0493     if (indexAsNumber >= _size) {
0494         return false;
0495     }
0496 
0497     if (_map[indexAsNumber].isNull()) {
0498         return false;
0499     }
0500 
0501     return true;
0502 }
0503 
0504 void IndexToNameMap::unMap(const Identifier &index)
0505 {
0506     bool indexIsNumber;
0507     int indexAsNumber = index.toStrictUInt32(&indexIsNumber);
0508 
0509     assert(indexIsNumber && indexAsNumber < _size);
0510 
0511     _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier;;
0512 }
0513 
0514 int IndexToNameMap::size() const
0515 {
0516     return _size;
0517 }
0518 
0519 Identifier &IndexToNameMap::operator[](int index)
0520 {
0521     return _map[index];
0522 }
0523 
0524 Identifier &IndexToNameMap::operator[](const Identifier &index)
0525 {
0526     bool indexIsNumber;
0527     int indexAsNumber = index.toStrictUInt32(&indexIsNumber);
0528 
0529     assert(indexIsNumber && indexAsNumber < _size);
0530 
0531     return (*this)[indexAsNumber];
0532 }
0533 
0534 // ------------------------------ Arguments ---------------------------------
0535 
0536 const ClassInfo Arguments::info = {"Arguments", nullptr, nullptr, nullptr};
0537 
0538 // ECMA 10.1.8
0539 Arguments::Arguments(ExecState *exec, FunctionImp *func, const List &args, ActivationImp *act)
0540     : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()),
0541       _activationObject(act),
0542       indexToNameMap(func, args)
0543 {
0544     putDirect(exec->propertyNames().callee, func, DontEnum);
0545     putDirect(exec->propertyNames().length, args.size(), DontEnum);
0546 
0547     int i = 0;
0548     ListIterator iterator = args.begin();
0549     for (; iterator != args.end(); i++, iterator++) {
0550         if (!indexToNameMap.isMapped(Identifier::from(i))) {
0551             //ECMAScript Edition 5.1r6 - 10.6.11.b, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true
0552             JSObject::put(exec, Identifier::from(i), *iterator, None);
0553         }
0554     }
0555 }
0556 
0557 void Arguments::mark()
0558 {
0559     JSObject::mark();
0560     if (_activationObject && !_activationObject->marked()) {
0561         _activationObject->mark();
0562     }
0563 }
0564 
0565 JSValue *Arguments::mappedIndexGetter(ExecState *exec, JSObject *, const Identifier &propertyName, const PropertySlot &slot)
0566 {
0567     Arguments *thisObj = static_cast<Arguments *>(slot.slotBase());
0568     return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
0569 }
0570 
0571 bool Arguments::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0572 {
0573     if (indexToNameMap.isMapped(propertyName)) {
0574         slot.setCustom(this, mappedIndexGetter);
0575         return true;
0576     }
0577 
0578     return JSObject::getOwnPropertySlot(exec, propertyName, slot);
0579 }
0580 
0581 void Arguments::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
0582 {
0583     if (indexToNameMap.isMapped(propertyName)) {
0584         unsigned attr = 0;
0585         JSObject::getPropertyAttributes(propertyName, attr);
0586         if (attr & ReadOnly) {
0587             return;
0588         }
0589 
0590         _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
0591     } else {
0592         JSObject::put(exec, propertyName, value, attr);
0593     }
0594 }
0595 
0596 bool Arguments::deleteProperty(ExecState *exec, const Identifier &propertyName)
0597 {
0598     if (indexToNameMap.isMapped(propertyName)) {
0599         bool result = JSObject::deleteProperty(exec, propertyName);
0600         if (result) {
0601             _activationObject->deleteProperty(exec, indexToNameMap[propertyName]);
0602             indexToNameMap.unMap(propertyName);
0603         }
0604         return true;
0605     } else {
0606         return JSObject::deleteProperty(exec, propertyName);
0607     }
0608 }
0609 
0610 void Arguments::getOwnPropertyNames(ExecState *exec, PropertyNameArray &propertyNames, PropertyMap::PropertyMode mode)
0611 {
0612     unsigned int length = indexToNameMap.size();
0613     unsigned attr;
0614     for (unsigned int i = 0; i < length; ++i) {
0615         attr = 0;
0616         Identifier ident = Identifier::from(i);
0617 
0618         if (indexToNameMap.isMapped(ident) &&
0619                 _activationObject->getPropertyAttributes(indexToNameMap[ident], attr)) {
0620             if (PropertyMap::checkEnumerable(attr, mode)) {
0621                 propertyNames.add(ident);
0622             }
0623         }
0624     }
0625 
0626     JSObject::getOwnPropertyNames(exec, propertyNames, mode);
0627 }
0628 
0629 bool Arguments::defineOwnProperty(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc, bool shouldThrow)
0630 {
0631     bool isMapped = indexToNameMap.isMapped(propertyName);
0632 
0633     Identifier mappedName;
0634     if (isMapped) {
0635         mappedName = indexToNameMap[propertyName];
0636     } else {
0637         mappedName = propertyName;
0638     }
0639 
0640     bool allowed = JSObject::defineOwnProperty(exec, propertyName, desc, false);
0641 
0642     if (!allowed) {
0643         if (shouldThrow) {
0644             throwError(exec, TypeError);
0645         }
0646         return false;
0647     }
0648     if (isMapped) {
0649         if (desc.isAccessorDescriptor()) {
0650             indexToNameMap.unMap(propertyName);
0651         } else {
0652             if (desc.value()) {
0653                 _activationObject->putDirect(mappedName, desc.value(), desc.attributes());
0654             }
0655             if (desc.writableSet() && desc.writable() == false) {
0656                 indexToNameMap.unMap(propertyName);
0657             }
0658         }
0659     }
0660 
0661     return true;
0662 }
0663 
0664 // ------------------------------ ActivationImp --------------------------------
0665 
0666 const ClassInfo ActivationImp::info = {"Activation", nullptr, nullptr, nullptr};
0667 
0668 // ECMA 10.1.6
0669 void ActivationImp::setup(ExecState *exec, FunctionImp *function,
0670                           const List *arguments, LocalStorageEntry *entries)
0671 {
0672     FunctionBodyNode *body = function->body.get();
0673 
0674     size_t total = body->numLocalsAndRegisters();
0675     localStorage  = entries;
0676     lengthSlot()  = total;
0677 
0678     // we can now link ourselves into the scope, which will also fix up our scopeLink().
0679     exec->pushVariableObjectScope(this);
0680 
0681     const FunctionBodyNode::SymbolInfo *symInfo = body->getLocalInfo();
0682 
0683     // Setup our fields
0684     this->arguments = arguments;
0685     functionSlot()  = function;
0686     argumentsObjectSlot() = jsUndefined();
0687     symbolTable     = &body->symbolTable();
0688 
0689     // Set the mark/don't mark flags and attributes for everything
0690     for (size_t p = 0; p < total; ++p) {
0691         entries[p].attributes = symInfo[p].attr;
0692     }
0693 
0694     // Pass in the parameters (ECMA 10.1.3q)
0695 #ifdef KJS_VERBOSE
0696     fprintf(stderr, "%s---------------------------------------------------\n"
0697             "%sprocessing parameters for %s call\n", ind(), ind(),
0698             function->functionName().isEmpty() ? "(internal)" : function->functionName().ascii());
0699 #endif
0700     size_t numParams   = body->numParams();
0701     size_t numPassedIn = min(numParams, static_cast<size_t>(arguments->size()));
0702 
0703     size_t pos = 0;
0704     for (; pos < numPassedIn; ++pos) {
0705         size_t symNum = pos + ActivationImp::NumReservedSlots;
0706         JSValue *v = arguments->atUnchecked(pos);
0707 
0708         entries[symNum].val.valueVal = v;
0709 
0710 #ifdef KJS_VERBOSE
0711         fprintf(stderr, "%s setting parameter %s", ind(), body->paramName(pos).ascii());
0712         printInfo(exec, "to", v);
0713 #endif
0714     }
0715 
0716     for (; pos < numParams; ++pos) {
0717         size_t symNum = pos + ActivationImp::NumReservedSlots;
0718         entries[symNum].val.valueVal = jsUndefined();
0719 
0720 #ifdef KJS_VERBOSE
0721         fprintf(stderr, "%s setting parameter %s to undefined (not passed in)", ind(), body->paramName(pos).ascii());
0722 #endif
0723     }
0724 
0725 #ifdef KJS_VERBOSE
0726     fprintf(stderr, "\n%s---------------------------------\n", ind());
0727     fprintf(stderr, "%sBody:\n", ind());
0728     fprintf(stderr, "%s---------------------------------\n", ind());
0729     printInd(body->toString().ascii());
0730     fprintf(stderr, "\n%s---------------------------------\n\n", ind());
0731 #endif
0732 
0733     // Initialize the rest of the locals to 'undefined'
0734     for (size_t pos = numParams + ActivationImp::NumReservedSlots; pos < total; ++pos) {
0735         entries[pos].val.valueVal = jsUndefined();
0736     }
0737 
0738     // Finally, put in the functions. Note that this relies on above
0739     // steps to have completed, since it can trigger a GC.
0740     size_t  numFuns  = body->numFunctionLocals();
0741     size_t *funsData = body->getFunctionLocalInfo();
0742     for (size_t fun = 0; fun < numFuns; ++fun) {
0743         size_t id = funsData[fun];
0744         entries[id].val.valueVal = symInfo[id].funcDecl->makeFunctionObject(exec);
0745     }
0746 }
0747 
0748 void ActivationImp::performTearOff()
0749 {
0750     // Create a new local array, copy stuff over
0751     size_t total = lengthSlot();
0752     LocalStorageEntry *entries = new LocalStorageEntry[total];
0753     std::memcpy(entries, localStorage, total * sizeof(LocalStorageEntry));
0754     localStorage  = entries;
0755 }
0756 
0757 void ActivationImp::requestTearOff()
0758 {
0759     tearOffNeededSlot() = true;
0760 }
0761 
0762 JSValue *ActivationImp::argumentsGetter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot)
0763 {
0764     ActivationImp *thisObj = static_cast<ActivationImp *>(slot.slotBase());
0765 
0766     if (thisObj->argumentsObjectSlot() == jsUndefined()) {
0767         thisObj->createArgumentsObject(exec);
0768     }
0769 
0770     return thisObj->argumentsObjectSlot();
0771 }
0772 
0773 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
0774 {
0775     return ActivationImp::argumentsGetter;
0776 }
0777 
0778 bool ActivationImp::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0779 {
0780     if (symbolTableGet(propertyName, slot)) {
0781         return true;
0782     }
0783 
0784     if (JSValue **location = getDirectLocation(propertyName)) {
0785         slot.setValueSlot(this, location);
0786         return true;
0787     }
0788 
0789     // Only return the built-in arguments object if it wasn't overridden above.
0790     if (propertyName == exec->propertyNames().arguments) {
0791         slot.setCustom(this, getArgumentsGetter());
0792         return true;
0793     }
0794 
0795     // We don't call through to JSObject because there's no way to give an
0796     // activation object getter properties or a prototype.
0797     ASSERT(!_prop.hasGetterSetterProperties());
0798     ASSERT(prototype() == jsNull());
0799     return false;
0800 }
0801 
0802 bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
0803 {
0804     if (propertyName == exec->propertyNames().arguments) {
0805         return false;
0806     }
0807 
0808     return JSVariableObject::deleteProperty(exec, propertyName);
0809 }
0810 
0811 void ActivationImp::putDirect(const Identifier &propertyName, JSValue *value, int attr)
0812 {
0813     size_t index = symbolTable->get(propertyName.ustring().rep());
0814     if (index != missingSymbolMarker()) {
0815         LocalStorageEntry &entry = localStorage[index];
0816         entry.val.valueVal = value;
0817         entry.attributes = attr;
0818         return;
0819     }
0820 
0821     JSVariableObject::putDirect(propertyName, value, attr);
0822 }
0823 
0824 JSValue *ActivationImp::getDirect(const Identifier &propertyName) const
0825 {
0826     size_t index = symbolTable->get(propertyName.ustring().rep());
0827     if (index != missingSymbolMarker()) {
0828         LocalStorageEntry &entry = localStorage[index];
0829         return entry.val.valueVal;
0830     }
0831 
0832     return JSVariableObject::getDirect(propertyName);
0833 }
0834 
0835 bool ActivationImp::getPropertyAttributes(const Identifier &propertyName, unsigned int &attributes) const
0836 {
0837     size_t index = symbolTable->get(propertyName.ustring().rep());
0838     if (index != missingSymbolMarker()) {
0839         LocalStorageEntry &entry = localStorage[index];
0840         attributes = entry.attributes;
0841         return true;
0842     }
0843 
0844     return JSVariableObject::getPropertyAttributes(propertyName, attributes);
0845 }
0846 
0847 void ActivationImp::put(ExecState *, const Identifier &propertyName, JSValue *value, int attr)
0848 {
0849     // If any bits other than DontDelete are set, then we bypass the read-only check.
0850     bool checkReadOnly = !(attr & ~DontDelete);
0851     if (symbolTablePut(propertyName, value, checkReadOnly)) {
0852         return;
0853     }
0854 
0855     // We don't call through to JSObject because __proto__ and getter/setter
0856     // properties are non-standard extensions that other implementations do not
0857     // expose in the activation object.
0858     ASSERT(!_prop.hasGetterSetterProperties());
0859     _prop.put(propertyName, value, attr, checkReadOnly);
0860 }
0861 
0862 void ActivationImp::createArgumentsObject(ExecState *exec)
0863 {
0864     requestTearOff();
0865     argumentsObjectSlot() = new Arguments(exec, static_cast<FunctionImp *>(functionSlot()),
0866                                           *arguments, const_cast<ActivationImp *>(this));
0867 }
0868 
0869 // ------------------------------ GlobalFunc -----------------------------------
0870 
0871 GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototype *funcProto, int i, int len, const Identifier &name)
0872     : InternalFunctionImp(funcProto, name)
0873     , id(i)
0874 {
0875     putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
0876 }
0877 
0878 static JSValue *encode(ExecState *exec, const List &args, const char *do_not_escape)
0879 {
0880     UString r = "", s, str = JSValue::toString(args[0], exec);
0881     CString cstr = str.UTF8String();
0882     const char *p = cstr.c_str();
0883     for (size_t k = 0; k < cstr.size(); k++, p++) {
0884         char c = *p;
0885         if (c && strchr(do_not_escape, c)) {
0886             r.append(c);
0887         } else {
0888             char tmp[4];
0889             sprintf(tmp, "%%%02X", (unsigned char)c);
0890             r += tmp;
0891         }
0892     }
0893     return jsString(r);
0894 }
0895 
0896 static JSValue *decode(ExecState *exec, const List &args, const char *do_not_unescape)
0897 {
0898     UString s = "", str = JSValue::toString(args[0], exec);
0899     int k = 0, len = str.size();
0900     const UChar *d = str.data();
0901     UChar u;
0902     while (k < len) {
0903         const UChar *p = d + k;
0904         UChar c = *p;
0905         if (c == '%') {
0906             int charLen = 0;
0907             if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) {
0908                 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
0909                 const int sequenceLen = UTF8SequenceLength(b0);
0910                 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
0911                     charLen = sequenceLen * 3;
0912                     char sequence[5];
0913                     sequence[0] = b0;
0914                     for (int i = 1; i < sequenceLen; ++i) {
0915                         const UChar *q = p + i * 3;
0916                         if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc)) {
0917                             sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
0918                         } else {
0919                             charLen = 0;
0920                             break;
0921                         }
0922                     }
0923                     if (charLen != 0) {
0924                         sequence[sequenceLen] = 0;
0925                         const int character = decodeUTF8Sequence(sequence);
0926                         if (character < 0 || character >= 0x110000) {
0927                             charLen = 0;
0928                         } else if (character >= 0x10000) {
0929                             // Convert to surrogate pair.
0930                             s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
0931                             u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
0932                         } else {
0933                             u = static_cast<unsigned short>(character);
0934                         }
0935                     }
0936                 }
0937             }
0938             if (charLen == 0) {
0939                 return throwError(exec, URIError);
0940             }
0941             if (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low())) {
0942                 c = u;
0943                 k += charLen - 1;
0944             }
0945         }
0946         k++;
0947         s.append(c);
0948     }
0949     return jsString(s);
0950 }
0951 
0952 static int parseDigit(unsigned short c, int radix)
0953 {
0954     int digit = -1;
0955 
0956     if (c >= '0' && c <= '9') {
0957         digit = c - '0';
0958     } else if (c >= 'A' && c <= 'Z') {
0959         digit = c - 'A' + 10;
0960     } else if (c >= 'a' && c <= 'z') {
0961         digit = c - 'a' + 10;
0962     }
0963 
0964     if (digit >= radix) {
0965         return -1;
0966     }
0967     return digit;
0968 }
0969 
0970 double parseIntOverflow(const char *s, int length, int radix)
0971 {
0972     double number = 0.0;
0973     double radixMultiplier = 1.0;
0974 
0975     for (const char *p = s + length - 1; p >= s; p--) {
0976         if (radixMultiplier == Inf) {
0977             if (*p != '0') {
0978                 number = Inf;
0979                 break;
0980             }
0981         } else {
0982             int digit = parseDigit(*p, radix);
0983             number += digit * radixMultiplier;
0984         }
0985 
0986         radixMultiplier *= radix;
0987     }
0988 
0989     return number;
0990 }
0991 
0992 double parseInt(const UString &s, int radix)
0993 {
0994     int length = s.size();
0995     int p = 0;
0996 
0997     while (p < length && CommonUnicode::isStrWhiteSpace(s[p].uc)) {
0998         ++p;
0999     }
1000 
1001     double sign = 1;
1002     if (p < length) {
1003         if (s[p] == '+') {
1004             ++p;
1005         } else if (s[p] == '-') {
1006             sign = -1;
1007             ++p;
1008         }
1009     }
1010 
1011     if ((radix == 0 || radix == 16) && length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
1012         radix = 16;
1013         p += 2;
1014     } else if (radix == 0) {
1015         // ECMAscript test262 S15.1.2.2_A5.1_T1 says we should no longer accept octal. To fix remove next 3 lines.
1016         if (p < length && s[p] == '0') {
1017             radix = 8;
1018         } else {
1019             radix = 10;
1020         }
1021     }
1022 
1023     if (radix < 2 || radix > 36) {
1024         return NaN;
1025     }
1026 
1027     int firstDigitPosition = p;
1028     bool sawDigit = false;
1029     double number = 0;
1030     while (p < length) {
1031         int digit = parseDigit(s[p].uc, radix);
1032         if (digit == -1) {
1033             break;
1034         }
1035         sawDigit = true;
1036         number *= radix;
1037         number += digit;
1038         ++p;
1039     }
1040 
1041     if (number >= mantissaOverflowLowerBound) {
1042         if (radix == 10) {
1043             number = kjs_strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), nullptr);
1044         } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) {
1045             number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix);
1046         }
1047     }
1048 
1049     if (!sawDigit) {
1050         return NaN;
1051     }
1052 
1053     return sign * number;
1054 }
1055 
1056 double parseFloat(const UString &s)
1057 {
1058     // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0.
1059     // Need to skip any whitespace and then one + or - sign.
1060     int length = s.size();
1061     int p = 0;
1062     while (p < length && CommonUnicode::isStrWhiteSpace(s[p].uc)) {
1063         ++p;
1064     }
1065     if (p < length && (s[p] == '+' || s[p] == '-')) {
1066         ++p;
1067     }
1068     if (length - p >= 2 && s[p] == '0' && (s[p + 1] == 'x' || s[p + 1] == 'X')) {
1069         return 0;
1070     }
1071 
1072     return s.toDouble(true /*tolerant*/, false /* NaN for empty string */);
1073 }
1074 
1075 JSValue *GlobalFuncImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args)
1076 {
1077     JSValue *res = jsUndefined();
1078 
1079     static const char do_not_escape[] =
1080         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1081         "abcdefghijklmnopqrstuvwxyz"
1082         "0123456789"
1083         "*+-./@_";
1084 
1085     static const char do_not_escape_when_encoding_URI_component[] =
1086         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1087         "abcdefghijklmnopqrstuvwxyz"
1088         "0123456789"
1089         "!'()*-._~";
1090     static const char do_not_escape_when_encoding_URI[] =
1091         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1092         "abcdefghijklmnopqrstuvwxyz"
1093         "0123456789"
1094         "!#$&'()*+,-./:;=?@_~";
1095     static const char do_not_unescape_when_decoding_URI[] =
1096         "#$&+,/:;=?@";
1097 
1098     switch (id) {
1099     case Eval: { // eval()
1100         JSValue *x = args[0];
1101         if (!JSValue::isString(x)) {
1102             return x;
1103         } else {
1104             UString s = JSValue::toString(x, exec);
1105 
1106             int sourceId;
1107             int errLine;
1108             UString errMsg;
1109             RefPtr<ProgramNode> progNode(parser().parseProgram(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg));
1110 
1111             Debugger *dbg = exec->dynamicInterpreter()->debugger();
1112             if (dbg) {
1113                 dbg->reportSourceParsed(exec, progNode.get(), sourceId, UString(), s, 0, errLine, errMsg);
1114             }
1115 
1116             // no program node means a syntax occurred
1117             if (!progNode) {
1118                 return throwError(exec, SyntaxError, errMsg, errLine, sourceId, nullptr);
1119             }
1120 
1121             // If the variable object we're working with is an activation, we better
1122             // tear it off since stuff inside eval can capture it in a closure
1123             if (exec->variableObject()->isActivation()) {
1124                 static_cast<ActivationImp *>(exec->variableObject())->requestTearOff();
1125             }
1126 
1127             // enter a new execution context
1128             EvalExecState newExec(exec->dynamicInterpreter(),
1129                                   exec->dynamicInterpreter()->globalObject(),
1130                                   progNode.get(),
1131                                   exec);
1132 
1133             if (exec->hadException()) {
1134                 newExec.setException(exec->exception());
1135             }
1136 
1137             if (dbg) {
1138                 bool cont = dbg->enterContext(&newExec, sourceId, 0, nullptr, List::empty());
1139                 if (!cont) {
1140                     dbg->imp()->abort();
1141                     return jsUndefined();
1142                 }
1143             }
1144 
1145             // execute the code
1146             progNode->processDecls(&newExec);
1147             Completion c = progNode->execute(&newExec);
1148 
1149             dbg = exec->dynamicInterpreter()->debugger();
1150             if (dbg) {
1151                 bool cont = dbg->exitContext(&newExec, sourceId, 0, nullptr);
1152                 if (!cont) {
1153                     dbg->imp()->abort();
1154                     return jsUndefined();
1155                 }
1156             }
1157 
1158             // if an exception occurred, propagate it back to the previous execution object
1159             if (newExec.hadException()) {
1160                 exec->setException(newExec.exception());
1161             }
1162 
1163             res = jsUndefined();
1164             if (c.complType() == Throw) {
1165                 exec->setException(c.value());
1166             } else if (c.isValueCompletion()) {
1167                 res = c.value();
1168             }
1169         }
1170         break;
1171     }
1172     case ParseInt:
1173         res = jsNumber(parseInt(JSValue::toString(args[0], exec), JSValue::toInt32(args[1], exec)));
1174         break;
1175     case ParseFloat:
1176         res = jsNumber(parseFloat(JSValue::toString(args[0], exec)));
1177         break;
1178     case IsNaN:
1179         res = jsBoolean(isNaN(JSValue::toNumber(args[0], exec)));
1180         break;
1181     case IsFinite: {
1182         double n = JSValue::toNumber(args[0], exec);
1183         res = jsBoolean(!isNaN(n) && !isInf(n));
1184         break;
1185     }
1186     case DecodeURI:
1187         res = decode(exec, args, do_not_unescape_when_decoding_URI);
1188         break;
1189     case DecodeURIComponent:
1190         res = decode(exec, args, "");
1191         break;
1192     case EncodeURI:
1193         res = encode(exec, args, do_not_escape_when_encoding_URI);
1194         break;
1195     case EncodeURIComponent:
1196         res = encode(exec, args, do_not_escape_when_encoding_URI_component);
1197         break;
1198     case Escape: {
1199         UString r = "", s, str = JSValue::toString(args[0], exec);
1200         const UChar *c = str.data();
1201         for (int k = 0; k < str.size(); k++, c++) {
1202             int u = c->uc;
1203             if (u > 255) {
1204                 char tmp[7];
1205                 sprintf(tmp, "%%u%04X", u);
1206                 s = UString(tmp);
1207             } else if (u != 0 && strchr(do_not_escape, (char)u)) {
1208                 s = UString(c, 1);
1209             } else {
1210                 char tmp[4];
1211                 sprintf(tmp, "%%%02X", u);
1212                 s = UString(tmp);
1213             }
1214             r += s;
1215         }
1216         res = jsString(r);
1217         break;
1218     }
1219     case UnEscape: {
1220         UString s = "", str = JSValue::toString(args[0], exec);
1221         int k = 0, len = str.size();
1222         while (k < len) {
1223             const UChar *c = str.data() + k;
1224             UChar u;
1225             if (*c == UChar('%') && k <= len - 6 && *(c + 1) == UChar('u')) {
1226                 if (Lexer::isHexDigit((c + 2)->uc) && Lexer::isHexDigit((c + 3)->uc) &&
1227                         Lexer::isHexDigit((c + 4)->uc) && Lexer::isHexDigit((c + 5)->uc)) {
1228                     u = Lexer::convertUnicode((c + 2)->uc, (c + 3)->uc,
1229                                               (c + 4)->uc, (c + 5)->uc);
1230                     c = &u;
1231                     k += 5;
1232                 }
1233             } else if (*c == UChar('%') && k <= len - 3 &&
1234                        Lexer::isHexDigit((c + 1)->uc) && Lexer::isHexDigit((c + 2)->uc)) {
1235                 u = UChar(Lexer::convertHex((c + 1)->uc, (c + 2)->uc));
1236                 c = &u;
1237                 k += 2;
1238             }
1239             k++;
1240             s += UString(c, 1);
1241         }
1242         res = jsString(s);
1243         break;
1244     }
1245 #ifndef NDEBUG
1246     case KJSPrint:
1247         puts(JSValue::toString(args[0], exec).ascii());
1248         break;
1249 #endif
1250     }
1251 
1252     return res;
1253 }
1254 
1255 } // namespace
1256