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